API and code changes to allowing stopping playback

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@25464 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Václav Slavík 2004-02-02 00:49:39 +00:00
parent 98840d95db
commit 83f7f12df2
3 changed files with 245 additions and 69 deletions

View File

@ -73,6 +73,12 @@ public:
bool Create(int size, const wxByte* data);
bool IsOk() const { return m_data != NULL; }
// Stop playing any sound
static void Stop();
// Returns true if a sound is being played
static bool IsPlaying();
// for internal use
static void UnloadBackend();
@ -105,6 +111,16 @@ private:
// the need for backends. This class is for use by wxWindows and people writing
// additional backends only, it is _not_ for use by applications!
// Structure that holds playback status information
struct wxSoundPlaybackStatus
{
// playback is in progress
bool m_playing;
// main thread called wxSound::Stop()
bool m_stopRequested;
};
// Audio backend interface
class wxSoundBackend
{
public:
@ -123,11 +139,23 @@ public:
// Returns true if the backend is capable of playing sound asynchronously.
// If false, then wxWindows creates a playback thread and handles async
// playback, otherwise it is left up to the backend (will usually be more
// effective)
// effective).
virtual bool HasNativeAsyncPlayback() const = 0;
// Plays the sound. flags are same flags as those passed to wxSound::Play.
// The function should periodically check the value of
// status->m_stopRequested and terminate if it is set to true (it may
// be modified by another thread)
virtual bool Play(wxSoundData *data, unsigned flags,
volatile wxSoundPlaybackStatus *status) = 0;
// Plays the sound. flags are same flags as those passed to wxSound::Play
virtual bool Play(wxSoundData *data, unsigned flags) = 0;
// Stops playback (if something is played).
virtual void Stop() = 0;
// Returns true if the backend is playing anything at the moment.
// (This method is never called for backends that don't support async
// playback.)
virtual bool IsPlaying() const = 0;
};

View File

