]>
Commit | Line | Data |
---|---|---|
9be32e8f VS |
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$ | |
f156e20c | 8 | // Copyright: (c) 2004, Open Source Applications Foundation |
9be32e8f VS |
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 | ||
f156e20c | 21 | #if wxUSE_SOUND && wxUSE_LIBSDL |
9be32e8f VS |
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" | |
9be32e8f VS |
29 | #include "wx/utils.h" |
30 | #endif | |
31 | ||
32 | #include "wx/thread.h" | |
33 | #include "wx/module.h" | |
34 | #include "wx/sound.h" | |
9be32e8f VS |
35 | |
36 | // ---------------------------------------------------------------------------- | |
37 | // wxSoundBackendSDL, for Unix with libSDL | |
38 | // ---------------------------------------------------------------------------- | |
39 | ||
9be32e8f VS |
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() | |
64f4500a | 77 | : m_initialized(false), m_playing(false), m_audioOpen(false), |
342dc928 | 78 | m_data(NULL), m_evtHandler(NULL) {} |
9be32e8f VS |
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; } | |
83f7f12d VS |
85 | bool Play(wxSoundData *data, unsigned flags, |
86 | volatile wxSoundPlaybackStatus *status); | |
9be32e8f VS |
87 | |
88 | void FillAudioBuffer(Uint8 *stream, int len); | |
83f7f12d VS |
89 | void FinishedPlayback(); |
90 | ||
64f4500a VS |
91 | void Stop(); |
92 | bool IsPlaying() const { return m_playing; } | |
9be32e8f VS |
93 | |
94 | private: | |
342dc928 VS |
95 | bool OpenAudio(); |
96 | void CloseAudio(); | |
97 | ||
9be32e8f | 98 | bool m_initialized; |
64f4500a VS |
99 | bool m_playing, m_audioOpen; |
100 | // playback information: | |
101 | wxSoundData *m_data; | |
102 | unsigned m_pos; | |
103 | SDL_AudioSpec m_spec; | |
104 | bool m_loop; | |
105 | ||
9be32e8f VS |
106 | wxSoundBackendSDLEvtHandler *m_evtHandler; |
107 | }; | |
108 | ||
109 | class wxSoundBackendSDLEvtHandler : public wxEvtHandler | |
110 | { | |
111 | public: | |
112 | wxSoundBackendSDLEvtHandler(wxSoundBackendSDL *bk) : m_backend(bk) {} | |
113 | ||
114 | private: | |
115 | void OnNotify(wxSoundBackendSDLNotification& WXUNUSED(event)) | |
116 | { | |
117 | wxLogTrace(_T("sound"), | |
118 | _T("received playback status change notification")); | |
83f7f12d | 119 | m_backend->FinishedPlayback(); |
9be32e8f VS |
120 | } |
121 | wxSoundBackendSDL *m_backend; | |
122 | ||
123 | DECLARE_EVENT_TABLE() | |
124 | }; | |
125 | ||
126 | BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler, wxEvtHandler) | |
127 | EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify) | |
128 | END_EVENT_TABLE() | |
129 | ||
130 | wxSoundBackendSDL::~wxSoundBackendSDL() | |
131 | { | |
64f4500a | 132 | Stop(); |
342dc928 | 133 | CloseAudio(); |
64f4500a | 134 | delete m_evtHandler; |
9be32e8f VS |
135 | } |
136 | ||
137 | bool wxSoundBackendSDL::IsAvailable() const | |
138 | { | |
139 | if (m_initialized) | |
140 | return true; | |
141 | if (SDL_WasInit(SDL_INIT_AUDIO) != SDL_INIT_AUDIO) | |
142 | { | |
143 | if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) == -1) | |
144 | return false; | |
145 | } | |
146 | wxConstCast(this, wxSoundBackendSDL)->m_initialized = true; | |
147 | wxLogTrace(_T("sound"), _T("initialized SDL audio subsystem")); | |
148 | return true; | |
149 | } | |
150 | ||
151 | extern "C" void wx_sdl_audio_callback(void *userdata, Uint8 *stream, int len) | |
152 | { | |
153 | wxSoundBackendSDL *bk = (wxSoundBackendSDL*)userdata; | |
154 | bk->FillAudioBuffer(stream, len); | |
155 | } | |
156 | ||
157 | void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len) | |
158 | { | |
64f4500a | 159 | if (m_playing) |
9be32e8f VS |
160 | { |
161 | // finished playing the sample | |
64f4500a | 162 | if (m_pos == m_data->m_dataBytes) |
9be32e8f | 163 | { |
9be32e8f VS |
164 | m_playing = false; |
165 | wxSoundBackendSDLNotification event; | |
166 | m_evtHandler->AddPendingEvent(event); | |
167 | } | |
168 | // still something to play | |
169 | else | |
170 | { | |
64f4500a | 171 | unsigned size = ((len + m_pos) < m_data->m_dataBytes) ? |
9be32e8f | 172 | len : |
64f4500a VS |
173 | (m_data->m_dataBytes - m_pos); |
174 | memcpy(stream, m_data->m_data + m_pos, size); | |
175 | m_pos += size; | |
9be32e8f VS |
176 | len -= size; |
177 | stream += size; | |
178 | } | |
179 | } | |
180 | // the sample doesn't play, fill the buffer with silence and wait for | |
181 | // the main thread to shut the playback down: | |
182 | if (len > 0) | |
183 | { | |
64f4500a | 184 | if (m_loop) |
9be32e8f | 185 | { |
64f4500a | 186 | m_pos = 0; |
9be32e8f VS |
187 | FillAudioBuffer(stream, len); |
188 | return; | |
189 | } | |
190 | else | |
191 | { | |
64f4500a | 192 | memset(stream, m_spec.silence, len); |
9be32e8f VS |
193 | } |
194 | } | |
195 | } | |
196 | ||
83f7f12d VS |
197 | void wxSoundBackendSDL::FinishedPlayback() |
198 | { | |
199 | if (!m_playing) | |
200 | Stop(); | |
201 | } | |
202 | ||
342dc928 VS |
203 | bool wxSoundBackendSDL::OpenAudio() |
204 | { | |
205 | if (!m_audioOpen) | |
206 | { | |
207 | if (!m_evtHandler) | |
208 | m_evtHandler = new wxSoundBackendSDLEvtHandler(this); | |
209 | ||
210 | m_spec.silence = 0; | |
211 | m_spec.samples = 4096; | |
212 | m_spec.size = 0; | |
213 | m_spec.callback = wx_sdl_audio_callback; | |
214 | m_spec.userdata = (void*)this; | |
215 | ||
216 | wxLogTrace(_T("sound"), _T("opening SDL audio...")); | |
217 | if (SDL_OpenAudio(&m_spec, NULL) >= 0) | |
218 | { | |
219 | #if wxUSE_LOG_DEBUG | |
220 | char driver[256]; | |
221 | SDL_AudioDriverName(driver, 256); | |
222 | wxLogTrace(_T("sound"), _T("opened audio, driver '%s'"), | |
223 | wxString(driver, wxConvLocal).c_str()); | |
224 | #endif | |
225 | m_audioOpen = true; | |
226 | return true; | |
227 | } | |
228 | else | |
229 | { | |
230 | wxString err(SDL_GetError(), wxConvLocal); | |
231 | wxLogError(_("Couldn't open audio: %s"), err.c_str()); | |
232 | return false; | |
233 | } | |
234 | } | |
235 | return true; | |
236 | } | |
237 | ||
238 | void wxSoundBackendSDL::CloseAudio() | |
239 | { | |
240 | if (m_audioOpen) | |
241 | { | |
242 | SDL_CloseAudio(); | |
243 | wxLogTrace(_T("sound"), _T("closed audio")); | |
244 | m_audioOpen = false; | |
245 | } | |
246 | } | |
247 | ||
83f7f12d VS |
248 | bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags, |
249 | volatile wxSoundPlaybackStatus *WXUNUSED(status)) | |
9be32e8f | 250 | { |
98840d95 VS |
251 | Stop(); |
252 | ||
64f4500a | 253 | int format; |
9be32e8f | 254 | if (data->m_bitsPerSample == 8) |
64f4500a | 255 | format = AUDIO_U8; |
9be32e8f | 256 | else if (data->m_bitsPerSample == 16) |
64f4500a | 257 | format = AUDIO_S16LSB; |
9be32e8f VS |
258 | else |
259 | return false; | |
64f4500a | 260 | |
64f4500a VS |
261 | bool needsOpen = true; |
262 | if (m_audioOpen) | |
263 | { | |
64f4500a VS |
264 | if (format == m_spec.format && |
265 | m_spec.freq == (int)data->m_samplingRate && | |
266 | m_spec.channels == data->m_channels) | |
267 | { | |
268 | needsOpen = false; | |
269 | } | |
270 | else | |
271 | { | |
342dc928 | 272 | CloseAudio(); |
64f4500a | 273 | } |
64f4500a | 274 | } |
342dc928 | 275 | |
64f4500a VS |
276 | if (needsOpen) |
277 | { | |
278 | m_spec.format = format; | |
279 | m_spec.freq = data->m_samplingRate; | |
280 | m_spec.channels = data->m_channels; | |
342dc928 | 281 | if (!OpenAudio()) |
342dc928 | 282 | return false; |
64f4500a | 283 | } |
98840d95 VS |
284 | |
285 | SDL_LockAudio(); | |
83f7f12d | 286 | wxLogTrace(_T("sound"), _T("playing new sound")); |
342dc928 VS |
287 | m_playing = true; |
288 | m_pos = 0; | |
289 | m_loop = (flags & wxSOUND_LOOP); | |
290 | m_data = data; | |
291 | data->IncRef(); | |
98840d95 | 292 | SDL_UnlockAudio(); |
9be32e8f | 293 | |
342dc928 | 294 | SDL_PauseAudio(0); |
9be32e8f | 295 | |
64f4500a | 296 | // wait until playback finishes if called in sync mode: |
9be32e8f VS |
297 | if (!(flags & wxSOUND_ASYNC)) |
298 | { | |
299 | wxLogTrace(_T("sound"), _T("waiting for sample to finish")); | |
83f7f12d | 300 | while (m_playing && m_data == data) |
9be32e8f VS |
301 | { |
302 | #if wxUSE_THREADS | |
303 | // give the playback thread a chance to add event to pending | |
304 | // events queue, release GUI lock temporarily: | |
305 | if (wxThread::IsMain()) | |
306 | wxMutexGuiLeave(); | |
307 | #endif | |
308 | wxUsleep(10); | |
309 | #if wxUSE_THREADS | |
310 | if (wxThread::IsMain()) | |
311 | wxMutexGuiEnter(); | |
312 | #endif | |
313 | } | |
314 | wxLogTrace(_T("sound"), _T("sample finished")); | |
315 | } | |
64f4500a | 316 | |
9be32e8f VS |
317 | return true; |
318 | } | |
9be32e8f | 319 | |
64f4500a VS |
320 | void wxSoundBackendSDL::Stop() |
321 | { | |
9be32e8f | 322 | SDL_LockAudio(); |
342dc928 | 323 | SDL_PauseAudio(1); |
98840d95 | 324 | m_playing = false; |
342dc928 | 325 | if (m_data) |
9be32e8f | 326 | { |
64f4500a | 327 | m_data->DecRef(); |
342dc928 | 328 | m_data = NULL; |
9be32e8f | 329 | } |
64f4500a | 330 | SDL_UnlockAudio(); |
9be32e8f VS |
331 | } |
332 | ||
333 | extern "C" wxSoundBackend *wxCreateSoundBackendSDL() | |
334 | { | |
335 | return new wxSoundBackendSDL(); | |
336 | } | |
337 | ||
f156e20c | 338 | #endif // wxUSE_SOUND && wxUSE_LIBSDL |