]> git.saurik.com Git - wxWidgets.git/blame - src/unix/sound_sdl.cpp
Change wxWakeUpPipe to be a wxEventLoopSourceHandler.
[wxWidgets.git] / src / unix / sound_sdl.cpp
CommitLineData
9be32e8f 1/////////////////////////////////////////////////////////////////////////////
521bf4ff 2// Name: src/unix/sound_sdl.cpp
9be32e8f
VS
3// Purpose: wxSound backend using SDL
4// Author: Vaclav Slavik
5// Modified by:
6// Created: 2004/01/31
7// RCS-ID: $Id$
f156e20c 8// Copyright: (c) 2004, Open Source Applications Foundation
521bf4ff 9// Licence: wxWindows licence
9be32e8f
VS
10/////////////////////////////////////////////////////////////////////////////
11
12// for compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
9be32e8f 15#if defined(__BORLANDC__)
02761f6c 16 #pragma hdrstop
9be32e8f
VS
17#endif
18
f156e20c 19#if wxUSE_SOUND && wxUSE_LIBSDL
9be32e8f
VS
20
21#include <SDL.h>
22
23#ifndef WX_PRECOMP
24 #include "wx/event.h"
25 #include "wx/intl.h"
26 #include "wx/log.h"
9be32e8f 27 #include "wx/utils.h"
02761f6c 28 #include "wx/module.h"
9be32e8f
VS
29#endif
30
31#include "wx/thread.h"
9be32e8f 32#include "wx/sound.h"
9be32e8f
VS
33
34// ----------------------------------------------------------------------------
35// wxSoundBackendSDL, for Unix with libSDL
36// ----------------------------------------------------------------------------
37
9be32e8f
VS
38class wxSoundBackendSDLNotification : public wxEvent
39{
40public:
41 DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification)
42 wxSoundBackendSDLNotification();
521bf4ff 43 wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); }
9be32e8f
VS
44};
45
46typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction)
47 (wxSoundBackendSDLNotification&);
48
ff3fd98a 49wxDECLARE_EVENT(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, wxSoundBackendSDLNotification);
9be32e8f
VS
50
51#define EVT_SOUND_BACKEND_SDL_NOTIFICATON(func) \
52 DECLARE_EVENT_TABLE_ENTRY(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, \
53 -1, \
54 -1, \
3c778901 55 wxEVENT_HANDLER_CAST( wxSoundBackendSDLNotificationFunction, func ), \
d3b9f782 56 NULL ),
9be32e8f
VS
57
58IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler)
9b11752c 59wxDEFINE_EVENT( wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, wxSoundBackendSDLNotification );
9be32e8f
VS
60
61wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
62{
63 SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION);
64}
65
66class wxSoundBackendSDLEvtHandler;
67
68class wxSoundBackendSDL : public wxSoundBackend
69{
70public:
521bf4ff 71 wxSoundBackendSDL()
64f4500a 72 : m_initialized(false), m_playing(false), m_audioOpen(false),
342dc928 73 m_data(NULL), m_evtHandler(NULL) {}
9be32e8f 74 virtual ~wxSoundBackendSDL();
521bf4ff 75
9a83f860 76 wxString GetName() const { return wxT("Simple DirectMedia Layer"); }
9be32e8f
VS
77 int GetPriority() const { return 9; }
78 bool IsAvailable() const;
79 bool HasNativeAsyncPlayback() const { return true; }
83f7f12d
VS
80 bool Play(wxSoundData *data, unsigned flags,
81 volatile wxSoundPlaybackStatus *status);
9be32e8f
VS
82
83 void FillAudioBuffer(Uint8 *stream, int len);
83f7f12d 84 void FinishedPlayback();
521bf4ff 85
64f4500a
VS
86 void Stop();
87 bool IsPlaying() const { return m_playing; }
521bf4ff 88
9be32e8f 89private:
342dc928
VS
90 bool OpenAudio();
91 void CloseAudio();
521bf4ff 92
9be32e8f 93 bool m_initialized;
64f4500a
VS
94 bool m_playing, m_audioOpen;
95 // playback information:
96 wxSoundData *m_data;
97 unsigned m_pos;
98 SDL_AudioSpec m_spec;
99 bool m_loop;
100
9be32e8f
VS
101 wxSoundBackendSDLEvtHandler *m_evtHandler;
102};
103
104class wxSoundBackendSDLEvtHandler : public wxEvtHandler
105{
106public:
107 wxSoundBackendSDLEvtHandler(wxSoundBackendSDL *bk) : m_backend(bk) {}
108
109private:
110 void OnNotify(wxSoundBackendSDLNotification& WXUNUSED(event))
111 {
9a83f860
VZ
112 wxLogTrace(wxT("sound"),
113 wxT("received playback status change notification"));
83f7f12d 114 m_backend->FinishedPlayback();
9be32e8f
VS
115 }
116 wxSoundBackendSDL *m_backend;
117
118 DECLARE_EVENT_TABLE()
119};
120
121BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler, wxEvtHandler)
122 EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify)
123END_EVENT_TABLE()
124
125wxSoundBackendSDL::~wxSoundBackendSDL()
126{
64f4500a 127 Stop();
342dc928 128 CloseAudio();
64f4500a 129 delete m_evtHandler;
9be32e8f
VS
130}
131
132bool wxSoundBackendSDL::IsAvailable() const
133{
134 if (m_initialized)
135 return true;
136 if (SDL_WasInit(SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
137 {
138 if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) == -1)
139 return false;
140 }
141 wxConstCast(this, wxSoundBackendSDL)->m_initialized = true;
9a83f860 142 wxLogTrace(wxT("sound"), wxT("initialized SDL audio subsystem"));
9be32e8f
VS
143 return true;
144}
145
146extern "C" void wx_sdl_audio_callback(void *userdata, Uint8 *stream, int len)
147{
148 wxSoundBackendSDL *bk = (wxSoundBackendSDL*)userdata;
149 bk->FillAudioBuffer(stream, len);
150}
151
152void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
153{
64f4500a 154 if (m_playing)
9be32e8f
VS
155 {
156 // finished playing the sample
64f4500a 157 if (m_pos == m_data->m_dataBytes)
9be32e8f 158 {
9be32e8f
VS
159 m_playing = false;
160 wxSoundBackendSDLNotification event;
161 m_evtHandler->AddPendingEvent(event);
162 }
163 // still something to play
164 else
165 {
64f4500a 166 unsigned size = ((len + m_pos) < m_data->m_dataBytes) ?
9be32e8f 167 len :
64f4500a
VS
168 (m_data->m_dataBytes - m_pos);
169 memcpy(stream, m_data->m_data + m_pos, size);
170 m_pos += size;
9be32e8f
VS
171 len -= size;
172 stream += size;
173 }
174 }
175 // the sample doesn't play, fill the buffer with silence and wait for
176 // the main thread to shut the playback down:
177 if (len > 0)
178 {
64f4500a 179 if (m_loop)
9be32e8f 180 {
64f4500a 181 m_pos = 0;
9be32e8f
VS
182 FillAudioBuffer(stream, len);
183 return;
184 }
185 else
186 {
64f4500a 187 memset(stream, m_spec.silence, len);
9be32e8f
VS
188 }
189 }
190}
191
83f7f12d
VS
192void wxSoundBackendSDL::FinishedPlayback()
193{
194 if (!m_playing)
195 Stop();
196}
197
342dc928
VS
198bool wxSoundBackendSDL::OpenAudio()
199{
200 if (!m_audioOpen)
201 {
202 if (!m_evtHandler)
203 m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
521bf4ff 204
342dc928
VS
205 m_spec.silence = 0;
206 m_spec.samples = 4096;
207 m_spec.size = 0;
208 m_spec.callback = wx_sdl_audio_callback;
209 m_spec.userdata = (void*)this;
521bf4ff 210
9a83f860 211 wxLogTrace(wxT("sound"), wxT("opening SDL audio..."));
342dc928
VS
212 if (SDL_OpenAudio(&m_spec, NULL) >= 0)
213 {
214#if wxUSE_LOG_DEBUG
215 char driver[256];
cb50bdc0 216#if SDL_MAJOR_VERSION == 1
521bf4ff 217 SDL_AudioDriverName(driver, 256);
cb50bdc0
VZ
218#elif SDL_MAJOR_VERSION > 1
219 strncpy(driver, SDL_GetCurrentAudioDriver(), 256);
220#endif
9a83f860 221 wxLogTrace(wxT("sound"), wxT("opened audio, driver '%s'"),
342dc928
VS
222 wxString(driver, wxConvLocal).c_str());
223#endif
224 m_audioOpen = true;
225 return true;
226 }
227 else
228 {
229 wxString err(SDL_GetError(), wxConvLocal);
230 wxLogError(_("Couldn't open audio: %s"), err.c_str());
231 return false;
232 }
233 }
234 return true;
235}
236
237void wxSoundBackendSDL::CloseAudio()
238{
239 if (m_audioOpen)
240 {
241 SDL_CloseAudio();
9a83f860 242 wxLogTrace(wxT("sound"), wxT("closed audio"));
342dc928
VS
243 m_audioOpen = false;
244 }
245}
246
83f7f12d
VS
247bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags,
248 volatile wxSoundPlaybackStatus *WXUNUSED(status))
9be32e8f 249{
98840d95 250 Stop();
521bf4ff 251
64f4500a 252 int format;
9be32e8f 253 if (data->m_bitsPerSample == 8)
64f4500a 254 format = AUDIO_U8;
9be32e8f 255 else if (data->m_bitsPerSample == 16)
64f4500a 256 format = AUDIO_S16LSB;
9be32e8f
VS
257 else
258 return false;
64f4500a 259
64f4500a
VS
260 bool needsOpen = true;
261 if (m_audioOpen)
262 {
64f4500a
VS
263 if (format == m_spec.format &&
264 m_spec.freq == (int)data->m_samplingRate &&
265 m_spec.channels == data->m_channels)
266 {
267 needsOpen = false;
268 }
269 else
270 {
342dc928 271 CloseAudio();
64f4500a 272 }
64f4500a 273 }
521bf4ff 274
64f4500a
VS
275 if (needsOpen)
276 {
277 m_spec.format = format;
278 m_spec.freq = data->m_samplingRate;
279 m_spec.channels = data->m_channels;
342dc928 280 if (!OpenAudio())
342dc928 281 return false;
64f4500a 282 }
521bf4ff 283
98840d95 284 SDL_LockAudio();
9a83f860 285 wxLogTrace(wxT("sound"), wxT("playing new sound"));
342dc928
VS
286 m_playing = true;
287 m_pos = 0;
288 m_loop = (flags & wxSOUND_LOOP);
289 m_data = data;
290 data->IncRef();
98840d95 291 SDL_UnlockAudio();
9be32e8f 292
342dc928 293 SDL_PauseAudio(0);
9be32e8f 294
64f4500a 295 // wait until playback finishes if called in sync mode:
9be32e8f
VS
296 if (!(flags & wxSOUND_ASYNC))
297 {
9a83f860 298 wxLogTrace(wxT("sound"), wxT("waiting for sample to finish"));
83f7f12d 299 while (m_playing && m_data == data)
9be32e8f
VS
300 {
301#if wxUSE_THREADS
302 // give the playback thread a chance to add event to pending
303 // events queue, release GUI lock temporarily:
304 if (wxThread::IsMain())
305 wxMutexGuiLeave();
306#endif
7648370f 307 wxMilliSleep(10);
9be32e8f
VS
308#if wxUSE_THREADS
309 if (wxThread::IsMain())
310 wxMutexGuiEnter();
311#endif
312 }
9a83f860 313 wxLogTrace(wxT("sound"), wxT("sample finished"));
9be32e8f 314 }
64f4500a 315
9be32e8f
VS
316 return true;
317}
9be32e8f 318
64f4500a
VS
319void wxSoundBackendSDL::Stop()
320{
9be32e8f 321 SDL_LockAudio();
342dc928 322 SDL_PauseAudio(1);
98840d95 323 m_playing = false;
342dc928 324 if (m_data)
9be32e8f 325 {
64f4500a 326 m_data->DecRef();
342dc928 327 m_data = NULL;
9be32e8f 328 }
64f4500a 329 SDL_UnlockAudio();
9be32e8f
VS
330}
331
332extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
333{
334 return new wxSoundBackendSDL();
335}
336
f156e20c 337#endif // wxUSE_SOUND && wxUSE_LIBSDL