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