From f7bf2ef2fabafc5b3cda3b27bc04fb556a8947d6 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sun, 22 Dec 2002 08:13:33 +0000 Subject: [PATCH] changed sound API --- stepmania/src/RageSound.cpp | 118 ++++++++++-------- stepmania/src/RageSound.h | 24 ++-- stepmania/src/RageSoundManager.cpp | 71 ++++++----- stepmania/src/RageSoundManager.h | 9 +- stepmania/src/ScreenGameplay.cpp | 5 +- stepmania/src/ScreenSandbox.cpp | 39 ++++-- stepmania/src/ScreenSandbox.h | 2 +- stepmania/src/arch/Sound/RageSoundDriver.h | 3 +- .../src/arch/Sound/RageSoundDriver_DSound.cpp | 5 +- .../Sound/RageSoundDriver_DSound_Software.cpp | 2 +- .../arch/Sound/RageSoundDriver_WaveOut.cpp | 2 +- 11 files changed, 171 insertions(+), 109 deletions(-) diff --git a/stepmania/src/RageSound.cpp b/stepmania/src/RageSound.cpp index 135077dc22..4751d9f34e 100644 --- a/stepmania/src/RageSound.cpp +++ b/stepmania/src/RageSound.cpp @@ -75,6 +75,7 @@ RageSound::RageSound() initialized = true; } + original = this; stream.Sample = NULL; position = 0; playing = false; @@ -91,6 +92,11 @@ RageSound::RageSound() RageSound::~RageSound() { + /* If we're a "master" sound (not a copy), tell RageSoundManager to + * stop mixing us and everything that's copied from us. */ + if(original == this) + SOUNDMAN->StopPlayingSound(*this); + Unload(); /* Unregister ourselves. */ @@ -104,6 +110,7 @@ RageSound::RageSound(const RageSound &cpy) stream.Sample = NULL; + original = cpy.original; full_buf = cpy.full_buf; big = cpy.big; m_sFilePath = cpy.m_sFilePath; @@ -131,7 +138,7 @@ RageSound::RageSound(const RageSound &cpy) void RageSound::Unload() { if(IsPlaying()) - Stop(); + StopPlaying(); Sound_FreeSample(stream.Sample); stream.Sample = NULL; @@ -146,6 +153,9 @@ void RageSound::Load(CString sSoundFilePath, bool cache) { LOG->Trace( "RageSound::LoadSound( '%s' )", sSoundFilePath.GetString() ); + /* Don't load over copies. */ +// ASSERT(original == this); + Unload(); m_sFilePath = sSoundFilePath; @@ -231,22 +241,9 @@ void RageSound::SetLengthSeconds(float secs) m_LengthSamples = int(secs*samplerate); } -/* Start playing from the current position. If the sound is already - * playing, Stop is called. */ -void RageSound::Play() -{ - LockMut(SOUNDMAN->lock); - - if(playing) Stop(); - playing = true; - - // Tell the sound manager to start mixing us - SOUNDMAN->StartMixing(this); -} - void RageSound::Update(float delta) { - if(playing && big) + if(playing && big && delta) stream.FillBuf(int(delta * samplerate * samplesize)); } @@ -299,10 +296,6 @@ int RageSound::GetPCM(char *buffer, int size, int sampleno) { LockMut(SOUNDMAN->lock); - /* If the sound is paused, just fill the buffer with silence. - * Hmm. Pausing is annoying, since we'll get startup latency if - * we keep our buffer playing; we should stop the stream completely - * when we pause ... */ ASSERT(playing); int bytes_stored = 0; @@ -391,7 +384,7 @@ int RageSound::GetPCM(char *buffer, int size, int sampleno) if(Loop && m_LengthSamples != 0) { /* Rewind and start over. */ - SetPositionSeconds(float(m_StartSample) / samplerate); + SetPositionSamples(m_StartSample); continue; } if(AutoStop) @@ -446,36 +439,39 @@ int RageSound::GetPCM(char *buffer, int size, int sampleno) return bytes_stored; } -/* After we finish via the GetPCM return value, this is called by - * RageSoundManager once the sound is actually flushed, which means - * we're *really* stopped. */ -void RageSound::SoundStopped() +/* Start playing from the current position. If the sound is already + * playing, Stop is called. */ +void RageSound::StartPlaying() { - LOG->Trace("stop completed"); - Stop(); + LockMut(SOUNDMAN->lock); + + ASSERT(!playing); + + // Tell the sound manager to start mixing us + playing = true; + SOUNDMAN->StartMixing(this); } -void RageSound::Pause() +void RageSound::StopPlaying() { - if(!playing) return; - - // Tell the sound manager to stop mixing this sound. + /* Tell the sound manager to stop mixing this sound. */ SOUNDMAN->StopMixing(this); - playing = false; + + pos_map.clear(); +} + +RageSound *RageSound::Play() +{ + return SOUNDMAN->PlaySound(*this); } void RageSound::Stop() { - /* Tell the sound manager to stop mixing this sound. */ - SOUNDMAN->StopMixing(this); - - SetPositionSeconds(float(m_StartSample)/samplerate); - - playing = false; - pos_map.clear(); + SOUNDMAN->StopPlayingSound(*this); } + float RageSound::GetLengthSeconds() { if(big) { @@ -564,23 +560,43 @@ float RageSound::GetPositionSeconds() const void RageSound::SetPositionSeconds( float fSeconds ) { + SetPositionSamples( fSeconds == -1? -1: int(fSeconds * samplerate) ); +} + +void RageSound::SetPositionSamples( int samples ) +{ + if(samples == -1) + samples = m_StartSample; + /* This can take a while. Only lock the sound buffer if we're actually playing. */ SOUNDMAN->lock.Lock(); - if(!playing) - SOUNDMAN->lock.Unlock(); - position = int(fSeconds * samplerate); - if( fSeconds < 0 ) - fSeconds = 0; + if(!playing) + { + SOUNDMAN->lock.Unlock(); + /* If we're already there, don't do anything. */ + if(position == samples) + return; + } + + position = samples; + if( samples < 0 ) + samples = 0; if(big) { ASSERT(stream.Sample); - if(fSeconds == 0) + int ms = int(float(samples) * 1000.f / samplerate); + + if(ms == 0) Sound_Rewind(stream.Sample); else if(AccurateSync) - Sound_AccurateSeek(stream.Sample, int(fSeconds * 1000)); + Sound_AccurateSeek(stream.Sample, ms); else - Sound_FastSeek(stream.Sample, int(fSeconds * 1000)); + { +RageTimer tm; + Sound_FastSeek(stream.Sample, ms); +LOG->Trace("%f", tm.GetDeltaTime()); + } stream.buf.clear(); } @@ -603,11 +619,15 @@ void RageSound::LoadAndPlayIfNotAlready( CString sSoundFilePath ) return; // do nothing Load( sSoundFilePath ); - SetPositionSeconds(0); - SetStartSeconds(0); + if(IsPlaying()) + StopPlaying(); + + /* Use defaults: the beginning, the whole file. */ + SetStartSeconds(); SetLengthSeconds(); + SetPositionSamples(); SetLooping(); - Play(); + StartPlaying(); } void CircBuf::reserve(unsigned n) diff --git a/stepmania/src/RageSound.h b/stepmania/src/RageSound.h index e74ce16f95..8cae0ad374 100644 --- a/stepmania/src/RageSound.h +++ b/stepmania/src/RageSound.h @@ -26,6 +26,10 @@ public: class RageSound { + /* If we were copied from another RageSound, this will point to it; otherwise + * this is ourself. */ + RageSound *original; + /* These are only used when big == true: */ struct stream_t { Sound_Sample *Sample; @@ -79,11 +83,15 @@ class RageSound * stopped, and PlayCopy can't be used.) Default is true. Ignored when looping. */ bool AutoStop; + void SetPositionSamples( int samples = -1 ); + public: RageSound(); ~RageSound(); RageSound(const RageSound &cpy); + /* Used by RageSoundManager: */ + RageSound *GetOriginal() { return original; } /* Called only by the sound drivers: */ /* This function should return the number of bytes actually put into buffer. @@ -92,10 +100,6 @@ public: * can still be called (the sound is still playing). */ int GetPCM(char *buffer, int size, int sampleno); - /* Called by the sound driver when a sound has actually finished stopping - * normally. Not called when Stop() is called to stop the sound prematurely. */ - void SoundStopped(); - void Update(float delta); /* User API from here on: */ @@ -110,18 +114,22 @@ public: void Unload(); /* If enabled, then the sound will automatically stop when it reaches - * the end; otherwise it'll feed silence until Stop() is called. */ + * the end; otherwise it'll feed silence until stopped manually. */ void SetAutoStop(bool yes=true) { AutoStop=yes; } bool GetAutoStop() const { return AutoStop; } void SetStartSeconds(float secs = 0); /* default = beginning */ void SetLengthSeconds(float secs = -1); /* default = no length limit */ - void Play(); - void Pause(); + void StartPlaying(); +// void Pause(); + void StopPlaying(); + + RageSound *Play(); void Stop(); + float GetLengthSeconds(); float GetPositionSeconds() const; - void SetPositionSeconds( float fSeconds ); + void SetPositionSeconds( float fSeconds = -1); void SetAccurateSync(bool yes=true) { AccurateSync = yes; } void SetPlaybackRate( float fScale ); float GetPlaybackRate() const { return speed; } diff --git a/stepmania/src/RageSoundManager.cpp b/stepmania/src/RageSoundManager.cpp index df738147b4..d311f237df 100644 --- a/stepmania/src/RageSoundManager.cpp +++ b/stepmania/src/RageSoundManager.cpp @@ -32,11 +32,14 @@ RageSoundManager::~RageSoundManager() void RageSoundManager::StartMixing(RageSound *snd) { + playing_sounds.insert(snd); driver->StartMixing(snd); } void RageSoundManager::StopMixing(RageSound *snd) { + playing_sounds.erase(snd); + driver->StopMixing(snd); /* The sound is finished, and should be deleted if it's in owned_sounds. @@ -103,7 +106,7 @@ void RageSoundManager::Update(float delta) int got = s->GetPCM(buf, bytes, now); if(got < bytes) - s->SoundStopped(); + s->StopPlaying(); j = next; } @@ -127,38 +130,50 @@ float RageSoundManager::GetPlayLatency() const return driver->GetPlayLatency(); } -void RageSoundManager::PlayCopy( const RageSound &snd ) +RageSound *RageSoundManager::PlaySound(RageSound &snd) { - /* Make a copy of the sound and play it; this detaches the sound - * from the screen. This has a few effects: - * - * 1. If this is called more than once, it actually plays a new - * copy of the sound, rather than rewinding the currently-playing - * one. (This is important for keyed games.) - * - * 2. If the screen closes, the sound plays to completion. - * - * I'm not sure if copying will become a performance problem. As a - * flag, we could play snd instead of a copy if snd isn't actually - * playing. This would keep property 1 and lose 2, which would be - * fine for keyed games. Copying a streamed sound reopens the sound; - * copying a prebuffered sound makes a new copy of the buffer (unless - * std::basic_string happens to be refcounted). That's not too bad, - * but we might do five of these in the same frame ... - */ + RageSound *sound_to_play; + if(!snd.IsPlaying()) + sound_to_play = &snd; + else + { + sound_to_play = new RageSound(snd); - RageSound *newsnd = new RageSound(snd); + /* We're responsible for freeing it. */ + owned_sounds.insert(sound_to_play); + } - /* PlayCopy's sounds must always stop on their own. */ - newsnd->SetLooping(false); - newsnd->SetAutoStop(true); + // Move to the start position. + sound_to_play->SetPositionSeconds(); + sound_to_play->StartPlaying(); - /* We're responsible for freeing it. */ - owned_sounds.insert(newsnd); - - newsnd->Play(); + return sound_to_play; } +void RageSoundManager::StopPlayingSound(RageSound &snd) +{ + /* Stop playing all playing sounds derived from the same parent as snd. */ + vector snds; + GetCopies(snd, snds); + for(vector::iterator i = snds.begin(); i != snds.end(); i++) + { + if((*i)->IsPlaying()) + (*i)->StopPlaying(); + } +} + +void RageSoundManager::GetCopies(RageSound &snd, vector &snds) +{ + RageSound *parent = snd.GetOriginal(); + + snds.clear(); + for(set::iterator i = playing_sounds.begin(); + i != playing_sounds.end(); i++) + if((*i)->GetOriginal() == parent) + snds.push_back(*i); +} + + void RageSoundManager::PlayOnce( CString sPath ) { /* We want this to start quickly, so don't try to prebuffer it. */ @@ -189,7 +204,7 @@ void RageSoundManager::PlayOnceFromDir( CString sDir ) return; int index = rand() % arraySoundFiles.size(); - PlayOnce( sDir + arraySoundFiles[index] ); + SOUNDMAN->PlayOnce( sDir + arraySoundFiles[index] ); } /* Standalone helpers: */ diff --git a/stepmania/src/RageSoundManager.h b/stepmania/src/RageSoundManager.h index 33245e1ab4..2608798d6b 100644 --- a/stepmania/src/RageSoundManager.h +++ b/stepmania/src/RageSoundManager.h @@ -21,6 +21,8 @@ class RageSoundManager /* Set of sounds that are finished and should be deleted. */ set sounds_to_delete; + set playing_sounds; + struct FakeSound { float begin; int samples_read; @@ -43,10 +45,11 @@ public: float GetPlayLatency() const; void PlayOnce( CString sPath ); - void PlayOnceFromDir( CString sDir ); - void PlayCopy( const RageSound &snd ); - + static void PlayOnceFromDir( CString sDir ); + RageSound *PlaySound(RageSound &snd); + void StopPlayingSound(RageSound &snd); + void GetCopies(RageSound &snd, vector &snds); /* A list of all sounds that currently exist. RageSound adds and removes * itself to this. */ set all_sounds; diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index 7af4d4ba15..5c228032ec 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -1005,7 +1005,8 @@ void ScreenGameplay::Input( const DeviceInput& DeviceI, const InputEventType typ * of the song while we tween out, which looks really strange. * -glenn */ - m_soundMusic.Pause(); + /* but with the new sound code, Stop leaves the position alone -glenn (XXX remove this comment) */ + m_soundMusic.Stop(); this->ClearMessageQueue(); m_Fade.CloseWipingLeft( SM_SaveChangedBeforeGoingBack ); @@ -1414,7 +1415,7 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) case SM_BeginFailed: m_DancingState = STATE_OUTRO; - m_soundMusic.Pause(); + m_soundMusic.Stop(); m_StarWipe.SetTransitionTime( 1.5f ); m_StarWipe.CloseWipingRight( SM_None ); int p; diff --git a/stepmania/src/ScreenSandbox.cpp b/stepmania/src/ScreenSandbox.cpp index c40649aa0d..dc848596cb 100644 --- a/stepmania/src/ScreenSandbox.cpp +++ b/stepmania/src/ScreenSandbox.cpp @@ -32,6 +32,7 @@ ScreenSandbox::ScreenSandbox() { +MUSIC->Stop(); // m_quad.StretchTo( RectI(SCREEN_LEFT, SCREEN_TOP, SCREEN_RIGHT, SCREEN_BOTTOM) ); // m_quad.SetDiffuse( RageColor(1,1,1,1) ); // this->AddChild( &m_quad ); @@ -44,7 +45,7 @@ ScreenSandbox::ScreenSandbox() // this->AddChild(&obj); this->AddChild(&HEEEEEEEEELP); - + HEEEEEEEEELP.SetXY(450, 400); HEEEEEEEEELP.LoadFromFont( THEME->GetPathTo("Fonts","normal") ); HEEEEEEEEELP.SetZoom(.5); @@ -52,8 +53,7 @@ ScreenSandbox::ScreenSandbox() "p Play\n" "s Stop\n" "l Toggle looping\n" - "a Toggle autostop\n" - "f Fire sound in the background"); + "a Toggle autostop\n"); for(int i = 0; i < nsounds; ++i) { @@ -65,14 +65,21 @@ ScreenSandbox::ScreenSandbox() s[0].txt.SetXY(150, 100); s[1].txt.SetXY(450, 100); s[2].txt.SetXY(150, 250); - s[3].txt.SetXY(450, 250); - s[4].txt.SetXY(150, 400); +// s[3].txt.SetXY(450, 250); +// s[4].txt.SetXY(150, 400); s[0].s.Load("Themes/default/Sounds/_common menu music.ogg"); s[1].s.Load("Themes/default/Sounds/music scroll music.ogg"); s[2].s.Load("Themes/default/Sounds/evaluation extra stage.mp3"); - s[3].s.Load("Themes/default/Sounds/gameplay oni die.mp3"); - s[4].s.Load("Themes/default/Sounds/gameplay toasty.mp3"); +// s[3].s.Load("Themes/default/Sounds/gameplay oni die.mp3"); +// s[4].s.Load("Themes/default/Sounds/gameplay toasty.mp3"); + +/*s[0].s.SetPositionSeconds(3); +s[0].s.SetStartSeconds(3); +s[0].s.SetLengthSeconds(1); +s[0].s.SetAutoStop(false); +s[0].s.Play(); +*/ selected = 0; } @@ -84,19 +91,30 @@ void ScreenSandbox::UpdateText(int n) unsigned x = fn.find_last_of("/\\"); if(x != fn.npos) fn.erase(0, x+1); + vector snds; + SOUNDMAN->GetCopies(s[n].s, snds); + + CString pos; + for(unsigned p = 0; p < snds.size(); ++p) + { + if(p) pos += ", "; + pos += ssprintf("%.3f", snds[p]->GetPositionSeconds()); + } + s[n].txt.SetText(ssprintf( "%i: %s\n" "%s\n" "%s\n" "%s\n" - "%.3f of %.3f\n" + "(%s) of %.3f\n" "%s\n" "%s", n+1, fn.GetString(), s[n].s.IsPlaying()? "Playing":"Stopped", s[n].s.GetLooping()? "Looping": "Not looping", s[n].s.GetAutoStop()? "Stop when finished": "Continue until stopped", - s[n].s.GetPositionSeconds(), s[n].s.GetLengthSeconds(), + pos.size()? pos.GetString(): "none playing", + s[n].s.GetLengthSeconds(), s[n].s.IsStreaming()? "Streaming":"Preloaded", selected == n? "^^^^^^":"" )); @@ -135,9 +153,6 @@ void ScreenSandbox::Input( const DeviceInput& DeviceI, const InputEventType type case 'a': s[selected].s.SetAutoStop(!s[selected].s.GetAutoStop()); break; - case 'f': - SOUNDMAN->PlayCopy(s[selected].s); - break; /* case SDLK_LEFT: obj.SetX(obj.GetX() - 10); diff --git a/stepmania/src/ScreenSandbox.h b/stepmania/src/ScreenSandbox.h index 5f581283e3..2e7ce37027 100644 --- a/stepmania/src/ScreenSandbox.h +++ b/stepmania/src/ScreenSandbox.h @@ -19,7 +19,7 @@ #include "RageSound.h" #include "Sample3dObject.h" -const int nsounds = 5; +const int nsounds = 3; class ScreenSandbox : public Screen { diff --git a/stepmania/src/arch/Sound/RageSoundDriver.h b/stepmania/src/arch/Sound/RageSoundDriver.h index c908267ab6..09bdceee81 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver.h +++ b/stepmania/src/arch/Sound/RageSoundDriver.h @@ -24,8 +24,7 @@ protected: /* When a sound is finished playing (GetPCM returns less than requested) and * the sound has been completely flushed (so GetPosition is no longer meaningful), - * call RageSound::SoundStopped(). Do *not* call this when StopMixing is - * called. */ + * call RageSound::StopPlaying(). Do *not* call it when StopMixing is called. */ /* Optional, if needed: */ virtual void Update(float delta) { } diff --git a/stepmania/src/arch/Sound/RageSoundDriver_DSound.cpp b/stepmania/src/arch/Sound/RageSoundDriver_DSound.cpp index 7f00570a04..78c73631d1 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_DSound.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_DSound.cpp @@ -75,12 +75,13 @@ void RageSound_DSound::Update(float delta) { if(str[i]->state != str[i]->STOPPING) continue; - if(str[i]->str_ds->GetPosition() < str[i]->flush_pos) + int ps = str[i]->str_ds->GetPosition(); + if(ps < str[i]->flush_pos) continue; /* stopping but still flushing */ /* The sound has stopped and flushed all of its buffers. */ if(str[i]->snd != NULL) - str[i]->snd->SoundStopped(); + str[i]->snd->StopPlaying(); str[i]->snd = NULL; str[i]->str_ds->Stop(); diff --git a/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.cpp b/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.cpp index eadfa00acf..cd1cbf350f 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.cpp @@ -125,7 +125,7 @@ void RageSound_DSound_Software::Update(float delta) continue; /* stopping but still flushing */ /* This sound is done. */ - snds[i]->snd->SoundStopped(); + snds[i]->snd->StopPlaying(); } } diff --git a/stepmania/src/arch/Sound/RageSoundDriver_WaveOut.cpp b/stepmania/src/arch/Sound/RageSoundDriver_WaveOut.cpp index fa2793956f..199e41b70e 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_WaveOut.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_WaveOut.cpp @@ -136,7 +136,7 @@ void RageSound_WaveOut::Update(float delta) continue; /* stopping but still flushing */ /* This sound is done. */ - snds[i]->snd->SoundStopped(); + snds[i]->snd->StopPlaying(); } }