added support for async playback to Unix implementation of wxSound, implemented SDL backend for playback if OSS is not available, fixed OSS to work on non-Linux Unices
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@25455 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
334
src/unix/sound_sdl.cpp
Normal file
334
src/unix/sound_sdl.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: sound_sdl.cpp
|
||||
// Purpose: wxSound backend using SDL
|
||||
// Author: Vaclav Slavik
|
||||
// Modified by:
|
||||
// Created: 2004/01/31
|
||||
// RCS-ID: $Id$
|
||||
// Copyright: (c) 2004, Vaclav Slavik
|
||||
// Licence: wxWindows licence
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// for compilers that support precompilation, includes "wx.h".
|
||||
#include "wx/wxprec.h"
|
||||
|
||||
#include "wx/setup.h"
|
||||
|
||||
#if defined(__BORLANDC__)
|
||||
#pragma hdrstop
|
||||
#endif
|
||||
|
||||
#if wxUSE_WAVE && wxUSE_LIBSDL
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#ifndef WX_PRECOMP
|
||||
#include "wx/event.h"
|
||||
#include "wx/intl.h"
|
||||
#include "wx/log.h"
|
||||
#include "wx/list.h"
|
||||
#include "wx/utils.h"
|
||||
#endif
|
||||
|
||||
#include "wx/thread.h"
|
||||
#include "wx/module.h"
|
||||
#include "wx/sound.h"
|
||||
#include "wx/listimpl.cpp"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxSoundBackendSDL, for Unix with libSDL
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
struct wxSoundBackendSDLQueueEntry
|
||||
{
|
||||
wxSoundData *m_data;
|
||||
unsigned m_pos;
|
||||
SDL_AudioSpec m_spec;
|
||||
bool m_loop;
|
||||
bool m_finished;
|
||||
};
|
||||
|
||||
WX_DECLARE_LIST(wxSoundBackendSDLQueueEntry, wxSoundBackendSDLQueue);
|
||||
WX_DEFINE_LIST(wxSoundBackendSDLQueue);
|
||||
|
||||
|
||||
class wxSoundBackendSDLNotification : public wxEvent
|
||||
{
|
||||
public:
|
||||
DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification)
|
||||
wxSoundBackendSDLNotification();
|
||||
wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); }
|
||||
};
|
||||
|
||||
typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction)
|
||||
(wxSoundBackendSDLNotification&);
|
||||
|
||||
BEGIN_DECLARE_EVENT_TYPES()
|
||||
DECLARE_LOCAL_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, -1)
|
||||
END_DECLARE_EVENT_TYPES()
|
||||
|
||||
#define EVT_SOUND_BACKEND_SDL_NOTIFICATON(func) \
|
||||
DECLARE_EVENT_TABLE_ENTRY(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, \
|
||||
-1, \
|
||||
-1, \
|
||||
(wxObjectEventFunction) \
|
||||
(wxSoundBackendSDLNotificationFunction)& func, \
|
||||
(wxObject *) NULL ),
|
||||
|
||||
IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler)
|
||||
DEFINE_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION)
|
||||
|
||||
wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
|
||||
{
|
||||
SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION);
|
||||
}
|
||||
|
||||
class wxSoundBackendSDLEvtHandler;
|
||||
|
||||
class wxSoundBackendSDL : public wxSoundBackend
|
||||
{
|
||||
public:
|
||||
wxSoundBackendSDL()
|
||||
: m_initialized(false), m_playing(false), m_evtHandler(NULL) {}
|
||||
virtual ~wxSoundBackendSDL();
|
||||
|
||||
wxString GetName() const { return _T("Simple DirectMedia Layer"); }
|
||||
int GetPriority() const { return 9; }
|
||||
bool IsAvailable() const;
|
||||
bool HasNativeAsyncPlayback() const { return true; }
|
||||
bool Play(wxSoundData *data, unsigned flags);
|
||||
|
||||
void FillAudioBuffer(Uint8 *stream, int len);
|
||||
bool PlayNextSampleInQueue();
|
||||
|
||||
private:
|
||||
bool m_initialized;
|
||||
bool m_playing;
|
||||
wxSoundBackendSDLQueue m_queue;
|
||||
wxSoundBackendSDLEvtHandler *m_evtHandler;
|
||||
};
|
||||
|
||||
class wxSoundBackendSDLEvtHandler : public wxEvtHandler
|
||||
{
|
||||
public:
|
||||
wxSoundBackendSDLEvtHandler(wxSoundBackendSDL *bk) : m_backend(bk) {}
|
||||
|
||||
private:
|
||||
void OnNotify(wxSoundBackendSDLNotification& WXUNUSED(event))
|
||||
{
|
||||
wxLogTrace(_T("sound"),
|
||||
_T("received playback status change notification"));
|
||||
m_backend->PlayNextSampleInQueue();
|
||||
}
|
||||
wxSoundBackendSDL *m_backend;
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler, wxEvtHandler)
|
||||
EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
wxSoundBackendSDL::~wxSoundBackendSDL()
|
||||
{
|
||||
SDL_LockAudio();
|
||||
if (m_playing)
|
||||
SDL_CloseAudio();
|
||||
SDL_UnlockAudio();
|
||||
wxDELETE(m_evtHandler);
|
||||
WX_CLEAR_LIST(wxSoundBackendSDLQueue, m_queue)
|
||||
}
|
||||
|
||||
bool wxSoundBackendSDL::IsAvailable() const
|
||||
{
|
||||
if (m_initialized)
|
||||
return true;
|
||||
if (SDL_WasInit(SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) == -1)
|
||||
return false;
|
||||
}
|
||||
wxConstCast(this, wxSoundBackendSDL)->m_initialized = true;
|
||||
wxLogTrace(_T("sound"), _T("initialized SDL audio subsystem"));
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" void wx_sdl_audio_callback(void *userdata, Uint8 *stream, int len)
|
||||
{
|
||||
wxSoundBackendSDL *bk = (wxSoundBackendSDL*)userdata;
|
||||
bk->FillAudioBuffer(stream, len);
|
||||
}
|
||||
|
||||
void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
|
||||
{
|
||||
wxSoundBackendSDLQueueEntry *e = m_queue.front();
|
||||
if (!e->m_finished)
|
||||
{
|
||||
// finished playing the sample
|
||||
if (e->m_pos == e->m_data->m_dataBytes)
|
||||
{
|
||||
e->m_finished = true;
|
||||
m_playing = false;
|
||||
wxSoundBackendSDLNotification event;
|
||||
m_evtHandler->AddPendingEvent(event);
|
||||
}
|
||||
// still something to play
|
||||
else
|
||||
{
|
||||
unsigned size = ((len + e->m_pos) < e->m_data->m_dataBytes) ?
|
||||
len :
|
||||
(e->m_data->m_dataBytes - e->m_pos);
|
||||
memcpy(stream, e->m_data->m_data + e->m_pos, size);
|
||||
e->m_pos += size;
|
||||
len -= size;
|
||||
stream += size;
|
||||
}
|
||||
}
|
||||
// the sample doesn't play, fill the buffer with silence and wait for
|
||||
// the main thread to shut the playback down:
|
||||
if (len > 0)
|
||||
{
|
||||
if (e->m_loop)
|
||||
{
|
||||
e->m_pos = 0;
|
||||
FillAudioBuffer(stream, len);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(stream, e->m_spec.silence, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags)
|
||||
{
|
||||
data->IncRef();
|
||||
|
||||
wxSoundBackendSDLQueueEntry *e = new wxSoundBackendSDLQueueEntry();
|
||||
e->m_data = data;
|
||||
e->m_pos = 0;
|
||||
e->m_loop = (flags & wxSOUND_LOOP);
|
||||
e->m_finished = false;
|
||||
e->m_spec.freq = data->m_samplingRate;
|
||||
e->m_spec.channels = data->m_channels;
|
||||
e->m_spec.silence = 0;
|
||||
e->m_spec.samples = 4096;
|
||||
e->m_spec.size = 0;
|
||||
e->m_spec.callback = wx_sdl_audio_callback;
|
||||
e->m_spec.userdata = (void*)this;
|
||||
|
||||
if (data->m_bitsPerSample == 8)
|
||||
e->m_spec.format = AUDIO_U8;
|
||||
else if (data->m_bitsPerSample == 16)
|
||||
e->m_spec.format = AUDIO_S16LSB;
|
||||
else
|
||||
return false;
|
||||
|
||||
m_queue.push_back(e);
|
||||
wxLogTrace(_T("sound"), _T("queued sample %p for playback"), e);
|
||||
|
||||
if (!PlayNextSampleInQueue())
|
||||
return false;
|
||||
|
||||
if (!(flags & wxSOUND_ASYNC))
|
||||
{
|
||||
wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
|
||||
while (!m_queue.empty() && m_queue.front() == e && !e->m_finished)
|
||||
{
|
||||
#if wxUSE_THREADS
|
||||
// give the playback thread a chance to add event to pending
|
||||
// events queue, release GUI lock temporarily:
|
||||
if (wxThread::IsMain())
|
||||
wxMutexGuiLeave();
|
||||
#endif
|
||||
wxUsleep(10);
|
||||
#if wxUSE_THREADS
|
||||
if (wxThread::IsMain())
|
||||
wxMutexGuiEnter();
|
||||
#endif
|
||||
}
|
||||
wxLogTrace(_T("sound"), _T("sample finished"));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool wxSoundBackendSDL::PlayNextSampleInQueue()
|
||||
{
|
||||
bool status = true;
|
||||
|
||||
SDL_LockAudio();
|
||||
|
||||
if (!m_evtHandler)
|
||||
m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
|
||||
|
||||
if (!m_playing && !m_queue.empty())
|
||||
{
|
||||
bool needsReopen = true;
|
||||
// shut down playing of finished sound:
|
||||
wxSoundBackendSDLQueueEntry *e = m_queue.front();
|
||||
if (e->m_finished)
|
||||
{
|
||||
SDL_PauseAudio(1);
|
||||
e->m_data->DecRef();
|
||||
m_queue.pop_front();
|
||||
if (!m_queue.empty() &&
|
||||
e->m_spec.freq == m_queue.front()->m_spec.freq &&
|
||||
e->m_spec.channels == m_queue.front()->m_spec.channels &&
|
||||
e->m_spec.format == m_queue.front()->m_spec.format)
|
||||
{
|
||||
needsReopen = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_CloseAudio();
|
||||
wxLogTrace(_T("sound"), _T("closed audio"));
|
||||
}
|
||||
delete e;
|
||||
}
|
||||
// start playing another one:
|
||||
if (!m_queue.empty())
|
||||
{
|
||||
wxSoundBackendSDLQueueEntry *e = m_queue.front();
|
||||
m_playing = true;
|
||||
wxLogTrace(_T("sound"), _T("playing sample %p"), e);
|
||||
|
||||
if (needsReopen)
|
||||
{
|
||||
wxLogTrace(_T("sound"), _T("opening SDL audio..."));
|
||||
status = (SDL_OpenAudio(&e->m_spec, NULL) >= 0);
|
||||
if (status)
|
||||
{
|
||||
#if wxUSE_LOG_DEBUG
|
||||
char driver[256];
|
||||
SDL_AudioDriverName(driver, 256);
|
||||
wxLogTrace(_T("sound"), _T("opened audio, driver '%s'"),
|
||||
wxString(driver, wxConvLocal).c_str());
|
||||
#endif
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString err(SDL_GetError(), wxConvLocal);
|
||||
wxLogError(_("Couldn't open audio: %s"), err.c_str());
|
||||
m_queue.pop_front();
|
||||
delete e;
|
||||
}
|
||||
}
|
||||
else
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UnlockAudio();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
|
||||
{
|
||||
return new wxSoundBackendSDL();
|
||||
}
|
||||
|
||||
#endif // wxUSE_WAVE && wxUSE_LIBSDL
|
Reference in New Issue
Block a user