@ -30,7 +30,7 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#if HAVE_SYS_SOUNDCARD_H
#ifdef HAVE_SYS_SOUNDCARD_H
#include <sys/soundcard.h>
#endif
@ -46,6 +46,39 @@
#include "wx/sound.h"
#include "wx/dynlib.h"
#if wxUSE_THREADS
// mutex for all wxSound's synchronization
static wxMutex gs_soundMutex;
#endif
// ----------------------------------------------------------------------------
// wxSoundData
// ----------------------------------------------------------------------------
void wxSoundData::IncRef()
{
#if wxUSE_THREADS
wxMutexLocker locker(gs_soundMutex);
#endif
m_refCnt++;
}
void wxSoundData::DecRef()
{
#if wxUSE_THREADS
wxMutexLocker locker(gs_soundMutex);
#endif
if (--m_refCnt == 0)
delete this;
}
wxSoundData::~wxSoundData()
{
delete[] m_dataWithHeader;
}
// ----------------------------------------------------------------------------
// wxSoundBackendNull, used in absence of audio API or card
// ----------------------------------------------------------------------------
@ -57,8 +90,11 @@ public:
int GetPriority() const { return 0; }
bool IsAvailable() const { return true; }
bool HasNativeAsyncPlayback() const { return true; }
bool Play(wxSoundData *WXUNUSED(data), unsigned WXUNUSED(flags))
bool Play(wxSoundData *WXUNUSED(data), unsigned WXUNUSED(flags),
volatile wxSoundPlaybackStatus *WXUNUSED(status))
{ return true; }
void Stop() {}
bool IsPlaying() const { return false; }
};
@ -79,7 +115,10 @@ public:
int GetPriority() const { return 10; }
bool IsAvailable() const;
bool HasNativeAsyncPlayback() const { return false; }
bool Play(wxSoundData *data, unsigned flags);
bool Play(wxSoundData *data, unsigned flags,
volatile wxSoundPlaybackStatus *status);
void Stop() {}
bool IsPlaying() const { return false; }
private:
int OpenDSP(const wxSoundData *data);
@ -99,7 +138,8 @@ bool wxSoundBackendOSS::IsAvailable() const
return true;
}
bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags)
bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags,
volatile wxSoundPlaybackStatus *status)
{
int dev = OpenDSP(data);
@ -117,6 +157,13 @@ bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags)
do
{
if (status->m_stopRequested)
{
wxLogTrace(_T("sound"), _T("playback stopped"));
close(dev);
return true;
}
i= (int)((l + m_DSPblkSize) < datasize ?
m_DSPblkSize : (datasize - l));
if (write(dev, &data->m_data[l], i) != i)
@ -128,6 +175,7 @@ bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags)
} while (flags & wxSOUND_LOOP);
close(dev);
return true;
}
@ -155,6 +203,7 @@ bool wxSoundBackendOSS::InitDSP(int dev, int iDataBits, int iChannel,
{
if (ioctl(dev, SNDCTL_DSP_GETBLKSIZE, &m_DSPblkSize) < 0)
return false;
wxLogTrace(_T("sound"), _T("OSS block size: %i"), m_DSPblkSize);
if (m_DSPblkSize < 4096 || m_DSPblkSize > 65536)
return false;
if (ioctl(dev, SNDCTL_DSP_SAMPLESIZE, &iDataBits) < 0)
@ -168,62 +217,147 @@ bool wxSoundBackendOSS::InitDSP(int dev, int iDataBits, int iChannel,
#endif // HAVE_SYS_SOUNDCARD_H
// ----------------------------------------------------------------------------
// wxSoundData
// ----------------------------------------------------------------------------
void wxSoundData::IncRef()
{
m_refCnt++;
}
void wxSoundData::DecRef()
{
if (--m_refCnt == 0)
delete this;
}
wxSoundData::~wxSoundData()
{
delete[] m_dataWithHeader;
}
// ----------------------------------------------------------------------------
// wxSoundAsyncPlaybackThread
// wxSoundSyncOnlyAdaptor
// ----------------------------------------------------------------------------
#if wxUSE_THREADS
// mutex for all wxSound's synchronization
static wxMutex gs_soundMutex;
class wxSoundSyncOnlyAdaptor;
// this class manages asynchronous playback of audio if the backend doesn't
// support it natively (e.g. OSS backend)
class wxSoundAsyncPlaybackThread : public wxThread
{
public:
wxSoundAsyncPlaybackThread(wxSoundBackend *backend,
wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor *adaptor,
wxSoundData *data, unsigned flags)
: wxThread(), m_backend(backend), m_data(data), m_flags(flags) {}
virtual ExitCode Entry()
{
m_backend->Play(m_data, m_flags & ~wxSOUND_ASYNC);
wxMutexLocker locker(gs_soundMutex);
m_data->DecRef();
wxLogTrace(_T("sound"), _T("terminated async playback thread"));
return 0;
}
: wxThread(), m_adapt(adaptor), m_data(data), m_flags(flags) {}
virtual ExitCode Entry();
protected:
wxSoundBackend *m_backend;
wxSoundSyncOnlyAdaptor *m_adapt;
wxSoundData *m_data;
unsigned m_flags;
};
#endif // wxUSE_THREADS
// This class turns wxSoundBackend that doesn't support asynchronous playback
// into one that does
class wxSoundSyncOnlyAdaptor : public wxSoundBackend
{
public:
wxSoundSyncOnlyAdaptor(wxSoundBackend *backend)
: m_backend(backend), m_playing(false) {}
~wxSoundSyncOnlyAdaptor()
{
delete m_backend;
}
wxString GetName() const
{
return m_backend->GetName();
}
int GetPriority() const
{
return m_backend->GetPriority();
}
bool IsAvailable() const
{
return m_backend->IsAvailable();
}
bool HasNativeAsyncPlayback() const
{
return true;
}
bool Play(wxSoundData *data, unsigned flags,
volatile wxSoundPlaybackStatus *status);
void Stop();
bool IsPlaying() const;
private:
friend class wxSoundAsyncPlaybackThread;
wxSoundBackend *m_backend;
bool m_playing;
#if wxUSE_THREADS
// player thread holds this mutex and releases it after it finishes
// playing, so that the main thread knows when it can play sound
wxMutex m_mutexRightToPlay;
wxSoundPlaybackStatus m_status;
#endif
};
#if wxUSE_THREADS
wxThread::ExitCode wxSoundAsyncPlaybackThread::Entry()
{
m_adapt->m_backend->Play(m_data, m_flags & ~wxSOUND_ASYNC,
&m_adapt->m_status);
m_data->DecRef();
m_adapt->m_playing = false;
m_adapt->m_mutexRightToPlay.Unlock();
wxLogTrace(_T("sound"), _T("terminated async playback thread"));
return 0;
}
#endif
bool wxSoundSyncOnlyAdaptor::Play(wxSoundData *data, unsigned flags,
volatile wxSoundPlaybackStatus *status)
{
Stop();
if (flags & wxSOUND_ASYNC)
{
#if wxUSE_THREADS
m_mutexRightToPlay.Lock();
m_status.m_playing = true;
m_status.m_stopRequested = false;
data->IncRef();
wxThread *th = new wxSoundAsyncPlaybackThread(this, data, flags);
th->Create();
th->Run();
wxLogTrace(_T("sound"), _T("launched async playback thread"));
return true;
#else
wxLogError(_("Unable to play sound asynchronously."));
return false;
#endif
}
else
{
#if wxUSE_THREADS
m_mutexRightToPlay.Lock();
#endif
bool rv = m_backend->Play(data, flags, status);
#if wxUSE_THREADS
m_mutexRightToPlay.Unlock();
#endif
return rv;
}
}
void wxSoundSyncOnlyAdaptor::Stop()
{
wxLogTrace(_T("sound"), _T("asking audio to stop"));
// tell the player thread (if running) to stop playback ASAP:
m_status.m_stopRequested = true;
// acquire the mutex to be sure no sound is being played, then
// release it because we don't need it for anything (the effect of this
// is that calling thread will wait until playback thread reacts to
// our request to interrupt playback):
m_mutexRightToPlay.Lock();
m_mutexRightToPlay.Unlock();
wxLogTrace(_T("sound"), _T("audio was stopped"));
}
bool wxSoundSyncOnlyAdaptor::IsPlaying() const
{
return m_status.m_playing;
}
// ----------------------------------------------------------------------------
// wxSound
// ----------------------------------------------------------------------------
@ -357,6 +491,9 @@ bool wxSound::Create(int size, const wxByte* data)
if (!ms_backend)
ms_backend = new wxSoundBackendNull();
if (!ms_backend->HasNativeAsyncPlayback())
ms_backend = new wxSoundSyncOnlyAdaptor(ms_backend);
wxLogTrace(_T("sound"),
_T("using backend '%s'"), ms_backend->GetName().c_str());
}
@ -367,6 +504,9 @@ bool wxSound::Create(int size, const wxByte* data)
if (ms_backend)
{
wxLogTrace(_T("sound"), _T("unloading backend"));
Stop();
delete ms_backend;
ms_backend = NULL;
#if wxUSE_LIBSDL && wxUSE_PLUGINS
@ -377,36 +517,33 @@ bool wxSound::Create(int size, const wxByte* data)
bool wxSound::DoPlay(unsigned flags)
{
wxASSERT_MSG( (flags & wxSOUND_LOOP) == 0 || (flags & wxSOUND_ASYNC) != 0,
_T("sound can only be looped asynchronously") );
wxCHECK_MSG( IsOk(), false, _T("Attempt to play invalid wave data") );
EnsureBackend();
wxSoundPlaybackStatus status;
status.m_playing = true;
status.m_stopRequested = false;
return ms_backend->Play(m_data, flags, &status);
}
if ((flags & wxSOUND_ASYNC) && !ms_backend->HasNativeAsyncPlayback())
{
#if wxUSE_THREADS
wxMutexLocker locker(gs_soundMutex);
m_data->IncRef();
wxThread *th = new wxSoundAsyncPlaybackThread(ms_backend, m_data, flags);
th->Create();
th->Run();
wxLogTrace(_T("sound"), _T("launched async playback thread"));
#else
wxLogError(_("Unable to play sound asynchronously."));
return false;
#endif
}
/*static*/ void wxSound::Stop()
{
if (ms_backend)
ms_backend->Stop();
}
/*static*/ bool wxSound::IsPlaying()
{
if (ms_backend)
return ms_backend->IsPlaying();
else
{
ms_backend->Play(m_data, flags);
}
return true;
return false;
}
void wxSound::Free()
{
#if wxUSE_THREADS
wxMutexLocker locker(gs_soundMutex);
#endif
if (m_data)
m_data->DecRef();
}
@ -420,7 +557,7 @@ typedef struct
wxUint32 ulAvgBytesPerSec;
wxUint16 uiBlockAlign;
wxUint16 uiBitsPerSample;
} WAVEFORMAT;
} WAVEFORMAT;
#define MONO 1 // and stereo is 2 by wav format
#define WAVE_FORMAT_PCM 1

View File

@ -82,9 +82,12 @@ public:
int GetPriority() const { return 9; }
bool IsAvailable() const;
bool HasNativeAsyncPlayback() const { return true; }
bool Play(wxSoundData *data, unsigned flags);
bool Play(wxSoundData *data, unsigned flags,
volatile wxSoundPlaybackStatus *status);
void FillAudioBuffer(Uint8 *stream, int len);
void FinishedPlayback();
void Stop();
bool IsPlaying() const { return m_playing; }
@ -113,7 +116,7 @@ private:
{
wxLogTrace(_T("sound"),
_T("received playback status change notification"));
m_backend->Stop();
m_backend->FinishedPlayback();
}
wxSoundBackendSDL *m_backend;
@ -191,6 +194,12 @@ void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
}
}
void wxSoundBackendSDL::FinishedPlayback()
{
if (!m_playing)
Stop();
}
bool wxSoundBackendSDL::OpenAudio()
{
if (!m_audioOpen)
@ -236,7 +245,8 @@ void wxSoundBackendSDL::CloseAudio()
}
}
bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags)
bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags,
volatile wxSoundPlaybackStatus *WXUNUSED(status))
{
Stop();
@ -273,6 +283,7 @@ bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags)
}
SDL_LockAudio();
wxLogTrace(_T("sound"), _T("playing new sound"));
m_playing = true;
m_pos = 0;
m_loop = (flags & wxSOUND_LOOP);
@ -286,7 +297,7 @@ bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags)
if (!(flags & wxSOUND_ASYNC))
{
wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
while (m_playing)
while (m_playing && m_data == data)
{
#if wxUSE_THREADS
// give the playback thread a chance to add event to pending