summaryrefslogtreecommitdiff
path: root/plugins/shn
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-06-20 20:29:25 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-06-20 20:29:25 +0200
commit7088867879c41de81e485f79f60cd2bb08d456d1 (patch)
tree41cbc372885a640681be3aa5551cef43718ea653 /plugins/shn
parent9300f16316eb33715193a0f9a916991a2b24ba9d (diff)
initial port of xmms-shorten plugin
no gapless playback, no configuration, no sample-accurate seeking
Diffstat (limited to 'plugins/shn')
-rw-r--r--plugins/shn/AUTHORS2
-rw-r--r--plugins/shn/LICENSE.shorten20
-rw-r--r--plugins/shn/Makefile.am10
-rw-r--r--plugins/shn/README355
-rw-r--r--plugins/shn/array.c41
-rw-r--r--plugins/shn/bitshift.h33
-rw-r--r--plugins/shn/convert.c42
-rw-r--r--plugins/shn/misc.c151
-rw-r--r--plugins/shn/output.c99
-rw-r--r--plugins/shn/seek.c274
-rw-r--r--plugins/shn/shn.c1798
-rw-r--r--plugins/shn/shn.h277
-rw-r--r--plugins/shn/shorten.c54
-rw-r--r--plugins/shn/shorten.h222
-rw-r--r--plugins/shn/sulawalaw.c192
-rw-r--r--plugins/shn/vario.c146
-rw-r--r--plugins/shn/wave.c264
17 files changed, 3980 insertions, 0 deletions
diff --git a/plugins/shn/AUTHORS b/plugins/shn/AUTHORS
new file mode 100644
index 00000000..cf097345
--- /dev/null
+++ b/plugins/shn/AUTHORS
@@ -0,0 +1,2 @@
+original xmms-shn plugin: Jason Jordan <shnutils@freeshell.org>
+deadbeef port: Alexey Yakovenko <waker@users.sourceforge.net>
diff --git a/plugins/shn/LICENSE.shorten b/plugins/shn/LICENSE.shorten
new file mode 100644
index 00000000..d5066c7a
--- /dev/null
+++ b/plugins/shn/LICENSE.shorten
@@ -0,0 +1,20 @@
+SHORTEN SOFTWARE LICENSE
+
+This software is being provided to you, the LICENSEE, by Tony Robinson
+and SoftSound under the following license. By obtaining, using and/or
+copying this software, you agree that you have read, understood, and
+will comply with these terms and conditions:
+
+This software may not be sold or incorporated into any product which is
+sold without prior permission from SoftSound. When no charge is made,
+this software may be copied and distributed freely.
+
+Permission is granted to use this software for decoding and
+non-commercial encoding (e.g. private or research use). Please email
+shorten@softsound.com for commercial encoding terms.
+
+DISCLAIMER
+
+This software carries no warranty, expressed or implied. The user
+assumes all risks, known or unknown, direct or indirect, which involve
+this software in any way.
diff --git a/plugins/shn/Makefile.am b/plugins/shn/Makefile.am
new file mode 100644
index 00000000..44671140
--- /dev/null
+++ b/plugins/shn/Makefile.am
@@ -0,0 +1,10 @@
+if HAVE_SHN
+shndir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = shn.la
+shn_la_SOURCES = array.c config.h convert.c misc.c output.c seek.c shn.c shn.h shorten.c shorten.h sulawalaw.c vario.c wave.c
+
+shn_la_LDFLAGS = -module
+
+shn_la_LIBADD = $(LDADD)
+AM_CFLAGS = $(CFLAGS) -std=c99
+endif
diff --git a/plugins/shn/README b/plugins/shn/README
new file mode 100644
index 00000000..942d8ace
--- /dev/null
+++ b/plugins/shn/README
@@ -0,0 +1,355 @@
+xmms-shn version 2.4.x
+
+
+--------
+Overview
+--------
+
+xmms-shn provides playback support for shorten (.shn) files in XMMS. Real-time
+seeking support is provided for .shn files that have accompanying seek tables
+generated by shorten 3.x. Otherwise, seeking is not supported.
+
+See the "Shorten 3.x overview" section below for more information about this new
+seek-enabled version of shorten.
+
+
+-------
+License
+-------
+
+xmms-shn is dual-licensed. The code taken from the sources of 'shorten' remains
+under the shorten license (see doc/LICENSE.shorten), while the rest of the code
+is GPL (see COPYING).
+
+
+------------
+Availability
+------------
+
+The latest version of this plugin can always be found at the sites below:
+
+ http://www.etree.org/shnutils/
+ http://shnutils.freeshell.org/
+
+Please see the ChangeLog file for changes to this plugin since its creation.
+
+
+------------
+Dependencies
+------------
+
+As of version 2.0, xmms-shn no longer depends on an external shorten executable
+to work, since the core shorten algorithm has been incorporated directly into
+xmms-shn.
+
+You should have XMMS 1.0.0 or newer, and GTK & GLIB 1.2.2 or newer. The
+configure script will usually find these if you installed them from source.
+However, if you installed any of the above via .rpm's, then you may need to tell
+the configure script where to find them. To see what options are available,
+type:
+
+% ./configure --help
+
+The applicable options are the following:
+
+ --with-xmms-prefix=PFX Prefix where XMMS is installed (optional)
+ --with-xmms-exec-prefix=PFX Exec prefix where XMMS is installed (optional)
+ --with-glib-prefix=PFX Prefix where GLIB is installed (optional)
+ --with-glib-exec-prefix=PFX Exec prefix where GLIB is installed (optional)
+ --disable-glibtest Do not try to compile and run a test GLIB program
+ --with-gtk-prefix=PFX Prefix where GTK is installed (optional)
+ --with-gtk-exec-prefix=PFX Exec prefix where GTK is installed (optional)
+ --disable-gtktest Do not try to compile and run a test GTK program
+
+
+-----------------------------------
+Building and installing this plugin
+-----------------------------------
+
+For instructions on how to build and install this plugin, please consult the
+INSTALL file. It is usually as simple as the following:
+
+% ./configure
+% make
+% su -c "make install"
+Password: (give your root password here)
+%
+
+
+---------------------
+Configuration options
+---------------------
+
+This section details the options that can be found in the plugin's configuration
+window in XMMS, under Preferences -> Audio I/O Plugins -> SHN Player 2.4.x ->
+Configure.
+
+
+Error Display tab:
+==================
+
+ Error display options:
+ ----------------------
+
+ This option determines where any error messages go - to a popup window,
+ to standard error, or to the bit bucket. Pretty self-explanatory.
+
+
+Seek Tables tab:
+================
+
+ Alternate seek table file locations:
+ ------------------------------------
+
+ These options allow you to specify alternate directores to search for .skt
+ (seek table) files. These directories will be searched after all other attempts
+ to locate a seek table for a given file have failed.
+
+ The "Relative seek table path" option allows you to specify a subdirectory
+ relative to the base directory of a given file, where seek tables may reside.
+ This is useful if you create seek table files for a group of .shn files, and
+ store them in a commonly-named subdirectory of that group.
+
+ The "Absolute seek table path" option allows you to specify an absolute directory
+ where seek tables may reside. This is useful if you create seek table files for
+ multiple groups of .shn files and store them in the same directory.
+
+ When searching for seek tables belonging to a file (e.g. '/mnt/shn/filename.shn'),
+ xmms-shn will do the following, in this order, until it either finds a seek table
+ or runs out of places to check for one:
+
+ 1. look for a seek table appended to '/mnt/shn/filename.shn' (whether at the end,
+ or hidden behind an ID3v1 tag)
+
+ 2. look for a seek table in '/mnt/shn/filename.skt'
+
+ 3. look for a seek table in '/mnt/shn/relative_dir/filename.skt', where
+ 'relative_dir' is the directory specified in the "Relative seek table path"
+ option
+
+ 4. look for a seek table in '/absolute_dir/filename.skt', where 'absolute_dir'
+ is the directory specified in the "Absolute seek table path" option
+
+
+Miscellaneous tab:
+==================
+
+ Miscellaneous options:
+ ----------------------
+
+ The "Swap audio bytes on output" option is useful in situations where every file
+ you play back is static. This is known to help on the following platforms:
+
+ + Sun Ultra 10 and Ultra 30 both running Solaris 8
+ + SGI Octane/Irix 6.5
+
+
+ The "Display debug info to stderr" option specifies whether to display debugging
+ messages to stderr. This is potentially useful for remote debugging, and also
+ just to see what's going on. Debug lines are always shown with a prefix of
+ "xmms-shn [debug]:".
+
+ Note that if "Display debug info to stderr" is enabled, and the error display
+ option is set to /dev/null, then all non-debug messages that normally would be
+ suppressed are displayed to stderr with a prefix of "xmms-shn [error]:".
+
+ The "Load text files in file information box" option specifies whether text files
+ found in the same or parent directory as the given file should be loaded into the
+ file information box. Each file found will be loaded in a separate tab. The tabs
+ will be labeled "Text file n", where n starts at 1 and increases with each new
+ text file.
+
+ The "Text file extensions" option lets you specify a comma-separated list of case-
+ sensitive file extensions that xmms-shn will recognize as text files. The default
+ list is "txt,nfo". This option is only useful if "Load text files in file
+ information box" is enabled.
+
+ Note:
+
+ Text file support is useful for quickly determining information about the given
+ file and/or the show it belongs to, assuming such information is contained within
+ the text files.
+
+ Text file location assumes some combination of the following two directory
+ structures: all text files and .shn files in the same directory, or text files
+ in a directory with all .shn files in subdirectories one level deep. This
+ pretty much covers all directory structures seen on live music servers and file-
+ sharing programs.
+
+
+--------------------
+File information box
+--------------------
+
+This section describes the output shown in the file information window, which
+can be viewed by choosing 'File Inf.' from the 'Misc. Opt' button in the
+playlist editor, or selecting 'View File Info' from XMMS's main popup menu.
+
+Properties tab:
+===============
+
+ Filename: Shows the full path to the .shn file being examined.
+
+ Length: Shows the length of the WAVE data in the file, in m:ss.nnn format.
+ If the WAVE data is CD-quality (see below for definition), then the length is
+ shown in m:ss.ff format, where ff is a number from 00 to 74 that best
+ approximates the number of frames (2352-byte blocks) remaining after m:ss.
+
+ Note on rounding: If the WAVE data is CD-quality, then its length is
+ rounded to the nearest frame. Otherwise, it is rounded
+ to the nearest millisecond.
+
+ Seekable: Shows whether a seek table was found for the given file.
+
+ Seek table revision: Shows the revision number of the seek tables, if
+ present. Note that it is possible for this field to contain a numeric value,
+ even when the file is not seekable - for example, xmms-shn might detect that
+ certain seek tables are unsupported or buggy, and disable seeking in those
+ files.
+
+ Compression ratio: Shows the compression ratio for the given file. This is
+ simply the file's actual size divided by its total size, as calculated from
+ the chunk size contained in the WAVE header. Note that if the given file is
+ truncated (or has junk appended to it), then the compression ratio will be
+ skewed accordingly.
+
+ CD-quality properties:
+ ----------------------
+
+ CD-quality: Shows whether the given file is CD-quality. A file is considered
+ to be CD-quality if its header indicates 2 channels, 16 bits/sample, 44100
+ samples/second, and 176400 average bytes/second.
+
+ Cut on a sector boundary: If the given file is CD-quality, then this shows
+ whether the length of its WAVE data is a multiple of 2352 bytes (1/75 of a
+ second). Otherwise, "n/a" is displayed.
+
+ Sector misalignemt: If the file is CD-quality, then the sector misalignment
+ is simply the remainder when the data size is divided by 2352; i.e. it is the
+ number of bytes by which the audio data exceeds the previous sector boundary.
+
+ Long enough to be burned: If the given file is CD-quality, then this shows
+ whether the length of its WAVE data is long enough to be burned (705600 bytes,
+ or 3 seconds in length). Otherwise, "n/a" is displayed.
+
+ WAVE properties:
+ ----------------
+
+ Non-canonical header: Shows whether the WAVE header is canonical. A header
+ is considered canonical if it is 44 bytes long (this is the smallest possible
+ size that a WAVE header can be).
+
+ Extra RIFF chunks: Shows whether there are any extra RIFF chunks following
+ the data chunk in the WAVE stream.
+
+ Possible problems:
+ ------------------
+
+ File contains ID3v2 tag: Shows whether the file contains an ID3v2 tag, and
+ if so, how many bytes it occupies. This is not a problem for xmms-shn (it
+ was able to read the file after all), but could pose a problem for programs
+ that are not able to process files with ID3v2 tags.
+
+
+Details tab:
+============
+
+ This tab primarily shows the raw information taken from the WAVE header of the
+ given file. Each entry is self-explanatory.
+
+
+Text file n tab(s):
+===================
+
+ These tabs, if any, show the contents of all text files found in the same
+ directory or parent directory as the given file. The associated file name for
+ each tab is contained in the frame surrounding the text, and the full path to
+ the file being displayed is shown just above the text. You can control which
+ files are considered to be text files with the "Text file extensions" option in
+ the configuration window. Make sure to select the "Load text files in file
+ information box" option if you want to see these tabs.
+
+
+--------------------
+Shorten 3.x overview
+--------------------
+
+Wayne Stielau has extended shorten 2.3 to support the creation of seek tables.
+Seek tables are simply descriptions of the internal state of the shorten
+algorithm at specific points in the decompression. This allows a modified
+shorten decoder to restore the decoder state of the shorten algorithm for a
+point at (or very close to) the desired seek point. You can get the latest
+version at any of the URLs below:
+
+ http://www.etree.org/shnutils/shorten/
+ http://shnutils.freeshell.org/shorten/
+
+Seek tables may be appended to the .shn itself, or stored in a separate file
+(usually with a suffix of '.skt'). xmms-shn supports both of these options.
+
+The current implementation creates a seek table entry once every 25600 samples.
+For 'CD-quality' WAVE data (44100 samples/sec), this provides a granularity of 1
+seek table entry per 25600/44100 ~= 0.58 seconds, more than what is needed by
+either XMMS or WinAmp.
+
+At 80 bytes per seek table entry, you can expect the seek table to add about
+0.1% to the size of the existing shortened file, for 'CD-quality' WAVE data.
+So the seek table generated for 1 GB of already-shortened 'CD-quality' WAVE data
+will fit on a floppy! Quite a deal, if you ask me. :-)
+
+Best of all, shorten 3.x is fully backward compatible with version 2.3, meaning
+that any files created by shorten 3.x can be processed by version 2.3 with no
+problems.
+
+The command line options for dealing with seek tables in shorten 3.x are:
+
+ -e erase seek table appended to input file *
+ -i inquire as to whether a seek table is appended to input file *
+ -k append seek table information to existing shorten file
+ -s generate seek table information in separate file [input file].skt
+ -S[name] generate seek table information in separate file given by [name]
+ -v 3 format version number (2: no seek info; 3: default) *
+
+ * only available in versions 3.2 and up
+
+By default, any file shortened with version 3.x will automatically have seek
+tables appended to it. In later versions (3.2 and up), you can disable this
+with the -v2 switch.
+
+** NOTE ON SEEKING IN FILES ENCODED WITH NON-DEFAULT OPTIONS **
+
+Seek tables are unable to support all types of SHN files due to limitations in
+their original design. The only files they can fully support are ones encoded
+with the following values:
+
+ -c = 1 or 2 (number of channels, default is 1)
+ -p in the range [0 .. 3] (maximum LPC predictor order, default is 0)
+ -m in the range [0 .. 4] (number of block for mean estimation, default is 4)
+
+If xmms-shn detects a file encoded with parameters that fall outside of these
+ranges, then seeking is automatically disabled for that file.
+
+
+----------
+Known bugs
+----------
+
+I test this plugin aggressively until I can no longer make it crash. That does
+not mean that there are no bugs. If you can crash it, I want to hear about it -
+please report it to me at the address below. The xmms-shn webpage (see the
+Availability section above) will always contain an up-to-date list of reported
+bugs, so be sure to check that page before you send me a bug report.
+
+Also, feel free to send me any questions, comments, feature requests, patches...
+I always enjoy getting feedback.
+
+Enjoy!
+
+Jason Jordan <shnutils@freeshell.org>
+
+
+==================
+Document revision:
+==================
+
+$Id: README,v 1.27 2007/03/29 00:08:05 jason Exp $
diff --git a/plugins/shn/array.c b/plugins/shn/array.c
new file mode 100644
index 00000000..0adf8549
--- /dev/null
+++ b/plugins/shn/array.c
@@ -0,0 +1,41 @@
+/******************************************************************************
+* *
+* Copyright (C) 1992-1995 Tony Robinson *
+* *
+* See the file doc/LICENSE.shorten for conditions on distribution and usage *
+* *
+******************************************************************************/
+
+/*
+ * $Id: array.c,v 1.7 2003/08/26 05:34:04 jason Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "shorten.h"
+#include "shn.h"
+
+void *pmalloc(ulong size, shn_file *this_shn) {
+ void *ptr;
+
+ ptr = malloc(size);
+
+ if(ptr == NULL)
+ shn_error_fatal(this_shn,"Call to malloc(%ld) failed in pmalloc() -\nyour system may be low on memory", size);
+
+ return(ptr);
+}
+
+slong **long2d(ulong n0, ulong n1, shn_file *this_shn) {
+ slong **array0 = NULL;
+
+ if((array0 = (slong**) pmalloc((ulong) (n0 * sizeof(slong*) +
+ n0 * n1 * sizeof(slong)),this_shn)) != NULL ) {
+ slong *array1 = (slong*) (array0 + n0);
+ int i;
+
+ for(i = 0; i < n0; i++)
+ array0[i] = array1 + i * n1;
+ }
+ return(array0);
+}
diff --git a/plugins/shn/bitshift.h b/plugins/shn/bitshift.h
new file mode 100644
index 00000000..ad491f92
--- /dev/null
+++ b/plugins/shn/bitshift.h
@@ -0,0 +1,33 @@
+char ulaw_maxshift[256] = {12,8,7,9,7,8,7,10,7,8,7,9,7,8,7,11,6,7,6,8,6,7,6,9,6,7,6,8,6,7,6,10,5,6,5,7,5,6,5,8,5,6,5,7,5,6,5,9,5,4,6,4,5,4,7,4,5,4,6,4,5,4,8,4,3,5,3,4,3,6,3,4,3,5,3,4,3,7,3,4,2,3,2,5,2,3,2,4,2,3,2,6,2,3,2,4,1,2,1,3,1,2,1,5,1,2,1,3,1,2,1,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,0,7,8,7,9,7,8,7,10,7,8,7,9,7,8,7,11,6,7,6,8,6,7,6,9,6,7,6,8,6,7,6,10,5,6,5,7,5,6,5,8,5,6,5,7,5,6,5,9,5,4,6,4,5,4,7,4,5,4,6,4,5,4,8,4,3,5,3,4,3,6,3,4,3,5,3,4,3,7,3,4,2,3,2,5,2,3,2,4,2,3,2,6,2,3,2,4,1,2,1,3,1,2,1,5,1,2,1,3,1,2,1,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,12};
+
+/*schar ulaw_inward[13][256] = {
+{4294967169,4294967170,4294967171,4294967172,4294967173,4294967174,4294967175,4294967176,4294967177,4294967178,4294967179,4294967180,4294967181,4294967182,4294967183,4294967184,4294967185,4294967186,4294967187,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967282,4294967283,4294967284,4294967285,4294967286,4294967287,4294967288,4294967289,4294967290,4294967291,4294967292,4294967293,4294967294,4294967295,4294967168,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0},
+{4294967177,4294967178,4294967179,4294967180,4294967181,4294967182,4294967183,4294967184,4294967185,4294967186,4294967187,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967282,4294967283,4294967284,4294967285,4294967286,4294967287,4294967288,4294967168,4294967289,4294967169,4294967290,4294967170,4294967291,4294967171,4294967292,4294967172,4294967293,4294967173,4294967294,4294967174,4294967295,4294967175,4294967176,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,127,7,126,6,125,5,124,4,123,3,122,2,121,1,120,0},
+{4294967189,4294967190,4294967191,4294967192,4294967193,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967282,4294967283,4294967284,4294967168,4294967285,4294967169,4294967286,4294967170,4294967287,4294967171,4294967288,4294967172,4294967289,4294967173,4294967290,4294967174,4294967291,4294967175,4294967292,4294967176,4294967177,4294967178,4294967293,4294967179,4294967180,4294967181,4294967294,4294967182,4294967183,4294967184,4294967295,4294967185,4294967186,4294967187,4294967188,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,127,11,126,10,125,9,124,8,123,7,122,6,121,5,120,4,119,118,117,3,116,115,114,2,113,112,111,1,110,109,108,0},
+{4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967282,4294967168,4294967283,4294967169,4294967284,4294967170,4294967285,4294967171,4294967286,4294967172,4294967287,4294967173,4294967288,4294967174,4294967289,4294967175,4294967290,4294967176,4294967177,4294967178,4294967291,4294967179,4294967180,4294967181,4294967292,4294967182,4294967183,4294967184,4294967293,4294967185,4294967186,4294967187,4294967294,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967194,4294967295,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967202,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,127,13,126,12,125,11,124,10,123,9,122,8,121,7,120,6,119,118,117,5,116,115,114,4,113,112,111,3,110,109,108,2,107,106,105,104,103,102,101,1,100,99,98,97,96,95,94,0},
+{4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967168,4294967282,4294967169,4294967283,4294967170,4294967284,4294967171,4294967285,4294967172,4294967286,4294967173,4294967287,4294967174,4294967288,4294967175,4294967289,4294967176,4294967177,4294967178,4294967290,4294967179,4294967180,4294967181,4294967291,4294967182,4294967183,4294967184,4294967292,4294967185,4294967186,4294967187,4294967293,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967194,4294967294,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967295,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,127,14,126,13,125,12,124,11,123,10,122,9,121,8,120,7,119,118,117,6,116,115,114,5,113,112,111,4,110,109,108,3,107,106,105,104,103,102,101,2,100,99,98,97,96,95,94,1,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,0},
+{4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967168,4294967282,4294967169,4294967283,4294967170,4294967284,4294967171,4294967285,4294967172,4294967286,4294967173,4294967287,4294967174,4294967288,4294967175,4294967176,4294967289,4294967177,4294967178,4294967179,4294967290,4294967180,4294967181,4294967182,4294967291,4294967183,4294967184,4294967185,4294967292,4294967186,4294967187,4294967188,4294967189,4294967190,4294967293,4294967191,4294967192,4294967193,4294967194,4294967195,4294967196,4294967197,4294967294,4294967198,4294967199,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967295,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,127,14,126,13,125,12,124,11,123,10,122,9,121,8,120,119,7,118,117,116,6,115,114,113,5,112,111,110,4,109,108,107,106,105,3,104,103,102,101,100,99,98,2,97,96,95,94,93,92,91,90,89,88,87,1,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,0},
+{4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967168,4294967281,4294967169,4294967282,4294967170,4294967283,4294967171,4294967284,4294967172,4294967285,4294967173,4294967286,4294967174,4294967287,4294967175,4294967288,4294967176,4294967177,4294967289,4294967178,4294967179,4294967180,4294967290,4294967181,4294967182,4294967183,4294967291,4294967184,4294967185,4294967186,4294967292,4294967187,4294967188,4294967189,4294967190,4294967191,4294967192,4294967293,4294967193,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967294,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967295,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,127,15,126,14,125,13,124,12,123,11,122,10,121,9,120,8,119,118,7,117,116,115,6,114,113,112,5,111,110,109,4,108,107,106,105,104,103,3,102,101,100,99,98,97,96,2,95,94,93,92,91,90,89,88,87,86,85,84,83,1,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,0},
+{4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967168,4294967281,4294967169,4294967282,4294967170,4294967283,4294967171,4294967284,4294967172,4294967285,4294967173,4294967286,4294967174,4294967287,4294967175,4294967288,4294967176,4294967177,4294967178,4294967289,4294967179,4294967180,4294967181,4294967290,4294967182,4294967183,4294967184,4294967291,4294967185,4294967186,4294967187,4294967292,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967293,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967294,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967295,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,127,15,126,14,125,13,124,12,123,11,122,10,121,9,120,8,119,118,117,7,116,115,114,6,113,112,111,5,110,109,108,4,107,106,105,104,103,102,3,101,100,99,98,97,96,95,2,94,93,92,91,90,89,88,87,86,85,84,83,82,81,1,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,0},
+{4294967280,4294967281,4294967168,4294967282,4294967169,4294967283,4294967170,4294967284,4294967171,4294967285,4294967172,4294967286,4294967173,4294967287,4294967174,4294967288,4294967175,4294967176,4294967177,4294967289,4294967178,4294967179,4294967180,4294967290,4294967181,4294967182,4294967183,4294967291,4294967184,4294967185,4294967186,4294967292,4294967187,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967293,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967294,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967295,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,127,15,126,14,125,13,124,12,123,11,122,10,121,9,120,8,119,118,117,7,116,115,114,6,113,112,111,5,110,109,108,4,107,106,105,104,103,102,101,3,100,99,98,97,96,95,94,2,93,92,91,90,89,88,87,86,85,84,83,82,81,80,1,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,0},
+{4294967288,4294967168,4294967169,4294967289,4294967170,4294967171,4294967172,4294967290,4294967173,4294967174,4294967175,4294967291,4294967176,4294967177,4294967178,4294967292,4294967179,4294967180,4294967181,4294967182,4294967183,4294967184,4294967185,4294967293,4294967186,4294967187,4294967188,4294967189,4294967190,4294967191,4294967192,4294967294,4294967193,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967295,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967282,4294967283,4294967284,4294967285,4294967286,4294967287,127,126,125,7,124,123,122,6,121,120,119,5,118,117,116,4,115,114,113,112,111,110,109,3,108,107,106,105,104,103,102,2,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,1,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,0},
+{4294967292,4294967168,4294967169,4294967170,4294967171,4294967172,4294967173,4294967293,4294967174,4294967175,4294967176,4294967177,4294967178,4294967179,4294967180,4294967294,4294967181,4294967182,4294967183,4294967184,4294967185,4294967186,4294967187,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967194,4294967195,4294967295,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967282,4294967283,4294967284,4294967285,4294967286,4294967287,4294967288,4294967289,4294967290,4294967291,127,126,125,124,123,122,121,3,120,119,118,117,116,115,114,2,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,1,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,0},
+{4294967294,4294967168,4294967169,4294967170,4294967171,4294967172,4294967173,4294967174,4294967175,4294967176,4294967177,4294967178,4294967179,4294967180,4294967181,4294967295,4294967182,4294967183,4294967184,4294967185,4294967186,4294967187,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967282,4294967283,4294967284,4294967285,4294967286,4294967287,4294967288,4294967289,4294967290,4294967291,4294967292,4294967293,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,1,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,0},
+{4294967295,4294967168,4294967169,4294967170,4294967171,4294967172,4294967173,4294967174,4294967175,4294967176,4294967177,4294967178,4294967179,4294967180,4294967181,4294967182,4294967183,4294967184,4294967185,4294967186,4294967187,4294967188,4294967189,4294967190,4294967191,4294967192,4294967193,4294967194,4294967195,4294967196,4294967197,4294967198,4294967199,4294967200,4294967201,4294967202,4294967203,4294967204,4294967205,4294967206,4294967207,4294967208,4294967209,4294967210,4294967211,4294967212,4294967213,4294967214,4294967215,4294967216,4294967217,4294967218,4294967219,4294967220,4294967221,4294967222,4294967223,4294967224,4294967225,4294967226,4294967227,4294967228,4294967229,4294967230,4294967231,4294967232,4294967233,4294967234,4294967235,4294967236,4294967237,4294967238,4294967239,4294967240,4294967241,4294967242,4294967243,4294967244,4294967245,4294967246,4294967247,4294967248,4294967249,4294967250,4294967251,4294967252,4294967253,4294967254,4294967255,4294967256,4294967257,4294967258,4294967259,4294967260,4294967261,4294967262,4294967263,4294967264,4294967265,4294967266,4294967267,4294967268,4294967269,4294967270,4294967271,4294967272,4294967273,4294967274,4294967275,4294967276,4294967277,4294967278,4294967279,4294967280,4294967281,4294967282,4294967283,4294967284,4294967285,4294967286,4294967287,4294967288,4294967289,4294967290,4294967291,4294967292,4294967293,4294967294,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0}
+};*/
+
+uchar ulaw_outward[13][256] = {
+{127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128},
+{112,114,116,118,120,122,124,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,113,115,117,119,121,123,125,255,253,251,249,247,245,243,241,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,252,250,248,246,244,242,240},
+{96,98,100,102,104,106,108,110,112,113,114,116,117,118,120,121,122,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,99,101,103,105,107,109,111,115,119,123,255,251,247,243,239,237,235,233,231,229,227,225,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,250,249,248,246,245,244,242,241,240,238,236,234,232,230,228,226,224},
+{80,82,84,86,88,90,92,94,96,97,98,100,101,102,104,105,106,108,109,110,112,113,114,115,116,117,118,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,81,83,85,87,89,91,93,95,99,103,107,111,119,255,247,239,235,231,227,223,221,219,217,215,213,211,209,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,246,245,244,243,242,241,240,238,237,236,234,233,232,230,229,228,226,225,224,222,220,218,216,214,212,210,208},
+{64,66,68,70,72,74,76,78,80,81,82,84,85,86,88,89,90,92,93,94,96,97,98,99,100,101,102,104,105,106,107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,65,67,69,71,73,75,77,79,83,87,91,95,103,111,255,239,231,223,219,215,211,207,205,203,201,199,197,195,193,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,238,237,236,235,234,233,232,230,229,228,227,226,225,224,222,221,220,218,217,216,214,213,212,210,209,208,206,204,202,200,198,196,194,192},
+{49,51,53,55,57,59,61,63,64,66,67,68,70,71,72,74,75,76,78,79,80,81,82,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,52,54,56,58,60,62,65,69,73,77,83,91,103,255,231,219,211,205,201,197,193,190,188,186,184,182,180,178,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,230,229,228,227,226,225,224,223,222,221,220,218,217,216,215,214,213,212,210,209,208,207,206,204,203,202,200,199,198,196,195,194,192,191,189,187,185,183,181,179,177},
+{32,34,36,38,40,42,44,46,48,49,51,52,53,55,56,57,59,60,61,63,64,65,66,67,68,70,71,72,73,74,75,76,78,79,80,81,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,35,37,39,41,43,45,47,50,54,58,62,69,77,91,255,219,205,197,190,186,182,178,175,173,171,169,167,165,163,161,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,218,217,216,215,214,213,212,211,210,209,208,207,206,204,203,202,201,200,199,198,196,195,194,193,192,191,189,188,187,185,184,183,181,180,179,177,176,174,172,170,168,166,164,162,160},
+{16,18,20,22,24,26,28,30,32,33,34,36,37,38,40,41,42,44,45,46,48,49,50,51,52,53,55,56,57,58,59,60,61,63,64,65,66,67,68,69,70,71,72,73,74,75,76,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,19,21,23,25,27,29,31,35,39,43,47,54,62,77,255,205,190,182,175,171,167,163,159,157,155,153,151,149,147,145,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,204,203,202,201,200,199,198,197,196,195,194,193,192,191,189,188,187,186,185,184,183,181,180,179,178,177,176,174,173,172,170,169,168,166,165,164,162,161,160,158,156,154,152,150,148,146,144},
+{2,4,6,8,10,12,14,16,17,18,20,21,22,24,25,26,28,29,30,32,33,34,35,36,37,38,40,41,42,43,44,45,46,48,49,50,51,52,53,54,55,56,57,58,59,60,61,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,3,5,7,9,11,13,15,19,23,27,31,39,47,62,255,190,175,167,159,155,151,147,143,141,139,137,135,133,131,129,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,189,188,187,186,185,184,183,182,181,180,179,178,177,176,174,173,172,171,170,169,168,166,165,164,163,162,161,160,158,157,156,154,153,152,150,149,148,146,145,144,142,140,138,136,134,132,130,128},
+{1,2,4,5,6,8,9,10,12,13,14,16,17,18,19,20,21,22,24,25,26,27,28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,3,7,11,15,23,31,47,255,175,159,151,143,139,135,131,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,158,157,156,155,154,153,152,150,149,148,147,146,145,144,142,141,140,138,137,136,134,133,132,130,129,128},
+{1,2,3,4,5,6,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,7,15,31,255,159,143,135,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,142,141,140,139,138,137,136,134,133,132,131,130,129,128},
+{1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,15,255,143,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128},
+{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128}
+};
diff --git a/plugins/shn/convert.c b/plugins/shn/convert.c
new file mode 100644
index 00000000..c11d86c0
--- /dev/null
+++ b/plugins/shn/convert.c
@@ -0,0 +1,42 @@
+/* convert.c - functions to convert little-endian data to endian of host
+ * Copyright (C) 2000-2007 Jason Jordan <shnutils@freeshell.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * $Id: convert.c,v 1.9 2007/03/23 05:49:48 jason Exp $
+ */
+
+#include <stdlib.h>
+#include "shorten.h"
+
+ulong shn_uchar_to_ulong_le(uchar *buf)
+/* converts 4 bytes stored in little-endian format to a ulong */
+{
+ return (ulong)((buf[3] << 24) + (buf[2] << 16) + (buf[1] << 8) + buf[0]);
+}
+
+slong shn_uchar_to_slong_le(uchar *buf)
+/* converts 4 bytes stored in little-endian format to an slong */
+{
+ return (slong)shn_uchar_to_ulong_le(buf);
+}
+
+ushort shn_uchar_to_ushort_le(uchar *buf)
+/* converts 4 bytes stored in little-endian format to a ushort */
+{
+ return (ushort)((buf[1] << 8) + buf[0]);
+}
diff --git a/plugins/shn/misc.c b/plugins/shn/misc.c
new file mode 100644
index 00000000..759bb1d1
--- /dev/null
+++ b/plugins/shn/misc.c
@@ -0,0 +1,151 @@
+/* misc.c - miscellaneous functions
+ * Copyright (C) 2000-2007 Jason Jordan <shnutils@freeshell.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * $Id: misc.c,v 1.14 2007/03/23 05:49:48 jason Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include "shorten.h"
+
+void shn_snprintf(char *dest,int maxlen,char *formatstr, ...)
+/* acts like snprintf, but makes 100% sure the string is NULL-terminated */
+{
+ va_list args;
+
+ va_start(args,formatstr);
+
+ shn_vsnprintf(dest,maxlen,formatstr,args);
+
+ dest[maxlen-1] = 0;
+
+ va_end(args);
+}
+
+int shn_filename_contains_a_dot(const char *filename)
+{
+ char *slash,*dot;
+
+ dot = strrchr(filename,'.');
+ if (!dot)
+ return 0;
+
+ slash = strrchr(filename,'/');
+ if (!slash)
+ return 1;
+
+ if (slash < dot)
+ return 1;
+ else
+ return 0;
+}
+
+char *shn_get_base_filename(const char *filename)
+{
+ const char *b,*e,*p;
+ char *base;
+
+ b = strrchr(filename,'/');
+
+ if (b)
+ b++;
+ else
+ b = filename;
+
+ e = strrchr(filename,'.');
+
+ if (e < b)
+ e = filename + strlen(filename);
+
+ if (NULL == (base = malloc((e - b + 1) * sizeof(char))))
+ {
+ shn_debug("Could not allocate memory for base filename");
+ return NULL;
+ }
+
+ for (p=b;p<e;p++)
+ *(base + (p - b)) = *p;
+
+ *(base + (p - b)) = '\0';
+
+ return base;
+}
+
+char *shn_get_base_directory(const char *filename)
+{
+ const char *e,*p;
+ char *base;
+
+ e = strrchr(filename,'/');
+
+ if (!e)
+ e = filename;
+
+ if (NULL == (base = malloc((e - filename + 1) * sizeof(char))))
+ {
+ shn_debug("Could not allocate memory for base directory");
+ return NULL;
+ }
+
+ for (p=filename;p<e;p++)
+ *(base + (p - filename)) = *p;
+
+ *(base + (p - filename)) = '\0';
+
+ return base;
+}
+
+void shn_length_to_str(shn_file *info)
+/* converts length of file to a string in m:ss or m:ss.ff format */
+{
+ ulong newlength,rem1,rem2,frames,ms;
+ double tmp;
+
+ if (PROB_NOT_CD(info->wave_header)) {
+ newlength = (ulong)info->wave_header.exact_length;
+
+ tmp = info->wave_header.exact_length - (double)((ulong)info->wave_header.exact_length);
+ ms = (ulong)((tmp * 1000.0) + 0.5);
+
+ if (1000 == ms) {
+ ms = 0;
+ newlength++;
+ }
+
+ shn_snprintf(info->wave_header.m_ss,16,"%lu:%02lu.%03lu",newlength/60,newlength%60,ms);
+ }
+ else {
+ newlength = info->wave_header.length;
+
+ rem1 = info->wave_header.data_size % CD_RATE;
+ rem2 = rem1 % CD_BLOCK_SIZE;
+
+ frames = rem1 / CD_BLOCK_SIZE;
+ if (rem2 >= (CD_BLOCK_SIZE / 2))
+ frames++;
+
+ if (frames == CD_BLOCKS_PER_SEC) {
+ frames = 0;
+ newlength++;
+ }
+
+ shn_snprintf(info->wave_header.m_ss,16,"%lu:%02lu.%02lu",newlength/60,newlength%60,frames);
+ }
+}
diff --git a/plugins/shn/output.c b/plugins/shn/output.c
new file mode 100644
index 00000000..1c1f9092
--- /dev/null
+++ b/plugins/shn/output.c
@@ -0,0 +1,99 @@
+/* output.c - functions for message and error output
+ * Copyright (C) 2000-2007 Jason Jordan <shnutils@freeshell.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * $Id: output.c,v 1.11 2007/03/23 05:49:48 jason Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "shorten.h"
+#include "shn.h"
+
+void print_lines(char *prefix,char *message)
+{
+ char *head, *tail;
+
+ head = tail = message;
+ while (*head != '\0') {
+ if (*head == '\n') {
+ *head = '\0';
+ fprintf(stderr,"%s%s\n",prefix,tail);
+ tail = head + 1;
+ }
+ head++;
+ }
+ fprintf(stderr,"%s%s\n",prefix,tail);
+}
+
+void shn_error(char *msg, ...)
+{
+ va_list args;
+ char msgbuf[BUF_SIZE];
+
+ va_start(args,msg);
+
+ shn_vsnprintf(msgbuf,BUF_SIZE,msg,args);
+
+ switch (shn_cfg.error_output_method) {
+ case ERROR_OUTPUT_STDERR:
+ print_lines(PACKAGE ": ",msgbuf);
+ break;
+// case ERROR_OUTPUT_WINDOW:
+// shn_message_box(msgbuf);
+// break;
+ default:
+ if (0 != shn_cfg.verbose)
+ print_lines(PACKAGE " [error]: ",msgbuf);
+ }
+
+ va_end(args);
+}
+
+void shn_debug(char *msg, ...)
+{
+ va_list args;
+ char msgbuf[BUF_SIZE];
+
+ va_start(args,msg);
+
+ shn_vsnprintf(msgbuf,BUF_SIZE,msg,args);
+
+ if (0 != shn_cfg.verbose)
+ print_lines(PACKAGE " [debug]: ",msgbuf);
+
+ va_end(args);
+}
+
+void shn_error_fatal(shn_file *this_shn,char *complaint, ...)
+{
+ va_list args;
+
+ va_start(args,complaint);
+
+ if (NULL != this_shn) {
+ if (0 == this_shn->vars.fatal_error) {
+ this_shn->vars.fatal_error = 1;
+ this_shn->vars.going = 0;
+ shn_vsnprintf(this_shn->vars.fatal_error_msg,BUF_SIZE,complaint,args);
+ }
+ }
+
+ va_end(args);
+}
diff --git a/plugins/shn/seek.c b/plugins/shn/seek.c
new file mode 100644
index 00000000..a0eb3af4
--- /dev/null
+++ b/plugins/shn/seek.c
@@ -0,0 +1,274 @@
+/* seek.c - functions related to real-time seeking
+ * Copyright (C) 2000-2007 Jason Jordan <shnutils@freeshell.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * $Id: seek.c,v 1.18 2007/03/23 05:49:48 jason Exp $
+ */
+
+#include <stdlib.h>
+#include "shorten.h"
+#include "shn.h"
+
+#define ID3V1_TAG_SIZE 128
+
+shn_seek_entry *shn_seek_entry_search(shn_seek_entry *table,ulong goal,ulong min,ulong max,ulong resolution)
+{
+ ulong med = (min + max) / 2;
+ shn_seek_entry *middle = table + med;
+ ulong sample = shn_uchar_to_ulong_le(middle->data);
+
+ shn_debug("Examining seek table entry %lu with sample %lu (min/max = %lu/%lu, goal sample is %lu, resolution is %lu samples)",med,sample,min,max,goal,resolution);
+
+ if (goal < sample)
+ return shn_seek_entry_search(table,goal,min,med-1,resolution);
+ if (goal > sample + resolution)
+ return shn_seek_entry_search(table,goal,med+1,max,resolution);
+ return middle;
+}
+
+int load_separate_seek_table_generic(char *filename,shn_file *this_shn)
+{
+ FILE *f;
+ slong seek_table_len;
+
+ shn_debug("Looking for seek table in separate file: '%s'",filename);
+
+ if (!(f=fopen(filename,"rb")))
+ {
+ return 0;
+ }
+
+ fseek(f,0,SEEK_END);
+ seek_table_len = (slong)ftell(f) - SEEK_HEADER_SIZE;
+ fseek(f,0,SEEK_SET);
+
+ if (fread((void *)this_shn->seek_header.data,1,SEEK_HEADER_SIZE,f) == SEEK_HEADER_SIZE)
+ {
+ this_shn->seek_header.version = (slong)shn_uchar_to_ulong_le(this_shn->seek_header.data+4);
+ this_shn->seek_header.shnFileSize = shn_uchar_to_ulong_le(this_shn->seek_header.data+8);
+ if (memcmp(this_shn->seek_header.data,SEEK_HEADER_SIGNATURE,strlen(SEEK_HEADER_SIGNATURE)) == 0)
+ {
+ if (this_shn->seek_header.shnFileSize != this_shn->wave_header.actual_size)
+ {
+ shn_debug("warning: Seek table expected .shn file size %lu differs from actual .shn file size %lu - seek table might not belong to this file",
+ this_shn->seek_header.shnFileSize,this_shn->wave_header.actual_size);
+ }
+
+ if ((this_shn->seek_table = malloc(seek_table_len)))
+ {
+ if (fread((void *)this_shn->seek_table,1,seek_table_len,f) == seek_table_len)
+ {
+ shn_debug("Successfully loaded seek table in separate file: '%s'",filename);
+
+ this_shn->vars.seek_table_entries = seek_table_len / SEEK_ENTRY_SIZE;
+
+ if (this_shn->vars.seek_table_entries > 1)
+ this_shn->vars.seek_resolution = shn_uchar_to_ulong_le(this_shn->seek_table->data+SEEK_ENTRY_SIZE);
+ else
+ this_shn->vars.seek_resolution = SEEK_RESOLUTION;
+
+ fclose(f);
+
+ return 1;
+ }
+ }
+ }
+ }
+
+ fclose(f);
+ return 0;
+}
+
+int load_appended_seek_table(shn_file *this_shn,const char *filename,long bytes_from_end)
+{
+ switch (bytes_from_end)
+ {
+ case 0:
+ shn_debug("Looking for seek table appended to file: '%s'",filename);
+ break;
+ case ID3V1_TAG_SIZE:
+ shn_debug("Looking for seek table hidden behind an ID3v1 tag at the end of file: '%s'",filename);
+ break;
+ default:
+ shn_debug("Looking for seek table located %ld bytes from the end of file: '%s'",bytes_from_end,filename);
+ break;
+ }
+
+ deadbeef->fseek(this_shn->vars.fd,-(SEEK_TRAILER_SIZE+bytes_from_end),SEEK_END);
+ if (deadbeef->fread((void *)this_shn->seek_trailer.data,1,SEEK_TRAILER_SIZE,this_shn->vars.fd) == SEEK_TRAILER_SIZE)
+ {
+ this_shn->seek_trailer.seekTableSize = shn_uchar_to_ulong_le(this_shn->seek_trailer.data);
+ if (memcmp(this_shn->seek_trailer.data+4,SEEK_TRAILER_SIGNATURE,strlen(SEEK_TRAILER_SIGNATURE)) == 0)
+ {
+ deadbeef->fseek(this_shn->vars.fd,-(this_shn->seek_trailer.seekTableSize+bytes_from_end),SEEK_END);
+ this_shn->seek_trailer.seekTableSize -= (SEEK_HEADER_SIZE + SEEK_TRAILER_SIZE);
+ if (deadbeef->fread((void *)this_shn->seek_header.data,1,SEEK_HEADER_SIZE,this_shn->vars.fd) == SEEK_HEADER_SIZE)
+ {
+ this_shn->seek_header.version = (slong)shn_uchar_to_ulong_le(this_shn->seek_header.data+4);
+ this_shn->seek_header.shnFileSize = shn_uchar_to_ulong_le(this_shn->seek_header.data+8);
+ if ((this_shn->seek_table = malloc(this_shn->seek_trailer.seekTableSize)))
+ {
+ if (deadbeef->fread((void *)this_shn->seek_table,1,this_shn->seek_trailer.seekTableSize,this_shn->vars.fd) == this_shn->seek_trailer.seekTableSize)
+ {
+ shn_debug("Successfully loaded seek table appended to file: '%s'",filename);
+
+ this_shn->vars.seek_table_entries = this_shn->seek_trailer.seekTableSize / SEEK_ENTRY_SIZE;
+
+ if (this_shn->vars.seek_table_entries > 1)
+ this_shn->vars.seek_resolution = shn_uchar_to_ulong_le(this_shn->seek_table->data+SEEK_ENTRY_SIZE);
+ else
+ this_shn->vars.seek_resolution = SEEK_RESOLUTION;
+
+ return 1;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int load_separate_seek_table_samedir(shn_file *this_shn,const char *filename)
+{
+ char *altfilename,*basefile,*basedir;
+
+ if (!(basefile = shn_get_base_filename(filename)))
+ {
+ return 0;
+ }
+
+ if (!(basedir = shn_get_base_directory(filename)))
+ {
+ free(basefile);
+ return 0;
+ }
+
+ if (!(altfilename = malloc(strlen(basedir)+strlen(basefile)+sizeof(SEEK_SUFFIX)+3)))
+ {
+ shn_debug("Could not allocate memory for same dir filename");
+ free(basefile);
+ free(basedir);
+ return 0;
+ }
+
+ sprintf(altfilename,"%s/%s.%s",basedir,basefile,SEEK_SUFFIX);
+
+ free(basefile);
+ free(basedir);
+
+ if (load_separate_seek_table_generic(altfilename,this_shn))
+ {
+ free(altfilename);
+ return 1;
+ }
+
+ free(altfilename);
+ return 0;
+}
+
+int load_separate_seek_table_relative(shn_file *this_shn,const char *filename)
+{
+ char *altfilename,*basefile,*basedir;
+
+ if (0 == strcmp(shn_cfg.relative_seek_tables_path,""))
+ return 0;
+
+ if (!(basefile = shn_get_base_filename(filename)))
+ {
+ return 0;
+ }
+
+ if (!(basedir = shn_get_base_directory(filename)))
+ {
+ free(basefile);
+ return 0;
+ }
+
+ if (!(altfilename = malloc(strlen(basedir)+strlen(shn_cfg.relative_seek_tables_path)+strlen(basefile)+sizeof(SEEK_SUFFIX)+4)))
+ {
+ shn_debug("Could not allocate memory for absolute filename");
+ free(basefile);
+ free(basedir);
+ return 0;
+ }
+
+ sprintf(altfilename,"%s/%s/%s.%s",basedir,shn_cfg.relative_seek_tables_path,basefile,SEEK_SUFFIX);
+
+ free(basefile);
+ free(basedir);
+
+ if (load_separate_seek_table_generic(altfilename,this_shn))
+ {
+ free(altfilename);
+ return 1;
+ }
+
+ free(altfilename);
+ return 0;
+}
+
+int load_separate_seek_table_absolute(shn_file *this_shn,const char *filename)
+{
+ char *altfilename,*basefile;
+
+ if (!(basefile = shn_get_base_filename(filename)))
+ {
+ return 0;
+ }
+
+ if (!(altfilename = malloc(strlen(shn_cfg.seek_tables_path)+strlen(basefile)+sizeof(SEEK_SUFFIX)+3)))
+ {
+ shn_debug("Could not allocate memory for same dir filename");
+ free(basefile);
+ return 0;
+ }
+
+ sprintf(altfilename,"%s/%s.%s",shn_cfg.seek_tables_path,basefile,SEEK_SUFFIX);
+
+ free(basefile);
+
+ if (load_separate_seek_table_generic(altfilename,this_shn))
+ {
+ free(altfilename);
+ return 1;
+ }
+
+ free(altfilename);
+ return 0;
+}
+
+void shn_load_seek_table(shn_file *this_shn,const char *filename)
+{
+ if (load_appended_seek_table(this_shn,filename,0))
+ return;
+
+ if (load_appended_seek_table(this_shn,filename,ID3V1_TAG_SIZE))
+ return;
+
+ if (load_separate_seek_table_samedir(this_shn,filename))
+ return;
+
+ if (load_separate_seek_table_relative(this_shn,filename))
+ return;
+
+ if (load_separate_seek_table_absolute(this_shn,filename))
+ return;
+
+ shn_debug("Could not find any seek tables");
+}
diff --git a/plugins/shn/shn.c b/plugins/shn/shn.c
new file mode 100644
index 00000000..17d2e231
--- /dev/null
+++ b/plugins/shn/shn.c
@@ -0,0 +1,1798 @@
+/*
+ DeaDBeeF - ultimate music player for GNU/Linux systems with X11
+ Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+// based on xmms-shn, http://www.etree.org/shnutils/xmms-shn/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "shorten.h"
+#include "../../deadbeef.h"
+#include "bitshift.h"
+
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+static DB_decoder_t plugin;
+DB_functions_t *deadbeef;
+
+shn_file *load_shn(const char *filename);
+static void shn_play(char *);
+static void shn_stop(void);
+static int shn_get_time(void);
+static void shn_get_file_info(char *,char **,int *);
+static void shn_display_file_info(char *);
+
+typedef struct {
+ DB_fileinfo_t info;
+ shn_file *shnfile;
+
+ slong **buffer, **offset;
+ slong lpcqoffset;
+ int version, bitshift;
+ int ftype;
+ char *magic;
+ int blocksize, nchan;
+ int i, chan, nwrap, nskip;
+ int *qlpc, maxnlpc, nmean;
+ int cmd;
+ int internal_ftype;
+ int blk_size;
+ int cklen;
+ uchar tmp;
+
+ int currentsample;
+ int startsample;
+ int endsample;
+} shn_fileinfo_t;
+
+shn_config shn_cfg;
+
+DB_fileinfo_t *
+shn_open (void) {
+ DB_fileinfo_t *_info = malloc (sizeof (shn_fileinfo_t));
+ shn_fileinfo_t *info = (shn_fileinfo_t *)_info;
+ memset (info, 0, sizeof (shn_fileinfo_t));
+ return _info;
+}
+
+int
+ddb_getc (DB_FILE *fp) {
+ uint8_t c;
+ if (deadbeef->fread (&c, 1, 1, fp) != 1) {
+ return EOF;
+ }
+ return (int)c;
+}
+
+int init_decode_state(shn_file *this_shn)
+{
+ if (this_shn->decode_state)
+ {
+ if (this_shn->decode_state->getbuf)
+ {
+ free(this_shn->decode_state->getbuf);
+ this_shn->decode_state->getbuf = NULL;
+ }
+
+ if (this_shn->decode_state->writebuf)
+ {
+ free(this_shn->decode_state->writebuf);
+ this_shn->decode_state->writebuf = NULL;
+ }
+
+ if (this_shn->decode_state->writefub)
+ {
+ free(this_shn->decode_state->writefub);
+ this_shn->decode_state->writefub = NULL;
+ }
+
+ free(this_shn->decode_state);
+ this_shn->decode_state = NULL;
+ }
+
+ if (!(this_shn->decode_state = malloc(sizeof(shn_decode_state))))
+ {
+ shn_debug("Could not allocate memory for decode state data structure");
+ return 0;
+ }
+
+ this_shn->decode_state->getbuf = NULL;
+ this_shn->decode_state->getbufp = NULL;
+ this_shn->decode_state->nbitget = 0;
+ this_shn->decode_state->nbyteget = 0;
+ this_shn->decode_state->gbuffer = 0;
+ this_shn->decode_state->writebuf = NULL;
+ this_shn->decode_state->writefub = NULL;
+ this_shn->decode_state->nwritebuf = 0;
+
+ this_shn->vars.bytes_in_buf = 0;
+
+ return 1;
+}
+
+int
+shn_init_decoder (shn_fileinfo_t *info) {
+ int version = FORMAT_VERSION;
+ info->ftype = TYPE_EOF;
+ info->magic = MAGIC;
+ info->blocksize = DEFAULT_BLOCK_SIZE;
+ info->nchan = DEFAULT_NCHAN;
+ info->nskip = DEFAULT_NSKIP;
+ info->maxnlpc = DEFAULT_MAXNLPC;
+ info->nmean = UNDEFINED_UINT;
+
+ info->shnfile->vars.bytes_in_buf = 0;
+ if (!init_decode_state(info->shnfile)) {
+ trace ("shn: init_decode_state failed\n");
+ return -1;
+ }
+ info->shnfile->vars.going = 1;
+
+ info->blk_size = 512 * (info->shnfile->wave_header.bits_per_sample / 8) * info->shnfile->wave_header.channels;
+
+ /* read magic number */
+#ifdef STRICT_FORMAT_COMPATABILITY
+ if(FORMAT_VERSION < 2)
+ {
+ for(i = 0; i < strlen(magic); i++)
+ if(getc_exit(this_shn->vars.fd) != magic[i]) {
+ shn_error_fatal(this_shn,"Bad magic number");
+ goto exit_thread;
+ }
+
+ /* get version number */
+ version = getc_exit(this_shn->vars.fd);
+ }
+ else
+#endif /* STRICT_FORMAT_COMPATABILITY */
+ {
+ int nscan = 0;
+
+ version = MAX_VERSION + 1;
+ while(version > MAX_VERSION)
+ {
+ int byte = ddb_getc(info->shnfile->vars.fd);
+ if(byte == EOF) {
+ shn_error_fatal(info->shnfile,"No magic number");
+ trace ("shn_init: no magic number\n");
+ return -1;
+ }
+ if(info->magic[nscan] != '\0' && byte == info->magic[nscan]) {
+ nscan++;
+ }
+ else {
+ if(info->magic[nscan] == '\0' && byte <= MAX_VERSION)
+ version = byte;
+ else
+ {
+ if(byte == info->magic[0])
+ nscan = 1;
+ else
+ {
+ nscan = 0;
+ }
+ version = MAX_VERSION + 1;
+ }
+ }
+ }
+ }
+
+ /* check version number */
+ if(version > MAX_SUPPORTED_VERSION) {
+ shn_error_fatal(info->shnfile,"Can't decode version %d", version);
+ trace ("shn_init: can't decode version %d\n", version);
+ return -1;
+ }
+
+ /* set up the default nmean, ignoring the command line state */
+ info->nmean = (version < 2) ? DEFAULT_V0NMEAN : DEFAULT_V2NMEAN;
+
+ /* initialise the variable length file read for the compressed stream */
+ trace ("decode_state=%p\n", info->shnfile->decode_state);
+ var_get_init(info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("var_get_init failed\n");
+ return -1;
+ }
+
+ /* initialise the fixed length file write for the uncompressed stream */
+ fwrite_type_init(info->shnfile);
+
+ /* get the internal file type */
+ info->internal_ftype = UINT_GET(TYPESIZE, info->shnfile);
+ trace ("internal_ftype=%d\n", info->internal_ftype);
+
+ /* has the user requested a change in file type? */
+ if(info->internal_ftype != info->ftype) {
+ if(info->ftype == TYPE_EOF) {
+ info->ftype = info->internal_ftype; /* no problems here */
+ }
+ else { /* check that the requested conversion is valid */
+ if(info->internal_ftype == TYPE_AU1 || info->internal_ftype == TYPE_AU2 ||
+ info->internal_ftype == TYPE_AU3 || info->ftype == TYPE_AU1 || info->ftype == TYPE_AU2 || info->ftype == TYPE_AU3) {
+ shn_error_fatal(info->shnfile,"Not able to perform requested output format conversion");
+ trace ("shn_init_decoder: Not able to perform requested output format conversion\n");
+ return -1;
+ }
+ }
+ trace ("ftype=%d\n", info->ftype);
+ }
+
+ info->nchan = UINT_GET(CHANSIZE, info->shnfile);
+
+ /* get blocksize if version > 0 */
+ if(version > 0)
+ {
+ int byte;
+ info->blocksize = UINT_GET((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2),info->shnfile);
+ info->maxnlpc = UINT_GET(LPCQSIZE, info->shnfile);
+ info->nmean = UINT_GET(0, info->shnfile);
+ info->nskip = UINT_GET(NSKIPSIZE, info->shnfile);
+ for(int i = 0; i < info->nskip; i++)
+ {
+ byte = uvar_get(XBYTESIZE,info->shnfile);
+ }
+ }
+ else
+ info->blocksize = DEFAULT_BLOCK_SIZE;
+
+ info->nwrap = MAX(NWRAP, info->maxnlpc);
+
+ /* grab some space for the input buffer */
+ info->buffer = long2d((ulong) info->nchan, (ulong) (info->blocksize + info->nwrap),info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("failed to alloc buffer\n");
+ return -1;
+ }
+ info->offset = long2d((ulong) info->nchan, (ulong) MAX(1, info->nmean),info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ if (info->buffer) {
+ free(info->buffer);
+ info->buffer = NULL;
+ }
+ trace ("failed to alloc offset\n");
+ return -1;
+ }
+
+ for(info->chan = 0; info->chan < info->nchan; info->chan++)
+ {
+ for(int i = 0; i < info->nwrap; i++)
+ info->buffer[info->chan][i] = 0;
+ info->buffer[info->chan] += info->nwrap;
+ }
+
+ if(info->maxnlpc > 0) {
+ info->qlpc = (int*) pmalloc((ulong) (info->maxnlpc * sizeof(*info->qlpc)),info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ if (info->buffer) {
+ free(info->buffer);
+ info->buffer = NULL;
+ }
+ if (info->offset) {
+ free(info->offset);
+ info->offset = NULL;
+ }
+ trace ("failed to alloc qlpc\n");
+ return -1;
+ }
+ }
+
+ if(version > 1)
+ info->lpcqoffset = V2LPCQOFFSET;
+
+ init_offset (info->offset, info->nchan, MAX(1, info->nmean), info->internal_ftype);
+
+ /* get commands from file and execute them */
+ info->chan = 0;
+ info->version = version;
+ trace ("shn_init: success\n");
+ return 0;
+}
+
+int
+shn_init(DB_fileinfo_t *_info, DB_playItem_t *it) {
+ shn_fileinfo_t *info = (shn_fileinfo_t *)_info;
+
+ shn_cfg.error_output_method = ERROR_OUTPUT_DEVNULL;
+ shn_cfg.error_output_method_config_name = "error_output_method";
+ shn_cfg.seek_tables_path = NULL;
+ shn_cfg.seek_tables_path_config_name = "seek_tables_path";
+ shn_cfg.relative_seek_tables_path = NULL;
+ shn_cfg.relative_seek_tables_path_config_name = "relative_seek_tables_path";
+ shn_cfg.verbose = 0;
+ shn_cfg.verbose_config_name = "verbose";
+ shn_cfg.swap_bytes = 0;
+ shn_cfg.swap_bytes_config_name = "swap_bytes";
+ shn_cfg.load_textfiles = 0;
+ shn_cfg.load_textfiles_config_name = "load_textfiles";
+ shn_cfg.textfile_extensions = NULL;
+ shn_cfg.textfile_extensions_config_name = "textfile_extensions";
+
+// {{{ xmms config reader
+#if 0
+ filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL);
+ if ((cfg = xmms_cfg_open_file(filename)) != 0)
+ {
+ xmms_cfg_read_int(cfg, XMMS_SHN_VERSION_TAG, shn_cfg.error_output_method_config_name, &shn_cfg.error_output_method);
+ xmms_cfg_read_boolean(cfg, XMMS_SHN_VERSION_TAG, shn_cfg.verbose_config_name, &shn_cfg.verbose);
+ if (!xmms_cfg_read_string(cfg, XMMS_SHN_VERSION_TAG, shn_cfg.seek_tables_path_config_name, &shn_cfg.seek_tables_path))
+ shn_cfg.seek_tables_path = g_strdup(g_get_home_dir());
+ if (!xmms_cfg_read_string(cfg, XMMS_SHN_VERSION_TAG, shn_cfg.relative_seek_tables_path_config_name, &shn_cfg.relative_seek_tables_path))
+ shn_cfg.relative_seek_tables_path = g_strdup("");
+ xmms_cfg_read_boolean(cfg, XMMS_SHN_VERSION_TAG, shn_cfg.swap_bytes_config_name, &shn_cfg.swap_bytes);
+ xmms_cfg_read_boolean(cfg, XMMS_SHN_VERSION_TAG, shn_cfg.load_textfiles_config_name, &shn_cfg.load_textfiles);
+ if (!xmms_cfg_read_string(cfg, XMMS_SHN_VERSION_TAG, shn_cfg.textfile_extensions_config_name, &shn_cfg.textfile_extensions))
+ shn_cfg.textfile_extensions = g_strdup("txt,nfo");
+ xmms_cfg_free(cfg);
+ }
+
+ g_free(filename);
+#endif
+// }}}
+
+ char data[4];
+ DB_FILE *f;
+
+ f = deadbeef->fopen (it->fname);
+ if (!f) {
+ trace ("shn: failed to open %s\n", it->fname);
+ return -1;
+ }
+
+ int id3v2_tag_size = deadbeef->junk_get_leading_size (f);
+ if (id3v2_tag_size > 0) {
+ deadbeef->fseek (f, id3v2_tag_size, SEEK_SET);
+ }
+
+ if (deadbeef->fread((void *)data,1,4,f) != 4)
+ {
+ deadbeef->fclose(f);
+ trace ("shn: failed to read magic from %s\n", it->fname);
+ return -1;
+ }
+ deadbeef->fclose(f);
+
+ if (memcmp(data,MAGIC,4)) {
+ trace ("shn: invalid MAGIC\n");
+ return -1;
+ }
+
+ if (!(info->shnfile = load_shn(it->fname))) {
+ trace ("shn: load_shn failed\n");
+ return -1;
+ }
+
+ _info->bps = info->shnfile->wave_header.bits_per_sample;
+ _info->channels = info->shnfile->wave_header.channels;
+ _info->samplerate = info->shnfile->wave_header.samples_per_sec;
+ _info->plugin = &plugin;
+
+ int totalsamples = info->shnfile->wave_header.length * info->shnfile->wave_header.samples_per_sec;
+ trace ("totalsamples: %d\n", totalsamples);
+
+ if (it->endsample > 0) {
+ info->startsample = it->startsample;
+ info->endsample = it->endsample;
+ plugin.seek_sample (_info, 0);
+ }
+ else {
+ info->startsample = 0;
+ info->endsample = totalsamples-1;
+ }
+
+ if (info->shnfile->wave_header.file_has_id3v2_tag) {
+ deadbeef->fseek (info->shnfile->vars.fd, info->shnfile->wave_header.file_has_id3v2_tag, SEEK_SET);
+ }
+ else {
+ deadbeef->rewind (info->shnfile->vars.fd);
+ }
+
+ if (shn_init_decoder (info) < 0) {
+ trace ("shn_init_decoder failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+shn_free (DB_fileinfo_t *_info) {
+ shn_fileinfo_t *info = (shn_fileinfo_t *)_info;
+ if (info->shnfile) {
+ if (info->shnfile->decode_state) {
+ if(info->shnfile->decode_state->writebuf != NULL) {
+ free(info->shnfile->decode_state->writebuf);
+ info->shnfile->decode_state->writebuf = NULL;
+ }
+ if(info->shnfile->decode_state->writefub != NULL) {
+ free(info->shnfile->decode_state->writefub);
+ info->shnfile->decode_state->writefub = NULL;
+ }
+ }
+ if (info->shnfile) {
+ shn_unload(info->shnfile);
+ info->shnfile = NULL;
+ }
+ }
+ if (info->buffer) {
+ free(info->buffer);
+ info->buffer = NULL;
+ }
+ if (info->offset) {
+ free(info->offset);
+ info->offset = NULL;
+ }
+ if(info->maxnlpc > 0 && info->qlpc) {
+ free(info->qlpc);
+ info->qlpc = NULL;
+ }
+ free (info);
+}
+
+void
+swap_bytes(shn_file *this_shn,int bytes)
+{
+ int i;
+ uchar tmp;
+
+ for (i=0;i<bytes;i=i+2) {
+ tmp = this_shn->vars.buffer[i+1];
+ this_shn->vars.buffer[i+1] = this_shn->vars.buffer[i];
+ this_shn->vars.buffer[i] = tmp;
+ }
+}
+
+int
+shn_decode (shn_fileinfo_t *info) {
+ int i;
+ int version = info->version;
+ while(1)
+ {
+ info->cmd = uvar_get(FNSIZE,info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: uvar_get error\n");
+ return -1;
+ }
+
+ switch(info->cmd)
+ {
+ case FN_ZERO:
+ case FN_DIFF0:
+ case FN_DIFF1:
+ case FN_DIFF2:
+ case FN_DIFF3:
+ case FN_QLPC:
+ {
+ slong coffset, *cbuffer = info->buffer[info->chan];
+ int resn = 0, nlpc, j;
+
+ if(info->cmd != FN_ZERO)
+ {
+ resn = uvar_get(ENERGYSIZE,info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 1\n");
+ return -1;
+ }
+ /* this is a hack as version 0 differed in definition of var_get */
+ if(info->version == 0)
+ resn--;
+ }
+
+ /* find mean offset : N.B. this code duplicated */
+ if(info->nmean == 0)
+ coffset = info->offset[info->chan][0];
+ else
+ {
+ slong sum = (info->version < 2) ? 0 : info->nmean / 2;
+ for(i = 0; i < info->nmean; i++)
+ sum += info->offset[info->chan][i];
+ if(info->version < 2)
+ coffset = sum / info->nmean;
+ else
+ coffset = ROUNDEDSHIFTDOWN(sum / info->nmean, info->bitshift);
+ }
+
+ switch(info->cmd)
+ {
+ case FN_ZERO:
+ for(i = 0; i < info->blocksize; i++)
+ cbuffer[i] = 0;
+ break;
+ case FN_DIFF0:
+ for(i = 0; i < info->blocksize; i++) {
+ cbuffer[i] = var_get(resn,info->shnfile) + coffset;
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 2\n");
+ return -1;
+ }
+ }
+ break;
+ case FN_DIFF1:
+ for(i = 0; i < info->blocksize; i++) {
+ cbuffer[i] = var_get(resn,info->shnfile) + cbuffer[i - 1];
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 3\n");
+ return -1;
+ }
+ }
+ break;
+ case FN_DIFF2:
+ for(i = 0; i < info->blocksize; i++) {
+ cbuffer[i] = var_get(resn,info->shnfile) + (2 * cbuffer[i - 1] - cbuffer[i - 2]);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 4\n");
+ return -1;
+ }
+ }
+ break;
+ case FN_DIFF3:
+ for(i = 0; i < info->blocksize; i++) {
+ cbuffer[i] = var_get(resn,info->shnfile) + 3 * (cbuffer[i - 1] - cbuffer[i - 2]) + cbuffer[i - 3];
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 5\n");
+ return -1;
+ }
+ }
+ break;
+ case FN_QLPC:
+ nlpc = uvar_get(LPCQSIZE,info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 6\n");
+ return -1;
+ }
+
+ for(i = 0; i < nlpc; i++) {
+ info->qlpc[i] = var_get(LPCQUANT,info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 7\n");
+ return -1;
+ }
+ }
+ for(i = 0; i < nlpc; i++)
+ cbuffer[i - nlpc] -= coffset;
+ for(i = 0; i < info->blocksize; i++)
+ {
+ slong sum = info->lpcqoffset;
+
+ for(j = 0; j < nlpc; j++)
+ sum += info->qlpc[j] * cbuffer[i - j - 1];
+ cbuffer[i] = var_get(resn,info->shnfile) + (sum >> LPCQUANT);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 8\n");
+ return -1;
+ }
+ }
+ if(coffset != 0)
+ for(i = 0; i < info->blocksize; i++)
+ cbuffer[i] += coffset;
+ break;
+ }
+
+ /* store mean value if appropriate : N.B. Duplicated code */
+ if(info->nmean > 0)
+ {
+ slong sum = (info->version < 2) ? 0 : info->blocksize / 2;
+
+ for(i = 0; i < info->blocksize; i++)
+ sum += cbuffer[i];
+
+ for(i = 1; i < info->nmean; i++)
+ info->offset[info->chan][i - 1] = info->offset[info->chan][i];
+ if(info->version < 2)
+ info->offset[info->chan][info->nmean - 1] = sum / info->blocksize;
+ else
+ info->offset[info->chan][info->nmean - 1] = (sum / info->blocksize) << info->bitshift;
+ }
+
+ /* do the wrap */
+ for(i = -info->nwrap; i < 0; i++)
+ cbuffer[i] = cbuffer[i + info->blocksize];
+
+ fix_bitshift(cbuffer, info->blocksize, info->bitshift, info->internal_ftype);
+
+ if(info->chan == info->nchan - 1)
+ {
+ if (!info->shnfile->vars.going || info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 9\n");
+ return -1;
+ }
+
+ fwrite_type(info->buffer, info->ftype, info->nchan, info->blocksize, info->shnfile);
+ info->chan = (info->chan + 1) % info->nchan;
+ // now we have buffer of size info->shnfile->vars.bytes_in_buf
+ if (shn_cfg.swap_bytes) {
+ swap_bytes(info->shnfile, info->shnfile->vars.bytes_in_buf);
+ }
+ return info->shnfile->vars.bytes_in_buf;
+
+
+// !!!!!!!!!!!!!!!!!!FIXME
+ // write_and_wait(info->shnfile,blk_size);
+
+#if 0 // seeking
+ if (info->shnfile->vars.seek_to != -1)
+ {
+ shn_seek_entry *seek_info;
+ int j;
+
+ shn_debug("Seeking to %d:%02d",info->shnfile->vars.seek_to/60,info->shnfile->vars.seek_to%60);
+
+ seek_info = shn_seek_entry_search(info->shnfile->seek_table,info->shnfile->vars.seek_to * (ulong)info->shnfile->wave_header.samples_per_sec,0,
+ (ulong)(info->shnfile->vars.seek_table_entries - 1),info->shnfile->vars.seek_resolution);
+
+ /* loop through number of channels in this file */
+ for (i=0;i<info->nchan;i++) {
+ /* load the three sample buffer values for this channel */
+ for (j=0;j<3;j++)
+ info->buffer[i][j-3] = shn_uchar_to_slong_le(seek_info->data+32+12*i-4*j);
+
+ /* load the variable number of offset history values for this channel */
+ for (j=0;j<MAX(1,info->nmean);j++)
+ info->offset[i][j] = shn_uchar_to_slong_le(seek_info->data+48+16*i+4*j);
+ }
+
+ info->bitshift = shn_uchar_to_ushort_le(seek_info->data+22);
+
+ info->seekto_offset = shn_uchar_to_ulong_le(seek_info->data+8) + info->shnfile->vars.seek_offset;
+
+ deadbeef->fseek(info->shnfile->vars.fd,(slong)seekto_offset,SEEK_SET);
+ deadbeef->fread((uchar*) info->shnfile->decode_state->getbuf, 1, BUFSIZ, info->shnfile->vars.fd);
+
+ info->shnfile->decode_state->getbufp = info->shnfile->decode_state->getbuf + shn_uchar_to_ushort_le(seek_info->data+14);
+ info->shnfile->decode_state->nbitget = shn_uchar_to_ushort_le(seek_info->data+16);
+ info->shnfile->decode_state->nbyteget = shn_uchar_to_ushort_le(seek_info->data+12);
+ info->shnfile->decode_state->gbuffer = shn_uchar_to_ulong_le(seek_info->data+18);
+
+ info->shnfile->vars.bytes_in_buf = 0;
+
+ shn_ip.output->flush(info->shnfile->vars.seek_to * 1000);
+ info->shnfile->vars.seek_to = -1;
+ }
+#endif
+
+ }
+ info->chan = (info->chan + 1) % info->nchan;
+ break;
+ }
+
+ break;
+
+ case FN_QUIT:
+ /* empty out last of buffer */
+ info->shnfile->vars.eof = 1;
+ if (shn_cfg.swap_bytes) {
+ swap_bytes(info->shnfile, info->shnfile->vars.bytes_in_buf);
+ }
+ return info->shnfile->vars.bytes_in_buf;
+#if 0
+ write_and_wait(info->shnfile,info->shnfile->vars.bytes_in_buf);
+ info->shnfile->vars.eof = 1;
+
+ while (1)
+ {
+ if (!info->shnfile->vars.going)
+ goto finish;
+ if (info->shnfile->vars.seek_to != -1)
+ {
+ var_get_quit(info->shnfile);
+ fwrite_type_quit(info->shnfile);
+
+ if (buffer) free((void *) buffer);
+ if (offset) free((void *) offset);
+ if(maxnlpc > 0 && qlpc)
+ free((void *) qlpc);
+
+ fseek(info->shnfile->vars.fd,0,SEEK_SET);
+ goto restart;
+ }
+ else
+ xmms_usleep(10000);
+ }
+
+ goto cleanup;
+#endif
+ break;
+
+ case FN_BLOCKSIZE:
+ info->blocksize = UINT_GET((int) (log((double) info->blocksize) / M_LN2), info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 10\n");
+ return -1;
+ }
+ break;
+ case FN_BITSHIFT:
+ info->bitshift = uvar_get(BITSHIFTSIZE,info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 11\n");
+ return -1;
+ }
+ break;
+ case FN_VERBATIM:
+ info->cklen = uvar_get(VERBATIM_CKSIZE_SIZE,info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 12\n");
+ return -1;
+ }
+
+ while (info->cklen--) {
+ info->tmp = (uchar)uvar_get(VERBATIM_BYTE_SIZE,info->shnfile);
+ if (info->shnfile->vars.fatal_error) {
+ trace ("shn_decode: error 13\n");
+ return -1;
+ }
+ }
+
+ break;
+
+ default:
+ shn_error_fatal(info->shnfile,"Sanity check fails trying to decode function: %d",info->cmd);
+ trace ("shn_decode: error 14\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+shn_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) {
+ shn_fileinfo_t *info = (shn_fileinfo_t *)_info;
+ if (info->currentsample + size / (2 * _info->channels) > info->endsample) {
+ size = (info->endsample - info->currentsample + 1) * 2 * _info->channels;
+ if (size <= 0) {
+ return 0;
+ }
+ }
+ int initsize = size;
+ int ch = min (_info->channels, 2);
+ int sample_size = ch * (_info->bps >> 3);
+
+ while (size > 0) {
+ if (info->shnfile->vars.bytes_in_buf > 0) {
+ int n = size / sample_size;
+ int nsamples = info->shnfile->vars.bytes_in_buf / (_info->channels * 2);
+ n = min (nsamples, n);
+ char *src = info->shnfile->vars.buffer;
+ for (int i = 0; i < n; i++) {
+ memcpy (bytes, src, sample_size);
+ bytes += sample_size;
+ src += _info->channels * 2;
+ }
+
+ size -= n * sample_size;
+ if (n == info->shnfile->vars.bytes_in_buf / (_info->channels * 2)) {
+ info->shnfile->vars.bytes_in_buf = 0;
+ }
+ else {
+ memmove (info->shnfile->vars.buffer, src, info->shnfile->vars.bytes_in_buf - n * (_info->channels * 2));
+ info->shnfile->vars.bytes_in_buf -= n * (_info->channels * 2);
+ }
+ continue;
+ }
+ if (shn_decode (info) <= 0) {
+ trace ("shn_decode returned error\n");
+ break;
+ }
+ }
+
+ info->currentsample += (initsize-size) / sample_size;
+ if (size != 0) {
+ trace ("shn_read_int16 eof\n");
+ }
+ return initsize-size;
+}
+
+int
+shn_seek_sample (DB_fileinfo_t *_info, int sample) {
+ shn_fileinfo_t *info = (shn_fileinfo_t *)_info;
+
+ sample += info->startsample;
+
+ info->shnfile->vars.seek_to = sample / _info->samplerate;
+
+ ulong seekto_offset;
+ int i, j;
+ shn_seek_entry *seek_info;
+
+ trace ("Seeking to %d:%02d\n",info->shnfile->vars.seek_to/60,info->shnfile->vars.seek_to%60);
+
+ seek_info = shn_seek_entry_search(info->shnfile->seek_table,info->shnfile->vars.seek_to * (ulong)info->shnfile->wave_header.samples_per_sec,0,
+ (ulong)(info->shnfile->vars.seek_table_entries - 1),info->shnfile->vars.seek_resolution);
+
+ /* loop through number of channels in this file */
+ for (i=0;i<info->nchan;i++) {
+ /* load the three sample buffer values for this channel */
+ for (j=0;j<3;j++)
+ info->buffer[i][j-3] = shn_uchar_to_slong_le(seek_info->data+32+12*i-4*j);
+
+ /* load the variable number of offset history values for this channel */
+ for (j=0;j<MAX(1,info->nmean);j++)
+ info->offset[i][j] = shn_uchar_to_slong_le(seek_info->data+48+16*i+4*j);
+ }
+
+ info->bitshift = shn_uchar_to_ushort_le(seek_info->data+22);
+
+ seekto_offset = shn_uchar_to_ulong_le(seek_info->data+8) + info->shnfile->vars.seek_offset;
+
+ deadbeef->fseek(info->shnfile->vars.fd,(slong)seekto_offset,SEEK_SET);
+ deadbeef->fread((uchar*) info->shnfile->decode_state->getbuf, 1, BUFSIZ, info->shnfile->vars.fd);
+
+ info->shnfile->decode_state->getbufp = info->shnfile->decode_state->getbuf + shn_uchar_to_ushort_le(seek_info->data+14);
+ info->shnfile->decode_state->nbitget = shn_uchar_to_ushort_le(seek_info->data+16);
+ info->shnfile->decode_state->nbyteget = shn_uchar_to_ushort_le(seek_info->data+12);
+ info->shnfile->decode_state->gbuffer = shn_uchar_to_ulong_le(seek_info->data+18);
+
+ info->shnfile->vars.bytes_in_buf = 0;
+
+ info->currentsample = info->shnfile->vars.seek_to * _info->samplerate;
+ _info->readpos = info->shnfile->vars.seek_to;
+ return 0;
+}
+
+int
+shn_seek (DB_fileinfo_t *_info, float time) {
+ return shn_seek_sample (_info, time * _info->samplerate);
+ return 0;
+}
+
+DB_playItem_t *
+shn_insert (DB_playItem_t *after, const char *fname) {
+ shn_file *tmp_file;
+ DB_FILE *f;
+ char data[4];
+
+ f = deadbeef->fopen (fname);
+ if (!f) {
+ return NULL;
+ }
+
+ int id3v2_tag_size = deadbeef->junk_get_leading_size (f);
+ if (id3v2_tag_size > 0) {
+ deadbeef->fseek (f, id3v2_tag_size, SEEK_SET);
+ }
+
+ if (deadbeef->fread((void *)data,1,4,f) != 4)
+ {
+ deadbeef->fclose(f);
+ return NULL;
+ }
+ deadbeef->fclose(f);
+
+ if (memcmp(data,MAGIC,4)) {
+ trace ("shn: invalid MAGIC\n");
+ return NULL;
+ }
+
+ if (!(tmp_file = load_shn(fname))) {
+ trace ("shn: load_shn failed\n");
+ return NULL;
+ }
+
+ DB_playItem_t *it = deadbeef->pl_item_alloc ();
+ it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id);
+ it->fname = strdup (fname);
+ it->filetype = "Shorten";
+ deadbeef->pl_set_item_duration (it, tmp_file->wave_header.length);
+
+ int apeerr = deadbeef->junk_apev2_read (it, tmp_file->vars.fd);
+ int v2err = deadbeef->junk_id3v2_read (it, tmp_file->vars.fd);
+ int v1err = deadbeef->junk_id3v1_read (it, tmp_file->vars.fd);
+
+ shn_unload(tmp_file);
+
+ deadbeef->pl_add_meta (it, "title", NULL);
+
+ after = deadbeef->pl_insert_item (after, it);
+ deadbeef->pl_item_unref (it);
+ return after;
+}
+
+#define CAPMAXSCHAR(x) ((x > 127) ? 127 : x)
+#define CAPMAXUCHAR(x) ((x > 255) ? 255 : x)
+#define CAPMAXSHORT(x) ((x > 32767) ? 32767 : x)
+#define CAPMAXUSHORT(x) ((x > 65535) ? 65535 : x)
+
+static int sizeof_sample[TYPE_EOF];
+
+void init_sizeof_sample() {
+ sizeof_sample[TYPE_AU1] = sizeof(uchar);
+ sizeof_sample[TYPE_S8] = sizeof(schar);
+ sizeof_sample[TYPE_U8] = sizeof(uchar);
+ sizeof_sample[TYPE_S16HL] = sizeof(ushort);
+ sizeof_sample[TYPE_U16HL] = sizeof(ushort);
+ sizeof_sample[TYPE_S16LH] = sizeof(ushort);
+ sizeof_sample[TYPE_U16LH] = sizeof(ushort);
+ sizeof_sample[TYPE_ULAW] = sizeof(uchar);
+ sizeof_sample[TYPE_AU2] = sizeof(uchar);
+ sizeof_sample[TYPE_AU3] = sizeof(uchar);
+ sizeof_sample[TYPE_ALAW] = sizeof(uchar);
+}
+
+
+/***************/
+/* fixed write */
+/***************/
+
+void fwrite_type_init(shn_file *this_shn) {
+ init_sizeof_sample();
+ this_shn->decode_state->writebuf = (schar*) NULL;
+ this_shn->decode_state->writefub = (schar*) NULL;
+ this_shn->decode_state->nwritebuf = 0;
+}
+
+void fwrite_type_quit(shn_file *this_shn) {
+ if(this_shn->decode_state->writebuf != NULL) {
+ free(this_shn->decode_state->writebuf);
+ this_shn->decode_state->writebuf = NULL;
+ }
+ if(this_shn->decode_state->writefub != NULL) {
+ free(this_shn->decode_state->writefub);
+ this_shn->decode_state->writefub = NULL;
+ }
+}
+
+/* convert from signed ints to a given type and write */
+void fwrite_type(slong **data,int ftype,int nchan,int nitem,shn_file *this_shn)
+{
+ int hiloint = 1, hilo = !(*((char*) &hiloint));
+ int i, nwrite = 0, datasize = sizeof_sample[ftype], chan;
+ slong *data0 = data[0];
+ int bufAvailable = OUT_BUFFER_SIZE - this_shn->vars.bytes_in_buf;
+
+ if(this_shn->decode_state->nwritebuf < nchan * nitem * datasize) {
+ this_shn->decode_state->nwritebuf = nchan * nitem * datasize;
+ if(this_shn->decode_state->writebuf != NULL) free(this_shn->decode_state->writebuf);
+ if(this_shn->decode_state->writefub != NULL) free(this_shn->decode_state->writefub);
+ this_shn->decode_state->writebuf = (schar*) pmalloc((ulong) this_shn->decode_state->nwritebuf,this_shn);
+ if (!this_shn->decode_state->writebuf)
+ return;
+ this_shn->decode_state->writefub = (schar*) pmalloc((ulong) this_shn->decode_state->nwritebuf,this_shn);
+ if (!this_shn->decode_state->writefub)
+ return;
+ }
+
+ switch(ftype) {
+ case TYPE_AU1: /* leave the conversion to fix_bitshift() */
+ case TYPE_AU2: {
+ uchar *writebufp = (uchar*) this_shn->decode_state->writebuf;
+ if(nchan == 1)
+ for(i = 0; i < nitem; i++)
+ *writebufp++ = data0[i];
+ else
+ for(i = 0; i < nitem; i++)
+ for(chan = 0; chan < nchan; chan++)
+ *writebufp++ = data[chan][i];
+ break;
+ }
+ case TYPE_U8: {
+ uchar *writebufp = (uchar*) this_shn->decode_state->writebuf;
+ if(nchan == 1)
+ for(i = 0; i < nitem; i++)
+ *writebufp++ = CAPMAXUCHAR(data0[i]);
+ else
+ for(i = 0; i < nitem; i++)
+ for(chan = 0; chan < nchan; chan++)
+ *writebufp++ = CAPMAXUCHAR(data[chan][i]);
+ break;
+ }
+ case TYPE_S8: {
+ schar *writebufp = (schar*) this_shn->decode_state->writebuf;
+ if(nchan == 1)
+ for(i = 0; i < nitem; i++)
+ *writebufp++ = CAPMAXSCHAR(data0[i]);
+ else
+ for(i = 0; i < nitem; i++)
+ for(chan = 0; chan < nchan; chan++)
+ *writebufp++ = CAPMAXSCHAR(data[chan][i]);
+ break;
+ }
+ case TYPE_S16HL:
+ case TYPE_S16LH: {
+ short *writebufp = (short*) this_shn->decode_state->writebuf;
+ if(nchan == 1)
+ for(i = 0; i < nitem; i++)
+ *writebufp++ = CAPMAXSHORT(data0[i]);
+ else
+ for(i = 0; i < nitem; i++)
+ for(chan = 0; chan < nchan; chan++)
+ *writebufp++ = CAPMAXSHORT(data[chan][i]);
+ break;
+ }
+ case TYPE_U16HL:
+ case TYPE_U16LH: {
+ ushort *writebufp = (ushort*) this_shn->decode_state->writebuf;
+ if(nchan == 1)
+ for(i = 0; i < nitem; i++)
+ *writebufp++ = CAPMAXUSHORT(data0[i]);
+ else
+ for(i = 0; i < nitem; i++)
+ for(chan = 0; chan < nchan; chan++)
+ *writebufp++ = CAPMAXUSHORT(data[chan][i]);
+ break;
+ }
+ case TYPE_ULAW: {
+ uchar *writebufp = (uchar*) this_shn->decode_state->writebuf;
+ if(nchan == 1)
+ for(i = 0; i < nitem; i++)
+ *writebufp++ = Slinear2ulaw(CAPMAXSHORT((data0[i] << 3)));
+ else
+ for(i = 0; i < nitem; i++)
+ for(chan = 0; chan < nchan; chan++)
+ *writebufp++ = Slinear2ulaw(CAPMAXSHORT((data[chan][i] << 3)));
+ break;
+ }
+ case TYPE_AU3: {
+ uchar *writebufp = (uchar*) this_shn->decode_state->writebuf;
+ if(nchan == 1)
+ for(i = 0; i < nitem; i++)
+ if(data0[i] < 0)
+ *writebufp++ = (127 - data0[i]) ^ 0xd5;
+ else
+ *writebufp++ = (data0[i] + 128) ^ 0x55;
+ else
+ for(i = 0; i < nitem; i++)
+ for(chan = 0; chan < nchan; chan++)
+ if(data[chan][i] < 0)
+ *writebufp++ = (127 - data[chan][i]) ^ 0xd5;
+ else
+ *writebufp++ = (data[chan][i] + 128) ^ 0x55;
+ break;
+ }
+ case TYPE_ALAW: {
+ uchar *writebufp = (uchar*) this_shn->decode_state->writebuf;
+ if(nchan == 1)
+ for(i = 0; i < nitem; i++)
+ *writebufp++ = Slinear2alaw(CAPMAXSHORT((data0[i] << 3)));
+ else
+ for(i = 0; i < nitem; i++)
+ for(chan = 0; chan < nchan; chan++)
+ *writebufp++ = Slinear2alaw(CAPMAXSHORT((data[chan][i] << 3)));
+ break;
+ }
+ }
+
+ switch(ftype) {
+ case TYPE_AU1:
+ case TYPE_S8:
+ case TYPE_U8:
+ case TYPE_ULAW:
+ case TYPE_AU2:
+ case TYPE_AU3:
+ case TYPE_ALAW:
+ if (datasize*nchan*nitem <= bufAvailable) {
+ memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writebuf,datasize*nchan*nitem);
+ this_shn->vars.bytes_in_buf += datasize*nchan*nitem;
+ nwrite = nitem;
+ }
+ else
+ shn_debug("Buffer overrun in fwrite_type() [case 1]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable);
+ break;
+ case TYPE_S16HL:
+ case TYPE_U16HL:
+ if(hilo)
+ {
+ if (datasize*nchan*nitem <= bufAvailable) {
+ memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writebuf,datasize*nchan*nitem);
+ this_shn->vars.bytes_in_buf += datasize*nchan*nitem;
+ nwrite = nitem;
+ }
+ else
+ shn_debug("Buffer overrun in fwrite_type() [case 2]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable);
+ }
+ else
+ {
+ swab(this_shn->decode_state->writebuf, this_shn->decode_state->writefub, datasize * nchan * nitem);
+ if (datasize*nchan*nitem <= bufAvailable) {
+ memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writefub,datasize*nchan*nitem);
+ this_shn->vars.bytes_in_buf += datasize*nchan*nitem;
+ nwrite = nitem;
+ }
+ else
+ shn_debug("Buffer overrun in fwrite_type() [case 3]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable);
+ }
+ break;
+ case TYPE_S16LH:
+ case TYPE_U16LH:
+ if(hilo)
+ {
+ swab(this_shn->decode_state->writebuf, this_shn->decode_state->writefub, datasize * nchan * nitem);
+ if (datasize*nchan*nitem <= bufAvailable) {
+ memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writefub,datasize*nchan*nitem);
+ this_shn->vars.bytes_in_buf += datasize*nchan*nitem;
+ nwrite = nitem;
+ }
+ else
+ shn_debug("Buffer overrun in fwrite_type() [case 4]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable);
+ }
+ else
+ {
+ if (datasize*nchan*nitem <= bufAvailable) {
+ memcpy((void *)&this_shn->vars.buffer[this_shn->vars.bytes_in_buf],(const void *)this_shn->decode_state->writebuf,datasize*nchan*nitem);
+ this_shn->vars.bytes_in_buf += datasize*nchan*nitem;
+ nwrite = nitem;
+ }
+ else
+ shn_debug("Buffer overrun in fwrite_type() [case 5]: %d bytes to read, but only %d bytes are available",datasize*nchan*nitem,bufAvailable);
+ }
+ break;
+ }
+
+ if(nwrite != nitem)
+ shn_error_fatal(this_shn,"Failed to write decompressed stream -\npossible corrupt or truncated file");
+}
+
+/*************/
+/* bitshifts */
+/*************/
+
+void fix_bitshift(buffer, nitem, bitshift, ftype) slong *buffer; int nitem,
+ bitshift, ftype; {
+ int i;
+
+ if(ftype == TYPE_AU1)
+ for(i = 0; i < nitem; i++)
+ buffer[i] = ulaw_outward[bitshift][buffer[i] + 128];
+ else if(ftype == TYPE_AU2)
+ for(i = 0; i < nitem; i++) {
+ if(buffer[i] >= 0)
+ buffer[i] = ulaw_outward[bitshift][buffer[i] + 128];
+ else if(buffer[i] == -1)
+ buffer[i] = NEGATIVE_ULAW_ZERO;
+ else
+ buffer[i] = ulaw_outward[bitshift][buffer[i] + 129];
+ }
+ else
+ if(bitshift != 0)
+ for(i = 0; i < nitem; i++)
+ buffer[i] <<= bitshift;
+}
+
+int get_wave_header(shn_file *this_shn)
+{
+ slong **buffer = NULL, **offset = NULL;
+ slong lpcqoffset = 0;
+ int version = FORMAT_VERSION, bitshift = 0;
+ int ftype = TYPE_EOF;
+ char *magic = MAGIC;
+ int blocksize = DEFAULT_BLOCK_SIZE, nchan = DEFAULT_NCHAN;
+ int i, chan, nwrap, nskip = DEFAULT_NSKIP;
+ int *qlpc = NULL, maxnlpc = DEFAULT_MAXNLPC, nmean = UNDEFINED_UINT;
+ int cmd;
+ int internal_ftype;
+ int cklen;
+ int retval = 0;
+
+ if (!init_decode_state(this_shn))
+ return 0;
+
+ /***********************/
+ /* EXTRACT starts here */
+ /***********************/
+
+ /* read magic number */
+#ifdef STRICT_FORMAT_COMPATABILITY
+ if(FORMAT_VERSION < 2)
+ {
+ for(i = 0; i < strlen(magic); i++) {
+ if(getc_exit(this_shn->vars.fd) != magic[i])
+ return 0;
+ this_shn->vars.bytes_read++;
+ }
+
+ /* get version number */
+ version = getc_exit(this_shn->vars.fd);
+ this_shn->vars.bytes_read++;
+ }
+ else
+#endif /* STRICT_FORMAT_COMPATABILITY */
+ {
+ int nscan = 0;
+
+ version = MAX_VERSION + 1;
+ while(version > MAX_VERSION)
+ {
+ int byte = ddb_getc(this_shn->vars.fd);
+ this_shn->vars.bytes_read++;
+ if(byte == EOF)
+ return 0;
+ if(magic[nscan] != '\0' && byte == magic[nscan])
+ nscan++;
+ else
+ if(magic[nscan] == '\0' && byte <= MAX_VERSION)
+ version = byte;
+ else
+ {
+ if(byte == magic[0])
+ nscan = 1;
+ else
+ {
+ nscan = 0;
+ }
+ version = MAX_VERSION + 1;
+ }
+ }
+ }
+
+ /* check version number */
+ if(version > MAX_SUPPORTED_VERSION)
+ return 0;
+
+ /* set up the default nmean, ignoring the command line state */
+ nmean = (version < 2) ? DEFAULT_V0NMEAN : DEFAULT_V2NMEAN;
+
+ /* initialise the variable length file read for the compressed stream */
+ var_get_init(this_shn);
+ if (this_shn->vars.fatal_error)
+ return 0;
+
+ /* initialise the fixed length file write for the uncompressed stream */
+ fwrite_type_init(this_shn);
+
+ /* get the internal file type */
+ internal_ftype = UINT_GET(TYPESIZE, this_shn);
+
+ /* has the user requested a change in file type? */
+ if(internal_ftype != ftype) {
+ if(ftype == TYPE_EOF) {
+ ftype = internal_ftype; /* no problems here */
+ }
+ else { /* check that the requested conversion is valid */
+ if(internal_ftype == TYPE_AU1 || internal_ftype == TYPE_AU2 ||
+ internal_ftype == TYPE_AU3 || ftype == TYPE_AU1 ||ftype == TYPE_AU2 || ftype == TYPE_AU3)
+ {
+ retval = 0;
+ goto got_enough_data;
+ }
+ }
+ }
+
+ nchan = UINT_GET(CHANSIZE, this_shn);
+ this_shn->vars.actual_nchan = nchan;
+
+ /* get blocksize if version > 0 */
+ if(version > 0)
+ {
+ int byte;
+ blocksize = UINT_GET((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2),this_shn);
+ maxnlpc = UINT_GET(LPCQSIZE, this_shn);
+ this_shn->vars.actual_maxnlpc = maxnlpc;
+ nmean = UINT_GET(0, this_shn);
+ this_shn->vars.actual_nmean = nmean;
+ nskip = UINT_GET(NSKIPSIZE, this_shn);
+ for(i = 0; i < nskip; i++)
+ {
+ byte = uvar_get(XBYTESIZE,this_shn);
+ }
+ }
+ else
+ blocksize = DEFAULT_BLOCK_SIZE;
+
+ nwrap = MAX(NWRAP, maxnlpc);
+
+ /* grab some space for the input buffer */
+ buffer = long2d((ulong) nchan, (ulong) (blocksize + nwrap),this_shn);
+ if (this_shn->vars.fatal_error)
+ return 0;
+ offset = long2d((ulong) nchan, (ulong) MAX(1, nmean),this_shn);
+ if (this_shn->vars.fatal_error) {
+ if (buffer) {
+ free(buffer);
+ buffer = NULL;
+ }
+ return 0;
+ }
+
+ for(chan = 0; chan < nchan; chan++)
+ {
+ for(i = 0; i < nwrap; i++)
+ buffer[chan][i] = 0;
+ buffer[chan] += nwrap;
+ }
+
+ if(maxnlpc > 0) {
+ qlpc = (int*) pmalloc((ulong) (maxnlpc * sizeof(*qlpc)),this_shn);
+ if (this_shn->vars.fatal_error) {
+ if (buffer) {
+ free(buffer);
+ buffer = NULL;
+ }
+ if (offset) {
+ free(offset);
+ buffer = NULL;
+ }
+ return 0;
+ }
+ }
+
+ if(version > 1)
+ lpcqoffset = V2LPCQOFFSET;
+
+ init_offset(offset, nchan, MAX(1, nmean), internal_ftype);
+
+ /* get commands from file and execute them */
+ chan = 0;
+ while(1)
+ {
+ this_shn->vars.reading_function_code = 1;
+ cmd = uvar_get(FNSIZE,this_shn);
+ this_shn->vars.reading_function_code = 0;
+
+ switch(cmd)
+ {
+ case FN_ZERO:
+ case FN_DIFF0:
+ case FN_DIFF1:
+ case FN_DIFF2:
+ case FN_DIFF3:
+ case FN_QLPC:
+ {
+ slong coffset, *cbuffer = buffer[chan];
+ int resn = 0, nlpc, j;
+
+ if(cmd != FN_ZERO)
+ {
+ resn = uvar_get(ENERGYSIZE,this_shn);
+ if (this_shn->vars.fatal_error) {
+ retval = 0;
+ goto got_enough_data;
+ }
+ /* this is a hack as version 0 differed in definition of var_get */
+ if(version == 0)
+ resn--;
+ }
+
+ /* find mean offset : N.B. this code duplicated */
+ if(nmean == 0)
+ coffset = offset[chan][0];
+ else
+ {
+ slong sum = (version < 2) ? 0 : nmean / 2;
+ for(i = 0; i < nmean; i++)
+ sum += offset[chan][i];
+ if(version < 2)
+ coffset = sum / nmean;
+ else
+ coffset = ROUNDEDSHIFTDOWN(sum / nmean, bitshift);
+ }
+
+ switch(cmd)
+ {
+ case FN_ZERO:
+ for(i = 0; i < blocksize; i++)
+ cbuffer[i] = 0;
+ break;
+ case FN_DIFF0:
+ for(i = 0; i < blocksize; i++) {
+ cbuffer[i] = var_get(resn,this_shn) + coffset;
+ if (this_shn->vars.fatal_error) {
+ retval = 0;
+ goto got_enough_data;
+ }
+ }
+ break;
+ case FN_DIFF1:
+ for(i = 0; i < blocksize; i++) {
+ cbuffer[i] = var_get(resn,this_shn) + cbuffer[i - 1];
+ if (this_shn->vars.fatal_error) {
+ retval = 0;
+ goto got_enough_data;
+ }
+ }
+ break;
+ case FN_DIFF2:
+ for(i = 0; i < blocksize; i++) {
+ cbuffer[i] = var_get(resn,this_shn) + (2 * cbuffer[i - 1] - cbuffer[i - 2]);
+ if (this_shn->vars.fatal_error) {
+ retval = 0;
+ goto got_enough_data;
+ }
+ }
+ break;
+ case FN_DIFF3:
+ for(i = 0; i < blocksize; i++) {
+ cbuffer[i] = var_get(resn,this_shn) + 3 * (cbuffer[i - 1] - cbuffer[i - 2]) + cbuffer[i - 3];
+ if (this_shn->vars.fatal_error) {
+ retval = 0;
+ goto got_enough_data;
+ }
+ }
+ break;
+ case FN_QLPC:
+ nlpc = uvar_get(LPCQSIZE,this_shn);
+ if (this_shn->vars.fatal_error) {
+ retval = 0;
+ goto got_enough_data;
+ }
+
+ for(i = 0; i < nlpc; i++) {
+ qlpc[i] = var_get(LPCQUANT,this_shn);
+ if (this_shn->vars.fatal_error) {
+ retval = 0;
+ goto got_enough_data;
+ }
+ }
+ for(i = 0; i < nlpc; i++)
+ cbuffer[i - nlpc] -= coffset;
+ for(i = 0; i < blocksize; i++)
+ {
+ slong sum = lpcqoffset;
+
+ for(j = 0; j < nlpc; j++)
+ sum += qlpc[j] * cbuffer[i - j - 1];
+ cbuffer[i] = var_get(resn,this_shn) + (sum >> LPCQUANT);
+ if (this_shn->vars.fatal_error) {
+ retval = 0;
+ goto got_enough_data;
+ }
+ }
+ if(coffset != 0)
+ for(i = 0; i < blocksize; i++)
+ cbuffer[i] += coffset;
+ break;
+ }
+
+ /* store mean value if appropriate : N.B. Duplicated code */
+ if(nmean > 0)
+ {
+ slong sum = (version < 2) ? 0 : blocksize / 2;
+
+ for(i = 0; i < blocksize; i++)
+ sum += cbuffer[i];
+
+ for(i = 1; i < nmean; i++)
+ offset[chan][i - 1] = offset[chan][i];
+ if(version < 2)
+ offset[chan][nmean - 1] = sum / blocksize;
+ else
+ offset[chan][nmean - 1] = (sum / blocksize) << bitshift;
+ }
+
+ if (0 == chan) {
+ this_shn->vars.initial_file_position = this_shn->vars.last_file_position_no_really;
+ goto got_enough_data;
+ }
+
+ /* do the wrap */
+ for(i = -nwrap; i < 0; i++)
+ cbuffer[i] = cbuffer[i + blocksize];
+
+ fix_bitshift(cbuffer, blocksize, bitshift, internal_ftype);
+
+ if(chan == nchan - 1)
+ {
+ fwrite_type(buffer, ftype, nchan, blocksize, this_shn);
+ this_shn->vars.bytes_in_buf = 0;
+ }
+
+ chan = (chan + 1) % nchan;
+ break;
+ }
+ break;
+
+ case FN_BLOCKSIZE:
+ UINT_GET((int) (log((double) blocksize) / M_LN2), this_shn);
+ break;
+
+ case FN_VERBATIM:
+ cklen = uvar_get(VERBATIM_CKSIZE_SIZE,this_shn);
+
+ while (cklen--) {
+ if (this_shn->vars.bytes_in_header >= OUT_BUFFER_SIZE) {
+ shn_debug("Unexpectedly large header - " PACKAGE " can only handle a maximum of %d bytes",OUT_BUFFER_SIZE);
+ goto got_enough_data;
+ }
+ this_shn->vars.bytes_in_buf = 0;
+ this_shn->vars.header[this_shn->vars.bytes_in_header++] = (char)uvar_get(VERBATIM_BYTE_SIZE,this_shn);
+ }
+ retval = 1;
+ break;
+
+ case FN_BITSHIFT:
+ bitshift = uvar_get(BITSHIFTSIZE,this_shn);
+ this_shn->vars.actual_bitshift = bitshift;
+ break;
+
+ default:
+ goto got_enough_data;
+ }
+ }
+
+got_enough_data:
+
+ /* wind up */
+ var_get_quit(this_shn);
+ fwrite_type_quit(this_shn);
+
+ if (buffer) free((void *) buffer);
+ if (offset) free((void *) offset);
+ if(maxnlpc > 0 && qlpc)
+ free((void *) qlpc);
+
+ this_shn->vars.bytes_in_buf = 0;
+
+ return retval;
+}
+
+void shn_unload(shn_file *this_shn)
+{
+ if (this_shn)
+ {
+ if (this_shn->vars.fd)
+ {
+ deadbeef->fclose(this_shn->vars.fd);
+ this_shn->vars.fd = NULL;
+ }
+
+ if (this_shn->decode_state)
+ {
+ if (this_shn->decode_state->getbuf)
+ {
+ free(this_shn->decode_state->getbuf);
+ this_shn->decode_state->getbuf = NULL;
+ }
+
+ if (this_shn->decode_state->writebuf)
+ {
+ free(this_shn->decode_state->writebuf);
+ this_shn->decode_state->writebuf = NULL;
+ }
+
+ if (this_shn->decode_state->writefub)
+ {
+ free(this_shn->decode_state->writefub);
+ this_shn->decode_state->writefub = NULL;
+ }
+
+ free(this_shn->decode_state);
+ this_shn->decode_state = NULL;
+ }
+
+ if (this_shn->seek_table)
+ {
+ free(this_shn->seek_table);
+ this_shn->seek_table = NULL;
+ }
+
+ free(this_shn);
+ this_shn = NULL;
+ }
+}
+
+shn_file *load_shn(const char *filename)
+{
+ shn_file *tmp_file;
+ shn_seek_entry *first_seek_table;
+
+ shn_debug("Loading file: '%s'",filename);
+
+ if (!(tmp_file = malloc(sizeof(shn_file))))
+ {
+ shn_debug("Could not allocate memory for SHN data structure");
+ return NULL;
+ }
+
+ memset(tmp_file, 0, sizeof(shn_file));
+
+ tmp_file->vars.fd = NULL;
+ tmp_file->vars.seek_to = -1;
+ tmp_file->vars.eof = 0;
+ tmp_file->vars.going = 0;
+ tmp_file->vars.seek_table_entries = NO_SEEK_TABLE;
+ tmp_file->vars.bytes_in_buf = 0;
+ tmp_file->vars.bytes_in_header = 0;
+ tmp_file->vars.reading_function_code = 0;
+ tmp_file->vars.initial_file_position = 0;
+ tmp_file->vars.last_file_position = 0;
+ tmp_file->vars.last_file_position_no_really = 0;
+ tmp_file->vars.bytes_read = 0;
+ tmp_file->vars.actual_bitshift = 0;
+ tmp_file->vars.actual_maxnlpc = 0;
+ tmp_file->vars.actual_nmean = 0;
+ tmp_file->vars.actual_nchan = 0;
+ tmp_file->vars.seek_offset = 0;
+
+ tmp_file->decode_state = NULL;
+
+ tmp_file->wave_header.filename = filename;
+ tmp_file->wave_header.wave_format = 0;
+ tmp_file->wave_header.channels = 0;
+ tmp_file->wave_header.block_align = 0;
+ tmp_file->wave_header.bits_per_sample = 0;
+ tmp_file->wave_header.samples_per_sec = 0;
+ tmp_file->wave_header.avg_bytes_per_sec = 0;
+ tmp_file->wave_header.rate = 0;
+ tmp_file->wave_header.header_size = 0;
+ tmp_file->wave_header.data_size = 0;
+ tmp_file->wave_header.file_has_id3v2_tag = 0;
+ tmp_file->wave_header.id3v2_tag_size = 0;
+
+ tmp_file->seek_header.version = NO_SEEK_TABLE;
+ tmp_file->seek_header.shnFileSize = 0;
+
+ tmp_file->seek_trailer.seekTableSize = 0;
+
+ tmp_file->seek_table = NULL;
+
+ tmp_file->vars.fd = deadbeef->fopen (filename);
+ if (!tmp_file->vars.fd) {
+ shn_debug("Could not open file: '%s'",filename);
+ shn_unload(tmp_file);
+ return NULL;
+ }
+
+ tmp_file->wave_header.id3v2_tag_size = deadbeef->junk_get_leading_size (tmp_file->vars.fd);
+ if (tmp_file->wave_header.id3v2_tag_size > 0) {
+ tmp_file->wave_header.file_has_id3v2_tag = 2;
+ trace ("found id3v2 tag, size: %d\n", tmp_file->wave_header.id3v2_tag_size);
+ if (0 != deadbeef->fseek(tmp_file->vars.fd,(long)tmp_file->wave_header.id3v2_tag_size,SEEK_SET)) {
+ shn_debug("Error while discarding ID3v2 tag in file '%s'.",filename);
+ deadbeef->rewind (tmp_file->vars.fd);
+ }
+ }
+
+ if (0 == get_wave_header(tmp_file))
+ {
+ shn_debug("Unable to read WAVE header from file '%s'",filename);
+ shn_unload(tmp_file);
+ return NULL;
+ }
+
+ if (tmp_file->wave_header.file_has_id3v2_tag)
+ {
+ deadbeef->fseek(tmp_file->vars.fd,tmp_file->wave_header.id3v2_tag_size,SEEK_SET);
+ tmp_file->vars.bytes_read += tmp_file->wave_header.id3v2_tag_size;
+ tmp_file->vars.seek_offset = tmp_file->wave_header.id3v2_tag_size;
+ }
+ else
+ {
+ deadbeef->fseek(tmp_file->vars.fd,0,SEEK_SET);
+ }
+
+ if (0 == shn_verify_header(tmp_file))
+ {
+ shn_debug("Invalid WAVE header in file: '%s'",filename);
+ shn_unload(tmp_file);
+ return NULL;
+ }
+
+ if (tmp_file->decode_state)
+ {
+ free(tmp_file->decode_state);
+ tmp_file->decode_state = NULL;
+ }
+
+ shn_load_seek_table(tmp_file,filename);
+
+ if (NO_SEEK_TABLE != tmp_file->vars.seek_table_entries)
+ {
+ /* verify seek tables */
+
+ first_seek_table = (shn_seek_entry *)tmp_file->seek_table;
+
+ if (tmp_file->vars.actual_bitshift != shn_uchar_to_ushort_le(first_seek_table->data+22))
+ {
+ /* initial bitshift value in the file does not match the first bitshift value of the first seektable entry - seeking is broken */
+ shn_debug("Broken seek table detected (invalid bitshift) - seeking disabled for this file.");
+ tmp_file->vars.seek_table_entries = NO_SEEK_TABLE;
+ }
+ else if (tmp_file->vars.actual_nchan > 2)
+ {
+ /* nchan is greater than the number of such entries stored in a seek table entry - seeking won't work */
+ shn_debug("Broken seek table detected (nchan %d not in range [1 .. 2]) - seeking disabled for this file.",tmp_file->vars.actual_nchan);
+ tmp_file->vars.seek_table_entries = NO_SEEK_TABLE;
+ }
+ else if (tmp_file->vars.actual_maxnlpc > 3)
+ {
+ /* maxnlpc is greater than the number of such entries stored in a seek table entry - seeking won't work */
+ shn_debug("Broken seek table detected (maxnlpc %d not in range [0 .. 3]) - seeking disabled for this file.",tmp_file->vars.actual_maxnlpc);
+ tmp_file->vars.seek_table_entries = NO_SEEK_TABLE;
+ }
+ else if (tmp_file->vars.actual_nmean > 4)
+ {
+ /* nmean is greater than the number of such entries stored in a seek table entry - seeking won't work */
+ shn_debug("Broken seek table detected (nmean %d not in range [0 .. 4]) - seeking disabled for this file.",tmp_file->vars.actual_nmean);
+ tmp_file->vars.seek_table_entries = NO_SEEK_TABLE;
+ }
+ else
+ {
+ /* seek table appears to be valid - now adjust byte offsets in seek table to match the file */
+ tmp_file->vars.seek_offset += tmp_file->vars.initial_file_position - shn_uchar_to_ulong_le(first_seek_table->data+8);
+
+ if (0 != tmp_file->vars.seek_offset)
+ {
+ shn_debug("Adjusting seek table offsets by %ld bytes due to mismatch between seek table values and input file - seeking might not work correctly.",
+ tmp_file->vars.seek_offset);
+ }
+ }
+ }
+
+ shn_debug("Successfully loaded file: '%s'",filename);
+
+ return tmp_file;
+}
+
+#if 0
+void write_and_wait(shn_file *this_shn,int block_size)
+{
+ int bytes_to_write,bytes_in_block,i;
+
+ if (this_shn->vars.bytes_in_buf < block_size)
+ return;
+
+ bytes_in_block = min(this_shn->vars.bytes_in_buf, block_size);
+
+ if (bytes_in_block <= 0)
+ return;
+
+ bytes_to_write = bytes_in_block;
+ while ((bytes_to_write + bytes_in_block) <= this_shn->vars.bytes_in_buf)
+ bytes_to_write += bytes_in_block;
+
+ shn_ip.add_vis_pcm(shn_ip.output->written_time(), (this_shn->wave_header.bits_per_sample == 16) ? FMT_S16_LE : FMT_U8,
+ this_shn->wave_header.channels, bytes_to_write, this_shn->vars.buffer);
+
+ while(shn_ip.output->buffer_free() < bytes_to_write && this_shn->vars.going && this_shn->vars.seek_to == -1)
+ xmms_usleep(10000);
+
+ if(this_shn->vars.going && this_shn->vars.seek_to == -1) {
+ if (shn_cfg.swap_bytes)
+ swap_bytes(this_shn, bytes_to_write);
+ shn_ip.output->write_audio(this_shn->vars.buffer, bytes_to_write);
+ } else
+ return;
+
+ /* shift data from end of buffer to the front */
+ this_shn->vars.bytes_in_buf -= bytes_to_write;
+
+ for(i=0;i<this_shn->vars.bytes_in_buf;i++)
+ this_shn->vars.buffer[i] = this_shn->vars.buffer[i+bytes_to_write];
+}
+#endif
+
+static const char * exts[] = { "shn", NULL };
+static const char *filetypes[] = { "Shorten", NULL };
+
+// define plugin interface
+static DB_decoder_t plugin = {
+ DB_PLUGIN_SET_API_VERSION
+ .plugin.version_major = 0,
+ .plugin.version_minor = 1,
+ .plugin.type = DB_PLUGIN_DECODER,
+ .plugin.id = "shn",
+ .plugin.name = "SHN player",
+ .plugin.descr = "SHN player based on xmms-shn",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .open = shn_open,
+ .init = shn_init,
+ .free = shn_free,
+ .read_int16 = shn_read_int16,
+ .seek = shn_seek,
+ .seek_sample = shn_seek_sample,
+ .insert = shn_insert,
+ .exts = exts,
+ .filetypes = filetypes
+};
+
+DB_plugin_t *
+shn_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
diff --git a/plugins/shn/shn.h b/plugins/shn/shn.h
new file mode 100644
index 00000000..3f64a23c
--- /dev/null
+++ b/plugins/shn/shn.h
@@ -0,0 +1,277 @@
+/* xmms-shn - a shorten (.shn) plugin for XMMS
+ * Copyright (C) 2000-2007 Jason Jordan <shnutils@freeshell.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * $Id: shn.h,v 1.27 2007/03/23 05:49:48 jason Exp $
+ */
+
+#ifndef _SHN_H
+#define _SHN_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "../../deadbeef.h"
+
+extern DB_functions_t *deadbeef;
+
+#define shn_vsnprintf(a,b,c,d) vsnprintf(a,b,c,d)
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+/* surely no headers will be this large. right? RIGHT? */
+#define OUT_BUFFER_SIZE 16384
+
+#define BUF_SIZE 4096
+
+#define ERROR_OUTPUT_DEVNULL 0
+#define ERROR_OUTPUT_STDERR 1
+#define ERROR_OUTPUT_WINDOW 2
+
+#define SEEK_SUFFIX "skt"
+
+#define NO_SEEK_TABLE -1
+
+#define SEEK_HEADER_SIGNATURE "SEEK"
+#define SEEK_TRAILER_SIGNATURE "SHNAMPSK"
+
+#define SEEK_HEADER_SIZE 12
+#define SEEK_TRAILER_SIZE 12
+#define SEEK_ENTRY_SIZE 80
+#define SEEK_RESOLUTION 25600
+
+#define WAVE_RIFF (0x46464952) /* 'RIFF' in little-endian */
+#define WAVE_WAVE (0x45564157) /* 'WAVE' in little-endian */
+#define WAVE_FMT (0x20746d66) /* ' fmt' in little-endian */
+#define WAVE_DATA (0x61746164) /* 'data' in little-endian */
+
+#define AIFF_FORM (0x4D524F46) /* 'FORM' in little-endian */
+
+#define WAVE_FORMAT_UNKNOWN (0x0000)
+#define WAVE_FORMAT_PCM (0x0001)
+#define WAVE_FORMAT_ADPCM (0x0002)
+#define WAVE_FORMAT_IEEE_FLOAT (0x0003)
+#define WAVE_FORMAT_ALAW (0x0006)
+#define WAVE_FORMAT_MULAW (0x0007)
+#define WAVE_FORMAT_OKI_ADPCM (0x0010)
+#define WAVE_FORMAT_IMA_ADPCM (0x0011)
+#define WAVE_FORMAT_DIGISTD (0x0015)
+#define WAVE_FORMAT_DIGIFIX (0x0016)
+#define WAVE_FORMAT_DOLBY_AC2 (0x0030)
+#define WAVE_FORMAT_GSM610 (0x0031)
+#define WAVE_FORMAT_ROCKWELL_ADPCM (0x003b)
+#define WAVE_FORMAT_ROCKWELL_DIGITALK (0x003c)
+#define WAVE_FORMAT_G721_ADPCM (0x0040)
+#define WAVE_FORMAT_G728_CELP (0x0041)
+#define WAVE_FORMAT_MPEG (0x0050)
+#define WAVE_FORMAT_MPEGLAYER3 (0x0055)
+#define WAVE_FORMAT_G726_ADPCM (0x0064)
+#define WAVE_FORMAT_G722_ADPCM (0x0065)
+
+#define CD_BLOCK_SIZE (2352)
+#define CD_BLOCKS_PER_SEC (75)
+#define CD_MIN_BURNABLE_SIZE (705600)
+#define CD_CHANNELS (2)
+#define CD_SAMPLES_PER_SEC (44100)
+#define CD_BITS_PER_SAMPLE (16)
+#define CD_RATE (176400)
+
+#define CANONICAL_HEADER_SIZE (44)
+
+#define PROBLEM_NOT_CD_QUALITY (0x00000001)
+#define PROBLEM_CD_BUT_BAD_BOUND (0x00000002)
+#define PROBLEM_CD_BUT_TOO_SHORT (0x00000004)
+#define PROBLEM_HEADER_NOT_CANONICAL (0x00000008)
+#define PROBLEM_EXTRA_CHUNKS (0x00000010)
+#define PROBLEM_HEADER_INCONSISTENT (0x00000020)
+
+#define PROB_NOT_CD(f) ((f.problems) & (PROBLEM_NOT_CD_QUALITY))
+#define PROB_BAD_BOUND(f) ((f.problems) & (PROBLEM_CD_BUT_BAD_BOUND))
+#define PROB_TOO_SHORT(f) ((f.problems) & (PROBLEM_CD_BUT_TOO_SHORT))
+#define PROB_HDR_NOT_CANONICAL(f) ((f.problems) & (PROBLEM_HEADER_NOT_CANONICAL))
+#define PROB_EXTRA_CHUNKS(f) ((f.problems) & (PROBLEM_EXTRA_CHUNKS))
+#define PROB_HDR_INCONSISTENT(f) ((f.problems) & (PROBLEM_HEADER_INCONSISTENT))
+
+typedef struct _shn_config
+{
+ int error_output_method;
+ char *error_output_method_config_name;
+ char *seek_tables_path;
+ char *seek_tables_path_config_name;
+ char *relative_seek_tables_path;
+ char *relative_seek_tables_path_config_name;
+ int verbose;
+ char *verbose_config_name;
+ int swap_bytes;
+ char *swap_bytes_config_name;
+ int load_textfiles;
+ char *load_textfiles_config_name;
+ char *textfile_extensions;
+ char *textfile_extensions_config_name;
+} shn_config;
+
+typedef struct _shn_decode_state
+{
+ uchar *getbuf;
+ uchar *getbufp;
+ int nbitget;
+ int nbyteget;
+ ulong gbuffer;
+ schar *writebuf;
+ schar *writefub;
+ int nwritebuf;
+} shn_decode_state;
+
+typedef struct _shn_seek_header
+{
+ uchar data[SEEK_HEADER_SIZE];
+ slong version;
+ ulong shnFileSize;
+} shn_seek_header;
+
+typedef struct _shn_seek_trailer
+{
+ uchar data[SEEK_TRAILER_SIZE];
+ ulong seekTableSize;
+} shn_seek_trailer;
+
+typedef struct _shn_seek_entry
+{
+ uchar data[SEEK_ENTRY_SIZE];
+} shn_seek_entry;
+
+/* old way, kept for reference.
+ (changed because some compilers apparently don't support #pragma pack(1))
+
+typedef struct _shn_seek_header
+{
+ char signature[4];
+ unsigned long version;
+ unsigned long shnFileSize;
+} shn_seek_header;
+
+typedef struct _shn_seek_trailer
+{
+ unsigned long seekTableSize;
+ char signature[8];
+} shn_seek_trailer;
+
+typedef struct _shn_seek_entry
+{
+ unsigned long shnSample;
+ unsigned long shnByteOffset;
+ unsigned long shnLastPosition;
+ unsigned short shnByteGet;
+ unsigned short shnBufferOffset;
+ unsigned short shnBitOffset;
+ unsigned long shnGBuffer;
+ unsigned short shnBitShift;
+ long cbuf0[3];
+ long cbuf1[3];
+ long offset0[4];
+ long offset1[4];
+} shn_seek_entry;
+*/
+
+typedef struct _shn_wave_header
+{
+ const char *filename;
+ char m_ss[16];
+
+ uint header_size;
+
+ ushort channels,
+ block_align,
+ bits_per_sample,
+ wave_format;
+
+ ulong samples_per_sec,
+ avg_bytes_per_sec,
+ rate,
+ length,
+ data_size,
+ total_size,
+ chunk_size,
+ actual_size;
+
+ double exact_length;
+
+ int file_has_id3v2_tag;
+ long id3v2_tag_size;
+
+ ulong problems;
+} shn_wave_header;
+
+typedef struct _shn_vars
+{
+ DB_FILE *fd;
+ int seek_to;
+ int eof;
+ int going;
+ slong seek_table_entries;
+ ulong seek_resolution;
+ int bytes_in_buf;
+ uchar buffer[OUT_BUFFER_SIZE];
+ int bytes_in_header;
+ uchar header[OUT_BUFFER_SIZE];
+ int fatal_error;
+ schar fatal_error_msg[BUF_SIZE];
+ int reading_function_code;
+ ulong last_file_position;
+ ulong last_file_position_no_really;
+ ulong initial_file_position;
+ ulong bytes_read;
+ unsigned short actual_bitshift;
+ int actual_maxnlpc;
+ int actual_nmean;
+ int actual_nchan;
+ long seek_offset;
+} shn_vars;
+
+typedef struct _shn_file
+{
+ shn_vars vars;
+ shn_decode_state *decode_state;
+ shn_wave_header wave_header;
+ shn_seek_header seek_header;
+ shn_seek_trailer seek_trailer;
+ shn_seek_entry *seek_table;
+} shn_file;
+
+extern shn_config shn_cfg;
+
+extern shn_seek_entry *shn_seek_entry_search(shn_seek_entry *,ulong,ulong,ulong,ulong);
+extern void shn_load_seek_table(shn_file *,const char *);
+extern void shn_unload(shn_file *);
+extern int shn_verify_header(shn_file *);
+extern int shn_filename_contains_a_dot(const char *);
+extern char *shn_get_base_filename(const char *);
+extern char *shn_get_base_directory(const char *);
+extern void shn_length_to_str(shn_file *);
+extern char *shn_format_to_str(ushort);
+extern ulong shn_uchar_to_ulong_le(uchar *);
+extern slong shn_uchar_to_slong_le(uchar *);
+extern ushort shn_uchar_to_ushort_le(uchar *);
+extern void shn_debug(char *, ...);
+extern void shn_error(char *, ...);
+extern void shn_error_fatal(shn_file *,char *, ...);
+extern void shn_snprintf(char *,int,char *, ...);
+
+#endif
diff --git a/plugins/shn/shorten.c b/plugins/shn/shorten.c
new file mode 100644
index 00000000..0c92af83
--- /dev/null
+++ b/plugins/shn/shorten.c
@@ -0,0 +1,54 @@
+/******************************************************************************
+* *
+* Copyright (C) 1992-1995 Tony Robinson *
+* *
+* See the file doc/LICENSE.shorten for conditions on distribution and usage *
+* *
+******************************************************************************/
+
+/*
+ * $Id: shorten.c,v 1.7 2003/08/26 05:34:04 jason Exp $
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <setjmp.h>
+#include "shorten.h"
+
+void init_offset(slong **offset,int nchan,int nblock,int ftype)
+{
+ slong mean = 0;
+ int chan, i;
+
+ /* initialise offset */
+ switch(ftype)
+ {
+ case TYPE_AU1:
+ case TYPE_S8:
+ case TYPE_S16HL:
+ case TYPE_S16LH:
+ case TYPE_ULAW:
+ case TYPE_AU2:
+ case TYPE_AU3:
+ case TYPE_ALAW:
+ mean = 0;
+ break;
+ case TYPE_U8:
+ mean = 0x80;
+ break;
+ case TYPE_U16HL:
+ case TYPE_U16LH:
+ mean = 0x8000;
+ break;
+ default:
+ shn_debug("Unknown file type: %d", ftype);
+ }
+
+ for(chan = 0; chan < nchan; chan++)
+ for(i = 0; i < nblock; i++)
+ offset[chan][i] = mean;
+}
diff --git a/plugins/shn/shorten.h b/plugins/shn/shorten.h
new file mode 100644
index 00000000..006b9bee
--- /dev/null
+++ b/plugins/shn/shorten.h
@@ -0,0 +1,222 @@
+/******************************************************************************
+* *
+* Copyright (C) 1992-1995 Tony Robinson *
+* *
+* See the file doc/LICENSE.shorten for conditions on distribution and usage *
+* *
+******************************************************************************/
+
+/*
+ * $Id: shorten.h,v 1.4 2001/12/30 05:12:04 jason Exp $
+ */
+
+#ifndef _SHORTEN_H
+#define _SHORTEN_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <pthread.h>
+
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if SIZEOF_UNSIGNED_LONG == 4
+# define uint32_t unsigned long
+# define int32_t long
+# else
+# define uint32_t unsigned int
+# define int32_t int
+# endif
+# define uint16_t unsigned short
+# define uint8_t unsigned char
+# define int16_t short
+# define int8_t char
+#endif
+
+#undef ulong
+#undef ushort
+#undef uchar
+#undef slong
+#undef sshort
+#undef schar
+#define ulong uint32_t
+#define ushort uint16_t
+#define uchar uint8_t
+#define slong int32_t
+#define sshort int16_t
+#define schar int8_t
+
+#include "shn.h"
+
+extern shn_file *shnfile;
+
+#define MAGIC "ajkg"
+#define FORMAT_VERSION 2
+#define MIN_SUPPORTED_VERSION 1
+#define MAX_SUPPORTED_VERSION 3
+#define MAX_VERSION 7
+
+#define UNDEFINED_UINT -1
+#define DEFAULT_BLOCK_SIZE 256
+#define DEFAULT_V0NMEAN 0
+#define DEFAULT_V2NMEAN 4
+#define DEFAULT_MAXNLPC 0
+#define DEFAULT_NCHAN 1
+#define DEFAULT_NSKIP 0
+#define DEFAULT_NDISCARD 0
+#define NBITPERLONG 32
+#define DEFAULT_MINSNR 256
+#define DEFAULT_MAXRESNSTR "32.0"
+#define DEFAULT_QUANTERROR 0
+#define MINBITRATE 2.5
+
+#define MAX_LPC_ORDER 64
+#define CHANSIZE 0
+#define ENERGYSIZE 3
+#define BITSHIFTSIZE 2
+#define NWRAP 3
+
+#define FNSIZE 2
+#define FN_DIFF0 0
+#define FN_DIFF1 1
+#define FN_DIFF2 2
+#define FN_DIFF3 3
+#define FN_QUIT 4
+#define FN_BLOCKSIZE 5
+#define FN_BITSHIFT 6
+#define FN_QLPC 7
+#define FN_ZERO 8
+#define FN_VERBATIM 9
+
+#define VERBATIM_CKSIZE_SIZE 5 /* a var_put code size */
+#define VERBATIM_BYTE_SIZE 8 /* code size 8 on single bytes means
+ * no compression at all */
+#define VERBATIM_CHUNK_MAX 256 /* max. size of a FN_VERBATIM chunk */
+
+#define ULONGSIZE 2
+#define NSKIPSIZE 1
+#define LPCQSIZE 2
+#define LPCQUANT 5
+#define XBYTESIZE 7
+
+#define TYPESIZE 4
+#define TYPE_AU1 0 /* original lossless ulaw */
+#define TYPE_S8 1 /* signed 8 bit characters */
+#define TYPE_U8 2 /* unsigned 8 bit characters */
+#define TYPE_S16HL 3 /* signed 16 bit shorts: high-low */
+#define TYPE_U16HL 4 /* unsigned 16 bit shorts: high-low */
+#define TYPE_S16LH 5 /* signed 16 bit shorts: low-high */
+#define TYPE_U16LH 6 /* unsigned 16 bit shorts: low-high */
+#define TYPE_ULAW 7 /* lossy ulaw: internal conversion to linear */
+#define TYPE_AU2 8 /* new ulaw with zero mapping */
+#define TYPE_AU3 9 /* lossless alaw */
+#define TYPE_ALAW 10 /* lossy alaw: internal conversion to linear */
+#define TYPE_RIFF_WAVE 11 /* Microsoft .WAV files */
+#define TYPE_EOF 12
+#define TYPE_GENERIC_ULAW 128
+#define TYPE_GENERIC_ALAW 129
+
+#define POSITIVE_ULAW_ZERO 0xff
+#define NEGATIVE_ULAW_ZERO 0x7f
+
+#ifndef MAX_PATH
+#define MAX_PATH 2048
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#if defined(unix) && !defined(linux)
+#define labs abs
+#endif
+
+#define ROUNDEDSHIFTDOWN(x, n) (((n) == 0) ? (x) : ((x) >> ((n) - 1)) >> 1)
+
+#ifndef M_LN2
+#define M_LN2 0.69314718055994530942
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+/* BUFSIZ must be a multiple of four to contain a whole number of words */
+#ifdef BUFSIZ
+#undef BUFSIZ
+#endif
+
+#define BUFSIZ 512
+
+#define V2LPCQOFFSET (1 << LPCQUANT);
+
+#define UINT_GET(nbit, shnfile) \
+ ((version == 0) ? uvar_get(nbit, shnfile) : ulong_get(shnfile))
+
+#define putc_exit(val, stream)\
+{ char rval;\
+ if((rval = putc((val), (stream))) != (char) (val))\
+ complain("FATALERROR: write failed: putc returns EOF");\
+}
+
+extern int getc_exit_val;
+#define getc_exit(stream)\
+(((getc_exit_val = getc(stream)) == EOF) ? \
+ complain("FATALERROR: read failed: getc returns EOF"), 0: getc_exit_val)
+
+/************************/
+/* defined in shorten.c */
+extern void init_offset(slong**, int, int, int);
+extern int shorten(FILE*, FILE*, int, char**);
+
+/**************************/
+/* defined in Sulawalaw.c */
+extern int Sulaw2lineartab[];
+#define Sulaw2linear(i) (Sulaw2lineartab[i])
+#ifndef Sulaw2linear
+extern int Sulaw2linear(uchar);
+#endif
+extern uchar Slinear2ulaw(int);
+
+extern int Salaw2lineartab[];
+#define Salaw2linear(i) (Salaw2lineartab[i])
+#ifndef Salaw2linear
+extern int Salaw2linear(uchar);
+#endif
+extern uchar Slinear2alaw(int);
+
+/**********************/
+/* defined in fixio.c */
+extern void init_sizeof_sample(void);
+extern void fwrite_type_init(shn_file*);
+extern void fwrite_type(slong**,int,int,int,shn_file*);
+extern void fwrite_type_quit(shn_file*);
+extern void fix_bitshift(slong*, int, int, int);
+
+/**********************/
+/* defined in vario.c */
+extern void var_get_init(shn_file*);
+extern slong uvar_get(int, shn_file*);
+extern slong var_get(int, shn_file*);
+extern ulong ulong_get(shn_file*);
+extern void var_get_quit(shn_file*);
+
+extern int sizeof_uvar(ulong, int);
+extern int sizeof_var(slong, int);
+
+extern void mkmasktab(void);
+extern ulong word_get(shn_file*);
+
+/**********************/
+/* defined in array.c */
+extern void* pmalloc(ulong, shn_file*);
+extern slong** long2d(ulong, ulong, shn_file*);
+
+#endif
diff --git a/plugins/shn/sulawalaw.c b/plugins/shn/sulawalaw.c
new file mode 100644
index 00000000..61b417b8
--- /dev/null
+++ b/plugins/shn/sulawalaw.c
@@ -0,0 +1,192 @@
+/*
+ * $Id: sulawalaw.c,v 1.5 2001/12/30 05:12:04 jason Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <math.h>
+#include "shorten.h"
+
+int Sulaw2lineartab[] = {-32124, -31100, -30076, -29052, -28028, -27004,
+ -25980, -24956, -23932, -22908, -21884, -20860, -19836, -18812,
+ -17788, -16764, -15996, -15484, -14972, -14460, -13948, -13436,
+ -12924, -12412, -11900, -11388, -10876, -10364, -9852, -9340, -8828,
+ -8316, -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, -5884,
+ -5628, -5372, -5116, -4860, -4604, -4348, -4092, -3900, -3772, -3644,
+ -3516, -3388, -3260, -3132, -3004, -2876, -2748, -2620, -2492, -2364,
+ -2236, -2108, -1980, -1884, -1820, -1756, -1692, -1628, -1564, -1500,
+ -1436, -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, -876,
+ -844, -812, -780, -748, -716, -684, -652, -620, -588, -556, -524,
+ -492, -460, -428, -396, -372, -356, -340, -324, -308, -292, -276,
+ -260, -244, -228, -212, -196, -180, -164, -148, -132, -120, -112,
+ -104, -96, -88, -80, -72, -64, -56, -48, -40, -32, -24, -16, -8, 0,
+ 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, 23932, 22908,
+ 21884, 20860, 19836, 18812, 17788, 16764, 15996, 15484, 14972, 14460,
+ 13948, 13436, 12924, 12412, 11900, 11388, 10876, 10364, 9852, 9340,
+ 8828, 8316, 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, 5884,
+ 5628, 5372, 5116, 4860, 4604, 4348, 4092, 3900, 3772, 3644, 3516,
+ 3388, 3260, 3132, 3004, 2876, 2748, 2620, 2492, 2364, 2236, 2108,
+ 1980, 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, 1372, 1308,
+ 1244, 1180, 1116, 1052, 988, 924, 876, 844, 812, 780, 748, 716, 684,
+ 652, 620, 588, 556, 524, 492, 460, 428, 396, 372, 356, 340, 324, 308,
+ 292, 276, 260, 244, 228, 212, 196, 180, 164, 148, 132, 120, 112, 104,
+ 96, 88, 80, 72, 64, 56, 48, 40, 32, 24, 16, 8, 0};
+
+#ifndef Sulaw2linear
+#ifdef __STDC__
+int Sulaw2linear(uchar ulaw) {
+#else
+int Sulaw2linear(ulaw) uchar ulaw; {
+#endif
+ return(Sulaw2lineartab[ulaw]);
+}
+#endif
+
+/* adapted by ajr for int input */
+#ifdef __STDC__
+uchar Slinear2ulaw(int sample) {
+#else
+uchar Slinear2ulaw(sample) int sample; {
+#endif
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711 (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+** Continuous PCM Companding Law," Villeret, Michel,
+** et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+** 1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+** for Analog-to_Digital Conversion Techniques,"
+** 17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+ static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+ int sign, exponent, mantissa;
+ uchar ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ if(sample < 0) {
+ sign = 0x80;
+ sample = -sample;
+ }
+ else
+ sign = 0;
+
+ /* clip the magnitude */
+ if(sample > CLIP) sample = CLIP;
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[( sample >> 7 ) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+
+ return(ulawbyte);
+}
+
+
+/******************
+ * ALAW starts here
+ */
+
+int Salaw2lineartab[] = {-5504, -5248, -6016, -5760, -4480, -4224,
+ -4992, -4736, -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+ -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, -3776, -3648,
+ -4032, -3904, -3264, -3136, -3520, -3392, -22016, -20992, -24064,
+ -23040, -17920, -16896, -19968, -18944, -30208, -29184, -32256,
+ -31232, -26112, -25088, -28160, -27136, -11008, -10496, -12032,
+ -11520, -8960, -8448, -9984, -9472, -15104, -14592, -16128, -15616,
+ -13056, -12544, -14080, -13568, -344, -328, -376, -360, -280, -264,
+ -312, -296, -472, -456, -504, -488, -408, -392, -440, -424, -88, -72,
+ -120, -104, -24, -8, -56, -40, -216, -200, -248, -232, -152, -136,
+ -184, -168, -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+ -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, -688, -656,
+ -752, -720, -560, -528, -624, -592, -944, -912, -1008, -976, -816,
+ -784, -880, -848, 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
+ 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, 2752, 2624, 3008,
+ 2880, 2240, 2112, 2496, 2368, 3776, 3648, 4032, 3904, 3264, 3136,
+ 3520, 3392, 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, 11008, 10496,
+ 12032, 11520, 8960, 8448, 9984, 9472, 15104, 14592, 16128, 15616,
+ 13056, 12544, 14080, 13568, 344, 328, 376, 360, 280, 264, 312, 296,
+ 472, 456, 504, 488, 408, 392, 440, 424, 88, 72, 120, 104, 24, 8, 56,
+ 40, 216, 200, 248, 232, 152, 136, 184, 168, 1376, 1312, 1504, 1440,
+ 1120, 1056, 1248, 1184, 1888, 1824, 2016, 1952, 1632, 1568, 1760,
+ 1696, 688, 656, 752, 720, 560, 528, 624, 592, 944, 912, 1008, 976,
+ 816, 784, 880, 848};
+
+#ifndef Salaw2linear
+#ifdef __STDC__
+int Salaw2linear(uchar alaw) {
+#else
+int Salaw2linear(alaw) uchar alaw; {
+#endif
+ return(Salaw2lineartab[alaw]);
+}
+#endif
+
+/* this is derived from the Sun code - it is a bit simpler and has int input */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#ifdef __STDC__
+uchar Slinear2alaw(int linear) {
+#else
+uchar Slinear2alaw(linear) int linear; {
+#endif
+ int seg;
+ uchar aval, mask;
+ static sshort seg_aend[NSEGS] = {0x1f,0x3f,0x7f,0xff,0x1ff,0x3ff,0x7ff,0xfff};
+
+ linear = linear >> 3;
+
+ if(linear >= 0) {
+ mask = 0xd5; /* sign (7th) bit = 1 */
+ } else {
+ mask = 0x55; /* sign bit = 0 */
+ linear = -linear - 1;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ for(seg = 0; seg < NSEGS && linear > seg_aend[seg]; seg++);
+
+ /* Combine the sign, segment, and quantization bits. */
+ if(seg >= NSEGS) /* out of range, return maximum value. */
+ return (uchar) (0x7F ^ mask);
+ else {
+ aval = (uchar) seg << SEG_SHIFT;
+ if (seg < 2)
+ aval |= (linear >> 1) & QUANT_MASK;
+ else
+ aval |= (linear >> seg) & QUANT_MASK;
+ return (aval ^ mask);
+ }
+}
diff --git a/plugins/shn/vario.c b/plugins/shn/vario.c
new file mode 100644
index 00000000..988e2eb8
--- /dev/null
+++ b/plugins/shn/vario.c
@@ -0,0 +1,146 @@
+/******************************************************************************
+* *
+* Copyright (C) 1992-1995 Tony Robinson *
+* *
+* See the file doc/LICENSE.shorten for conditions on distribution and usage *
+* *
+******************************************************************************/
+
+/*
+ * $Id: vario.c,v 1.10 2004/05/04 02:26:36 jason Exp $
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "shorten.h"
+
+#define MASKTABSIZE 33
+ulong masktab[MASKTABSIZE];
+
+void mkmasktab() {
+ int i;
+ ulong val = 0;
+
+ masktab[0] = val;
+ for(i = 1; i < MASKTABSIZE; i++) {
+ val <<= 1;
+ val |= 1;
+ masktab[i] = val;
+ }
+}
+
+void var_get_init(shn_file *this_shn)
+{
+ mkmasktab();
+
+ this_shn->decode_state->getbuf = (uchar*) pmalloc((ulong) BUFSIZ,this_shn);
+ this_shn->decode_state->getbufp = this_shn->decode_state->getbuf;
+ this_shn->decode_state->nbyteget = 0;
+ this_shn->decode_state->gbuffer = 0;
+ this_shn->decode_state->nbitget = 0;
+}
+
+ulong word_get(shn_file *this_shn)
+{
+ ulong buffer;
+ int bytes;
+
+ if(this_shn->decode_state->nbyteget < 4)
+ {
+ this_shn->vars.last_file_position = this_shn->vars.bytes_read;
+
+ bytes = deadbeef->fread((uchar*) this_shn->decode_state->getbuf, 1, BUFSIZ, this_shn->vars.fd);
+ this_shn->decode_state->nbyteget += bytes;
+
+ if(this_shn->decode_state->nbyteget < 4) {
+ shn_error_fatal(this_shn,"Premature EOF on compressed stream -\npossible corrupt or truncated file");
+ return (ulong)0;
+ }
+
+ this_shn->vars.bytes_read += bytes;
+
+ this_shn->decode_state->getbufp = this_shn->decode_state->getbuf;
+ }
+
+ buffer = (((slong) (this_shn->decode_state->getbufp[0])) << 24) | (((slong) (this_shn->decode_state->getbufp[1])) << 16) |
+ (((slong) (this_shn->decode_state->getbufp[2])) << 8) | ((slong) (this_shn->decode_state->getbufp[3]));
+
+ this_shn->decode_state->getbufp += 4;
+ this_shn->decode_state->nbyteget -= 4;
+
+ return(buffer);
+}
+
+slong uvar_get(int nbin,shn_file *this_shn)
+{
+ slong result;
+
+ if (this_shn->vars.reading_function_code) {
+ this_shn->vars.last_file_position_no_really = this_shn->vars.last_file_position;
+ }
+
+ if(this_shn->decode_state->nbitget == 0)
+ {
+ this_shn->decode_state->gbuffer = word_get(this_shn);
+ if (this_shn->vars.fatal_error)
+ return (ulong)0;
+ this_shn->decode_state->nbitget = 32;
+ }
+
+ for(result = 0; !(this_shn->decode_state->gbuffer & (1L << --(this_shn->decode_state->nbitget))); result++)
+ {
+ if(this_shn->decode_state->nbitget == 0)
+ {
+ this_shn->decode_state->gbuffer = word_get(this_shn);
+ if (this_shn->vars.fatal_error)
+ return (ulong)0;
+ this_shn->decode_state->nbitget = 32;
+ }
+ }
+
+ while(nbin != 0)
+ {
+ if(this_shn->decode_state->nbitget >= nbin)
+ {
+ result = (result << nbin) | ((this_shn->decode_state->gbuffer >> (this_shn->decode_state->nbitget-nbin)) &masktab[nbin]);
+ this_shn->decode_state->nbitget -= nbin;
+ nbin = 0;
+ }
+ else
+ {
+ result = (result << this_shn->decode_state->nbitget) | (this_shn->decode_state->gbuffer & masktab[this_shn->decode_state->nbitget]);
+ this_shn->decode_state->gbuffer = word_get(this_shn);
+ if (this_shn->vars.fatal_error)
+ return (ulong)0;
+ nbin -= this_shn->decode_state->nbitget;
+ this_shn->decode_state->nbitget = 32;
+ }
+ }
+
+ return(result);
+}
+
+ulong ulong_get(shn_file *this_shn)
+{
+ unsigned int nbit = uvar_get(ULONGSIZE,this_shn);
+ if (this_shn->vars.fatal_error)
+ return (ulong)0;
+ return(uvar_get(nbit,this_shn));
+}
+
+slong var_get(int nbin,shn_file *this_shn)
+{
+ ulong uvar = uvar_get(nbin + 1,this_shn);
+ if (this_shn->vars.fatal_error)
+ return (slong)0;
+
+ if(uvar & 1) return((slong) ~(uvar >> 1));
+ else return((slong) (uvar >> 1));
+}
+
+void var_get_quit(shn_file *this_shn)
+{
+ free((void *) this_shn->decode_state->getbuf);
+ this_shn->decode_state->getbuf = NULL;
+}
diff --git a/plugins/shn/wave.c b/plugins/shn/wave.c
new file mode 100644
index 00000000..a21a6941
--- /dev/null
+++ b/plugins/shn/wave.c
@@ -0,0 +1,264 @@
+/* wave.c - functions to parse and verify WAVE headers
+ * Copyright (C) 2000-2007 Jason Jordan <shnutils@freeshell.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * $Id: wave.c,v 1.13 2007/03/23 05:49:48 jason Exp $
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include "shorten.h"
+#include "shn.h"
+
+int is_valid_file(shn_file *info)
+/* determines whether the given filename (info->filename) is a regular file, and is readable */
+{
+ struct stat sz;
+ FILE *f;
+
+ if (0 != stat(info->wave_header.filename,&sz)) {
+ if (errno == ENOENT)
+ shn_error("cannot open '%s' because it does not exist",info->wave_header.filename);
+ else if (errno == EACCES)
+ shn_error("cannot open '%s' due to insufficient permissions",info->wave_header.filename);
+ else if (errno == EFAULT)
+ shn_error("cannot open '%s' due to bad address",info->wave_header.filename);
+ else if (errno == ENOMEM)
+ shn_error("cannot open '%s' because the kernel ran out of memory",info->wave_header.filename);
+ else if (errno == ENAMETOOLONG)
+ shn_error("cannot open '%s' because the file name is too long",info->wave_header.filename);
+ else
+ shn_error("cannot open '%s' due to an unknown problem",info->wave_header.filename);
+ return 0;
+ }
+ if (0 == S_ISREG(sz.st_mode)) {
+ if (S_ISLNK(sz.st_mode))
+ shn_error("'%s' is a symbolic link, not a regular file",info->wave_header.filename);
+ else if (S_ISDIR(sz.st_mode))
+ shn_error("'%s' is a directory, not a regular file",info->wave_header.filename);
+ else if (S_ISCHR(sz.st_mode))
+ shn_error("'%s' is a character device, not a regular file",info->wave_header.filename);
+ else if (S_ISBLK(sz.st_mode))
+ shn_error("'%s' is a block device, not a regular file",info->wave_header.filename);
+ else if (S_ISFIFO(sz.st_mode))
+ shn_error("'%s' is a fifo, not a regular file",info->wave_header.filename);
+ else if (S_ISSOCK(sz.st_mode))
+ shn_error("'%s' is a socket, not a regular file",info->wave_header.filename);
+ return 0;
+ }
+ info->wave_header.actual_size = (ulong)sz.st_size;
+
+ if (NULL == (f = fopen(info->wave_header.filename,"rb"))) {
+ shn_error("could not open '%s': %s",info->wave_header.filename,strerror(errno));
+ return 0;
+ }
+ fclose(f);
+
+ return 1;
+}
+
+int shn_verify_header(shn_file *this_shn)
+{
+ ulong l;
+ int cur = 0;
+
+ if (0 == is_valid_file(this_shn))
+ {
+ shn_debug("while processing '%s': something went wrong while opening this file, see above",this_shn->wave_header.filename);
+ return 0;
+ }
+
+ if (this_shn->vars.bytes_in_header < CANONICAL_HEADER_SIZE) {
+ shn_debug("while processing '%s': header is only %d bytes (should be at least %d bytes)",
+ this_shn->wave_header.filename,this_shn->vars.bytes_in_header,CANONICAL_HEADER_SIZE);
+ return 0;
+ }
+
+ if (WAVE_RIFF != shn_uchar_to_ulong_le(this_shn->vars.header+cur))
+ {
+ if (AIFF_FORM == shn_uchar_to_ulong_le(this_shn->vars.header+cur))
+ shn_debug("while processing '%s': file contains AIFF data, which is currently not supported",this_shn->wave_header.filename);
+ else
+ shn_debug("while processing '%s': WAVE header is missing RIFF tag - possible corrupt file",this_shn->wave_header.filename);
+ return 0;
+ }
+ cur += 4;
+
+ this_shn->wave_header.chunk_size = shn_uchar_to_ulong_le(this_shn->vars.header+cur);
+ cur += 4;
+
+ if (WAVE_WAVE != shn_uchar_to_ulong_le(this_shn->vars.header+cur))
+ {
+ shn_debug("while processing '%s': WAVE header is missing WAVE tag",this_shn->wave_header.filename);
+ return 0;
+ }
+ cur += 4;
+
+ for (;;)
+ {
+ cur += 4;
+
+ l = shn_uchar_to_ulong_le(this_shn->vars.header+cur);
+ cur += 4;
+
+ if (WAVE_FMT == shn_uchar_to_ulong_le(this_shn->vars.header+cur-8))
+ break;
+
+ cur += l;
+ }
+
+ if (l < 16)
+ {
+ shn_debug("while processing '%s': fmt chunk in WAVE header was too short",this_shn->wave_header.filename);
+ return 0;
+ }
+
+ this_shn->wave_header.wave_format = shn_uchar_to_ushort_le(this_shn->vars.header+cur);
+ cur += 2;
+
+ switch (this_shn->wave_header.wave_format)
+ {
+ case WAVE_FORMAT_PCM:
+ break;
+ default:
+ shn_debug("while processing '%s': unsupported format 0x%04x (%s) - only PCM data is supported at this time",
+ this_shn->wave_header.filename,this_shn->wave_header.wave_format,shn_format_to_str(this_shn->wave_header.wave_format));
+ return 0;
+ }
+
+ this_shn->wave_header.channels = shn_uchar_to_ushort_le(this_shn->vars.header+cur);
+ cur += 2;
+ this_shn->wave_header.samples_per_sec = shn_uchar_to_ulong_le(this_shn->vars.header+cur);
+ cur += 4;
+ this_shn->wave_header.avg_bytes_per_sec = shn_uchar_to_ulong_le(this_shn->vars.header+cur);
+ cur += 4;
+ this_shn->wave_header.block_align = shn_uchar_to_ushort_le(this_shn->vars.header+cur);
+ cur += 2;
+ this_shn->wave_header.bits_per_sample = shn_uchar_to_ushort_le(this_shn->vars.header+cur);
+ cur += 2;
+
+ if (this_shn->wave_header.bits_per_sample != 8 && this_shn->wave_header.bits_per_sample != 16)
+ {
+ shn_debug("while processing '%s': bits per sample is neither 8 nor 16",this_shn->wave_header.filename);
+ return 0;
+ }
+
+ l -= 16;
+
+ if (l > 0)
+ cur += l;
+
+ for (;;)
+ {
+ cur += 4;
+
+ l = shn_uchar_to_ulong_le(this_shn->vars.header+cur);
+ cur += 4;
+
+ if (WAVE_DATA == shn_uchar_to_ulong_le(this_shn->vars.header+cur-8))
+ break;
+
+ cur += l;
+ }
+
+ this_shn->wave_header.rate = ((uint)this_shn->wave_header.samples_per_sec *
+ (uint)this_shn->wave_header.channels *
+ (uint)this_shn->wave_header.bits_per_sample) / 8;
+ this_shn->wave_header.header_size = cur;
+ this_shn->wave_header.data_size = l;
+ this_shn->wave_header.total_size = this_shn->wave_header.chunk_size + 8;
+ this_shn->wave_header.length = this_shn->wave_header.data_size / this_shn->wave_header.rate;
+ this_shn->wave_header.exact_length = (double)this_shn->wave_header.data_size / (double)this_shn->wave_header.rate;
+
+ if (this_shn->wave_header.channels == CD_CHANNELS &&
+ this_shn->wave_header.bits_per_sample == CD_BITS_PER_SAMPLE &&
+ this_shn->wave_header.samples_per_sec == CD_SAMPLES_PER_SEC &&
+ this_shn->wave_header.avg_bytes_per_sec == CD_RATE &&
+ this_shn->wave_header.rate == CD_RATE)
+ {
+ if (this_shn->wave_header.data_size < CD_MIN_BURNABLE_SIZE)
+ this_shn->wave_header.problems |= PROBLEM_CD_BUT_TOO_SHORT;
+ if (this_shn->wave_header.data_size % CD_BLOCK_SIZE != 0)
+ this_shn->wave_header.problems |= PROBLEM_CD_BUT_BAD_BOUND;
+ }
+ else
+ this_shn->wave_header.problems |= PROBLEM_NOT_CD_QUALITY;
+
+ if (this_shn->wave_header.header_size != CANONICAL_HEADER_SIZE)
+ this_shn->wave_header.problems |= PROBLEM_HEADER_NOT_CANONICAL;
+
+ if ((ulong)this_shn->wave_header.header_size + this_shn->wave_header.data_size > this_shn->wave_header.total_size)
+ this_shn->wave_header.problems |= PROBLEM_HEADER_INCONSISTENT;
+
+ if ((ulong)this_shn->wave_header.header_size + this_shn->wave_header.data_size < this_shn->wave_header.total_size)
+ this_shn->wave_header.problems |= PROBLEM_EXTRA_CHUNKS;
+
+ shn_length_to_str(this_shn);
+
+ /* header looks ok */
+ return 1;
+}
+
+char *shn_format_to_str(ushort format)
+{
+ switch (format) {
+ case WAVE_FORMAT_UNKNOWN:
+ return "Microsoft Official Unknown";
+ case WAVE_FORMAT_PCM:
+ return "Microsoft PCM";
+ case WAVE_FORMAT_ADPCM:
+ return "Microsoft ADPCM";
+ case WAVE_FORMAT_IEEE_FLOAT:
+ return "IEEE Float";
+ case WAVE_FORMAT_ALAW:
+ return "Microsoft A-law";
+ case WAVE_FORMAT_MULAW:
+ return "Microsoft U-law";
+ case WAVE_FORMAT_OKI_ADPCM:
+ return "OKI ADPCM format";
+ case WAVE_FORMAT_IMA_ADPCM:
+ return "IMA ADPCM";
+ case WAVE_FORMAT_DIGISTD:
+ return "Digistd format";
+ case WAVE_FORMAT_DIGIFIX:
+ return "Digifix format";
+ case WAVE_FORMAT_DOLBY_AC2:
+ return "Dolby AC2";
+ case WAVE_FORMAT_GSM610:
+ return "GSM 6.10";
+ case WAVE_FORMAT_ROCKWELL_ADPCM:
+ return "Rockwell ADPCM";
+ case WAVE_FORMAT_ROCKWELL_DIGITALK:
+ return "Rockwell DIGITALK";
+ case WAVE_FORMAT_G721_ADPCM:
+ return "G.721 ADPCM";
+ case WAVE_FORMAT_G728_CELP:
+ return "G.728 CELP";
+ case WAVE_FORMAT_MPEG:
+ return "MPEG";
+ case WAVE_FORMAT_MPEGLAYER3:
+ return "MPEG Layer 3";
+ case WAVE_FORMAT_G726_ADPCM:
+ return "G.726 ADPCM";
+ case WAVE_FORMAT_G722_ADPCM:
+ return "G.722 ADPCM";
+ }
+ return "Unknown";
+}