1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Marcel Rasche, Vaclav Slavik
8 // Copyright: (c) Julian Smart, Vaclav Slavik
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 #if HAVE_SYS_SOUNDCARD_H
34 #include <sys/soundcard.h>
43 #include "wx/thread.h"
45 #include "wx/module.h"
47 #include "wx/dynlib.h"
49 // ----------------------------------------------------------------------------
50 // wxSoundBackendNull, used in absence of audio API or card
51 // ----------------------------------------------------------------------------
53 class wxSoundBackendNull
: public wxSoundBackend
56 wxString
GetName() const { return _("No sound"); }
57 int GetPriority() const { return 0; }
58 bool IsAvailable() const { return true; }
59 bool HasNativeAsyncPlayback() const { return true; }
60 bool Play(wxSoundData
*WXUNUSED(data
), unsigned WXUNUSED(flags
))
65 // ----------------------------------------------------------------------------
66 // wxSoundBackendOSS, for Linux
67 // ----------------------------------------------------------------------------
69 #ifdef HAVE_SYS_SOUNDCARD_H
72 #define AUDIODEV "/dev/dsp" // Default path for audio device
75 class wxSoundBackendOSS
: public wxSoundBackend
78 wxString
GetName() const { return _T("Open Sound System"); }
79 int GetPriority() const { return 10; }
80 bool IsAvailable() const;
81 bool HasNativeAsyncPlayback() const { return false; }
82 bool Play(wxSoundData
*data
, unsigned flags
);
85 int OpenDSP(const wxSoundData
*data
);
86 bool InitDSP(int dev
, int iDataBits
, int iChannel
,
87 unsigned long ulSamplingRate
);
89 int m_DSPblkSize
; // Size of the DSP buffer
92 bool wxSoundBackendOSS::IsAvailable() const
95 fd
= open(AUDIODEV
, O_WRONLY
| O_NONBLOCK
);
102 bool wxSoundBackendOSS::Play(wxSoundData
*data
, unsigned flags
)
104 int dev
= OpenDSP(data
);
109 ioctl(dev
, SNDCTL_DSP_SYNC
, 0);
116 size_t datasize
= data
->m_dataBytes
;
120 i
= (int)((l
+ m_DSPblkSize
) < datasize
?
121 m_DSPblkSize
: (datasize
- l
));
122 if (write(dev
, &data
->m_data
[l
], i
) != i
)
127 } while (play
&& l
< datasize
);
128 } while (flags
& wxSOUND_LOOP
);
134 int wxSoundBackendOSS::OpenDSP(const wxSoundData
*data
)
138 if ((dev
= open(AUDIODEV
, O_WRONLY
, 0)) <0)
142 (int)data
->m_bitsPerSample
,
143 data
->m_channels
== 1 ? 0 : 1,
144 data
->m_samplingRate
))
153 bool wxSoundBackendOSS::InitDSP(int dev
, int iDataBits
, int iChannel
,
154 unsigned long ulSamplingRate
)
156 if (ioctl(dev
, SNDCTL_DSP_GETBLKSIZE
, &m_DSPblkSize
) < 0)
158 if (m_DSPblkSize
< 4096 || m_DSPblkSize
> 65536)
160 if (ioctl(dev
, SNDCTL_DSP_SAMPLESIZE
, &iDataBits
) < 0)
162 if (ioctl(dev
, SNDCTL_DSP_STEREO
, &iChannel
) < 0)
164 if (ioctl(dev
, SNDCTL_DSP_SPEED
, &ulSamplingRate
) < 0)
169 #endif // HAVE_SYS_SOUNDCARD_H
172 // ----------------------------------------------------------------------------
174 // ----------------------------------------------------------------------------
176 void wxSoundData::IncRef()
181 void wxSoundData::DecRef()
187 wxSoundData::~wxSoundData()
189 delete[] m_dataWithHeader
;
193 // ----------------------------------------------------------------------------
194 // wxSoundAsyncPlaybackThread
195 // ----------------------------------------------------------------------------
199 // mutex for all wxSound's synchronization
200 static wxMutex gs_soundMutex
;
202 // this class manages asynchronous playback of audio if the backend doesn't
203 // support it natively (e.g. OSS backend)
204 class wxSoundAsyncPlaybackThread
: public wxThread
207 wxSoundAsyncPlaybackThread(wxSoundBackend
*backend
,
208 wxSoundData
*data
, unsigned flags
)
209 : wxThread(), m_backend(backend
), m_data(data
), m_flags(flags
) {}
210 virtual ExitCode
Entry()
212 m_backend
->Play(m_data
, m_flags
& ~wxSOUND_ASYNC
);
213 wxMutexLocker
locker(gs_soundMutex
);
215 wxLogTrace(_T("sound"), _T("terminated async playback thread"));
220 wxSoundBackend
*m_backend
;
225 #endif // wxUSE_THREADS
227 // ----------------------------------------------------------------------------
229 // ----------------------------------------------------------------------------
231 wxSoundBackend
*wxSound::ms_backend
= NULL
;
233 // FIXME - temporary, until we have plugins architecture
236 wxDynamicLibrary
*wxSound::ms_backendSDL
= NULL
;
238 extern "C" wxSoundBackend
*wxCreateSoundBackendSDL();
242 wxSound::wxSound() : m_data(NULL
)
246 wxSound::wxSound(const wxString
& sFileName
, bool isResource
) : m_data(NULL
)
248 Create(sFileName
, isResource
);
251 wxSound::wxSound(int size
, const wxByte
* data
) : m_data(NULL
)
261 bool wxSound::Create(const wxString
& fileName
, bool isResource
)
263 wxASSERT_MSG( !isResource
,
264 _T("Loading sound from resources is only supported on Windows") );
269 if (!fileWave
.Open(fileName
, wxFile::read
))
274 size_t len
= fileWave
.Length();
275 wxUint8
*data
= new wxUint8
[len
];
276 if (fileWave
.Read(data
, len
) != len
)
278 wxLogError(_("Couldn't load sound data from '%s'."), fileName
.c_str());
282 if (!LoadWAV(data
, len
, false))
284 wxLogError(_("Sound file '%s' is in unsupported format."),
292 bool wxSound::Create(int size
, const wxByte
* data
)
294 wxASSERT( data
!= NULL
);
297 if (!LoadWAV(data
, size
, true))
299 wxLogError(_("Sound data are in unsupported format."));
305 /*static*/ void wxSound::EnsureBackend()
309 // FIXME -- make this fully dynamic when plugins architecture is in
311 #ifdef HAVE_SYS_SOUNDCARD_H
312 ms_backend
= new wxSoundBackendOSS();
313 if (!ms_backend
->IsAvailable())
315 wxDELETE(ms_backend
);
323 ms_backend
= wxCreateSoundBackendSDL();
326 dllname
.Printf(_T("%s/%s"),
327 wxDynamicLibrary::GetPluginsDirectory().c_str(),
328 wxDynamicLibrary::CanonicalizePluginName(
329 _T("sound_sdl"), wxDL_PLUGIN_BASE
).c_str());
330 wxLogTrace(_T("sound"),
331 _T("trying to load SDL plugin from '%s'..."),
334 ms_backendSDL
= new wxDynamicLibrary(dllname
, wxDL_NOW
);
335 if (!ms_backendSDL
->IsLoaded())
337 wxDELETE(ms_backendSDL
);
341 typedef wxSoundBackend
*(*wxCreateSoundBackend_t
)();
342 wxDYNLIB_FUNCTION(wxCreateSoundBackend_t
,
343 wxCreateSoundBackendSDL
, *ms_backendSDL
);
344 if (pfnwxCreateSoundBackendSDL
)
346 ms_backend
= (*pfnwxCreateSoundBackendSDL
)();
350 if (ms_backend
&& !ms_backend
->IsAvailable())
352 wxDELETE(ms_backend
);
358 ms_backend
= new wxSoundBackendNull();
360 wxLogTrace(_T("sound"),
361 _T("using backend '%s'"), ms_backend
->GetName().c_str());
365 /*static*/ void wxSound::UnloadBackend()
369 wxLogTrace(_T("sound"), _T("unloading backend"));
372 #if wxUSE_LIBSDL && wxUSE_PLUGINS
373 delete ms_backendSDL
;
378 bool wxSound::DoPlay(unsigned flags
)
380 wxCHECK_MSG( IsOk(), false, _T("Attempt to play invalid wave data") );
384 if ((flags
& wxSOUND_ASYNC
) && !ms_backend
->HasNativeAsyncPlayback())
387 wxMutexLocker
locker(gs_soundMutex
);
389 wxThread
*th
= new wxSoundAsyncPlaybackThread(ms_backend
, m_data
, flags
);
392 wxLogTrace(_T("sound"), _T("launched async playback thread"));
394 wxLogError(_("Unable to play sound asynchronously."));
400 ms_backend
->Play(m_data
, flags
);
408 wxMutexLocker
locker(gs_soundMutex
);
417 wxUint16 uiFormatTag
;
419 wxUint32 ulSamplesPerSec
;
420 wxUint32 ulAvgBytesPerSec
;
421 wxUint16 uiBlockAlign
;
422 wxUint16 uiBitsPerSample
;
425 #define MONO 1 // and stereo is 2 by wav format
426 #define WAVE_FORMAT_PCM 1
430 bool wxSound::LoadWAV(const wxUint8
*data
, size_t length
, bool copyData
)
432 WAVEFORMAT waveformat
;
435 if (length
< 32 + sizeof(WAVEFORMAT
))
438 memcpy(&waveformat
, &data
[FMT_INDEX
+ 4], sizeof(WAVEFORMAT
));
439 waveformat
.uiSize
= wxUINT32_SWAP_ON_BE(waveformat
.uiSize
);
440 waveformat
.uiFormatTag
= wxUINT16_SWAP_ON_BE(waveformat
.uiFormatTag
);
441 waveformat
.uiChannels
= wxUINT16_SWAP_ON_BE(waveformat
.uiChannels
);
442 waveformat
.ulSamplesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulSamplesPerSec
);
443 waveformat
.ulAvgBytesPerSec
= wxUINT32_SWAP_ON_BE(waveformat
.ulAvgBytesPerSec
);
444 waveformat
.uiBlockAlign
= wxUINT16_SWAP_ON_BE(waveformat
.uiBlockAlign
);
445 waveformat
.uiBitsPerSample
= wxUINT16_SWAP_ON_BE(waveformat
.uiBitsPerSample
);
447 if (memcmp(data
, "RIFF", 4) != 0)
449 if (memcmp(&data
[WAVE_INDEX
], "WAVE", 4) != 0)
451 if (memcmp(&data
[FMT_INDEX
], "fmt ", 4) != 0)
453 if (memcmp(&data
[FMT_INDEX
+ waveformat
.uiSize
+ 8], "data", 4) != 0)
455 memcpy(&ul
,&data
[FMT_INDEX
+ waveformat
.uiSize
+ 12], 4);
456 ul
= wxUINT32_SWAP_ON_BE(ul
);
458 //WAS: if (ul + FMT_INDEX + waveformat.uiSize + 16 != length)
459 if (ul
+ FMT_INDEX
+ waveformat
.uiSize
+ 16 > length
)
462 if (waveformat
.uiFormatTag
!= WAVE_FORMAT_PCM
)
465 if (waveformat
.ulSamplesPerSec
!=
466 waveformat
.ulAvgBytesPerSec
/ waveformat
.uiBlockAlign
)
469 m_data
= new wxSoundData
;
470 m_data
->m_channels
= waveformat
.uiChannels
;
471 m_data
->m_samplingRate
= waveformat
.ulSamplesPerSec
;
472 m_data
->m_bitsPerSample
= waveformat
.uiBitsPerSample
;
473 m_data
->m_samples
= ul
/ (m_data
->m_channels
* m_data
->m_bitsPerSample
/ 8);
474 m_data
->m_dataBytes
= ul
;
478 m_data
->m_dataWithHeader
= new wxUint8
[length
];
479 memcpy(m_data
->m_dataWithHeader
, data
, length
);
482 m_data
->m_dataWithHeader
= (wxUint8
*)data
;
485 (&m_data
->m_dataWithHeader
[FMT_INDEX
+ waveformat
.uiSize
+ 8]);
491 // ----------------------------------------------------------------------------
492 // wxSoundCleanupModule
493 // ----------------------------------------------------------------------------
495 class wxSoundCleanupModule
: public wxModule
498 bool OnInit() { return true; }
499 void OnExit() { wxSound::UnloadBackend(); }
500 DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule
)
503 IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule
, wxModule
)