Files
itgmania212121/stepmania/src/RageSoundManager.cpp
T

482 lines
14 KiB
C++
Raw Normal View History

2002-12-13 08:31:39 +00:00
/* Handle and provide an interface to the sound driver. Delete sounds that
2004-05-06 02:40:33 +00:00
* have been detached from their owner when they're finished playing. */
2002-12-13 07:44:34 +00:00
2004-05-06 02:40:33 +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
#if defined(WIN32)
set<void *> g_ProtectedPages;
void EnableWrites()
{
DWORD ignore;
for( set<void *>::iterator it = g_ProtectedPages.begin(); it != g_ProtectedPages.end(); ++it )
VirtualProtect( *it, 4096, PAGE_READWRITE, &ignore );
}
void DisableWrites()
{
DWORD ignore;
for( set<void *>::iterator it = g_ProtectedPages.begin(); it != g_ProtectedPages.end(); ++it )
VirtualProtect( *it, 4096, PAGE_READONLY, &ignore );
}
#else
void EnableWrites() { }
void DisableWrites() { }
#endif
/*
* This mutex is locked before Update() deletes old sounds from owned_sounds. Lock
* this mutex if you want to ensure that sounds remain valid. (Other threads may
* still delete them; this only guarantees that RageSoundManager won't.)
*
* The lock ordering requirements are:
* g_DeletionMutex before RageSound::Lock
* RageSound::Lock before g_SoundManMutex
* RageSound::Lock must not be locked when calling driver calls (since the driver
* may lock a mutex and then make RageSound calls back)
*
* (This is important: you must not make RageSound calls that might lock while holding
* g_SoundManMutex, but you can do so while holding g_DeletionMutex.)
*/
static RageMutex g_DeletionMutex("SoundDeletionMutex");
static RageMutex g_SoundManMutex("SoundMan");
2003-02-14 06:40:08 +00:00
RageSoundManager *SOUNDMAN = NULL;
RageSoundManager::RageSoundManager()
2002-12-13 07:44:34 +00:00
{
pos_map_queue.reserve( 1024 );
MixVolume = 1.0f;
DisableWrites();
}
void RageSoundManager::Init( CString drivers )
{
driver = MakeRageSoundDriver(drivers);
if( driver == NULL )
RageException::Throw( "Couldn't find a sound driver that works" );
2002-12-13 07:44:34 +00:00
}
RageSoundManager::~RageSoundManager()
{
g_SoundManMutex.Lock(); /* lock for access to owned_sounds */
set<RageSound *> sounds = owned_sounds;
g_SoundManMutex.Unlock(); /* finished with owned_sounds */
2002-12-13 07:44:34 +00:00
/* Clear any sounds that we own and havn't freed yet. */
set<RageSound *>::iterator j = sounds.begin();
while(j != sounds.end())
2002-12-13 07:44:34 +00:00
delete *(j++);
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;
EnableWrites(); /* for dtor */
2002-12-13 07:44:34 +00:00
}
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);
}
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)
{
FlushPosMapQueue();
g_DeletionMutex.Lock();
2004-04-07 03:31:27 +00:00
/* Scan the owned_sounds list for sounds that are no longer playing, and delete them. */
g_SoundManMutex.Lock(); /* lock for access to owned_sounds and all_sounds */
2004-04-07 07:35:26 +00:00
set<RageSound *>::iterator it;
set<RageSound *> ToDelete;
for( it = owned_sounds.begin(); it != owned_sounds.end(); ++it )
2004-04-07 03:31:27 +00:00
if( !(*it)->IsPlaying() )
{
LOG->Trace("XXX: deleting '%s'", (*it)->GetLoadedFilePath().c_str());
2004-04-07 07:35:26 +00:00
ToDelete.insert( *it );
}
/* Don't delete any sounds that are the parent of another sound. Always delete
* child sounds first. Otherwise, another sound might be allocated that has the
* same pointer as an old, deleted parent, and since we use the pointer to the
* parent to determine which sounds share the same parent, it'll confuse GetCopies(). */
for( all_sounds_type::iterator iter = all_sounds.begin(); iter != all_sounds.end(); ++iter )
if( (*iter)->GetOriginal() != (*iter) ) // child
{
set<RageSound *>::iterator parent = ToDelete.find( (*iter)->GetOriginal() );
if( parent != ToDelete.end() )
ToDelete.erase( parent );
}
2004-04-07 07:35:26 +00:00
for( it = ToDelete.begin(); it != ToDelete.end(); ++it )
2004-04-07 20:17:49 +00:00
owned_sounds.erase( *it );
g_SoundManMutex.Unlock(); /* finished with owned_sounds and all_sounds */
/* We can safely delete sounds while holding g_DeletionMutex, but not while
* holding g_SoundManMutex (see the mutex ordering at the top of the file). */
for( it = ToDelete.begin(); it != ToDelete.end(); ++it )
delete *it;
g_DeletionMutex.Unlock();
2002-12-13 07:44:34 +00:00
driver->Update(delta);
}
2004-03-18 01:31:34 +00:00
/* Register the given sound, and return a unique ID. */
void RageSoundManager::RegisterSound( RageSound *p )
2004-03-18 01:31:34 +00:00
{
g_SoundManMutex.Lock(); /* lock for access to all_sounds */
EnableWrites();
2004-03-18 01:31:34 +00:00
all_sounds.insert( p );
DisableWrites();
g_SoundManMutex.Unlock(); /* finished with all_sounds */
2004-03-18 01:31:34 +00:00
}
void RageSoundManager::UnregisterSound( RageSound *p )
{
g_SoundManMutex.Lock(); /* lock for access to all_sounds */
EnableWrites();
2004-03-18 01:31:34 +00:00
all_sounds.erase( p );
DisableWrites();
g_SoundManMutex.Unlock(); /* finished with all_sounds */
}
/* Return a unique ID. */
int RageSoundManager::GetUniqueID()
{
LockMut(g_SoundManMutex); /* serialize iID */
static int iID = 0;
return ++iID;
}
void RageSoundManager::RegisterPlayingSound( RageSound *p )
{
g_SoundManMutex.Lock(); /* lock for access to playing_sounds */
SOUNDMAN->playing_sounds.insert( p );
g_SoundManMutex.Unlock(); /* finished with playing_sounds */
}
void RageSoundManager::UnregisterPlayingSound( RageSound *p )
{
g_SoundManMutex.Lock(); /* lock for access to playing_sounds */
SOUNDMAN->playing_sounds.erase( p );
g_SoundManMutex.Unlock(); /* finished with playing_sounds */
2004-03-18 01:31:34 +00:00
}
void RageSoundManager::CommitPlayingPosition( int ID, int64_t frameno, int pos, int got_frames )
{
/* This can be called from realtime threads; don't lock any mutexes. */
queued_pos_map_t p;
p.ID = ID;
p.frameno = frameno;
p.pos = pos;
p.got_frames = got_frames;
pos_map_queue.write( &p, 1 );
}
RageSound *RageSoundManager::GetSoundByID( int ID )
{
2004-04-16 22:28:01 +00:00
LockMut( g_SoundManMutex );
/* Find the sound with p.ID. */
all_sounds_type::iterator it;
for( it = all_sounds.begin(); it != all_sounds.end(); ++it )
if( (*it)->GetID() == ID )
return *it;
return NULL;
}
/* This is only called by RageSoundManager::Update. */
void RageSoundManager::FlushPosMapQueue()
{
queued_pos_map_t p;
/* We don't need to lock to access pos_map_queue. */
while( pos_map_queue.read( &p, 1 ) )
{
RageSound *pSound = GetSoundByID( p.ID );
/* If we can't find the ID, the sound was probably deleted before we got here. */
if( pSound == NULL )
{
2004-04-25 05:56:31 +00:00
// LOG->Trace("ignored unknown (stale?) commit ID %i", p.ID);
continue;
}
pSound->CommitPlayingPosition( p.frameno, p.pos, p.got_frames );
}
}
2002-12-13 07:44:34 +00:00
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
}
RageSound *RageSoundManager::PlaySound( RageSound &snd, const RageSoundParams *params )
2002-12-13 07:44:34 +00:00
{
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. */
g_SoundManMutex.Lock(); /* lock for access to owned_sounds */
2002-12-22 08:13:33 +00:00
owned_sounds.insert(sound_to_play);
g_SoundManMutex.Unlock(); /* finished with owned_sounds */
2002-12-22 08:13:33 +00:00
}
2002-12-13 07:44:34 +00:00
if( params )
sound_to_play->SetParams( *params );
2002-12-22 08:13:33 +00:00
// Move to the start position.
sound_to_play->SetPositionSeconds( sound_to_play->GetParams().m_StartSecond );
2002-12-22 08:13:33 +00:00
sound_to_play->StartPlaying();
return sound_to_play;
2002-12-13 07:44:34 +00:00
}
/* Stop playing all playing sounds derived from the same parent as snd. */
void RageSoundManager::StopPlayingAllCopiesOfSound(RageSound &snd)
2002-12-22 08:13:33 +00:00
{
g_DeletionMutex.Lock();
2004-01-22 23:21:07 +00:00
2002-12-22 08:13:33 +00:00
vector<RageSound *> snds;
GetCopies( snd, snds );
vector<RageSound *>::iterator it;
for( it = snds.begin(); it != snds.end(); ++it )
2002-12-22 08:13:33 +00:00
{
if( (*it)->IsPlaying() )
(*it)->StopPlaying();
2002-12-22 08:13:33 +00:00
}
g_DeletionMutex.Unlock();
}
/* XXX: If this is ever called from a thread, it should take a bLockSounds parameter,
* like GetCopies. */
set<RageSound *> RageSoundManager::GetPlayingSounds() const
{
LockMut(g_SoundManMutex); /* lock for access to playing_sounds */
return playing_sounds;
2002-12-22 08:13:33 +00:00
}
2004-04-16 22:28:01 +00:00
void RageSoundManager::DeleteSound( RageSound *p )
{
/* Stop playing the sound. */
p->StopPlaying();
/* Add it to owned_sounds. It'll be deleted the next time we come around
* to Update(). */
g_SoundManMutex.Lock(); /* lock for access to owned_sounds */
owned_sounds.insert( p );
g_SoundManMutex.Unlock(); /* finished with owned_sounds */
}
void RageSoundManager::StopPlayingSoundsForThisThread()
{
/* Lock to make sure sounds don't become invalidated below before we get to them. */
g_DeletionMutex.Lock();
set<RageSound *> Sounds = GetPlayingSounds();
set<RageSound *>::iterator it;
for( it = Sounds.begin(); it != Sounds.end(); ++it )
{
if( (*it)->GetPlayingThread() != RageThread::GetCurrentThreadID() )
continue;
(*it)->Stop();
}
g_DeletionMutex.Unlock();
}
/*
* If bLockSounds is true, all returned sounds will be locked; you must call Unlock()
* on all returned sounds when you're done. This is used for thread safety: without
* it, if this is called in a separate thread, returned copies might stop playing
* and be deleted by RageSoundManager::Update before you're done with them.
*
* If bLockSounds is false, sounds are not locked. This is only safe to use in the same
* thread as RageSoundManager::Update (the gameplay thread).
*/
void RageSoundManager::GetCopies( RageSound &snd, vector<RageSound *> &snds, bool bLockSounds )
2002-12-22 08:13:33 +00:00
{
snds.clear();
/* Locking this means that Update() will not delete sounds. g_SoundManMutex does that,
* too, but we can't hold g_SoundManMutex when we lock sounds. */
g_DeletionMutex.Lock();
2004-01-22 23:21:07 +00:00
g_SoundManMutex.Lock(); /* lock for access to all_sounds */
const all_sounds_type sounds = all_sounds;
2004-04-16 22:28:01 +00:00
g_SoundManMutex.Unlock(); /* finished with all_sounds */
2002-12-22 08:13:33 +00:00
RageSound *parent = snd.GetOriginal();
all_sounds_type::const_iterator it;
for( it = sounds.begin(); it != sounds.end(); ++it )
{
2004-05-09 08:27:20 +00:00
CHECKPOINT_M( ssprintf("%p %p", *it, parent) );
if( (*it)->GetOriginal() != parent )
continue;
if( bLockSounds )
(*it)->LockSound();
snds.push_back( *it );
}
g_DeletionMutex.Unlock();
2002-12-22 08:13:33 +00:00
}
2004-04-11 01:03:18 +00:00
/* Don't hold the lock when we don't have to. We call this function from other
* threads, to avoid stalling the gameplay thread. */
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);
2004-04-11 01:03:18 +00:00
snd->Play();
/* We're responsible for freeing it. Add it to owned_sounds *after* we start
* playing, so RageSoundManager::Update doesn't free it before we actually start
* it. */
g_SoundManMutex.Lock(); /* lock for access to owned_sounds */
2002-12-13 07:44:34 +00:00
owned_sounds.insert(snd);
g_SoundManMutex.Unlock(); /* finished with owned_sounds */
2002-12-13 07:44:34 +00:00
}
void RageSoundManager::SetPrefs(float MixVol)
{
g_SoundManMutex.Lock(); /* lock for access to MixVolume */
MixVolume = MixVol;
g_SoundManMutex.Unlock(); /* finished with MixVolume */
driver->VolumeChanged();
}
2004-01-22 23:21:07 +00:00
/* Standalone helpers: */
2004-05-11 20:32:55 +00:00
void RageSoundManager::AttenuateBuf( int16_t *buf, int samples, float vol )
2003-10-22 03:39:52 +00:00
{
while( samples-- )
{
2004-05-11 20:32:55 +00:00
*buf = int16_t( (*buf) * vol );
2003-10-22 03:39:52 +00:00
++buf;
}
}
2002-12-31 01:09:31 +00:00
SoundMixBuffer::SoundMixBuffer()
{
bufsize = used = 0;
mixbuf = NULL;
2003-10-16 19:56:54 +00:00
SetVolume( SOUNDMAN->GetMixVolume() );
}
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-05-11 20:32:55 +00:00
void SoundMixBuffer::write( const int16_t *buf, unsigned size, float volume, int offset )
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 );
2004-03-18 01:23:35 +00:00
const unsigned realsize = size+offset;
if( bufsize < realsize )
2002-12-31 01:09:31 +00:00
{
2004-05-11 20:32:55 +00:00
mixbuf = (int32_t *) realloc( mixbuf, sizeof(int32_t) * realsize );
2004-03-18 04:10:50 +00:00
bufsize = realsize;
}
2002-12-31 01:09:31 +00:00
2004-03-18 01:23:35 +00:00
if( used < realsize )
{
2004-05-11 20:32:55 +00:00
memset( mixbuf + used, 0, (realsize - used) * sizeof(int32_t) );
2004-03-18 01:23:35 +00:00
used = realsize;
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)
2004-03-18 01:23:35 +00:00
mixbuf[pos+offset] += buf[pos] * factor;
2002-12-13 08:31:39 +00:00
}
2002-12-31 01:09:31 +00:00
2004-05-11 20:32:55 +00:00
void SoundMixBuffer::read(int16_t *buf)
2002-12-31 01:09:31 +00:00
{
2004-01-21 10:32:01 +00:00
for( unsigned pos = 0; pos < used; ++pos )
2004-01-14 22:09:55 +00:00
{
2004-05-11 20:32:55 +00:00
int32_t out = (mixbuf[pos]) / 256;
buf[pos] = (int16_t) 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
used = 0;
2002-12-31 01:09:31 +00:00
}
2004-05-06 02:40:33 +00:00
/*
* Copyright (c) 2002-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.
*/