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