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