1 /////////////////////////////////////////////////////////////////////////////
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"
17 #if defined(__BORLANDC__)
26 #include <sys/ioctl.h>
28 #ifdef HAVE_SYS_SOUNDCARD_H
29 #include <sys/soundcard.h>
38 #include "wx/thread.h"
40 #include "wx/module.h"
42 #include "wx/dynlib.h"
46 // mutex for all wxSound's synchronization
47 static wxMutex gs_soundMutex
;
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
54 void wxSoundData::IncRef()
57 wxMutexLocker
locker(gs_soundMutex
);
62 void wxSoundData::DecRef()
65 wxMutexLocker
locker(gs_soundMutex
);
71 wxSoundData::~wxSoundData()
73 delete[] m_dataWithHeader
;
77 // ----------------------------------------------------------------------------
78 // wxSoundBackendNull, used in absence of audio API or card
79 // ----------------------------------------------------------------------------
81 class wxSoundBackendNull
: public wxSoundBackend
84 wxString
GetName() const { return _("No sound"); }
85 int GetPriority() const { return 0; }
86 bool IsAvailable() const { return true; }
87 bool HasNativeAsyncPlayback() const { return true; }
88 bool Play(wxSoundData
*WXUNUSED(data
), unsigned WXUNUSED(flags
),
89 volatile wxSoundPlaybackStatus
*WXUNUSED(status
))
92 bool IsPlaying() const { return false; }
96 // ----------------------------------------------------------------------------
97 // wxSoundBackendOSS, for Linux
98 // ----------------------------------------------------------------------------
100 #ifdef HAVE_SYS_SOUNDCARD_H
103 #define AUDIODEV "/dev/dsp" // Default path for audio device
106 class wxSoundBackendOSS
: public wxSoundBackend
109 wxString
GetName() const { return _T("Open Sound System"); }
110 int GetPriority() const { return 10; }
111 bool IsAvailable() const;
112 bool HasNativeAsyncPlayback() const { return false; }
113 bool Play(wxSoundData
*data
, unsigned flags
,
114 volatile wxSoundPlaybackStatus
*status
);
116 bool IsPlaying() const { return false; }
119 int OpenDSP(const wxSoundData
*data
);
120 bool InitDSP(int dev
, const wxSoundData
*data
);
122 int m_DSPblkSize
; // Size of the DSP buffer
123 bool m_needConversion
;
126 bool wxSoundBackendOSS::IsAvailable() const
129 fd
= open(AUDIODEV
, O_WRONLY
| O_NONBLOCK
);
136 bool wxSoundBackendOSS::Play(wxSoundData
*data
, unsigned flags
,
137 volatile wxSoundPlaybackStatus
*status
)
139 int dev
= OpenDSP(data
);
144 ioctl(dev
, SNDCTL_DSP_SYNC
, 0);
151 size_t datasize
= data
->m_dataBytes
;
155 if (status
->m_stopRequested
)
157 wxLogTrace(_T("sound"), _T("playback stopped"));
162 i
= (int)((l
+ m_DSPblkSize
) < datasize
?
163 m_DSPblkSize
: (datasize
- l
));
164 if (write(dev
, &data
->m_data
[l
], i
) != i
)
169 } while (play
&& l
< datasize
);
170 } while (flags
& wxSOUND_LOOP
);
176 int wxSoundBackendOSS::OpenDSP(const wxSoundData
*data
)
180 if ((dev
= open(AUDIODEV
, O_WRONLY
, 0)) <0)
183 if (!InitDSP(dev
, data
) || m_needConversion
)
193 bool wxSoundBackendOSS::InitDSP(int dev
, const wxSoundData
*data
)
198 if (ioctl(dev
, SNDCTL_DSP_RESET
, 0) < 0)
200 wxLogTrace(_T("sound"), _T("unable to reset dsp"));
204 m_needConversion
= false;
206 tmp
= data
->m_bitsPerSample
;
207 if (ioctl(dev
, SNDCTL_DSP_SAMPLESIZE
, &tmp
) < 0)
209 wxLogTrace(_T("sound"), _T("IOCTL failure (SNDCTL_DSP_SAMPLESIZE)"));
212 if (tmp
!= data
->m_bitsPerSample
)
214 wxLogTrace(_T("sound"),
215 _T("Unable to set DSP sample size to %d (wants %d)"),
216 data
->m_bitsPerSample
, tmp
);
217 m_needConversion
= true;
220 unsigned stereo
= data
->m_channels
== 1 ? 0 : 1;
222 if (ioctl(dev
, SNDCTL_DSP_STEREO
, &tmp
) < 0)
224 wxLogTrace(_T("sound"), _T("IOCTL failure (SNDCTL_DSP_STEREO)"));
229 wxLogTrace(_T("sound"), _T("Unable to set DSP to %s."), stereo
? _T("stereo"):_T("mono"));
230 m_needConversion
= true;
233 tmp
= data
->m_samplingRate
;
234 if (ioctl(dev
, SNDCTL_DSP_SPEED
, &tmp
) < 0)
236 wxLogTrace(_T("sound"), _T("IOCTL failure (SNDCTL_DSP_SPEED)"));
239 if (tmp
!= data
->m_samplingRate
)
241 // If the rate the sound card is using is not within 1% of what the
242 // data specified then override the data setting. The only reason not
243 // to always override this is because of clock-rounding
244 // problems. Sound cards will sometimes use things like 44101 when you
245 // ask for 44100. No need overriding this and having strange output
246 // file rates for something that we can't hear anyways.
247 if (data
->m_samplingRate
- tmp
> (tmp
* .01) ||
248 tmp
- data
->m_samplingRate
> (tmp
* .01)) {
249 wxLogTrace(_T("sound"),
250 _T("Unable to set DSP sampling rate to %d (wants %d)"),
251 data
->m_samplingRate
, tmp
);
252 m_needConversion
= true;
256 // Do this last because some drivers can adjust the buffer sized based on
257 // the sampling rate, etc.
258 if (ioctl(dev
, SNDCTL_DSP_GETBLKSIZE
, &m_DSPblkSize
) < 0)
260 wxLogTrace(_T("sound"), _T("IOCTL failure (SNDCTL_DSP_GETBLKSIZE)"));
266 #endif // HAVE_SYS_SOUNDCARD_H
268 // ----------------------------------------------------------------------------
269 // wxSoundSyncOnlyAdaptor
270 // ----------------------------------------------------------------------------
274 class wxSoundSyncOnlyAdaptor
;
276 // this class manages asynchronous playback of audio if the backend doesn't
277 // support it natively (e.g. OSS backend)
278 class wxSoundAsyncPlaybackThread
: public wxThread
281 wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor
*adaptor
,
282 wxSoundData
*data
, unsigned flags
)
283 : wxThread(), m_adapt(adaptor
), m_data(data
), m_flags(flags
) {}
284 virtual ExitCode
Entry();
287 wxSoundSyncOnlyAdaptor
*m_adapt
;
292 #endif // wxUSE_THREADS
294 // This class turns wxSoundBackend that doesn't support asynchronous playback
295 // into one that does
296 class wxSoundSyncOnlyAdaptor
: public wxSoundBackend
299 wxSoundSyncOnlyAdaptor(wxSoundBackend
*backend
)
300 : m_backend(backend
), m_playing(false) {}
301 ~wxSoundSyncOnlyAdaptor()
305 wxString
GetName() const
307 return m_backend
->GetName();
309 int GetPriority() const
311 return m_backend
->GetPriority();
313 bool IsAvailable() const
315 return m_backend
->IsAvailable();
317 bool HasNativeAsyncPlayback() const
321 bool Play(wxSoundData
*data
, unsigned flags
,
322 volatile wxSoundPlaybackStatus
*status
);
324 bool IsPlaying() const;
327 friend class wxSoundAsyncPlaybackThread
;
329 wxSoundBackend
*m_backend
;
332 // player thread holds this mutex and releases it after it finishes
333 // playing, so that the main thread knows when it can play sound
334 wxMutex m_mutexRightToPlay
;
335 wxSoundPlaybackStatus m_status
;
341 wxThread::ExitCode
wxSoundAsyncPlaybackThread::Entry()
343 m_adapt
->m_backend
->Play(m_data
, m_flags
& ~wxSOUND_ASYNC
,
347 m_adapt
->m_playing
= false;
348 m_adapt
->m_mutexRightToPlay
.Unlock();
349 wxLogTrace(_T("sound"), _T("terminated async playback thread"));
354 bool wxSoundSyncOnlyAdaptor::Play(wxSoundData
*data
, unsigned flags
,
355 volatile wxSoundPlaybackStatus
*status
)
358 if (flags
& wxSOUND_ASYNC
)
361 m_mutexRightToPlay
.Lock();
362 m_status
.m_playing
= true;
363 m_status
.m_stopRequested
= false;
365 wxThread
*th
= new wxSoundAsyncPlaybackThread(this, data
, flags
);
368 wxLogTrace(_T("sound"), _T("launched async playback thread"));
371 wxLogError(_("Unable to play sound asynchronously."));
378 m_mutexRightToPlay
.Lock();
380 bool rv
= m_backend
->Play(data
, flags
, status
);
382 m_mutexRightToPlay
.Unlock();
388 void wxSoundSyncOnlyAdaptor::Stop()
390 wxLogTrace(_T("sound"), _T("asking audio to stop"));
393 // tell the player thread (if running) to stop playback ASAP:
394 m_status
.m_stopRequested
= true;
396 // acquire the mutex to be sure no sound is being played, then
397 // release it because we don't need it for anything (the effect of this
398 // is that calling thread will wait until playback thread reacts to
399 // our request to interrupt playback):
400 m_mutexRightToPlay
.Lock();
401 m_mutexRightToPlay
.Unlock();
402 wxLogTrace(_T("sound"), _T("audio was stopped"));
406 bool wxSoundSyncOnlyAdaptor::IsPlaying() const
409 return m_status
.m_playing
;
416 // ----------------------------------------------------------------------------
418 // ----------------------------------------------------------------------------
420 wxSoundBackend
*wxSound::ms_backend
= NULL
;
422 // FIXME - temporary, until we have plugins architecture
425 wxDynamicLibrary
*wxSound::ms_backendSDL
= NULL
;
427 extern "C" wxSoundBackend
*wxCreateSoundBackendSDL();
431 wxSound::wxSound() : m_data(NULL
)
435 wxSound::wxSound(const wxString
& sFileName
, bool isResource
) : m_data(NULL
)
437 Create(sFileName
, isResource
);
440 wxSound::wxSound(int size
, const wxByte
* data
) : m_data(NULL
)
450 bool wxSound::Create(const wxString
& fileName
, bool isResource
)
452 wxASSERT_MSG( !isResource
,
453 _T("Loading sound from resources is only supported on Windows") );
458 if (!fileWave
.Open(fileName
, wxFile::read
))
463 wxFileOffset lenOrig
= fileWave
.Length();
464 if ( lenOrig
== wxInvalidOffset
)
467 size_t len
= wx_truncate_cast(size_t, lenOrig
);
468 wxUint8
*data
= new wxUint8
[len
];
469 if ( fileWave
.Read(data
, len
) != lenOrig
)
471 wxLogError(_("Couldn't load sound data from '%s'."), fileName
.c_str());
475 if (!LoadWAV(data
, len
, false))
477 wxLogError(_("Sound file '%s' is in unsupported format."),
485 bool wxSound::Create(int size
, const wxByte
* data
)
487 wxASSERT( data
!= NULL
);
490 if (!LoadWAV(data
, size
, true))
492 wxLogError(_("Sound data are in unsupported format."));
498 /*static*/ void wxSound::EnsureBackend()
502 // FIXME -- make this fully dynamic when plugins architecture is in
508 ms_backend
= wxCreateSoundBackendSDL();
511 dllname
.Printf(_T("%s/%s"),
512 wxDynamicLibrary::GetPluginsDirectory().c_str(),
513 wxDynamicLibrary::CanonicalizePluginName(
514 _T("sound_sdl"), wxDL_PLUGIN_BASE
).c_str());
515 wxLogTrace(_T("sound"),
516 _T("trying to load SDL plugin from '%s'..."),
519 ms_backendSDL
= new wxDynamicLibrary(dllname
, wxDL_NOW
);
520 if (!ms_backendSDL
->IsLoaded())
522 wxDELETE(ms_backendSDL
);
526 typedef wxSoundBackend
*(*wxCreateSoundBackend_t
)();
527 wxDYNLIB_FUNCTION(wxCreateSoundBackend_t
,
528 wxCreateSoundBackendSDL
, *ms_backendSDL
);
529 if (pfnwxCreateSoundBackendSDL
)
531 ms_backend
= (*pfnwxCreateSoundBackendSDL
)();
535 if (ms_backend
&& !ms_backend
->IsAvailable())
537 wxDELETE(ms_backend
);
542 #ifdef HAVE_SYS_SOUNDCARD_H
545 ms_backend
= new wxSoundBackendOSS();
546 if (!ms_backend
->IsAvailable())
548 wxDELETE(ms_backend
);
554 ms_backend
= new wxSoundBackendNull();
556 if (!ms_backend
->HasNativeAsyncPlayback())
557 ms_backend
= new wxSoundSyncOnlyAdaptor(ms_backend
);
559 wxLogTrace(_T("sound"),
560 _T("using backend '%s'"), ms_backend
->GetName().c_str());
564 /*static*/ void wxSound::UnloadBackend()
568 wxLogTrace(_T("sound"), _T("unloading backend"));
574 #if wxUSE_LIBSDL && wxUSE_PLUGINS
575 delete ms_backendSDL
;
580 bool wxSound::DoPlay(unsigned flags
) const
582 wxCHECK_MSG( IsOk(), false, _T("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 WAVEFORMAT waveformat
;
631 if (length
< 32 + sizeof(WAVEFORMAT
))
634 memcpy(&waveformat
, &data
[FMT_INDEX
+ 4], sizeof(WAVEFORMAT
));
635 waveformat
.uiSize
= wxUINT32_SWAP_ON_BE(waveformat
.uiSize
);
636 waveformat
.uiFormatTag
= wxUINT16_SWAP_ON_BE(waveformat
.uiFormatTag
);
637 waveformat
.uiChannels
= wxUINT16_SWAP_ON_BE(waveformat
.uiChannels
);
638 waveformat
.ulSamplesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulSamplesPerSec
);
639 waveformat
.ulAvgBytesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulAvgBytesPerSec
);
640 waveformat
.uiBlockAlign
= wxUINT16_SWAP_ON_BE(waveformat
.uiBlockAlign
);
641 waveformat
.uiBitsPerSample
= wxUINT16_SWAP_ON_BE(waveformat
.uiBitsPerSample
);
643 if (memcmp(data
, "RIFF", 4) != 0)
645 if (memcmp(&data
[WAVE_INDEX
], "WAVE", 4) != 0)
647 if (memcmp(&data
[FMT_INDEX
], "fmt ", 4) != 0)
649 if (memcmp(&data
[FMT_INDEX
+ waveformat
.uiSize
+ 8], "data", 4) != 0)
651 memcpy(&ul
,&data
[FMT_INDEX
+ waveformat
.uiSize
+ 12], 4);
652 ul
= wxUINT32_SWAP_ON_BE(ul
);
654 //WAS: if (ul + FMT_INDEX + waveformat.uiSize + 16 != length)
655 if (ul
+ FMT_INDEX
+ waveformat
.uiSize
+ 16 > length
)
658 if (waveformat
.uiFormatTag
!= WAVE_FORMAT_PCM
)
661 if (waveformat
.ulSamplesPerSec
!=
662 waveformat
.ulAvgBytesPerSec
/ waveformat
.uiBlockAlign
)
665 m_data
= new wxSoundData
;
666 m_data
->m_channels
= waveformat
.uiChannels
;
667 m_data
->m_samplingRate
= waveformat
.ulSamplesPerSec
;
668 m_data
->m_bitsPerSample
= waveformat
.uiBitsPerSample
;
669 m_data
->m_samples
= ul
/ (m_data
->m_channels
* m_data
->m_bitsPerSample
/ 8);
670 m_data
->m_dataBytes
= ul
;
674 m_data
->m_dataWithHeader
= new wxUint8
[length
];
675 memcpy(m_data
->m_dataWithHeader
, data
, length
);
678 m_data
->m_dataWithHeader
= (wxUint8
*)data
;
681 (&m_data
->m_dataWithHeader
[FMT_INDEX
+ waveformat
.uiSize
+ 8]);
687 // ----------------------------------------------------------------------------
688 // wxSoundCleanupModule
689 // ----------------------------------------------------------------------------
691 class wxSoundCleanupModule
: public wxModule
694 bool OnInit() { return true; }
695 void OnExit() { wxSound::UnloadBackend(); }
696 DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule
)
699 IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule
, wxModule
)