added support for async playback to Unix implementation of wxSound, implemented SDL backend for playback if OSS is not available, fixed OSS to work on non-Linux Unices
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@25455 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
136
include/wx/unix/sound.h
Normal file
136
include/wx/unix/sound.h
Normal file
@@ -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
|
@@ -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
|
|
||||||
|
|
505
src/unix/sound.cpp
Normal file
505
src/unix/sound.cpp
Normal file
@@ -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
|
334
src/unix/sound_sdl.cpp
Normal file
334
src/unix/sound_sdl.cpp
Normal file
@@ -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
|
@@ -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
|
|
||||||
|
|
Reference in New Issue
Block a user