]> git.saurik.com Git - wxWidgets.git/blob - src/unix/sound_sdl.cpp
Don't reuse the same event object for multiple events in wxGTK.
[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 // 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 #if defined(__BORLANDC__)
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_SOUND && wxUSE_LIBSDL
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"
27 #include "wx/utils.h"
28 #include "wx/module.h"
29 #endif
30
31 #include "wx/thread.h"
32 #include "wx/sound.h"
33
34 // ----------------------------------------------------------------------------
35 // wxSoundBackendSDL, for Unix with libSDL
36 // ----------------------------------------------------------------------------
37
38 class wxSoundBackendSDLNotification : public wxEvent
39 {
40 public:
41 DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification)
42 wxSoundBackendSDLNotification();
43 wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); }
44 };
45
46 typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction)
47 (wxSoundBackendSDLNotification&);
48
49 wxDECLARE_EVENT(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, wxSoundBackendSDLNotification);
50
51 #define EVT_SOUND_BACKEND_SDL_NOTIFICATON(func) \
52 DECLARE_EVENT_TABLE_ENTRY(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, \
53 -1, \
54 -1, \
55 wxEVENT_HANDLER_CAST( wxSoundBackendSDLNotificationFunction, func ), \
56 NULL ),
57
58 IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler)
59 wxDEFINE_EVENT( wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, wxSoundBackendSDLNotification );
60
61 wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
62 {
63 SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION);
64 }
65
66 class wxSoundBackendSDLEvtHandler;
67
68 class wxSoundBackendSDL : public wxSoundBackend
69 {
70 public:
71 wxSoundBackendSDL()
72 : m_initialized(false), m_playing(false), m_audioOpen(false),
73 m_data(NULL), m_evtHandler(NULL) {}
74 virtual ~wxSoundBackendSDL();
75
76 wxString GetName() const { return wxT("Simple DirectMedia Layer"); }
77 int GetPriority() const { return 9; }
78 bool IsAvailable() const;
79 bool HasNativeAsyncPlayback() const { return true; }
80 bool Play(wxSoundData *data, unsigned flags,
81 volatile wxSoundPlaybackStatus *status);
82
83 void FillAudioBuffer(Uint8 *stream, int len);
84 void FinishedPlayback();
85
86 void Stop();
87 bool IsPlaying() const { return m_playing; }
88
89 private:
90 bool OpenAudio();
91 void CloseAudio();
92
93 bool m_initialized;
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
101 wxSoundBackendSDLEvtHandler *m_evtHandler;
102 };
103
104 class wxSoundBackendSDLEvtHandler : public wxEvtHandler
105 {
106 public:
107 wxSoundBackendSDLEvtHandler(wxSoundBackendSDL *bk) : m_backend(bk) {}
108
109 private:
110 void OnNotify(wxSoundBackendSDLNotification& WXUNUSED(event))
111 {
112 wxLogTrace(wxT("sound"),
113 wxT("received playback status change notification"));
114 m_backend->FinishedPlayback();
115 }
116 wxSoundBackendSDL *m_backend;
117
118 DECLARE_EVENT_TABLE()
119 };
120
121 BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler, wxEvtHandler)
122 EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify)
123 END_EVENT_TABLE()
124
125 wxSoundBackendSDL::~wxSoundBackendSDL()
126 {
127 Stop();
128 CloseAudio();
129 delete m_evtHandler;
130 }
131
132 bool 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;
142 wxLogTrace(wxT("sound"), wxT("initialized SDL audio subsystem"));
143 return true;
144 }
145
146 extern "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
152 void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
153 {
154 if (m_playing)
155 {
156 // finished playing the sample
157 if (m_pos == m_data->m_dataBytes)
158 {
159 m_playing = false;
160 wxSoundBackendSDLNotification event;
161 m_evtHandler->AddPendingEvent(event);
162 }
163 // still something to play
164 else
165 {
166 unsigned size = ((len + m_pos) < m_data->m_dataBytes) ?
167 len :
168 (m_data->m_dataBytes - m_pos);
169 memcpy(stream, m_data->m_data + m_pos, size);
170 m_pos += size;
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 {
179 if (m_loop)
180 {
181 m_pos = 0;
182 FillAudioBuffer(stream, len);
183 return;
184 }
185 else
186 {
187 memset(stream, m_spec.silence, len);
188 }
189 }
190 }
191
192 void wxSoundBackendSDL::FinishedPlayback()
193 {
194 if (!m_playing)
195 Stop();
196 }
197
198 bool wxSoundBackendSDL::OpenAudio()
199 {
200 if (!m_audioOpen)
201 {
202 if (!m_evtHandler)
203 m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
204
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;
210
211 wxLogTrace(wxT("sound"), wxT("opening SDL audio..."));
212 if (SDL_OpenAudio(&m_spec, NULL) >= 0)
213 {
214 #if wxUSE_LOG_DEBUG
215 char driver[256];
216 SDL_AudioDriverName(driver, 256);
217 wxLogTrace(wxT("sound"), wxT("opened audio, driver '%s'"),
218 wxString(driver, wxConvLocal).c_str());
219 #endif
220 m_audioOpen = true;
221 return true;
222 }
223 else
224 {
225 wxString err(SDL_GetError(), wxConvLocal);
226 wxLogError(_("Couldn't open audio: %s"), err.c_str());
227 return false;
228 }
229 }
230 return true;
231 }
232
233 void wxSoundBackendSDL::CloseAudio()
234 {
235 if (m_audioOpen)
236 {
237 SDL_CloseAudio();
238 wxLogTrace(wxT("sound"), wxT("closed audio"));
239 m_audioOpen = false;
240 }
241 }
242
243 bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags,
244 volatile wxSoundPlaybackStatus *WXUNUSED(status))
245 {
246 Stop();
247
248 int format;
249 if (data->m_bitsPerSample == 8)
250 format = AUDIO_U8;
251 else if (data->m_bitsPerSample == 16)
252 format = AUDIO_S16LSB;
253 else
254 return false;
255
256 bool needsOpen = true;
257 if (m_audioOpen)
258 {
259 if (format == m_spec.format &&
260 m_spec.freq == (int)data->m_samplingRate &&
261 m_spec.channels == data->m_channels)
262 {
263 needsOpen = false;
264 }
265 else
266 {
267 CloseAudio();
268 }
269 }
270
271 if (needsOpen)
272 {
273 m_spec.format = format;
274 m_spec.freq = data->m_samplingRate;
275 m_spec.channels = data->m_channels;
276 if (!OpenAudio())
277 return false;
278 }
279
280 SDL_LockAudio();
281 wxLogTrace(wxT("sound"), wxT("playing new sound"));
282 m_playing = true;
283 m_pos = 0;
284 m_loop = (flags & wxSOUND_LOOP);
285 m_data = data;
286 data->IncRef();
287 SDL_UnlockAudio();
288
289 SDL_PauseAudio(0);
290
291 // wait until playback finishes if called in sync mode:
292 if (!(flags & wxSOUND_ASYNC))
293 {
294 wxLogTrace(wxT("sound"), wxT("waiting for sample to finish"));
295 while (m_playing && m_data == data)
296 {
297 #if wxUSE_THREADS
298 // give the playback thread a chance to add event to pending
299 // events queue, release GUI lock temporarily:
300 if (wxThread::IsMain())
301 wxMutexGuiLeave();
302 #endif
303 wxMilliSleep(10);
304 #if wxUSE_THREADS
305 if (wxThread::IsMain())
306 wxMutexGuiEnter();
307 #endif
308 }
309 wxLogTrace(wxT("sound"), wxT("sample finished"));
310 }
311
312 return true;
313 }
314
315 void wxSoundBackendSDL::Stop()
316 {
317 SDL_LockAudio();
318 SDL_PauseAudio(1);
319 m_playing = false;
320 if (m_data)
321 {
322 m_data->DecRef();
323 m_data = NULL;
324 }
325 SDL_UnlockAudio();
326 }
327
328 extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
329 {
330 return new wxSoundBackendSDL();
331 }
332
333 #endif // wxUSE_SOUND && wxUSE_LIBSDL