/* * Musepack audio compression * Copyright (C) 1999-2004 Buschmann/Klemm/Piecha/Wolf * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // // Still to do: // // * documentation (need a reader to find difficult to understand comments or unclear code // * removal of the huge amount of global variables // * multithreading support // * fast forward using skip information instead of decoding // * Makefile dependencies are broken // * lame --alt-preset xxx - outputfile.mp3 (mit xxx = 80, 103, 132) #include #include #include #include "mppdec.h" // global variables (ugly, not killed yet) Quant_t Q [32]; // quantized matrixed subband samples FloatArray Y_L [36]; // scaled dematrixed subband samples FloatArray Y_R [36]; Float V_L [1024 + 64*(VIRT_SHIFT-1)]; // 1st stage of the subband synthesizer Float V_R [1024 + 64*(VIRT_SHIFT-1)]; CPair_t SCF_Index [3] [32]; // scale factors for transforming Q -> Y CPair_t Res [32]; // sample resolution for decoding Bitstream -> Q CPair_t SCFI [32]; // grouping of SCF, needed for SCF decoding Bool_t MS_Band [32]; // dematrixing information for transforming Q -> Y static Int V_L_offset; static Int V_R_offset; Bool_t MS_used = 0; // 0: all is LR coded, 1: MS or LR coding per subband Bool_t IS_used = 0; // is IS used (if yes, a fixed number of subbands is IS coded) static Float Scale = 1.; // user defined scale factor static Bool_t ClipPrev = 0; // if set, clipping is prevented if needed information is available static int ReplayGainType = 0; // 0: no additional gain, 1: CD based gain correction, 2: title based gain correction Bool_t TrueGaplessPresent = 0; // is true gapless used? Int LastValidSamples = 0; // number of valid samples within last frame unsigned int SampleFreq = 44100; static const Uint16_t sftable [4] = { 44100, 48000, 37800, 32000 }; #if defined MAKE_16BIT || defined MAKE_24BIT || defined MAKE_32BIT static int Bits = SAMPLE_SIZE; static int NoiseShapeType = 0; static Float Dither = -1; // Dithering if not Noise shaping #endif static Uint StreamVersion; static Uint Blockgroesse; static Ulong InputBuffRead; #ifdef USE_ASM static SyntheseFilter16_t Synthese_Filter_16; #endif const char About [] = "MPC Decoder " MAX_SV " " MPPDEC_VERSION " " BUILD " " COPYRIGHT; const char CompileFlags [] = COMPILER_FLAGS; const char Date [] = __DATE__ " " __TIME__; Bool_t output_endianess = LITTLE; static void usage ( void ) { stderr_printf ( "\n" "\x1B[1m\rusage:\n" "\x1B[0m\r "PROG_NAME" [--options] \n" " "PROG_NAME" [--options] \n" " "PROG_NAME" [--options] \n" "\n" "\x1B[1m\roptions:\n" "\x1B[0m\r --start x start decoding at x sec (x>0) or at |x|%% of the file (x<0)\n" " --dur x decode a sequence of x sec duration (dflt: 100%%)\n" " --prev activate clipping prevention (gain=0,2:title based; 1,3:album based)\n" " --noprev deactivate clipping prevention (dflt)\n" " --scale x additional scale signal by x (dflt: 1)\n" " --gain x replay gain control (0,1:off (dflt), 2:title, 3:album)\n" " --silent no messages to the terminal\n" " --wav write Microsoft's WAVE file (dflt)\n" " --aiff write Apple's AIFF file\n" " --raw write RAW file in native byte order\n" " --raw-le write RAW file in little endian byte order\n" " --raw-be write RAW file in big endian byte order\n" " --random random play order (don't use options after this one)\n" #if defined MAKE_16BIT || defined MAKE_24BIT || defined MAKE_32BIT " --bits x output with x bits (dflt: " STR(SAMPLE_SIZE) ")\n" " --dither x dithering factor (dflt: auto, useful 0.00...1.00)\n" " --shape x set shaping type (0:off (dflt), 1:light, 2:medium, 3:heavy)\n" #endif "\n" "\x1B[1m\rspecial files:\n" "\x1B[0m\r - standard input or standard output\n" " /dev/null device null, the trash can\n" #ifdef USE_OSS_AUDIO " /dev/dsp* use Open Sound System (OSS)" SAMPLE_SIZE_STRING "\n" #endif #ifdef USE_ESD_AUDIO " /dev/esd use Enlightenment Sound Daemon (EsounD)" SAMPLE_SIZE_STRING "\n" #endif #ifdef USE_SUN_AUDIO " /dev/audio use Sun Onboard-Audio" SAMPLE_SIZE_STRING "\n" #endif #ifdef USE_IRIX_AUDIO " /dev/audio use SGI IRIX Onboard-Audio" SAMPLE_SIZE_STRING "\n" #endif #ifdef USE_WIN_AUDIO " /dev/audio use Windows WAVEOUT Audio" SAMPLE_SIZE_STRING "\n" #endif #ifdef USE_HTTP " protocol://[username[:password]@]servername[:port]/directories/file\n" " file addressed via URL; username, password and port are optional,\n" " protocol can be ftp, http, rtp. servername can be DNS, IP4 or IP6\n" // other interesting protocols are file:// und https:// #endif "\n" "\x1B[1m\rexamples:\n" "\x1B[0m\r "PROG_NAME" Overtune.mpc Overtune.wav\n" #if PATH_SEP == '/' " "PROG_NAME" \"/Archive/Rossini/Wilhelm Tell -- [01] Overtune.mpc\" Overtune.wav\n" " "PROG_NAME" \"/Archive/Rossini/*.mpc\" - | wavplay -\n" " "PROG_NAME" --start -50%% --duration -5%% *.mpc - | wavplay -\n" " "PROG_NAME" --prev *.mp+ . && cdrecord -v -dao dev=sony -audio *.wav\n" #else " cd \\Archive\\Rossini; "PROG_NAME" \"Wilhelm Tell -- [01] Overtune.mpc\" Overtune.wav\n" " cd \\Archive\\Rossini; "PROG_NAME" *.mpc - | wavplay -\n" " "PROG_NAME" --start -50%% --duration -5%% *.mpc - | wavplay -\n" " "PROG_NAME" --prev *.mp+ . ; cdrecord -v -dao dev=sony -audio *.wav\n" #endif #if defined MAKE_16BIT || defined MAKE_24BIT || defined MAKE_32BIT " "PROG_NAME" --bits " STR(SAMPLE_SIZE) " --shape 2 *.mpc .\n" #endif #ifdef USE_OSS_AUDIO " "PROG_NAME" */*.mpc /dev/dsp ; "PROG_NAME" */*.mpc /dev/dsp1\n" #endif #ifdef USE_SUN_AUDIO " "PROG_NAME" */*.mpc /dev/audio\n" #endif #ifdef USE_IRIX_AUDIO " "PROG_NAME" */*.mpc /dev/audio\n" #endif #ifdef USE_WIN_AUDIO " "PROG_NAME" *.mpc /dev/audio\n" #endif #ifdef USE_HTTP " "PROG_NAME" http://www.uni-jena.de/~pfk/mpp/audio/Maire_10bit_48kHz_Dithered.mpc\n" #endif #ifdef USE_ARGV # if PATH_SEP == '/' " "PROG_NAME" --gain 2 --prev --random /Archive/Audio/ /dev/audio\n" # else " "PROG_NAME" --gain 2 --prev --random C:\\AUDIO\\ D:\\AUDIO\\ /dev/audio\n" # endif #endif " "PROG_NAME" playlist.m3u /dev/audio\n" "\n" "For further information see the file \"MANUAL.TXT\".\n" ); } static const char* ProfileName ( Uint profile ) // profile is 0...15, where 1, 5...15 is used { static const char na [] = "n.a."; static const char* Names [] = { na, "Unstable/Experimental", na, na, na, "below 'Telephone'", "below 'Telephone'", "'Telephone'", "'Thumb'", "'Radio'", "'Standard'", "'Xtreme'", "'Insane'", "'BrainDead'", "above 'BrainDead'", "above 'BrainDead'" }; return profile >= sizeof(Names)/sizeof(*Names) ? na : Names [profile]; } /* * Print out the time to stderr with a precision of 10 ms always using * 12 characters. Time is represented by the sample count. An additional * prefix character (normally ' ' or '-') is prepended before the first * digit. */ static const char* Print_Time ( Ulong samples, char sgn ) { static char ret [16]; Ulong csec = (Ulong)(samples/(SampleFreq/100.)); Uint hour = (Uint) (csec/360000L); Uint min = (Uint) (csec/6000 % 60); Uint sec = (Uint) (csec/100 % 60); if ( hour > 9 ) sprintf ( ret, "%c%2u:%02u", sgn, hour, min ); else if ( hour > 0 ) sprintf ( ret, " %c%1u:%02u", sgn, hour, min ); else if ( min > 9 ) sprintf ( ret, " %c%2u", sgn, min ); else sprintf ( ret, " %c%1u", sgn, min ); sprintf ( ret+6, ":%02u.%02u", sec, (Uint)(csec % 100) ); return ret; } static double TestForGap ( Int2xSample_t* p, int valid ) // Rough estimation of bug error effect, too late starts can't be detected anytime { double sum1 = 2000; double sum2 = 2000; double tmp1; double tmp2; double ret; int i; for ( i = 0; i < valid; i++ ) { tmp1 = (double) p[i][0]*p[i][0] + (double) p[i][1]*p[i][1]; tmp1 = sqrt ( sqrt (tmp1) ); tmp2 = 1. + exp ( (i-240.) / 30. ); sum1 += tmp1; sum2 += tmp1 / tmp2; } // fprintf ( stderr, "\n\n******** %4u: %.0f %.0f\n", valid, sum1, sum2 ); ret = sum1 / sum2 - 0.99999999; ret = log (100 * ret); return ret; } static Uint32_t Decode ( FILE_T OutputFile, FILE_T InputFile, Uint32_t TotalFrames, Uint32_t Start, Uint32_t Duration ) { Int2xSample_t Stream [BLK_SIZE]; Ulong StartBitPos; size_t valid; size_t ring; Uint32_t FrameNo; Uint32_t ret = 0; Uint32_t CurrBlkSize = 0; time_t T = time (NULL); ENTER(3); // decode frame by frame, note some differences in decoding the last frame (TotalFrames-1) memset ( Stream, 0, sizeof(Stream) ); for ( FrameNo = 0; FrameNo < TotalFrames && Duration > 0; FrameNo++ ) { ring = InputCnt; REP ((printf ("\nFrame %lu\n", (Ulong)FrameNo), fflush (stdout))); // read skip information (designed for rewind/fast forward, but here used for checking purpose) if ( FrameNo % Blockgroesse == 0 ) CurrBlkSize = Bitstream_read (20); StartBitPos = BitsRead (); // decode bitstream (see decode.c) switch ( StreamVersion ) { case 0x04: case 0x05: case 0x06: Read_Bitstream_SV6 (); break; case 0x17: case 0x07: Read_Bitstream_SV7 (); break; #ifdef USE_SV8 case 8: Read_Bitstream_SV8 (); break; #endif default: assert (0); } REP (printf ("Frame end\n")); // check skip information against value determined by decoding (buggy for CBR) if ( (FrameNo+1) % Blockgroesse == 0 && BitsRead () - StartBitPos != CurrBlkSize ) if ( FrameNo != TotalFrames-1 || StreamVersion > 5 ) { if ( BitsRead() < InputBuffRead * (Ulong)(CHAR_BIT * sizeof(*InputBuff)) ) stderr_printf ("\n\n"PROG_NAME": broken frame %lu/%lu (decoded size=%lu, size in stream=%lu)\n\n", (Ulong)FrameNo, (Ulong)TotalFrames, (Ulong)(BitsRead () - StartBitPos), (Ulong)CurrBlkSize ); else stderr_printf ("\n\n"PROG_NAME": unexpected end of file after frame %lu/%lu\n\n", (Ulong)FrameNo, (Ulong)TotalFrames ); LEAVE(3); return ret; } // reload data if more than 50% of the buffer is decoded if ( (ring ^ InputCnt) & IBUFSIZE2 ) InputBuffRead += Read_LittleEndians ( InputFile, InputBuff + (ring & IBUFSIZE2), IBUFSIZE2 ); // Subband synthesizer if ( Start <= BLK_SIZE ) { if ( Scale != 0. ) { if (IS_used) { Requantize_MidSideStereo ( Min_Band-1, MS_Band ); Requantize_IntensityStereo ( Min_Band, Max_Band ); } else { Requantize_MidSideStereo ( Max_Band, MS_Band ); } Synthese_Filter ( (Int2xSample_t*)&Stream[0][0], &V_L_offset, V_L, Y_L, 0 ); Synthese_Filter ( (Int2xSample_t*)&Stream[0][1], &V_R_offset, V_R, Y_R, 1 ); } // write PCM data to destination (except the data in the last frame, this is done behind the for loop if ( FrameNo < TotalFrames-1 ) { valid = BLK_SIZE - Start; if ( valid > Duration ) valid = Duration; ret += Write_PCM ( OutputFile, Stream + Start, valid ); Duration -= valid; Start = 0; } } else { Start -= BLK_SIZE; } // output report if a real time second is over sinse the last report if ( (Int)(time (NULL) - T) >= 0 ) { T += 1; stderr_printf ("\r%s/", Print_Time ( Start ? Start : ret, (char)(Start ? '-' : ' ') ) ); stderr_printf ("%s %s (%4.1f%%)", Print_Time ( TotalFrames * BLK_SIZE, ' ') + 1, Start ? "jumping" : "decoded", 100.* FrameNo / TotalFrames ); } } // write PCM data to destination for the last frame if ( Duration > 0 ) { // reconstruct exact size for SV6 and higher (for SV4...5 this is unknown) switch ( StreamVersion ) { default: assert (0); case 0x04: case 0x05: valid = 0; break; case 0x06: case 0x07: case 0x17: #ifdef USE_SV8 case 0x08: #endif valid = (Int) Bitstream_read (11); if (valid == 0) valid = BLK_SIZE; // Old encoder writes a 0 instead of a 1152, Bugfix valid += DECODER_DELAY - Start; if ( valid > Duration ) valid = Duration; if ( Start + valid > BLK_SIZE ) { // write out data for the last frame if ( Start < BLK_SIZE ) { ret += Write_PCM ( OutputFile, Stream + Start, BLK_SIZE - Start ); valid -= BLK_SIZE - Start; Start = 0; } else { Start -= BLK_SIZE; } if ( ! TrueGaplessPresent ) { // due to the subband synthesizer latency there may up to 481 data samples still in the pipeline, synthesize it (this comment is wrong!) // is it better to clear or to leave the next two statements (???) memset ( Y_L, 0, sizeof Y_L ); memset ( Y_R, 0, sizeof Y_R ); } else { // new feature for true gapless encoding/decoding delivers the true // quantized values needed for gapless playback!! CurrBlkSize = Bitstream_read (20); StartBitPos = BitsRead (); Read_Bitstream_SV7 (); // check skip information against value determined by decoding (buggy for CBR) if ( BitsRead () - StartBitPos != CurrBlkSize ) { if ( BitsRead() < InputBuffRead * (Ulong)(CHAR_BIT * sizeof(*InputBuff)) ) stderr_printf ("\n\n"PROG_NAME": broken frame %lu/%lu (decoded size=%lu, size in stream=%lu)\n\n", (Ulong)FrameNo, (Ulong)TotalFrames, (Ulong)(BitsRead () - StartBitPos), (Ulong)CurrBlkSize ); else stderr_printf ("\n\n"PROG_NAME": unexpected end of file after frame %lu/%lu\n\n", (Ulong)FrameNo, (Ulong)TotalFrames ); LEAVE(3); return ret; } if ( Scale != 0. ) { if (IS_used) { Requantize_MidSideStereo ( Min_Band-1, MS_Band ); Requantize_IntensityStereo ( Min_Band, Max_Band ); } else { Requantize_MidSideStereo ( Max_Band, MS_Band ); } } } Synthese_Filter ( (Int2xSample_t*)&Stream[0][0], &V_L_offset, V_L, Y_L, 0 ); Synthese_Filter ( (Int2xSample_t*)&Stream[0][1], &V_R_offset, V_R, Y_R, 1 ); } break; } // write PCM data to destination from the "very last" frame ret += Write_PCM ( OutputFile, Stream + Start, valid ); } // time report stderr_printf ("\r%s", Print_Time ( ret, (char)' ' ) ); LEAVE(3); return ret; } // 0: No ID3v2 tag // >0: ID2v2 tag with this length static Int32_t JumpID3v2 ( void ) { Int32_t ret = 10; if ( (Bitstream_read (32) & 0xFFFFFF) != 0x334449L ) return 0; if ( Bitstream_read (1) ) return 0; ret += Bitstream_read (7) << 14; if ( Bitstream_read (1) ) return 0; ret += Bitstream_read (7) << 21; Bitstream_read (1); if ( Bitstream_read (1) ) ret += 10; Bitstream_read (14); Bitstream_read (16); if ( Bitstream_read (1) ) return 0; ret += Bitstream_read (7) << 0; if ( Bitstream_read (1) ) return 0; ret += Bitstream_read (7) << 7; return ret; } const char* EncoderName ( int encoderno ) { static char Name [32]; if ( encoderno <= 0 ) Name [0] = '\0'; else if ( encoderno % 10 == 0 ) sprintf ( Name, " (Release %u.%u)", encoderno/100, encoderno/10%10 ); else if ( (encoderno & 1) == 0 ) sprintf ( Name, " (Beta %u.%02u)", encoderno/100, encoderno%100 ); else sprintf ( Name, " (--Alpha-- %u.%02u)", encoderno/100, encoderno%100 ); return Name; } /**************************** Decode a file *****************************/ static Ulong DecodeFile ( FILE_T OutputFile, FILE_T InputFile, Double Start, Double Duration ) { Int MaxBandDesired = 0; Uint32_t TotalFrames = 0; Ulong DecodedSamples = 0; Uint Profile = (Uint)-1; Double AverageBitrate; TagInfo_t taginfo; clock_t T; Int32_t ID3v2; Uint16_t PeakTitle = 0; Uint16_t PeakAlbum = 0; Uint16_t Peak; Uint16_t tmp; Int16_t GainTitle = 0; Int16_t GainAlbum = 0; Int16_t Gain; int Encoder; Bool_t SecurePeakTitle = 0; Float ReplayGain; // 0...1...+oo Float ClipCorr; // 0...1 ENTER(2); // Fill the bitstream buffer for the first time resume: Bitstream_init (); InputBuffRead = Read_LittleEndians ( InputFile, InputBuff, IBUFSIZE ); // Test the first 4 bytes ("MP+": SV7+, "ID3": ID3V2, other: may be SV4...6) switch ( Bitstream_preview(32) ) { case (Uint32_t)0x01334449L: /* ID3 V2.1...2.4 */ case (Uint32_t)0x02334449L: case (Uint32_t)0x03334449L: case (Uint32_t)0x04334449L: stderr_printf ("\n"PROG_NAME": Stream was corrupted by an ID3 Version 2 tagger\n\n" ); ID3v2 = JumpID3v2 (); if ( SEEK ( InputFile, ID3v2, SEEK_SET ) < 0 ) { stderr_printf ( "\n\nSorry, recovering fails.\n\a" ); return 0; } sleep (1); stderr_printf ("\b\b\b\b, ignore %lu words and %u bits ...\n\a", (unsigned long)ID3v2 >> 2, (int)(ID3v2&3) << 3 ); goto resume; case (Uint32_t)0x072B504DL: /* MP+ SV7 */ case (Uint32_t)0x172B504DL: /* MP+ SV7.1 */ #ifdef USE_SV8 case (Uint32_t)0x082B504DL: /* MP+ SV8 */ #endif StreamVersion = (Int) Bitstream_read (8); if ( (Uint)StreamVersion < 7 ) { stderr_printf ("\n"PROG_NAME": StreamVersion 7+ like header with SV0...6\n" ); return 0; } (void) Bitstream_read (24); break; case (Uint32_t)0x2043414DL: /* MAC */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "Monkey's Audio" ); return 0; case (Uint32_t)0x7961722E: /* Real Audio */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "Real Audio" ); return 0; case (Uint32_t)0x46464952L: /* WAV */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "Microsoft WAVE" ); return 0; case (Uint32_t)0x43614C66L: /* FLAC */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "FLAC" ); return 0; case (Uint32_t)0x4341504CL: /* LPAC */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "LPAC" ); return 0; case (Uint32_t)0x37414B52L: /* RKAU */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "RKAU" ); return 0; case (Uint32_t)0x676B6A61L: /* Shorten */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "Shorten" ); return 0; case (Uint32_t)0x040A5A53L: /* SZIP 1.12 */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "szip" ); return 0; case (Uint32_t)0x5367674FL: /* OggS */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "Ogg Stream" ); return 0; case (Uint32_t)0x46494441L: /* AAC-ADIF */ stderr_printf ("\n"PROG_NAME": Input File is a %s file\n", "AAC Audio Data Interchange Format" ); return 0; default: StreamVersion = (Uint32_t) Bitstream_preview(32); if ( ( StreamVersion & 0x00FFFFFF ) == (Uint32_t)0x002B504DL ) { StreamVersion >>= 24; if ( StreamVersion >= 0x72 ) { stderr_printf ( "\n"PROG_NAME": Input File seems to be a MPC file StreamVersion %u.%u\nVisit http://www.uni-jena.de/~pfk/mpc/ and update your software.\n\n", StreamVersion & 15, StreamVersion >> 4 ); return 0; } } StreamVersion = (Int)(Bitstream_preview(21) & 0x3FF); if ( StreamVersion < 4 || StreamVersion > 6 ) { stderr_printf ("\n"PROG_NAME": Input File is not a MPC file, neither SV 4...6 nor SV 7 (SV %u)\n", StreamVersion ); return 0; } break; } // decode the header for SV4...6 or SV7 or SV8 switch ( StreamVersion ) { case 0x04: case 0x05: case 0x06: Bitrate = (Int) Bitstream_read (9); IS_used = (Int) Bitstream_read (1); MS_used = (Int) Bitstream_read (1); StreamVersion = (Int) Bitstream_read(10); MaxBandDesired = (Int) Bitstream_read (5); Blockgroesse = (Int) Bitstream_read (6); TotalFrames = Bitstream_read (StreamVersion < 5 ? 16 : 32); SampleFreq = 44100; Encoder = -1; if ( StreamVersion >= 4 && StreamVersion <= 6 ) break; default: // it should be impossible to execute the following code stderr_printf ("\n"PROG_NAME": Internal error\n" ); stderr_printf ("\n"PROG_NAME": Not a MPC file, neither SV 4...6 nor SV 7 (SV %u.%u)\n", StreamVersion & 15, StreamVersion >> 4 ); return 0; case 0x07: case 0x17: #ifdef USE_SV8 case 0x08: #endif Bitrate = 0; Blockgroesse = 1; TotalFrames = Bitstream_read (32); IS_used = (Int) Bitstream_read (1); MS_used = (Int) Bitstream_read (1); MaxBandDesired = (Int) Bitstream_read (6); // reading the profile Profile = (Int) Bitstream_read (4); (void) Bitstream_read ( 2); SampleFreq = sftable [ Bitstream_read ( 2) ]; // reading peak and gain values from the file (or use useful values if they are absent) PeakTitle = (Uint16_t)(1.18 * (Uint32_t)Bitstream_read (16)); GainTitle = Bitstream_read (16); tmp = Bitstream_read (16); if ( SecurePeakTitle = (tmp != 0) ) PeakTitle = tmp; GainAlbum = Bitstream_read (16); PeakAlbum = Bitstream_read (16); if ( PeakAlbum == 0 ) PeakAlbum = PeakTitle; // reading true gapless TrueGaplessPresent = Bitstream_read ( 1); LastValidSamples = Bitstream_read (11); (void) Bitstream_read (20); // reserved bytes for future use Encoder = Bitstream_read ( 8); break; } // check for ID3V1(.1) tags or APE tags, output information if available if ( Read_ID3V1_Tags ( InputFile, &taginfo ) || Read_APE_Tags ( InputFile, &taginfo ) ) { stderr_printf ("\n %s: %s ", taginfo.Artist, taginfo.Album ); stderr_printf ( taginfo.Genre[0] != '?' || taginfo.Year[0] != '\0' ? " (" : "" ); stderr_printf ( taginfo.Genre[0] == '?' ? "%0.0s" : "%s", taginfo.Genre ); stderr_printf ( taginfo.Genre[0] != '?' && taginfo.Year[0] ? ", " : "" ); stderr_printf ( "%s", taginfo.Year ); stderr_printf ( taginfo.Genre[0] != '?' || taginfo.Year[0] != '\0' ? ")\n" : "\n" ); stderr_printf (" %s %s", taginfo.Track, taginfo.Title ); stderr_printf ( taginfo.Comment[0] != '\0' ? " (%s)\n" : "%s\n", taginfo.Comment ); } // calculate bitrate for informational purpose if ( Bitrate == 0 ) { AverageBitrate = ( SampleFreq / 1000. / BLK_SIZE * 8 ) * taginfo.FileSize / TotalFrames ; } else { AverageBitrate = Bitrate; } stderr_printf ("\n"); if ( AverageBitrate > 0. ) stderr_printf ("%7.1f kbps,", AverageBitrate ); // Output total time, Streamversion, Profile stderr_printf ("%s, SV %u.%u, Profile %s%s", Print_Time (TotalFrames * BLK_SIZE, ' ') + 1, StreamVersion & 15, StreamVersion >> 4, ProfileName(Profile), EncoderName(Encoder) ); // Choose the select type of Peak and Gain values switch ( ReplayGainType ) { case 0: // no replay gain, use title peak for clipping prevention Gain = 0; Peak = PeakTitle; break; case 1: // no replay gain, use album peak for clipping prevention Gain = 0; Peak = PeakAlbum; break; default: // title replay gain Gain = GainTitle; Peak = PeakTitle; break; case 3: // album replay gain Gain = GainAlbum; Peak = PeakAlbum; break; } // calculate the multiplier from the original integer peak and gain data ReplayGain = (Float) exp ( (M_LN10/2000.) * (Int16_t)Gain ); ClipCorr = (Float) (32767. / ( (Uint32_t)Peak + 1 )); // avoid divide by 0 // Perform or not perform clipping prevention, that is here the question if ( ClipPrev ) { stderr_printf (", ClipDamp " ); if ( Peak == 0 ) { stderr_printf ("1 ???" ); ClipCorr = 1.f; } else if ( ReplayGain * fabs(Scale) > ClipCorr ) { stderr_printf (".%04d%s", (int)(1.e4 * ClipCorr / (ReplayGain * Scale) + 0.5), SecurePeakTitle ? "" : "?" ); ClipCorr = ClipCorr / (ReplayGain * Scale); } else { stderr_printf ("1%s", SecurePeakTitle ? "" : "?" ); ClipCorr = 1.f; } } else { ClipCorr = 1.f; } // report replay gain if != 1. if ( ReplayGain != 1. ) stderr_printf (", Gain %.4f", ReplayGain ); stderr_printf ("\n\n"); // init subband structure (MaxBandDesired, StreamVersion, bitrate) and Scale factors Init_QuantTab ( MaxBandDesired, IS_used, ClipCorr * ReplayGain * Scale, StreamVersion ); // calculate Start and Duration if they are given in precent as negative values if ( Start < 0. ) Start *= TotalFrames * -(0.01 * BLK_SIZE / SampleFreq); if ( Duration < 0. ) Duration *= TotalFrames * -(0.01 * BLK_SIZE / SampleFreq); // reset arrays to avoid HF noise if recent MaxBand > current MaxBand memset ( Y_L, 0, sizeof(Y_L) ); memset ( Y_R, 0, sizeof(Y_R) ); memset ( Q , 0, sizeof(Q ) ); memset ( V_L, 0, sizeof(V_L) ); memset ( V_R, 0, sizeof(V_R) ); V_L_offset = V_R_offset = 0; // decoding kernel with time measurement T = clock (); DecodedSamples = Decode ( OutputFile, InputFile, TotalFrames, (Uint32_t) (Start * (double)SampleFreq + 0.5) + DECODER_DELAY, Duration <= 0. || Duration > 0xFFFFFFFFL/SampleFreq ? (Uint32_t)0xFFFFFFFFL : (Uint32_t)(Duration * (double)SampleFreq + 0.5) ); T = clock () - T; #ifdef __TURBOC__ // wraps around at midnight if ( (Long)T < 0 ) { T += (time_t) (86400. * CLOCKS_PER_SEC); } #endif // output at the end of a decoded title (void) stderr_printf (" (runtime: %.2f s speed: %.2fx)\n", (Double) (T * (1. / CLOCKS_PER_SEC )), T ? (Double) ((CLOCKS_PER_SEC/(Float)SampleFreq) * DecodedSamples / T) : (Double)0. ); LEAVE(2); return DecodedSamples; } /* * LAME and numerous decoders only append a '.wav' to make it harder to overwrite the binary source * It will probably be changed if i can think of a better mechanism */ // Create an useful output name for automatic output file mode (destination is only a directory name) // The destination directory and the file name is merged together with the new file extentions. // This gives file names as directory/filename.mpc.wav which can be distinguish from the // original .wav file. This is the way most programs to it (including "Lame"). static char* Create_Extention ( const char* Path, const char* Name, const char* Extention ) { static char ret [PATHLEN_MAX + 3]; char* p = strrchr ( Name, PATH_SEP ); if ( p != NULL ) Name = p + 1; if ( strlen(Path) + strlen(Name) + strlen(Extention) > sizeof(ret) - 3 ) { stderr_printf (PROG_NAME":\tTarget buffer for new file name too short,\n\tincrease PATHLEN_MAX and recompile\n\a" ); exit (4); } sprintf ( ret, "%s%c%s.%s", Path, PATH_SEP, Name, Extention ); return ret; } // It should refuse to output WAV-content into existing '.mp?'-files, // in this case the file shouldn't be the output file, // but an (additional) input file, and all should be written to . // This is non-orthogonal and dirty, but it should prevent quite some data loss. // // Maybe it should only overwrite files if it's stated by '--forceoverwrite', // got to think about that some more. static Int Unintended_OutputFile ( const char* Name ) { FILE_T fp; char buff [4]; #ifdef USE_HTTP if ( 0 == strncasecmp (Name, "http://", 7) || 0 == strncasecmp (Name, "ftp://", 6) ) return 1; #endif if ( (fp = OPEN ( Name )) == INVALID_FILEDESC ) return 0; READ ( fp, buff, 4 ); CLOSE (fp); if ( memcmp (buff, "MP+\007", 4) == 0 ) return 1; if ( strlen (Name) <= 4 ) return 0; Name += strlen (Name) - 4; if ( Name[0] == '.' && (Name[1] & 0xDF) == 'M' && (Name[2] & 0xDF) == 'P' ) return 1; if ( Name[0] == '.' && (Name[1] & 0xDF) == 'M' && Name[2] == '3' && (Name[3] & 0xDF) == 'U' ) return 1; if ( Name[0] == '.' && (Name[1] & 0xDF) == 'P' && Name[2] == 'A' && (Name[3] & 0xDF) == 'C' ) return 1; if ( Name[0] == '.' && (Name[1] & 0xDF) == 'A' && Name[2] == 'P' && (Name[3] & 0xDF) == 'E' ) return 1; if ( Name[0] == '.' && (Name[1] & 0xDF) == 'O' && Name[2] == 'F' && (Name[3] & 0xDF) == 'R' ) return 1; if ( Name[0] == '.' && (Name[1] & 0xDF) == 'R' && Name[2] == 'K' && (Name[3] & 0xDF) == 'A' ) return 1; if ( Name[3] == PATH_SEP ) return 1; return 0; } enum NameMode_t { automode, nullmode, filemode, filemode_noinit, pipemode, pipemode_noinit, dspmode , dspmode_noinit , esdmode , esdmode_noinit , sunmode , sunmode_noinit , winmode , winmode_noinit , irixmode, irixmode_noinit, }; /**************************** main interpreter loop ******************/ static void randomize ( const char** argv ) { int argc; int i; int j; const char* tmp; for ( argc = 0; argv[argc] != NULL; argc++ ) ; srand ( time (NULL) ); for ( i = 0; i < argc; i++ ) { j = rand() % argc; tmp = argv[i]; argv[i] = argv[j]; argv[j] = tmp; } } static int hexdigit ( const char s ) { if ( (unsigned char)(s-'0') < 10u ) return s-'0'; if ( (unsigned char)(s-'A') < 6u ) return s-'A'+10; return -1; } static void decode_html ( FILE_T fp, const char* src ) { char ch; if ( GetStderrSilent () ) return; for ( ; src[0] != '\0' ; src++) { if ( src[0] == '_' ) WRITE (fp, " ", 1 ); else if ( src[0] != '%' || hexdigit(src[1]) < 0 || hexdigit(src[2]) < 0 ) { WRITE ( fp, src, 1); } else { ch = hexdigit(src[1]) * 16 + hexdigit(src[2]), src += 2; WRITE ( fp, &ch, 1 ); } } } static void Analyze_fs ( const char* filename ) { FILE_T f = OPEN (filename); unsigned char buff [28]; int bytes; SampleFreq = 44100; if ( f == INVALID_FILEDESC ) return; bytes = READ ( f, buff, sizeof buff ); CLOSE (f); if ( sizeof buff != bytes ) return; if ( buff[0] != 'M' || buff[1] != 'P' || buff[2] != '+' ) return; SampleFreq = sftable [ buff[10] & 3 ]; } static int mainloop ( int argc, char** argv ) { enum NameMode_t OutputMode; FILE_T InputFile; FILE_T OutputFile; const char* OutputName; const char* OutputDir; const char* OutputComment = ""; Double Start = 0.; // Starting time when > 0 Double Duration = 0.; // length to decode when > 0 Ulong DecodedSamples = 0; #ifdef USE_ESD_AUDIO int ESDFileHandle; #endif HeaderWriter_t HeaderWriter = Write_WAVE_Header; const char* OutputFileExt = "wav"; const char* arg; // take care of the output file (got to think all this over) OutputName = argv [argc-1]; if ( 0 == strcmp (OutputName, "-") || 0 == strcmp (OutputName, "/dev/stdout") ) { if ( argc > 2 || ISATTY (FILENO(STDIN)) ) argv [argc-1] = NULL; pipe: if ( ISATTY (FILENO (STDOUT)) ) { argc++; #if defined USE_OSS_AUDIO OutputName = "/dev/audio"; goto ossjump; #elif defined USE_ESD_AUDIO goto esdjump; #elif defined USE_SUN_AUDIO OutputName = "/dev/audio"; goto sunjump; #elif defined USE_IRIX_AUDIO OutputName = "/dev/audio"; goto irixjump; #elif defined USE_WIN_AUDIO OutputName = "/dev/audio"; goto winjump; #endif } OutputMode = pipemode_noinit; OutputName = "/dev/stdout"; } else if ( 0 == strcmp (OutputName, "/dev/null") ) { OutputMode = nullmode; OutputFile = NULL_FD; OutputComment = " (Null Device)"; argv [argc-1] = NULL; } #ifdef USE_SUN_AUDIO else if ( 0 == strcmp (OutputName, "/dev/audio") ) { sunjump: OutputMode = sunmode_noinit; argv [argc-1] = NULL; } #endif /* USE_SUN_AUDIO */ #ifdef USE_IRIX_AUDIO else if ( 0 == strcmp (OutputName, "/dev/audio") ) { irixjump: OutputMode = irixmode_noinit; argv [argc-1] = NULL; } #endif /* USE_IRIX_AUDIO */ #ifdef USE_ESD_AUDIO else if ( 0 == strcmp (OutputName, "/dev/esd") ) { esdjump: OutputMode = esdmode_noinit; output_endianess = ENDIAN == HAVE_LITTLE_ENDIAN ? LITTLE : BIG; argv [argc-1] = NULL; } #endif /* USE_ESD_AUDIO */ #ifdef USE_OSS_AUDIO else if ( 0 == strncmp (OutputName, "/dev/", 5) ) { ossjump: OutputMode = dspmode_noinit; argv [argc-1] = NULL; } #endif /* USE_OSS_AUDIO */ #ifdef USE_WIN_AUDIO else if ( 0 == strcmp (OutputName, "/dev/audio") ) { winjump: OutputMode = winmode_noinit; argv [argc-1] = NULL; } #endif /* USE_WIN_AUDIO */ else if ( Unintended_OutputFile (OutputName) ) { goto pipe; } else { OutputMode = filemode_noinit; if ( (OutputFile = CREATE ( OutputName )) != INVALID_FILEDESC ) { UNBUFFER ( OutputFile ); argv [argc-1] = NULL; } else if ( isdir ( OutputName ) ) { OutputMode = automode; OutputFile = INVALID_FILEDESC; OutputDir = OutputName; argv [argc-1] = NULL; } else { stderr_printf ("\n"PROG_NAME": Can't create output file '%s': %s\n", OutputName, strerror(errno) ); return 3; } } while ( *++argv != NULL ) { // decode options if ( argv[0][0] == '-' && argv[0][1] == '-' ) { arg = argv[0] + 2; if ( 0 == strncmp (arg, "random", 3) ) { randomize ( argv + 1 ); continue; } else if ( 0 == strncmp (arg, "start", 2) || 0 == strncmp (arg, "skip", 2) ) { // Start if ( *++argv == NULL ) { stderr_printf ("\n"PROG_NAME": --%s x?\n\n", "start" ); return 1; } else { if ( 0 == strncmp (*argv, "mid", 1) ) Start = -50.f; // 50%, negative values are percentages else Start = atof (*argv); continue; } } else if ( 0 == strncmp (arg, "duration", 2) ) { // Duration if ( *++argv == NULL ) { stderr_printf ("\n"PROG_NAME": --%s x?\n\n", "duration" ); return 1; } else { Duration = atof (*argv); continue; } } else if ( 0 == strncmp (arg, "scale", 2) ) { // Level scaling if ( *++argv == NULL ) { stderr_printf ("\n"PROG_NAME": --%s x?\n\n", "scale" ); return 1; } else { Scale = (Float) atof (*argv); continue; } } else if ( 0 == strncmp (arg, "noprev", 3) || 0 == strncmp (arg, "noclip", 3) ) { // Clipping prevention disabled ClipPrev = 0; continue; } else if ( 0 == strncmp (arg, "prev" , 1) || 0 == strncmp (arg, "clip" , 1) ) { // Clipping prevention enabled ClipPrev = 1; continue; } else if ( 0 == strncmp (arg, "gain", 1) ) { // Disable replay gain if ( *++argv == NULL ) { stderr_printf ("\n"PROG_NAME": --%s x?\n\n", "gain" ); return 1; } else { ReplayGainType = atoi (*argv); continue; } } else if ( 0 == strncmp (arg, "silent", 2) || 0 == strncmp (arg, "quiet", 1) ) { // Disable display SetStderrSilent (1); continue; } #if defined MAKE_16BIT || defined MAKE_24BIT || defined MAKE_32BIT else if ( 0 == strncmp (arg, "bits", 1) ) { // Output bits if ( *++argv == NULL ) { stderr_printf ("\n"PROG_NAME": --%s x?\n\n", "bits" ); return 1; } else { Init_Dither ( Bits = atoi (*argv), NoiseShapeType, Dither ); continue; } } else if ( 0 == strncmp (arg, "dither", 2) ) { // Output bits if ( *++argv == NULL ) { stderr_printf ("\n"PROG_NAME": --%s x?\n\n", "dither" ); return 1; } else { Init_Dither ( Bits, NoiseShapeType, Dither = (Float)atof (*argv) ); continue; } } else if ( 0 == strncmp (arg, "shape", 2) ) { // Noise shaping type if ( *++argv == NULL ) { stderr_printf ("\n"PROG_NAME": --%s x?\n\n", "shape" ); return 1; } else { Init_Dither ( Bits, NoiseShapeType = atoi(*argv), Dither ); continue; } } #endif else if ( 0 == strncmp (arg, "wav", 1) ) { // Microsoft's WAVE HeaderWriter = Write_WAVE_Header; output_endianess = LITTLE; OutputFileExt = "wav"; continue; } else if ( 0 == strncmp (arg, "aiff", 1) ) { // Apple's AIFF HeaderWriter = Write_AIFF_Header; output_endianess = BIG; OutputFileExt = "aiff"; continue; } else if ( 0 == strncmp (arg, "raw-le", 5) ) { // Raw PCM, little endian HeaderWriter = Write_Raw_Header; output_endianess = LITTLE; OutputFileExt = "pcm.le"; continue; } else if ( 0 == strncmp (arg, "raw-be", 5) ) { // Raw PCM big endian HeaderWriter = Write_Raw_Header; output_endianess = BIG; OutputFileExt = "pcm.be"; continue; } else if ( 0 == strcmp (arg, "raw") ) { // Raw PCM native endian HeaderWriter = Write_Raw_Header; output_endianess = ENDIAN == HAVE_LITTLE_ENDIAN ? LITTLE : BIG; OutputFileExt = "pcm"; continue; } } // open input-file if ( 0 == strcmp (*argv, "-") || 0 == strcmp (*argv, "/dev/stdin") ) { InputFile = SETBINARY_IN (STDIN); if ( ISATTY (FILENO(InputFile)) ) { stderr_printf ("\n"PROG_NAME": Can't decode data from a terminal\n" ); return 2; } TitleBar (""); Analyze_fs (""); stderr_printf ("\ndecoding of \n"); } else { if ( (InputFile = OPEN (*argv)) == INVALID_FILEDESC ) { #if defined USE_HTTP if ( (InputFile = FDOPEN ( http_open (*argv), "rb" )) == INVALID_FILEDESC ) { #endif stderr_printf ("\n"PROG_NAME": Can't open input file '%s': %s\n", *argv, strerror(errno) ); return 1; #if defined USE_HTTP } #endif } stderr_printf ("\ndecoding of file '"); decode_html ( STDERR, *argv); TitleBar (*argv); Analyze_fs (*argv); if ( SampleFreq != 44100 ) stderr_printf ("' (%g kHz)\n", 1.e-3*SampleFreq ); else stderr_printf ("'\n"); } UNBUFFER ( InputFile ); // open output-file oder continue to use it switch ( OutputMode ) { case automode: OutputName = Create_Extention ( OutputDir, *argv, OutputFileExt ); if ( (OutputFile = CREATE ( OutputName )) == INVALID_FILEDESC ) { stderr_printf ("\n"PROG_NAME": Can't create output file '%s': %s\n", OutputName, strerror(errno) ); return 3; } DecodedSamples = 0; goto filemode2; case filemode_noinit: OutputMode = filemode; filemode2: UNBUFFER ( OutputFile ); HeaderWriter ( OutputFile, SampleFreq, SAMPLE_SIZE, 2, 0xFFFFFFFFL ); case filemode: stderr_printf (" to file '%s'" SAMPLE_SIZE_STRING "\n", OutputName ); break; case pipemode_noinit: OutputMode = pipemode; OutputFile = SETBINARY_OUT (STDOUT); UNBUFFER ( OutputFile ); #if defined USE_OSS_AUDIO if ( 0 == Set_DSP_OSS_Params ( OutputFile, SampleFreq, SAMPLE_SIZE, 2 ) ) OutputComment = " (Open Sound System)"; else #elif defined USE_SUN_AUDIO if ( 0 == Set_DSP_Sun_Params ( OutputFile, SampleFreq, SAMPLE_SIZE, 2 ) ) OutputComment = " (Sun Onboard-Audio)"; else #endif HeaderWriter ( OutputFile, SampleFreq, SAMPLE_SIZE, 2, 0xFFFFFFFFL ); case pipemode: stderr_printf (" to %s" SAMPLE_SIZE_STRING "\n", OutputComment ); break; case sunmode_noinit: #ifdef USE_SUN_AUDIO output_endianess = BIG; if ( (OutputFile = CREATE ( OutputName )) == INVALID_FILEDESC ) { stderr_printf ("\n"PROG_NAME": Can't access device %s: %s\n", OutputName, strerror(errno) ); return 3; } UNBUFFER ( OutputFile ); if ( 0 == Set_DSP_Sun_Params ( OutputFile, SampleFreq, SAMPLE_SIZE, 2 ) ) OutputComment = " (Sun Onboard-Audio)"; #endif OutputMode = sunmode; goto init_done; case dspmode_noinit: #ifdef USE_OSS_AUDIO output_endianess = ENDIAN == HAVE_LITTLE_ENDIAN ? LITTLE : BIG; if ( (OutputFile = CREATE ( OutputName )) == INVALID_FILEDESC ) { stderr_printf ("\n"PROG_NAME": Can't access device %s: %s\n", OutputName, strerror(errno) ); return 3; } UNBUFFER ( OutputFile ); if ( 0 == Set_DSP_OSS_Params ( OutputFile, SampleFreq, SAMPLE_SIZE, 2 ) ) OutputComment = " (Open Sound System)"; OutputMode = dspmode; #endif goto init_done; case esdmode_noinit: #ifdef USE_ESD_AUDIO output_endianess = ENDIAN == HAVE_LITTLE_ENDIAN ? LITTLE : BIG; OutputComment = (ESDFileHandle = Set_ESD_Params ( INVALID_FILEDESC, SampleFreq, SAMPLE_SIZE, 2 )) < 0 ? "" : " (Enlightenment Sound Daemon)"; if ( (OutputFile = FDOPEN ( ESDFileHandle, "wb" )) == INVALID_FILEDESC ) { stderr_printf ("\n"PROG_NAME": Can't access %s: %s\n", OutputName, strerror(errno) ); return 3; } UNBUFFER ( OutputFile ); OutputMode = esdmode; #endif goto init_done; case winmode_noinit: #ifdef USE_WIN_AUDIO output_endianess = ENDIAN == HAVE_LITTLE_ENDIAN ? LITTLE : BIG; if ( Set_WIN_Params ( INVALID_FILEDESC, SampleFreq, SAMPLE_SIZE, 2 ) < 0 ) { stderr_printf ("\n"PROG_NAME": Can't access %s: %s\n", "WAVE OUT", strerror(errno) ); return 3; } OutputFile = WINAUDIO_FD; OutputComment = " (Windows WAVEOUT Audio)"; OutputMode = winmode; #endif goto init_done; case irixmode_noinit: #ifdef USE_IRIX_AUDIO output_endianess = BIG; if ( Set_IRIX_Params ( INVALID_FILEDESC, SampleFreq, SAMPLE_SIZE, 2 ) < 0 ) { stderr_printf ("\n"PROG_NAME": Can't access %s: %s\n", "SGI Irix Audio", strerror(errno) ); return 3; } OutputFile = IRIXAUDIO_FD; OutputComment = " (SGI IRIX Audio)"; OutputMode = irixmode; #endif goto init_done; case sunmode: case dspmode: case esdmode: case winmode: case irixmode: case nullmode: init_done: stderr_printf (" to device %s%s" SAMPLE_SIZE_STRING "\n", OutputName, OutputComment ); break; } // Decode DecodedSamples += DecodeFile ( OutputFile, InputFile, Start, Duration ); // close output-file if necessary (inner loop) switch ( OutputMode ) { case automode: if ( SEEK (OutputFile, 0L, SEEK_SET) != -1 ) HeaderWriter ( OutputFile, SampleFreq, SAMPLE_SIZE, 2, DecodedSamples ); CLOSE ( OutputFile ); break; case filemode: case pipemode: case pipemode_noinit: case filemode_noinit: case dspmode: case esdmode: case sunmode: case winmode: case nullmode: case irixmode: break; } } // close output-file if necessary (final) switch ( OutputMode ) { case pipemode: case filemode: if ( SEEK (OutputFile, 0L, SEEK_SET) != -1 ) { HeaderWriter ( OutputFile, SampleFreq, SAMPLE_SIZE, 2, DecodedSamples ); } // fall through case pipemode_noinit: case filemode_noinit: case dspmode: case esdmode: CLOSE ( OutputFile ); break; case sunmode: case automode: case nullmode: break; case winmode: #ifdef USE_WIN_AUDIO WIN_Audio_close (); #endif break; case irixmode: #ifdef USE_IRIX_AUDIO IRIX_Audio_close (); #endif break; } TitleBar (""); return 0; } /************ The main() function *****************************/ int Cdecl main ( int argc, char** argv ) { static const char* extentions [] = { ".mpc", ".mpp", ".mp+", NULL }; int ret; #if (defined USE_OSS_AUDIO || defined USE_ESD_AUDIO || defined USE_SUN_AUDIO) && (defined USE_REALTIME || defined USE_NICE) DisableSUID (); #endif #if defined _OS2 _wildcard ( &argc, &argv ); #elif defined USE_ARGV mysetargv ( &argc, &argv, extentions ); #endif START(); ENTER(1); // Welcome message if ( argc < 2 || (0 != strcmp (argv[1], "--silent") && 0 != strcmp (argv[1], "--quiet")) ) (void) stderr_printf ("\r\x1B[1m\r%s\n\x1B[0m\r \r", About ); // no arguments or call for help if ( argc < 2 || 0==strcmp (argv[1],"-h") || 0==strcmp (argv[1],"-?") || 0==strcmp (argv[1],"--help") ) { usage (); return 1; } // initialize tables which must initialized once and only once Init_Huffman_Decoder_SV4_6 (); Init_Huffman_Decoder_SV7 (); #ifdef USE_ASM Synthese_Filter_16 = Get_Synthese_Filter (); #endif #if defined MAKE_16BIT || defined MAKE_24BIT || defined MAKE_32BIT Init_Dither ( Bits, NoiseShapeType, Dither ); // initialize dither parameter with standard settings #endif ret = mainloop ( argc, argv ); // analyze command line and do the requested work OverdriveReport (); // output a report if clipping was necessary LEAVE(1); REPORT(); return ret; } /* end of mppdec.c */