1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/unix/sound.cpp
3 // Purpose:     wxSound
4 // Author:      Marcel Rasche, Vaclav Slavik
5 // Modified by:
6 // Created:     25/10/98
7 // Copyright:   (c) Julian Smart, Open Source Applications Foundation
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // for compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 #if defined(__BORLANDC__)
15     #pragma hdrstop
16 #endif
17 
18 #if wxUSE_SOUND
19 
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <sys/ioctl.h>
24 
25 #ifdef HAVE_SYS_SOUNDCARD_H
26 #include <sys/soundcard.h>
27 #endif
28 
29 #ifndef WX_PRECOMP
30     #include "wx/event.h"
31     #include "wx/intl.h"
32     #include "wx/log.h"
33     #include "wx/module.h"
34 #endif
35 
36 #include "wx/thread.h"
37 #include "wx/file.h"
38 #include "wx/sound.h"
39 #include "wx/dynlib.h"
40 
41 
42 #if wxUSE_THREADS
43 // mutex for all wxSound's synchronization
44 static wxMutex gs_soundMutex;
45 #endif
46 
47 // ----------------------------------------------------------------------------
48 // wxSoundData
49 // ----------------------------------------------------------------------------
50 
IncRef()51 void wxSoundData::IncRef()
52 {
53 #if wxUSE_THREADS
54     wxMutexLocker locker(gs_soundMutex);
55 #endif
56     m_refCnt++;
57 }
58 
DecRef()59 void wxSoundData::DecRef()
60 {
61 #if wxUSE_THREADS
62     wxMutexLocker locker(gs_soundMutex);
63 #endif
64     if (--m_refCnt == 0)
65         delete this;
66 }
67 
~wxSoundData()68 wxSoundData::~wxSoundData()
69 {
70     delete[] m_dataWithHeader;
71 }
72 
73 
74 // ----------------------------------------------------------------------------
75 // wxSoundBackendNull, used in absence of audio API or card
76 // ----------------------------------------------------------------------------
77 
78 class wxSoundBackendNull : public wxSoundBackend
79 {
80 public:
GetName() const81     wxString GetName() const { return _("No sound"); }
GetPriority() const82     int GetPriority() const { return 0; }
IsAvailable() const83     bool IsAvailable() const { return true; }
HasNativeAsyncPlayback() const84     bool HasNativeAsyncPlayback() const { return true; }
Play(wxSoundData * WXUNUSED (data),unsigned WXUNUSED (flags),volatile wxSoundPlaybackStatus * WXUNUSED (status))85     bool Play(wxSoundData *WXUNUSED(data), unsigned WXUNUSED(flags),
86               volatile wxSoundPlaybackStatus *WXUNUSED(status))
87         { return true; }
Stop()88     void Stop() {}
IsPlaying() const89     bool IsPlaying() const { return false; }
90 };
91 
92 
93 // ----------------------------------------------------------------------------
94 // wxSoundBackendOSS, for Linux
95 // ----------------------------------------------------------------------------
96 
97 #ifdef HAVE_SYS_SOUNDCARD_H
98 
99 #ifndef AUDIODEV
100 #define AUDIODEV   "/dev/dsp"    // Default path for audio device
101 #endif
102 
103 class wxSoundBackendOSS : public wxSoundBackend
104 {
105 public:
GetName() const106     wxString GetName() const { return wxT("Open Sound System"); }
GetPriority() const107     int GetPriority() const { return 10; }
108     bool IsAvailable() const;
HasNativeAsyncPlayback() const109     bool HasNativeAsyncPlayback() const { return false; }
110     bool Play(wxSoundData *data, unsigned flags,
111               volatile wxSoundPlaybackStatus *status);
Stop()112     void Stop() {}
IsPlaying() const113     bool IsPlaying() const { return false; }
114 
115 private:
116     int OpenDSP(const wxSoundData *data);
117     bool InitDSP(int dev, const wxSoundData *data);
118 
119     int m_DSPblkSize;        // Size of the DSP buffer
120     bool m_needConversion;
121 };
122 
IsAvailable() const123 bool wxSoundBackendOSS::IsAvailable() const
124 {
125     int fd;
126     fd = open(AUDIODEV, O_WRONLY | O_NONBLOCK);
127     if (fd < 0)
128         return false;
129     close(fd);
130     return true;
131 }
132 
Play(wxSoundData * data,unsigned flags,volatile wxSoundPlaybackStatus * status)133 bool wxSoundBackendOSS::Play(wxSoundData *data, unsigned flags,
134                              volatile wxSoundPlaybackStatus *status)
135 {
136     int dev = OpenDSP(data);
137 
138     if (dev < 0)
139         return false;
140 
141     ioctl(dev, SNDCTL_DSP_SYNC, 0);
142 
143     do
144     {
145         bool play = true;
146         int i;
147         unsigned l = 0;
148         size_t datasize = data->m_dataBytes;
149 
150         do
151         {
152             if (status->m_stopRequested)
153             {
154                 wxLogTrace(wxT("sound"), wxT("playback stopped"));
155                 close(dev);
156                 return true;
157             }
158 
159             i= (int)((l + m_DSPblkSize) < datasize ?
160                      m_DSPblkSize : (datasize - l));
161             if (write(dev, &data->m_data[l], i) != i)
162             {
163                 play = false;
164             }
165             l += i;
166         } while (play && l < datasize);
167     } while (flags & wxSOUND_LOOP);
168 
169     close(dev);
170     return true;
171 }
172 
OpenDSP(const wxSoundData * data)173 int wxSoundBackendOSS::OpenDSP(const wxSoundData *data)
174 {
175     int dev = -1;
176 
177     if ((dev = open(AUDIODEV, O_WRONLY, 0)) <0)
178         return -1;
179 
180     if (!InitDSP(dev, data) || m_needConversion)
181     {
182         close(dev);
183         return -1;
184     }
185 
186     return dev;
187 }
188 
189 
InitDSP(int dev,const wxSoundData * data)190 bool wxSoundBackendOSS::InitDSP(int dev, const wxSoundData *data)
191 {
192     unsigned tmp;
193 
194     // Reset the dsp
195     if (ioctl(dev, SNDCTL_DSP_RESET, 0) < 0)
196     {
197         wxLogTrace(wxT("sound"), wxT("unable to reset dsp"));
198         return false;
199     }
200 
201     m_needConversion = false;
202 
203     tmp = data->m_bitsPerSample;
204     if (ioctl(dev, SNDCTL_DSP_SAMPLESIZE, &tmp) < 0)
205     {
206         wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_SAMPLESIZE)"));
207         return false;
208     }
209     if (tmp != data->m_bitsPerSample)
210     {
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;
215     }
216 
217     unsigned stereo = data->m_channels == 1 ? 0 : 1;
218     tmp = stereo;
219     if (ioctl(dev, SNDCTL_DSP_STEREO, &tmp) < 0)
220     {
221         wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_STEREO)"));
222         return false;
223     }
224     if (tmp != stereo)
225     {
226         wxLogTrace(wxT("sound"), wxT("Unable to set DSP to %s."), stereo?  wxT("stereo"):wxT("mono"));
227         m_needConversion = true;
228     }
229 
230     tmp = data->m_samplingRate;
231     if (ioctl(dev, SNDCTL_DSP_SPEED, &tmp) < 0)
232     {
233         wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_SPEED)"));
234        return false;
235     }
236     if (tmp != data->m_samplingRate)
237     {
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;
250         }
251     }
252 
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)
256     {
257         wxLogTrace(wxT("sound"), wxT("IOCTL failure (SNDCTL_DSP_GETBLKSIZE)"));
258         return false;
259     }
260     return true;
261 }
262 
263 #endif // HAVE_SYS_SOUNDCARD_H
264 
265 // ----------------------------------------------------------------------------
266 // wxSoundSyncOnlyAdaptor
267 // ----------------------------------------------------------------------------
268 
269 #if wxUSE_THREADS
270 
271 class wxSoundSyncOnlyAdaptor;
272 
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
276 {
277 public:
wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor * adaptor,wxSoundData * data,unsigned flags)278     wxSoundAsyncPlaybackThread(wxSoundSyncOnlyAdaptor *adaptor,
279                               wxSoundData *data, unsigned flags)
280         : wxThread(), m_adapt(adaptor), m_data(data), m_flags(flags) {}
281     virtual ExitCode Entry();
282 
283 protected:
284     wxSoundSyncOnlyAdaptor *m_adapt;
285     wxSoundData *m_data;
286     unsigned m_flags;
287 };
288 
289 #endif // wxUSE_THREADS
290 
291 // This class turns wxSoundBackend that doesn't support asynchronous playback
292 // into one that does
293 class wxSoundSyncOnlyAdaptor : public wxSoundBackend
294 {
295 public:
wxSoundSyncOnlyAdaptor(wxSoundBackend * backend)296     wxSoundSyncOnlyAdaptor(wxSoundBackend *backend)
297         : m_backend(backend), m_playing(false) {}
~wxSoundSyncOnlyAdaptor()298     virtual ~wxSoundSyncOnlyAdaptor()
299     {
300         delete m_backend;
301     }
GetName() const302     wxString GetName() const
303     {
304         return m_backend->GetName();
305     }
GetPriority() const306     int GetPriority() const
307     {
308         return m_backend->GetPriority();
309     }
IsAvailable() const310     bool IsAvailable() const
311     {
312         return m_backend->IsAvailable();
313     }
HasNativeAsyncPlayback() const314     bool HasNativeAsyncPlayback() const
315     {
316         return true;
317     }
318     bool Play(wxSoundData *data, unsigned flags,
319               volatile wxSoundPlaybackStatus *status);
320     void Stop();
321     bool IsPlaying() const;
322 
323 private:
324     friend class wxSoundAsyncPlaybackThread;
325 
326     wxSoundBackend *m_backend;
327     bool m_playing;
328 #if wxUSE_THREADS
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;
333 #endif
334 };
335 
336 
337 #if wxUSE_THREADS
Entry()338 wxThread::ExitCode wxSoundAsyncPlaybackThread::Entry()
339 {
340     m_adapt->m_backend->Play(m_data, m_flags & ~wxSOUND_ASYNC,
341                              &m_adapt->m_status);
342 
343     m_data->DecRef();
344     m_adapt->m_playing = false;
345     m_adapt->m_mutexRightToPlay.Unlock();
346     wxLogTrace(wxT("sound"), wxT("terminated async playback thread"));
347     return 0;
348 }
349 #endif
350 
Play(wxSoundData * data,unsigned flags,volatile wxSoundPlaybackStatus * status)351 bool wxSoundSyncOnlyAdaptor::Play(wxSoundData *data, unsigned flags,
352                                   volatile wxSoundPlaybackStatus *status)
353 {
354     Stop();
355     if (flags & wxSOUND_ASYNC)
356     {
357 #if wxUSE_THREADS
358         m_mutexRightToPlay.Lock();
359         m_status.m_playing = true;
360         m_status.m_stopRequested = false;
361         data->IncRef();
362         wxThread *th = new wxSoundAsyncPlaybackThread(this, data, flags);
363         th->Create();
364         th->Run();
365         wxLogTrace(wxT("sound"), wxT("launched async playback thread"));
366         return true;
367 #else
368         wxLogError(_("Unable to play sound asynchronously."));
369         return false;
370 #endif
371     }
372     else
373     {
374 #if wxUSE_THREADS
375         m_mutexRightToPlay.Lock();
376 #endif
377         bool rv = m_backend->Play(data, flags, status);
378 #if wxUSE_THREADS
379         m_mutexRightToPlay.Unlock();
380 #endif
381         return rv;
382     }
383 }
384 
Stop()385 void wxSoundSyncOnlyAdaptor::Stop()
386 {
387     wxLogTrace(wxT("sound"), wxT("asking audio to stop"));
388 
389 #if wxUSE_THREADS
390     // tell the player thread (if running) to stop playback ASAP:
391     m_status.m_stopRequested = true;
392 
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"));
400 #endif
401 }
402 
IsPlaying() const403 bool wxSoundSyncOnlyAdaptor::IsPlaying() const
404 {
405 #if wxUSE_THREADS
406     return m_status.m_playing;
407 #else
408     return false;
409 #endif
410 }
411 
412 
413 // ----------------------------------------------------------------------------
414 // wxSound
415 // ----------------------------------------------------------------------------
416 
417 wxSoundBackend *wxSound::ms_backend = NULL;
418 
419 // FIXME - temporary, until we have plugins architecture
420 #if wxUSE_LIBSDL
421     #if wxUSE_PLUGINS
422         wxDynamicLibrary *wxSound::ms_backendSDL = NULL;
423     #else
424         extern "C" wxSoundBackend *wxCreateSoundBackendSDL();
425     #endif
426 #endif
427 
wxSound()428 wxSound::wxSound() : m_data(NULL)
429 {
430 }
431 
wxSound(const wxString & sFileName,bool isResource)432 wxSound::wxSound(const wxString& sFileName, bool isResource) : m_data(NULL)
433 {
434     Create(sFileName, isResource);
435 }
436 
wxSound(size_t size,const void * data)437 wxSound::wxSound(size_t size, const void* data) : m_data(NULL)
438 {
439     Create(size, data);
440 }
441 
~wxSound()442 wxSound::~wxSound()
443 {
444     Free();
445 }
446 
Create(const wxString & fileName,bool WXUNUSED_UNLESS_DEBUG (isResource))447 bool wxSound::Create(const wxString& fileName,
448                      bool WXUNUSED_UNLESS_DEBUG(isResource))
449 {
450     wxASSERT_MSG( !isResource,
451              wxT("Loading sound from resources is only supported on Windows") );
452 
453     Free();
454 
455     wxFile fileWave;
456     if (!fileWave.Open(fileName, wxFile::read))
457     {
458         return false;
459     }
460 
461     wxFileOffset lenOrig = fileWave.Length();
462     if ( lenOrig == wxInvalidOffset )
463         return false;
464 
465     size_t len = wx_truncate_cast(size_t, lenOrig);
466     wxUint8 *data = new wxUint8[len];
467     if ( fileWave.Read(data, len) != lenOrig )
468     {
469         delete [] data;
470         wxLogError(_("Couldn't load sound data from '%s'."), fileName.c_str());
471         return false;
472     }
473 
474     if (!LoadWAV(data, len, false))
475     {
476         delete [] data;
477         wxLogError(_("Sound file '%s' is in unsupported format."),
478                    fileName.c_str());
479         return false;
480     }
481 
482     return true;
483 }
484 
Create(size_t size,const void * data)485 bool wxSound::Create(size_t size, const void* data)
486 {
487     wxASSERT( data != NULL );
488 
489     Free();
490     if (!LoadWAV(data, size, true))
491     {
492         wxLogError(_("Sound data are in unsupported format."));
493         return false;
494     }
495     return true;
496 }
497 
EnsureBackend()498 /*static*/ void wxSound::EnsureBackend()
499 {
500     if (!ms_backend)
501     {
502         // FIXME -- make this fully dynamic when plugins architecture is in
503         // place
504 #if wxUSE_LIBSDL
505         //if (!ms_backend)
506         {
507 #if !wxUSE_PLUGINS
508             ms_backend = wxCreateSoundBackendSDL();
509 #else
510             wxString dllname;
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'..."),
517                        dllname.c_str());
518             wxLogNull null;
519             ms_backendSDL = new wxDynamicLibrary(dllname, wxDL_NOW);
520             if (!ms_backendSDL->IsLoaded())
521             {
522                 wxDELETE(ms_backendSDL);
523             }
524             else
525             {
526                 typedef wxSoundBackend *(*wxCreateSoundBackend_t)();
527                 wxDYNLIB_FUNCTION(wxCreateSoundBackend_t,
528                                   wxCreateSoundBackendSDL, *ms_backendSDL);
529                 if (pfnwxCreateSoundBackendSDL)
530                 {
531                     ms_backend = (*pfnwxCreateSoundBackendSDL)();
532                 }
533             }
534 #endif
535             if (ms_backend && !ms_backend->IsAvailable())
536             {
537                 wxDELETE(ms_backend);
538             }
539         }
540 #endif
541 
542 #ifdef HAVE_SYS_SOUNDCARD_H
543         if (!ms_backend)
544         {
545             ms_backend = new wxSoundBackendOSS();
546             if (!ms_backend->IsAvailable())
547             {
548                 wxDELETE(ms_backend);
549             }
550         }
551 #endif
552 
553         if (!ms_backend)
554             ms_backend = new wxSoundBackendNull();
555 
556         if (!ms_backend->HasNativeAsyncPlayback())
557             ms_backend = new wxSoundSyncOnlyAdaptor(ms_backend);
558 
559         wxLogTrace(wxT("sound"),
560                    wxT("using backend '%s'"), ms_backend->GetName().c_str());
561     }
562 }
563 
UnloadBackend()564 /*static*/ void wxSound::UnloadBackend()
565 {
566     if (ms_backend)
567     {
568         wxLogTrace(wxT("sound"), wxT("unloading backend"));
569 
570         Stop();
571 
572         wxDELETE(ms_backend);
573 #if wxUSE_LIBSDL && wxUSE_PLUGINS
574         delete ms_backendSDL;
575 #endif
576     }
577 }
578 
DoPlay(unsigned flags) const579 bool wxSound::DoPlay(unsigned flags) const
580 {
581     wxCHECK_MSG( IsOk(), false, wxT("Attempt to play invalid wave data") );
582 
583     EnsureBackend();
584     wxSoundPlaybackStatus status;
585     status.m_playing = true;
586     status.m_stopRequested = false;
587     return ms_backend->Play(m_data, flags, &status);
588 }
589 
Stop()590 /*static*/ void wxSound::Stop()
591 {
592     if (ms_backend)
593         ms_backend->Stop();
594 }
595 
IsPlaying()596 /*static*/ bool wxSound::IsPlaying()
597 {
598     if (ms_backend)
599         return ms_backend->IsPlaying();
600     else
601         return false;
602 }
603 
Free()604 void wxSound::Free()
605 {
606     if (m_data)
607         m_data->DecRef();
608 }
609 
610 typedef struct
611 {
612     wxUint32      uiSize;
613     wxUint16      uiFormatTag;
614     wxUint16      uiChannels;
615     wxUint32      ulSamplesPerSec;
616     wxUint32      ulAvgBytesPerSec;
617     wxUint16      uiBlockAlign;
618     wxUint16      uiBitsPerSample;
619 } WAVEFORMAT;
620 
621 #define WAVE_FORMAT_PCM  1
622 #define WAVE_INDEX       8
623 #define FMT_INDEX       12
624 
LoadWAV(const void * data_,size_t length,bool copyData)625 bool wxSound::LoadWAV(const void* data_, size_t length, bool copyData)
626 {
627     // the simplest wave file header consists of 44 bytes:
628     //
629     //      0   "RIFF"
630     //      4   file size - 8
631     //      8   "WAVE"
632     //
633     //      12  "fmt "
634     //      16  chunk size                  |
635     //      20  format tag                  |
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             |
641     //
642     //      36  "data"
643     //      40  number of data bytes
644     //      44  (wave signal) data
645     //
646     // so check that we have at least as much
647     if ( length < 44 )
648         return false;
649 
650     const wxUint8* data = static_cast<const wxUint8*>(data_);
651 
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);
661 
662     if (memcmp(data, "RIFF", 4) != 0)
663         return false;
664     if (memcmp(&data[WAVE_INDEX], "WAVE", 4) != 0)
665         return false;
666     if (memcmp(&data[FMT_INDEX], "fmt ", 4) != 0)
667         return false;
668 
669     // Check that the format chunk size is correct: it must be 16 for PCM,
670     // which is the only format we handle.
671     if (waveformat.uiSize != 16)
672         return false;
673 
674     if (memcmp(&data[FMT_INDEX + waveformat.uiSize + 8], "data", 4) != 0)
675         return false;
676 
677     if (waveformat.uiFormatTag != WAVE_FORMAT_PCM)
678         return false;
679 
680     if (waveformat.ulAvgBytesPerSec !=
681         waveformat.ulSamplesPerSec * waveformat.uiBlockAlign)
682         return false;
683 
684     // We divide by the sample size below to obtain the number of samples, so
685     // it definitely can't be 0. Also take care to avoid integer overflow when
686     // computing it.
687     unsigned tmp = waveformat.uiChannels;
688     if (tmp >= 0x10000)
689         return false;
690 
691     tmp *= waveformat.uiBitsPerSample;
692 
693     wxUint32 const sampleSize = tmp / 8;
694     if (!sampleSize)
695         return false;
696 
697     // get file size from header
698     wxUint32 chunkSize;
699     memcpy(&chunkSize, &data[4], 4);
700     chunkSize = wxUINT32_SWAP_ON_BE(chunkSize);
701 
702     // ensure file length is at least length in header
703     if (chunkSize > length - 8)
704         return false;
705 
706     // get the sound data size
707     wxUint32 ul;
708     memcpy(&ul, &data[FMT_INDEX + waveformat.uiSize + 12], 4);
709     ul = wxUINT32_SWAP_ON_BE(ul);
710 
711     // ensure we actually have at least that much data in the input
712     if (ul > length - FMT_INDEX - waveformat.uiSize - 16)
713         return false;
714 
715     m_data = new wxSoundData;
716     m_data->m_channels = waveformat.uiChannels;
717     m_data->m_samplingRate = waveformat.ulSamplesPerSec;
718     m_data->m_bitsPerSample = waveformat.uiBitsPerSample;
719     m_data->m_samples = ul / sampleSize;
720     m_data->m_dataBytes = ul;
721 
722     if (copyData)
723     {
724         m_data->m_dataWithHeader = new wxUint8[length];
725         memcpy(m_data->m_dataWithHeader, data, length);
726     }
727     else
728         m_data->m_dataWithHeader = (wxUint8*)data;
729 
730     m_data->m_data =
731         (&m_data->m_dataWithHeader[FMT_INDEX + waveformat.uiSize + 8]);
732 
733     return true;
734 }
735 
736 
737 // ----------------------------------------------------------------------------
738 // wxSoundCleanupModule
739 // ----------------------------------------------------------------------------
740 
741 class wxSoundCleanupModule: public wxModule
742 {
743 public:
OnInit()744     bool OnInit() { return true; }
OnExit()745     void OnExit() { wxSound::UnloadBackend(); }
746     DECLARE_DYNAMIC_CLASS(wxSoundCleanupModule)
747 };
748 
749 IMPLEMENT_DYNAMIC_CLASS(wxSoundCleanupModule, wxModule)
750 
751 #endif
752