1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/sound.cpp
4 // Author: Marcel Rasche, Vaclav Slavik
7 // Copyright: (c) Julian Smart, Open Source Applications Foundation
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // for compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
14 #if defined(__BORLANDC__)
23 #include <sys/ioctl.h>
25 #ifdef HAVE_SYS_SOUNDCARD_H
26 #include <sys/soundcard.h>
33 #include "wx/module.h"
36 #include "wx/thread.h"
39 #include "wx/dynlib.h"
43 // mutex for all wxSound's synchronization
44 static wxMutex gs_soundMutex
;
47 // ----------------------------------------------------------------------------
49 // ----------------------------------------------------------------------------
51 void wxSoundData::IncRef()
54 wxMutexLocker
locker(gs_soundMutex
);
59 void wxSoundData::DecRef()
62 wxMutexLocker
locker(gs_soundMutex
);
68 wxSoundData::~wxSoundData()
70 delete[] m_dataWithHeader
;
74 // ----------------------------------------------------------------------------
75 // wxSoundBackendNull, used in absence of audio API or card
76 // ----------------------------------------------------------------------------
78 class wxSoundBackendNull
: public wxSoundBackend
81 wxString
GetName() const { return _("No sound"); }
82 int GetPriority() const { return 0; }
83 bool IsAvailable() const { return true; }
84 bool HasNativeAsyncPlayback() const { return true; }
85 bool Play(wxSoundData
*WXUNUSED(data
), unsigned WXUNUSED(flags
),
86 volatile wxSoundPlaybackStatus
*WXUNUSED(status
))
89 bool IsPlaying() const { return false; }
93 // ----------------------------------------------------------------------------
94 // wxSoundBackendOSS, for Linux
95 // ----------------------------------------------------------------------------
97 #ifdef HAVE_SYS_SOUNDCARD_H
100 #define AUDIODEV "/dev/dsp" // Default path for audio device
103 class wxSoundBackendOSS
: public wxSoundBackend
106 wxString
GetName() const { return wxT("Open Sound System"); }
107 int GetPriority() const { return 10; }
108 bool IsAvailable() const;
109 bool HasNativeAsyncPlayback() const { return false; }
110 bool Play(wxSoundData
*data
, unsigned flags
,
111 volatile wxSoundPlaybackStatus
*status
);
113 bool IsPlaying() const { return false; }
116 int OpenDSP(const wxSoundData
*data
);
117 bool InitDSP(int dev
, const wxSoundData
*data
);
119 int m_DSPblkSize
; // Size of the DSP buffer
120 bool m_needConversion
;
123 bool wxSoundBackendOSS::IsAvailable() const
126 fd
= open(AUDIODEV
, O_WRONLY
| O_NONBLOCK
);
133 bool wxSoundBackendOSS::Play(wxSoundData
*data
, unsigned flags
,
134 volatile wxSoundPlaybackStatus
*status
)
136 int dev
= OpenDSP(data
);
141 ioctl(dev
, SNDCTL_DSP_SYNC
, 0);
148 size_t datasize
= data
->m_dataBytes
;
152 if (status
->m_stopRequested
)
154 wxLogTrace(wxT("sound"), wxT("playback stopped"));
159 i
= (int)((l
+ m_DSPblkSize
) < datasize
?
160 m_DSPblkSize
: (datasize
- l
));
161 if (write(dev
, &data
->m_data
[l
], i
) != i
)
166 } while (play
&& l
< datasize
);
167 } while (flags
& wxSOUND_LOOP
);
173 int wxSoundBackendOSS::OpenDSP(const wxSoundData
*data
)
177 if ((dev
= open(AUDIODEV
, O_WRONLY
, 0)) <0)
180 if (!InitDSP(dev
, data
) || m_needConversion
)
190 bool wxSoundBackendOSS::InitDSP(int dev
, const wxSoundData
*data
)
195 if (ioctl(dev
, SNDCTL_DSP_RESET
, 0) < 0)
197 wxLogTrace(wxT("sound"), wxT("unable to reset dsp"));
201 m_needConversion
= false;
203 tmp
= data
->m_bitsPerSample
;
204 if (ioctl(dev
, SNDCTL_DSP_SAMPLESIZE
, &tmp
) < 0)
206 wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_SAMPLESIZE)"));
209 if (tmp
!= data
->m_bitsPerSample
)
211 wxLogTrace(wxT("sound"),
212 wxT("Unable to set DSP sample size to %d (wants %d)"),
213 data
->m_bitsPerSample
, tmp
);
214 m_needConversion
= true;
217 unsigned stereo
= data
->m_channels
== 1 ? 0 : 1;
219 if (ioctl(dev
, SNDCTL_DSP_STEREO
, &tmp
) < 0)
221 wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_STEREO)"));
226 wxLogTrace(wxT("sound"), wxT("Unable to set DSP to %s."), stereo
? wxT("stereo"):wxT("mono"));
227 m_needConversion
= true;
230 tmp
= data
->m_samplingRate
;
231 if (ioctl(dev
, SNDCTL_DSP_SPEED
, &tmp
) < 0)
233 wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_SPEED)"));
236 if (tmp
!= data
->m_samplingRate
)
238 // If the rate the sound card is using is not within 1% of what the
239 // data specified then override the data setting. The only reason not
240 // to always override this is because of clock-rounding
241 // problems. Sound cards will sometimes use things like 44101 when you
242 // ask for 44100. No need overriding this and having strange output
243 // file rates for something that we can't hear anyways.
244 if (data
->m_samplingRate
- tmp
> (tmp
* .01) ||
245 tmp
- data
->m_samplingRate
> (tmp
* .01)) {
246 wxLogTrace(wxT("sound"),
247 wxT("Unable to set DSP sampling rate to %d (wants %d)"),
248 data
->m_samplingRate
, tmp
);
249 m_needConversion
= true;
253 // Do this last because some drivers can adjust the buffer sized based on
254 // the sampling rate, etc.
255 if (ioctl(dev
, SNDCTL_DSP_GETBLKSIZE
, &m_DSPblkSize
) < 0)
257 wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_GETBLKSIZE)"));
263 #endif // HAVE_SYS_SOUNDCARD_H
265 // ----------------------------------------------------------------------------
266 // wxSoundSyncOnlyAdaptor
267 // ----------------------------------------------------------------------------
271 class wxSoundSyncOnlyAdaptor
;
273 // this class manages asynchronous playback of audio if the backend doesn't
274 // support it natively (e.g. OSS backend)
275 class wxSoundAsyncPlaybackThread
: public wxThread
278 wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor
*adaptor
,
279 wxSoundData
*data
, unsigned flags
)
280 : wxThread(), m_adapt(adaptor
), m_data(data
), m_flags(flags
) {}
281 virtual ExitCode
Entry();
284 wxSoundSyncOnlyAdaptor
*m_adapt
;
289 #endif // wxUSE_THREADS
291 // This class turns wxSoundBackend that doesn't support asynchronous playback
292 // into one that does
293 class wxSoundSyncOnlyAdaptor
: public wxSoundBackend
296 wxSoundSyncOnlyAdaptor(wxSoundBackend
*backend
)
297 : m_backend(backend
), m_playing(false) {}
298 virtual ~wxSoundSyncOnlyAdaptor()
302 wxString
GetName() const
304 return m_backend
->GetName();
306 int GetPriority() const
308 return m_backend
->GetPriority();
310 bool IsAvailable() const
312 return m_backend
->IsAvailable();
314 bool HasNativeAsyncPlayback() const
318 bool Play(wxSoundData
*data
, unsigned flags
,
319 volatile wxSoundPlaybackStatus
*status
);
321 bool IsPlaying() const;
324 friend class wxSoundAsyncPlaybackThread
;
326 wxSoundBackend
*m_backend
;
329 // player thread holds this mutex and releases it after it finishes
330 // playing, so that the main thread knows when it can play sound
331 wxMutex m_mutexRightToPlay
;
332 wxSoundPlaybackStatus m_status
;
338 wxThread::ExitCode
wxSoundAsyncPlaybackThread::Entry()
340 m_adapt
->m_backend
->Play(m_data
, m_flags
& ~wxSOUND_ASYNC
,
344 m_adapt
->m_playing
= false;
345 m_adapt
->m_mutexRightToPlay
.Unlock();
346 wxLogTrace(wxT("sound"), wxT("terminated async playback thread"));
351 bool wxSoundSyncOnlyAdaptor::Play(wxSoundData
*data
, unsigned flags
,
352 volatile wxSoundPlaybackStatus
*status
)
355 if (flags
& wxSOUND_ASYNC
)
358 m_mutexRightToPlay
.Lock();
359 m_status
.m_playing
= true;
360 m_status
.m_stopRequested
= false;
362 wxThread
*th
= new wxSoundAsyncPlaybackThread(this, data
, flags
);
365 wxLogTrace(wxT("sound"), wxT("launched async playback thread"));
368 wxLogError(_("Unable to play sound asynchronously."));
375 m_mutexRightToPlay
.Lock();
377 bool rv
= m_backend
->Play(data
, flags
, status
);
379 m_mutexRightToPlay
.Unlock();
385 void wxSoundSyncOnlyAdaptor::Stop()
387 wxLogTrace(wxT("sound"), wxT("asking audio to stop"));
390 // tell the player thread (if running) to stop playback ASAP:
391 m_status
.m_stopRequested
= true;
393 // acquire the mutex to be sure no sound is being played, then
394 // release it because we don't need it for anything (the effect of this
395 // is that calling thread will wait until playback thread reacts to
396 // our request to interrupt playback):
397 m_mutexRightToPlay
.Lock();
398 m_mutexRightToPlay
.Unlock();
399 wxLogTrace(wxT("sound"), wxT("audio was stopped"));
403 bool wxSoundSyncOnlyAdaptor::IsPlaying() const
406 return m_status
.m_playing
;
413 // ----------------------------------------------------------------------------
415 // ----------------------------------------------------------------------------
417 wxSoundBackend
*wxSound::ms_backend
= NULL
;
419 // FIXME - temporary, until we have plugins architecture
422 wxDynamicLibrary
*wxSound::ms_backendSDL
= NULL
;
424 extern "C" wxSoundBackend
*wxCreateSoundBackendSDL();
428 wxSound::wxSound() : m_data(NULL
)
432 wxSound::wxSound(const wxString
& sFileName
, bool isResource
) : m_data(NULL
)
434 Create(sFileName
, isResource
);
437 wxSound::wxSound(size_t size
, const void* data
) : m_data(NULL
)
447 bool wxSound::Create(const wxString
& fileName
,
448 bool WXUNUSED_UNLESS_DEBUG(isResource
))
450 wxASSERT_MSG( !isResource
,
451 wxT("Loading sound from resources is only supported on Windows") );
456 if (!fileWave
.Open(fileName
, wxFile::read
))
461 wxFileOffset lenOrig
= fileWave
.Length();
462 if ( lenOrig
== wxInvalidOffset
)
465 size_t len
= wx_truncate_cast(size_t, lenOrig
);
466 wxUint8
*data
= new wxUint8
[len
];
467 if ( fileWave
.Read(data
, len
) != lenOrig
)
470 wxLogError(_("Couldn't load sound data from '%s'."), fileName
.c_str());
474 if (!LoadWAV(data
, len
, false))
477 wxLogError(_("Sound file '%s' is in unsupported format."),
485 bool wxSound::Create(size_t size
, const void* 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(wxT("%s/%s"),
512 wxDynamicLibrary::GetPluginsDirectory().c_str(),
513 wxDynamicLibrary::CanonicalizePluginName(
514 wxT("sound_sdl"), wxDL_PLUGIN_BASE
).c_str());
515 wxLogTrace(wxT("sound"),
516 wxT("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(wxT("sound"),
560 wxT("using backend '%s'"), ms_backend
->GetName().c_str());
564 /*static*/ void wxSound::UnloadBackend()
568 wxLogTrace(wxT("sound"), wxT("unloading backend"));
572 wxDELETE(ms_backend
);
573 #if wxUSE_LIBSDL && wxUSE_PLUGINS
574 delete ms_backendSDL
;
579 bool wxSound::DoPlay(unsigned flags
) const
581 wxCHECK_MSG( IsOk(), false, wxT("Attempt to play invalid wave data") );
584 wxSoundPlaybackStatus status
;
585 status
.m_playing
= true;
586 status
.m_stopRequested
= false;
587 return ms_backend
->Play(m_data
, flags
, &status
);
590 /*static*/ void wxSound::Stop()
596 /*static*/ bool wxSound::IsPlaying()
599 return ms_backend
->IsPlaying();
613 wxUint16 uiFormatTag
;
615 wxUint32 ulSamplesPerSec
;
616 wxUint32 ulAvgBytesPerSec
;
617 wxUint16 uiBlockAlign
;
618 wxUint16 uiBitsPerSample
;
621 #define WAVE_FORMAT_PCM 1
625 bool wxSound::LoadWAV(const void* data_
, size_t length
, bool copyData
)
627 // the simplest wave file header consists of 44 bytes:
636 // 22 number of channels |
637 // 24 sample rate | WAVEFORMAT
638 // 28 average bytes per second |
639 // 32 bytes per frame |
640 // 34 bits per sample |
643 // 40 number of data bytes
644 // 44 (wave signal) data
646 // so check that we have at least as much
650 const wxUint8
* data
= static_cast<const wxUint8
*>(data_
);
652 WAVEFORMAT waveformat
;
653 memcpy(&waveformat
, &data
[FMT_INDEX
+ 4], sizeof(WAVEFORMAT
));
654 waveformat
.uiSize
= wxUINT32_SWAP_ON_BE(waveformat
.uiSize
);
655 waveformat
.uiFormatTag
= wxUINT16_SWAP_ON_BE(waveformat
.uiFormatTag
);
656 waveformat
.uiChannels
= wxUINT16_SWAP_ON_BE(waveformat
.uiChannels
);
657 waveformat
.ulSamplesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulSamplesPerSec
);
658 waveformat
.ulAvgBytesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulAvgBytesPerSec
);
659 waveformat
.uiBlockAlign
= wxUINT16_SWAP_ON_BE(waveformat
.uiBlockAlign
);
660 waveformat
.uiBitsPerSample
= wxUINT16_SWAP_ON_BE(waveformat
.uiBitsPerSample
);
662 // get the sound data size
664 memcpy(&ul
, &data
[FMT_INDEX
+ waveformat
.uiSize
+ 12], 4);
665 ul
= wxUINT32_SWAP_ON_BE(ul
);
667 if ( length
< ul
+ FMT_INDEX
+ waveformat
.uiSize
+ 16 )
670 if (memcmp(data
, "RIFF", 4) != 0)
672 if (memcmp(&data
[WAVE_INDEX
], "WAVE", 4) != 0)
674 if (memcmp(&data
[FMT_INDEX
], "fmt ", 4) != 0)
676 if (memcmp(&data
[FMT_INDEX
+ waveformat
.uiSize
+ 8], "data", 4) != 0)
679 if (waveformat
.uiFormatTag
!= WAVE_FORMAT_PCM
)
682 if (waveformat
.ulSamplesPerSec
!=
683 waveformat
.ulAvgBytesPerSec
/ waveformat
.uiBlockAlign
)
686 m_data
= new wxSoundData
;
687 m_data
->m_channels
= waveformat
.uiChannels
;
688 m_data
->m_samplingRate
= waveformat
.ulSamplesPerSec
;
689 m_data
->m_bitsPerSample
= waveformat
.uiBitsPerSample
;
690 m_data
->m_samples
= ul
/ (m_data
->m_channels
* m_data
->m_bitsPerSample
/ 8);
691 m_data
->m_dataBytes
= ul
;
695 m_data
->m_dataWithHeader
= new wxUint8
[length
];
696 memcpy(m_data
->m_dataWithHeader
, data
, length
);
699 m_data
->m_dataWithHeader
= (wxUint8
*)data
;
702 (&m_data
->m_dataWithHeader
[FMT_INDEX
+ waveformat
.uiSize
+ 8]);
708 // ----------------------------------------------------------------------------
709 // wxSoundCleanupModule
710 // ----------------------------------------------------------------------------
712 class wxSoundCleanupModule
: public wxModule
715 bool OnInit() { return true; }
716 void OnExit() { wxSound::UnloadBackend(); }
717 DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule
)
720 IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule
, wxModule
)