changed sound API

This commit is contained in:
Glenn Maynard
2002-12-22 08:13:33 +00:00
parent 5830d4cc97
commit f7bf2ef2fa
11 changed files with 171 additions and 109 deletions
+69 -49
View File
@@ -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)
+16 -8
View File
@@ -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; }
+43 -28
View File
@@ -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<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)
{
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);
}
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: */
+6 -3
View File
@@ -21,6 +21,8 @@ class RageSoundManager
/* Set of sounds that are finished and should be deleted. */
set<RageSound *> sounds_to_delete;
set<RageSound *> 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<RageSound *> &snds);
/* A list of all sounds that currently exist. RageSound adds and removes
* itself to this. */
set<RageSound *> all_sounds;
+3 -2
View File
@@ -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;
+27 -12
View File
@@ -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<RageSound *> 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);
+1 -1
View File
@@ -19,7 +19,7 @@
#include "RageSound.h"
#include "Sample3dObject.h"
const int nsounds = 5;
const int nsounds = 3;
class ScreenSandbox : public Screen
{
+1 -2
View File
@@ -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) { }
@@ -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();
@@ -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();
}
}
@@ -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();
}
}