/*____________________________________________________________________________ FreeAmp - The Free MP3 Player MP3 Decoder originally Copyright (C) 1995-1997 Xing Technology Corp. http://www.xingtech.com Portions Copyright (C) 1998-1999 EMusic.com 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., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: towave.c,v 1.3 1999/10/19 07:13:09 elrod Exp $ ____________________________________________________________________________*/ /* ------------------------------------------------------------------------ NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE This file exists for reference only. It is not actually used in the FreeAmp project. There is no need to mess with this file. There is no need to flatten the beavers, either. NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE /*---- towave.c -------------------------------------------- 32 bit version only decode mpeg Layer I/II/III file using portable ANSI C decoder, output to pcm wave file. mod 8/19/98 decode 22 sf bands mod 5/14/98 allow mpeg25 (dec8 not supported for mpeg25 samp rate) mod 3/4/98 bs_trigger bs_bufbytes made signed, unsigned may not terminate properly. Also extra test in bs_fill. mod 8/6/96 add 8 bit output to standard decoder ver 1.4 mods 7/18/96 32 bit and add asm option mods 6/29/95 allow MS wave file for u-law. bugfix u-law table dec8.c mods 2/95 add sample rate reduction, freq_limit and conversions. add _decode8 for 8Ks output, 16bit 8bit, u-law output. add additional control parameters to init. add _info function mod 5/12/95 add quick window cwinq.c mod 5/19/95 change from stream io to handle io mod 11/16/95 add Layer I mod 1/5/95 integer overflow mod iup.c ver 1.3 mod 2/5/96 portability mods drop Tom and Gloria pcm file types ver 2.0 mod 1/7/97 Layer 3 (float mpeg-1 only) 2/6/97 Layer 3 MPEG-2 ver 3.01 Layer III bugfix crc problem 8/18/97 ver 3.02 Layer III fix wannabe.mp3 problem 10/9/97 ver 3.03 allow mpeg 2.5 5/14/98 Decoder functions for _decode8 are defined in dec8.c. Useage is same as regular decoder. Towave illustrates use of decoder. Towave converts mpeg audio files to 16 bit (short) pcm. Default pcm file format is wave. Other formats can be accommodated by adding alternative write_pcm_header and write_pcm_tailer functions. The functions kbhit and getch used in towave.c may not port to other systems. The decoder handles all mpeg1 and mpeg2 Layer I/II bitstreams. For compatability with the asm decoder and future C versions, source code users are discouraged from making modifications to the decoder proper. MS Windows applications can use wrapper functions in a separate module if decoder functions need to be exported. NOTE additional control parameters. mod 8/6/96 standard decoder adds 8 bit output decode8 (8Ks output) convert_code: convert_code = 4*bit_code + chan_code bit_code: 1 = 16 bit linear pcm 2 = 8 bit (unsigned) linear pcm 3 = u-law (8 bits unsigned) chan_code: 0 = convert two chan to mono 1 = convert two chan to mono 2 = convert two chan to left chan 3 = convert two chan to right chan decode (standard decoder) convert_code: 0 = two chan output 1 = convert two chan to mono 2 = convert two chan to left chan 3 = convert two chan to right chan or with 8 = 8 bit output (other bits ignored) decode (standard decoder) reduction_code: 0 = full sample rate output 1 = half rate 2 = quarter rate -----------------------------------------------------------*/ #include #include #include #include #include #ifdef WIN32 #include #endif #include /* file open flags */ #include /* someone wants for port */ #include /* forward slash for portability */ #include "mhead.h" /* mpeg header structure, decode protos */ #include "port.h" // JDW #ifdef __linux__ #include #include #include #include #endif // JDW #include "mp3struct.h" #include #ifndef byte typedef unsigned char byte; #endif typedef struct id3v1_1 { char id[3]; char title[30]; // char artist[30]; // "Raven Software" char album[30]; // "#UNCOMP %d" // needed char year[4]; // "2000" char comment[28]; // "#MAXVOL %g" // needed char zero; char track; char genre; } id3v1_1; // 128 bytes in size id3v1_1 *gpTAG; #define BYTESREMAINING_ACCOUNT_FOR_REAR_TAG(_pvData, _iBytesRemaining) \ \ /* account for trailing ID3 tag in _iBytesRemaining */ \ gpTAG = (id3v1_1*) (((byte *)_pvData + _iBytesRemaining)-sizeof(id3v1_1)); /* sizeof = 128 */ \ if (!strncmp(gpTAG->id, "TAG", 3)) \ { \ _iBytesRemaining -= sizeof(id3v1_1); \ } /******** pcm buffer ********/ #define PCM_BUFBYTES 60000U // more than enough to cover the largest that one packet will ever expand to char PCM_Buffer[PCM_BUFBYTES]; // better off being declared, so we don't do mallocs in this module (MAC reasons) typedef struct { int (*decode_init) (MPEG_HEAD * h, int framebytes_arg, int reduction_code, int transform_code, int convert_code, int freq_limit); void (*decode_info) (DEC_INFO * info); IN_OUT(*decode) (unsigned char *bs, short *pcm, unsigned char *pNextByteAfterData); } AUDIO; #if 0 // fuck this... static AUDIO audio_table[2][2] = { { {audio_decode_init, audio_decode_info, audio_decode}, {audio_decode8_init, audio_decode8_info, audio_decode8}, }, { {i_audio_decode_init, i_audio_decode_info, i_audio_decode}, {audio_decode8_init, audio_decode8_info, audio_decode8}, } }; #else static AUDIO audio_table[2][2] = { { {audio_decode_init, audio_decode_info, audio_decode}, {audio_decode_init, audio_decode_info, audio_decode}, }, { {audio_decode_init, audio_decode_info, audio_decode}, {audio_decode_init, audio_decode_info, audio_decode}, } }; #endif static const AUDIO audio = {audio_decode_init, audio_decode_info, audio_decode}; //audio_table[0][0]; // Do NOT change these, ever!!!!!!!!!!!!!!!!!! // const int reduction_code = 0; // unpack at full sample rate output const int convert_code_mono = 1; const int convert_code_stereo = 0; const int freq_limit = 24000; // no idea what this is about, but it's always this value so... // the entire decode mechanism uses this now... // MP3STREAM _MP3Stream; LP_MP3STREAM pMP3Stream = &_MP3Stream; int bFastEstimateOnly = 0; // MUST DEFAULT TO THIS VALUE!!!!!!!!! // char *return is NZ for any errors (no trailing CR!) // char *C_MP3_IsValid(void *pvData, int iDataLen, int bStereoDesired) { // char sTemp[1024]; ///////////////////////////////////////////////// unsigned int iRealDataStart; MPEG_HEAD head; DEC_INFO decinfo; int iBitRate; int iFrameBytes; #ifdef _DEBUG int iIgnoreThisForNowIJustNeedItToBreakpointOnToReadAValue = sizeof(MP3STREAM); iBitRate = iIgnoreThisForNowIJustNeedItToBreakpointOnToReadAValue; // get rid of unused-variable warning #endif memset(pMP3Stream,0,sizeof(*pMP3Stream)); iFrameBytes = head_info3( pvData, iDataLen/2, &head, &iBitRate, &iRealDataStart); if (iFrameBytes == 0) { return "MP3ERR: Bad or unsupported file!"; } // check for files with bad frame unpack sizes (that would crash the game), or stereo files. // // although the decoder can convert stereo to mono (apparently), we want to know about stereo files // because they're a waste of source space... (all FX are mono, and moved via panning) // if (audio.decode_init(&head, iFrameBytes, reduction_code, iRealDataStart, bStereoDesired?convert_code_stereo:convert_code_mono, freq_limit)) { if (bStereoDesired) { if (pMP3Stream->outbytes > 4608) { return "MP3ERR: Source file has output packet size > 2304 (*2 for stereo) bytes!"; } } else { if (pMP3Stream->outbytes > 2304) { return "MP3ERR: Source file has output packet size > 2304 bytes!"; } } audio.decode_info(&decinfo); if (decinfo.bits != 16) { return "MP3ERR: Source file is not 16bit!"; // will this ever happen? oh well... } if (decinfo.samprate != 44100) { return "MP3ERR: Source file is not sampled @ 44100!"; } //// this code should never be executed now, since music can legally be stereo, and FX will be unpacked as mono anyway //#ifdef REJECT_STEREO_MP3s // if (decinfo.channels == 2) // { // return "MP3ERR: Source file is stereo!"; // } //#endif } else { return "MP3ERR: Decoder failed to initialise"; } // file seems to be valid... // return NULL; } // char *return is NZ for any errors (no trailing CR!) // char* C_MP3_GetHeaderData(void *pvData, int iDataLen, int *piRate, int *piWidth, int *piChannels, int bStereoDesired) { unsigned int iRealDataStart; MPEG_HEAD head; DEC_INFO decinfo; int iBitRate; int iFrameBytes; memset(pMP3Stream,0,sizeof(*pMP3Stream)); iFrameBytes = head_info3( pvData, iDataLen/2, &head, &iBitRate, &iRealDataStart); if (iFrameBytes == 0) { return "MP3ERR: Bad or unsupported file!"; } if (audio.decode_init(&head, iFrameBytes, reduction_code, iRealDataStart, bStereoDesired?convert_code_stereo:convert_code_mono, freq_limit)) { audio.decode_info(&decinfo); *piRate = decinfo.samprate; // rate (eg 22050, 44100 etc) *piWidth = decinfo.bits/8; // 1 for 8bit, 2 for 16 bit *piChannels = decinfo.channels; // 1 for mono, 2 for stereo } else { return "MP3ERR: Decoder failed to initialise"; } // everything ok... // return NULL; } // this duplicates work done in C_MP3_IsValid(), but it avoids global structs, and means that you can call this anytime // if you just want info for some reason // // ( size is now workd out just by decompressing each packet header, not the whole stream. MUCH faster :-) // // char *return is NZ for any errors (no trailing CR!) // char *C_MP3_GetUnpackedSize(void *pvData, int iSourceBytesRemaining, int *piUnpackedSize, int bStereoDesired ) { int iReadLimit; unsigned int iRealDataStart; MPEG_HEAD head; int iBitRate; char *pPCM_Buffer = PCM_Buffer; char *psReturn = NULL; // int iSourceReadIndex = 0; int iDestWriteIndex = 0; int iFrameBytes; int iFrameCounter; DEC_INFO decinfo; IN_OUT x; memset(pMP3Stream,0,sizeof(*pMP3Stream)); #define iSourceReadIndex iRealDataStart // iFrameBytes = head_info2( pvData, 0, &head, &iBitRate); iFrameBytes = head_info3( pvData, iSourceBytesRemaining/2, &head, &iBitRate, &iRealDataStart); BYTESREMAINING_ACCOUNT_FOR_REAR_TAG(pvData, iSourceBytesRemaining) iSourceBytesRemaining -= iRealDataStart; iReadLimit = iSourceReadIndex + iSourceBytesRemaining; if (iFrameBytes) { //pPCM_Buffer = Z_Malloc(PCM_BUFBYTES); //if (pPCM_Buffer) { // init decoder... if (audio.decode_init(&head, iFrameBytes, reduction_code, iRealDataStart, bStereoDesired?convert_code_stereo:convert_code_mono, freq_limit)) { audio.decode_info(&decinfo); // decode... // for (iFrameCounter = 0;;iFrameCounter++) { if ( iSourceBytesRemaining == 0 || iSourceBytesRemaining < iFrameBytes) break; // end of file bFastEstimateOnly = 1; /////////////////////////////// x = audio.decode((unsigned char *)pvData + iSourceReadIndex, (short *) ((char *)pPCM_Buffer //+ iDestWriteIndex // keep decoding over the same spot since we're only counting bytes in this function ), (unsigned char *)pvData + iReadLimit ); bFastEstimateOnly = 0; /////////////////////////////// iSourceReadIndex += x.in_bytes; iSourceBytesRemaining -= x.in_bytes; iDestWriteIndex += x.out_bytes; if (x.in_bytes <= 0) { //psReturn = "MP3ERR: Bad sync in file"; break; } } *piUnpackedSize = iDestWriteIndex; // yeeehaaa! } else { psReturn = "MP3ERR: Decoder failed to initialise"; } } // else // { // psReturn = "MP3ERR: Unable to alloc temp decomp buffer"; // } } else { psReturn = "MP3ERR: Bad or Unsupported MP3 file!"; } // if (pPCM_Buffer) // { // Z_Free(pPCM_Buffer); // pPCM_Buffer = NULL; // I know, I know... // } return psReturn; #undef iSourceReadIndex } char *C_MP3_UnpackRawPCM( void *pvData, int iSourceBytesRemaining, int *piUnpackedSize, void *pbUnpackBuffer, int bStereoDesired) { int iReadLimit; unsigned int iRealDataStart; MPEG_HEAD head; int iBitRate; char *psReturn = NULL; // int iSourceReadIndex = 0; int iDestWriteIndex = 0; int iFrameBytes; int iFrameCounter; DEC_INFO decinfo; IN_OUT x; memset(pMP3Stream,0,sizeof(*pMP3Stream)); #define iSourceReadIndex iRealDataStart // iFrameBytes = head_info2( pvData, 0, &head, &iBitRate); iFrameBytes = head_info3( pvData, iSourceBytesRemaining/2, &head, &iBitRate, &iRealDataStart); BYTESREMAINING_ACCOUNT_FOR_REAR_TAG(pvData, iSourceBytesRemaining) iSourceBytesRemaining -= iRealDataStart; iReadLimit = iSourceReadIndex + iSourceBytesRemaining; if (iFrameBytes) { // if (1)////////////////////////pPCM_Buffer) { // init decoder... if (audio.decode_init(&head, iFrameBytes, reduction_code, iRealDataStart, bStereoDesired?convert_code_stereo:convert_code_mono, freq_limit)) { audio.decode_info(&decinfo); // printf("\n output samprate = %6ld",decinfo.samprate); // printf("\n output channels = %6d", decinfo.channels); // printf("\n output bits = %6d", decinfo.bits); // printf("\n output type = %6d", decinfo.type); //=============== // decode... // for (iFrameCounter = 0;;iFrameCounter++) { if ( iSourceBytesRemaining == 0 || iSourceBytesRemaining < iFrameBytes) break; // end of file x = audio.decode((unsigned char *)pvData + iSourceReadIndex, (short *) ((char *)pbUnpackBuffer + iDestWriteIndex), (unsigned char *)pvData + iReadLimit ); iSourceReadIndex += x.in_bytes; iSourceBytesRemaining -= x.in_bytes; iDestWriteIndex += x.out_bytes; if (x.in_bytes <= 0) { //psReturn = "MP3ERR: Bad sync in file"; break; } } *piUnpackedSize = iDestWriteIndex; // yeeehaaa! } else { psReturn = "MP3ERR: Decoder failed to initialise"; } } } else { psReturn = "MP3ERR: Bad or Unsupported MP3 file!"; } return psReturn; #undef iSourceReadIndex } // called once, after we've decided to keep something as MP3. This just sets up the decoder for subsequent stream-calls. // // (the struct pSFX_MP3Stream is cleared internally, so pass as args anything you want stored in it) // // char * return is NULL for ok, else error string // char *C_MP3Stream_DecodeInit( LP_MP3STREAM pSFX_MP3Stream, void *pvSourceData, int iSourceBytesRemaining, int iGameAudioSampleRate, int iGameAudioSampleBits, int bStereoDesired ) { char *psReturn = NULL; MPEG_HEAD head; // only relevant within this function during init DEC_INFO decinfo; // " " int iBitRate; // not used after being filled in by head_info3() pMP3Stream = pSFX_MP3Stream; memset(pMP3Stream,0,sizeof(*pMP3Stream)); pMP3Stream->pbSourceData = (byte *) pvSourceData; pMP3Stream->iSourceBytesRemaining = iSourceBytesRemaining; pMP3Stream->iSourceFrameBytes = head_info3( (byte *) pvSourceData, iSourceBytesRemaining/2, &head, &iBitRate, (unsigned int*)&pMP3Stream->iSourceReadIndex ); // hack, do NOT do this for stereo, since music files are now streamed and therefore the data isn't actually fully // loaded at this point, only about 4k or so for the header is actually in memory!!!... // if (!bStereoDesired) { BYTESREMAINING_ACCOUNT_FOR_REAR_TAG(pvSourceData, pMP3Stream->iSourceBytesRemaining); pMP3Stream->iSourceBytesRemaining -= pMP3Stream->iSourceReadIndex; } // backup a couple of fields so we can play this again later... // pMP3Stream->iRewind_SourceReadIndex = pMP3Stream->iSourceReadIndex; pMP3Stream->iRewind_SourceBytesRemaining= pMP3Stream->iSourceBytesRemaining; assert(pMP3Stream->iSourceFrameBytes); if (pMP3Stream->iSourceFrameBytes) { if (audio.decode_init(&head, pMP3Stream->iSourceFrameBytes, reduction_code, pMP3Stream->iSourceReadIndex, bStereoDesired?convert_code_stereo:convert_code_mono, freq_limit)) { pMP3Stream->iRewind_FinalReductionCode = reduction_code; // default = 0 (no reduction), 1=half, 2 = quarter pMP3Stream->iRewind_FinalConvertCode = bStereoDesired?convert_code_stereo:convert_code_mono; // default = 1 (mono), OR with 8 for 8-bit output // only now can we ask what kind of properties this file has, and then adjust to fit what the game wants... // audio.decode_info(&decinfo); // printf("\n output samprate = %6ld",decinfo.samprate); // printf("\n output channels = %6d", decinfo.channels); // printf("\n output bits = %6d", decinfo.bits); // printf("\n output type = %6d", decinfo.type); // decoder offers half or quarter rate adjustement only... // if (iGameAudioSampleRate == decinfo.samprate>>1) pMP3Stream->iRewind_FinalReductionCode = 1; else if (iGameAudioSampleRate == decinfo.samprate>>2) pMP3Stream->iRewind_FinalReductionCode = 2; if (iGameAudioSampleBits == decinfo.bits>>1) // if game wants 8 bit sounds, then setup for that pMP3Stream->iRewind_FinalConvertCode |= 8; if (audio.decode_init(&head, pMP3Stream->iSourceFrameBytes, pMP3Stream->iRewind_FinalReductionCode, pMP3Stream->iSourceReadIndex, pMP3Stream->iRewind_FinalConvertCode, freq_limit)) { audio.decode_info(&decinfo); #ifdef _DEBUG assert( iGameAudioSampleRate == decinfo.samprate ); assert( iGameAudioSampleBits == decinfo.bits ); #endif // sod it, no harm in one last check... (should never happen) // if ( iGameAudioSampleRate != decinfo.samprate || iGameAudioSampleBits != decinfo.bits ) { psReturn = "MP3ERR: Decoder unable to convert to current game audio settings"; } } else { psReturn = "MP3ERR: Decoder failed to initialise for pass 2 sample adjust"; } } else { psReturn = "MP3ERR: Decoder failed to initialise"; } } else { psReturn = "MP3ERR: Errr.... something's broken with this MP3 file"; // should never happen by this point } // restore global stream ptr before returning to normal functions (so the rest of the MP3 code still works)... // pMP3Stream = &_MP3Stream; return psReturn; } // return value is decoded bytes for this packet, which is effectively a BOOL, NZ for not finished decoding yet... // unsigned int C_MP3Stream_Decode( LP_MP3STREAM pSFX_MP3Stream, int bFastForwarding ) { unsigned int uiDecoded = 0; // default to "finished" IN_OUT x; pMP3Stream = pSFX_MP3Stream; do { if ( pSFX_MP3Stream->iSourceBytesRemaining == 0 )//|| pSFX_MP3Stream->iSourceBytesRemaining < pSFX_MP3Stream->iSourceFrameBytes) { uiDecoded = 0; // finished break; } bFastEstimateOnly = bFastForwarding; /////////////////////////////// x = audio.decode(pSFX_MP3Stream->pbSourceData + pSFX_MP3Stream->iSourceReadIndex, (short *) (pSFX_MP3Stream->bDecodeBuffer), pSFX_MP3Stream->pbSourceData + pSFX_MP3Stream->iRewind_SourceReadIndex + pSFX_MP3Stream->iRewind_SourceBytesRemaining ); bFastEstimateOnly = 0; /////////////////////////////// #ifdef _DEBUG pSFX_MP3Stream->iSourceFrameCounter++; #endif pSFX_MP3Stream->iSourceReadIndex += x.in_bytes; pSFX_MP3Stream->iSourceBytesRemaining -= x.in_bytes; pSFX_MP3Stream->iBytesDecodedTotal += x.out_bytes; pSFX_MP3Stream->iBytesDecodedThisPacket = x.out_bytes; uiDecoded = x.out_bytes; if (x.in_bytes <= 0) { //psReturn = "MP3ERR: Bad sync in file"; uiDecoded= 0; // finished break; } } #pragma warning (disable : 4127 ) // conditional expression is constant while (0); // #pragma warning (default : 4127 ) // conditional expression is constant // restore global stream ptr before returning to normal functions (so the rest of the MP3 code still works)... // pMP3Stream = &_MP3Stream; return uiDecoded; } // ret is char* errstring, else NULL for ok // char *C_MP3Stream_Rewind( LP_MP3STREAM pSFX_MP3Stream ) { char* psReturn = NULL; MPEG_HEAD head; // only relevant within this function during init int iBitRate; // ditto int iNULL; pMP3Stream = pSFX_MP3Stream; pMP3Stream->iSourceReadIndex = pMP3Stream->iRewind_SourceReadIndex; pMP3Stream->iSourceBytesRemaining = pMP3Stream->iRewind_SourceBytesRemaining; // already adjusted for tags etc // I'm not sure that this is needed, but where else does decode_init get passed useful data ptrs?... // if (pMP3Stream->iSourceFrameBytes == head_info3( pMP3Stream->pbSourceData, pMP3Stream->iSourceBytesRemaining/2, &head, &iBitRate, (unsigned int*)&iNULL ) ) { if (audio.decode_init(&head, pMP3Stream->iSourceFrameBytes, pMP3Stream->iRewind_FinalReductionCode, pMP3Stream->iSourceReadIndex, pMP3Stream->iRewind_FinalConvertCode, freq_limit)) { // we should always get here... // } else { psReturn = "MP3ERR: Failed to re-init decoder for rewind!"; } } else { psReturn = "MP3ERR: Frame bytes mismatch during rewind header-read!"; } // restore global stream ptr before returning to normal functions (so the rest of the MP3 code still works)... // pMP3Stream = &_MP3Stream; return psReturn; }