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
238 lines
5.9 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
|
|
#include "audio_pch.h" |
|
#include "snd_mp3_source.h" |
|
#include "snd_wave_mixer_mp3.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#ifndef DEDICATED // have to test this because VPC is forcing us to compile this file. |
|
|
|
extern IVAudio *vaudio; |
|
|
|
CAudioMixerWaveMP3::CAudioMixerWaveMP3( IWaveData *data ) : CAudioMixerWave( data ) |
|
{ |
|
m_sampleCount = 0; |
|
m_samplePosition = 0; |
|
m_offset = 0; |
|
m_delaySamples = 0; |
|
m_headerOffset = 0; |
|
m_pStream = NULL; |
|
m_bStreamInit = false; |
|
m_channelCount = 0; |
|
} |
|
|
|
|
|
CAudioMixerWaveMP3::~CAudioMixerWaveMP3( void ) |
|
{ |
|
if ( m_pStream ) |
|
delete m_pStream; |
|
} |
|
|
|
|
|
void CAudioMixerWaveMP3::Mix( IAudioDevice *pDevice, channel_t *pChannel, void *pData, int outputOffset, int inputOffset, fixedint fracRate, int outCount, int timecompress ) |
|
{ |
|
Assert( IsReadyToMix() ); |
|
if ( m_channelCount == 1 ) |
|
pDevice->Mix16Mono( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress ); |
|
else |
|
pDevice->Mix16Stereo( pChannel, (short *)pData, outputOffset, inputOffset, fracRate, outCount, timecompress ); |
|
} |
|
|
|
|
|
// Some MP3 files are wrapped in ID3 |
|
void CAudioMixerWaveMP3::GetID3HeaderOffset() |
|
{ |
|
char copyBuf[AUDIOSOURCE_COPYBUF_SIZE]; |
|
byte *pData; |
|
|
|
int bytesRead = m_pData->ReadSourceData( (void **)&pData, 0, 10, copyBuf ); |
|
if ( bytesRead < 10 ) |
|
return; |
|
|
|
m_headerOffset = 0; |
|
if (( pData[ 0 ] == 0x49 ) && |
|
( pData[ 1 ] == 0x44 ) && |
|
( pData[ 2 ] == 0x33 ) && |
|
( pData[ 3 ] < 0xff ) && |
|
( pData[ 4 ] < 0xff ) && |
|
( pData[ 6 ] < 0x80 ) && |
|
( pData[ 7 ] < 0x80 ) && |
|
( pData[ 8 ] < 0x80 ) && |
|
( pData[ 9 ] < 0x80 ) ) |
|
{ |
|
// this is in id3 file |
|
// compute the size of the wrapper and skip it |
|
m_headerOffset = 10 + ( pData[9] | (pData[8]<<7) | (pData[7]<<14) | (pData[6]<<21) ); |
|
} |
|
} |
|
|
|
int CAudioMixerWaveMP3::StreamRequestData( void *pBuffer, int bytesRequested, int offset ) |
|
{ |
|
if ( offset < 0 ) |
|
{ |
|
offset = m_offset; |
|
} |
|
else |
|
{ |
|
m_offset = offset; |
|
} |
|
// read the data out of the source |
|
int totalBytesRead = 0; |
|
|
|
if ( offset == 0 ) |
|
{ |
|
// top of file, check for ID3 wrapper |
|
GetID3HeaderOffset(); |
|
} |
|
|
|
offset += m_headerOffset; // skip any id3 header/wrapper |
|
|
|
while ( bytesRequested > 0 ) |
|
{ |
|
char *pOutputBuffer = (char *)pBuffer; |
|
pOutputBuffer += totalBytesRead; |
|
|
|
void *pData = NULL; |
|
int bytesRead = m_pData->ReadSourceData( &pData, offset + totalBytesRead, bytesRequested, pOutputBuffer ); |
|
|
|
if ( !bytesRead ) |
|
break; |
|
if ( bytesRead > bytesRequested ) |
|
{ |
|
bytesRead = bytesRequested; |
|
} |
|
// if the source is buffering it, copy it to the MP3 decomp buffer |
|
if ( pData != pOutputBuffer ) |
|
{ |
|
memcpy( pOutputBuffer, pData, bytesRead ); |
|
} |
|
totalBytesRead += bytesRead; |
|
bytesRequested -= bytesRead; |
|
} |
|
|
|
m_offset += totalBytesRead; |
|
return totalBytesRead; |
|
} |
|
|
|
bool CAudioMixerWaveMP3::DecodeBlock() |
|
{ |
|
IAudioStream *pStream = GetStream(); |
|
if ( !pStream ) |
|
{ |
|
return false; |
|
} |
|
|
|
m_sampleCount = pStream->Decode( m_samples, sizeof(m_samples) ); |
|
m_samplePosition = 0; |
|
return m_sampleCount > 0; |
|
} |
|
|
|
IAudioStream *CAudioMixerWaveMP3::GetStream() |
|
{ |
|
if ( !m_bStreamInit ) |
|
{ |
|
m_bStreamInit = true; |
|
|
|
if ( vaudio ) |
|
{ |
|
m_pStream = vaudio->CreateMP3StreamDecoder( static_cast<IAudioStreamEvent *>(this) ); |
|
} |
|
else |
|
{ |
|
Warning( "Attempting to play MP3 with no vaudio [ %s ]\n", m_pData->Source().GetFileName() ); |
|
} |
|
|
|
if ( m_pStream ) |
|
{ |
|
m_channelCount = m_pStream->GetOutputChannels(); |
|
//Assert( m_pStream->GetOutputRate() == m_pData->Source().SampleRate() ); |
|
} |
|
|
|
if ( !m_pStream ) |
|
{ |
|
Warning( "Failed to create decoder for MP3 [ %s ]\n", m_pData->Source().GetFileName() ); |
|
} |
|
} |
|
|
|
return m_pStream; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Read existing buffer or decompress a new block when necessary |
|
// Input : **pData - output data pointer |
|
// sampleCount - number of samples (or pairs) |
|
// Output : int - available samples (zero to stop decoding) |
|
//----------------------------------------------------------------------------- |
|
int CAudioMixerWaveMP3::GetOutputData( void **pData, int sampleCount, char copyBuf[AUDIOSOURCE_COPYBUF_SIZE] ) |
|
{ |
|
if ( m_samplePosition >= m_sampleCount ) |
|
{ |
|
if ( !DecodeBlock() ) |
|
return 0; |
|
} |
|
|
|
IAudioStream *pStream = GetStream(); |
|
if ( !pStream ) |
|
{ |
|
// Needed for channel count, and with a failed stream init we probably should fail to return data anyway. |
|
return 0; |
|
} |
|
|
|
if ( m_samplePosition < m_sampleCount ) |
|
{ |
|
int sampleSize = pStream->GetOutputChannels() * 2; |
|
*pData = (void *)(m_samples + m_samplePosition); |
|
int available = m_sampleCount - m_samplePosition; |
|
int bytesRequired = sampleCount * sampleSize; |
|
if ( available > bytesRequired ) |
|
available = bytesRequired; |
|
|
|
m_samplePosition += available; |
|
|
|
int samples_loaded = available / sampleSize; |
|
|
|
// update count of max samples loaded in CAudioMixerWave |
|
|
|
CAudioMixerWave::m_sample_max_loaded += samples_loaded; |
|
|
|
// update index of last sample loaded |
|
|
|
CAudioMixerWave::m_sample_loaded_index += samples_loaded; |
|
|
|
return samples_loaded; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Seek to a new position in the file |
|
// NOTE: In most cases, only call this once, and call it before playing |
|
// any data. |
|
// Input : newPosition - new position in the sample clocks of this sample |
|
//----------------------------------------------------------------------------- |
|
void CAudioMixerWaveMP3::SetSampleStart( int newPosition ) |
|
{ |
|
// UNDONE: Implement this? |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : delaySamples - |
|
//----------------------------------------------------------------------------- |
|
void CAudioMixerWaveMP3::SetStartupDelaySamples( int delaySamples ) |
|
{ |
|
m_delaySamples = delaySamples; |
|
} |
|
|
|
#endif
|
|
|