Counter Strike : Global Offensive Source Code
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.

308 lines
7.8 KiB

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