1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxSound backend using SDL
4 // Author: Vaclav Slavik
8 // Copyright: (c) 2004, Open Source Applications Foundation
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // for compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
17 #if defined(__BORLANDC__)
21 #if wxUSE_SOUND && wxUSE_LIBSDL
32 #include "wx/thread.h"
33 #include "wx/module.h"
36 // ----------------------------------------------------------------------------
37 // wxSoundBackendSDL, for Unix with libSDL
38 // ----------------------------------------------------------------------------
40 class wxSoundBackendSDLNotification : public wxEvent
43 DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification)
44 wxSoundBackendSDLNotification();
45 wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); }
48 typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction)
49 (wxSoundBackendSDLNotification&);
51 BEGIN_DECLARE_EVENT_TYPES()
52 DECLARE_LOCAL_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, -1)
53 END_DECLARE_EVENT_TYPES()
55 #define EVT_SOUND_BACKEND_SDL_NOTIFICATON(func) \
56 DECLARE_EVENT_TABLE_ENTRY(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, \
59 (wxObjectEventFunction) wxStaticCastEvent( wxSoundBackendSDLNotificationFunction, & func ), \
62 IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler)
63 DEFINE_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION)
65 wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
67 SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION);
70 class wxSoundBackendSDLEvtHandler;
72 class wxSoundBackendSDL : public wxSoundBackend
76 : m_initialized(false), m_playing(false), m_audioOpen(false),
77 m_data(NULL), m_evtHandler(NULL) {}
78 virtual ~wxSoundBackendSDL();
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);
87 void FillAudioBuffer(Uint8 *stream, int len);
88 void FinishedPlayback();
91 bool IsPlaying() const { return m_playing; }
98 bool m_playing, m_audioOpen;
99 // playback information:
102 SDL_AudioSpec m_spec;
105 wxSoundBackendSDLEvtHandler *m_evtHandler;
108 class wxSoundBackendSDLEvtHandler : public wxEvtHandler
111 wxSoundBackendSDLEvtHandler(wxSoundBackendSDL *bk) : m_backend(bk) {}
114 void OnNotify(wxSoundBackendSDLNotification& WXUNUSED(event))
116 wxLogTrace(_T("sound"),
117 _T("received playback status change notification"));
118 m_backend->FinishedPlayback();
120 wxSoundBackendSDL *m_backend;
122 DECLARE_EVENT_TABLE()
125 BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler, wxEvtHandler)
126 EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify)
129 wxSoundBackendSDL::~wxSoundBackendSDL()
136 bool wxSoundBackendSDL::IsAvailable() const
140 if (SDL_WasInit(SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
142 if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) == -1)
145 wxConstCast(this, wxSoundBackendSDL)->m_initialized = true;
146 wxLogTrace(_T("sound"), _T("initialized SDL audio subsystem"));
150 extern "C" void wx_sdl_audio_callback(void *userdata, Uint8 *stream, int len)
152 wxSoundBackendSDL *bk = (wxSoundBackendSDL*)userdata;
153 bk->FillAudioBuffer(stream, len);
156 void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
160 // finished playing the sample
161 if (m_pos == m_data->m_dataBytes)
164 wxSoundBackendSDLNotification event;
165 m_evtHandler->AddPendingEvent(event);
167 // still something to play
170 unsigned size = ((len + m_pos) < m_data->m_dataBytes) ?
172 (m_data->m_dataBytes - m_pos);
173 memcpy(stream, m_data->m_data + m_pos, size);
179 // the sample doesn't play, fill the buffer with silence and wait for
180 // the main thread to shut the playback down:
186 FillAudioBuffer(stream, len);
191 memset(stream, m_spec.silence, len);
196 void wxSoundBackendSDL::FinishedPlayback()
202 bool wxSoundBackendSDL::OpenAudio()
207 m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
210 m_spec.samples = 4096;
212 m_spec.callback = wx_sdl_audio_callback;
213 m_spec.userdata = (void*)this;
215 wxLogTrace(_T("sound"), _T("opening SDL audio..."));
216 if (SDL_OpenAudio(&m_spec, NULL) >= 0)
220 SDL_AudioDriverName(driver, 256);
221 wxLogTrace(_T("sound"), _T("opened audio, driver '%s'"),
222 wxString(driver, wxConvLocal).c_str());
229 wxString err(SDL_GetError(), wxConvLocal);
230 wxLogError(_("Couldn't open audio: %s"), err.c_str());
237 void wxSoundBackendSDL::CloseAudio()
242 wxLogTrace(_T("sound"), _T("closed audio"));
247 bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags,
248 volatile wxSoundPlaybackStatus *WXUNUSED(status))
253 if (data->m_bitsPerSample == 8)
255 else if (data->m_bitsPerSample == 16)
256 format = AUDIO_S16LSB;
260 bool needsOpen = true;
263 if (format == m_spec.format &&
264 m_spec.freq == (int)data->m_samplingRate &&
265 m_spec.channels == data->m_channels)
277 m_spec.format = format;
278 m_spec.freq = data->m_samplingRate;
279 m_spec.channels = data->m_channels;
285 wxLogTrace(_T("sound"), _T("playing new sound"));
288 m_loop = (flags & wxSOUND_LOOP);
295 // wait until playback finishes if called in sync mode:
296 if (!(flags & wxSOUND_ASYNC))
298 wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
299 while (m_playing && m_data == data)
302 // give the playback thread a chance to add event to pending
303 // events queue, release GUI lock temporarily:
304 if (wxThread::IsMain())
309 if (wxThread::IsMain())
313 wxLogTrace(_T("sound"), _T("sample finished"));
319 void wxSoundBackendSDL::Stop()
332 extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
334 return new wxSoundBackendSDL();
337 #endif // wxUSE_SOUND && wxUSE_LIBSDL