X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f156e20c8e21594f901bfce15db4459208ec9571..04fa04d8067d235ab45b5bc05b65f0679634b541:/src/unix/sound.cpp diff --git a/src/unix/sound.cpp b/src/unix/sound.cpp index 30ce40656f..500e861718 100644 --- a/src/unix/sound.cpp +++ b/src/unix/sound.cpp @@ -1,26 +1,19 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: sound.cpp +// Name: src/unix/sound.cpp // Purpose: wxSound // Author: Marcel Rasche, Vaclav Slavik // Modified by: // Created: 25/10/98 // RCS-ID: $Id$ // Copyright: (c) Julian Smart, Open Source Applications Foundation -// Licence: wxWindows licence +// 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 + #pragma hdrstop #endif #if wxUSE_SOUND @@ -38,11 +31,11 @@ #include "wx/event.h" #include "wx/intl.h" #include "wx/log.h" + #include "wx/module.h" #endif #include "wx/thread.h" #include "wx/file.h" -#include "wx/module.h" #include "wx/sound.h" #include "wx/dynlib.h" @@ -55,7 +48,7 @@ static wxMutex gs_soundMutex; // ---------------------------------------------------------------------------- // wxSoundData // ---------------------------------------------------------------------------- - + void wxSoundData::IncRef() { #if wxUSE_THREADS @@ -111,7 +104,7 @@ public: class wxSoundBackendOSS : public wxSoundBackend { public: - wxString GetName() const { return _T("Open Sound System"); } + wxString GetName() const { return wxT("Open Sound System"); } int GetPriority() const { return 10; } bool IsAvailable() const; bool HasNativeAsyncPlayback() const { return false; } @@ -122,10 +115,10 @@ public: private: int OpenDSP(const wxSoundData *data); - bool InitDSP(int dev, int iDataBits, int iChannel, - unsigned long ulSamplingRate); - + bool InitDSP(int dev, const wxSoundData *data); + int m_DSPblkSize; // Size of the DSP buffer + bool m_needConversion; }; bool wxSoundBackendOSS::IsAvailable() const @@ -142,12 +135,12 @@ bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags, volatile wxSoundPlaybackStatus *status) { int dev = OpenDSP(data); - + if (dev < 0) return false; ioctl(dev, SNDCTL_DSP_SYNC, 0); - + do { bool play = true; @@ -159,13 +152,13 @@ bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags, { if (status->m_stopRequested) { - wxLogTrace(_T("sound"), _T("playback stopped")); + wxLogTrace(wxT("sound"), wxT("playback stopped")); close(dev); return true; } i= (int)((l + m_DSPblkSize) < datasize ? - m_DSPblkSize : (datasize - l)); + m_DSPblkSize : (datasize - l)); if (write(dev, &data->m_data[l], i) != i) { play = false; @@ -175,21 +168,17 @@ bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags, } 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)) + + if (!InitDSP(dev, data) || m_needConversion) { close(dev); return -1; @@ -198,20 +187,77 @@ int wxSoundBackendOSS::OpenDSP(const wxSoundData *data) return dev; } -bool wxSoundBackendOSS::InitDSP(int dev, int iDataBits, int iChannel, - unsigned long ulSamplingRate) + +bool wxSoundBackendOSS::InitDSP(int dev, const wxSoundData *data) { - if (ioctl(dev, SNDCTL_DSP_GETBLKSIZE, &m_DSPblkSize) < 0) - return false; - wxLogTrace(_T("sound"), _T("OSS block size: %i"), m_DSPblkSize); - if (m_DSPblkSize < 4096 || m_DSPblkSize > 65536) + unsigned tmp; + + // Reset the dsp + if (ioctl(dev, SNDCTL_DSP_RESET, 0) < 0) + { + wxLogTrace(wxT("sound"), wxT("unable to reset dsp")); return false; - if (ioctl(dev, SNDCTL_DSP_SAMPLESIZE, &iDataBits) < 0) + } + + m_needConversion = false; + + tmp = data->m_bitsPerSample; + if (ioctl(dev, SNDCTL_DSP_SAMPLESIZE, &tmp) < 0) + { + wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_SAMPLESIZE)")); return false; - if (ioctl(dev, SNDCTL_DSP_STEREO, &iChannel) < 0) + } + if (tmp != data->m_bitsPerSample) + { + wxLogTrace(wxT("sound"), + wxT("Unable to set DSP sample size to %d (wants %d)"), + data->m_bitsPerSample, tmp); + m_needConversion = true; + } + + unsigned stereo = data->m_channels == 1 ? 0 : 1; + tmp = stereo; + if (ioctl(dev, SNDCTL_DSP_STEREO, &tmp) < 0) + { + wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_STEREO)")); return false; - if (ioctl(dev, SNDCTL_DSP_SPEED, &ulSamplingRate) < 0) + } + if (tmp != stereo) + { + wxLogTrace(wxT("sound"), wxT("Unable to set DSP to %s."), stereo? wxT("stereo"):wxT("mono")); + m_needConversion = true; + } + + tmp = data->m_samplingRate; + if (ioctl(dev, SNDCTL_DSP_SPEED, &tmp) < 0) + { + wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_SPEED)")); + return false; + } + if (tmp != data->m_samplingRate) + { + // If the rate the sound card is using is not within 1% of what the + // data specified then override the data setting. The only reason not + // to always override this is because of clock-rounding + // problems. Sound cards will sometimes use things like 44101 when you + // ask for 44100. No need overriding this and having strange output + // file rates for something that we can't hear anyways. + if (data->m_samplingRate - tmp > (tmp * .01) || + tmp - data->m_samplingRate > (tmp * .01)) { + wxLogTrace(wxT("sound"), + wxT("Unable to set DSP sampling rate to %d (wants %d)"), + data->m_samplingRate, tmp); + m_needConversion = true; + } + } + + // Do this last because some drivers can adjust the buffer sized based on + // the sampling rate, etc. + if (ioctl(dev, SNDCTL_DSP_GETBLKSIZE, &m_DSPblkSize) < 0) + { + wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_GETBLKSIZE)")); return false; + } return true; } @@ -234,7 +280,7 @@ public: wxSoundData *data, unsigned flags) : wxThread(), m_adapt(adaptor), m_data(data), m_flags(flags) {} virtual ExitCode Entry(); - + protected: wxSoundSyncOnlyAdaptor *m_adapt; wxSoundData *m_data; @@ -250,7 +296,7 @@ class wxSoundSyncOnlyAdaptor : public wxSoundBackend public: wxSoundSyncOnlyAdaptor(wxSoundBackend *backend) : m_backend(backend), m_playing(false) {} - ~wxSoundSyncOnlyAdaptor() + virtual ~wxSoundSyncOnlyAdaptor() { delete m_backend; } @@ -298,7 +344,7 @@ wxThread::ExitCode wxSoundAsyncPlaybackThread::Entry() m_data->DecRef(); m_adapt->m_playing = false; m_adapt->m_mutexRightToPlay.Unlock(); - wxLogTrace(_T("sound"), _T("terminated async playback thread")); + wxLogTrace(wxT("sound"), wxT("terminated async playback thread")); return 0; } #endif @@ -317,7 +363,7 @@ bool wxSoundSyncOnlyAdaptor::Play(wxSoundData *data, unsigned flags, wxThread *th = new wxSoundAsyncPlaybackThread(this, data, flags); th->Create(); th->Run(); - wxLogTrace(_T("sound"), _T("launched async playback thread")); + wxLogTrace(wxT("sound"), wxT("launched async playback thread")); return true; #else wxLogError(_("Unable to play sound asynchronously.")); @@ -339,27 +385,34 @@ bool wxSoundSyncOnlyAdaptor::Play(wxSoundData *data, unsigned flags, void wxSoundSyncOnlyAdaptor::Stop() { - wxLogTrace(_T("sound"), _T("asking audio to stop")); + wxLogTrace(wxT("sound"), wxT("asking audio to stop")); + +#if wxUSE_THREADS // tell the player thread (if running) to stop playback ASAP: m_status.m_stopRequested = true; - + // acquire the mutex to be sure no sound is being played, then // release it because we don't need it for anything (the effect of this // is that calling thread will wait until playback thread reacts to // our request to interrupt playback): m_mutexRightToPlay.Lock(); m_mutexRightToPlay.Unlock(); - wxLogTrace(_T("sound"), _T("audio was stopped")); + wxLogTrace(wxT("sound"), wxT("audio was stopped")); +#endif } bool wxSoundSyncOnlyAdaptor::IsPlaying() const { +#if wxUSE_THREADS return m_status.m_playing; +#else + return false; +#endif } // ---------------------------------------------------------------------------- -// wxSound +// wxSound // ---------------------------------------------------------------------------- wxSoundBackend *wxSound::ms_backend = NULL; @@ -392,34 +445,41 @@ wxSound::~wxSound() Free(); } -bool wxSound::Create(const wxString& fileName, bool isResource) +bool wxSound::Create(const wxString& fileName, + bool WXUNUSED_UNLESS_DEBUG(isResource)) { wxASSERT_MSG( !isResource, - _T("Loading sound from resources is only supported on Windows") ); - + wxT("Loading sound from resources is only supported on Windows") ); + Free(); - + wxFile fileWave; if (!fileWave.Open(fileName, wxFile::read)) - { - return false; - } + { + return false; + } + + wxFileOffset lenOrig = fileWave.Length(); + if ( lenOrig == wxInvalidOffset ) + return false; - size_t len = fileWave.Length(); + size_t len = wx_truncate_cast(size_t, lenOrig); wxUint8 *data = new wxUint8[len]; - if (fileWave.Read(data, len) != len) + if ( fileWave.Read(data, len) != lenOrig ) { + delete [] data; wxLogError(_("Couldn't load sound data from '%s'."), fileName.c_str()); return false; } if (!LoadWAV(data, len, false)) { + delete [] data; wxLogError(_("Sound file '%s' is in unsupported format."), fileName.c_str()); return false; } - + return true; } @@ -442,27 +502,19 @@ bool wxSound::Create(int size, const wxByte* data) { // 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 (!ms_backend) { #if !wxUSE_PLUGINS ms_backend = wxCreateSoundBackendSDL(); #else wxString dllname; - dllname.Printf(_T("%s/%s"), + dllname.Printf(wxT("%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'..."), + wxT("sound_sdl"), wxDL_PLUGIN_BASE).c_str()); + wxLogTrace(wxT("sound"), + wxT("trying to load SDL plugin from '%s'..."), dllname.c_str()); wxLogNull null; ms_backendSDL = new wxDynamicLibrary(dllname, wxDL_NOW); @@ -488,14 +540,25 @@ bool wxSound::Create(int size, const wxByte* data) } #endif +#ifdef HAVE_SYS_SOUNDCARD_H + if (!ms_backend) + { + ms_backend = new wxSoundBackendOSS(); + if (!ms_backend->IsAvailable()) + { + wxDELETE(ms_backend); + } + } +#endif + if (!ms_backend) ms_backend = new wxSoundBackendNull(); if (!ms_backend->HasNativeAsyncPlayback()) ms_backend = new wxSoundSyncOnlyAdaptor(ms_backend); - wxLogTrace(_T("sound"), - _T("using backend '%s'"), ms_backend->GetName().c_str()); + wxLogTrace(wxT("sound"), + wxT("using backend '%s'"), ms_backend->GetName().c_str()); } } @@ -503,12 +566,11 @@ bool wxSound::Create(int size, const wxByte* data) { if (ms_backend) { - wxLogTrace(_T("sound"), _T("unloading backend")); + wxLogTrace(wxT("sound"), wxT("unloading backend")); Stop(); - - delete ms_backend; - ms_backend = NULL; + + wxDELETE(ms_backend); #if wxUSE_LIBSDL && wxUSE_PLUGINS delete ms_backendSDL; #endif @@ -517,7 +579,7 @@ bool wxSound::Create(int size, const wxByte* data) bool wxSound::DoPlay(unsigned flags) const { - wxCHECK_MSG( IsOk(), false, _T("Attempt to play invalid wave data") ); + wxCHECK_MSG( IsOk(), false, wxT("Attempt to play invalid wave data") ); EnsureBackend(); wxSoundPlaybackStatus status; @@ -547,7 +609,7 @@ void wxSound::Free() } typedef struct -{ +{ wxUint32 uiSize; wxUint16 uiFormatTag; wxUint16 uiChannels; @@ -557,19 +619,36 @@ typedef struct 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)) + // the simplest wave file header consists of 44 bytes: + // + // 0 "RIFF" + // 4 file size - 8 + // 8 "WAVE" + // + // 12 "fmt " + // 16 chunk size | + // 20 format tag | + // 22 number of channels | + // 24 sample rate | WAVEFORMAT + // 28 average bytes per second | + // 32 bytes per frame | + // 34 bits per sample | + // + // 36 "data" + // 40 number of data bytes + // 44 (wave signal) data + // + // so check that we have at least as much + if ( length < 44 ) return false; + WAVEFORMAT waveformat; memcpy(&waveformat, &data[FMT_INDEX + 4], sizeof(WAVEFORMAT)); waveformat.uiSize = wxUINT32_SWAP_ON_BE(waveformat.uiSize); waveformat.uiFormatTag = wxUINT16_SWAP_ON_BE(waveformat.uiFormatTag); @@ -579,6 +658,14 @@ bool wxSound::LoadWAV(const wxUint8 *data, size_t length, bool copyData) waveformat.uiBlockAlign = wxUINT16_SWAP_ON_BE(waveformat.uiBlockAlign); waveformat.uiBitsPerSample = wxUINT16_SWAP_ON_BE(waveformat.uiBitsPerSample); + // get the sound data size + wxUint32 ul; + memcpy(&ul, &data[FMT_INDEX + waveformat.uiSize + 12], 4); + ul = wxUINT32_SWAP_ON_BE(ul); + + if ( length < ul + FMT_INDEX + waveformat.uiSize + 16 ) + return false; + if (memcmp(data, "RIFF", 4) != 0) return false; if (memcmp(&data[WAVE_INDEX], "WAVE", 4) != 0) @@ -587,20 +674,14 @@ bool wxSound::LoadWAV(const wxUint8 *data, size_t length, bool copyData) 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 != + + 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; @@ -616,7 +697,7 @@ bool wxSound::LoadWAV(const wxUint8 *data, size_t length, bool copyData) else m_data->m_dataWithHeader = (wxUint8*)data; - m_data->m_data = + m_data->m_data = (&m_data->m_dataWithHeader[FMT_INDEX + waveformat.uiSize + 8]); return true;