1 ///////////////////////////////////////////////////////////////////////////// 
   4 // Author:      Marcel Rasche, Vaclav Slavik 
   8 // Copyright:   (c) Julian Smart, Open Source Applications Foundation 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  13 #pragma implementation "sound.h" 
  14 #pragma implementation "soundbase.h" 
  17 // for compilers that support precompilation, includes "wx.h". 
  18 #include "wx/wxprec.h" 
  22 #if defined(__BORLANDC__) 
  31 #include <sys/ioctl.h> 
  33 #ifdef HAVE_SYS_SOUNDCARD_H 
  34 #include <sys/soundcard.h> 
  43 #include "wx/thread.h" 
  45 #include "wx/module.h" 
  47 #include "wx/dynlib.h" 
  51 // mutex for all wxSound's synchronization 
  52 static wxMutex gs_soundMutex
; 
  55 // ---------------------------------------------------------------------------- 
  57 // ---------------------------------------------------------------------------- 
  59 void wxSoundData::IncRef() 
  62     wxMutexLocker 
locker(gs_soundMutex
); 
  67 void wxSoundData::DecRef() 
  70     wxMutexLocker 
locker(gs_soundMutex
); 
  76 wxSoundData::~wxSoundData() 
  78     delete[] m_dataWithHeader
; 
  82 // ---------------------------------------------------------------------------- 
  83 // wxSoundBackendNull, used in absence of audio API or card 
  84 // ---------------------------------------------------------------------------- 
  86 class wxSoundBackendNull 
: public wxSoundBackend
 
  89     wxString 
GetName() const { return _("No sound"); } 
  90     int GetPriority() const { return 0; } 
  91     bool IsAvailable() const { return true; } 
  92     bool HasNativeAsyncPlayback() const { return true; } 
  93     bool Play(wxSoundData 
*WXUNUSED(data
), unsigned WXUNUSED(flags
), 
  94               volatile wxSoundPlaybackStatus 
*WXUNUSED(status
)) 
  97     bool IsPlaying() const { return false; } 
 101 // ---------------------------------------------------------------------------- 
 102 // wxSoundBackendOSS, for Linux 
 103 // ---------------------------------------------------------------------------- 
 105 #ifdef HAVE_SYS_SOUNDCARD_H 
 108 #define AUDIODEV   "/dev/dsp"    // Default path for audio device 
 111 class wxSoundBackendOSS 
: public wxSoundBackend
 
 114     wxString 
GetName() const { return _T("Open Sound System"); } 
 115     int GetPriority() const { return 10; } 
 116     bool IsAvailable() const; 
 117     bool HasNativeAsyncPlayback() const { return false; } 
 118     bool Play(wxSoundData 
*data
, unsigned flags
, 
 119               volatile wxSoundPlaybackStatus 
*status
); 
 121     bool IsPlaying() const { return false; } 
 124     int OpenDSP(const wxSoundData 
*data
); 
 125     bool InitDSP(int dev
, const wxSoundData 
*data
); 
 127     int m_DSPblkSize
;        // Size of the DSP buffer 
 128     bool m_needConversion
; 
 131 bool wxSoundBackendOSS::IsAvailable() const 
 134     fd 
= open(AUDIODEV
, O_WRONLY 
| O_NONBLOCK
); 
 141 bool wxSoundBackendOSS::Play(wxSoundData 
*data
, unsigned flags
, 
 142                              volatile wxSoundPlaybackStatus 
*status
) 
 144     int dev 
= OpenDSP(data
); 
 149     ioctl(dev
, SNDCTL_DSP_SYNC
, 0); 
 156         size_t datasize 
= data
->m_dataBytes
; 
 160             if (status
->m_stopRequested
) 
 162                 wxLogTrace(_T("sound"), _T("playback stopped")); 
 167             i
