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:
parent
98840d95db
commit
83f7f12df2
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user