]> git.saurik.com Git - wxWidgets.git/blobdiff - src/unix/sound_sdl.cpp
Allow OS X Cocoa (or any OS X port) to override GetBestSize and provide a native...
[wxWidgets.git] / src / unix / sound_sdl.cpp
index 72c0e70dc28f5319d128bc89886653fb945da7ea..3962e6dc722038401cca5b8ff09c1381b3673a3c 100644 (file)
@@ -1,24 +1,22 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        sound_sdl.cpp
+// Name:        src/unix/sound_sdl.cpp
 // Purpose:     wxSound backend using SDL
 // Author:      Vaclav Slavik
 // Modified by:
 // Created:     2004/01/31
 // RCS-ID:      $Id$
-// Copyright:   (c) 2004, Vaclav Slavik
-// Licence:    wxWindows licence
+// Copyright:   (c) 2004, Open Source Applications Foundation
+// Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 
 // for compilers that support precompilation, includes "wx.h".
 #include "wx/wxprec.h"
 
-#include "wx/setup.h"
-
 #if defined(__BORLANDC__)
-#pragma hdrstop
+    #pragma hdrstop
 #endif
 
-#if wxUSE_WAVE && wxUSE_LIBSDL
+#if wxUSE_SOUND && wxUSE_LIBSDL
 
 #include <SDL.h>
 
     #include "wx/event.h"
     #include "wx/intl.h"
     #include "wx/log.h"
-    #include "wx/list.h"
     #include "wx/utils.h"
+    #include "wx/module.h"
 #endif
 
 #include "wx/thread.h"
-#include "wx/module.h"
 #include "wx/sound.h"
-#include "wx/listimpl.cpp"
 
 // ----------------------------------------------------------------------------
 // wxSoundBackendSDL, for Unix with libSDL
 // ----------------------------------------------------------------------------
 
-struct wxSoundBackendSDLQueueEntry
-{
-    wxSoundData       *m_data;
-    unsigned          m_pos;
-    SDL_AudioSpec     m_spec;
-    bool              m_loop;
-    bool              m_finished;
-};
-
-WX_DECLARE_LIST(wxSoundBackendSDLQueueEntry, wxSoundBackendSDLQueue);
-WX_DEFINE_LIST(wxSoundBackendSDLQueue);
-
-
 class wxSoundBackendSDLNotification : public wxEvent
 {
 public:
     DECLARE_DYNAMIC_CLASS(wxSoundBackendSDLNotification)
     wxSoundBackendSDLNotification();
-       wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); }
+    wxEvent *Clone() const { return new wxSoundBackendSDLNotification(*this); }
 };
 
 typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction)
              (wxSoundBackendSDLNotification&);
 
-BEGIN_DECLARE_EVENT_TYPES()
-    DECLARE_LOCAL_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, -1)
-END_DECLARE_EVENT_TYPES()
+wxDECLARE_EVENT(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, wxSoundBackendSDLNotification);
 
 #define EVT_SOUND_BACKEND_SDL_NOTIFICATON(func) \
     DECLARE_EVENT_TABLE_ENTRY(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, \
                               -1,                       \
                               -1,                       \
-                              (wxObjectEventFunction)   \
-                              (wxSoundBackendSDLNotificationFunction)& func, \
-                              (wxObject *) NULL ),
+                              wxEVENT_HANDLER_CAST( wxSoundBackendSDLNotificationFunction, func ), \
+                              NULL ),
 
 IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler)
