Files
itgmania212121/stepmania/src/RageSoundReader_Chain.cpp
T

437 lines
13 KiB
C++
Raw Normal View History

2004-10-25 00:53:17 +00:00
#include "global.h"
#include "RageSoundReader_Chain.h"
#include "RageSoundReader_FileReader.h"
#include "RageSoundReader_Resample_Good.h"
2004-10-25 00:53:17 +00:00
#include "RageSoundReader_Preload.h"
#include "RageLog.h"
#include "RageUtil.h"
#include "RageSoundMixBuffer.h"
2004-10-25 01:53:19 +00:00
#include "RageSoundUtil.h"
2004-10-25 00:53:17 +00:00
/*
* Keyed sounds should pass this object to SoundReader_Preload, to preprocess it.
* Streaming more than two or three sounds is too expensive (keyed games can play
* two dozen), and reading from disk is too latent.
*
* This can also be used for chained background music, which should always stream,
* so we don't do the preloading in here.
*/
RageSoundReader_Chain::RageSoundReader_Chain()
{
m_iPreferredSampleRate = 44100;
m_iActualSampleRate = -1;
m_iChannels = 0;
m_iCurrentFrame = 0;
2004-10-28 05:10:47 +00:00
m_iNextSound = 0;
2004-10-25 00:53:17 +00:00
}
RageSoundReader_Chain::~RageSoundReader_Chain()
{
/* Clear m_apActiveSounds. */
while( !m_apActiveSounds.empty() )
ReleaseSound( 0 );
2006-11-30 04:19:51 +00:00
map<RString, RageSoundReader *>::iterator it;
2004-10-25 00:53:17 +00:00
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
delete it->second;
}
2006-10-20 00:01:18 +00:00
RageSoundReader_Chain *RageSoundReader_Chain::Copy() const
2004-10-25 00:53:17 +00:00
{
// XXX
FAIL_M("unimplemented");
}
/* 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
* read it more than once at a time. */
2006-01-22 01:00:06 +00:00
bool RageSoundReader_Chain::AddSound( RString sPath, float fOffsetSecs, float fPan )
2004-10-25 00:53:17 +00:00
{
2006-03-01 00:39:32 +00:00
sPath.MakeLower();
2004-10-25 00:53:17 +00:00
2006-11-30 04:19:51 +00:00
map<RString, RageSoundReader *>::const_iterator it;
2004-10-25 00:53:17 +00:00
it = m_apLoadedSounds.find( sPath );
if( it == m_apLoadedSounds.end() )
{
2006-12-09 03:44:36 +00:00
RString sError;
RageSoundReader *pReader = RageSoundReader_FileReader::OpenFile( sPath, sError );
2004-10-25 00:53:17 +00:00
if( pReader == NULL )
{
LOG->Warn( "RageSoundReader_Chain: error opening sound \"%s\": %s",
2006-12-09 03:44:36 +00:00
sPath.c_str(), sError.c_str() );
2004-10-25 00:53:17 +00:00
return false;
}
m_apLoadedSounds[sPath] = pReader;
}
2006-12-09 03:40:55 +00:00
Sound s;
2004-10-25 00:53:17 +00:00
s.sPath = sPath;
s.iOffsetMS = lrintf( fOffsetSecs * 1000 );
s.fPan = fPan;
2006-12-09 03:40:55 +00:00
m_aSounds.push_back( s );
2004-10-25 00:53:17 +00:00
return true;
}
/* If every sound has the same sample rate, return it. Otherwise, return -1. */
int RageSoundReader_Chain::GetSampleRateInternal() const
{
2004-11-01 06:46:46 +00:00
if( m_apLoadedSounds.empty() )
return m_iPreferredSampleRate;
2006-11-30 04:19:51 +00:00
map<RString, RageSoundReader *>::const_iterator it;
2004-10-25 00:53:17 +00:00
int iRate = -1;
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
{
if( iRate == -1 )
iRate = it->second->GetSampleRate();
else if( iRate != it->second->GetSampleRate() )
return -1;
}
return iRate;
}
void RageSoundReader_Chain::Finish()
{
2006-12-09 03:45:49 +00:00
/* Remove any sounds that don't have corresponding RageSoundReaders. */
2006-12-09 03:40:55 +00:00
for( unsigned i = 0; i < m_aSounds.size(); )
2004-10-25 00:53:17 +00:00
{
2006-12-09 03:40:55 +00:00
Sound &sound = m_aSounds[i];
2004-10-25 00:53:17 +00:00
2006-11-30 04:19:51 +00:00
map<RString, RageSoundReader *>::iterator it = m_apLoadedSounds.find( sound.sPath );
2004-10-25 00:53:17 +00:00
if( it == m_apLoadedSounds.end() )
{
2006-12-09 03:40:55 +00:00
m_aSounds.erase( m_aSounds.begin()+i );
2004-10-25 00:53:17 +00:00
continue;
}
++i;
}
/* Figure out how many channels we have. */
m_iChannels = 1;
2006-11-30 04:19:51 +00:00
map<RString, RageSoundReader *>::iterator it;
2004-10-25 00:53:17 +00:00
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. */
2006-12-09 03:40:55 +00:00
for( unsigned i = 0; i < m_aSounds.size(); ++i )
if( fabs(m_aSounds[i].fPan) > 0.0001f )
2004-10-25 00:53:17 +00:00
m_iChannels = 2;
/*
* We might get different sample rates fro mour sources. If they're all the same
* 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
* should avoid redundant resampling later.)
*/
m_iActualSampleRate = GetSampleRateInternal();
if( m_iActualSampleRate == -1 )
{
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
{
2006-11-30 04:19:51 +00:00
RageSoundReader *&pSound = it->second;
2004-10-25 00:53:17 +00:00
RageSoundReader_Resample_Good *pResample = new RageSoundReader_Resample_Good( pSound, m_iPreferredSampleRate );
2004-10-25 00:53:17 +00:00
pSound = pResample;
}
m_iActualSampleRate = m_iPreferredSampleRate;
}
/* Attempt to preload all sounds. */
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
{
2006-11-30 04:19:51 +00:00
RageSoundReader *&pSound = it->second;
2004-10-25 00:53:17 +00:00
RageSoundReader_Preload::PreloadSound( pSound );
}
/* Sort the sounds by start time. */
2006-12-09 03:40:55 +00:00
sort( m_aSounds.begin(), m_aSounds.end() );
2004-10-25 00:53:17 +00:00
}
int RageSoundReader_Chain::SetPosition_Accurate( int iFrame )
2004-10-25 00:53:17 +00:00
{
/* Clear m_apActiveSounds. */
while( !m_apActiveSounds.empty() )
ReleaseSound( 0 );
m_iCurrentFrame = iFrame;
2004-10-25 00:53:17 +00:00
/* Run through all sounds in the chain, and activate all sounds which have data
* at ms. */
2006-12-09 03:40:55 +00:00
for( unsigned i = 0; i < m_aSounds.size(); ++i )
2004-10-25 00:53:17 +00:00
{
2006-12-09 03:40:55 +00:00
Sound &sound = m_aSounds[i];
int iOffsetFrame = sound.GetOffsetFrame( GetSampleRate() );
2004-10-25 00:53:17 +00:00
/* If this sound is in the future, skip it. */
if( iOffsetFrame > iFrame )
2004-10-25 00:53:17 +00:00
continue;
2006-11-30 04:19:51 +00:00
/* Find the RageSoundReader. */
2004-10-25 00:53:17 +00:00
int n = ActivateSound( sound );
2006-11-30 04:19:51 +00:00
RageSoundReader *pSound = m_apActiveSounds[n].pSound;
2004-10-25 00:53:17 +00:00
int iOffsetFrames = iFrame - iOffsetFrame;
if( pSound->SetPosition_Accurate(iOffsetFrames) == 0 )
2004-10-25 00:53:17 +00:00
{
/* We're past the end of this sound. */
ReleaseSound( n );
continue;
}
}
2004-10-28 05:10:47 +00:00
m_iNextSound = GetNextSoundIndex();
2004-10-25 00:53:17 +00:00
/* If no sounds were started, and we have no sounds ahead of us, we've seeked
* past EOF. */
2006-12-09 03:40:55 +00:00
if( m_apActiveSounds.empty() && m_iNextSound == m_aSounds.size() )
2004-10-25 00:53:17 +00:00
return 0;
return iFrame;
2004-10-25 00:53:17 +00:00
}
2006-12-09 03:40:55 +00:00
unsigned RageSoundReader_Chain::ActivateSound( const Sound &s )
2004-10-25 00:53:17 +00:00
{
2006-11-30 04:19:51 +00:00
RageSoundReader *pSound = m_apLoadedSounds[s.sPath];
2004-10-25 00:53:17 +00:00
ActiveSound add;
add.fPan = s.fPan;
add.pSound = pSound->Copy();
2004-10-25 00:53:17 +00:00
m_apActiveSounds.push_back( add );
return m_apActiveSounds.size() - 1;
}
void RageSoundReader_Chain::ReleaseSound( unsigned n )
{
2004-10-25 03:44:28 +00:00
ASSERT_M( n < m_apActiveSounds.size(), ssprintf("%u, %u", n, unsigned(m_apActiveSounds.size())) );
2006-11-30 04:19:51 +00:00
RageSoundReader *pSound = m_apActiveSounds[n].pSound;
2004-10-25 00:53:17 +00:00
delete pSound;
2004-10-25 00:53:17 +00:00
m_apActiveSounds.erase( m_apActiveSounds.begin()+n );
}
bool RageSoundReader_Chain::IsStreamingFromDisk() const
{
2006-11-30 04:19:51 +00:00
map<RString, RageSoundReader *>::const_iterator it;
2004-10-25 00:53:17 +00:00
for( it = m_apLoadedSounds.begin(); it != m_apLoadedSounds.end(); ++it )
if( it->second->IsStreamingFromDisk() )
return true;
return false;
}
2006-11-30 02:52:41 +00:00
bool RageSoundReader_Chain::SetProperty( const RString &sProperty, float fValue )
{
bool bRet = false;
for( unsigned i = 0; i < m_apActiveSounds.size(); ++i )
2006-11-30 02:52:41 +00:00
{
if( m_apActiveSounds[i].pSound->SetProperty(sProperty, fValue) )
bRet = true;
}
return bRet;
}
2006-11-29 21:52:31 +00:00
int RageSoundReader_Chain::GetNextSourceFrame() const
{
return m_iCurrentFrame;
/* int iPosition = m_apActiveSounds[0].pSound->GetPosition();
for( unsigned i = 1; i < m_apActiveSounds.size(); )
{
if( m_apActiveSounds[i].pSound->GetPosition() != iPosition )
LOG->Warn( "RageSoundReader_Chain: sound positions moving at different rates" );
}
return iPosition;
*/
}
float RageSoundReader_Chain::GetStreamToSourceRatio() const
{
if( m_apActiveSounds.empty() )
return 1.0f;
float iRate = m_apActiveSounds[0].pSound->GetStreamToSourceRatio();
for( unsigned i = 1; i < m_apActiveSounds.size(); )
{
if( m_apActiveSounds[i].pSound->GetStreamToSourceRatio() != iRate )
LOG->Warn( "RageSoundReader_Chain: sound rates changing differently" );
}
return iRate;
}
2006-12-09 03:40:55 +00:00
/* Find the next sound we'll need to start, if any. m_aSounds is sorted by time. */
2004-10-25 00:53:17 +00:00
unsigned RageSoundReader_Chain::GetNextSoundIndex() const
{
unsigned iNextSound = 0;
2006-12-09 03:40:55 +00:00
while( iNextSound < m_aSounds.size() && m_iCurrentFrame > m_aSounds[iNextSound].GetOffsetFrame(m_iActualSampleRate) )
2004-10-25 00:53:17 +00:00
++iNextSound;
return iNextSound;
}
2004-10-28 05:10:47 +00:00
int RageSoundReader_Chain::ReadBlock( int16_t *pBuffer, int iFrames )
2004-10-25 00:53:17 +00:00
{
2004-10-28 05:10:47 +00:00
/* How many samples should we read before we need to start up a sound? */
2006-12-09 03:40:55 +00:00
if( m_iNextSound < m_aSounds.size() )
2004-10-28 05:10:47 +00:00
{
2006-12-09 03:40:55 +00:00
int iOffsetFrame = m_aSounds[m_iNextSound].GetOffsetFrame(m_iActualSampleRate);
2006-12-09 03:50:06 +00:00
ASSERT_M( iOffsetFrame >= m_iCurrentFrame, ssprintf("%i %i", iOffsetFrame, m_iCurrentFrame) );
int iFramesToRead = iOffsetFrame - m_iCurrentFrame;
iFrames = min( iFramesToRead, iFrames );
2004-10-28 05:10:47 +00:00
}
2004-10-25 00:53:17 +00:00
2006-12-09 03:51:32 +00:00
if( iFrames == 0 )
return 0;
if( m_apActiveSounds.size() == 1 &&
2004-10-28 05:10:47 +00:00
m_apActiveSounds.front().fPan == 0 &&
m_apActiveSounds.front().pSound->GetNumChannels() == m_iChannels &&
m_apActiveSounds.front().pSound->GetSampleRate() == m_iActualSampleRate )
{
/* We have only one source, and it matches our target. Don't mix; read
2004-11-01 07:47:38 +00:00
* directly from the source into the destination. This is to optimize
* the common case of having one BGM track and no autoplay sounds. */
2006-12-09 03:50:06 +00:00
int iBytes = m_apActiveSounds.front().pSound->Read( (char *) pBuffer, iFrames * sizeof(int16_t) * m_iChannels );
2004-10-28 05:10:47 +00:00
if( iBytes == 0 )
ReleaseSound( 0 );
return iBytes / (sizeof(int16_t) * m_iChannels);
}
2004-10-25 00:53:17 +00:00
2006-12-09 03:51:32 +00:00
if( m_apActiveSounds.empty() )
{
/* If we have more sounds ahead of us, pretend we read the entire block, since
* there's silence in between. Otherwise, we're at EOF. */
memset( pBuffer, 0, iFrames * m_iChannels * sizeof(int16_t) );
return iFrames;
}
2006-12-09 03:51:46 +00:00
RageSoundMixBuffer mix;
/* Read iFrames from each sound. */
int16_t Buffer[2048];
iFrames = min( iFrames, 1024 );
int iMaxFramesRead = 0;
for( unsigned i = 0; i < m_apActiveSounds.size(); )
2004-10-25 00:53:17 +00:00
{
2006-12-09 03:51:46 +00:00
ActiveSound &s = m_apActiveSounds[i];
RageSoundReader *pSound = s.pSound;
int iSamples = min( iFrames * pSound->GetNumChannels(), ARRAYLEN(Buffer) );
int iBytesRead = pSound->Read( (char *) Buffer, iSamples*sizeof(int16_t) );
if( iBytesRead == -1 || iBytesRead == 0 )
{
/* The sound is at EOF. Release it. */
ReleaseSound( i );
continue;
}
int iSamplesRead = iBytesRead / sizeof(int16_t);
int iFramesRead = iSamplesRead / pSound->GetNumChannels();
iMaxFramesRead = max( iMaxFramesRead, iFramesRead );
if( m_iChannels == 2 && pSound->GetNumChannels() == 1 )
2004-10-25 00:53:17 +00:00
{
2006-12-09 03:51:46 +00:00
RageSoundUtil::ConvertMonoToStereoInPlace( Buffer, iSamplesRead );
iSamplesRead *= 2;
2004-10-25 00:53:17 +00:00
}
2006-12-09 03:51:46 +00:00
if( fabsf(s.fPan) > 0.0001f )
RageSoundUtil::Pan( Buffer, iFramesRead, s.fPan );
mix.write( Buffer, iSamplesRead );
++i;
2004-10-28 05:10:47 +00:00
}
2006-12-09 03:51:46 +00:00
/* Read mixed frames into the output buffer. */
mix.read( (int16_t *) pBuffer );
return iMaxFramesRead;
2004-10-28 05:10:47 +00:00
}
int RageSoundReader_Chain::Read( char *pBuffer, unsigned iLength )
{
int iNumFramesToRead = iLength / (sizeof(int16_t) * m_iChannels);
int iTotalFramesRead = 0;
/* If we have no sources, and no more sounds to play, EOF. */
2006-12-09 03:40:55 +00:00
while( iNumFramesToRead > 0 && (!m_apActiveSounds.empty() || m_iNextSound < m_aSounds.size()) )
2004-10-28 05:10:47 +00:00
{
int iFramesRead = ReadBlock( (int16_t *) pBuffer, iNumFramesToRead );
if( iFramesRead == -1 )
return -1;
m_iCurrentFrame += iFramesRead;
iNumFramesToRead -= iFramesRead;
iTotalFramesRead += iFramesRead;
pBuffer += iFramesRead * sizeof(int16_t) * m_iChannels;
2006-12-09 03:40:55 +00:00
while( m_iNextSound < m_aSounds.size() && m_iCurrentFrame == m_aSounds[m_iNextSound].GetOffsetFrame(m_iActualSampleRate) )
2004-10-25 00:53:17 +00:00
{
2006-12-09 03:40:55 +00:00
Sound &sound = m_aSounds[m_iNextSound];
2004-10-25 00:53:17 +00:00
ActivateSound( sound );
2004-10-28 05:10:47 +00:00
++m_iNextSound;
2004-10-25 00:53:17 +00:00
}
}
2004-10-28 05:10:47 +00:00
return iTotalFramesRead * sizeof(int16_t) * m_iChannels;
2004-10-25 00:53:17 +00:00
}
int RageSoundReader_Chain::GetLength() const
{
int iLength = 0;
2006-12-09 03:40:55 +00:00
for( unsigned i = 0; i < m_aSounds.size(); ++i )
2004-10-25 00:53:17 +00:00
{
2006-12-09 03:40:55 +00:00
const Sound &sound = m_aSounds[i];
2006-11-30 04:19:51 +00:00
const RageSoundReader *pSound = m_apLoadedSounds.find( sound.sPath )->second;
2004-10-25 00:53:17 +00:00
int iThisLength = pSound->GetLength();
if( iThisLength )
iLength = max( iLength, iThisLength + sound.iOffsetMS );
}
return iLength;
}
int RageSoundReader_Chain::GetLength_Fast() const
{
int iLength = 0;
2006-12-09 03:40:55 +00:00
for( unsigned i = 0; i < m_aSounds.size(); ++i )
2004-10-25 00:53:17 +00:00
{
2006-12-09 03:40:55 +00:00
const Sound &sound = m_aSounds[i];
2006-11-30 04:19:51 +00:00
const RageSoundReader *pSound = m_apLoadedSounds.find( sound.sPath )->second;
2004-10-25 00:53:17 +00:00
int iThisLength = pSound->GetLength_Fast();
if( iThisLength )
iLength = max( iLength, iThisLength + sound.iOffsetMS );
}
return iLength;
}
/*
* Copyright (c) 2004 Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/