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