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