Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
7.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "audio_pch.h"
  7. #include "tier0/platform.h"
  8. #include "MPAFile.h" // also includes vbrheader.h
  9. #include "tier0/dbg.h"
  10. #ifndef MAKEFOURCC
  11. #define MAKEFOURCC(ch0, ch1, ch2, ch3) \
  12. ((uint32)(BYTE)(ch0) | ((uint32)(BYTE)(ch1) << 8) | \
  13. ((uint32)(BYTE)(ch2) << 16) | ((uint32)(BYTE)(ch3) << 24 ))
  14. #endif //defined(MAKEFOURCC)
  15. // XING Header offset: 1. index = lsf, 2. index = mono
  16. uint32 CVBRHeader::m_dwXINGOffsets[2][2] =
  17. {
  18. // MPEG 1 (not mono, mono)
  19. { 32 + MPA_HEADER_SIZE, 17 + MPA_HEADER_SIZE },
  20. // MPEG 2/2.5
  21. { 17 + MPA_HEADER_SIZE, 9 + MPA_HEADER_SIZE }
  22. };
  23. // first test with this static method, if it does exist
  24. bool CVBRHeader::IsVBRHeaderAvailable( CMPAFile* pMPAFile, VBRHeaderType& HeaderType, uint32& dwOffset )
  25. {
  26. Assert(pMPAFile);
  27. // where does VBR header begin (XING)
  28. uint32 dwNewOffset = dwOffset + m_dwXINGOffsets[pMPAFile->m_pMPAHeader->IsLSF()][pMPAFile->m_pMPAHeader->IsMono()];
  29. // check for XING header first
  30. if( CheckXING( pMPAFile, dwNewOffset ) )
  31. {
  32. HeaderType = XINGHeader;
  33. // seek offset back to header begin
  34. dwOffset = dwNewOffset - 4;
  35. return true;
  36. }
  37. // VBRI header always at fixed offset
  38. dwNewOffset = dwOffset + 32 + MPA_HEADER_SIZE;
  39. if( CheckVBRI( pMPAFile, dwNewOffset ) )
  40. {
  41. HeaderType = VBRIHeader;
  42. // seek offset back to header begin
  43. dwOffset = dwNewOffset - 4;
  44. return true;
  45. }
  46. HeaderType = NoHeader;
  47. return false;
  48. }
  49. CVBRHeader::CVBRHeader( CMPAFile* pMPAFile, VBRHeaderType HeaderType, uint32 dwOffset ) :
  50. m_pMPAFile( pMPAFile ), m_pnToc(NULL), m_HeaderType( HeaderType ), m_dwOffset(dwOffset), m_dwFrames(0), m_dwBytes(0)
  51. {
  52. switch( m_HeaderType )
  53. {
  54. case NoHeader:
  55. // no Header found
  56. throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
  57. break;
  58. case XINGHeader:
  59. if( !ExtractXINGHeader( m_dwOffset ) )
  60. throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
  61. break;
  62. case VBRIHeader:
  63. if( !ExtractVBRIHeader( m_dwOffset ) )
  64. throw CMPAException( CMPAException::NoVBRHeader, pMPAFile->GetFilename(), NULL, false );
  65. break;
  66. }
  67. // calc bitrate
  68. if( m_dwBytes > 0 && m_dwFrames > 0 )
  69. {
  70. // calc number of seconds
  71. m_dwBytesPerSec = m_pMPAFile->m_pMPAHeader->GetBytesPerSecond( m_dwFrames, m_dwBytes );
  72. }
  73. else // incomplete header found
  74. {
  75. throw CMPAException( CMPAException::IncompleteVBRHeader, pMPAFile->GetFilename(), NULL, false );
  76. }
  77. }
  78. bool CVBRHeader::CheckID( CMPAFile* pMPAFile, char ch0, char ch1, char ch2, char ch3, uint32& dwOffset )
  79. {
  80. return ( pMPAFile->ExtractBytes( dwOffset, 4 ) == MAKEFOURCC( ch3, ch2, ch1, ch0 ) );
  81. }
  82. bool CVBRHeader::CheckXING( CMPAFile* pMPAFile, uint32& dwOffset )
  83. {
  84. // XING ID found?
  85. if( !CheckID( pMPAFile, 'X', 'i', 'n', 'g', dwOffset) && !CheckID( pMPAFile, 'I', 'n', 'f', 'o', dwOffset) )
  86. return false;
  87. return true;
  88. }
  89. bool CVBRHeader::CheckVBRI( CMPAFile* pMPAFile, uint32& dwOffset )
  90. {
  91. // VBRI ID found?
  92. if( !CheckID( pMPAFile, 'V', 'B', 'R', 'I', dwOffset ) )
  93. return false;
  94. return true;
  95. }
  96. // currently not used
  97. bool CVBRHeader::ExtractLAMETag( uint32 dwOffset )
  98. {
  99. // LAME ID found?
  100. if( !CheckID( m_pMPAFile, 'L', 'A', 'M', 'E', dwOffset ) && !CheckID( m_pMPAFile, 'G', 'O', 'G', 'O', dwOffset ) )
  101. return false;
  102. return true;
  103. }
  104. bool CVBRHeader::ExtractXINGHeader( uint32 dwOffset )
  105. {
  106. /* XING VBR-Header
  107. size description
  108. 4 'Xing' or 'Info'
  109. 4 flags (indicates which fields are used)
  110. 4 frames (optional)
  111. 4 bytes (optional)
  112. 100 toc (optional)
  113. 4 a VBR quality indicator: 0=best 100=worst (optional)
  114. */
  115. if( !CheckXING( m_pMPAFile, dwOffset ) )
  116. return false;
  117. uint32 dwFlags;
  118. // get flags (mandatory in XING header)
  119. dwFlags = m_pMPAFile->ExtractBytes( dwOffset, 4 );
  120. // extract total number of frames in file
  121. if(dwFlags & FRAMES_FLAG)
  122. m_dwFrames = m_pMPAFile->ExtractBytes(dwOffset,4);
  123. // extract total number of bytes in file
  124. if(dwFlags & BYTES_FLAG)
  125. m_dwBytes = m_pMPAFile->ExtractBytes(dwOffset,4);
  126. // extract TOC (for more accurate seeking)
  127. if (dwFlags & TOC_FLAG)
  128. {
  129. m_dwTableSize = 100;
  130. m_pnToc = new int[m_dwTableSize];
  131. if( m_pnToc )
  132. {
  133. for(uint32 i=0;i<m_dwTableSize;i++)
  134. m_pnToc[i] = m_pMPAFile->ExtractBytes( dwOffset, 1 );
  135. }
  136. }
  137. m_dwQuality = (uint32)-1;
  138. if(dwFlags & VBR_SCALE_FLAG )
  139. m_dwQuality = m_pMPAFile->ExtractBytes(dwOffset, 4);
  140. return true;
  141. }
  142. bool CVBRHeader::ExtractVBRIHeader( uint32 dwOffset )
  143. {
  144. /* FhG VBRI Header
  145. size description
  146. 4 'VBRI' (ID)
  147. 2 version
  148. 2 delay
  149. 2 quality
  150. 4 # bytes
  151. 4 # frames
  152. 2 table size (for TOC)
  153. 2 table scale (for TOC)
  154. 2 size of table entry (max. size = 4 byte (must be stored in an integer))
  155. 2 frames per table entry
  156. ?? dynamic table consisting out of frames with size 1-4
  157. whole length in table size! (for TOC)
  158. */
  159. if( !CheckVBRI( m_pMPAFile, dwOffset ) )
  160. return false;
  161. // extract all fields from header (all mandatory)
  162. m_dwVersion = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  163. m_fDelay = (float)m_pMPAFile->ExtractBytes(dwOffset, 2 );
  164. m_dwQuality = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  165. m_dwBytes = m_pMPAFile->ExtractBytes(dwOffset, 4 );
  166. m_dwFrames = m_pMPAFile->ExtractBytes(dwOffset, 4 );
  167. m_dwTableSize = m_pMPAFile->ExtractBytes(dwOffset, 2 ) + 1; //!!!
  168. m_dwTableScale = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  169. m_dwBytesPerEntry = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  170. m_dwFramesPerEntry = m_pMPAFile->ExtractBytes(dwOffset, 2 );
  171. // extract TOC (for more accurate seeking)
  172. m_pnToc = new int[m_dwTableSize];
  173. if( m_pnToc )
  174. {
  175. for ( unsigned int i = 0 ; i < m_dwTableSize ; i++)
  176. {
  177. m_pnToc[i] = m_pMPAFile->ExtractBytes(dwOffset, m_dwBytesPerEntry );
  178. }
  179. }
  180. return true;
  181. }
  182. CVBRHeader::~CVBRHeader(void)
  183. {
  184. if( m_pnToc )
  185. delete[] m_pnToc;
  186. }
  187. // get byte position for percentage value (fPercent) of file
  188. bool CVBRHeader::SeekPoint(float fPercent, uint32& dwSeekPoint)
  189. {
  190. if( !m_pnToc || m_dwBytes == 0 )
  191. return false;
  192. if( fPercent < 0.0f )
  193. fPercent = 0.0f;
  194. if( fPercent > 100.0f )
  195. fPercent = 100.0f;
  196. switch( m_HeaderType )
  197. {
  198. case XINGHeader:
  199. dwSeekPoint = SeekPointXING( fPercent );
  200. break;
  201. case VBRIHeader:
  202. dwSeekPoint = SeekPointVBRI( fPercent );
  203. break;
  204. }
  205. return true;
  206. }
  207. uint32 CVBRHeader::SeekPointXING(float fPercent) const
  208. {
  209. // interpolate in TOC to get file seek point in bytes
  210. int a;
  211. float fa, fb, fx;
  212. a = (int)fPercent;
  213. if( a > 99 ) a = 99;
  214. fa = (float)m_pnToc[a];
  215. if( a < 99 )
  216. {
  217. fb = (float)m_pnToc[a+1];
  218. }
  219. else
  220. {
  221. fb = 256.0f;
  222. }
  223. fx = fa + (fb-fa)*(fPercent-a);
  224. uint32 dwSeekpoint = (int)((1.0f/256.0f)*fx*m_dwBytes);
  225. return dwSeekpoint;
  226. }
  227. uint32 CVBRHeader::SeekPointVBRI(float fPercent) const
  228. {
  229. return SeekPointByTimeVBRI( (fPercent/100.0f) * m_pMPAFile->m_pMPAHeader->GetLengthSecond( m_dwFrames ) * 1000.0f );
  230. }
  231. uint32 CVBRHeader::SeekPointByTimeVBRI(float fEntryTimeMS) const
  232. {
  233. unsigned int i=0, fraction = 0;
  234. uint32 dwSeekPoint = 0;
  235. float fLengthMS;
  236. float fLengthMSPerTOCEntry;
  237. float fAccumulatedTimeMS = 0.0f ;
  238. fLengthMS = (float)m_pMPAFile->m_pMPAHeader->GetLengthSecond( m_dwFrames ) * 1000.0f ;
  239. fLengthMSPerTOCEntry = fLengthMS / (float)m_dwTableSize;
  240. if ( fEntryTimeMS > fLengthMS )
  241. fEntryTimeMS = fLengthMS;
  242. while ( fAccumulatedTimeMS <= fEntryTimeMS )
  243. {
  244. dwSeekPoint += m_pnToc[i++];
  245. fAccumulatedTimeMS += fLengthMSPerTOCEntry;
  246. }
  247. // Searched too far; correct result
  248. fraction = ( (int)(((( fAccumulatedTimeMS - fEntryTimeMS ) / fLengthMSPerTOCEntry )
  249. + (1.0f/(2.0f*(float)m_dwFramesPerEntry))) * (float)m_dwFramesPerEntry));
  250. dwSeekPoint -= (uint32)((float)m_pnToc[i-1] * (float)(fraction)
  251. / (float)m_dwFramesPerEntry);
  252. return dwSeekPoint;
  253. }