1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxSound backend using SDL
4 // Author: Vaclav Slavik
8 // Copyright: (c) 2004, Vaclav Slavik
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_WAVE && 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) \
60 (wxSoundBackendSDLNotificationFunction)& func, \
63 IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification
, wxEvtHandler
)
64 DEFINE_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION
)
66 wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
68 SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION
);
71 class wxSoundBackendSDLEvtHandler
;
73 class wxSoundBackendSDL
: public wxSoundBackend
77 : m_initialized(false), m_playing(false), m_audioOpen(false),
79 virtual ~wxSoundBackendSDL();
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; }
85 bool Play(wxSoundData
*data
, unsigned flags
);
87 void FillAudioBuffer(Uint8
*stream
, int len
);
89 bool IsPlaying() const { return m_playing
; }
93 bool m_playing
, m_audioOpen
;
94 // playback information:
100 wxSoundBackendSDLEvtHandler
*m_evtHandler
;
103 class wxSoundBackendSDLEvtHandler
: public wxEvtHandler
106 wxSoundBackendSDLEvtHandler(wxSoundBackendSDL
*bk
) : m_backend(bk
) {}
109 void OnNotify(wxSoundBackendSDLNotification
& WXUNUSED(event
))
111 wxLogTrace(_T("sound"),
112 _T("received playback status change notification"));
115 wxSoundBackendSDL
*m_backend
;
117 DECLARE_EVENT_TABLE()
120 BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler
, wxEvtHandler
)
121 EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify
)
124 wxSoundBackendSDL::~wxSoundBackendSDL()
130 bool wxSoundBackendSDL::IsAvailable() const
134 if (SDL_WasInit(SDL_INIT_AUDIO
) != SDL_INIT_AUDIO
)
136 if (SDL_Init(SDL_INIT_AUDIO
| SDL_INIT_NOPARACHUTE
) == -1)
139 wxConstCast(this, wxSoundBackendSDL
)->m_initialized
= true;
140 wxLogTrace(_T("sound"), _T("initialized SDL audio subsystem"));
144 extern "C" void wx_sdl_audio_callback(void *userdata
, Uint8
*stream
, int len
)
146 wxSoundBackendSDL
*bk
= (wxSoundBackendSDL
*)userdata
;
147 bk
->FillAudioBuffer(stream
, len
);
150 void wxSoundBackendSDL::FillAudioBuffer(Uint8
*stream
, int len
)
154 // finished playing the sample
155 if (m_pos
== m_data
->m_dataBytes
)
158 wxSoundBackendSDLNotification event
;
159 m_evtHandler
->AddPendingEvent(event
);
161 // still something to play
164 unsigned size
= ((len
+ m_pos
) < m_data
->m_dataBytes
) ?
166 (m_data
->m_dataBytes
- m_pos
);
167 memcpy(stream
, m_data
->m_data
+ m_pos
, size
);
173 // the sample doesn't play, fill the buffer with silence and wait for
174 // the main thread to shut the playback down:
180 FillAudioBuffer(stream
, len
);
185 memset(stream
, m_spec
.silence
, len
);
190 bool wxSoundBackendSDL::Play(wxSoundData
*data
, unsigned flags
)
193 if (data
->m_bitsPerSample
== 8)
195 else if (data
->m_bitsPerSample
== 16)
196 format
= AUDIO_S16LSB
;
203 m_evtHandler
= new wxSoundBackendSDLEvtHandler(this);
207 bool needsOpen
= true;
210 wxLogTrace(_T("sound"), _T("another sound playing, will be stopped"));
211 if (format
== m_spec
.format
&&
212 m_spec
.freq
== (int)data
->m_samplingRate
&&
213 m_spec
.channels
== data
->m_channels
)
221 wxLogTrace(_T("sound"), _T("closed audio"));
229 m_loop
= (flags
& wxSOUND_LOOP
);
230 wxLogTrace(_T("sound"), _T("prepared sound for playback"));
236 m_spec
.format
= format
;
237 m_spec
.freq
= data
->m_samplingRate
;
238 m_spec
.channels
= data
->m_channels
;
240 m_spec
.samples
= 4096;
242 m_spec
.callback
= wx_sdl_audio_callback
;
243 m_spec
.userdata
= (void*)this;
245 wxLogTrace(_T("sound"), _T("opening SDL audio..."));
246 status
= (SDL_OpenAudio(&m_spec
, NULL
) >= 0);
251 SDL_AudioDriverName(driver
, 256);
252 wxLogTrace(_T("sound"), _T("opened audio, driver '%s'"),
253 wxString(driver
, wxConvLocal
).c_str());
260 wxString
err(SDL_GetError(), wxConvLocal
);
261 wxLogError(_("Couldn't open audio: %s"), err
.c_str());
270 // wait until playback finishes if called in sync mode:
271 if (!(flags
& wxSOUND_ASYNC
))
273 wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
277 // give the playback thread a chance to add event to pending
278 // events queue, release GUI lock temporarily:
279 if (wxThread::IsMain())
284 if (wxThread::IsMain())
288 wxLogTrace(_T("sound"), _T("sample finished"));
294 void wxSoundBackendSDL::Stop()
302 wxLogTrace(_T("sound"), _T("closed audio"));
309 extern "C" wxSoundBackend
*wxCreateSoundBackendSDL()
311 return new wxSoundBackendSDL();
314 #endif // wxUSE_WAVE && wxUSE_LIBSDL