]> git.saurik.com Git - wxWidgets.git/blame - src/unix/sound_sdl.cpp
bug didn't happen
[wxWidgets.git] / src / unix / sound_sdl.cpp
CommitLineData
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
65571936 9// Licence: wxWindows licence
9be32e8f
VS
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
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, \
3a818b15 59 (wxObjectEventFunction) wxStaticCastEvent( wxSoundBackendSDLNotificationFunction, & func ), \
9be32e8f
VS
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()
64f4500a 76 : m_initialized(false), m_playing(false), m_audioOpen(false),
342dc928 77 m_data(NULL), m_evtHandler(NULL) {}
9be32e8f
VS
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; }
83f7f12d
VS
84 bool Play(wxSoundData *data, unsigned flags,
85 volatile wxSoundPlaybackStatus *status);
9be32e8f
VS
86
87 void FillAudioBuffer(Uint8 *stream, int len);
83f7f12d
VS
88 void FinishedPlayback();
89
64f4500a
VS
90 void Stop();
91 bool IsPlaying() const { return m_playing; }
9be32e8f
VS
92
93private:
342dc928
VS
94 bool OpenAudio();
95 void CloseAudio();
96
9be32e8f 97 bool m_initialized;
64f4500a
VS
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
9be32e8f
VS
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"));
83f7f12d 118 m_backend->FinishedPlayback();
9be32e8f
VS
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{
64f4500a 131 Stop();
342dc928 132 CloseAudio();
64f4500a 133 delete m_evtHandler;
9be32e8f
VS
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{
64f4500a 158 if (m_playing)
9be32e8f
VS
159 {
160 // finished playing the sample
64f4500a 161 if (m_pos == m_data->m_dataBytes)
9be32e8f 162 {
9be32e8f
VS
163 m_playing = false;
164 wxSoundBackendSDLNotification event;
165 m_evtHandler->AddPendingEvent(event);
166 }
167 // still something to play
168 else
169 {
64f4500a 170 unsigned size = ((len + m_pos) < m_data->m_dataBytes) ?
9be32e8f 171 len :
64f4500a
VS
172 (m_data->m_dataBytes - m_pos);
173 memcpy(stream, m_data->m_data + m_pos, size);
174 m_pos += size;
9be32e8f
VS
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 {
64f4500a 183 if (m_loop)
9be32e8f 184 {
64f4500a 185 m_pos = 0;
9be32e8f
VS
186 FillAudioBuffer(stream, len);
187 return;
188 }
189 else
190 {
64f4500a 191 memset(stream, m_spec.silence, len);
9be32e8f
VS
192 }
193 }
194}
195
83f7f12d
VS
196void wxSoundBackendSDL::FinishedPlayback()
197{
198 if (!m_playing)
199 Stop();
200}
201
342dc928
VS
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
83f7f12d
VS
247bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags,
248 volatile wxSoundPlaybackStatus *WXUNUSED(status))
9be32e8f 249{
98840d95
VS
250 Stop();
251
64f4500a 252 int format;
9be32e8f 253 if (data->m_bitsPerSample == 8)
64f4500a 254 format = AUDIO_U8;
9be32e8f 255 else if (data->m_bitsPerSample == 16)
64f4500a 256 format = AUDIO_S16LSB;
9be32e8f
VS
257 else
258 return false;
64f4500a 259
64f4500a
VS
260 bool needsOpen = true;
261 if (m_audioOpen)
262 {
64f4500a
VS
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 {
342dc928 271 CloseAudio();
64f4500a 272 }
64f4500a 273 }
342dc928 274
64f4500a
VS
275 if (needsOpen)
276 {
277 m_spec.format = format;
278 m_spec.freq = data->m_samplingRate;
279 m_spec.channels = data->m_channels;
342dc928 280 if (!OpenAudio())
342dc928 281 return false;
64f4500a 282 }
98840d95
VS
283
284 SDL_LockAudio();
83f7f12d 285 wxLogTrace(_T("sound"), _T("playing new sound"));
342dc928
VS
286 m_playing = true;
287 m_pos = 0;
288 m_loop = (flags & wxSOUND_LOOP);
289 m_data = data;
290 data->IncRef();
98840d95 291 SDL_UnlockAudio();
9be32e8f 292
342dc928 293 SDL_PauseAudio(0);
9be32e8f 294
64f4500a 295 // wait until playback finishes if called in sync mode:
9be32e8f
VS
296 if (!(flags & wxSOUND_ASYNC))
297 {
298 wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
83f7f12d 299 while (m_playing && m_data == data)
9be32e8f
VS
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
7648370f 307 wxMilliSleep(10);
9be32e8f
VS
308#if wxUSE_THREADS
309 if (wxThread::IsMain())
310 wxMutexGuiEnter();
311#endif
312 }
313 wxLogTrace(_T("sound"), _T("sample finished"));
314 }
64f4500a 315
9be32e8f
VS
316 return true;
317}
9be32e8f 318
64f4500a
VS
319void wxSoundBackendSDL::Stop()
320{
9be32e8f 321 SDL_LockAudio();
342dc928 322 SDL_PauseAudio(1);
98840d95 323 m_playing = false;
342dc928 324 if (m_data)
9be32e8f 325 {
64f4500a 326 m_data->DecRef();
342dc928 327 m_data = NULL;
9be32e8f 328 }
64f4500a 329 SDL_UnlockAudio();
9be32e8f
VS
330}
331
332extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
333{
334 return new wxSoundBackendSDL();
335}
336
f156e20c 337#endif // wxUSE_SOUND && wxUSE_LIBSDL