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.

238 lines
5.9 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "audio_pch.h"
  8. #include "snd_mp3_source.h"
  9. #include "snd_wave_mixer_mp3.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. #ifndef DEDICATED // have to test this because VPC is forcing us to compile this file.
  13. extern IVAudio *vaudio;
  14. CAudioMixerWaveMP3::CAudioMixerWaveMP3( IWaveData *data ) : CAudioMixerWave( data )
  15. {
  16. m_sampleCount = 0;
  17. m_samplePosition = 0;
  18. m_offset = 0;
  19. m_delaySamples = 0;
  20. m_headerOffset = 0;
  21. m_pStream = NULL;
  22. m_bStreamInit = false;
  23. m_channelCount = 0;
  24. }
  25. CAudioMixerWaveMP3::~CAudioMixerWaveMP3( void )
  26. {
  27. if ( m_pStream )
  28. delete m_pStream;
  29. }
  30. void CAudioMixerWaveMP3::Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress )
  31. {
  32. Assert( IsReadyToMix() );
  33. if ( m_channelCount == 1 )
  34. pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress );
  35. else
  36. pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress );
  37. }
  38. // Some MP3 files are wrapped in ID3
  39. void CAudioMixerWaveMP3::GetID3HeaderOffset()
  40. {
  41. char copyBuf[AUDIOSOURCE_COPYBUF_SIZE];
  42. byte *pData;
  43. int bytesRead = m_pData->ReadSourceData( (void **)&pData, 0, 10, copyBuf );
  44. if ( bytesRead < 10 )
  45. return;
  46. m_headerOffset = 0;
  47. if (( pData[ 0 ] == 0x49 ) &&
  48. ( pData[ 1 ] == 0x44 ) &&
  49. ( pData[ 2 ] == 0x33 ) &&
  50. ( pData[ 3 ] < 0xff ) &&
  51. ( pData[ 4 ] < 0xff ) &&
  52. ( pData[ 6 ] < 0x80 ) &&
  53. ( pData[ 7 ] < 0x80 ) &&
  54. ( pData[ 8 ] < 0x80 ) &&
  55. ( pData[ 9 ] < 0x80 ) )
  56. {
  57. // this is in id3 file
  58. // compute the size of the wrapper and skip it
  59. m_headerOffset = 10 + ( pData[9] | (pData[8]<<7) | (pData[7]<<14) | (pData[6]<<21) );
  60. }
  61. }
  62. int CAudioMixerWaveMP3::StreamRequestData( void *pBuffer, int bytesRequested, int offset )
  63. {
  64. if ( offset < 0 )
  65. {
  66. offset = m_offset;
  67. }
  68. else
  69. {
  70. m_offset = offset;
  71. }
  72. // read the data out of the source
  73. int totalBytesRead = 0;
  74. if ( offset == 0 )
  75. {
  76. // top of file, check for ID3 wrapper
  77. GetID3HeaderOffset();
  78. }
  79. offset += m_headerOffset; // skip any id3 header/wrapper
  80. while ( bytesRequested > 0 )
  81. {
  82. char *pOutputBuffer = (char *)pBuffer;
  83. pOutputBuffer += totalBytesRead;
  84. void *pData = NULL;
  85. int bytesRead = m_pData->ReadSourceData( &pData, offset + totalBytesRead, bytesRequested, pOutputBuffer );
  86. if ( !bytesRead )
  87. break;
  88. if ( bytesRead > bytesRequested )
  89. {
  90. bytesRead = bytesRequested;
  91. }
  92. // if the source is buffering it, copy it to the MP3 decomp buffer
  93. if ( pData != pOutputBuffer )
  94. {
  95. memcpy( pOutputBuffer, pData, bytesRead );
  96. }
  97. totalBytesRead += bytesRead;
  98. bytesRequested -= bytesRead;
  99. }
  100. m_offset += totalBytesRead;
  101. return totalBytesRead;
  102. }
  103. bool CAudioMixerWaveMP3::DecodeBlock()
  104. {
  105. IAudioStream *pStream = GetStream();
  106. if ( !pStream )
  107. {
  108. return false;
  109. }
  110. m_sampleCount = pStream->Decode( m_samples, sizeof(m_samples) );
  111. m_samplePosition = 0;
  112. return m_sampleCount > 0;
  113. }
  114. IAudioStream *CAudioMixerWaveMP3::GetStream()
  115. {
  116. if ( !m_bStreamInit )
  117. {
  118. m_bStreamInit = true;
  119. if ( vaudio )
  120. {
  121. m_pStream = vaudio->CreateMP3StreamDecoder( static_cast<IAudioStreamEvent *>(this) );
  122. }
  123. else
  124. {
  125. Warning( "Attempting to play MP3 with no vaudio [ %s ]\n", m_pData->Source().GetFileName() );
  126. }
  127. if ( m_pStream )
  128. {
  129. m_channelCount = m_pStream->GetOutputChannels();
  130. //Assert( m_pStream->GetOutputRate() == m_pData->Source().SampleRate() );
  131. }
  132. if ( !m_pStream )
  133. {
  134. Warning( "Failed to create decoder for MP3 [ %s ]\n", m_pData->Source().GetFileName() );
  135. }
  136. }
  137. return m_pStream;
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose: Read existing buffer or decompress a new block when necessary
  141. // Input : **pData - output data pointer
  142. // sampleCount - number of samples (or pairs)
  143. // Output : int - available samples (zero to stop decoding)
  144. //-----------------------------------------------------------------------------
  145. int CAudioMixerWaveMP3::GetOutputData( void **pData, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] )
  146. {
  147. if ( m_samplePosition >= m_sampleCount )
  148. {
  149. if ( !DecodeBlock() )
  150. return 0;
  151. }
  152. IAudioStream *pStream = GetStream();
  153. if ( !pStream )
  154. {
  155. // Needed for channel count, and with a failed stream init we probably should fail to return data anyway.
  156. return 0;
  157. }
  158. if ( m_samplePosition < m_sampleCount )
  159. {
  160. int sampleSize = pStream->GetOutputChannels() * 2;
  161. *pData = (void *)(m_samples + m_samplePosition);
  162. int available = m_sampleCount - m_samplePosition;
  163. int bytesRequired = sampleCount * sampleSize;
  164. if ( available > bytesRequired )
  165. available = bytesRequired;
  166. m_samplePosition += available;
  167. int samples_loaded = available / sampleSize;
  168. // update count of max samples loaded in CAudioMixerWave
  169. CAudioMixerWave::m_sample_max_loaded += samples_loaded;
  170. // update index of last sample loaded
  171. CAudioMixerWave::m_sample_loaded_index += samples_loaded;
  172. return samples_loaded;
  173. }
  174. return 0;
  175. }
  176. //-----------------------------------------------------------------------------
  177. // Purpose: Seek to a new position in the file
  178. // NOTE: In most cases, only call this once, and call it before playing
  179. // any data.
  180. // Input : newPosition - new position in the sample clocks of this sample
  181. //-----------------------------------------------------------------------------
  182. void CAudioMixerWaveMP3::SetSampleStart( int newPosition )
  183. {
  184. // UNDONE: Implement this?
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose:
  188. // Input : delaySamples -
  189. //-----------------------------------------------------------------------------
  190. void CAudioMixerWaveMP3::SetStartupDelaySamples( int delaySamples )
  191. {
  192. m_delaySamples = delaySamples;
  193. }
  194. #endif