-DEFINE_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION)
+wxDEFINE_EVENT( wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, wxSoundBackendSDLNotification );
 
 wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
 {
@@ -88,23 +68,36 @@ class wxSoundBackendSDLEvtHandler;
 class wxSoundBackendSDL : public wxSoundBackend
 {
 public:
-    wxSoundBackendSDL() 
-        : m_initialized(false), m_playing(false), m_evtHandler(NULL) {}
+    wxSoundBackendSDL()
+        : m_initialized(false), m_playing(false), m_audioOpen(false),
+          m_data(NULL), m_evtHandler(NULL) {}
     virtual ~wxSoundBackendSDL();
-    
-    wxString GetName() const { return _T("Simple DirectMedia Layer"); }
+
+    wxString GetName() const { return wxT("Simple DirectMedia Layer"); }
     int GetPriority() const { return 9; }
     bool IsAvailable() const;
     bool HasNativeAsyncPlayback() const { return true; }
-    bool Play(wxSoundData *data, unsigned flags);
+    bool Play(wxSoundData *data, unsigned flags,
+              volatile wxSoundPlaybackStatus *status);
 
     void FillAudioBuffer(Uint8 *stream, int len);
-    bool PlayNextSampleInQueue();
-    
+    void FinishedPlayback();
+
+    void Stop();
+    bool IsPlaying() const { return m_playing; }
+
 private:
+    bool OpenAudio();
+    void CloseAudio();
+
     bool                        m_initialized;
-    bool                        m_playing;
-    wxSoundBackendSDLQueue       m_queue;
+    bool                        m_playing, m_audioOpen;
+    // playback information:
+    wxSoundData                 *m_data;
+    unsigned                     m_pos;
+    SDL_AudioSpec                m_spec;
+    bool                         m_loop;
+
     wxSoundBackendSDLEvtHandler *m_evtHandler;
 };
 
@@ -116,9 +109,9 @@ public:
 private:
     void OnNotify(wxSoundBackendSDLNotification& WXUNUSED(event))
     {
-        wxLogTrace(_T("sound"),
-                   _T("received playback status change notification"));
-        m_backend->PlayNextSampleInQueue();
+        wxLogTrace(wxT("sound"),
+                   wxT("received playback status change notification"));
+        m_backend->FinishedPlayback();
     }
     wxSoundBackendSDL *m_backend;
 
@@ -131,12 +124,9 @@ END_EVENT_TABLE()
 
 wxSoundBackendSDL::~wxSoundBackendSDL()
 {
-    SDL_LockAudio();
-    if (m_playing)
-        SDL_CloseAudio();
-    SDL_UnlockAudio();
-    wxDELETE(m_evtHandler);
-    WX_CLEAR_LIST(wxSoundBackendSDLQueue, m_queue)
+    Stop();
+    CloseAudio();
+    delete m_evtHandler;
 }
 
 bool wxSoundBackendSDL::IsAvailable() const
@@ -149,7 +139,7 @@ bool wxSoundBackendSDL::IsAvailable() const
             return false;
     }
     wxConstCast(this, wxSoundBackendSDL)->m_initialized = true;
-    wxLogTrace(_T("sound"), _T("initialized SDL audio subsystem"));
+    wxLogTrace(wxT("sound"), wxT("initialized SDL audio subsystem"));
     return true;
 }
 
