]>
Commit | Line | Data |
---|---|---|
9be32e8f | 1 | ///////////////////////////////////////////////////////////////////////////// |
521bf4ff | 2 | // Name: src/unix/sound_sdl.cpp |
9be32e8f VS |
3 | // Purpose: wxSound backend using SDL |
4 | // Author: Vaclav Slavik | |
5 | // Modified by: | |
6 | // Created: 2004/01/31 | |
f156e20c | 7 | // Copyright: (c) 2004, Open Source Applications Foundation |
521bf4ff | 8 | // Licence: wxWindows licence |
9be32e8f VS |
9 | ///////////////////////////////////////////////////////////////////////////// |
10 | ||
11 | // for compilers that support precompilation, includes "wx.h". | |
12 | #include "wx/wxprec.h" | |
13 | ||
9be32e8f | 14 | #if defined(__BORLANDC__) |
02761f6c | 15 | #pragma hdrstop |
9be32e8f VS |
16 | #endif |
17 | ||
f156e20c | 18 | #if wxUSE_SOUND && wxUSE_LIBSDL |
9be32e8f VS |
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" | |
9be32e8f | 26 | #include "wx/utils.h" |
02761f6c | 27 | #include "wx/module.h" |
9be32e8f VS |
28 | #endif |
29 | ||
30 | #include "wx/thread.h" | |
9be32e8f | 31 | #include "wx/sound.h" |
9be32e8f VS |
32 | |
33 | // ---------------------------------------------------------------------------- | |
34 | // wxSoundBackendSDL, for Unix with libSDL | |
35 | // ---------------------------------------------------------------------------- | |
36 | ||
9be32e8f VS |
37 | class wxSoundBackendSDLNotification : public wxEvent |
38 | { | |
39 | public: | |
40 | DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification) | |
41 | wxSoundBackendSDLNotification(); | |
521bf4ff | 42 | wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); } |
9be32e8f VS |
43 | }; |
44 | ||
45 | typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction) | |
46 | (wxSoundBackendSDLNotification&); | |
47 | ||
ff3fd98a | 48 | wxDECLARE_EVENT(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, wxSoundBackendSDLNotification); |
9be32e8f VS |
49 | |
50 | #define EVT_SOUND_BACKEND_SDL_NOTIFICATON(func) \ | |
51 | DECLARE_EVENT_TABLE_ENTRY(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, \ | |
52 | -1, \ | |
53 | -1, \ | |
3c778901 | 54 | wxEVENT_HANDLER_CAST( wxSoundBackendSDLNotificationFunction, func ), \ |
d3b9f782 | 55 | NULL ), |
9be32e8f VS |
56 | |
57 | IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler) | |
9b11752c | 58 | wxDEFINE_EVENT( wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, wxSoundBackendSDLNotification ); |
9be32e8f VS |
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: | |
521bf4ff | 70 | wxSoundBackendSDL() |
64f4500a | 71 | : m_initialized(false), m_playing(false), m_audioOpen(false), |
342dc928 | 72 | m_data(NULL), m_evtHandler(NULL) {} |
9be32e8f | 73 | virtual ~wxSoundBackendSDL(); |
521bf4ff | 74 | |
9a83f860 | 75 | wxString GetName() const { return wxT("Simple DirectMedia Layer"); } |
9be32e8f VS |
76 | int GetPriority() const { return 9; } |
77 | bool IsAvailable() const; | |
78 | bool HasNativeAsyncPlayback() const { return true; } | |
83f7f12d VS |
79 | bool Play(wxSoundData *data, unsigned flags, |
80 | volatile wxSoundPlaybackStatus *status); | |
9be32e8f VS |
81 | |
82 | void FillAudioBuffer(Uint8 *stream, int len); | |
83f7f12d | 83 | void FinishedPlayback(); |
521bf4ff | 84 | |
64f4500a VS |
85 | void Stop(); |
86 | bool IsPlaying() const { return m_playing; } | |
521bf4ff | 87 | |
9be32e8f | 88 | private: |
342dc928 VS |
89 | bool OpenAudio(); |
90 | void CloseAudio(); | |
521bf4ff | 91 | |
9be32e8f | 92 | bool m_initialized; |
64f4500a VS |
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 | ||
9be32e8f VS |
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 | { | |
9a83f860 VZ |
111 | wxLogTrace(wxT("sound"), |
112 | wxT("received playback status change notification")); | |
83f7f12d | 113 | m_backend->FinishedPlayback(); |
9be32e8f VS |
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 | { | |
64f4500a | 126 | Stop(); |
342dc928 | 127 | CloseAudio(); |
64f4500a | 128 | delete m_evtHandler; |
9be32e8f VS |
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; | |
9a83f860 | 141 | wxLogTrace(wxT("sound"), wxT("initialized SDL audio subsystem")); |
9be32e8f VS |
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 | { | |
64f4500a | 153 | if (m_playing) |
9be32e8f VS |
154 | { |
155 | // finished playing the sample | |
64f4500a | 156 | if (m_pos == m_data->m_dataBytes) |
9be32e8f | 157 | { |
9be32e8f VS |
158 | m_playing = false; |
159 | wxSoundBackendSDLNotification event; | |
160 | m_evtHandler->AddPendingEvent(event); | |
161 | } | |
162 | // still something to play | |
163 | else | |
164 | { | |
64f4500a | 165 | unsigned size = ((len + m_pos) < m_data->m_dataBytes) ? |
9be32e8f | 166 | len : |
64f4500a VS |
167 | (m_data->m_dataBytes - m_pos); |
168 | memcpy(stream, m_data->m_data + m_pos, size); | |
169 | m_pos += size; | |
9be32e8f VS |
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 | { | |
64f4500a | 178 | if (m_loop) |
9be32e8f | 179 | { |
64f4500a | 180 | m_pos = 0; |
9be32e8f VS |
181 | FillAudioBuffer(stream, len); |
182 | return; | |
183 | } | |
184 | else | |
185 | { | |
64f4500a | 186 | memset(stream, m_spec.silence, len); |
9be32e8f VS |
187 | } |
188 | } | |
189 | } | |
190 | ||
83f7f12d VS |
191 | void wxSoundBackendSDL::FinishedPlayback() |
192 | { | |
193 | if (!m_playing) | |
194 | Stop(); | |
195 | } | |
196 | ||
342dc928 VS |
197 | bool wxSoundBackendSDL::OpenAudio() |
198 | { | |
199 | if (!m_audioOpen) | |
200 | { | |
201 | if (!m_evtHandler) | |
202 | m_evtHandler = new wxSoundBackendSDLEvtHandler(this); | |
521bf4ff | 203 | |
342dc928 VS |
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; | |
521bf4ff | 209 | |
9a83f860 | 210 | wxLogTrace(wxT("sound"), wxT("opening SDL audio...")); |
342dc928 VS |
211 | if (SDL_OpenAudio(&m_spec, NULL) >= 0) |
212 | { | |
213 | #if wxUSE_LOG_DEBUG | |
214 | char driver[256]; | |
cb50bdc0 | 215 | #if SDL_MAJOR_VERSION == 1 |
521bf4ff | 216 | SDL_AudioDriverName(driver, 256); |
cb50bdc0 VZ |
217 | #elif SDL_MAJOR_VERSION > 1 |
218 | strncpy(driver, SDL_GetCurrentAudioDriver(), 256); | |
219 | #endif | |
9a83f860 | 220 | wxLogTrace(wxT("sound"), wxT("opened audio, driver '%s'"), |
342dc928 VS |
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(); | |
9a83f860 | 241 | wxLogTrace(wxT("sound"), wxT("closed audio")); |
342dc928 VS |
242 | m_audioOpen = false; |
243 | } | |
244 | } | |
245 | ||
83f7f12d VS |
246 | bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags, |
247 | volatile wxSoundPlaybackStatus *WXUNUSED(status)) | |
9be32e8f | 248 | { |
98840d95 | 249 | Stop(); |
521bf4ff | 250 | |
64f4500a | 251 | int format; |
9be32e8f | 252 | if (data->m_bitsPerSample == 8) |
64f4500a | 253 | format = AUDIO_U8; |
9be32e8f | 254 | else if (data->m_bitsPerSample == 16) |
64f4500a | 255 | format = AUDIO_S16LSB; |
9be32e8f VS |
256 | else |
257 | return false; | |
64f4500a | 258 | |
64f4500a VS |
259 | bool needsOpen = true; |
260 | if (m_audioOpen) | |
261 | { | |
64f4500a VS |
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 | { | |
342dc928 | 270 | CloseAudio(); |
64f4500a | 271 | } |
64f4500a | 272 | } |
521bf4ff | 273 | |
64f4500a VS |
274 | if (needsOpen) |
275 | { | |
276 | m_spec.format = format; | |
277 | m_spec.freq = data->m_samplingRate; | |
278 | m_spec.channels = data->m_channels; | |
342dc928 | 279 | if (!OpenAudio()) |
342dc928 | 280 | return false; |
64f4500a | 281 | } |
521bf4ff | 282 | |
98840d95 | 283 | SDL_LockAudio(); |
9a83f860 | 284 | wxLogTrace(wxT("sound"), wxT("playing new sound")); |
342dc928 VS |
285 | m_playing = true; |
286 | m_pos = 0; | |
287 | m_loop = (flags & wxSOUND_LOOP); | |
288 | m_data = data; | |
289 | data->IncRef(); | |
98840d95 | 290 | SDL_UnlockAudio(); |
9be32e8f | 291 | |
342dc928 | 292 | SDL_PauseAudio(0); |
9be32e8f | 293 | |
64f4500a | 294 | // wait until playback finishes if called in sync mode: |
9be32e8f VS |
295 | if (!(flags & wxSOUND_ASYNC)) |
296 | { | |
9a83f860 | 297 | wxLogTrace(wxT("sound"), wxT("waiting for sample to finish")); |
83f7f12d | 298 | while (m_playing && m_data == data) |
9be32e8f VS |
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 | |
7648370f | 306 | wxMilliSleep(10); |
9be32e8f VS |
307 | #if wxUSE_THREADS |
308 | if (wxThread::IsMain()) | |
309 | wxMutexGuiEnter(); | |
310 | #endif | |
311 | } | |
9a83f860 | 312 | wxLogTrace(wxT("sound"), wxT("sample finished")); |
9be32e8f | 313 | } |
64f4500a | 314 | |
9be32e8f VS |
315 | return true; |
316 | } | |
9be32e8f | 317 | |
64f4500a VS |
318 | void wxSoundBackendSDL::Stop() |
319 | { | |
9be32e8f | 320 | SDL_LockAudio(); |
342dc928 | 321 | SDL_PauseAudio(1); |
98840d95 | 322 | m_playing = false; |
342dc928 | 323 | if (m_data) |
9be32e8f | 324 | { |
64f4500a | 325 | m_data->DecRef(); |
342dc928 | 326 | m_data = NULL; |
9be32e8f | 327 | } |
64f4500a | 328 | SDL_UnlockAudio(); |
9be32e8f VS |
329 | } |
330 | ||
331 | extern "C" wxSoundBackend *wxCreateSoundBackendSDL() | |
332 | { | |
333 | return new wxSoundBackendSDL(); | |
334 | } | |
335 | ||
f156e20c | 336 | #endif // wxUSE_SOUND && wxUSE_LIBSDL |