/* DecMPA - simple MPEG Audio decoding library. Copyright (C) 2002 Hauke Duden 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 For more information look at the file License.txt in this package. email: hazard_hd@users.sourceforge.net */ //This file is a heavily modified version of SPlayPlugin.cpp from the original //mpeglib. See Readme.txt for details. #include "DefInc.h" #include "MPADecoder.h" #include "DecodeEngine.h" #include #include #include CMPADecoder::CMPADecoder(const DecMPA_Callbacks& Callbacks,void* pCallbackContext) { pow(6.0,3.0); // fixes bug in __math.h m_Callbacks=Callbacks; m_pCallbackContext=pCallbackContext; m_pFileAccess=new CDecMPAFileAccess(Callbacks,pCallbackContext); m_nResyncCounter=0; m_nBufferBytes=0; m_nUsedBufferBytes=0; m_pBuffer=NULL; m_OutputFormat.nType=0; m_OutputFormat.nFrequency=0; m_OutputFormat.nChannels=0; m_bOutputFormatChanged=false; memset(&m_MPEGHeader,0,sizeof(m_MPEGHeader)); m_bDoFloat=false; m_pPCMFrame=new PCMFrame(MP3FRAMESIZE); m_pFloatFrame=new FloatFrame(MP3FRAMESIZE); m_pAudioFrame=NULL; m_pFormatFrame=new AudioFrame; m_nReadTime=0; m_nBufferStartTime=0; m_nDecodeTime=0; m_bPrepared=false; m_bHasFrame=false; m_bFirstDecode=true; m_pDecodeEngine=DecodeEngine_Create(); m_bStoreID3v2=false; m_pID3v2Data=(unsigned char*)0; m_nID3v2DataSize=0; m_pDestroyNotify=(void (*)(void*))0; m_pDestroyNotifyContext=(void*)0; m_aParams[DECMPA_PARAM_OUTPUT]=DECMPA_OUTPUT_INT16; m_aParams[DECMPA_PARAM_PROVIDEID3V2]=DECMPA_NO; m_bNotMPEG=false; m_bDecoderNeedsFlush=false; } CMPADecoder::~CMPADecoder() { if(m_pDestroyNotify!=(void (*)(void*))0) m_pDestroyNotify(m_pDestroyNotifyContext); delete m_pPCMFrame; delete m_pFloatFrame; delete m_pFileAccess; delete m_pFormatFrame; DecodeEngine_Destroy(m_pDecodeEngine); } int CMPADecoder::DecodeNoData(long& nDecodedBytes) { return Decode(NULL,-1,nDecodedBytes); } int CMPADecoder::Decode(void* pBuffer,long nBufferBytes,long& nBytesDecoded) { int Result=DECMPA_OK; if(pBuffer==NULL) nBufferBytes=-1; nBytesDecoded=0; m_bOutputFormatChanged=false; if((Result=EnsurePrepared())!=DECMPA_OK) return Result; try { while(!ReadDecodedData(pBuffer,nBufferBytes,nBytesDecoded)) { Result=DecodeNextFrame(pBuffer==NULL); if(Result!=DECMPA_OK) break; } } catch(std::bad_alloc) { return DECMPA_ERROR_MEMORY; } catch(std::exception) { return DECMPA_ERROR_INTERNAL; } return Result; } int CMPADecoder::DecodeNextFrame(bool bNoData) { int Result=DECMPA_OK; //find and read the next frame //we may still have one frame buffered that was read to find out //the file information. In that case don't read another one if(!m_bHasFrame) m_bHasFrame=ReadNextFrame(); if(m_bHasFrame) { bool bDecodeOK; //don't use this frame again m_bHasFrame=false; //if bNoData==true we don't decode any audio data but only //parse the frame headers //this allows the user to do a quick pass over the file and //get an accurate estimate of the files properties //(actual duration, decoded size, etc...). if(!bNoData) { if(m_bDecoderNeedsFlush) { DecodeEngine_Flush(m_pDecodeEngine); m_bDecoderNeedsFlush=false; } bDecodeOK=DecodeEngine_Decode(m_pDecodeEngine,m_pAudioFrame,m_FrameFinder.GetFrameData(),m_FrameFinder.GetFrameSize()); } else { MpegAudioHeader* pHeader=m_FrameFinder.GetFrameHeader(); long nDecodedFrameSize; m_bDecoderNeedsFlush=true; nDecodedFrameSize=pHeader->getpcmperframe(); if(pHeader->getInputstereo()==1) nDecodedFrameSize*=2; //pretend that we have decoded something m_pAudioFrame->setLen(nDecodedFrameSize); m_pAudioFrame->setFrameFormat(pHeader->getInputstereo(),pHeader->getFrequencyHz()); //note that this "ghost data" will never be retrieved because the call //to ReadDecodedData that follows will always remove all of it //(see Decode) bDecodeOK=true; } if(bDecodeOK) { //make the decoded data available to the "user" HandleDecodedData(m_pAudioFrame); } //ignore if the decoding of the frame failed. Just continue with //the next frame } else { if(m_FrameFinder.IsStreamInvalid()) Result=DECMPA_ERROR_DECODE; else { if(m_pFileAccess->IsEndOfFile()) { //end of file reached if(!m_FrameFinder.KnowsCharacteristics()) { //the end was reached without being able to find out //essential stream characteristics //=> not an MPEG Audio file (or one that has less than 4 frames) Result=DECMPA_ERROR_DECODE; } else Result=DECMPA_END; } else { if(m_pFileAccess->GetLastError()!=DECMPA_OK) Result=m_pFileAccess->GetLastError(); else { //should never happen - an invalid stream, end of file //or a read error are the only things that could cause //ReadNextFrame to fail Result=DECMPA_ERROR_INTERNAL; } } } } return Result; } void CMPADecoder::Flush() { m_FrameFinder.Flush(); DecodeEngine_Flush(m_pDecodeEngine); m_nBufferBytes=0; m_nUsedBufferBytes=0; } bool CMPADecoder::ReadDecodedData(void* pDest,long nBytes,long& nRead) { long nAvailBytes=m_nBufferBytes-m_nUsedBufferBytes; if(nAvailBytes>0) { if(nBytes>nAvailBytes || nBytes==-1) nBytes=nAvailBytes; nBytes&=~m_nOutputBlockSize; //only full samples if(pDest!=NULL) memcpy(pDest,m_pBuffer+m_nUsedBufferBytes,nBytes); m_nUsedBufferBytes+=nBytes; nRead=nBytes; m_nReadTime=m_nBufferStartTime+(((double)(m_nUsedBufferBytes/m_nOutputBlockSize))*1000)/m_pFormatFrame->getFrequenceHZ(); return true; } else return false; } long CMPADecoder::GetFilePositionFromTime(long Millis) { int nResult; if((nResult=EnsurePrepared())!=DECMPA_OK) return nResult; return m_Info.GetFilePositionFromTime(Millis); } int CMPADecoder::SeekToTime(long Millis) { int Result=DECMPA_OK; if(!m_pFileAccess->CanSeek()) Result=DECMPA_ERROR_UNSUPPORTED; else { long nFilePos; if((Result=EnsurePrepared())==DECMPA_OK) { nFilePos=m_Info.GetFilePositionFromTime(Millis); if(nFilePos==-1) Result=DECMPA_ERROR_UNSUPPORTED; else { if(!m_pFileAccess->Seek(nFilePos)) Result=DECMPA_ERROR_SEEK; else { //throw away buffered data Flush(); if(Millis!=0) { //initiate resync (skip next 5 frames until back //references are ok again) m_nResyncCounter=5; } m_nDecodeTime=Millis; m_nBufferStartTime=Millis; m_nReadTime=Millis; } } } } return Result; } int CMPADecoder::GetTime(long& Time) { Time=(long)m_nReadTime; return DECMPA_OK; } int CMPADecoder::GetDuration(long& Duration) { int nResult; try { if((nResult=EnsurePrepared())!=DECMPA_OK) return nResult; Duration=m_Info.GetDuration(); return DECMPA_OK; } catch(std::bad_alloc) { return DECMPA_ERROR_MEMORY; } catch(std::exception) { return DECMPA_ERROR_INTERNAL; } } bool CMPADecoder::ReadNextFrame() { while(!m_FrameFinder.ReadNextFrame()) { if(m_FrameFinder.IsStreamInvalid()) return false; if(!m_FrameFinder.ReadInput(m_pFileAccess)) return false; } return true; } void CMPADecoder::SetOutputFormat(AudioFrame* pNewFormatFrame) { pNewFormatFrame->copyFormat(m_pFormatFrame); m_OutputFormat.nFrequency=m_pFormatFrame->getFrequenceHZ(); m_OutputFormat.nChannels=m_pFormatFrame->getStereo() ? 2 : 1; m_bOutputFormatChanged=true; m_nOutputBlockSize=m_pFormatFrame->getSampleSize()/8; m_nOutputBlockSize*=m_OutputFormat.nChannels; } void CMPADecoder::UpdateMPEGHeader(MpegAudioHeader* pHeader) { memcpy(m_MPEGHeader.aRawData,pHeader->getHeader(),4); m_MPEGHeader.bProtection=pHeader->getProtection()!=0; m_MPEGHeader.nLayer=pHeader->getLayer(); m_MPEGHeader.nVersion=pHeader->getVersion(); m_MPEGHeader.bPadding=pHeader->getPadding()!=0; m_MPEGHeader.nFrequencyIndex=pHeader->getFrequency(); m_MPEGHeader.nFrequency=pHeader->getFrequencyHz(); m_MPEGHeader.nBitRateIndex=pHeader->getBitrateindex(); m_MPEGHeader.nExtendedMode=pHeader->getExtendedmode(); m_MPEGHeader.nMode=pHeader->getMode(); m_MPEGHeader.bInputStereo=pHeader->getInputstereo()!=0; m_MPEGHeader.bMPEG25=pHeader->getLayer25()!=0; m_MPEGHeader.nFrameSize=pHeader->getFramesize(); m_MPEGHeader.nDecodedSamplesPerFrame=pHeader->getpcmperframe(); m_MPEGHeader.nBitRateKbps=pHeader->GetBitRateKbps(); } bool CMPADecoder::OutputFormatChanged() { return m_bOutputFormatChanged; } void CMPADecoder::GetOutputFormat(DecMPA_OutputFormat& Format) { Format=m_OutputFormat; } void CMPADecoder::GetMPEGHeader(DecMPA_MPEGHeader& Header) { Header=m_MPEGHeader; } void CMPADecoder::HandleDecodedData(AudioFrame* pDecodedFrame) { int nDecodedSamples; if(m_bFirstDecode) { SetOutputFormat(pDecodedFrame); m_bFirstDecode=false; } nDecodedSamples=pDecodedFrame->getLen(); if(pDecodedFrame->getStereo()==1) nDecodedSamples/=2; m_nBufferStartTime=m_nDecodeTime; m_nDecodeTime+=(((double)nDecodedSamples)*1000)/pDecodedFrame->getFrequenceHZ(); if(m_nResyncCounter>0) { //we need to resync = read a few frames to make sure //that the back references are ok again //do not output any of the decoded data m_nResyncCounter--; } else { if(!m_pFormatFrame->isFormatEqual(pDecodedFrame)) SetOutputFormat(pDecodedFrame); UpdateMPEGHeader(m_FrameFinder.GetFrameHeader()); if(m_bDoFloat) { m_nBufferBytes=m_pFloatFrame->getLen()*sizeof(float); m_pBuffer=(unsigned char*)m_pFloatFrame->getData(); } else { m_nBufferBytes=m_pPCMFrame->getLen()*sizeof(short); m_pBuffer=(unsigned char*)m_pPCMFrame->getData(); } m_nUsedBufferBytes=0; } } int CMPADecoder::EnsurePrepared() { int Result=DECMPA_OK; if(!m_bPrepared) { if(m_bNotMPEG) Result=DECMPA_ERROR_DECODE; else { m_bStoreID3v2=(m_aParams[DECMPA_PARAM_PROVIDEID3V2]==DECMPA_YES); m_bDoFloat=(m_aParams[DECMPA_PARAM_OUTPUT]==DECMPA_OUTPUT_FLOAT); m_nOutputBlockSize=1; if(m_bDoFloat) m_pAudioFrame=m_pFloatFrame; else m_pAudioFrame=m_pPCMFrame; m_OutputFormat.nType=m_aParams[DECMPA_PARAM_OUTPUT]; //Skip or store any leading ID3 tags - some tags are not //correctly escaped and contain "sync" codes that can confuse //the decoder HandleID3Tag(); if(!m_Info.InitInfo(&m_FrameFinder,m_pFileAccess)) { m_bNotMPEG=true; Result=DECMPA_ERROR_DECODE; } else { //info reads up to one frame. //we can use this in decoding, we only have to remember not //to read a fresh one in the first iteration of Decode m_bHasFrame=m_FrameFinder.HasFrame(); m_bPrepared=true; } } } return Result; } void CMPADecoder::HandleID3Tag() { unsigned char aHeaderData[10]; struct ID3v2Header { char sID[3]; unsigned char Version; unsigned char Revision; unsigned char Flags; unsigned long Size; } Header; int nBytesRead; long UndoID; UndoID=m_pFileAccess->StartUndoRecording(); if((nBytesRead=m_pFileAccess->Read((char*)&aHeaderData,10))==10) { //we do not read directly into the header because the fields may //have been padded by the compiler memcpy(Header.sID,aHeaderData,3); Header.Version=aHeaderData[3]; Header.Revision=aHeaderData[4]; Header.Flags=aHeaderData[5]; Header.Size=aHeaderData[6]; Header.Size<<=8; Header.Size|=aHeaderData[7]; Header.Size<<=8; Header.Size|=aHeaderData[8]; Header.Size<<=8; Header.Size|=aHeaderData[9]; //make sure its an ID3 header if(strncmp(Header.sID,"ID3",3)==0 && Header.Version!=0xff && Header.Revision!=0xff && (Header.Size & 0x80808080)==0) { unsigned long TagSize=Header.Size; //ok, we have an ID3 tag - no need for undo m_pFileAccess->EndUndoRecording(UndoID,false); UndoID=-1; //correct "unsyncing" of tag size TagSize=(TagSize & 0x7f) | ((TagSize & 0x7f00)>>1) | ((TagSize & 0x7f0000)>>2) | ((TagSize & 0x7f000000)>>3); if(m_bStoreID3v2) { //read ID3 tag m_pID3v2Data=new unsigned char[10+TagSize]; memcpy(m_pID3v2Data,aHeaderData,10); if(m_pFileAccess->Read(m_pID3v2Data+10,TagSize)==(long)TagSize) m_nID3v2DataSize=TagSize+10; else { delete[] m_pID3v2Data; m_pID3v2Data=(unsigned char*)0; } } else { //skip the ID3 tag m_pFileAccess->Skip(TagSize); } } } if(UndoID!=-1) { //no ID3 tag found //=> undo read operations m_pFileAccess->EndUndoRecording(UndoID,true); } } int CMPADecoder::GetID3v2Data(unsigned char*& pData,long& nDataSize) { int nResult; if((nResult=EnsurePrepared())!=DECMPA_OK) return nResult; if(m_pID3v2Data==(unsigned char*)0) return DECMPA_ERROR_NOTAVAILABLE; pData=m_pID3v2Data; nDataSize=m_nID3v2DataSize; return DECMPA_OK; } int CMPADecoder::SetParam(int ID,long Value) { bool bOK=false; if(m_bPrepared) return DECMPA_ERROR_WRONGSTATE; switch(ID) { case DECMPA_PARAM_OUTPUT: bOK=(Value==DECMPA_OUTPUT_INT16 || Value==DECMPA_OUTPUT_FLOAT); break; case DECMPA_PARAM_PROVIDEID3V2: bOK=(Value==DECMPA_YES || Value==DECMPA_NO); break; } if(!bOK) return DECMPA_ERROR_PARAM; m_aParams[ID]=Value; return DECMPA_OK; } long CMPADecoder::GetParam(int ID) { if(ID<0 || ID>=DECMPA_PARAMCOUNT) return 0; return m_aParams[ID]; }