]> git.saurik.com Git - wxWidgets.git/commitdiff
added support for async playback to Unix implementation of wxSound, implemented SDL...
authorVáclav Slavík <vslavik@fastmail.fm>
Sun, 1 Feb 2004 18:25:12 +0000 (18:25 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Sun, 1 Feb 2004 18:25:12 +0000 (18:25 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@25455 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/unix/sound.h [new file with mode: 0644]
include/wx/unix/wave.h [deleted file]
src/unix/sound.cpp [new file with mode: 0644]
src/unix/sound_sdl.cpp [new file with mode: 0644]
src/unix/wave.cpp [deleted file]

diff --git a/include/wx/unix/sound.h b/include/wx/unix/sound.h
new file mode 100644 (file)
index 0000000..c85b0ef
--- /dev/null
@@ -0,0 +1,136 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        wave.h
+// Purpose:     wxSound class
+// Author:      Julian Smart, Vaclav Slavik
+// Modified by:
+// Created:     25/10/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart, Vaclav Slavik
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef _WX_SOUND_H_
+#define _WX_SOUND_H_
+
+#include "wx/defs.h"
+
+#if wxUSE_WAVE
+
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
+#pragma interface "sound.h"
+#endif
+
+#include "wx/object.h"
+
+// ----------------------------------------------------------------------------
+// wxSound: simple audio playback class
+// ----------------------------------------------------------------------------
+
+class wxSoundBackend;
+class wxSound;
+class wxDynamicLibrary;
+
+/// Sound data, as loaded from .wav file:
+class wxSoundData
+{
+public:
+    wxSoundData() : m_refCnt(1) {}
+    void IncRef();
+    void DecRef();
+    
+    // .wav header information:
+    unsigned m_channels;       // num of channels (mono:1, stereo:2)
+    unsigned m_samplingRate;
+    unsigned m_bitsPerSample;  // if 8, then m_data contains unsigned 8bit
+                               // samples (wxUint8), if 16 then signed 16bit
+                               // (wxInt16)
+    unsigned m_samples;        // length in samples:
+    
+    // wave data:
+    size_t   m_dataBytes;
+    wxUint8 *m_data;           // m_dataBytes bytes of data
+
+private:
+    ~wxSoundData();
+    unsigned m_refCnt;
+    wxUint8 *m_dataWithHeader; // ditto, but prefixed with .wav header
+    friend class wxSound;
+};
+
+
+/// Simple sound class:
+class wxSound : public wxSoundBase
+{
+public:
+    wxSound();
+    wxSound(const wxString& fileName, bool isResource = false);
+    wxSound(int size, const wxByte* data);
+    ~wxSound();
+
+    // Create from resource or file
+    bool Create(const wxString& fileName, bool isResource = false);
+    // Create from data
+    bool Create(int size, const wxByte* data);
+
+    bool IsOk() const { return m_data != NULL; }
+    
+    // for internal use
+    static void UnloadBackend();
+    
+protected:
+    bool DoPlay(unsigned flags);
+
+    static void EnsureBackend();
+    void Free();
+    bool LoadWAV(const wxUint8 *data, size_t length, bool copyData);
+    
+    static wxSoundBackend *ms_backend;
+#if wxUSE_LIBSDL && wxUSE_PLUGINS
+    // FIXME - temporary, until we have plugins architecture
+    static wxDynamicLibrary *ms_backendSDL;
+#endif
+
+private:
+    wxSoundData *m_data;
+};
+
+
+// ----------------------------------------------------------------------------
+// wxSoundBackend: 
+// ----------------------------------------------------------------------------
+
+// This is interface to sound playing implementation. There are multiple
+// sound architectures in use on Unix platforms and wxWindows can use several
+// of them for playback, depending on their availability at runtime; hence
+// the need for backends. This class is for use by wxWindows and people writing
+// additional backends only, it is _not_ for use by applications! 
+
+class wxSoundBackend
+{
+public:
+    virtual ~wxSoundBackend() {}
+    
+    // Returns the name of the backend (e.g. "Open Sound System")
+    virtual wxString GetName() const = 0;
+
+    // Returns priority (higher priority backends are tried first)
+    virtual int GetPriority() const = 0;
+
+    // Checks if the backend's audio system is available and the backend can
+    // be used for playback
+    virtual bool IsAvailable() const = 0;
+
+    // Returns true if the backend is capable of playing sound asynchronously.
+    // If false, then wxWindows creates a playback thread and handles async
+    // playback, otherwise it is left up to the backend (will usually be more
+    // effective)
+    virtual bool HasNativeAsyncPlayback() const = 0;
+
+    // Plays the sound. flags are same flags as those passed to wxSound::Play
+    virtual bool Play(wxSoundData *data, unsigned flags) = 0;
+};
+
+
+#endif // wxUSE_WAVE
+
+#endif
diff --git a/include/wx/unix/wave.h b/include/wx/unix/wave.h
deleted file mode 100644 (file)
index 82393b6..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/////////////////////////////////////////////////////////////////////////////
-// Name:        wave.h
-// Purpose:     wxWave class
-// Author:      Julian Smart
-// Modified by:
-// Created:     25/10/98
-// RCS-ID:      $Id$
-// Copyright:   (c) Julian Smart
-// Licence:    wxWindows licence
-/////////////////////////////////////////////////////////////////////////////
-
-#ifndef _WX_WAVE_H_
-#define _WX_WAVE_H_
-
-#include "wx/defs.h"
-
-#if wxUSE_WAVE
-
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#pragma interface "wave.h"
-#endif
-
-#include "wx/object.h"
-
-#ifndef AUDIODEV
-#define AUDIODEV   "/dev/dsp"    // Default path for audio device
-#endif
-
-class wxWave : public wxObject
-{
-public:
-  wxWave();
-  wxWave(const wxString& fileName, bool isResource = FALSE);
-  wxWave(int size, const wxByte* data);
-  ~wxWave();
-
-public:
-  // Create from resource or file
-  bool  Create(const wxString& fileName, bool isResource = FALSE);
-  // Create from data
-  bool Create(int size, const wxByte* data);
-
-  bool  IsOk() const { return (m_waveData ? TRUE : FALSE); };
-  bool  Play(bool async = TRUE, bool looped = FALSE);
-
-protected:
-  bool  Free();
-
-private:
-  wxByte* m_waveData;
-  int   m_waveLength;
-  bool  m_isResource;
-
-
-  int OpenDSP(void);
-  bool InitDSP(int dev, int iDataBits, int iChannel,unsigned long ulSamplingRate);
-  int m_DSPblkSize;        // Size of the DSP buffer
-  char *m_data;
-  int m_sizeData;
-};
-
-#endif
-
-#endif
-
diff --git a/src/unix/sound.cpp b/src/unix/sound.cpp
new file mode 100644 (file)
index 0000000..b15498a
--- /dev/null
@@ -0,0 +1,505 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        sound.cpp
+// Purpose:     wxSound
+// Author:      Marcel Rasche, Vaclav Slavik
+// Modified by:
+// Created:     25/10/98
+// RCS-ID:      $Id$
+// Copyright:   (c) Julian Smart, Vaclav Slavik
+// Licence:    wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
+#pragma implementation "sound.h"
+#pragma implementation "soundbase.h"
+#endif
+
+// for compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#include "wx/setup.h"
+
+#if defined(__BORLANDC__)
+#pragma hdrstop
+#endif
+
+#if wxUSE_WAVE
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#if HAVE_SYS_SOUNDCARD_H
+#include <sys/soundcard.h>
+#endif
+
+#ifndef WX_PRECOMP
+    #include "wx/event.h"
+    #include "wx/intl.h"
+    #include "wx/log.h"
+#endif
+
+#include "wx/thread.h"
+#include "wx/file.h"
+#include "wx/module.h"
+#include "wx/sound.h"
+#include "wx/dynlib.h"
+
+// ----------------------------------------------------------------------------
+// wxSoundBackendNull, used in absence of audio API or card
+// ----------------------------------------------------------------------------
+
+class wxSoundBackendNull : public wxSoundBackend
+{
+public:
+    wxString GetName() const { return _("No sound"); }
+    int GetPriority() const { return 0; }
+    bool IsAvailable() const { return true; }
+    bool HasNativeAsyncPlayback() const { return true; }
+    bool Play(wxSoundData *WXUNUSED(data), unsigned WXUNUSED(flags))
+        { return true; }
+};
+
+
+// ----------------------------------------------------------------------------
+// wxSoundBackendOSS, for Linux
+// ----------------------------------------------------------------------------
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+
+#ifndef AUDIODEV
+#define AUDIODEV   "/dev/dsp"    // Default path for audio device
+#endif
+
+class wxSoundBackendOSS : public wxSoundBackend
+{
+public:
+    wxString GetName() const { return _T("Open Sound System"); }
+    int GetPriority() const { return 10; }
+    bool IsAvailable() const;
+    bool HasNativeAsyncPlayback() const { return false; }
+    bool Play(wxSoundData *data, unsigned flags);
+
+private:
+    int OpenDSP(const wxSoundData *data);
+    bool InitDSP(int dev, int iDataBits, int iChannel,
+                 unsigned long ulSamplingRate);
+    
+    int m_DSPblkSize;        // Size of the DSP buffer
+};
+
+bool wxSoundBackendOSS::IsAvailable() const
+{
+    int fd;
+    fd = open(AUDIODEV, O_WRONLY | O_NONBLOCK);
+    if (fd < 0)
+        return false;
+    close(fd);
+    return true;
+}
+
+bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags)
+{
+    int dev = OpenDSP(data);
+    
+    if (dev < 0)
+        return false;
+
+    ioctl(dev, SNDCTL_DSP_SYNC, 0);
+    do
+    {
+        bool play = true;
+        int i;
+        unsigned l = 0;
+        size_t datasize = data->m_dataBytes;
+
+        do
+        {
+            i= (int)((l + m_DSPblkSize) < datasize ?
+                    m_DSPblkSize : (datasize - l));
+            if (write(dev, &data->m_data[l], i) != i)
+            {
+                play = false;
+            }
+            l += i;
+        } while (play && l < datasize);
+    } while (flags & wxSOUND_LOOP);
+
+    close(dev);
+    return true;
+}
+
+int wxSoundBackendOSS::OpenDSP(const wxSoundData *data)
+{
+    int dev = -1;
+  
+    if ((dev = open(AUDIODEV, O_WRONLY, 0)) <0)
+        return -1;
+  
+    if (!InitDSP(dev,
+                 (int)data->m_bitsPerSample,
+                 data->m_channels == 1 ? 0 : 1,
+                 data->m_samplingRate))
+    {
+        close(dev);
+        return -1;
+    }
+
+    return dev;
+}
+
+bool wxSoundBackendOSS::InitDSP(int dev, int iDataBits, int iChannel,
+                               unsigned long ulSamplingRate)
+{
+    if (ioctl(dev, SNDCTL_DSP_GETBLKSIZE, &m_DSPblkSize) < 0)
+        return false;
+    if (m_DSPblkSize < 4096 || m_DSPblkSize > 65536)
+        return false;
+    if (ioctl(dev, SNDCTL_DSP_SAMPLESIZE, &iDataBits) < 0)
+        return false;
+    if (ioctl(dev, SNDCTL_DSP_STEREO, &iChannel) < 0)
+        return false;
+    if (ioctl(dev, SNDCTL_DSP_SPEED, &ulSamplingRate) < 0)
+        return false;
+    return true;
+}
+
+#endif // HAVE_SYS_SOUNDCARD_H
+
+
+// ----------------------------------------------------------------------------
+// wxSoundData
+// ----------------------------------------------------------------------------
+  
+void wxSoundData::IncRef()
+{
+    m_refCnt++;
+}
+
+void wxSoundData::DecRef()
+{
+    if (--m_refCnt == 0)
+        delete this;
+}
+
+wxSoundData::~wxSoundData()
+{
+    delete[] m_dataWithHeader;
+}
+
+
+// ----------------------------------------------------------------------------
+// wxSoundAsyncPlaybackThread
+// ----------------------------------------------------------------------------
+
+#if wxUSE_THREADS
+
+// mutex for all wxSound's synchronization
+static wxMutex gs_soundMutex;
+
+// this class manages asynchronous playback of audio if the backend doesn't
+// support it natively (e.g. OSS backend)
+class wxSoundAsyncPlaybackThread : public wxThread
+{
+public:
+    wxSoundAsyncPlaybackThread(wxSoundBackend *backend,
+                              wxSoundData *data, unsigned flags)
+        : wxThread(), m_backend(backend), m_data(data), m_flags(flags) {}
+    virtual ExitCode Entry()
+    {
+        m_backend->Play(m_data, m_flags & ~wxSOUND_ASYNC);
+        wxMutexLocker locker(gs_soundMutex);
+        m_data->DecRef();
+        wxLogTrace(_T("sound"), _T("terminated async playback thread"));
+        return 0;
+    }
+    
+protected:
+    wxSoundBackend *m_backend;
+    wxSoundData *m_data;
+    unsigned m_flags;
+};
+
+#endif // wxUSE_THREADS
+
+// ----------------------------------------------------------------------------
+// wxSound 
+// ----------------------------------------------------------------------------
+
+wxSoundBackend *wxSound::ms_backend = NULL;
+
+// FIXME - temporary, until we have plugins architecture
+#if wxUSE_LIBSDL
+    #if wxUSE_PLUGINS
+        wxDynamicLibrary *wxSound::ms_backendSDL = NULL;
+    #else
+        extern "C" wxSoundBackend *wxCreateSoundBackendSDL();
+    #endif
+#endif
+
+wxSound::wxSound() : m_data(NULL)
+{
+}
+
+wxSound::wxSound(const wxString& sFileName, bool isResource) : m_data(NULL)
+{
+    Create(sFileName, isResource);
+}
+
+wxSound::wxSound(int size, const wxByte* data) : m_data(NULL)
+{
+    Create(size, data);
+}
+
+wxSound::~wxSound()
+{
+    Free();
+}
+
+bool wxSound::Create(const wxString& fileName, bool isResource)
+{
+    wxASSERT_MSG( !isResource,
+             _T("Loading sound from resources is only supported on Windows") );
+    Free();
+  
+    wxFile fileWave;
+    if (!fileWave.Open(fileName, wxFile::read))
+       {
+            return false;
+       }
+
+    size_t len = fileWave.Length();
+    wxUint8 *data = new wxUint8[len];
+    if (fileWave.Read(data, len) != len)
+    {
+        wxLogError(_("Couldn't load sound data from '%s'."), fileName.c_str());
+        return false;
+    }
+
+    if (!LoadWAV(data, len, false))
+    {
+        wxLogError(_("Sound file '%s' is in unsupported format."),
+                   fileName.c_str());
+        return false;
+    }
+    
+    return true;
+}
+
+bool wxSound::Create(int size, const wxByte* data)
+{
+    wxASSERT( data != NULL );
+
+    Free();
+    if (!LoadWAV(data, size, true))
+    {
+        wxLogError(_("Sound data are in unsupported format."));
+        return false;
+    }
+    return true;
+}
+
+/*static*/ void wxSound::EnsureBackend()
+{
+    if (!ms_backend)
+    {
+        // FIXME -- make this fully dynamic when plugins architecture is in
+        // place
+#ifdef HAVE_SYS_SOUNDCARD_H
+        ms_backend = new wxSoundBackendOSS();
+        if (!ms_backend->IsAvailable())
+        {
+            wxDELETE(ms_backend);
+        }
+#endif
+
+#if wxUSE_LIBSDL
+        if (!ms_backend)
+        {
+#if !wxUSE_PLUGINS
+            ms_backend = wxCreateSoundBackendSDL();
+#else
+            wxString dllname;
+            dllname.Printf(_T("%s/%s"),
+                wxDynamicLibrary::GetPluginsDirectory().c_str(),
+                wxDynamicLibrary::CanonicalizePluginName(
+                    _T("sound_sdl"), wxDL_PLUGIN_BASE).c_str());
+            wxLogTrace(_T("sound"),
+                       _T("trying to load SDL plugin from '%s'..."),
+                       dllname.c_str());
+            wxLogNull null;
+            ms_backendSDL = new wxDynamicLibrary(dllname, wxDL_NOW);
+            if (!ms_backendSDL->IsLoaded())
+            {
+                wxDELETE(ms_backendSDL);
+            }
+            else
+            {
+                typedef wxSoundBackend *(*wxCreateSoundBackend_t)();
+                wxDYNLIB_FUNCTION(wxCreateSoundBackend_t,
+                                  wxCreateSoundBackendSDL, *ms_backendSDL);
+                if (pfnwxCreateSoundBackendSDL)
+                {
+                    ms_backend = (*pfnwxCreateSoundBackendSDL)();
+                }
+            }
+#endif
+            if (ms_backend && !ms_backend->IsAvailable())
+            {
+                wxDELETE(ms_backend);
+            }
+        }
+#endif
+
+        if (!ms_backend)
+            ms_backend = new wxSoundBackendNull();
+
+        wxLogTrace(_T("sound"),
+                   _T("using backend '%s'"), ms_backend->GetName().c_str());
+    }
+}
+
+/*static*/ void wxSound::UnloadBackend()
+{
+    if (ms_backend)
+    {
+        wxLogTrace(_T("sound"), _T("unloading backend"));
+        delete ms_backend;
+        ms_backend = NULL;
+#if wxUSE_LIBSDL && wxUSE_PLUGINS
+        delete ms_backendSDL;
+#endif
+    }
+}
+
+bool wxSound::DoPlay(unsigned flags)
+{
+    wxCHECK_MSG( IsOk(), false, _T("Attempt to play invalid wave data") );
+
+    EnsureBackend();
+
+    if ((flags & wxSOUND_ASYNC) && !ms_backend->HasNativeAsyncPlayback())
+    {
+#if wxUSE_THREADS
+        wxMutexLocker locker(gs_soundMutex);
+        m_data->IncRef();
+        wxThread *th = new wxSoundAsyncPlaybackThread(ms_backend, m_data, flags);
+        th->Create();
+        th->Run();
+        wxLogTrace(_T("sound"), _T("launched async playback thread"));
+#else
+        wxLogError(_("Unable to play sound asynchronously."));
+        return false;
+#endif
+    }
+    else
+    {
+        ms_backend->Play(m_data, flags);
+    }
+    return true;
+}
+
+void wxSound::Free()
+{
+#if wxUSE_THREADS
+    wxMutexLocker locker(gs_soundMutex);
+#endif
+    if (m_data)
+        m_data->DecRef();
+}
+
+typedef struct
+{ 
+    wxUint32      uiSize;
+    wxUint16      uiFormatTag;
+    wxUint16      uiChannels;
+    wxUint32      ulSamplesPerSec;
+    wxUint32      ulAvgBytesPerSec;
+    wxUint16      uiBlockAlign;
+    wxUint16      uiBitsPerSample;
+} WAVEFORMAT;    
+
+#define MONO             1  // and stereo is 2 by wav format
+#define WAVE_FORMAT_PCM  1
+#define WAVE_INDEX       8
+#define FMT_INDEX       12
+
+bool wxSound::LoadWAV(const wxUint8 *data, size_t length, bool copyData)
+{
+    WAVEFORMAT waveformat;
+    wxUint32 ul;
+
+    if (length < 32 + sizeof(WAVEFORMAT))
+        return false;
+
+    memcpy(&waveformat, &data[FMT_INDEX + 4], sizeof(WAVEFORMAT));
+    waveformat.uiSize = wxUINT32_SWAP_ON_BE(waveformat.uiSize);
+    waveformat.uiFormatTag = wxUINT16_SWAP_ON_BE(waveformat.uiFormatTag);
+    waveformat.uiChannels = wxUINT16_SWAP_ON_BE(waveformat.uiChannels);
+    waveformat.ulSamplesPerSec = wxUINT32_SWAP_ON_BE(waveformat.ulSamplesPerSec);
+    waveformat.ulAvgBytesPerSec = wxUINT32_SWAP_ON_BE(waveformat.ulAvgBytesPerSec);
+    waveformat.uiBlockAlign = wxUINT16_SWAP_ON_BE(waveformat.uiBlockAlign);
+    waveformat.uiBitsPerSample = wxUINT16_SWAP_ON_BE(waveformat.uiBitsPerSample);
+
+    if (memcmp(data, "RIFF", 4) != 0)
+        return false;
+    if (memcmp(&data[WAVE_INDEX], "WAVE", 4) != 0)
+        return false;
+    if (memcmp(&data[FMT_INDEX], "fmt ", 4) != 0)
+        return false;
+    if (memcmp(&data[FMT_INDEX + waveformat.uiSize + 8], "data", 4) != 0)
+        return false;
+    memcpy(&ul,&data[FMT_INDEX + waveformat.uiSize + 12], 4);
+    ul = wxUINT32_SWAP_ON_BE(ul);
+    
+    //WAS: if (ul + FMT_INDEX + waveformat.uiSize + 16 != length)
+    if (ul + FMT_INDEX + waveformat.uiSize + 16 > length)
+        return false;
+  
+    if (waveformat.uiFormatTag != WAVE_FORMAT_PCM)
+        return false;
+  
+    if (waveformat.ulSamplesPerSec != 
+        waveformat.ulAvgBytesPerSec / waveformat.uiBlockAlign)
+        return false;
+    
+    m_data = new wxSoundData;
+    m_data->m_channels = waveformat.uiChannels;
+    m_data->m_samplingRate = waveformat.ulSamplesPerSec;
+    m_data->m_bitsPerSample = waveformat.uiBitsPerSample;
+    m_data->m_samples = ul / (m_data->m_channels * m_data->m_bitsPerSample / 8);
+    m_data->m_dataBytes = ul;
+
+    if (copyData)
+    {
+        m_data->m_dataWithHeader = new wxUint8[length];
+        memcpy(m_data->m_dataWithHeader, data, length);
+    }
+    else
+        m_data->m_dataWithHeader = (wxUint8*)data;
+
+    m_data->m_data = 
+        (&m_data->m_dataWithHeader[FMT_INDEX + waveformat.uiSize + 8]);
+
+    return true;
+}
+
+
+// ----------------------------------------------------------------------------
+// wxSoundCleanupModule
+// ----------------------------------------------------------------------------
+
+class wxSoundCleanupModule: public wxModule
+{
+public:
+    bool OnInit() { return true; }
+    void OnExit() { wxSound::UnloadBackend(); }
+    DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule)
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule, wxModule)
+
+#endif
diff --git a/src/unix/sound_sdl.cpp b/src/unix/sound_sdl.cpp
new file mode 100644 (file)
index 0000000..72c0e70
--- /dev/null
@@ -0,0 +1,334 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        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
+/////////////////////////////////////////////////////////////////////////////
+
+// for compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#include "wx/setup.h"
+
+#if defined(__BORLANDC__)
+#pragma hdrstop
+#endif
+
+#if wxUSE_WAVE && wxUSE_LIBSDL
+
+#include <SDL.h>
+
+#ifndef WX_PRECOMP
+    #include "wx/event.h"
+    #include "wx/intl.h"
+    #include "wx/log.h"
+    #include "wx/list.h"
+    #include "wx/utils.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); }
+};
+
+typedef void (wxEvtHandler::*wxSoundBackendSDLNotificationFunction)
+             (wxSoundBackendSDLNotification&);
+
+BEGIN_DECLARE_EVENT_TYPES()
+    DECLARE_LOCAL_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, -1)
+END_DECLARE_EVENT_TYPES()
+
+#define EVT_SOUND_BACKEND_SDL_NOTIFICATON(func) \
+    DECLARE_EVENT_TABLE_ENTRY(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION, \
+                              -1,                       \
+                              -1,                       \
+                              (wxObjectEventFunction)   \
+                              (wxSoundBackendSDLNotificationFunction)& func, \
+                              (wxObject *) NULL ),
+
+IMPLEMENT_DYNAMIC_CLASS(wxSoundBackendSDLNotification, wxEvtHandler)
+DEFINE_EVENT_TYPE(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION)
+
+wxSoundBackendSDLNotification::wxSoundBackendSDLNotification()
+{
+    SetEventType(wxEVT_SOUND_BACKEND_SDL_NOTIFICATION);
+}
+
+class wxSoundBackendSDLEvtHandler;
+
+class wxSoundBackendSDL : public wxSoundBackend
+{
+public:
+    wxSoundBackendSDL() 
+        : m_initialized(false), m_playing(false), m_evtHandler(NULL) {}
+    virtual ~wxSoundBackendSDL();
+    
+    wxString GetName() const { return _T("Simple DirectMedia Layer"); }
+    int GetPriority() const { return 9; }
+    bool IsAvailable() const;
+    bool HasNativeAsyncPlayback() const { return true; }
+    bool Play(wxSoundData *data, unsigned flags);
+
+    void FillAudioBuffer(Uint8 *stream, int len);
+    bool PlayNextSampleInQueue();
+    
+private:
+    bool                        m_initialized;
+    bool                        m_playing;
+    wxSoundBackendSDLQueue       m_queue;
+    wxSoundBackendSDLEvtHandler *m_evtHandler;
+};
+
+class wxSoundBackendSDLEvtHandler : public wxEvtHandler
+{
+public:
+    wxSoundBackendSDLEvtHandler(wxSoundBackendSDL *bk) : m_backend(bk) {}
+
+private:
+    void OnNotify(wxSoundBackendSDLNotification& WXUNUSED(event))
+    {
+        wxLogTrace(_T("sound"),
+                   _T("received playback status change notification"));
+        m_backend->PlayNextSampleInQueue();
+    }
+    wxSoundBackendSDL *m_backend;
+
+    DECLARE_EVENT_TABLE()
+};
+
+BEGIN_EVENT_TABLE(wxSoundBackendSDLEvtHandler, wxEvtHandler)
+    EVT_SOUND_BACKEND_SDL_NOTIFICATON(wxSoundBackendSDLEvtHandler::OnNotify)
+END_EVENT_TABLE()
+
+wxSoundBackendSDL::~wxSoundBackendSDL()
+{
+    SDL_LockAudio();
+    if (m_playing)
+        SDL_CloseAudio();
+    SDL_UnlockAudio();
+    wxDELETE(m_evtHandler);
+    WX_CLEAR_LIST(wxSoundBackendSDLQueue, m_queue)
+}
+
+bool wxSoundBackendSDL::IsAvailable() const
+{
+    if (m_initialized)
+        return true;
+    if (SDL_WasInit(SDL_INIT_AUDIO) != SDL_INIT_AUDIO)
+    {
+        if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE) == -1)
+            return false;
+    }
+    wxConstCast(this, wxSoundBackendSDL)->m_initialized = true;
+    wxLogTrace(_T("sound"), _T("initialized SDL audio subsystem"));
+    return true;
+}
+
+extern "C" void wx_sdl_audio_callback(void *userdata, Uint8 *stream, int len)
+{
+    wxSoundBackendSDL *bk = (wxSoundBackendSDL*)userdata;
+    bk->FillAudioBuffer(stream, len);
+}
+
+void wxSoundBackendSDL::FillAudioBuffer(Uint8 *stream, int len)
+{
+    wxSoundBackendSDLQueueEntry *e = m_queue.front();
+    if (!e->m_finished)
+    {
+        // finished playing the sample
+        if (e->m_pos == e->m_data->m_dataBytes)
+        {
+            e->m_finished = true;
+            m_playing = false;
+            wxSoundBackendSDLNotification event;
+            m_evtHandler->AddPendingEvent(event);
+        }
+        // still something to play
+        else
+        {
+            unsigned size = ((len + e->m_pos) < e->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;
+            len -= size;
+            stream += size;
+        }
+    }
+    // the sample doesn't play, fill the buffer with silence and wait for
+    // the main thread to shut the playback down:
+    if (len > 0)
+    {
+        if (e->m_loop)
+        {
+            e->m_pos = 0;
+            FillAudioBuffer(stream, len);
+            return;
+        }
+        else
+        {
+            memset(stream, e->m_spec.silence, len);
+        }
+    }
+}
+
+bool wxSoundBackendSDL::Play(wxSoundData *data, unsigned flags)
+{
+    data->IncRef();
+
+    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;
+    
+    if (data->m_bitsPerSample == 8)
+        e->m_spec.format = AUDIO_U8;
+    else if (data->m_bitsPerSample == 16)
+        e->m_spec.format = AUDIO_S16LSB;
+    else
+        return false;
+
+    m_queue.push_back(e);
+    wxLogTrace(_T("sound"), _T("queued sample %p for playback"), e);
+
+    if (!PlayNextSampleInQueue())
+        return false;
+
+    if (!(flags & wxSOUND_ASYNC))
+    {
+        wxLogTrace(_T("sound"), _T("waiting for sample to finish"));
+        while (!m_queue.empty() && m_queue.front() == e && !e->m_finished)
+        {
+#if wxUSE_THREADS
+            // give the playback thread a chance to add event to pending
+            // events queue, release GUI lock temporarily:
+            if (wxThread::IsMain())
+                wxMutexGuiLeave();
+#endif
+            wxUsleep(10);
+#if wxUSE_THREADS
+            if (wxThread::IsMain())
+                wxMutexGuiEnter();
+#endif
+        }
+        wxLogTrace(_T("sound"), _T("sample finished"));
+    }
+   
+    return true;
+}
+    
+bool wxSoundBackendSDL::PlayNextSampleInQueue()
+{
+    bool status = true;
+
+    SDL_LockAudio();
+
+    if (!m_evtHandler)
+        m_evtHandler = new wxSoundBackendSDLEvtHandler(this);
+    
+    if (!m_playing && !m_queue.empty())
+    {
+        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);
+        }
+    }
+    
+    SDL_UnlockAudio();
+
+    return status;
+}
+
+extern "C" wxSoundBackend *wxCreateSoundBackendSDL()
+{
+    return new wxSoundBackendSDL();
+}
+
+#endif // wxUSE_WAVE && wxUSE_LIBSDL
diff --git a/src/unix/wave.cpp b/src/unix/wave.cpp
deleted file mode 100644 (file)
index 36c9c09..0000000
+++ /dev/null
@@ -1,231 +0,0 @@
-/////////////////////////////////////////////////////////////////////////////
-// Name:        wave.cpp
-// Purpose:     wxWave
-// Author:      Marcel Rasche
-// Modified by:
-// Created:     25/10/98
-// RCS-ID:      $Id$
-// Copyright:   (c) Julian Smart
-// Licence:    wxWindows licence
-/////////////////////////////////////////////////////////////////////////////
-
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#pragma implementation "wave.h"
-#endif
-
-// for compilers that support precompilation, includes "wx.h".
-#include "wx/wxprec.h"
-
-#include "wx/setup.h"
-
-#if wxUSE_WAVE
-
-// For compilers that support precompilation, includes "wx.h".
-#include "wx/wxprec.h"
-
-#if defined(__BORLANDC__)
-#pragma hdrstop
-#endif
-
-#include <stdio.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <linux/soundcard.h>
-
-#ifndef WX_PRECOMP
-#include "wx/wx.h"
-#endif
-
-#include "wx/file.h"
-#include "wx/wave.h"
-
-//-----------------------------------------------------------------
-// wxWave
-//-----------------------------------------------------------------
-
-wxWave::wxWave()
-  : m_waveData(NULL), m_waveLength(0), m_isResource(FALSE)
-{
-}
-
-wxWave::wxWave(const wxString& sFileName, bool isResource)
-  : m_waveData(NULL), m_waveLength(0), m_isResource(isResource)
-{
-    Create(sFileName, isResource);
-}
-
-wxWave::wxWave(int size, const wxByte* data)
-  : m_waveData(NULL), m_waveLength(0), m_isResource(FALSE)
-{
-    Create(size, data);
-}
-
-wxWave::~wxWave()
-{
-    Free();
-}
-
-bool wxWave::Create(const wxString& fileName, bool isResource)
-{
-    Free();
-  
-    if (isResource)
-    {
-        //  todo
-        return (m_waveData ? TRUE : FALSE);
-    }
-    else
-    {
-        m_isResource = FALSE;
-
-        wxFile fileWave;
-        if (!fileWave.Open(fileName, wxFile::read))
-       {
-            return FALSE;
-       }
-
-        m_waveLength = (int) fileWave.Length();
-    
-        m_waveData = new wxByte[m_waveLength];
-        if (!m_waveData)
-       {
-            return FALSE;
-       }
-    
-        fileWave.Read(m_waveData, m_waveLength);
-    
-        return TRUE;
-    }
-}
-
-bool wxWave::Create(int size, const wxByte* data)
-{
-    Free();
-    m_isResource = FALSE;
-    m_waveLength=size;
-    m_waveData = new wxByte[size];
-    if (!m_waveData)
-    {
-        return FALSE;
-    }
-  
-    for (int i=0; i<size; i++) m_waveData[i] = data[i];
-    
-    return TRUE;
-}
-
-bool wxWave::Play(bool async, bool looped)
-{
-    if (!IsOk()) return FALSE;
-
-    int dev = OpenDSP();
-    
-    if (dev<0) return FALSE;
-
-    ioctl(dev,SNDCTL_DSP_SYNC,0);
-  
-    bool play=TRUE;
-    int i,l=0;
-    do
-    {
-        i= (int)((l+m_DSPblkSize) < m_sizeData ? m_DSPblkSize : (m_sizeData-l));
-        if ( write(dev,&m_data[l],i) != i )
-       {
-           play=FALSE;
-       }
-        l +=i;
-    } while (play == TRUE && l<m_sizeData);
-
-    close(dev);
-    return TRUE;
-}
-
-bool wxWave::Free()
-{
-    if (m_waveData)
-    {
-        delete[] m_waveData;
-        m_waveData = NULL;
-        m_waveLength = 0;
-        return TRUE;
-    }
-  
-    return FALSE;
-}
-
-typedef  struct
-{ 
-  unsigned long   uiSize; 
-  unsigned short  uiFormatTag;
-  unsigned short  uiChannels;
-  unsigned long   ulSamplesPerSec;
-  unsigned long   ulAvgBytesPerSec;
-  unsigned short  uiBlockAlign;
-  unsigned short  uiBitsPerSample;
-} WAVEFORMAT;    
-
-#define MONO 1  // and stereo is 2 by wav format
-#define WAVE_FORMAT_PCM 1
-#define WAVE_INDEX  8
-#define FMT_INDEX   12
-
-int wxWave::OpenDSP(void)
-{
-  WAVEFORMAT  waveformat;
-  int dev=-1;
-  unsigned long ul;
-
-  if (m_waveLength < (int)(32+sizeof(WAVEFORMAT)))
-    return -1;
-
-  memcpy(&waveformat,&m_waveData[FMT_INDEX+4],sizeof(WAVEFORMAT));
-
-  if (memcmp(m_waveData, "RIFF", 4) != 0)
-    return -1;
-  if (memcmp(&m_waveData[WAVE_INDEX], "WAVE", 4) != 0)
-    return -1;
-  if (memcmp(&m_waveData[FMT_INDEX], "fmt ", 4) != 0)
-    return -1;
-  if (memcmp(&m_waveData[FMT_INDEX+waveformat.uiSize+8], "data", 4) != 0)
-    return -1;
-  memcpy(&ul,&m_waveData[FMT_INDEX+waveformat.uiSize+12],4);
-  m_sizeData=ul;
-  if ((int)(m_sizeData+FMT_INDEX+waveformat.uiSize+16) != m_waveLength)
-    return -1;
-  m_data=(char *)(&m_waveData[FMT_INDEX+waveformat.uiSize+8]);
-
-  if (waveformat.uiFormatTag != WAVE_FORMAT_PCM)
-    return -1;
-  if (waveformat.ulSamplesPerSec != waveformat.ulAvgBytesPerSec/waveformat.uiBlockAlign)
-    return -1;
-   
-  if ((dev = open(AUDIODEV,O_RDWR,0)) <0)
-    return -1;
-  
-  if (!InitDSP(dev,(int)waveformat.uiBitsPerSample,waveformat.uiChannels == MONO ? 0:1,waveformat.ulSamplesPerSec))
-    {
-      close(dev);
-      return -1;
-    }
-
-  return dev;
-}
-
-bool wxWave::InitDSP(int dev, int iDataBits, int iChannel,unsigned long ulSamplingRate)
-{
-  if ( ioctl(dev,SNDCTL_DSP_GETBLKSIZE,&m_DSPblkSize) < 0 ) 
-    return FALSE;
-  if (m_DSPblkSize < 4096 || m_DSPblkSize > 65536)
-    return FALSE;
-  if ( ioctl(dev,SNDCTL_DSP_SAMPLESIZE,&iDataBits) < 0 )
-    return FALSE;
-  if ( ioctl(dev,SNDCTL_DSP_STEREO,&iChannel) < 0 )
-    return FALSE;
-  if ( ioctl(dev,SNDCTL_DSP_SPEED,&ulSamplingRate) < 0 ) 
-    return FALSE;
-  
-  return TRUE;
-}
-#endif
-