= (int)((l 
+ m_DSPblkSize
) < datasize 
? 
 168                      m_DSPblkSize 
: (datasize 
- l
)); 
 169             if (write(dev
, &data
->m_data
[l
], i
) != i
) 
 174         } while (play 
&& l 
< datasize
); 
 175     } while (flags 
& wxSOUND_LOOP
); 
 181 int wxSoundBackendOSS::OpenDSP(const wxSoundData 
*data
) 
 185     if ((dev 
= open(AUDIODEV
, O_WRONLY
, 0)) <0) 
 188     if (!InitDSP(dev
, data
) || m_needConversion
) 
 198 bool wxSoundBackendOSS::InitDSP(int dev
, const wxSoundData 
*data
) 
 203     if (ioctl(dev
, SNDCTL_DSP_RESET
, 0) < 0) 
 205         wxLogTrace(_T("sound"), _T("unable to reset dsp")); 
 209     m_needConversion 
= false; 
 211     tmp 
= data
->m_bitsPerSample
; 
 212     if (ioctl(dev
, SNDCTL_DSP_SAMPLESIZE
, &tmp
) < 0) 
 214         wxLogTrace(_T("sound"), _T("IOCTL failure (SNDCTL_DSP_SAMPLESIZE)")); 
 217     if (tmp 
!= data
->m_bitsPerSample
) 
 219         wxLogTrace(_T("sound"), 
 220                    _T("Unable to set DSP sample size to %d (wants %d)"), 
 221                    data
->m_bitsPerSample
, tmp
); 
 222         m_needConversion 
= true; 
 225     unsigned stereo 
= data
->m_channels 
== 1 ? 0 : 1; 
 227     if (ioctl(dev
, SNDCTL_DSP_STEREO
, &tmp
) < 0) 
 229         wxLogTrace(_T("sound"), _T("IOCTL failure (SNDCTL_DSP_STEREO)")); 
 234         wxLogTrace(_T("sound"), _T("Unable to set DSP to %s."), stereo
?  _T("stereo"):_T("mono")); 
 235         m_needConversion 
= true; 
 238     tmp 
= data
->m_samplingRate
; 
 239     if (ioctl(dev
, SNDCTL_DSP_SPEED
, &tmp
) < 0) 
 241         wxLogTrace(_T("sound"), _T("IOCTL failure (SNDCTL_DSP_SPEED)")); 
 244     if (tmp 
!= data
->m_samplingRate
) 
 246         // If the rate the sound card is using is not within 1% of what the 
 247         // data specified then override the data setting.  The only reason not 
 248         // to always override this is because of clock-rounding 
 249         // problems. Sound cards will sometimes use things like 44101 when you 
 250         // ask for 44100.  No need overriding this and having strange output 
 251         // file rates for something that we can't hear anyways. 
 252         if (data
->m_samplingRate 
- tmp 
> (tmp 
* .01) || 
 253             tmp 
- data
->m_samplingRate 
> (tmp 
* .01)) { 
 254             wxLogTrace(_T("sound"), 
 255                        _T("Unable to set DSP sampling rate to %d (wants %d)"), 
 256                        data
->m_samplingRate
, tmp
); 
 257             m_needConversion 
= true; 
 261     // Do this last because some drivers can adjust the buffer sized based on 
 262     // the sampling rate, etc. 
 263     if (ioctl(dev
, SNDCTL_DSP_GETBLKSIZE
, &m_DSPblkSize
) < 0) 
 265         wxLogTrace(_T("sound"), _T("IOCTL failure (SNDCTL_DSP_GETBLKSIZE)")); 
 271 #endif // HAVE_SYS_SOUNDCARD_H 
 273 // ---------------------------------------------------------------------------- 
 274 // wxSoundSyncOnlyAdaptor 
 275 // ---------------------------------------------------------------------------- 
 279 class wxSoundSyncOnlyAdaptor
; 
 281 // this class manages asynchronous playback of audio if the backend doesn't 
 282 // support it natively (e.g. OSS backend) 
 283 class wxSoundAsyncPlaybackThread 
: public wxThread
 
 286     wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor 
*adaptor
, 
 287                               wxSoundData 
*data
, unsigned flags
) 
 288         : wxThread(), m_adapt(adaptor
), m_data(data
), m_flags(flags
) {} 
 289     virtual ExitCode 
