summaryrefslogtreecommitdiff
path: root/engine/audio/private/VBRHeader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engine/audio/private/VBRHeader.cpp')
-rw-r--r--engine/audio/private/VBRHeader.cpp304
1 files changed, 304 insertions, 0 deletions
diff --git a/engine/audio/private/VBRHeader.cpp b/engine/audio/private/VBRHeader.cpp
new file mode 100644
index 0000000..ce554d4
--- /dev/null
+++ b/engine/audio/private/VBRHeader.cpp
@@ -0,0 +1,304 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "audio_pch.h"
+#include "tier0/platform.h"
+#include "MPAFile.h" // also includes vbrheader.h
+#include "tier0/dbg.h"
+
+#ifndef MAKEFOURCC
+ #define MAKEFOURCC(ch0, ch1, ch2, ch3) \
+ ((uint32)(BYTE)(ch0) | ((uint32)(BYTE)(ch1) << 8) | \
+ ((uint32)(BYTE)(ch2) << 16) | ((uint32)(BYTE)(ch3) << 24 ))
+#endif //defined(MAKEFOURCC)
+
+// XING Header offset: 1. index = lsf, 2. index = mono
+uint32 CVBRHeader::m_dwXINGOffsets[2][2] =
+{
+ // MPEG 1 (not mono, mono)
+ { 32 + MPA_HEADER_SIZE, 17 + MPA_HEADER_SIZE },
+ // MPEG 2/2.5
+ { 17 + MPA_HEADER_SIZE, 9 + MPA_HEADER_SIZE }
+};
+
+// first test with this static method, if it does exist
+bool CVBRHeader::IsVBRHeaderAvailable( CMPAFile* pMPAFile, VBRHeaderType& HeaderType, uint32& dwOffset )
+{
+ Assert(pMPAFile);
+
+ // where does VBR header begin (XING)
+ uint32 dwNewOffset = dwOffset + m_dwXINGOffsets[pMPAFile->m_pMPAHeader->IsLSF()][pMPAFile->m_pMPAHeader->IsMono()];
+
+ // check for XING header first
+ if( CheckXING( pMPAFile, dwNewOffset ) )
+ {
+ HeaderType = XINGHeader;
+ // seek offset back to header begin
+ dwOffset = dwNewOffset - 4;
+ return true;
+ }
+
+ // VBRI header always at fixed offset
+ dwNewOffset = dwOffset + 32 + MPA_HEADER_SIZE;
+ if( CheckVBRI( pMPAFile, dwNewOffset ) )
+ {
+ HeaderType = VBRIHeader;
+ // seek offset back to header begin
+ dwOffset = dwNewOffset - 4;
+ return true;
+ }
+ HeaderType = NoHeader;
+ return false;
+}
+
+CVBRHeader::CVBRHeader( CMPAFile* pMPAFile, VBRHeaderType HeaderType, uint32 dwOffset ) :
+ m_pMPAFile( pMPAFile ), m_pnToc(NULL), m_HeaderType( HeaderType ), m_dwOffset(dwOffset), m_dwFrames(0), m_dwBytes(0)
+{
+ switch( m_HeaderType )
+ {
+ case NoHeader:
+ // no Header found
+ throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
+ break;
+ case XINGHeader:
+ if( !ExtractXINGHeader( m_dwOffset ) )
+ throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
+ break;
+ case VBRIHeader:
+ if( !ExtractVBRIHeader( m_dwOffset ) )
+ throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
+ break;
+ }
+ // calc bitrate
+ if( m_dwBytes > 0 && m_dwFrames > 0 )
+ {
+ // calc number of seconds
+ m_dwBytesPerSec = m_pMPAFile->m_pMPAHeader->GetBytesPerSecond( m_dwFrames, m_dwBytes );
+ }
+ else // incomplete header found
+ {
+ throw CMPAException( CMPAException::IncompleteVBRHeader, pMPAFile->GetFilename(), NULL, false );
+ }
+}
+
+bool CVBRHeader::CheckID( CMPAFile* pMPAFile, char ch0, char ch1, char ch2, char ch3, uint32& dwOffset )
+{
+ return ( pMPAFile->ExtractBytes( dwOffset, 4 ) == MAKEFOURCC( ch3, ch2, ch1, ch0 ) );
+}
+
+bool CVBRHeader::CheckXING( CMPAFile* pMPAFile, uint32& dwOffset )
+{
+ // XING ID found?
+ if( !CheckID( pMPAFile, 'X', 'i', 'n', 'g', dwOffset) && !CheckID( pMPAFile, 'I', 'n', 'f', 'o', dwOffset) )
+ return false;
+ return true;
+}
+
+bool CVBRHeader::CheckVBRI( CMPAFile* pMPAFile, uint32& dwOffset )
+{
+ // VBRI ID found?
+ if( !CheckID( pMPAFile, 'V', 'B', 'R', 'I', dwOffset ) )
+ return false;
+ return true;
+}
+
+
+// currently not used
+bool CVBRHeader::ExtractLAMETag( uint32 dwOffset )
+{
+ // LAME ID found?
+ if( !CheckID( m_pMPAFile, 'L', 'A', 'M', 'E', dwOffset ) && !CheckID( m_pMPAFile, 'G', 'O', 'G', 'O', dwOffset ) )
+ return false;
+
+ return true;
+}
+
+bool CVBRHeader::ExtractXINGHeader( uint32 dwOffset )
+{
+ /* XING VBR-Header
+
+ size description
+ 4 'Xing' or 'Info'
+ 4 flags (indicates which fields are used)
+ 4 frames (optional)
+ 4 bytes (optional)
+ 100 toc (optional)
+ 4 a VBR quality indicator: 0=best 100=worst (optional)
+
+ */
+ if( !CheckXING( m_pMPAFile, dwOffset ) )
+ return false;
+
+ uint32 dwFlags;
+
+ // get flags (mandatory in XING header)
+ dwFlags = m_pMPAFile->ExtractBytes( dwOffset, 4 );
+
+ // extract total number of frames in file
+ if(dwFlags & FRAMES_FLAG)
+ m_dwFrames = m_pMPAFile->ExtractBytes(dwOffset,4);
+
+ // extract total number of bytes in file
+ if(dwFlags & BYTES_FLAG)
+ m_dwBytes = m_pMPAFile->ExtractBytes(dwOffset,4);
+
+ // extract TOC (for more accurate seeking)
+ if (dwFlags & TOC_FLAG)
+ {
+ m_dwTableSize = 100;
+ m_pnToc = new int[m_dwTableSize];
+
+ if( m_pnToc )
+ {
+ for(uint32 i=0;i<m_dwTableSize;i++)
+ m_pnToc[i] = m_pMPAFile->ExtractBytes( dwOffset, 1 );
+ }
+ }
+
+ m_dwQuality = (uint32)-1;
+ if(dwFlags & VBR_SCALE_FLAG )
+ m_dwQuality = m_pMPAFile->ExtractBytes(dwOffset, 4);
+
+ return true;
+}
+
+bool CVBRHeader::ExtractVBRIHeader( uint32 dwOffset )
+{
+ /* FhG VBRI Header
+
+ size description
+ 4 'VBRI' (ID)
+ 2 version
+ 2 delay
+ 2 quality
+ 4 # bytes
+ 4 # frames
+ 2 table size (for TOC)
+ 2 table scale (for TOC)
+ 2 size of table entry (max. size = 4 byte (must be stored in an integer))
+ 2 frames per table entry
+
+ ?? dynamic table consisting out of frames with size 1-4
+ whole length in table size! (for TOC)
+
+ */
+
+ if( !CheckVBRI( m_pMPAFile, dwOffset ) )
+ return false;
+
+ // extract all fields from header (all mandatory)
+ m_dwVersion = m_pMPAFile->ExtractBytes(dwOffset, 2 );
+ m_fDelay = (float)m_pMPAFile->ExtractBytes(dwOffset, 2 );
+ m_dwQuality = m_pMPAFile->ExtractBytes(dwOffset, 2 );
+ m_dwBytes = m_pMPAFile->ExtractBytes(dwOffset, 4 );
+ m_dwFrames = m_pMPAFile->ExtractBytes(dwOffset, 4 );
+ m_dwTableSize = m_pMPAFile->ExtractBytes(dwOffset, 2 ) + 1; //!!!
+ m_dwTableScale = m_pMPAFile->ExtractBytes(dwOffset, 2 );
+ m_dwBytesPerEntry = m_pMPAFile->ExtractBytes(dwOffset, 2 );
+ m_dwFramesPerEntry = m_pMPAFile->ExtractBytes(dwOffset, 2 );
+
+ // extract TOC (for more accurate seeking)
+ m_pnToc = new int[m_dwTableSize];
+ if( m_pnToc )
+ {
+ for ( unsigned int i = 0 ; i < m_dwTableSize ; i++)
+ {
+ m_pnToc[i] = m_pMPAFile->ExtractBytes(dwOffset, m_dwBytesPerEntry );
+ }
+ }
+ return true;
+}
+
+CVBRHeader::~CVBRHeader(void)
+{
+ if( m_pnToc )
+ delete[] m_pnToc;
+}
+
+// get byte position for percentage value (fPercent) of file
+bool CVBRHeader::SeekPoint(float fPercent, uint32& dwSeekPoint)
+{
+ if( !m_pnToc || m_dwBytes == 0 )
+ return false;
+
+ if( fPercent < 0.0f )
+ fPercent = 0.0f;
+ if( fPercent > 100.0f )
+ fPercent = 100.0f;
+
+ switch( m_HeaderType )
+ {
+ case XINGHeader:
+ dwSeekPoint = SeekPointXING( fPercent );
+ break;
+ case VBRIHeader:
+ dwSeekPoint = SeekPointVBRI( fPercent );
+ break;
+ }
+ return true;
+}
+
+uint32 CVBRHeader::SeekPointXING(float fPercent) const
+{
+ // interpolate in TOC to get file seek point in bytes
+ int a;
+ float fa, fb, fx;
+
+ a = (int)fPercent;
+ if( a > 99 ) a = 99;
+ fa = (float)m_pnToc[a];
+
+ if( a < 99 )
+ {
+ fb = (float)m_pnToc[a+1];
+ }
+ else
+ {
+ fb = 256.0f;
+ }
+
+ fx = fa + (fb-fa)*(fPercent-a);
+
+ uint32 dwSeekpoint = (int)((1.0f/256.0f)*fx*m_dwBytes);
+ return dwSeekpoint;
+}
+
+uint32 CVBRHeader::SeekPointVBRI(float fPercent) const
+{
+ return SeekPointByTimeVBRI( (fPercent/100.0f) * m_pMPAFile->m_pMPAHeader->GetLengthSecond( m_dwFrames ) * 1000.0f );
+}
+
+uint32 CVBRHeader::SeekPointByTimeVBRI(float fEntryTimeMS) const
+{
+ unsigned int i=0, fraction = 0;
+ uint32 dwSeekPoint = 0;
+
+ float fLengthMS;
+ float fLengthMSPerTOCEntry;
+ float fAccumulatedTimeMS = 0.0f ;
+
+ fLengthMS = (float)m_pMPAFile->m_pMPAHeader->GetLengthSecond( m_dwFrames ) * 1000.0f ;
+ fLengthMSPerTOCEntry = fLengthMS / (float)m_dwTableSize;
+
+ if ( fEntryTimeMS > fLengthMS )
+ fEntryTimeMS = fLengthMS;
+
+ while ( fAccumulatedTimeMS <= fEntryTimeMS )
+ {
+ dwSeekPoint += m_pnToc[i++];
+ fAccumulatedTimeMS += fLengthMSPerTOCEntry;
+ }
+
+ // Searched too far; correct result
+ fraction = ( (int)(((( fAccumulatedTimeMS - fEntryTimeMS ) / fLengthMSPerTOCEntry )
+ + (1.0f/(2.0f*(float)m_dwFramesPerEntry))) * (float)m_dwFramesPerEntry));
+
+ dwSeekPoint -= (uint32)((float)m_pnToc[i-1] * (float)(fraction)
+ / (float)m_dwFramesPerEntry);
+
+ return dwSeekPoint;
+}
+