201 lines
4.5 KiB
C++
201 lines
4.5 KiB
C++
/* 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
|
|
*/
|
|
|
|
|
|
#include "MPAInfo.h"
|
|
|
|
CMPAInfo::CMPAInfo()
|
|
{
|
|
m_nEncodedDataSize=-1;
|
|
m_nEncodedDataOffset=0;
|
|
m_nDuration=-1;
|
|
m_bXingVBR=false;
|
|
|
|
m_XingHeader.toc=m_aXingTOC;
|
|
}
|
|
|
|
CMPAInfo::~CMPAInfo()
|
|
{
|
|
}
|
|
|
|
bool CMPAInfo::InitInfo(CMPAFrameFinder* pFrameFinder,IFileAccess* pFileAccess)
|
|
{
|
|
m_nEncodedDataSize=-1;
|
|
m_nEncodedDataOffset=0;
|
|
m_nDuration=-1;
|
|
m_bXingVBR=false;
|
|
|
|
while(!pFrameFinder->ReadNextFrame())
|
|
{
|
|
if(pFrameFinder->IsStreamInvalid())
|
|
return false;
|
|
|
|
if(!pFrameFinder->ReadInput(pFileAccess))
|
|
return false;
|
|
}
|
|
|
|
m_nEncodedDataOffset=pFrameFinder->GetFirstFramePosition();
|
|
|
|
m_nEncodedDataSize=pFileAccess->GetLength();
|
|
if(m_nEncodedDataSize!=-1)
|
|
{
|
|
//ignore any leading non-MPEG-Audio data
|
|
m_nEncodedDataSize-=m_nEncodedDataOffset;
|
|
}
|
|
|
|
Init(pFrameFinder);
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMPAInfo::Init(CMPAFrameFinder* pFrameFinder)
|
|
{
|
|
double nAvgFrameSize;
|
|
int nFrameSize;
|
|
long nFrameCount=-1;
|
|
|
|
nAvgFrameSize=pFrameFinder->GetAvgFrameSize();
|
|
nFrameSize=pFrameFinder->GetFrameSize();
|
|
if(nAvgFrameSize>0)
|
|
{
|
|
if(ReadXingHeader(pFrameFinder->GetFrameData(),nFrameSize))
|
|
{
|
|
m_bXingVBR=true;
|
|
nFrameCount=m_XingHeader.frames;
|
|
}
|
|
else
|
|
{
|
|
if(m_nEncodedDataSize>=0)
|
|
nFrameCount=(long)(m_nEncodedDataSize/nAvgFrameSize);
|
|
}
|
|
}
|
|
|
|
if(nFrameCount!=-1)
|
|
{
|
|
MpegAudioHeader* pHeader=pFrameFinder->GetFrameHeader();
|
|
int nDecodedSamplesPerFrame;
|
|
int nFrequency;
|
|
double nDecodedSamples;
|
|
|
|
nDecodedSamplesPerFrame=pHeader->getpcmperframe();
|
|
nFrequency=pHeader->getFrequencyHz();
|
|
|
|
nDecodedSamples=((double)nFrameCount)*nDecodedSamplesPerFrame;
|
|
|
|
if(nFrequency!=0)
|
|
m_nDuration=(long)((nDecodedSamples*1000)/nFrequency);
|
|
}
|
|
}
|
|
|
|
bool CMPAInfo::ReadXingHeader(void* pFrameData,int nFrameSize)
|
|
{
|
|
if(nFrameSize<152) //cannot have xing header
|
|
return false;
|
|
|
|
if(::GetXingHeader(&m_XingHeader,(unsigned char*)pFrameData)==0)
|
|
return false;
|
|
|
|
//we use floats so that we won't loose precision
|
|
//with the un-compensation stuff below
|
|
for(int i=0;i<100;i++)
|
|
m_aTOC[i]=((float)m_XingHeader.toc[i])/256.0f;
|
|
m_aTOC[100]=1.0f;
|
|
|
|
if(m_aTOC[0]!=0)
|
|
{
|
|
//the Xing table of contents compensates for some
|
|
//leading non-MP3 data (maybe an ID3 tag or something)
|
|
//Since that data may never have been passed to the library
|
|
//(or if it has been passed, it should have been skipped)
|
|
//we undo this compensation
|
|
float FullSizeValue;
|
|
|
|
FullSizeValue=1.0f-m_aTOC[0];
|
|
|
|
for(int i=100;i>=0;i--)
|
|
m_aTOC[i]=(m_aTOC[i]-m_aTOC[0])/FullSizeValue;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
long CMPAInfo::GetFilePositionFromTime(long Millis)
|
|
{
|
|
long nPos=-1;
|
|
double nTimeFraction;
|
|
|
|
if(m_nDuration>0 && m_nEncodedDataSize>=0)
|
|
{
|
|
nTimeFraction=((double)Millis)/m_nDuration;
|
|
|
|
if(m_bXingVBR)
|
|
{
|
|
nPos=GetEncodedDataPositionFromTOC((float)nTimeFraction);
|
|
|
|
if(nPos>=m_nEncodedDataSize)
|
|
nPos=m_nEncodedDataSize;
|
|
}
|
|
else
|
|
nPos=(long)(nTimeFraction*m_nEncodedDataSize);
|
|
}
|
|
|
|
if(nPos==-1)
|
|
{
|
|
//ok, we couldn't find the correct position because
|
|
//we don't know enough about the stream
|
|
|
|
//however, ONE position we know: the beginning
|
|
if(Millis==0)
|
|
nPos=0;
|
|
}
|
|
|
|
nPos+=m_nEncodedDataOffset;
|
|
|
|
return nPos;
|
|
}
|
|
|
|
long CMPAInfo::GetEncodedDataPositionFromTOC(float TimeFract)
|
|
{
|
|
// interpolate in TOC to get file seek point in bytes
|
|
int nTOCIndex;
|
|
float LowerPosFract;
|
|
float UpperPosFract;
|
|
float PosFract;
|
|
float Percent=TimeFract*100.0f;
|
|
|
|
if(Percent<0.0f)
|
|
Percent=0.0f;
|
|
if(Percent>100.0f)
|
|
Percent=100.0f;
|
|
|
|
nTOCIndex=(int)Percent;
|
|
if(nTOCIndex>99)
|
|
nTOCIndex=99;
|
|
LowerPosFract=m_aTOC[nTOCIndex];
|
|
UpperPosFract=m_aTOC[nTOCIndex+1];
|
|
|
|
PosFract=LowerPosFract + ((UpperPosFract-LowerPosFract)*(Percent-nTOCIndex));
|
|
|
|
return (long)(PosFract*m_nEncodedDataSize);
|
|
}
|
|
|