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