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
, int iDataBits
, int iChannel
,
126 unsigned long ulSamplingRate
);
128 int m_DSPblkSize
; // Size of the DSP buffer
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
);
182 int wxSoundBackendOSS::OpenDSP(const wxSoundData
*data
)
186 if ((dev
= open(AUDIODEV
, O_WRONLY
, 0)) <0)
190 (int)data
->m_bitsPerSample
,
191 data
->m_channels
== 1 ? 0 : 1,
192 data
->m_samplingRate
))
201 bool wxSoundBackendOSS::InitDSP(int dev
, int iDataBits
, int iChannel
,
202 unsigned long ulSamplingRate
)
204 if (ioctl(dev
, SNDCTL_DSP_GETBLKSIZE
, &m_DSPblkSize
) < 0)
206 wxLogTrace(_T("sound"), _T("OSS block size: %i"), m_DSPblkSize
);
207 if (m_DSPblkSize
< 4096 || m_DSPblkSize
> 65536)
209 if (ioctl(dev
, SNDCTL_DSP_SAMPLESIZE
, &iDataBits
) < 0)
211 if (ioctl(dev
, SNDCTL_DSP_STEREO
, &iChannel
) < 0)
213 if (ioctl(dev
, SNDCTL_DSP_SPEED
, &ulSamplingRate
) < 0)
218 #endif // HAVE_SYS_SOUNDCARD_H
220 // ----------------------------------------------------------------------------
221 // wxSoundSyncOnlyAdaptor
222 // ----------------------------------------------------------------------------
226 class wxSoundSyncOnlyAdaptor
;
228 // this class manages asynchronous playback of audio if the backend doesn't
229 // support it natively (e.g. OSS backend)
230 class wxSoundAsyncPlaybackThread
: public wxThread
233 wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor
*adaptor
,
234 wxSoundData
*data
, unsigned flags
)
235 : wxThread(), m_adapt(adaptor
), m_data(data
), m_flags(flags
) {}
236 virtual ExitCode
Entry();
239 wxSoundSyncOnlyAdaptor
*m_adapt
;
244 #endif // wxUSE_THREADS
246 // This class turns wxSoundBackend that doesn't support asynchronous playback
247 // into one that does
248 class wxSoundSyncOnlyAdaptor
: public wxSoundBackend
251 wxSoundSyncOnlyAdaptor(wxSoundBackend
*backend
)
252 : m_backend(backend
), m_playing(false) {}
253 ~wxSoundSyncOnlyAdaptor()
257 wxString
GetName() const
259 return m_backend
->GetName();
261 int GetPriority() const
263 return m_backend
->GetPriority();
265 bool IsAvailable() const
267 return m_backend
->IsAvailable();
269 bool HasNativeAsyncPlayback() const
273 bool Play(wxSoundData
*data
, unsigned flags
,
274 volatile wxSoundPlaybackStatus
*status
);
276 bool IsPlaying() const;
279 friend class wxSoundAsyncPlaybackThread
;
281 wxSoundBackend
*m_backend
;
284 // player thread holds this mutex and releases it after it finishes
285 // playing, so that the main thread knows when it can play sound
286 wxMutex m_mutexRightToPlay
;
287 wxSoundPlaybackStatus m_status
;
293 wxThread::ExitCode
wxSoundAsyncPlaybackThread::Entry()
295 m_adapt
->m_backend
->Play(m_data
, m_flags
& ~wxSOUND_ASYNC
,
299 m_adapt
->m_playing
= false;
300 m_adapt
->m_mutexRightToPlay
.Unlock();
301 wxLogTrace(_T("sound"), _T("terminated async playback thread"));
306 bool wxSoundSyncOnlyAdaptor::Play(wxSoundData
*data
, unsigned flags
,
307 volatile wxSoundPlaybackStatus
*status
)
310 if (flags
& wxSOUND_ASYNC
)
313 m_mutexRightToPlay
.Lock();
314 m_status
.m_playing
= true;
315 m_status
.m_stopRequested
= false;
317 wxThread
*th
= new wxSoundAsyncPlaybackThread(this, data
, flags
);
320 wxLogTrace(_T("sound"), _T("launched async playback thread"));
323 wxLogError(_("Unable to play sound asynchronously."));
330 m_mutexRightToPlay
.Lock();
332 bool rv
= m_backend
->Play(data
, flags
, status
);
334 m_mutexRightToPlay
.Unlock();
340 void wxSoundSyncOnlyAdaptor::Stop()
342 wxLogTrace(_T("sound"), _T("asking audio to stop"));
343 // tell the player thread (if running) to stop playback ASAP:
344 m_status
.m_stopRequested
= true;
346 // acquire the mutex to be sure no sound is being played, then
347 // release it because we don't need it for anything (the effect of this
348 // is that calling thread will wait until playback thread reacts to
349 // our request to interrupt playback):
350 m_mutexRightToPlay
.Lock();
351 m_mutexRightToPlay
.Unlock();
352 wxLogTrace(_T("sound"), _T("audio was stopped"));
355 bool wxSoundSyncOnlyAdaptor::IsPlaying() const
357 return m_status
.m_playing
;
361 // ----------------------------------------------------------------------------
363 // ----------------------------------------------------------------------------
365 wxSoundBackend
*wxSound::ms_backend
= NULL
;
367 // FIXME - temporary, until we have plugins architecture
370 wxDynamicLibrary
*wxSound::ms_backendSDL
= NULL
;
372 extern "C" wxSoundBackend
*wxCreateSoundBackendSDL();
376 wxSound::wxSound() : m_data(NULL
)
380 wxSound::wxSound(const wxString
& sFileName
, bool isResource
) : m_data(NULL
)
382 Create(sFileName
, isResource
);
385 wxSound::wxSound(int size
, const wxByte
* data
) : m_data(NULL
)
395 bool wxSound::Create(const wxString
& fileName
, bool isResource
)
397 wxASSERT_MSG( !isResource
,
398 _T("Loading sound from resources is only supported on Windows") );
403 if (!fileWave
.Open(fileName
, wxFile::read
))
408 size_t len
= fileWave
.Length();
409 wxUint8
*data
= new wxUint8
[len
];
410 if (fileWave
.Read(data
, len
) != len
)
412 wxLogError(_("Couldn't load sound data from '%s'."), fileName
.c_str());
416 if (!LoadWAV(data
, len
, false))
418 wxLogError(_("Sound file '%s' is in unsupported format."),
426 bool wxSound::Create(int size
, const wxByte
* data
)
428 wxASSERT( data
!= NULL
);
431 if (!LoadWAV(data
, size
, true))
433 wxLogError(_("Sound data are in unsupported format."));
439 /*static*/ void wxSound::EnsureBackend()
443 // FIXME -- make this fully dynamic when plugins architecture is in
445 #ifdef HAVE_SYS_SOUNDCARD_H
446 ms_backend
= new wxSoundBackendOSS();
447 if (!ms_backend
->IsAvailable())
449 wxDELETE(ms_backend
);
457 ms_backend
= wxCreateSoundBackendSDL();
460 dllname
.Printf(_T("%s/%s"),
461 wxDynamicLibrary::GetPluginsDirectory().c_str(),
462 wxDynamicLibrary::CanonicalizePluginName(
463 _T("sound_sdl"), wxDL_PLUGIN_BASE
).c_str());
464 wxLogTrace(_T("sound"),
465 _T("trying to load SDL plugin from '%s'..."),
468 ms_backendSDL
= new wxDynamicLibrary(dllname
, wxDL_NOW
);
469 if (!ms_backendSDL
->IsLoaded())
471 wxDELETE(ms_backendSDL
);
475 typedef wxSoundBackend
*(*wxCreateSoundBackend_t
)();
476 wxDYNLIB_FUNCTION(wxCreateSoundBackend_t
,
477 wxCreateSoundBackendSDL
, *ms_backendSDL
);
478 if (pfnwxCreateSoundBackendSDL
)
480 ms_backend
= (*pfnwxCreateSoundBackendSDL
)();
484 if (ms_backend
&& !ms_backend
->IsAvailable())
486 wxDELETE(ms_backend
);
492 ms_backend
= new wxSoundBackendNull();
494 if (!ms_backend
->HasNativeAsyncPlayback())
495 ms_backend
= new wxSoundSyncOnlyAdaptor(ms_backend
);
497 wxLogTrace(_T("sound"),
498 _T("using backend '%s'"), ms_backend
->GetName().c_str());
502 /*static*/ void wxSound::UnloadBackend()
506 wxLogTrace(_T("sound"), _T("unloading backend"));
512 #if wxUSE_LIBSDL && wxUSE_PLUGINS
513 delete ms_backendSDL
;
518 bool wxSound::DoPlay(unsigned flags
) const
520 wxCHECK_MSG( IsOk(), false, _T("Attempt to play invalid wave data") );
523 wxSoundPlaybackStatus status
;
524 status
.m_playing
= true;
525 status
.m_stopRequested
= false;
526 return ms_backend
->Play(m_data
, flags
, &status
);
529 /*static*/ void wxSound::Stop()
535 /*static*/ bool wxSound::IsPlaying()
538 return ms_backend
->IsPlaying();
552 wxUint16 uiFormatTag
;
554 wxUint32 ulSamplesPerSec
;
555 wxUint32 ulAvgBytesPerSec
;
556 wxUint16 uiBlockAlign
;
557 wxUint16 uiBitsPerSample
;
560 #define MONO 1 // and stereo is 2 by wav format
561 #define WAVE_FORMAT_PCM 1
565 bool wxSound::LoadWAV(const wxUint8
*data
, size_t length
, bool copyData
)
567 WAVEFORMAT waveformat
;
570 if (length
< 32 + sizeof(WAVEFORMAT
))
573 memcpy(&waveformat
, &data
[FMT_INDEX
+ 4], sizeof(WAVEFORMAT
));
574 waveformat
.uiSize
= wxUINT32_SWAP_ON_BE(waveformat
.uiSize
);
575 waveformat
.uiFormatTag
= wxUINT16_SWAP_ON_BE(waveformat
.uiFormatTag
);
576 waveformat
.uiChannels
= wxUINT16_SWAP_ON_BE(waveformat
.uiChannels
);
577 waveformat
.ulSamplesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulSamplesPerSec
);
578 waveformat
.ulAvgBytesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulAvgBytesPerSec
);
579 waveformat
.uiBlockAlign
= wxUINT16_SWAP_ON_BE(waveformat
.uiBlockAlign
);
580 waveformat
.uiBitsPerSample
= wxUINT16_SWAP_ON_BE(waveformat
.uiBitsPerSample
);
582 if (memcmp(data
, "RIFF", 4) != 0)
584 if (memcmp(&data
[WAVE_INDEX
], "WAVE", 4) != 0)
586 if (memcmp(&data
[FMT_INDEX
], "fmt ", 4) != 0)
588 if (memcmp(&data
[FMT_INDEX
+ waveformat
.uiSize
+ 8], "data", 4) != 0)
590 memcpy(&ul
,&data
[FMT_INDEX
+ waveformat
.uiSize
+ 12], 4);
591 ul
= wxUINT32_SWAP_ON_BE(ul
);
593 //WAS: if (ul + FMT_INDEX + waveformat.uiSize + 16 != length)
594 if (ul
+ FMT_INDEX
+ waveformat
.uiSize
+ 16 > length
)
597 if (waveformat
.uiFormatTag
!= WAVE_FORMAT_PCM
)
600 if (waveformat
.ulSamplesPerSec
!=
601 waveformat
.ulAvgBytesPerSec
/ waveformat
.uiBlockAlign
)
604 m_data
= new wxSoundData
;
605 m_data
->m_channels
= waveformat
.uiChannels
;
606 m_data
->m_samplingRate
= waveformat
.ulSamplesPerSec
;
607 m_data
->m_bitsPerSample
= waveformat
.uiBitsPerSample
;
608 m_data
->m_samples
= ul
/ (m_data
->m_channels
* m_data
->m_bitsPerSample
/ 8);
609 m_data
->m_dataBytes
= ul
;
613 m_data
->m_dataWithHeader
= new wxUint8
[length
];
614 memcpy(m_data
->m_dataWithHeader
, data
, length
);
617 m_data
->m_dataWithHeader
= (wxUint8
*)data
;
620 (&m_data
->m_dataWithHeader
[FMT_INDEX
+ waveformat
.uiSize
+ 8]);
626 // ----------------------------------------------------------------------------
627 // wxSoundCleanupModule
628 // ----------------------------------------------------------------------------
630 class wxSoundCleanupModule
: public wxModule
633 bool OnInit() { return true; }
634 void OnExit() { wxSound::UnloadBackend(); }
635 DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule
)
638 IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule
, wxModule
)