]> git.saurik.com Git - wxWidgets.git/blob - src/unix/sound_sdl.cpp
don't queue sounds
[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, Vaclav Slavik
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_WAVE && 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) \
60 (wxSoundBackendSDLNotificationFunction)& func, \
61 (wxObject *) NULL ),
62
63 IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler)
64 DEFINE_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION)
65
66 wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
67 {
68 SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION);
69 }
70
71 class wxSoundBackendSDLEvtHandler;
72
73 class wxSoundBackendSDL : public wxSoundBackend
74 {
75 public:
76 wxSoundBackendSDL()
77 : m_initialized(false), m_playing(false), m_audioOpen(false),
78 m_evtHandler(NULL) {}
79 virtual ~wxSoundBackendSDL();
80
81 wxString GetName() const { return _T("Simple DirectMedia Layer"); }
82 int GetPriority() const { return 9; }
83 bool IsAvailable() const;
84 bool HasNativeAsyncPlayback() const { return true; }
85 bool Play(wxSoundData *data, unsigned flags);
86
87 void FillAudioBuffer(Uint8 *stream, int len);
88 void Stop();
89 bool IsPlaying() const { return m_playing; }
90
91 private:
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(_T("sound"),
112 _T("received playback status change notification"));
113 m_backend->Stop();
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 delete m_evtHandler;
128 }
129
130 bool wxSoundBackendSDL::IsAvailable() const
131 {
132 if (m_initialized)
133 return true;
134 if (SDL_WasInit(SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
135 {
136 if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) == -1)
137 return false;
138 }
139 wxConstCast(this, wxSoundBackendSDL)->m_initialized = true;
140 wxLogTrace(_T("sound"), _T("initialized SDL audio subsystem"));
141 return true;
142 }
143
144 extern "C" void wx_sdl_audio_callback(void *userdata, Uint8 *stream, int len)
145 {
146 wxSoundBackendSDL *bk = (wxSoundBackendSDL*)userdata;
147 bk->FillAudioBuffer(stream, len);
148 }
149
150 void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
151 {
152 if (m_playing)
153 {
154 // finished playing the sample
155 if (m_pos == m_data->m_dataBytes)
156 {
157 m_playing = false;
158 wxSoundBackendSDLNotification event;
159 m_evtHandler->AddPendingEvent(event);
160 }
161 // still something to play
162 else
163 {
164 unsigned size = ((len + m_pos) < m_data->m_dataBytes) ?
165 len :
166 (m_data->m_dataBytes - m_pos);
167 memcpy(stream, m_data->m_data + m_pos, size);
168 m_pos += size;
169 len -= size;
170 stream += size;
171 }
172 }
173 // the sample doesn't play, fill the buffer with silence and wait for
174 // the main thread to shut the playback down:
175 if (len > 0)
176 {
177 if (m_loop)
178 {
179 m_pos = 0;
180 FillAudioBuffer(stream, len);
181 return;
182 }
183 else
184 {
185 memset(stream, m_spec.silence, len);
186 }
187 }
188 }
189
190 bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags)
191 {
192 int format;
193 if (data->m_bitsPerSample == 8)
194 format = AUDIO_U8;
195 else if (data->m_bitsPerSample == 16)
196 format = AUDIO_S16LSB;
197 else
198 return false;
199
200 SDL_LockAudio();
201
202 if (!m_evtHandler)
203 m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
204
205 data->IncRef();
206
207 bool needsOpen = true;
208 if (m_audioOpen)
209 {
210 wxLogTrace(_T("sound"), _T("another sound playing, will be stopped"));
211 if (format == m_spec.format &&
212 m_spec.freq == (int)data->m_samplingRate &&
213 m_spec.channels == data->m_channels)
214 {
215 needsOpen = false;
216 }
217 else
218 {
219 SDL_CloseAudio();
220 m_audioOpen = false;
221 wxLogTrace(_T("sound"), _T("closed audio"));
222 }
223 m_data->DecRef();
224 }
225
226 m_playing = true;
227 m_data = data;
228 m_pos = 0;
229 m_loop = (flags & wxSOUND_LOOP);
230 wxLogTrace(_T("sound"), _T("prepared sound for playback"));
231
232 bool status = true;
233
234 if (needsOpen)
235 {
236 m_spec.format = format;
237 m_spec.freq = data->m_samplingRate;
238 m_spec.channels = data->m_channels;
239 m_spec.silence = 0;
240 m_spec.samples = 4096;
241 m_spec.size = 0;
242 m_spec.callback = wx_sdl_audio_callback;
243 m_spec.userdata = (void*)this;
244
245 wxLogTrace(_T("sound"), _T("opening SDL audio..."));
246 status = (SDL_OpenAudio(&m_spec, NULL) >= 0);
247 if (status)
248 {
249 #if wxUSE_LOG_DEBUG
250 char driver[256];
251 SDL_AudioDriverName(driver, 256);
252 wxLogTrace(_T("sound"), _T("opened audio, driver '%s'"),
253 wxString(driver, wxConvLocal).c_str());
254 #endif
255 m_audioOpen = true;
256 SDL_PauseAudio(0);
257 }
258 else
259 {
260 wxString err(SDL_GetError(), wxConvLocal);
261 wxLogError(_("Couldn't open audio: %s"), err.c_str());
262 }
263 }
264
265 SDL_UnlockAudio();
266
267 if (!status)
268 return false;
269
270 // wait until playback finishes if called in sync mode:
271 if (!(flags & wxSOUND_ASYNC))
272 {
273 wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
274 while (m_playing)
275 {
276 #if wxUSE_THREADS
277 // give the playback thread a chance to add event to pending
278 // events queue, release GUI lock temporarily:
279 if (wxThread::IsMain())
280 wxMutexGuiLeave();
281 #endif
282 wxUsleep(10);
283 #if wxUSE_THREADS
284 if (wxThread::IsMain())
285 wxMutexGuiEnter();
286 #endif
287 }
288 wxLogTrace(_T("sound"), _T("sample finished"));
289 }
290
291 return true;
292 }
293
294 void wxSoundBackendSDL::Stop()
295 {
296 SDL_LockAudio();
297
298 if (m_audioOpen)
299 {
300 SDL_CloseAudio();
301 m_audioOpen = false;
302 wxLogTrace(_T("sound"), _T("closed audio"));
303 m_data->DecRef();
304 }
305
306 SDL_UnlockAudio();
307 }
308
309 extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
310 {
311 return new wxSoundBackendSDL();
312 }
313
314 #endif // wxUSE_WAVE && wxUSE_LIBSDL