1///////////////////////////////////////////////////////////////////////////// 2// Name: src/unix/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: sound_sdl.cpp 40943 2006-08-31 19:31:43Z ABX $ 8// Copyright: (c) 2004, Open Source Applications Foundation 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12// for compilers that support precompilation, includes "wx.h". 13#include "wx/wxprec.h" 14 15#if defined(__BORLANDC__) 16 #pragma hdrstop 17#endif 18 19#if wxUSE_SOUND && wxUSE_LIBSDL 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" 27 #include "wx/utils.h" 28 #include "wx/module.h" 29#endif 30 31#include "wx/thread.h" 32#include "wx/sound.h" 33 34// ---------------------------------------------------------------------------- 35// wxSoundBackendSDL, for Unix with libSDL 36// ---------------------------------------------------------------------------- 37 38class wxSoundBackendSDLNotification : public wxEvent 39{ 40public: 41 DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification) 42 wxSoundBackendSDLNotification(); 43 wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); } 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, \ 57 (wxObjectEventFunction) wxStaticCastEvent( wxSoundBackendSDLNotificationFunction, & func ), \ 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: 73 wxSoundBackendSDL() 74 : m_initialized(false), m_playing(false), m_audioOpen(false), 75 m_data(NULL), m_evtHandler(NULL) {} 76 virtual ~wxSoundBackendSDL(); 77 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; } 82 bool Play(wxSoundData *data, unsigned flags, 83 volatile wxSoundPlaybackStatus *status); 84 85 void FillAudioBuffer(Uint8 *stream, int len); 86 void FinishedPlayback(); 87 88 void Stop(); 89 bool IsPlaying() const { return m_playing; } 90 91private: 92 bool OpenAudio(); 93 void CloseAudio(); 94 95 bool m_initialized; 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 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")); 116 m_backend->FinishedPlayback(); 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{ 129 Stop(); 130 CloseAudio(); 131 delete m_evtHandler; 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{ 156 if (m_playing) 157 { 158 // finished playing the sample 159 if (m_pos == m_data->m_dataBytes) 160 { 161 m_playing = false; 162 wxSoundBackendSDLNotification event; 163 m_evtHandler->AddPendingEvent(event); 164 } 165 // still something to play 166 else 167 { 168 unsigned size = ((len + m_pos) < m_data->m_dataBytes) ? 169 len : 170 (m_data->m_dataBytes - m_pos); 171 memcpy(stream, m_data->m_data + m_pos, size); 172 m_pos += size; 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 { 181 if (m_loop) 182 { 183 m_pos = 0; 184 FillAudioBuffer(stream, len); 185 return; 186 } 187 else 188 { 189 memset(stream, m_spec.silence, len); 190 } 191 } 192} 193 194void wxSoundBackendSDL::FinishedPlayback() 195{ 196 if (!m_playing) 197 Stop(); 198} 199 200bool wxSoundBackendSDL::OpenAudio() 201{ 202 if (!m_audioOpen) 203 { 204 if (!m_evtHandler) 205 m_evtHandler = new wxSoundBackendSDLEvtHandler(this); 206 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; 212 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]; 218 SDL_AudioDriverName(driver, 256); 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 245bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags, 246 volatile wxSoundPlaybackStatus *WXUNUSED(status)) 247{ 248 Stop(); 249 250 int format; 251 if (data->m_bitsPerSample == 8) 252 format = AUDIO_U8; 253 else if (data->m_bitsPerSample == 16) 254 format = AUDIO_S16LSB; 255 else 256 return false; 257 258 bool needsOpen = true; 259 if (m_audioOpen) 260 { 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 { 269 CloseAudio(); 270 } 271 } 272 273 if (needsOpen) 274 { 275 m_spec.format = format; 276 m_spec.freq = data->m_samplingRate; 277 m_spec.channels = data->m_channels; 278 if (!OpenAudio()) 279 return false; 280 } 281 282 SDL_LockAudio(); 283 wxLogTrace(_T("sound"), _T("playing new sound")); 284 m_playing = true; 285 m_pos = 0; 286 m_loop = (flags & wxSOUND_LOOP); 287 m_data = data; 288 data->IncRef(); 289 SDL_UnlockAudio(); 290 291 SDL_PauseAudio(0); 292 293 // wait until playback finishes if called in sync mode: 294 if (!(flags & wxSOUND_ASYNC)) 295 { 296 wxLogTrace(_T("sound"), _T("waiting for sample to finish")); 297 while (m_playing && m_data == data) 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 305 wxMilliSleep(10); 306#if wxUSE_THREADS 307 if (wxThread::IsMain()) 308 wxMutexGuiEnter(); 309#endif 310 } 311 wxLogTrace(_T("sound"), _T("sample finished")); 312 } 313 314 return true; 315} 316 317void wxSoundBackendSDL::Stop() 318{ 319 SDL_LockAudio(); 320 SDL_PauseAudio(1); 321 m_playing = false; 322 if (m_data) 323 { 324 m_data->DecRef(); 325 m_data = NULL; 326 } 327 SDL_UnlockAudio(); 328} 329 330extern "C" wxSoundBackend *wxCreateSoundBackendSDL() 331{ 332 return new wxSoundBackendSDL(); 333} 334 335#endif // wxUSE_SOUND && wxUSE_LIBSDL 336