]> git.saurik.com Git - wxWidgets.git/blame - src/unix/sound_sdl.cpp
moved assertdlg_gtk.[ch] to GTK_LOWLEVEL_SRC/HDR to fix wxUniv/GTK build
[wxWidgets.git] / src / unix / sound_sdl.cpp
CommitLineData
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
7// RCS-ID: $Id$
f156e20c 8// Copyright: (c) 2004, Open Source Applications Foundation
521bf4ff 9// Licence: wxWindows licence
9be32e8f
VS
10/////////////////////////////////////////////////////////////////////////////
11
12// for compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
9be32e8f 15#if defined(__BORLANDC__)
02761f6c 16 #pragma hdrstop
9be32e8f
VS
17#endif
18
f156e20c 19#if wxUSE_SOUND && wxUSE_LIBSDL
9be32e8f
VS
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"
9be32e8f 27 #include "wx/utils.h"
02761f6c 28 #include "wx/module.h"
9be32e8f
VS
29#endif
30
31#include "wx/thread.h"
9be32e8f 32#include "wx/sound.h"
9be32e8f
VS
33
34// ----------------------------------------------------------------------------
35// wxSoundBackendSDL, for Unix with libSDL
36// ----------------------------------------------------------------------------
37
9be32e8f
VS
38class wxSoundBackendSDLNotification : public wxEvent
39{
40public:
41 DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification)
42 wxSoundBackendSDLNotification();
521bf4ff 43 wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); }
9be32e8f
VS
44};
45
46typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction)
47 (wxSoundBackendSDLNotification&);
48
49BEGIN_DECLARE_EVENT_TYPES()
50 DECLARE_LOCAL_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, -1)
51END_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, \
3a818b15 57 (wxObjectEventFunction) wxStaticCastEvent( wxSoundBackendSDLNotificationFunction, & func ), \
9be32e8f
VS
58 (wxObject *) NULL ),
59
60IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler)
61DEFINE_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION)
62
63wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
64{
65 SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION);
66}
67
68class wxSoundBackendSDLEvtHandler;
69
70class wxSoundBackendSDL : public wxSoundBackend
71{
72public:
521bf4ff 73 wxSoundBackendSDL()
64f4500a 74 : m_initialized(false), m_playing(false), m_audioOpen(false),
342dc928 75 m_data(NULL), m_evtHandler(NULL) {}
9be32e8f 76 virtual ~wxSoundBackendSDL();
521bf4ff 77
9be32e8f
VS
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; }
83f7f12d
VS
82 bool Play(wxSoundData *data, unsigned flags,
83 volatile wxSoundPlaybackStatus *status);
9be32e8f
VS
84
85 void FillAudioBuffer(Uint8 *stream, int len);
83f7f12d 86 void FinishedPlayback();
521bf4ff 87
64f4500a
VS
88 void Stop();
89 bool IsPlaying() const { return m_playing; }
521bf4ff 90
9be32e8f 91private:
342dc928
VS
92 bool OpenAudio();
93 void CloseAudio();
521bf4ff 94
9be32e8f 95 bool m_initialized;
64f4500a
VS
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
9be32e8f
VS
103 wxSoundBackendSDLEvtHandler *m_evtHandler;
104};
105
106class wxSoundBackendSDLEvtHandler : public wxEvtHandler
107{
108public:
109 wxSoundBackendSDLEvtHandler(wxSoundBackendSDL *bk) : m_backend(bk) {}
110
111private:
112 void OnNotify(wxSoundBackendSDLNotification& WXUNUSED(event))
113 {
114 wxLogTrace(_T("sound"),
115 _T("received playback status change notification"));
83f7f12d 116 m_backend->FinishedPlayback();
9be32e8f
VS
117 }
118 wxSoundBackendSDL *m_backend;
119
120 DECLARE_EVENT_TABLE()
121};
122
123BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler, wxEvtHandler)
124 EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify)
125END_EVENT_TABLE()
126
127wxSoundBackendSDL::~wxSoundBackendSDL()
128{
64f4500a 129 Stop();
342dc928 130 CloseAudio();
64f4500a 131 delete m_evtHandler;
9be32e8f
VS
132}
133
134bool 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
148extern "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
154void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
155{
64f4500a 156 if (m_playing)
9be32e8f
VS
157 {
158 // finished playing the sample
64f4500a 159 if (m_pos == m_data->m_dataBytes)
9be32e8f 160 {
9be32e8f
VS
161 m_playing = false;
162 wxSoundBackendSDLNotification event;
163 m_evtHandler->AddPendingEvent(event);
164 }
165 // still something to play
166 else
167 {
64f4500a 168 unsigned size = ((len + m_pos) < m_data->m_dataBytes) ?
9be32e8f 169 len :
64f4500a
VS
170 (m_data->m_dataBytes - m_pos);
171 memcpy(stream, m_data->m_data + m_pos, size);
172 m_pos += size;
9be32e8f
VS
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 {
64f4500a 181 if (m_loop)
9be32e8f 182 {
64f4500a 183 m_pos = 0;
9be32e8f
VS
184 FillAudioBuffer(stream, len);
185 return;
186 }
187 else
188 {
64f4500a 189 memset(stream, m_spec.silence, len);
9be32e8f
VS
190 }
191 }
192}
193
83f7f12d
VS
194void wxSoundBackendSDL::FinishedPlayback()
195{
196 if (!m_playing)
197 Stop();
198}
199
342dc928
VS
200bool wxSoundBackendSDL::OpenAudio()
201{
202 if (!m_audioOpen)
203 {
204 if (!m_evtHandler)
205 m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
521bf4ff 206
342dc928
VS
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;
521bf4ff 212
342dc928
VS
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];
521bf4ff 218 SDL_AudioDriverName(driver, 256);
342dc928
VS
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
235void 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
83f7f12d
VS
245bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags,
246 volatile wxSoundPlaybackStatus *WXUNUSED(status))
9be32e8f 247{
98840d95 248 Stop();
521bf4ff 249
64f4500a 250 int format;
9be32e8f 251 if (data->m_bitsPerSample == 8)
64f4500a 252 format = AUDIO_U8;
9be32e8f 253 else if (data->m_bitsPerSample == 16)
64f4500a 254 format = AUDIO_S16LSB;
9be32e8f
VS
255 else
256 return false;
64f4500a 257
64f4500a
VS
258 bool needsOpen = true;
259 if (m_audioOpen)
260 {
64f4500a
VS
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 {
342dc928 269 CloseAudio();
64f4500a 270 }
64f4500a 271 }
521bf4ff 272
64f4500a
VS
273 if (needsOpen)
274 {
275 m_spec.format = format;
276 m_spec.freq = data->m_samplingRate;
277 m_spec.channels = data->m_channels;
342dc928 278 if (!OpenAudio())
342dc928 279 return false;
64f4500a 280 }
521bf4ff 281
98840d95 282 SDL_LockAudio();
83f7f12d 283 wxLogTrace(_T("sound"), _T("playing new sound"));
342dc928
VS
284 m_playing = true;
285 m_pos = 0;
286 m_loop = (flags & wxSOUND_LOOP);
287 m_data = data;
288 data->IncRef();
98840d95 289 SDL_UnlockAudio();
9be32e8f 290
342dc928 291 SDL_PauseAudio(0);
9be32e8f 292
64f4500a 293 // wait until playback finishes if called in sync mode:
9be32e8f
VS
294 if (!(flags & wxSOUND_ASYNC))
295 {
296 wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
83f7f12d 297 while (m_playing && m_data == data)
9be32e8f
VS
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
7648370f 305 wxMilliSleep(10);
9be32e8f
VS
306#if wxUSE_THREADS
307 if (wxThread::IsMain())
308 wxMutexGuiEnter();
309#endif
310 }
311 wxLogTrace(_T("sound"), _T("sample finished"));
312 }
64f4500a 313
9be32e8f
VS
314 return true;
315}
9be32e8f 316
64f4500a
VS
317void wxSoundBackendSDL::Stop()
318{
9be32e8f 319 SDL_LockAudio();
342dc928 320 SDL_PauseAudio(1);
98840d95 321 m_playing = false;
342dc928 322 if (m_data)
9be32e8f 323 {
64f4500a 324 m_data->DecRef();
342dc928 325 m_data = NULL;
9be32e8f 326 }
64f4500a 327 SDL_UnlockAudio();
9be32e8f
VS
328}
329
330extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
331{
332 return new wxSoundBackendSDL();
333}
334
f156e20c 335#endif // wxUSE_SOUND && wxUSE_LIBSDL