Simplify RSR_Chain. Allow passing in sound readers directly.
If all sounds have one channel and nothing pans, keep one channel. Use RSR_Pan.
This commit is contained in:
@@ -38,7 +38,7 @@ void AutoKeysounds::FinishLoading()
|
|||||||
|
|
||||||
Song* pSong = GAMESTATE->m_pCurSong;
|
Song* pSong = GAMESTATE->m_pCurSong;
|
||||||
pChain->SetPreferredSampleRate( SOUNDMAN->GetDriverSampleRate(44100) );
|
pChain->SetPreferredSampleRate( SOUNDMAN->GetDriverSampleRate(44100) );
|
||||||
pChain->AddSound( pSong->GetMusicPath(), 0, 0 );
|
pChain->AddSound( pChain->LoadSound(pSong->GetMusicPath()), 0, 0 );
|
||||||
|
|
||||||
//
|
//
|
||||||
// Load sounds.
|
// Load sounds.
|
||||||
@@ -116,7 +116,8 @@ void AutoKeysounds::FinishLoading()
|
|||||||
float fPan = 0;
|
float fPan = 0;
|
||||||
if( !bSoundIsGlobal )
|
if( !bSoundIsGlobal )
|
||||||
fPan = (pn == PLAYER_1)? -1.0f:+1.0f;
|
fPan = (pn == PLAYER_1)? -1.0f:+1.0f;
|
||||||
pChain->AddSound( sKeysoundFilePath, fSeconds, fPan );
|
int iIndex = pChain->LoadSound( sKeysoundFilePath );
|
||||||
|
pChain->AddSound( iIndex, fSeconds, fPan );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
#include "RageSoundReader_FileReader.h"
|
#include "RageSoundReader_FileReader.h"
|
||||||
#include "RageSoundReader_Resample_Good.h"
|
#include "RageSoundReader_Resample_Good.h"
|
||||||
#include "RageSoundReader_Preload.h"
|
#include "RageSoundReader_Preload.h"
|
||||||
|
#include "RageSoundReader_Pan.h"
|
||||||
#include "RageLog.h"
|
#include "RageLog.h"
|
||||||
#include "RageUtil.h"
|
#include "RageUtil.h"
|
||||||
#include "RageSoundMixBuffer.h"
|
#include "RageSoundMixBuffer.h"
|
||||||
#include "RageSoundUtil.h"
|
#include "RageSoundUtil.h"
|
||||||
|
#include "Foreach.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Keyed sounds should pass this object to SoundReader_Preload, to preprocess it.
|
* Keyed sounds should pass this object to SoundReader_Preload, to preprocess it.
|
||||||
@@ -29,11 +31,11 @@ RageSoundReader_Chain::~RageSoundReader_Chain()
|
|||||||
{
|
{
|
||||||
/* Clear m_apActiveSounds. */
|
/* Clear m_apActiveSounds. */
|
||||||
while( !m_apActiveSounds.empty() )
|
while( !m_apActiveSounds.empty() )
|
||||||
ReleaseSound( 0 );
|
ReleaseSound( m_apActiveSounds.front() );
|
||||||
|
|
||||||
map<RString, RageSoundReader *>::iterator it;
|
map<RString, RageSoundReader *>::iterator it;
|
||||||
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
|
FOREACH( RageSoundReader *, m_apLoadedSounds, it )
|
||||||
delete it->second;
|
delete *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
RageSoundReader_Chain *RageSoundReader_Chain::Copy() const
|
RageSoundReader_Chain *RageSoundReader_Chain::Copy() const
|
||||||
@@ -45,33 +47,53 @@ RageSoundReader_Chain *RageSoundReader_Chain::Copy() const
|
|||||||
/* The same sound may be used several times, and by several different chains. Avoid
|
/* The same sound may be used several times, and by several different chains. Avoid
|
||||||
* loading the same sound multiple times. We need to make a Copy() if we need to
|
* loading the same sound multiple times. We need to make a Copy() if we need to
|
||||||
* read it more than once at a time. */
|
* read it more than once at a time. */
|
||||||
bool RageSoundReader_Chain::AddSound( RString sPath, float fOffsetSecs, float fPan )
|
void RageSoundReader_Chain::AddSound( int iIndex, float fOffsetSecs, float fPan )
|
||||||
|
{
|
||||||
|
if( iIndex == -1 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
Sound s;
|
||||||
|
s.iIndex = iIndex;
|
||||||
|
s.iOffsetMS = lrintf( fOffsetSecs * 1000 );
|
||||||
|
s.fPan = fPan;
|
||||||
|
s.pSound = NULL;
|
||||||
|
m_aSounds.push_back( s );
|
||||||
|
}
|
||||||
|
|
||||||
|
int RageSoundReader_Chain::LoadSound( RString sPath )
|
||||||
{
|
{
|
||||||
sPath.MakeLower();
|
sPath.MakeLower();
|
||||||
|
|
||||||
map<RString, RageSoundReader *>::const_iterator it;
|
map<RString, RageSoundReader *>::const_iterator it = m_apNamedSounds.find( sPath );
|
||||||
it = m_apLoadedSounds.find( sPath );
|
if( it != m_apNamedSounds.end() )
|
||||||
if( it == m_apLoadedSounds.end() )
|
|
||||||
{
|
{
|
||||||
RString sError;
|
const RageSoundReader *pReader = it->second;
|
||||||
RageSoundReader *pReader = RageSoundReader_FileReader::OpenFile( sPath, sError );
|
|
||||||
if( pReader == NULL )
|
|
||||||
{
|
|
||||||
LOG->Warn( "RageSoundReader_Chain: error opening sound \"%s\": %s",
|
|
||||||
sPath.c_str(), sError.c_str() );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_apLoadedSounds[sPath] = pReader;
|
for( int i = 0; i < (int) m_apLoadedSounds.size(); ++i )
|
||||||
|
if( m_apLoadedSounds[i] == pReader )
|
||||||
|
return i;
|
||||||
|
FAIL_M( sPath );
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound s;
|
RString sError;
|
||||||
s.sPath = sPath;
|
RageSoundReader *pReader = RageSoundReader_FileReader::OpenFile( sPath, sError );
|
||||||
s.iOffsetMS = lrintf( fOffsetSecs * 1000 );
|
if( pReader == NULL )
|
||||||
s.fPan = fPan;
|
{
|
||||||
m_aSounds.push_back( s );
|
LOG->Warn( "RageSoundReader_Chain: error opening sound \"%s\": %s",
|
||||||
|
sPath.c_str(), sError.c_str() );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
m_apNamedSounds[sPath] = pReader;
|
||||||
|
|
||||||
|
m_apLoadedSounds.push_back( m_apNamedSounds[sPath] );
|
||||||
|
return m_apLoadedSounds.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RageSoundReader_Chain::LoadSound( RageSoundReader *pSound )
|
||||||
|
{
|
||||||
|
m_apLoadedSounds.push_back( pSound );
|
||||||
|
return m_apLoadedSounds.size()-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If every sound has the same sample rate, return it. Otherwise, return -1. */
|
/* If every sound has the same sample rate, return it. Otherwise, return -1. */
|
||||||
@@ -82,11 +104,11 @@ int RageSoundReader_Chain::GetSampleRateInternal() const
|
|||||||
|
|
||||||
map<RString, RageSoundReader *>::const_iterator it;
|
map<RString, RageSoundReader *>::const_iterator it;
|
||||||
int iRate = -1;
|
int iRate = -1;
|
||||||
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
|
FOREACH_CONST( RageSoundReader *, m_apLoadedSounds, it )
|
||||||
{
|
{
|
||||||
if( iRate == -1 )
|
if( iRate == -1 )
|
||||||
iRate = it->second->GetSampleRate();
|
iRate = (*it)->GetSampleRate();
|
||||||
else if( iRate != it->second->GetSampleRate() )
|
else if( iRate != (*it)->GetSampleRate() )
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return iRate;
|
return iRate;
|
||||||
@@ -94,13 +116,33 @@ int RageSoundReader_Chain::GetSampleRateInternal() const
|
|||||||
|
|
||||||
void RageSoundReader_Chain::Finish()
|
void RageSoundReader_Chain::Finish()
|
||||||
{
|
{
|
||||||
|
/* Figure out how many channels we have. All sounds must either have 1 or 2 channels,
|
||||||
|
* which will be converted as needed, or have the same number of channels. */
|
||||||
|
m_iChannels = 1;
|
||||||
|
map<RString, RageSoundReader *>::iterator it;
|
||||||
|
FOREACH( RageSoundReader *, m_apLoadedSounds, it )
|
||||||
|
m_iChannels = max( m_iChannels, (*it)->GetNumChannels() );
|
||||||
|
|
||||||
|
if( m_iChannels > 2 )
|
||||||
|
{
|
||||||
|
FOREACH( RageSoundReader *, m_apLoadedSounds, it )
|
||||||
|
{
|
||||||
|
if( (*it)->GetNumChannels() != m_iChannels )
|
||||||
|
{
|
||||||
|
LOG->Warn( "Discarded sound with %i channels, not %i",
|
||||||
|
(*it)->GetNumChannels(), m_iChannels );
|
||||||
|
delete (*it);
|
||||||
|
(*it) = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Remove any sounds that don't have corresponding RageSoundReaders. */
|
/* Remove any sounds that don't have corresponding RageSoundReaders. */
|
||||||
for( unsigned i = 0; i < m_aSounds.size(); )
|
for( unsigned i = 0; i < m_aSounds.size(); )
|
||||||
{
|
{
|
||||||
Sound &sound = m_aSounds[i];
|
Sound &sound = m_aSounds[i];
|
||||||
|
|
||||||
map<RString, RageSoundReader *>::iterator it = m_apLoadedSounds.find( sound.sPath );
|
if( m_apLoadedSounds[sound.iIndex] == NULL )
|
||||||
if( it == m_apLoadedSounds.end() )
|
|
||||||
{
|
{
|
||||||
m_aSounds.erase( m_aSounds.begin()+i );
|
m_aSounds.erase( m_aSounds.begin()+i );
|
||||||
continue;
|
continue;
|
||||||
@@ -109,19 +151,8 @@ void RageSoundReader_Chain::Finish()
|
|||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Figure out how many channels we have. */
|
|
||||||
m_iChannels = 1;
|
|
||||||
map<RString, RageSoundReader *>::iterator it;
|
|
||||||
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
|
|
||||||
m_iChannels = max( m_iChannels, it->second->GetNumChannels() );
|
|
||||||
|
|
||||||
/* If any sounds have a non-0 pan, we're stereo. */
|
|
||||||
for( unsigned i = 0; i < m_aSounds.size(); ++i )
|
|
||||||
if( fabs(m_aSounds[i].fPan) > 0.0001f )
|
|
||||||
m_iChannels = 2;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We might get different sample rates fro mour sources. If they're all the same
|
* We might get different sample rates from our sources. If they're all the same
|
||||||
* sample rate, just leave it alone, so the whole sound can be resampled as a group.
|
* sample rate, just leave it alone, so the whole sound can be resampled as a group.
|
||||||
* If not, resample eveything to the preferred rate. (Using the preferred rate
|
* If not, resample eveything to the preferred rate. (Using the preferred rate
|
||||||
* should avoid redundant resampling later.)
|
* should avoid redundant resampling later.)
|
||||||
@@ -129,9 +160,9 @@ void RageSoundReader_Chain::Finish()
|
|||||||
m_iActualSampleRate = GetSampleRateInternal();
|
m_iActualSampleRate = GetSampleRateInternal();
|
||||||
if( m_iActualSampleRate == -1 )
|
if( m_iActualSampleRate == -1 )
|
||||||
{
|
{
|
||||||
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
|
FOREACH( RageSoundReader *, m_apLoadedSounds, it )
|
||||||
{
|
{
|
||||||
RageSoundReader *&pSound = it->second;
|
RageSoundReader *&pSound = (*it);
|
||||||
|
|
||||||
RageSoundReader_Resample_Good *pResample = new RageSoundReader_Resample_Good( pSound, m_iPreferredSampleRate );
|
RageSoundReader_Resample_Good *pResample = new RageSoundReader_Resample_Good( pSound, m_iPreferredSampleRate );
|
||||||
pSound = pResample;
|
pSound = pResample;
|
||||||
@@ -141,9 +172,9 @@ void RageSoundReader_Chain::Finish()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Attempt to preload all sounds. */
|
/* Attempt to preload all sounds. */
|
||||||
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
|
FOREACH( RageSoundReader *, m_apLoadedSounds, it )
|
||||||
{
|
{
|
||||||
RageSoundReader *&pSound = it->second;
|
RageSoundReader *&pSound = (*it);
|
||||||
RageSoundReader_Preload::PreloadSound( pSound );
|
RageSoundReader_Preload::PreloadSound( pSound );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,37 +186,34 @@ int RageSoundReader_Chain::SetPosition_Accurate( int iFrame )
|
|||||||
{
|
{
|
||||||
/* Clear m_apActiveSounds. */
|
/* Clear m_apActiveSounds. */
|
||||||
while( !m_apActiveSounds.empty() )
|
while( !m_apActiveSounds.empty() )
|
||||||
ReleaseSound( 0 );
|
ReleaseSound( m_apActiveSounds.front() );
|
||||||
|
|
||||||
m_iCurrentFrame = iFrame;
|
m_iCurrentFrame = iFrame;
|
||||||
|
|
||||||
/* Run through all sounds in the chain, and activate all sounds which have data
|
/* Run through all sounds in the chain, and activate all sounds which have data
|
||||||
* at ms. */
|
* at iFrame. */
|
||||||
for( unsigned i = 0; i < m_aSounds.size(); ++i )
|
for( m_iNextSound = 0; m_iNextSound < m_aSounds.size(); ++m_iNextSound )
|
||||||
{
|
{
|
||||||
Sound &sound = m_aSounds[i];
|
Sound *pSound = &m_aSounds[m_iNextSound];
|
||||||
int iOffsetFrame = sound.GetOffsetFrame( GetSampleRate() );
|
int iOffsetFrame = pSound->GetOffsetFrame( GetSampleRate() );
|
||||||
|
|
||||||
|
|
||||||
/* If this sound is in the future, skip it. */
|
/* If this sound is in the future, skip it. */
|
||||||
if( iOffsetFrame > iFrame )
|
if( iOffsetFrame > iFrame )
|
||||||
continue;
|
break;
|
||||||
|
|
||||||
/* Find the RageSoundReader. */
|
/* Find the RageSoundReader. */
|
||||||
int n = ActivateSound( sound );
|
ActivateSound( pSound );
|
||||||
RageSoundReader *pSound = m_apActiveSounds[n].pSound;
|
RageSoundReader *pReader = pSound->pSound;
|
||||||
|
|
||||||
int iOffsetFrames = iFrame - iOffsetFrame;
|
int iOffsetFrames = iFrame - iOffsetFrame;
|
||||||
if( pSound->SetPosition_Accurate(iOffsetFrames) == 0 )
|
if( iOffsetFrames > 0 && pReader->SetPosition_Accurate(iOffsetFrames) == 0 )
|
||||||
{
|
{
|
||||||
/* We're past the end of this sound. */
|
/* We're past the end of this sound. */
|
||||||
ReleaseSound( n );
|
ReleaseSound( pSound );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_iNextSound = GetNextSoundIndex();
|
|
||||||
|
|
||||||
/* If no sounds were started, and we have no sounds ahead of us, we've seeked
|
/* If no sounds were started, and we have no sounds ahead of us, we've seeked
|
||||||
* past EOF. */
|
* past EOF. */
|
||||||
if( m_apActiveSounds.empty() && m_iNextSound == m_aSounds.size() )
|
if( m_apActiveSounds.empty() && m_iNextSound == m_aSounds.size() )
|
||||||
@@ -194,33 +222,39 @@ int RageSoundReader_Chain::SetPosition_Accurate( int iFrame )
|
|||||||
return iFrame;
|
return iFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned RageSoundReader_Chain::ActivateSound( const Sound &s )
|
void RageSoundReader_Chain::ActivateSound( Sound *s )
|
||||||
{
|
{
|
||||||
RageSoundReader *pSound = m_apLoadedSounds[s.sPath];
|
RageSoundReader *pSound = m_apLoadedSounds[s->iIndex];
|
||||||
|
s->pSound = pSound->Copy();
|
||||||
|
|
||||||
ActiveSound add;
|
/* Add a balance filter. If this source has the same number of channels
|
||||||
add.fPan = s.fPan;
|
* as this sound, and does not need to be panned, we can omit this. */
|
||||||
add.pSound = pSound->Copy();
|
if( s->fPan != 0.0f || s->pSound->GetNumChannels() != this->GetNumChannels() )
|
||||||
|
{
|
||||||
|
s->pSound = new RageSoundReader_Pan( s->pSound );
|
||||||
|
s->pSound->SetProperty( "Pan", s->fPan );
|
||||||
|
}
|
||||||
|
|
||||||
m_apActiveSounds.push_back( add );
|
m_apActiveSounds.push_back( s );
|
||||||
return m_apActiveSounds.size() - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RageSoundReader_Chain::ReleaseSound( unsigned n )
|
void RageSoundReader_Chain::ReleaseSound( Sound *s )
|
||||||
{
|
{
|
||||||
ASSERT_M( n < m_apActiveSounds.size(), ssprintf("%u, %u", n, unsigned(m_apActiveSounds.size())) );
|
vector<Sound *>::iterator it = find( m_apActiveSounds.begin(), m_apActiveSounds.end(), s );
|
||||||
RageSoundReader *pSound = m_apActiveSounds[n].pSound;
|
ASSERT( it != m_apActiveSounds.end() );
|
||||||
|
RageSoundReader *&pSound = s->pSound;
|
||||||
|
|
||||||
delete pSound;
|
delete pSound;
|
||||||
|
pSound = NULL;
|
||||||
|
|
||||||
m_apActiveSounds.erase( m_apActiveSounds.begin()+n );
|
m_apActiveSounds.erase( it );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RageSoundReader_Chain::IsStreamingFromDisk() const
|
bool RageSoundReader_Chain::IsStreamingFromDisk() const
|
||||||
{
|
{
|
||||||
map<RString, RageSoundReader *>::const_iterator it;
|
map<RString, RageSoundReader *>::const_iterator it;
|
||||||
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
|
FOREACH_CONST( RageSoundReader *, m_apLoadedSounds, it )
|
||||||
if( it->second->IsStreamingFromDisk() )
|
if( (*it)->IsStreamingFromDisk() )
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -231,7 +265,7 @@ bool RageSoundReader_Chain::SetProperty( const RString &sProperty, float fValue
|
|||||||
bool bRet = false;
|
bool bRet = false;
|
||||||
for( unsigned i = 0; i < m_apActiveSounds.size(); ++i )
|
for( unsigned i = 0; i < m_apActiveSounds.size(); ++i )
|
||||||
{
|
{
|
||||||
if( m_apActiveSounds[i].pSound->SetProperty(sProperty, fValue) )
|
if( m_apActiveSounds[i]->pSound->SetProperty(sProperty, fValue) )
|
||||||
bRet = true;
|
bRet = true;
|
||||||
}
|
}
|
||||||
return bRet;
|
return bRet;
|
||||||
@@ -240,6 +274,7 @@ bool RageSoundReader_Chain::SetProperty( const RString &sProperty, float fValue
|
|||||||
int RageSoundReader_Chain::GetNextSourceFrame() const
|
int RageSoundReader_Chain::GetNextSourceFrame() const
|
||||||
{
|
{
|
||||||
return m_iCurrentFrame;
|
return m_iCurrentFrame;
|
||||||
|
// XXX: GetPosition is expected to differ by iOffsetMS
|
||||||
/* int iPosition = m_apActiveSounds[0].pSound->GetPosition();
|
/* int iPosition = m_apActiveSounds[0].pSound->GetPosition();
|
||||||
for( unsigned i = 1; i < m_apActiveSounds.size(); )
|
for( unsigned i = 1; i < m_apActiveSounds.size(); )
|
||||||
{
|
{
|
||||||
@@ -256,10 +291,10 @@ float RageSoundReader_Chain::GetStreamToSourceRatio() const
|
|||||||
if( m_apActiveSounds.empty() )
|
if( m_apActiveSounds.empty() )
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
float iRate = m_apActiveSounds[0].pSound->GetStreamToSourceRatio();
|
float iRate = m_apActiveSounds[0]->pSound->GetStreamToSourceRatio();
|
||||||
for( unsigned i = 1; i < m_apActiveSounds.size(); )
|
for( unsigned i = 1; i < m_apActiveSounds.size(); ++i )
|
||||||
{
|
{
|
||||||
if( m_apActiveSounds[i].pSound->GetStreamToSourceRatio() != iRate )
|
if( m_apActiveSounds[i]->pSound->GetStreamToSourceRatio() != iRate )
|
||||||
LOG->Warn( "RageSoundReader_Chain: sound rates changing differently" );
|
LOG->Warn( "RageSoundReader_Chain: sound rates changing differently" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,17 +305,31 @@ float RageSoundReader_Chain::GetStreamToSourceRatio() const
|
|||||||
unsigned RageSoundReader_Chain::GetNextSoundIndex() const
|
unsigned RageSoundReader_Chain::GetNextSoundIndex() const
|
||||||
{
|
{
|
||||||
unsigned iNextSound = 0;
|
unsigned iNextSound = 0;
|
||||||
while( iNextSound < m_aSounds.size() && m_iCurrentFrame > m_aSounds[iNextSound].GetOffsetFrame(m_iActualSampleRate) )
|
while( iNextSound < m_aSounds.size() )
|
||||||
|
{
|
||||||
|
if( m_iCurrentFrame <= m_aSounds[iNextSound].GetOffsetFrame(m_iActualSampleRate) ||
|
||||||
|
m_aSounds[iNextSound].pSound == NULL )
|
||||||
|
break;
|
||||||
|
if( m_aSounds[iNextSound].pSound )
|
||||||
|
++iNextSound;
|
||||||
|
}
|
||||||
|
|
||||||
|
while( iNextSound < m_aSounds.size() &&
|
||||||
|
m_iCurrentFrame > m_aSounds[iNextSound].GetOffsetFrame(m_iActualSampleRate) &&
|
||||||
|
m_aSounds[iNextSound].pSound
|
||||||
|
)
|
||||||
++iNextSound;
|
++iNextSound;
|
||||||
return iNextSound;
|
return iNextSound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* As we iterate through the sound tree, we'll find that we need data from different
|
||||||
|
* sounds; a sound may be needed by more than one other sound. */
|
||||||
int RageSoundReader_Chain::ReadBlock( int16_t *pBuffer, int iFrames )
|
int RageSoundReader_Chain::ReadBlock( int16_t *pBuffer, int iFrames )
|
||||||
{
|
{
|
||||||
/* How many samples should we read before we need to start up a sound? */
|
/* Clamp iFrames to the beginning of the next sound we need to start. */
|
||||||
if( m_iNextSound < m_aSounds.size() )
|
if( m_iNextSound < m_aSounds.size() )
|
||||||
{
|
{
|
||||||
int iOffsetFrame = m_aSounds[m_iNextSound].GetOffsetFrame(m_iActualSampleRate);
|
int iOffsetFrame = m_aSounds[m_iNextSound].GetOffsetFrame( m_iActualSampleRate );
|
||||||
ASSERT_M( iOffsetFrame >= m_iCurrentFrame, ssprintf("%i %i", iOffsetFrame, m_iCurrentFrame) );
|
ASSERT_M( iOffsetFrame >= m_iCurrentFrame, ssprintf("%i %i", iOffsetFrame, m_iCurrentFrame) );
|
||||||
int iFramesToRead = iOffsetFrame - m_iCurrentFrame;
|
int iFramesToRead = iOffsetFrame - m_iCurrentFrame;
|
||||||
iFrames = min( iFramesToRead, iFrames );
|
iFrames = min( iFramesToRead, iFrames );
|
||||||
@@ -290,16 +339,15 @@ int RageSoundReader_Chain::ReadBlock( int16_t *pBuffer, int iFrames )
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if( m_apActiveSounds.size() == 1 &&
|
if( m_apActiveSounds.size() == 1 &&
|
||||||
m_apActiveSounds.front().fPan == 0 &&
|
m_apActiveSounds.front()->pSound->GetNumChannels() == m_iChannels &&
|
||||||
m_apActiveSounds.front().pSound->GetNumChannels() == m_iChannels &&
|
m_apActiveSounds.front()->pSound->GetSampleRate() == m_iActualSampleRate )
|
||||||
m_apActiveSounds.front().pSound->GetSampleRate() == m_iActualSampleRate )
|
|
||||||
{
|
{
|
||||||
/* We have only one source, and it matches our target. Don't mix; read
|
/* We have only one source, and it matches our target. Don't mix; read
|
||||||
* directly from the source into the destination. This is to optimize
|
* directly from the source into the destination. This is to optimize
|
||||||
* the common case of having one BGM track and no autoplay sounds. */
|
* the common case of having one BGM track and no autoplay sounds. */
|
||||||
int iBytes = m_apActiveSounds.front().pSound->Read( (char *) pBuffer, iFrames * sizeof(int16_t) * m_iChannels );
|
int iBytes = m_apActiveSounds.front()->pSound->Read( (char *) pBuffer, iFrames * sizeof(int16_t) * m_iChannels );
|
||||||
if( iBytes == 0 )
|
if( iBytes == 0 )
|
||||||
ReleaseSound( 0 );
|
ReleaseSound( m_apActiveSounds.front() );
|
||||||
return iBytes / (sizeof(int16_t) * m_iChannels);
|
return iBytes / (sizeof(int16_t) * m_iChannels);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,14 +366,14 @@ int RageSoundReader_Chain::ReadBlock( int16_t *pBuffer, int iFrames )
|
|||||||
int iMaxFramesRead = 0;
|
int iMaxFramesRead = 0;
|
||||||
for( unsigned i = 0; i < m_apActiveSounds.size(); )
|
for( unsigned i = 0; i < m_apActiveSounds.size(); )
|
||||||
{
|
{
|
||||||
ActiveSound &s = m_apActiveSounds[i];
|
RageSoundReader *pSound = m_apActiveSounds[i]->pSound;
|
||||||
RageSoundReader *pSound = s.pSound;
|
ASSERT( pSound->GetNumChannels() == m_iChannels ); // guaranteed by ActivateSound and Finish
|
||||||
int iSamples = min( iFrames * pSound->GetNumChannels(), ARRAYLEN(Buffer) );
|
int iSamples = min( iFrames * pSound->GetNumChannels(), ARRAYLEN(Buffer) );
|
||||||
int iBytesRead = pSound->Read( (char *) Buffer, iSamples*sizeof(int16_t) );
|
int iBytesRead = pSound->Read( (char *) Buffer, iSamples*sizeof(int16_t) );
|
||||||
if( iBytesRead == -1 || iBytesRead == 0 )
|
if( iBytesRead == -1 || iBytesRead == 0 )
|
||||||
{
|
{
|
||||||
/* The sound is at EOF. Release it. */
|
/* The sound is at EOF. Release it. */
|
||||||
ReleaseSound( i );
|
ReleaseSound( m_apActiveSounds[i] );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,15 +382,6 @@ int RageSoundReader_Chain::ReadBlock( int16_t *pBuffer, int iFrames )
|
|||||||
|
|
||||||
iMaxFramesRead = max( iMaxFramesRead, iFramesRead );
|
iMaxFramesRead = max( iMaxFramesRead, iFramesRead );
|
||||||
|
|
||||||
if( m_iChannels == 2 && pSound->GetNumChannels() == 1 )
|
|
||||||
{
|
|
||||||
RageSoundUtil::ConvertMonoToStereoInPlace( Buffer, iSamplesRead );
|
|
||||||
iSamplesRead *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( fabsf(s.fPan) > 0.0001f )
|
|
||||||
RageSoundUtil::Pan( Buffer, iFramesRead, s.fPan );
|
|
||||||
|
|
||||||
mix.write( Buffer, iSamplesRead );
|
mix.write( Buffer, iSamplesRead );
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
@@ -372,8 +411,8 @@ int RageSoundReader_Chain::Read( char *pBuffer, unsigned iLength )
|
|||||||
|
|
||||||
while( m_iNextSound < m_aSounds.size() && m_iCurrentFrame == m_aSounds[m_iNextSound].GetOffsetFrame(m_iActualSampleRate) )
|
while( m_iNextSound < m_aSounds.size() && m_iCurrentFrame == m_aSounds[m_iNextSound].GetOffsetFrame(m_iActualSampleRate) )
|
||||||
{
|
{
|
||||||
Sound &sound = m_aSounds[m_iNextSound];
|
Sound *pSound = &m_aSounds[m_iNextSound];
|
||||||
ActivateSound( sound );
|
ActivateSound( pSound );
|
||||||
++m_iNextSound;
|
++m_iNextSound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,7 +426,7 @@ int RageSoundReader_Chain::GetLength() const
|
|||||||
for( unsigned i = 0; i < m_aSounds.size(); ++i )
|
for( unsigned i = 0; i < m_aSounds.size(); ++i )
|
||||||
{
|
{
|
||||||
const Sound &sound = m_aSounds[i];
|
const Sound &sound = m_aSounds[i];
|
||||||
const RageSoundReader *pSound = m_apLoadedSounds.find( sound.sPath )->second;
|
const RageSoundReader *pSound = m_apLoadedSounds[sound.iIndex];
|
||||||
int iThisLength = pSound->GetLength();
|
int iThisLength = pSound->GetLength();
|
||||||
if( iThisLength )
|
if( iThisLength )
|
||||||
iLength = max( iLength, iThisLength + sound.iOffsetMS );
|
iLength = max( iLength, iThisLength + sound.iOffsetMS );
|
||||||
@@ -401,7 +440,7 @@ int RageSoundReader_Chain::GetLength_Fast() const
|
|||||||
for( unsigned i = 0; i < m_aSounds.size(); ++i )
|
for( unsigned i = 0; i < m_aSounds.size(); ++i )
|
||||||
{
|
{
|
||||||
const Sound &sound = m_aSounds[i];
|
const Sound &sound = m_aSounds[i];
|
||||||
const RageSoundReader *pSound = m_apLoadedSounds.find( sound.sPath )->second;
|
const RageSoundReader *pSound = m_apLoadedSounds[sound.iIndex];
|
||||||
int iThisLength = pSound->GetLength_Fast();
|
int iThisLength = pSound->GetLength_Fast();
|
||||||
if( iThisLength )
|
if( iThisLength )
|
||||||
iLength = max( iLength, iThisLength + sound.iOffsetMS );
|
iLength = max( iLength, iThisLength + sound.iOffsetMS );
|
||||||
@@ -411,7 +450,7 @@ int RageSoundReader_Chain::GetLength_Fast() const
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2004 Glenn Maynard
|
* Copyright (c) 2004-2006 Glenn Maynard
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
#include "RageSoundReader.h"
|
#include "RageSoundReader.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
class RageSoundReader_Chain: public RageSoundReader
|
class RageSoundReader_Chain: public RageSoundReader
|
||||||
{
|
{
|
||||||
@@ -19,9 +18,12 @@ public:
|
|||||||
* use different sample rates. */
|
* use different sample rates. */
|
||||||
void SetPreferredSampleRate( int iSampleRate ) { m_iPreferredSampleRate = iSampleRate; }
|
void SetPreferredSampleRate( int iSampleRate ) { m_iPreferredSampleRate = iSampleRate; }
|
||||||
|
|
||||||
|
int LoadSound( RString sPath );
|
||||||
|
int LoadSound( RageSoundReader *pSound );
|
||||||
|
|
||||||
/* Add the given sound to play after fOffsetSecs seconds. Takes ownership
|
/* Add the given sound to play after fOffsetSecs seconds. Takes ownership
|
||||||
* of pSound. */
|
* of pSound. */
|
||||||
bool AddSound( RString sPath, float fOffsetSecs, float fPan );
|
void AddSound( int iIndex, float fOffsetSecs, float fPan );
|
||||||
|
|
||||||
/* Finish adding sounds. */
|
/* Finish adding sounds. */
|
||||||
void Finish();
|
void Finish();
|
||||||
@@ -48,13 +50,16 @@ private:
|
|||||||
int m_iActualSampleRate;
|
int m_iActualSampleRate;
|
||||||
unsigned m_iChannels;
|
unsigned m_iChannels;
|
||||||
|
|
||||||
map<RString, RageSoundReader *> m_apLoadedSounds;
|
map<RString, RageSoundReader *> m_apNamedSounds;
|
||||||
|
vector<RageSoundReader *> m_apLoadedSounds;
|
||||||
|
|
||||||
struct Sound
|
struct Sound
|
||||||
{
|
{
|
||||||
RString sPath;
|
int iIndex; // into m_apLoadedSounds
|
||||||
int iOffsetMS;
|
int iOffsetMS;
|
||||||
float fPan;
|
float fPan;
|
||||||
|
RageSoundReader *pSound; // NULL if not activated
|
||||||
|
|
||||||
int GetOffsetFrame( int iSampleRate ) const { return int( int64_t(iOffsetMS) * iSampleRate / 1000 ); }
|
int GetOffsetFrame( int iSampleRate ) const { return int( int64_t(iOffsetMS) * iSampleRate / 1000 ); }
|
||||||
bool operator<( const Sound &rhs ) const { return iOffsetMS < rhs.iOffsetMS; }
|
bool operator<( const Sound &rhs ) const { return iOffsetMS < rhs.iOffsetMS; }
|
||||||
};
|
};
|
||||||
@@ -64,21 +69,16 @@ private:
|
|||||||
/* Read state: */
|
/* Read state: */
|
||||||
int m_iCurrentFrame;
|
int m_iCurrentFrame;
|
||||||
unsigned m_iNextSound;
|
unsigned m_iNextSound;
|
||||||
struct ActiveSound
|
vector<Sound *> m_apActiveSounds;
|
||||||
{
|
|
||||||
RageSoundReader *pSound;
|
void ActivateSound( Sound *s );
|
||||||
float fPan;
|
void ReleaseSound( Sound *s );
|
||||||
bool operator< ( const ActiveSound &rhs ) const { return pSound < rhs.pSound; }
|
|
||||||
};
|
|
||||||
vector<ActiveSound> m_apActiveSounds;
|
|
||||||
unsigned ActivateSound( const Sound &s );
|
|
||||||
void ReleaseSound( unsigned n );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2004 Glenn Maynard
|
* Copyright (c) 2004-2006 Glenn Maynard
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
|||||||
Reference in New Issue
Block a user