1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/unix/sound.cpp 
   4 // Author:      Marcel Rasche, Vaclav Slavik 
   8 // Copyright:   (c) Julian Smart, Open Source Applications Foundation 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // for compilers that support precompilation, includes "wx.h". 
  13 #include "wx/wxprec.h" 
  15 #if defined(__BORLANDC__) 
  24 #include <sys/ioctl.h> 
  26 #ifdef HAVE_SYS_SOUNDCARD_H 
  27 #include <sys/soundcard.h> 
  34     #include "wx/module.h" 
  37 #include "wx/thread.h" 
  40 #include "wx/dynlib.h" 
  44 // mutex for all wxSound's synchronization 
  45 static wxMutex gs_soundMutex
; 
  48 // ---------------------------------------------------------------------------- 
  50 // ---------------------------------------------------------------------------- 
  52 void wxSoundData::IncRef() 
  55     wxMutexLocker 
locker(gs_soundMutex
); 
  60 void wxSoundData::DecRef() 
  63     wxMutexLocker 
locker(gs_soundMutex
); 
  69 wxSoundData::~wxSoundData() 
  71     delete[] m_dataWithHeader
; 
  75 // ---------------------------------------------------------------------------- 
  76 // wxSoundBackendNull, used in absence of audio API or card 
  77 // ---------------------------------------------------------------------------- 
  79 class wxSoundBackendNull 
: public wxSoundBackend
 
  82     wxString 
GetName() const { return _("No sound"); } 
  83     int GetPriority() const { return 0; } 
  84     bool IsAvailable() const { return true; } 
  85     bool HasNativeAsyncPlayback() const { return true; } 
  86     bool Play(wxSoundData 
*WXUNUSED(data
), unsigned WXUNUSED(flags
), 
  87               volatile wxSoundPlaybackStatus 
*WXUNUSED(status
)) 
  90     bool IsPlaying() const { return false; } 
  94 // ---------------------------------------------------------------------------- 
  95 // wxSoundBackendOSS, for Linux 
  96 // ---------------------------------------------------------------------------- 
  98 #ifdef HAVE_SYS_SOUNDCARD_H 
 101 #define AUDIODEV   "/dev/dsp"    // Default path for audio device 
 104 class wxSoundBackendOSS 
: public wxSoundBackend
 
 107     wxString 
GetName() const { return wxT("Open Sound System"); } 
 108     int GetPriority() const { return 10; } 
 109     bool IsAvailable() const; 
 110     bool HasNativeAsyncPlayback() const { return false; } 
 111     bool Play(wxSoundData 
*data
, unsigned flags
, 
 112               volatile wxSoundPlaybackStatus 
*status
); 
 114     bool IsPlaying() const { return false; } 
 117     int OpenDSP(const wxSoundData 
*data
); 
 118     bool InitDSP(int dev
, const wxSoundData 
*data
); 
 120     int m_DSPblkSize
;        // Size of the DSP buffer 
 121     bool m_needConversion
; 
 124 bool wxSoundBackendOSS::IsAvailable() const 
 127     fd 
= open(AUDIODEV
, O_WRONLY 
| O_NONBLOCK
); 
 134 bool wxSoundBackendOSS::Play(wxSoundData 
*data
, unsigned flags
, 
 135                              volatile wxSoundPlaybackStatus 
*status
) 
 137     int dev 
= OpenDSP(data
); 
 142     ioctl(dev
, SNDCTL_DSP_SYNC
, 0); 
 149         size_t datasize 
= data
->m_dataBytes
; 
 153             if (status
->m_stopRequested
) 
 155                 wxLogTrace(wxT("sound"), wxT("playback stopped")); 
 160             i
= (int)((l 
+ m_DSPblkSize
) < datasize 
? 
 161                      m_DSPblkSize 
: (datasize 
- l
)); 
 162             if (write(dev
, &data
->m_data
[l
], i
) != i
) 
 167         } while (play 
&& l 
< datasize
); 
 168     } while (flags 
& wxSOUND_LOOP
); 
 174 int wxSoundBackendOSS::OpenDSP(const wxSoundData 
*data
) 
 178     if ((dev 
= open(AUDIODEV
, O_WRONLY
, 0)) <0) 
 181     if (!InitDSP(dev
, data
) || m_needConversion
) 
 191 bool wxSoundBackendOSS::InitDSP(int dev
, const wxSoundData 
*data
) 
 196     if (ioctl(dev
, SNDCTL_DSP_RESET
, 0) < 0) 
 198         wxLogTrace(wxT("sound"), wxT("unable to reset dsp")); 
 202     m_needConversion 
= false; 
 204     tmp 
