1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxSound backend using SDL
4 // Author: Vaclav Slavik
8 // Copyright: (c) 2004, Vaclav Slavik
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // for compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
17 #if defined(__BORLANDC__)
21 #if wxUSE_WAVE && wxUSE_LIBSDL
33 #include "wx/thread.h"
34 #include "wx/module.h"
36 #include "wx/listimpl.cpp"
38 // ----------------------------------------------------------------------------
39 // wxSoundBackendSDL, for Unix with libSDL
40 // ----------------------------------------------------------------------------
42 struct wxSoundBackendSDLQueueEntry
51 WX_DECLARE_LIST(wxSoundBackendSDLQueueEntry
, wxSoundBackendSDLQueue
);
52 WX_DEFINE_LIST(wxSoundBackendSDLQueue
);
55 class wxSoundBackendSDLNotification
: public wxEvent
58 DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification
)
59 wxSoundBackendSDLNotification();
60 wxEvent
*Clone() const { return new wxSoundBackendSDLNotification(*this); }
63 typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction
)
64 (wxSoundBackendSDLNotification
&);
66 BEGIN_DECLARE_EVENT_TYPES()
67 DECLARE_LOCAL_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION
, -1)
68 END_DECLARE_EVENT_TYPES()
70 #define EVT_SOUND_BACKEND_SDL_NOTIFICATON(func) \
71 DECLARE_EVENT_TABLE_ENTRY(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, \
74 (wxObjectEventFunction) \
75 (wxSoundBackendSDLNotificationFunction)& func, \
78 IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification
, wxEvtHandler
)
79 DEFINE_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION
)
81 wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
83 SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION
);
86 class wxSoundBackendSDLEvtHandler
;
88 class wxSoundBackendSDL
: public wxSoundBackend
92 : m_initialized(false), m_playing(false), m_evtHandler(NULL
) {}
93 virtual ~wxSoundBackendSDL();
95 wxString
GetName() const { return _T("Simple DirectMedia Layer"); }
96 int GetPriority() const { return 9; }
97 bool IsAvailable() const;
98 bool HasNativeAsyncPlayback() const { return true; }
99 bool Play(wxSoundData
*data
, unsigned flags
);
101 void FillAudioBuffer(Uint8
*stream
, int len
);
102 bool PlayNextSampleInQueue();
107 wxSoundBackendSDLQueue m_queue
;
108 wxSoundBackendSDLEvtHandler
*m_evtHandler
;
111 class wxSoundBackendSDLEvtHandler
: public wxEvtHandler
114 wxSoundBackendSDLEvtHandler(wxSoundBackendSDL
*bk
) : m_backend(bk
) {}
117 void OnNotify(wxSoundBackendSDLNotification
& WXUNUSED(event
))
119 wxLogTrace(_T("sound"),
120 _T("received playback status change notification"));
121 m_backend
->PlayNextSampleInQueue();
123 wxSoundBackendSDL
*m_backend
;
125 DECLARE_EVENT_TABLE()
128 BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler
, wxEvtHandler
)
129 EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify
)
132 wxSoundBackendSDL::~wxSoundBackendSDL()
138 wxDELETE(m_evtHandler
);
139 WX_CLEAR_LIST(wxSoundBackendSDLQueue
, m_queue
)
142 bool wxSoundBackendSDL::IsAvailable() const
146 if (SDL_WasInit(SDL_INIT_AUDIO
) != SDL_INIT_AUDIO
)
148 if (SDL_Init(SDL_INIT_AUDIO
| SDL_INIT_NOPARACHUTE
) == -1)
151 wxConstCast(this, wxSoundBackendSDL
)->m_initialized
= true;
152 wxLogTrace(_T("sound"), _T("initialized SDL audio subsystem"));
156 extern "C" void wx_sdl_audio_callback(void *userdata
, Uint8
*stream
, int len
)
158 wxSoundBackendSDL
*bk
= (wxSoundBackendSDL
*)userdata
;
159 bk
->FillAudioBuffer(stream
, len
);
162 void wxSoundBackendSDL::FillAudioBuffer(Uint8
*stream
, int len
)
164 wxSoundBackendSDLQueueEntry
*e
= m_queue
.front();
167 // finished playing the sample
168 if (e
->m_pos
== e
->m_data
->m_dataBytes
)
170 e
->m_finished
= true;
172 wxSoundBackendSDLNotification event
;
173 m_evtHandler
->AddPendingEvent(event
);
175 // still something to play
178 unsigned size
= ((len
+ e
->m_pos
) < e
->m_data
->m_dataBytes
) ?
180 (e
->m_data
->m_dataBytes
- e
->m_pos
);
181 memcpy(stream
, e
->m_data
->m_data
+ e
->m_pos
, size
);
187 // the sample doesn't play, fill the buffer with silence and wait for
188 // the main thread to shut the playback down:
194 FillAudioBuffer(stream
, len
);
199 memset(stream
, e
->m_spec
.silence
, len
);
204 bool wxSoundBackendSDL::Play(wxSoundData
*data
, unsigned flags
)
208 wxSoundBackendSDLQueueEntry
*e
= new wxSoundBackendSDLQueueEntry();
211 e
->m_loop
= (flags
& wxSOUND_LOOP
);
212 e
->m_finished
= false;
213 e
->m_spec
.freq
= data
->m_samplingRate
;
214 e
->m_spec
.channels
= data
->m_channels
;
215 e
->m_spec
.silence
= 0;
216 e
->m_spec
.samples
= 4096;
218 e
->m_spec
.callback
= wx_sdl_audio_callback
;
219 e
->m_spec
.userdata
= (void*)this;
221 if (data
->m_bitsPerSample
== 8)
222 e
->m_spec
.format
= AUDIO_U8
;
223 else if (data
->m_bitsPerSample
== 16)
224 e
->m_spec
.format
= AUDIO_S16LSB
;
228 m_queue
.push_back(e
);
229 wxLogTrace(_T("sound"), _T("queued sample %p for playback"), e
);
231 if (!PlayNextSampleInQueue())
234 if (!(flags
& wxSOUND_ASYNC
))
236 wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
237 while (!m_queue
.empty() && m_queue
.front() == e
&& !e
->m_finished
)
240 // give the playback thread a chance to add event to pending
241 // events queue, release GUI lock temporarily:
242 if (wxThread::IsMain())
247 if (wxThread::IsMain())
251 wxLogTrace(_T("sound"), _T("sample finished"));
257 bool wxSoundBackendSDL::PlayNextSampleInQueue()
264 m_evtHandler
= new wxSoundBackendSDLEvtHandler(this);
266 if (!m_playing
&& !m_queue
.empty())
268 bool needsReopen
= true;
269 // shut down playing of finished sound:
270 wxSoundBackendSDLQueueEntry
*e
= m_queue
.front();
276 if (!m_queue
.empty() &&
277 e
->m_spec
.freq
== m_queue
.front()->m_spec
.freq
&&
278 e
->m_spec
.channels
== m_queue
.front()->m_spec
.channels
&&
279 e
->m_spec
.format
== m_queue
.front()->m_spec
.format
)
286 wxLogTrace(_T("sound"), _T("closed audio"));
290 // start playing another one:
291 if (!m_queue
.empty())
293 wxSoundBackendSDLQueueEntry
*e
= m_queue
.front();
295 wxLogTrace(_T("sound"), _T("playing sample %p"), e
);
299 wxLogTrace(_T("sound"), _T("opening SDL audio..."));
300 status
= (SDL_OpenAudio(&e
->m_spec
, NULL
) >= 0);
305 SDL_AudioDriverName(driver
, 256);
306 wxLogTrace(_T("sound"), _T("opened audio, driver '%s'"),
307 wxString(driver
, wxConvLocal
).c_str());
313 wxString
err(SDL_GetError(), wxConvLocal
);
314 wxLogError(_("Couldn't open audio: %s"), err
.c_str());
329 extern "C" wxSoundBackend
*wxCreateSoundBackendSDL()
331 return new wxSoundBackendSDL();
334 #endif // wxUSE_WAVE && wxUSE_LIBSDL