]> git.saurik.com Git - wxWidgets.git/blame - src/unix/sound.cpp
don't assert when creating an initially empty wxCB_READONLY combobox
[wxWidgets.git] / src / unix / sound.cpp
CommitLineData
9be32e8f
VS
1/////////////////////////////////////////////////////////////////////////////
2// Name: sound.cpp
3// Purpose: wxSound
4// Author: Marcel Rasche, Vaclav Slavik
5// Modified by:
6// Created: 25/10/98
7// RCS-ID: $Id$
f156e20c 8// Copyright: (c) Julian Smart, Open Source Applications Foundation
9be32e8f
VS
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13#pragma implementation "sound.h"
14#pragma implementation "soundbase.h"
15#endif
16
17// for compilers that support precompilation, includes "wx.h".
18#include "wx/wxprec.h"
19
20#include "wx/setup.h"
21
22#if defined(__BORLANDC__)
23#pragma hdrstop
24#endif
25
f156e20c 26#if wxUSE_SOUND
9be32e8f
VS
27
28#include <stdio.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <sys/ioctl.h>
32
83f7f12d 33#ifdef HAVE_SYS_SOUNDCARD_H
9be32e8f
VS
34#include <sys/soundcard.h>
35#endif
36
37#ifndef WX_PRECOMP
38 #include "wx/event.h"
39 #include "wx/intl.h"
40 #include "wx/log.h"
41#endif
42
43#include "wx/thread.h"
44#include "wx/file.h"
45#include "wx/module.h"
46#include "wx/sound.h"
47#include "wx/dynlib.h"
48
83f7f12d
VS
49
50#if wxUSE_THREADS
51// mutex for all wxSound's synchronization
52static wxMutex gs_soundMutex;
53#endif
54
55// ----------------------------------------------------------------------------
56// wxSoundData
57// ----------------------------------------------------------------------------
58
59void wxSoundData::IncRef()
60{
61#if wxUSE_THREADS
62 wxMutexLocker locker(gs_soundMutex);
63#endif
64 m_refCnt++;
65}
66
67void wxSoundData::DecRef()
68{
69#if wxUSE_THREADS
70 wxMutexLocker locker(gs_soundMutex);
71#endif
72 if (--m_refCnt == 0)
73 delete this;
74}
75
76wxSoundData::~wxSoundData()
77{
78 delete[] m_dataWithHeader;
79}
80
81
9be32e8f
VS
82// ----------------------------------------------------------------------------
83// wxSoundBackendNull, used in absence of audio API or card
84// ----------------------------------------------------------------------------
85
86class wxSoundBackendNull : public wxSoundBackend
87{
88public:
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; }
83f7f12d
VS
93 bool Play(wxSoundData *WXUNUSED(data), unsigned WXUNUSED(flags),
94 volatile wxSoundPlaybackStatus *WXUNUSED(status))
9be32e8f 95 { return true; }
83f7f12d
VS
96 void Stop() {}
97 bool IsPlaying() const { return false; }
9be32e8f
VS
98};
99
100
101// ----------------------------------------------------------------------------
102// wxSoundBackendOSS, for Linux
103// ----------------------------------------------------------------------------
104
105#ifdef HAVE_SYS_SOUNDCARD_H
106
107#ifndef AUDIODEV
108#define AUDIODEV "/dev/dsp" // Default path for audio device
109#endif
110
111class wxSoundBackendOSS : public wxSoundBackend
112{
113public:
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; }
83f7f12d
VS
118 bool Play(wxSoundData *data, unsigned flags,
119 volatile wxSoundPlaybackStatus *status);
120 void Stop() {}
121 bool IsPlaying() const { return false; }
9be32e8f
VS
122
123private:
124 int OpenDSP(const wxSoundData *data);
125 bool InitDSP(int dev, int iDataBits, int iChannel,
126 unsigned long ulSamplingRate);
127
128 int m_DSPblkSize; // Size of the DSP buffer
129};
130
131bool wxSoundBackendOSS::IsAvailable() const
132{
133 int fd;
134 fd = open(AUDIODEV, O_WRONLY | O_NONBLOCK);
135 if (fd < 0)
136 return false;
137 close(fd);
138 return true;
139}
140
83f7f12d
VS
141bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags,
142 volatile wxSoundPlaybackStatus *status)
9be32e8f
VS
143{
144 int dev = OpenDSP(data);
145
146 if (dev < 0)
147 return false;
148
149 ioctl(dev, SNDCTL_DSP_SYNC, 0);
150
151 do
152 {
153 bool play = true;
154 int i;
155 unsigned l = 0;
156 size_t datasize = data->m_dataBytes;
157
158 do
159 {
83f7f12d
VS
160 if (status->m_stopRequested)
161 {
162 wxLogTrace(_T("sound"), _T("playback stopped"));
163 close(dev);
164 return true;
165 }
166
9be32e8f
VS
167 i= (int)((l + m_DSPblkSize) < datasize ?
168 m_DSPblkSize : (datasize - l));
169 if (write(dev, &data->m_data[l], i) != i)
170 {
171 play = false;
172 }
173 l += i;
174 } while (play && l < datasize);
175 } while (flags & wxSOUND_LOOP);
176
177 close(dev);
83f7f12d 178
9be32e8f
VS
179 return true;
180}
181
182int wxSoundBackendOSS::OpenDSP(const wxSoundData *data)
183{
184 int dev = -1;
185
186 if ((dev = open(AUDIODEV, O_WRONLY, 0)) <0)
187 return -1;
188
189 if (!InitDSP(dev,
190 (int)data->m_bitsPerSample,
191 data->m_channels == 1 ? 0 : 1,
192 data->m_samplingRate))
193 {
194 close(dev);
195 return -1;
196 }
197
198 return dev;
199}
200
201bool wxSoundBackendOSS::InitDSP(int dev, int iDataBits, int iChannel,
202 unsigned long ulSamplingRate)
203{
204 if (ioctl(dev, SNDCTL_DSP_GETBLKSIZE, &m_DSPblkSize) < 0)
205 return false;
83f7f12d 206 wxLogTrace(_T("sound"), _T("OSS block size: %i"), m_DSPblkSize);
9be32e8f
VS
207 if (m_DSPblkSize < 4096 || m_DSPblkSize > 65536)
208 return false;
209 if (ioctl(dev, SNDCTL_DSP_SAMPLESIZE, &iDataBits) < 0)
210 return false;
211 if (ioctl(dev, SNDCTL_DSP_STEREO, &iChannel) < 0)
212 return false;
213 if (ioctl(dev, SNDCTL_DSP_SPEED, &ulSamplingRate) < 0)
214 return false;
215 return true;
216}
217
218#endif // HAVE_SYS_SOUNDCARD_H
219
9be32e8f 220// ----------------------------------------------------------------------------
83f7f12d 221// wxSoundSyncOnlyAdaptor
9be32e8f
VS
222// ----------------------------------------------------------------------------
223
224#if wxUSE_THREADS
225
83f7f12d 226class wxSoundSyncOnlyAdaptor;
9be32e8f
VS
227
228// this class manages asynchronous playback of audio if the backend doesn't
229// support it natively (e.g. OSS backend)
230class wxSoundAsyncPlaybackThread : public wxThread
231{
232public:
83f7f12d 233 wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor *adaptor,
9be32e8f 234 wxSoundData *data, unsigned flags)
83f7f12d
VS
235 : wxThread(), m_adapt(adaptor), m_data(data), m_flags(flags) {}
236 virtual ExitCode Entry();
9be32e8f
VS
237
238protected:
83f7f12d 239 wxSoundSyncOnlyAdaptor *m_adapt;
9be32e8f
VS
240 wxSoundData *m_data;
241 unsigned m_flags;
242};
243
244#endif // wxUSE_THREADS
245
83f7f12d
VS
246// This class turns wxSoundBackend that doesn't support asynchronous playback
247// into one that does
248class wxSoundSyncOnlyAdaptor : public wxSoundBackend
249{
250public:
251 wxSoundSyncOnlyAdaptor(wxSoundBackend *backend)
252 : m_backend(backend), m_playing(false) {}
253 ~wxSoundSyncOnlyAdaptor()
254 {
255 delete m_backend;
256 }
257 wxString GetName() const
258 {
259 return m_backend->GetName();
260 }
261 int GetPriority() const
262 {
263 return m_backend->GetPriority();
264 }
265 bool IsAvailable() const
266 {
267 return m_backend->IsAvailable();
268 }
269 bool HasNativeAsyncPlayback() const
270 {
271 return true;
272 }
273 bool Play(wxSoundData *data, unsigned flags,
274 volatile wxSoundPlaybackStatus *status);
275 void Stop();
276 bool IsPlaying() const;
277
278private:
279 friend class wxSoundAsyncPlaybackThread;
280
281 wxSoundBackend *m_backend;
282 bool m_playing;
283#if wxUSE_THREADS
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;
288#endif
289};
290
291
292#if wxUSE_THREADS
293wxThread::ExitCode wxSoundAsyncPlaybackThread::Entry()
294{
295 m_adapt->m_backend->Play(m_data, m_flags & ~wxSOUND_ASYNC,
296 &m_adapt->m_status);
297
298 m_data->DecRef();
299 m_adapt->m_playing = false;
300 m_adapt->m_mutexRightToPlay.Unlock();
301 wxLogTrace(_T("sound"), _T("terminated async playback thread"));
302 return 0;
303}
304#endif
305
306bool wxSoundSyncOnlyAdaptor::Play(wxSoundData *data, unsigned flags,
307 volatile wxSoundPlaybackStatus *status)
308{
309 Stop();
310 if (flags & wxSOUND_ASYNC)
311 {
312#if wxUSE_THREADS
313 m_mutexRightToPlay.Lock();
314 m_status.m_playing = true;
315 m_status.m_stopRequested = false;
316 data->IncRef();
317 wxThread *th = new wxSoundAsyncPlaybackThread(this, data, flags);
318 th->Create();
319 th->Run();
320 wxLogTrace(_T("sound"), _T("launched async playback thread"));
321 return true;
322#else
323 wxLogError(_("Unable to play sound asynchronously."));
324 return false;
325#endif
326 }
327 else
328 {
329#if wxUSE_THREADS
330 m_mutexRightToPlay.Lock();
331#endif
332 bool rv = m_backend->Play(data, flags, status);
333#if wxUSE_THREADS
334 m_mutexRightToPlay.Unlock();
335#endif
336 return rv;
337 }
338}
339
340void wxSoundSyncOnlyAdaptor::Stop()
341{
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;
345
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"));
353}
354
355bool wxSoundSyncOnlyAdaptor::IsPlaying() const
356{
357 return m_status.m_playing;
358}
359
360
9be32e8f
VS
361// ----------------------------------------------------------------------------
362// wxSound
363// ----------------------------------------------------------------------------
364
365wxSoundBackend *wxSound::ms_backend = NULL;
366
367// FIXME - temporary, until we have plugins architecture
368#if wxUSE_LIBSDL
369 #if wxUSE_PLUGINS
370 wxDynamicLibrary *wxSound::ms_backendSDL = NULL;
371 #else
372 extern "C" wxSoundBackend *wxCreateSoundBackendSDL();
373 #endif
374#endif
375
376wxSound::wxSound() : m_data(NULL)
377{
378}
379
380wxSound::wxSound(const wxString& sFileName, bool isResource) : m_data(NULL)
381{
382 Create(sFileName, isResource);
383}
384
385wxSound::wxSound(int size, const wxByte* data) : m_data(NULL)
386{
387 Create(size, data);
388}
389
390wxSound::~wxSound()
391{
392 Free();
393}
394
395bool wxSound::Create(const wxString& fileName, bool isResource)
396{
397 wxASSERT_MSG( !isResource,
398 _T("Loading sound from resources is only supported on Windows") );
399
400 Free();
401
402 wxFile fileWave;
403 if (!fileWave.Open(fileName, wxFile::read))
404 {
405 return false;
406 }
407
408 size_t len = fileWave.Length();
409 wxUint8 *data = new wxUint8[len];
410 if (fileWave.Read(data, len) != len)
411 {
412 wxLogError(_("Couldn't load sound data from '%s'."), fileName.c_str());
413 return false;
414 }
415
416 if (!LoadWAV(data, len, false))
417 {
418 wxLogError(_("Sound file '%s' is in unsupported format."),
419 fileName.c_str());
420 return false;
421 }
422
423 return true;
424}
425
426bool wxSound::Create(int size, const wxByte* data)
427{
428 wxASSERT( data != NULL );
429
430 Free();
431 if (!LoadWAV(data, size, true))
432 {
433 wxLogError(_("Sound data are in unsupported format."));
434 return false;
435 }
436 return true;
437}
438
439/*static*/ void wxSound::EnsureBackend()
440{
441 if (!ms_backend)
442 {
443 // FIXME -- make this fully dynamic when plugins architecture is in
444 // place
445#ifdef HAVE_SYS_SOUNDCARD_H
446 ms_backend = new wxSoundBackendOSS();
447 if (!ms_backend->IsAvailable())
448 {
449 wxDELETE(ms_backend);
450 }
451#endif
452
453#if wxUSE_LIBSDL
454 if (!ms_backend)
455 {
456#if !wxUSE_PLUGINS
457 ms_backend = wxCreateSoundBackendSDL();
458#else
459 wxString dllname;
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'..."),
466 dllname.c_str());
467 wxLogNull null;
468 ms_backendSDL = new wxDynamicLibrary(dllname, wxDL_NOW);
469 if (!ms_backendSDL->IsLoaded())
470 {
471 wxDELETE(ms_backendSDL);
472 }
473 else
474 {
475 typedef wxSoundBackend *(*wxCreateSoundBackend_t)();
476 wxDYNLIB_FUNCTION(wxCreateSoundBackend_t,
477 wxCreateSoundBackendSDL, *ms_backendSDL);
478 if (pfnwxCreateSoundBackendSDL)
479 {
480 ms_backend = (*pfnwxCreateSoundBackendSDL)();
481 }
482 }
483#endif
484 if (ms_backend && !ms_backend->IsAvailable())
485 {
486 wxDELETE(ms_backend);
487 }
488 }
489#endif
490
491 if (!ms_backend)
492 ms_backend = new wxSoundBackendNull();
493
83f7f12d
VS
494 if (!ms_backend->HasNativeAsyncPlayback())
495 ms_backend = new wxSoundSyncOnlyAdaptor(ms_backend);
496
9be32e8f
VS
497 wxLogTrace(_T("sound"),
498 _T("using backend '%s'"), ms_backend->GetName().c_str());
499 }
500}
501
502/*static*/ void wxSound::UnloadBackend()
503{
504 if (ms_backend)
505 {
506 wxLogTrace(_T("sound"), _T("unloading backend"));
83f7f12d
VS
507
508 Stop();
509
9be32e8f
VS
510 delete ms_backend;
511 ms_backend = NULL;
512#if wxUSE_LIBSDL && wxUSE_PLUGINS
513 delete ms_backendSDL;
514#endif
515 }
516}
517
f156e20c 518bool wxSound::DoPlay(unsigned flags) const
9be32e8f
VS
519{
520 wxCHECK_MSG( IsOk(), false, _T("Attempt to play invalid wave data") );
521
522 EnsureBackend();
83f7f12d
VS
523 wxSoundPlaybackStatus status;
524 status.m_playing = true;
525 status.m_stopRequested = false;
526 return ms_backend->Play(m_data, flags, &status);
527}
9be32e8f 528
83f7f12d
VS
529/*static*/ void wxSound::Stop()
530{
531 if (ms_backend)
532 ms_backend->Stop();
533}
534
535/*static*/ bool wxSound::IsPlaying()
536{
537 if (ms_backend)
538 return ms_backend->IsPlaying();
9be32e8f 539 else
83f7f12d 540 return false;
9be32e8f
VS
541}
542
543void wxSound::Free()
544{
9be32e8f
VS
545 if (m_data)
546 m_data->DecRef();
547}
548
549typedef struct
550{
551 wxUint32 uiSize;
552 wxUint16 uiFormatTag;
553 wxUint16 uiChannels;
554 wxUint32 ulSamplesPerSec;
555 wxUint32 ulAvgBytesPerSec;
556 wxUint16 uiBlockAlign;
557 wxUint16 uiBitsPerSample;
83f7f12d 558} WAVEFORMAT;
9be32e8f
VS
559
560#define MONO 1 // and stereo is 2 by wav format
561#define WAVE_FORMAT_PCM 1
562#define WAVE_INDEX 8
563#define FMT_INDEX 12
564
565bool wxSound::LoadWAV(const wxUint8 *data, size_t length, bool copyData)
566{
567 WAVEFORMAT waveformat;
568 wxUint32 ul;
569
570 if (length < 32 + sizeof(WAVEFORMAT))
571 return false;
572
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);
581
582 if (memcmp(data, "RIFF", 4) != 0)
583 return false;
584 if (memcmp(&data[WAVE_INDEX], "WAVE", 4) != 0)
585 return false;
586 if (memcmp(&data[FMT_INDEX], "fmt ", 4) != 0)
587 return false;
588 if (memcmp(&data[FMT_INDEX + waveformat.uiSize + 8], "data", 4) != 0)
589 return false;
590 memcpy(&ul,&data[FMT_INDEX + waveformat.uiSize + 12], 4);
591 ul = wxUINT32_SWAP_ON_BE(ul);
592
593 //WAS: if (ul + FMT_INDEX + waveformat.uiSize + 16 != length)
594 if (ul + FMT_INDEX + waveformat.uiSize + 16 > length)
595 return false;
596
597 if (waveformat.uiFormatTag != WAVE_FORMAT_PCM)
598 return false;
599
600 if (waveformat.ulSamplesPerSec !=
601 waveformat.ulAvgBytesPerSec / waveformat.uiBlockAlign)
602 return false;
603
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;
610
611 if (copyData)
612 {
613 m_data->m_dataWithHeader = new wxUint8[length];
614 memcpy(m_data->m_dataWithHeader, data, length);
615 }
616 else
617 m_data->m_dataWithHeader = (wxUint8*)data;
618
619 m_data->m_data =
620 (&m_data->m_dataWithHeader[FMT_INDEX + waveformat.uiSize + 8]);
621
622 return true;
623}
624
625
626// ----------------------------------------------------------------------------
627// wxSoundCleanupModule
628// ----------------------------------------------------------------------------
629
630class wxSoundCleanupModule: public wxModule
631{
632public:
633 bool OnInit() { return true; }
634 void OnExit() { wxSound::UnloadBackend(); }
635 DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule)
636};
637
638IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule, wxModule)
639
640#endif