= data
->m_bitsPerSample
; 
 205     if (ioctl(dev
, SNDCTL_DSP_SAMPLESIZE
, &tmp
) < 0) 
 207         wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_SAMPLESIZE)")); 
 210     if (tmp 
!= data
->m_bitsPerSample
) 
 212         wxLogTrace(wxT("sound"), 
 213                    wxT("Unable to set DSP sample size to %d (wants %d)"), 
 214                    data
->m_bitsPerSample
, tmp
); 
 215         m_needConversion 
= true; 
 218     unsigned stereo 
= data
->m_channels 
== 1 ? 0 : 1; 
 220     if (ioctl(dev
, SNDCTL_DSP_STEREO
, &tmp
) < 0) 
 222         wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_STEREO)")); 
 227         wxLogTrace(wxT("sound"), wxT("Unable to set DSP to %s."), stereo
?  wxT("stereo"):wxT("mono")); 
 228         m_needConversion 
= true; 
 231     tmp 
= data
->m_samplingRate
; 
 232     if (ioctl(dev
, SNDCTL_DSP_SPEED
, &tmp
) < 0) 
 234         wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_SPEED)")); 
 237     if (tmp 
!= data
->m_samplingRate
) 
 239         // If the rate the sound card is using is not within 1% of what the 
 240         // data specified then override the data setting.  The only reason not 
 241         // to always override this is because of clock-rounding 
 242         // problems. Sound cards will sometimes use things like 44101 when you 
 243         // ask for 44100.  No need overriding this and having strange output 
 244         // file rates for something that we can't hear anyways. 
 245         if (data
->m_samplingRate 
- tmp 
> (tmp 
* .01) || 
 246             tmp 
- data
->m_samplingRate 
> (tmp 
* .01)) { 
 247             wxLogTrace(wxT("sound"), 
 248                        wxT("Unable to set DSP sampling rate to %d (wants %d)"), 
 249                        data
->m_samplingRate
, tmp
); 
 250             m_needConversion 
= true; 
 254     // Do this last because some drivers can adjust the buffer sized based on 
 255     // the sampling rate, etc. 
 256     if (ioctl(dev
, SNDCTL_DSP_GETBLKSIZE
, &m_DSPblkSize
) < 0) 
 258         wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_GETBLKSIZE)")); 
 264 #endif // HAVE_SYS_SOUNDCARD_H 
 266 // ---------------------------------------------------------------------------- 
 267 // wxSoundSyncOnlyAdaptor 
 268 // ---------------------------------------------------------------------------- 
 272 class wxSoundSyncOnlyAdaptor
; 
 274 // this class manages asynchronous playback of audio if the backend doesn't 
 275 // support it natively (e.g. OSS backend) 
 276 class wxSoundAsyncPlaybackThread 
: public wxThread
 
 279     wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor 
*adaptor
, 
 280                               wxSoundData 
*data
, unsigned flags
) 
 281         : wxThread(), m_adapt(adaptor
), m_data(data
), m_flags(flags
) {} 
 282     virtual ExitCode 
Entry(); 
 285     wxSoundSyncOnlyAdaptor 
*m_adapt
; 
 290 #endif // wxUSE_THREADS 
 292 // This class turns wxSoundBackend that doesn't support asynchronous playback 
 293 // into one that does 
 294 class wxSoundSyncOnlyAdaptor 
: public wxSoundBackend
 
 297     wxSoundSyncOnlyAdaptor(wxSoundBackend 
*backend
) 
 298         : m_backend(backend
), m_playing(false) {} 
 299     virtual ~wxSoundSyncOnlyAdaptor() 
 303     wxString 
GetName() const 
 305         return m_backend
->GetName(); 
 307     int GetPriority() const 
 309         return m_backend
->GetPriority(); 
 311     bool IsAvailable() const 
 313         return m_backend
->IsAvailable(); 
 315     bool HasNativeAsyncPlayback() const 
 319     bool Play(wxSoundData 
*data
, unsigned flags
, 
 320               volatile wxSoundPlaybackStatus 
*status
); 
 322     bool IsPlaying() const; 
 325     friend class wxSoundAsyncPlaybackThread
; 
 327     wxSoundBackend 
*m_backend
; 
 330     // player thread holds this mutex and releases it after it finishes 
 331     // playing, so that the main thread knows when it can play sound 
 332     wxMutex m_mutexRightToPlay
; 
 333     wxSoundPlaybackStatus m_status
; 
 339 wxThread::ExitCode 
wxSoundAsyncPlaybackThread::Entry() 
 341     m_adapt
