diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/audio/private/snd_wave_mixer_mp3.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'engine/audio/private/snd_wave_mixer_mp3.cpp')
| -rw-r--r-- | engine/audio/private/snd_wave_mixer_mp3.cpp | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/engine/audio/private/snd_wave_mixer_mp3.cpp b/engine/audio/private/snd_wave_mixer_mp3.cpp new file mode 100644 index 0000000..8eaa16f --- /dev/null +++ b/engine/audio/private/snd_wave_mixer_mp3.cpp @@ -0,0 +1,238 @@ +//========= 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 |