Entry(); 
 292     wxSoundSyncOnlyAdaptor 
*m_adapt
; 
 297 #endif // wxUSE_THREADS 
 299 // This class turns wxSoundBackend that doesn't support asynchronous playback 
 300 // into one that does 
 301 class wxSoundSyncOnlyAdaptor 
: public wxSoundBackend
 
 304     wxSoundSyncOnlyAdaptor(wxSoundBackend 
*backend
) 
 305         : m_backend(backend
), m_playing(false) {} 
 306     ~wxSoundSyncOnlyAdaptor() 
 310     wxString 
GetName() const 
 312         return m_backend
->GetName(); 
 314     int GetPriority() const 
 316         return m_backend
->GetPriority(); 
 318     bool IsAvailable() const 
 320         return m_backend
->IsAvailable(); 
 322     bool HasNativeAsyncPlayback() const 
 326     bool Play(wxSoundData 
*data
, unsigned flags
, 
 327               volatile wxSoundPlaybackStatus 
*status
); 
 329     bool IsPlaying() const; 
 332     friend class wxSoundAsyncPlaybackThread
; 
 334     wxSoundBackend 
*m_backend
; 
 337     // player thread holds this mutex and releases it after it finishes 
 338     // playing, so that the main thread knows when it can play sound 
 339     wxMutex m_mutexRightToPlay
; 
 340     wxSoundPlaybackStatus m_status
; 
 346 wxThread::ExitCode 
wxSoundAsyncPlaybackThread::Entry() 
 348     m_adapt