->m_backend
->Play(m_data
, m_flags 
& ~wxSOUND_ASYNC
, 
 345     m_adapt
->m_playing 
= false; 
 346     m_adapt
->m_mutexRightToPlay
.Unlock(); 
 347     wxLogTrace(wxT("sound"), wxT("terminated async playback thread")); 
 352 bool wxSoundSyncOnlyAdaptor::Play(wxSoundData 
*data
, unsigned flags
, 
 353                                   volatile wxSoundPlaybackStatus 
*status
) 
 356     if (flags 
& wxSOUND_ASYNC
) 
 359         m_mutexRightToPlay
.Lock(); 
 360         m_status
.m_playing 
= true; 
 361         m_status
.m_stopRequested 
= false; 
 363         wxThread 
*th 
= new wxSoundAsyncPlaybackThread(this, data
, flags
); 
 366         wxLogTrace(wxT("sound"), wxT("launched async playback thread")); 
 369         wxLogError(_("Unable to play sound asynchronously.")); 
 376         m_mutexRightToPlay
.Lock(); 
 378         bool rv 
= m_backend
->Play(data
, flags
, status
); 
 380         m_mutexRightToPlay
.Unlock(); 
 386 void wxSoundSyncOnlyAdaptor::Stop() 
 388     wxLogTrace(wxT("sound"), wxT("asking audio to stop")); 
 391     // tell the player thread (if running) to stop playback ASAP: 
 392     m_status
.m_stopRequested 
= true; 
 394     // acquire the mutex to be sure no sound is being played, then 
 395     // release it because we don't need it for anything (the effect of this 
 396     // is that calling thread will wait until playback thread reacts to 
 397     // our request to interrupt playback): 
 398     m_mutexRightToPlay
.Lock(); 
 399     m_mutexRightToPlay
.Unlock(); 
 400     wxLogTrace(wxT("sound"), wxT("audio was stopped")); 
 404 bool wxSoundSyncOnlyAdaptor::IsPlaying() const 
 407     return m_status
.m_playing
; 
 414 // ---------------------------------------------------------------------------- 
 416 // ---------------------------------------------------------------------------- 
 418 wxSoundBackend 
*wxSound::ms_backend 
= NULL
; 
 420 // FIXME - temporary, until we have plugins architecture 
 423         wxDynamicLibrary 
*wxSound::ms_backendSDL 
= NULL
; 
 425         extern "C" wxSoundBackend 
*wxCreateSoundBackendSDL(); 
 429 wxSound::wxSound() : m_data(NULL
) 
 433 wxSound::wxSound(const wxString
& sFileName
, bool isResource
) : m_data(NULL
) 
 435     Create(sFileName
, isResource
); 
 438 wxSound::wxSound(int size
, const wxByte
* data
) : m_data(NULL
) 
 448 bool wxSound::Create(const wxString
& fileName
, 
 449                      bool WXUNUSED_UNLESS_DEBUG(isResource
)) 
 451     wxASSERT_MSG( !isResource
, 
 452              wxT("Loading sound from resources is only supported on Windows") ); 
 457     if (!fileWave
.Open(fileName
, wxFile::read
)) 
 462     wxFileOffset lenOrig 
= fileWave
.Length(); 
 463     if ( lenOrig 
== wxInvalidOffset 
) 
 466     size_t len 
= wx_truncate_cast(size_t, lenOrig
); 
 467     wxUint8 
*data 
= new wxUint8
[len
]; 
 468     if ( fileWave
.Read(data
, len
) != lenOrig 
) 
 471         wxLogError(_("Couldn't load sound data from '%s'."), fileName
.c_str()); 
 475     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(wxT("%s/%s"), 
 513                 wxDynamicLibrary::GetPluginsDirectory().c_str(), 
 514                 wxDynamicLibrary::CanonicalizePluginName( 
 515                     wxT("sound_sdl"), wxDL_PLUGIN_BASE
).c_str()); 
 516             wxLogTrace(wxT("sound"), 
 517                        wxT("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(wxT("sound"), 
 561                    wxT("using backend '%s'"), ms_backend
->GetName().c_str()); 
 565 /*static*/ void wxSound::UnloadBackend() 
 569         wxLogTrace(wxT("sound"), wxT("unloading backend")); 
 573         wxDELETE(ms_backend
); 
 574 #if wxUSE_LIBSDL && wxUSE_PLUGINS 
 575         delete ms_backendSDL
; 
 580 bool wxSound::DoPlay(unsigned flags
) const 
 582     wxCHECK_MSG( IsOk(), false, wxT("Attempt to play invalid wave data") ); 
 585     wxSoundPlaybackStatus status
; 
 586     status
.m_playing 
= true; 
 587     status
.m_stopRequested 
= false; 
 588     return ms_backend
->Play(m_data
, flags
, &status
); 
 591 /*static*/ void wxSound::Stop() 
 597 /*static*/ bool wxSound::IsPlaying() 
 600         return ms_backend
->IsPlaying(); 
 614     wxUint16      uiFormatTag
; 
 616     wxUint32      ulSamplesPerSec
; 
 617     wxUint32      ulAvgBytesPerSec
; 
 618     wxUint16      uiBlockAlign
; 
 619     wxUint16      uiBitsPerSample
; 
 622 #define WAVE_FORMAT_PCM  1 
 626 bool wxSound::LoadWAV(const wxUint8 
*data
, size_t length
, bool copyData
) 
 628     // the simplest wave file header consists of 44 bytes: 
 637     //      22  number of channels          | 
 638     //      24  sample rate                 | WAVEFORMAT 
 639     //      28  average bytes per second    | 
 640     //      32  bytes per frame             | 
 641     //      34  bits per sample             | 
 644     //      40  number of data bytes 
 645     //      44  (wave signal) data 
 647     // so check that we have at least as much 
 651     WAVEFORMAT waveformat
; 
 652     memcpy(&waveformat
, &data
[FMT_INDEX 
+ 4], sizeof(WAVEFORMAT
)); 
 653     waveformat
.uiSize 
= wxUINT32_SWAP_ON_BE(waveformat
.uiSize
); 
 654     waveformat
.uiFormatTag 
= wxUINT16_SWAP_ON_BE(waveformat
.uiFormatTag
); 
 655     waveformat
.uiChannels 
= wxUINT16_SWAP_ON_BE(waveformat
.uiChannels
); 
 656     waveformat
.ulSamplesPerSec 
= wxUINT32_SWAP_ON_BE(waveformat
.ulSamplesPerSec
); 
 657     waveformat
.ulAvgBytesPerSec 
= wxUINT32_SWAP_ON_BE(waveformat
.ulAvgBytesPerSec
); 
 658     waveformat
.uiBlockAlign 
= wxUINT16_SWAP_ON_BE(waveformat
.uiBlockAlign
); 
 659     waveformat
.uiBitsPerSample 
= wxUINT16_SWAP_ON_BE(waveformat
.uiBitsPerSample
); 
 661     // get the sound data size 
 663     memcpy(&ul
, &data
[FMT_INDEX 
+ waveformat
.uiSize 
+ 12], 4); 
 664     ul 
= wxUINT32_SWAP_ON_BE(ul
); 
 666     if ( length 
< ul 
+ FMT_INDEX 
+ waveformat
.uiSize 
+ 16 ) 
 669     if (memcmp(data
, "RIFF", 4) != 0) 
 671     if (memcmp(&data
[WAVE_INDEX
], "WAVE", 4) != 0) 
 673     if (memcmp(&data
[FMT_INDEX
], "fmt ", 4) != 0) 
 675     if (memcmp(&data
[FMT_INDEX 
+ waveformat
.uiSize 
+ 8], "data", 4) != 0) 
 678     if (waveformat
.uiFormatTag 
!= WAVE_FORMAT_PCM
) 
 681     if (waveformat
.ulSamplesPerSec 
!= 
 682         waveformat
.ulAvgBytesPerSec 
/ waveformat
.uiBlockAlign
) 
 685     m_data 
= new wxSoundData
; 
 686     m_data
->m_channels 
= waveformat
.uiChannels
; 
 687     m_data
->m_samplingRate 
= waveformat
.ulSamplesPerSec
; 
 688     m_data
->m_bitsPerSample 
= waveformat
.uiBitsPerSample
; 
 689     m_data
->m_samples 
= ul 
/ (m_data
->m_channels 
* m_data
->m_bitsPerSample 
/ 8); 
 690     m_data
->m_dataBytes 
= ul
; 
 694         m_data
->m_dataWithHeader 
= new wxUint8
[length
]; 
 695         memcpy(m_data
->m_dataWithHeader
, data
, length
); 
 698         m_data
->m_dataWithHeader 
= (wxUint8
*)data
; 
 701         (&m_data
->m_dataWithHeader
[FMT_INDEX 
+ waveformat
.uiSize 
+ 8]); 
 707 // ---------------------------------------------------------------------------- 
 708 // wxSoundCleanupModule 
 709 // ---------------------------------------------------------------------------- 
 711 class wxSoundCleanupModule
: public wxModule
 
 714     bool OnInit() { return true; } 
 715     void OnExit() { wxSound::UnloadBackend(); } 
 716     DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule
) 
 719 IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule
, wxModule
)