@@ -161,13 +151,11 @@ extern "C" void wx_sdl_audio_callback(void *userdata, Uint8 *stream, int len)
 
 void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
 {
-    wxSoundBackendSDLQueueEntry *e = m_queue.front();
-    if (!e->m_finished)
+    if (m_playing)
     {
         // finished playing the sample
-        if (e->m_pos == e->m_data->m_dataBytes)
+        if (m_pos == m_data->m_dataBytes)
         {
-            e->m_finished = true;
             m_playing = false;
             wxSoundBackendSDLNotification event;
             m_evtHandler->AddPendingEvent(event);
@@ -175,11 +163,11 @@ void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
         // still something to play
         else
         {
-            unsigned size = ((len + e->m_pos) < e->m_data->m_dataBytes) ?
+            unsigned size = ((len + m_pos) < m_data->m_dataBytes) ?
                             len :
-                            (e->m_data->m_dataBytes - e->m_pos);
-            memcpy(stream, e->m_data->m_data + e->m_pos, size);
-            e->m_pos += size;
+                            (m_data->m_dataBytes - m_pos);
+            memcpy(stream, m_data->m_data + m_pos, size);
+            m_pos += size;
             len -= size;
             stream += size;
         }
@@ -188,53 +176,123 @@ void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
     // the main thread to shut the playback down:
     if (len > 0)
     {
-        if (e->m_loop)
+        if (m_loop)
         {
-            e->m_pos = 0;
+            m_pos = 0;
             FillAudioBuffer(stream, len);
             return;
         }
         else
         {
-            memset(stream, e->m_spec.silence, len);
+            memset(stream, m_spec.silence, len);
         }
     }
 }
 
-bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags)
+void wxSoundBackendSDL::FinishedPlayback()
 {
-    data->IncRef();
+    if (!m_playing)
+        Stop();
+}
 
-    wxSoundBackendSDLQueueEntry *e = new wxSoundBackendSDLQueueEntry();
-    e->m_data = data;
-    e->m_pos = 0;
-    e->m_loop = (flags & wxSOUND_LOOP);
-    e->m_finished = false;
-    e->m_spec.freq = data->m_samplingRate;
-    e->m_spec.channels = data->m_channels;
-    e->m_spec.silence = 0;
-    e->m_spec.samples = 4096;
-    e->m_spec.size = 0;
-    e->m_spec.callback = wx_sdl_audio_callback;
-    e->m_spec.userdata = (void*)this;
-    
+bool wxSoundBackendSDL::OpenAudio()
+{
+    if (!m_audioOpen)
+    {
+        if (!m_evtHandler)
+            m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
+
+        m_spec.silence = 0;
+        m_spec.samples = 4096;
+        m_spec.size = 0;
+        m_spec.callback = wx_sdl_audio_callback;
+        m_spec.userdata = (void*)this;
+
+        wxLogTrace(wxT("sound"), wxT("opening SDL audio..."));
+        if (SDL_OpenAudio(&m_spec, NULL) >= 0)
+        {
+#if wxUSE_LOG_DEBUG
+            char driver[256];
+            SDL_AudioDriverName(driver, 256);
+            wxLogTrace(wxT("sound"), wxT("opened audio, driver '%s'"),
+                       wxString(driver, wxConvLocal).c_str());
+#endif
+            m_audioOpen = true;
+            return true;
+        }
+        else
+        {
+            wxString err(SDL_GetError(), wxConvLocal);
+            wxLogError(_("Couldn't open audio: %s"), err.c_str());
+            return false;
+        }
+    }
+    return true;
+}
+
+void wxSoundBackendSDL::CloseAudio()
+{
+    if (m_audioOpen)
+    {
+        SDL_CloseAudio();
+        wxLogTrace(wxT("sound"), wxT("closed audio"));
+        m_audioOpen = false;
+    }
+}
+
+bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags,
+                             volatile wxSoundPlaybackStatus *WXUNUSED(status))
+{
+    Stop();
+
+    int format;
     if (data->m_bitsPerSample == 8)
-        e->m_spec.format = AUDIO_U8;
+        format = AUDIO_U8;
     else if (data->m_bitsPerSample == 16)
-        e->m_spec.format = AUDIO_S16LSB;
+        format = AUDIO_S16LSB;
     else
         return false;
 
-    m_queue.push_back(e);
-    wxLogTrace(_T("sound"), _T("queued sample %p for playback"), e);
+    bool needsOpen = true;
+    if (m_audioOpen)
+    {
+        if (format == m_spec.format &&
+            m_spec.freq == (int)data->m_samplingRate &&
+            m_spec.channels == data->m_channels)
+        {
+            needsOpen = false;
+        }
+        else
+        {
+            CloseAudio();
+        }
+    }
 
-    if (!PlayNextSampleInQueue())
-        return false;
+    if (needsOpen)
+    {
+        m_spec.format = format;
+        m_spec.freq = data->m_samplingRate;
+        m_spec.channels = data->m_channels;
+        if (!OpenAudio())
+            return false;
+    }
+
+    SDL_LockAudio();
+    wxLogTrace(wxT("sound"), wxT("playing new sound"));
+    m_playing = true;
+    m_pos = 0;
+    m_loop = (flags & wxSOUND_LOOP);
+    m_data = data;
+    data->IncRef();
+    SDL_UnlockAudio();
 
+    SDL_PauseAudio(0);
+
+    // wait until playback finishes if called in sync mode:
     if (!(flags & wxSOUND_ASYNC))
     {
-        wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
-        while (!m_queue.empty() && m_queue.front() == e && !e->m_finished)
+        wxLogTrace(wxT("sound"), wxT("waiting for sample to finish"));
+        while (m_playing && m_data == data)
         {
 #if wxUSE_THREADS
             // give the playback thread a chance to add event to pending
@@ -242,88 +300,29 @@ bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags)
             if (wxThread::IsMain())
                 wxMutexGuiLeave();
 #endif
-            wxUsleep(10);
+            wxMilliSleep(10);
 #if wxUSE_THREADS
             if (wxThread::IsMain())
                 wxMutexGuiEnter();
 #endif
         }
-        wxLogTrace(_T("sound"), _T("sample finished"));
+        wxLogTrace(wxT("sound"), wxT("sample finished"));
     }
