2002-12-13 08:31:39 +00:00
|
|
|
/* Handle and provide an interface to the sound driver. Delete sounds that
|
|
|
|
|
* have been detached from their owner when they're finished playing. Distribute Update
|
|
|
|
|
* calls to all sounds. */
|
2003-02-16 04:01:45 +00:00
|
|
|
#include "global.h"
|
2002-12-13 07:44:34 +00:00
|
|
|
|
|
|
|
|
#include "RageSoundManager.h"
|
|
|
|
|
#include "RageException.h"
|
|
|
|
|
#include "RageUtil.h"
|
|
|
|
|
#include "RageSound.h"
|
|
|
|
|
#include "RageLog.h"
|
|
|
|
|
#include "RageTimer.h"
|
|
|
|
|
|
|
|
|
|
#include "arch/arch.h"
|
|
|
|
|
#include "arch/Sound/RageSoundDriver.h"
|
2002-12-27 23:15:39 +00:00
|
|
|
#include "SDL_audio.h"
|
2002-12-13 07:44:34 +00:00
|
|
|
|
2003-02-14 06:40:08 +00:00
|
|
|
RageSoundManager *SOUNDMAN = NULL;
|
|
|
|
|
|
2002-12-13 08:31:39 +00:00
|
|
|
RageSoundManager::RageSoundManager(CString drivers)
|
2002-12-13 07:44:34 +00:00
|
|
|
{
|
2003-01-19 19:55:24 +00:00
|
|
|
/* needs to be done first */
|
|
|
|
|
SOUNDMAN = this;
|
|
|
|
|
|
2003-02-16 05:10:08 +00:00
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
MixVolume = 1.0f;
|
2003-01-02 07:30:23 +00:00
|
|
|
|
2003-02-16 05:10:08 +00:00
|
|
|
driver = MakeRageSoundDriver(drivers);
|
|
|
|
|
if(!driver)
|
|
|
|
|
RageException::Throw("Couldn't find a sound driver that works");
|
|
|
|
|
} catch(...) {
|
|
|
|
|
SOUNDMAN = NULL;
|
|
|
|
|
throw;
|
|
|
|
|
}
|
2002-12-13 07:44:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RageSoundManager::~RageSoundManager()
|
|
|
|
|
{
|
2004-01-22 23:21:07 +00:00
|
|
|
lock.Lock();
|
2002-12-13 07:44:34 +00:00
|
|
|
/* Clear any sounds that we own and havn't freed yet. */
|
2004-01-15 03:12:18 +00:00
|
|
|
set<RageSoundBase *>::iterator j = owned_sounds.begin();
|
2002-12-13 07:44:34 +00:00
|
|
|
while(j != owned_sounds.end())
|
|
|
|
|
delete *(j++);
|
2004-01-22 23:21:07 +00:00
|
|
|
lock.Unlock();
|
2002-12-13 07:44:34 +00:00
|
|
|
|
2004-01-22 23:21:07 +00:00
|
|
|
/* Don't lock while deleting the driver (the decoder thread might deadlock). */
|
2002-12-13 07:44:34 +00:00
|
|
|
delete driver;
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-15 03:12:18 +00:00
|
|
|
void RageSoundManager::StartMixing( RageSoundBase *snd )
|
2002-12-13 07:44:34 +00:00
|
|
|
{
|
|
|
|
|
driver->StartMixing(snd);
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-15 03:12:18 +00:00
|
|
|
void RageSoundManager::StopMixing( RageSoundBase *snd )
|
2002-12-13 07:44:34 +00:00
|
|
|
{
|
|
|
|
|
driver->StopMixing(snd);
|
|
|
|
|
|
|
|
|
|
/* The sound is finished, and should be deleted if it's in owned_sounds.
|
|
|
|
|
* However, this call might be *from* the sound itself, and it'd be
|
|
|
|
|
* a mess to delete it while it's on the call stack. Instead, put it
|
|
|
|
|
* in a queue to delete, and delete it on the next update. */
|
2004-01-22 23:21:07 +00:00
|
|
|
LockMut(lock);
|
2002-12-13 07:44:34 +00:00
|
|
|
if(owned_sounds.find(snd) != owned_sounds.end()) {
|
|
|
|
|
sounds_to_delete.insert(snd);
|
|
|
|
|
owned_sounds.erase(snd);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-19 22:21:02 +00:00
|
|
|
int64_t RageSoundManager::GetPosition( const RageSoundBase *snd ) const
|
2002-12-13 07:44:34 +00:00
|
|
|
{
|
|
|
|
|
return driver->GetPosition(snd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RageSoundManager::Update(float delta)
|
|
|
|
|
{
|
2004-01-22 23:21:07 +00:00
|
|
|
LockMut(lock);
|
2002-12-13 07:44:34 +00:00
|
|
|
while(!sounds_to_delete.empty())
|
|
|
|
|
{
|
|
|
|
|
delete *sounds_to_delete.begin();
|
|
|
|
|
sounds_to_delete.erase(sounds_to_delete.begin());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(set<RageSound *>::iterator i = all_sounds.begin();
|
|
|
|
|
i != all_sounds.end(); ++i)
|
|
|
|
|
(*i)->Update(delta);
|
|
|
|
|
|
|
|
|
|
driver->Update(delta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float RageSoundManager::GetPlayLatency() const
|
|
|
|
|
{
|
|
|
|
|
return driver->GetPlayLatency();
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-03 02:36:14 +00:00
|
|
|
int RageSoundManager::GetDriverSampleRate( int rate ) const
|
2003-04-23 19:31:32 +00:00
|
|
|
{
|
2004-01-03 02:36:14 +00:00
|
|
|
return driver->GetSampleRate( rate );
|
2003-04-23 19:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
2002-12-22 08:13:33 +00:00
|
|
|
RageSound *RageSoundManager::PlaySound(RageSound &snd)
|
2002-12-13 07:44:34 +00:00
|
|
|
{
|
2004-01-22 23:21:07 +00:00
|
|
|
LockMut(lock);
|
|
|
|
|
|
2002-12-22 08:13:33 +00:00
|
|
|
RageSound *sound_to_play;
|
|
|
|
|
if(!snd.IsPlaying())
|
|
|
|
|
sound_to_play = &snd;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
sound_to_play = new RageSound(snd);
|
2002-12-18 07:48:54 +00:00
|
|
|
|
2002-12-22 08:13:33 +00:00
|
|
|
/* We're responsible for freeing it. */
|
|
|
|
|
owned_sounds.insert(sound_to_play);
|
|
|
|
|
}
|
2002-12-13 07:44:34 +00:00
|
|
|
|
2002-12-22 08:13:33 +00:00
|
|
|
// Move to the start position.
|
|
|
|
|
sound_to_play->SetPositionSeconds();
|
|
|
|
|
sound_to_play->StartPlaying();
|
|
|
|
|
|
|
|
|
|
return sound_to_play;
|
2002-12-13 07:44:34 +00:00
|
|
|
}
|
|
|
|
|
|
2002-12-22 08:13:33 +00:00
|
|
|
void RageSoundManager::StopPlayingSound(RageSound &snd)
|
|
|
|
|
{
|
2004-01-22 23:21:07 +00:00
|
|
|
LockMut(lock);
|
|
|
|
|
|
2002-12-22 08:13:33 +00:00
|
|
|
/* Stop playing all playing sounds derived from the same parent as snd. */
|
|
|
|
|
vector<RageSound *> snds;
|
|
|
|
|
GetCopies(snd, snds);
|
|
|
|
|
for(vector<RageSound *>::iterator i = snds.begin(); i != snds.end(); i++)
|
|
|
|
|
{
|
|
|
|
|
if((*i)->IsPlaying())
|
|
|
|
|
(*i)->StopPlaying();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RageSoundManager::GetCopies(RageSound &snd, vector<RageSound *> &snds)
|
|
|
|
|
{
|
2004-01-22 23:21:07 +00:00
|
|
|
LockMut(lock);
|
|
|
|
|
|
2002-12-22 08:13:33 +00:00
|
|
|
RageSound *parent = snd.GetOriginal();
|
|
|
|
|
|
|
|
|
|
snds.clear();
|
|
|
|
|
for(set<RageSound *>::iterator i = playing_sounds.begin();
|
|
|
|
|
i != playing_sounds.end(); i++)
|
|
|
|
|
if((*i)->GetOriginal() == parent)
|
|
|
|
|
snds.push_back(*i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-12-13 07:44:34 +00:00
|
|
|
void RageSoundManager::PlayOnce( CString sPath )
|
|
|
|
|
{
|
|
|
|
|
/* We want this to start quickly, so don't try to prebuffer it. */
|
|
|
|
|
RageSound *snd = new RageSound;
|
|
|
|
|
snd->Load(sPath, false);
|
|
|
|
|
|
|
|
|
|
/* We're responsible for freeing it. */
|
2004-01-22 23:21:07 +00:00
|
|
|
lock.Lock();
|
2002-12-13 07:44:34 +00:00
|
|
|
owned_sounds.insert(snd);
|
2004-01-22 23:21:07 +00:00
|
|
|
lock.Unlock();
|
2002-12-13 07:44:34 +00:00
|
|
|
|
2003-01-03 03:39:31 +00:00
|
|
|
snd->Play();
|
2002-12-13 07:44:34 +00:00
|
|
|
}
|
|
|
|
|
|
2003-01-19 19:55:24 +00:00
|
|
|
void RageSoundManager::SetPrefs(float MixVol)
|
|
|
|
|
{
|
2004-01-22 23:21:07 +00:00
|
|
|
LockMut(lock);
|
|
|
|
|
|
2003-01-19 19:55:24 +00:00
|
|
|
MixVolume = MixVol;
|
|
|
|
|
driver->VolumeChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2004-01-22 23:21:07 +00:00
|
|
|
/* Standalone helpers: */
|
2003-10-22 03:39:52 +00:00
|
|
|
void RageSoundManager::AttenuateBuf( Sint16 *buf, int samples, float vol )
|
|
|
|
|
{
|
|
|
|
|
while( samples-- )
|
|
|
|
|
{
|
|
|
|
|
*buf = Sint16( (*buf) * vol );
|
|
|
|
|
++buf;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-12-31 01:09:31 +00:00
|
|
|
SoundMixBuffer::SoundMixBuffer()
|
|
|
|
|
{
|
2003-04-25 19:40:49 +00:00
|
|
|
bufsize = used = 0;
|
|
|
|
|
mixbuf = NULL;
|
2003-10-16 19:56:54 +00:00
|
|
|
SetVolume( SOUNDMAN->GetMixVolume() );
|
2003-04-25 19:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SoundMixBuffer::~SoundMixBuffer()
|
|
|
|
|
{
|
|
|
|
|
free(mixbuf);
|
2002-12-31 01:09:31 +00:00
|
|
|
}
|
|
|
|
|
|
2003-10-16 19:56:54 +00:00
|
|
|
void SoundMixBuffer::SetVolume( float f )
|
|
|
|
|
{
|
2003-10-21 21:35:58 +00:00
|
|
|
vol = int(256*f);
|
2003-10-16 19:56:54 +00:00
|
|
|
}
|
|
|
|
|
|
2004-01-21 10:32:01 +00:00
|
|
|
void SoundMixBuffer::write( const Sint16 *buf, unsigned size, float volume )
|
2002-12-31 01:09:31 +00:00
|
|
|
{
|
2004-01-21 10:32:01 +00:00
|
|
|
int factor = vol;
|
|
|
|
|
if( volume != -1 )
|
|
|
|
|
factor = int( 256*volume );
|
|
|
|
|
|
2003-04-25 19:40:49 +00:00
|
|
|
if(bufsize < size)
|
2002-12-31 01:09:31 +00:00
|
|
|
{
|
2003-04-25 19:40:49 +00:00
|
|
|
mixbuf = (Sint32 *) realloc(mixbuf, sizeof(Sint32) * size);
|
|
|
|
|
bufsize = size;
|
|
|
|
|
}
|
2002-12-31 01:09:31 +00:00
|
|
|
|
2003-04-25 19:40:49 +00:00
|
|
|
if(used < size)
|
|
|
|
|
{
|
|
|
|
|
memset(mixbuf + used, 0, (size - used) * sizeof(Sint32));
|
|
|
|
|
used = size;
|
2002-12-31 01:09:31 +00:00
|
|
|
}
|
|
|
|
|
|
2004-01-21 10:32:01 +00:00
|
|
|
/* Scale volume and add. */
|
|
|
|
|
for(unsigned pos = 0; pos < size; ++pos)
|
|
|
|
|
mixbuf[pos] += buf[pos] * factor;
|
2002-12-13 08:31:39 +00:00
|
|
|
}
|
2002-12-31 01:09:31 +00:00
|
|
|
|
|
|
|
|
void SoundMixBuffer::read(Sint16 *buf)
|
|
|
|
|
{
|
2004-01-21 10:32:01 +00:00
|
|
|
for( unsigned pos = 0; pos < used; ++pos )
|
2004-01-14 22:09:55 +00:00
|
|
|
{
|
2004-01-21 10:32:01 +00:00
|
|
|
Sint32 out = (mixbuf[pos]) / 256;
|
|
|
|
|
buf[pos] = (Sint16) clamp( out, -32768, 32767 );
|
2004-01-14 22:09:55 +00:00
|
|
|
}
|
|
|
|
|
used = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SoundMixBuffer::read( float *buf )
|
|
|
|
|
{
|
2004-01-21 10:32:01 +00:00
|
|
|
const int Minimum = -32768 * 256;
|
|
|
|
|
const int Maximum = 32767 * 256;
|
2002-12-31 01:09:31 +00:00
|
|
|
|
2004-01-14 22:09:55 +00:00
|
|
|
for( unsigned pos = 0; pos < used; ++pos )
|
2004-01-15 03:22:53 +00:00
|
|
|
buf[pos] = SCALE( (float)mixbuf[pos], Minimum, Maximum, -1.0f, 1.0f );
|
2004-01-14 22:09:55 +00:00
|
|
|
|
2003-04-25 19:40:49 +00:00
|
|
|
used = 0;
|
2002-12-31 01:09:31 +00:00
|
|
|
}
|