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