->m_backend
->Play(m_data
, m_flags 
& ~wxSOUND_ASYNC
, 
 352     m_adapt
->m_playing 
= false; 
 353     m_adapt
->m_mutexRightToPlay
.Unlock(); 
 354     wxLogTrace(_T("sound"), _T("terminated async playback thread")); 
 359 bool wxSoundSyncOnlyAdaptor::Play(wxSoundData 
*data
, unsigned flags
, 
 360                                   volatile wxSoundPlaybackStatus 
*status
) 
 363     if (flags 
& wxSOUND_ASYNC
) 
 366         m_mutexRightToPlay
.Lock(); 
 367         m_status
.m_playing 
= true; 
 368         m_status
.m_stopRequested 
= false; 
 370         wxThread 
*th 
= new wxSoundAsyncPlaybackThread(this, data
, flags
); 
 373         wxLogTrace(_T("sound"), _T("launched async playback thread")); 
 376         wxLogError(_("Unable to play sound asynchronously.")); 
 383         m_mutexRightToPlay
.Lock(); 
 385         bool rv 
= m_backend
->Play(data
, flags
, status
); 
 387         m_mutexRightToPlay
.Unlock(); 
 393 void wxSoundSyncOnlyAdaptor::Stop() 
 395     wxLogTrace(_T("sound"), _T("asking audio to stop")); 
 398     // tell the player thread (if running) to stop playback ASAP: 
 399     m_status
.m_stopRequested 
= true; 
 401     // acquire the mutex to be sure no sound is being played, then 
 402     // release it because we don't need it for anything (the effect of this 
 403     // is that calling thread will wait until playback thread reacts to 
 404     // our request to interrupt playback): 
 405     m_mutexRightToPlay
.Lock(); 
 406     m_mutexRightToPlay
.Unlock(); 
 407     wxLogTrace(_T("sound"), _T("audio was stopped")); 
 411 bool wxSoundSyncOnlyAdaptor::IsPlaying() const 
 414     return m_status
.m_playing
; 
 421 // ---------------------------------------------------------------------------- 
 423 // ---------------------------------------------------------------------------- 
 425 wxSoundBackend 
*wxSound::ms_backend 
= NULL
; 
 427 // FIXME - temporary, until we have plugins architecture 
 430         wxDynamicLibrary 
*wxSound::ms_backendSDL 
= NULL
; 
 432         extern "C" wxSoundBackend 
*wxCreateSoundBackendSDL(); 
 436 wxSound::wxSound() : m_data(NULL
) 
 440 wxSound::wxSound(const wxString
& sFileName
, bool isResource
) : m_data(NULL
) 
 442     Create(sFileName
, isResource
); 
 445 wxSound::wxSound(int size
, const wxByte
* data
) : m_data(NULL
) 
 455 bool wxSound::Create(const wxString
& fileName
, bool isResource
) 
 457     wxASSERT_MSG( !isResource
, 
 458              _T("Loading sound from resources is only supported on Windows") ); 
 463     if (!fileWave
.Open(fileName
, wxFile::read
)) 
 468     wxFileOffset len 
= fileWave
.Length(); 
 469     wxUint8 
*data 
= new wxUint8
[len
]; 
 470     if (fileWave
.Read(data
, len
) != len
) 
 472         wxLogError(_("Couldn't load sound data from '%s'."), fileName
.c_str()); 
 476     if (!LoadWAV(data
, len
, false)) 
 478         wxLogError(_("Sound file '%s' is in unsupported format."), 
 486 bool wxSound::Create(int size
, const wxByte
* data
) 
 488     wxASSERT( data 
!= NULL 
); 
 491     if (!LoadWAV(data
, size
, true)) 
 493         wxLogError(_("Sound data are in unsupported format.")); 
 499 /*static*/ void wxSound::EnsureBackend() 
 503         // FIXME -- make this fully dynamic when plugins architecture is in 
 509             ms_backend 
= wxCreateSoundBackendSDL(); 
 512             dllname
.Printf(_T("%s/%s"), 
 513                 wxDynamicLibrary::GetPluginsDirectory().c_str(), 
 514                 wxDynamicLibrary::CanonicalizePluginName( 
 515                     _T("sound_sdl"), wxDL_PLUGIN_BASE
).c_str()); 
 516             wxLogTrace(_T("sound"), 
 517                        _T("trying to load SDL plugin from '%s'..."), 
 520             ms_backendSDL 
= new wxDynamicLibrary(dllname
, wxDL_NOW
); 
 521             if (!ms_backendSDL
->IsLoaded()) 
 523                 wxDELETE(ms_backendSDL
); 
 527                 typedef wxSoundBackend 
*(*wxCreateSoundBackend_t
)(); 
 528                 wxDYNLIB_FUNCTION(wxCreateSoundBackend_t
, 
 529                                   wxCreateSoundBackendSDL
, *ms_backendSDL
); 
 530                 if (pfnwxCreateSoundBackendSDL
) 
 532                     ms_backend 
= (*pfnwxCreateSoundBackendSDL
)(); 
 536             if (ms_backend 
&& !ms_backend
->IsAvailable()) 
 538                 wxDELETE(ms_backend
); 
 543 #ifdef HAVE_SYS_SOUNDCARD_H 
 546             ms_backend 
= new wxSoundBackendOSS(); 
 547             if (!ms_backend
->IsAvailable()) 
 549                 wxDELETE(ms_backend
); 
 555             ms_backend 
= new wxSoundBackendNull(); 
 557         if (!ms_backend
->HasNativeAsyncPlayback()) 
 558             ms_backend 
= new wxSoundSyncOnlyAdaptor(ms_backend
); 
 560         wxLogTrace(_T("sound"), 
 561                    _T("using backend '%s'"), ms_backend
->GetName().c_str()); 
 565 /*static*/ void wxSound::UnloadBackend() 
 569         wxLogTrace(_T("sound"), _T("unloading backend")); 
 575 #if wxUSE_LIBSDL && wxUSE_PLUGINS 
 576         delete ms_backendSDL
; 
 581 bool wxSound::DoPlay(unsigned flags
) const 
 583     wxCHECK_MSG( IsOk(), false, _T("Attempt to play invalid wave data") ); 
 586     wxSoundPlaybackStatus status
; 
 587     status
.m_playing 
= true; 
 588     status
.m_stopRequested 
= false; 
 589     return ms_backend
->Play(m_data
, flags
, &status
); 
 592 /*static*/ void wxSound::Stop() 
 598 /*static*/ bool wxSound::IsPlaying() 
 601         return ms_backend
->IsPlaying(); 
 615     wxUint16      uiFormatTag
; 
 617     wxUint32      ulSamplesPerSec
; 
 618     wxUint32      ulAvgBytesPerSec
; 
 619     wxUint16      uiBlockAlign
; 
 620     wxUint16      uiBitsPerSample
; 
 623 #define WAVE_FORMAT_PCM  1 
 627 bool wxSound::LoadWAV(const wxUint8 
*data
, size_t length
, bool copyData
) 
 629     WAVEFORMAT waveformat
; 
 632     if (length 
< 32 + sizeof(WAVEFORMAT
)) 
 635     memcpy(&waveformat
, &data
[FMT_INDEX 
+ 4], sizeof(WAVEFORMAT
)); 
 636     waveformat
.uiSize 
= wxUINT32_SWAP_ON_BE(waveformat
.uiSize
); 
 637     waveformat
.uiFormatTag 
= wxUINT16_SWAP_ON_BE(waveformat
.uiFormatTag
); 
 638     waveformat
.uiChannels 
= wxUINT16_SWAP_ON_BE(waveformat
.uiChannels
); 
 639     waveformat
.ulSamplesPerSec 
= wxUINT32_SWAP_ON_BE(waveformat
.ulSamplesPerSec
); 
 640     waveformat
.ulAvgBytesPerSec 
= wxUINT32_SWAP_ON_BE(waveformat
.ulAvgBytesPerSec
); 
 641     waveformat
.uiBlockAlign 
= wxUINT16_SWAP_ON_BE(waveformat
.uiBlockAlign
); 
 642     waveformat
.uiBitsPerSample 
= wxUINT16_SWAP_ON_BE(waveformat
.uiBitsPerSample
); 
 644     if (memcmp(data
, "RIFF", 4) != 0) 
 646     if (memcmp(&data
[WAVE_INDEX
], "WAVE", 4) != 0) 
 648     if (memcmp(&data
[FMT_INDEX
], "fmt ", 4) != 0) 
 650     if (memcmp(&data
[FMT_INDEX 
+ waveformat
.uiSize 
+ 8], "data", 4) != 0) 
 652     memcpy(&ul
,&data
[FMT_INDEX 
+ waveformat
.uiSize 
+ 12], 4); 
 653     ul 
= wxUINT32_SWAP_ON_BE(ul
); 
 655     //WAS: if (ul + FMT_INDEX + waveformat.uiSize + 16 != length) 
 656     if (ul 
+ FMT_INDEX 
+ waveformat
.uiSize 
+ 16 > length
) 
 659     if (waveformat
.uiFormatTag 
!= WAVE_FORMAT_PCM
) 
 662     if (waveformat
.ulSamplesPerSec 
!= 
 663         waveformat
.ulAvgBytesPerSec 
/ waveformat
.uiBlockAlign
) 
 666     m_data 
= new wxSoundData
; 
 667     m_data
->m_channels 
= waveformat
.uiChannels
; 
 668     m_data
->m_samplingRate 
= waveformat
.ulSamplesPerSec
; 
 669     m_data
->m_bitsPerSample 
= waveformat
.uiBitsPerSample
; 
 670     m_data
->m_samples 
= ul 
/ (m_data
->m_channels 
* m_data
->m_bitsPerSample 
/ 8); 
 671     m_data
->m_dataBytes 
= ul
; 
 675         m_data
->m_dataWithHeader 
= new wxUint8
[length
]; 
 676         memcpy(m_data
->m_dataWithHeader
, data
, length
); 
 679         m_data
->m_dataWithHeader 
= (wxUint8
*)data
; 
 682         (&m_data
->m_dataWithHeader
[FMT_INDEX 
+ waveformat
.uiSize 
+ 8]); 
 688 // ---------------------------------------------------------------------------- 
 689 // wxSoundCleanupModule 
 690 // ---------------------------------------------------------------------------- 
 692 class wxSoundCleanupModule
: public wxModule
 
 695     bool OnInit() { return true; } 
 696     void OnExit() { wxSound::UnloadBackend(); } 
 697     DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule
) 
 700 IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule
, wxModule
)