-   
+
     return true;
 }
-    
-bool wxSoundBackendSDL::PlayNextSampleInQueue()
-{
-    bool status = true;
 
+void wxSoundBackendSDL::Stop()
+{
     SDL_LockAudio();
-
-    if (!m_evtHandler)
-        m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
-    
-    if (!m_playing && !m_queue.empty())
+    SDL_PauseAudio(1);
+    m_playing = false;
+    if (m_data)
     {
-        bool needsReopen = true;
-        // shut down playing of finished sound:
-        wxSoundBackendSDLQueueEntry *e = m_queue.front();
-        if (e->m_finished)
-        {
-            SDL_PauseAudio(1);
-            e->m_data->DecRef();
-            m_queue.pop_front();
-            if (!m_queue.empty() &&
-                e->m_spec.freq == m_queue.front()->m_spec.freq &&
-                e->m_spec.channels == m_queue.front()->m_spec.channels &&
-                e->m_spec.format == m_queue.front()->m_spec.format)
-            {
-                needsReopen = false;
-            }
-            else
-            {
-                SDL_CloseAudio();
-                wxLogTrace(_T("sound"), _T("closed audio"));
-            }
-            delete e;
-        }
-        // start playing another one:
-        if (!m_queue.empty())
-        {
-            wxSoundBackendSDLQueueEntry *e = m_queue.front();
-            m_playing = true;
-            wxLogTrace(_T("sound"), _T("playing sample %p"), e);
-            
-            if (needsReopen)
-            { 
-                wxLogTrace(_T("sound"), _T("opening SDL audio..."));
-                status = (SDL_OpenAudio(&e->m_spec, NULL) >= 0);
-                if (status)
-                {
-#if wxUSE_LOG_DEBUG
-                    char driver[256];
-                    SDL_AudioDriverName(driver, 256);                    
-                    wxLogTrace(_T("sound"), _T("opened audio, driver '%s'"),
-                               wxString(driver, wxConvLocal).c_str());
-#endif
-                    SDL_PauseAudio(0);
-                }
-                else
-                {
-                    wxString err(SDL_GetError(), wxConvLocal);
-                    wxLogError(_("Couldn't open audio: %s"), err.c_str());
-                    m_queue.pop_front();
-                    delete e;
-                }
-            }
-            else
-                SDL_PauseAudio(0);
-        }
+        m_data->DecRef();
+        m_data = NULL;
     }
-    
     SDL_UnlockAudio();
-
-    return status;
 }
 
 extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
@@ -331,4 +330,4 @@ extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
     return new wxSoundBackendSDL();
 }
 
-#endif // wxUSE_WAVE && wxUSE_LIBSDL
+#endif // wxUSE_SOUND && wxUSE_LIBSDL