From 7088867879c41de81e485f79f60cd2bb08d456d1 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Sun, 20 Jun 2010 20:29:25 +0200 Subject: initial port of xmms-shorten plugin no gapless playback, no configuration, no sample-accurate seeking --- plugins/shn/AUTHORS | 2 + plugins/shn/LICENSE.shorten | 20 + plugins/shn/Makefile.am | 10 + plugins/shn/README | 355 +++++++++ plugins/shn/array.c | 41 + plugins/shn/bitshift.h | 33 + plugins/shn/convert.c | 42 + plugins/shn/misc.c | 151 ++++ plugins/shn/output.c | 99 +++ plugins/shn/seek.c | 274 +++++++ plugins/shn/shn.c | 1798 +++++++++++++++++++++++++++++++++++++++++++ plugins/shn/shn.h | 277 +++++++ plugins/shn/shorten.c | 54 ++ plugins/shn/shorten.h | 222 ++++++ plugins/shn/sulawalaw.c | 192 +++++ plugins/shn/vario.c | 146 ++++ plugins/shn/wave.c | 264 +++++++ 17 files changed, 3980 insertions(+) create mode 100644 plugins/shn/AUTHORS create mode 100644 plugins/shn/LICENSE.shorten create mode 100644 plugins/shn/Makefile.am create mode 100644 plugins/shn/README create mode 100644 plugins/shn/array.c create mode 100644 plugins/shn/bitshift.h create mode 100644 plugins/shn/convert.c create mode 100644 plugins/shn/misc.c create mode 100644 plugins/shn/output.c create mode 100644 plugins/shn/seek.c create mode 100644 plugins/shn/shn.c create mode 100644 plugins/shn/shn.h create mode 100644 plugins/shn/shorten.c create mode 100644 plugins/shn/shorten.h create mode 100644 plugins/shn/sulawalaw.c create mode 100644 plugins/shn/vario.c create mode 100644 plugins/shn/wave.c (limited to 'plugins/shn') 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 +deadbeef port: Alexey Yakovenko 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 + + +================== +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 +#include +#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 + * + * 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 +#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 + * + * 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 +#include +#include +#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;pwave_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 + * + * 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 +#include +#include +#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 + * + * 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 +#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 + + 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 +#include +#include +#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;ivars.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;inchan;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;jnmean);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;inchan;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;jnmean);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;ivars.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 + * + * 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 +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#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 +#include + +#ifdef HAVE_INTTYPES_H +# include +#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 +#include +#include +#include +#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 +#include +#include +#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 + * + * 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 +#include +#include +#include +#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"; +} -- cgit v1.2.3