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"));
345 // tell the player thread (if running) to stop playback ASAP:
346 m_status
.m_stopRequested
= true;
348 // acquire the mutex to be sure no sound is being played, then
349 // release it because we don't need it for anything (the effect of this
350 // is that calling thread will wait until playback thread reacts to
351 // our request to interrupt playback):
352 m_mutexRightToPlay
.Lock();
353 m_mutexRightToPlay
.Unlock();
354 wxLogTrace(_T("sound"), _T("audio was stopped"));
358 bool wxSoundSyncOnlyAdaptor::IsPlaying() const
361 return m_status
.m_playing
;
368 // ----------------------------------------------------------------------------
370 // ----------------------------------------------------------------------------
372 wxSoundBackend
*wxSound::ms_backend
= NULL
;
374 // FIXME - temporary, until we have plugins architecture
377 wxDynamicLibrary
*wxSound::ms_backendSDL
= NULL
;
379 extern "C" wxSoundBackend
*wxCreateSoundBackendSDL();
383 wxSound::wxSound() : m_data(NULL
)
387 wxSound::wxSound(const wxString
& sFileName
, bool isResource
) : m_data(NULL
)
389 Create(sFileName
, isResource
);
392 wxSound::wxSound(int size
, const wxByte
* data
) : m_data(NULL
)
402 bool wxSound::Create(const wxString
& fileName
, bool isResource
)
404 wxASSERT_MSG( !isResource
,
405 _T("Loading sound from resources is only supported on Windows") );
410 if (!fileWave
.Open(fileName
, wxFile::read
))
415 size_t len
= fileWave
.Length();
416 wxUint8
*data
= new wxUint8
[len
];
417 if (fileWave
.Read(data
, len
) != len
)
419 wxLogError(_("Couldn't load sound data from '%s'."), fileName
.c_str());
423 if (!LoadWAV(data
, len
, false))
425 wxLogError(_("Sound file '%s' is in unsupported format."),
433 bool wxSound::Create(int size
, const wxByte
* data
)
435 wxASSERT( data
!= NULL
);
438 if (!LoadWAV(data
, size
, true))
440 wxLogError(_("Sound data are in unsupported format."));
446 /*static*/ void wxSound::EnsureBackend()
450 // FIXME -- make this fully dynamic when plugins architecture is in
452 #ifdef HAVE_SYS_SOUNDCARD_H
453 ms_backend
= new wxSoundBackendOSS();
454 if (!ms_backend
->IsAvailable())
456 wxDELETE(ms_backend
);
464 ms_backend
= wxCreateSoundBackendSDL();
467 dllname
.Printf(_T("%s/%s"),
468 wxDynamicLibrary::GetPluginsDirectory().c_str(),
469 wxDynamicLibrary::CanonicalizePluginName(
470 _T("sound_sdl"), wxDL_PLUGIN_BASE
).c_str());
471 wxLogTrace(_T("sound"),
472 _T("trying to load SDL plugin from '%s'..."),
475 ms_backendSDL
= new wxDynamicLibrary(dllname
, wxDL_NOW
);
476 if (!ms_backendSDL
->IsLoaded())
478 wxDELETE(ms_backendSDL
);
482 typedef wxSoundBackend
*(*wxCreateSoundBackend_t
)();
483 wxDYNLIB_FUNCTION(wxCreateSoundBackend_t
,
484 wxCreateSoundBackendSDL
, *ms_backendSDL
);
485 if (pfnwxCreateSoundBackendSDL
)
487 ms_backend
= (*pfnwxCreateSoundBackendSDL
)();
491 if (ms_backend
&& !ms_backend
->IsAvailable())
493 wxDELETE(ms_backend
);
499 ms_backend
= new wxSoundBackendNull();
501 if (!ms_backend
->HasNativeAsyncPlayback())
502 ms_backend
= new wxSoundSyncOnlyAdaptor(ms_backend
);
504 wxLogTrace(_T("sound"),
505 _T("using backend '%s'"), ms_backend
->GetName().c_str());
509 /*static*/ void wxSound::UnloadBackend()
513 wxLogTrace(_T("sound"), _T("unloading backend"));
519 #if wxUSE_LIBSDL && wxUSE_PLUGINS
520 delete ms_backendSDL
;
525 bool wxSound::DoPlay(unsigned flags
) const
527 wxCHECK_MSG( IsOk(), false, _T("Attempt to play invalid wave data") );
530 wxSoundPlaybackStatus status
;
531 status
.m_playing
= true;
532 status
.m_stopRequested
= false;
533 return ms_backend
->Play(m_data
, flags
, &status
);
536 /*static*/ void wxSound::Stop()
542 /*static*/ bool wxSound::IsPlaying()
545 return ms_backend
->IsPlaying();
559 wxUint16 uiFormatTag
;
561 wxUint32 ulSamplesPerSec
;
562 wxUint32 ulAvgBytesPerSec
;
563 wxUint16 uiBlockAlign
;
564 wxUint16 uiBitsPerSample
;
567 #define MONO 1 // and stereo is 2 by wav format
568 #define WAVE_FORMAT_PCM 1
572 bool wxSound::LoadWAV(const wxUint8
*data
, size_t length
, bool copyData
)
574 WAVEFORMAT waveformat
;
577 if (length
< 32 + sizeof(WAVEFORMAT
))
580 memcpy(&waveformat
, &data
[FMT_INDEX
+ 4], sizeof(WAVEFORMAT
));
581 waveformat
.uiSize
= wxUINT32_SWAP_ON_BE(waveformat
.uiSize
);
582 waveformat
.uiFormatTag
= wxUINT16_SWAP_ON_BE(waveformat
.uiFormatTag
);
583 waveformat
.uiChannels
= wxUINT16_SWAP_ON_BE(waveformat
.uiChannels
);
584 waveformat
.ulSamplesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulSamplesPerSec
);
585 waveformat
.ulAvgBytesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulAvgBytesPerSec
);
586 waveformat
.uiBlockAlign
= wxUINT16_SWAP_ON_BE(waveformat
.uiBlockAlign
);
587 waveformat
.uiBitsPerSample
= wxUINT16_SWAP_ON_BE(waveformat
.uiBitsPerSample
);
589 if (memcmp(data
, "RIFF", 4) != 0)
591 if (memcmp(&data
[WAVE_INDEX
], "WAVE", 4) != 0)
593 if (memcmp(&data
[FMT_INDEX
], "fmt ", 4) != 0)
595 if (memcmp(&data
[FMT_INDEX
+ waveformat
.uiSize
+ 8], "data", 4) != 0)
597 memcpy(&ul
,&data
[FMT_INDEX
+ waveformat
.uiSize
+ 12], 4);
598 ul
= wxUINT32_SWAP_ON_BE(ul
);
600 //WAS: if (ul + FMT_INDEX + waveformat.uiSize + 16 != length)
601 if (ul
+ FMT_INDEX
+ waveformat
.uiSize
+ 16 > length
)
604 if (waveformat
.uiFormatTag
!= WAVE_FORMAT_PCM
)
607 if (waveformat
.ulSamplesPerSec
!=
608 waveformat
.ulAvgBytesPerSec
/ waveformat
.uiBlockAlign
)
611 m_data
= new wxSoundData
;
612 m_data
->m_channels
= waveformat
.uiChannels
;
613 m_data
->m_samplingRate
= waveformat
.ulSamplesPerSec
;
614 m_data
->m_bitsPerSample
= waveformat
.uiBitsPerSample
;
615 m_data
->m_samples
= ul
/ (m_data
->m_channels
* m_data
->m_bitsPerSample
/ 8);
616 m_data
->m_dataBytes
= ul
;
620 m_data
->m_dataWithHeader
= new wxUint8
[length
];
621 memcpy(m_data
->m_dataWithHeader
, data
, length
);
624 m_data
->m_dataWithHeader
= (wxUint8
*)data
;
627 (&m_data
->m_dataWithHeader
[FMT_INDEX
+ waveformat
.uiSize
+ 8]);
633 // ----------------------------------------------------------------------------
634 // wxSoundCleanupModule
635 // ----------------------------------------------------------------------------
637 class wxSoundCleanupModule
: public wxModule
640 bool OnInit() { return true; }
641 void OnExit() { wxSound::UnloadBackend(); }
642 DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule
)
645 IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule
, wxModule
)