1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/osx/carbon/sound.cpp
3 // Purpose:     wxSound class implementation: optional
4 // Author:      Ryan Norton
5 // Modified by: Stefan Csomor
6 // Created:     1998-01-01
7 // Copyright:   (c) Ryan Norton
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 #if wxUSE_SOUND
15 
16 #if wxOSX_USE_QUICKTIME
17 
18 #include "wx/sound.h"
19 
20 #ifndef WX_PRECOMP
21     #include "wx/object.h"
22     #include "wx/string.h"
23     #include "wx/intl.h"
24     #include "wx/log.h"
25     #include "wx/timer.h"
26 #endif
27 
28 #include "wx/file.h"
29 
30 // Carbon QT Implementation Details -
31 //
32 // Memory:
33 // 1) OpenDefaultComponent(MovieImportType, kQTFileTypeWave);
34 // 2) NewMovie(0);
35 // 3) MovieImportDataRef() //Pass Memory Location to this
36 // 4) PlayMovie();
37 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
38 //
39 // File:
40 // 1) Path as CFString
41 // 2) Call QTNewDataReferenceFromFullPathCFString
42 // 3) Call NewMovieFromDataRef
43 // 4) Call CloseMovieFile
44 // 4) PlayMovie();
45 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
46 //
47 
48 #ifdef __WXMAC__
49   #include "wx/osx/private.h"
50   #if wxOSX_USE_COCOA_OR_CARBON
51     #include <QuickTime/QuickTimeComponents.h>
52   #endif
53 #else
54   #include <qtml.h>
55 #endif
56 
57 #define MOVIE_DELAY 100
58 
59 // ------------------------------------------------------------------
60 //          SoundManager
61 // ------------------------------------------------------------------
62 
63 class wxOSXSoundManagerSoundData : public wxSoundData
64 {
65 public:
66     wxOSXSoundManagerSoundData(const wxString& fileName);
67     ~wxOSXSoundManagerSoundData();
68 
69     virtual bool Play(unsigned flags);
70     virtual void SoundTask();
71 
72     void        DoStop();
73 protected:
74     SndListHandle m_hSnd;
75     SndChannelPtr m_pSndChannel;
76 };
77 
wxOSXSoundManagerSoundData(const wxString & fileName)78 wxOSXSoundManagerSoundData::wxOSXSoundManagerSoundData(const wxString& fileName) :
79     m_pSndChannel(NULL)
80 {
81     Str255 lpSnd ;
82 
83     wxMacStringToPascal( fileName , lpSnd ) ;
84 
85     m_hSnd = (SndListHandle) GetNamedResource('snd ', (const unsigned char *) lpSnd);
86 }
87 
~wxOSXSoundManagerSoundData()88 wxOSXSoundManagerSoundData::~wxOSXSoundManagerSoundData()
89 {
90     DoStop();
91     ReleaseResource((Handle)m_hSnd);
92 }
93 
DoStop()94 void wxOSXSoundManagerSoundData::DoStop()
95 {
96     if ( m_pSndChannel )
97     {
98         SndDisposeChannel(m_pSndChannel, TRUE /* stop immediately, not after playing */);
99         m_pSndChannel = NULL;
100         wxSound::SoundStopped(this);
101     }
102 
103     if (IsMarkedForDeletion())
104         delete this;
105 }
106 
Play(unsigned flags)107 bool wxOSXSoundManagerSoundData::Play(unsigned flags)
108 {
109     Stop();
110 
111     m_flags = flags;
112 
113     SoundComponentData data;
114     unsigned long numframes, offset;
115 
116     ParseSndHeader((SndListHandle)m_hSnd, &data, &numframes, &offset);
117 
118     SndNewChannel(&m_pSndChannel, sampledSynth,
119                   initNoInterp
120                   + (data.numChannels == 1 ? initMono : initStereo), NULL);
121 
122     if(SndPlay(m_pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr)
123         return false;
124 
125     if (flags & wxSOUND_ASYNC)
126         CreateAndStartTimer();
127     else
128         DoStop();
129 
130     return true;
131 }
132 
SoundTask()133 void wxOSXSoundManagerSoundData::SoundTask()
134 {
135     SCStatus stat;
136 
137     if (SndChannelStatus((SndChannelPtr)m_pSndChannel, sizeof(SCStatus), &stat) != 0)
138         Stop();
139 
140     //if the sound isn't playing anymore, see if it's looped,
141     //and if so play it again, otherwise close things up
142     if (stat.scChannelBusy == FALSE)
143     {
144         if (m_flags & wxSOUND_LOOP)
145         {
146             if(SndPlay((SndChannelPtr)m_pSndChannel, (SndListHandle) m_hSnd, true) != noErr)
147                 Stop();
148         }
149         else
150             Stop();
151     }
152 }
153 
154 // ------------------------------------------------------------------
155 //          QuickTime
156 // ------------------------------------------------------------------
157 
158 bool wxInitQT();
wxInitQT()159 bool wxInitQT()
160 {
161 #ifndef __WXMAC__
162     int nError;
163     //-2093 no dll
164     if ((nError = InitializeQTML(0)) != noErr)
165     {
166         wxLogSysError(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError));
167         return false;
168     }
169 #endif
170     EnterMovies();
171     return true;
172 }
173 
174 void wxExitQT();
wxExitQT()175 void wxExitQT()
176 {
177     //Note that ExitMovies() is not necessary, but
178     //the docs are fuzzy on whether or not TerminateQTML is
179     ExitMovies();
180 
181 #ifndef __WXMAC__
182     TerminateQTML();
183 #endif
184 }
185 
186 class wxOSXQuickTimeSoundData : public wxSoundData
187 {
188 public:
189     wxOSXQuickTimeSoundData(const wxString& fileName);
190     wxOSXQuickTimeSoundData(size_t size, const void* data);
191     ~wxOSXQuickTimeSoundData();
192 
193     virtual bool Play(unsigned flags);
194     virtual void SoundTask();
195     virtual void DoStop();
196 protected:
197     Movie m_movie;
198 
199     wxString m_sndname; //file path
200     Handle m_soundHandle;
201 };
202 
203 
wxOSXQuickTimeSoundData(const wxString & fileName)204 wxOSXQuickTimeSoundData::wxOSXQuickTimeSoundData(const wxString& fileName) :
205     m_movie(NULL), m_soundHandle(NULL)
206 {
207     m_sndname = fileName;
208 }
209 
wxOSXQuickTimeSoundData(size_t size,const void * data)210 wxOSXQuickTimeSoundData::wxOSXQuickTimeSoundData(size_t size, const void* data) :
211     m_movie(NULL)
212 {
213     m_soundHandle = NewHandleClear((Size)size);
214     BlockMove(data, *m_soundHandle, size);
215 }
216 
~wxOSXQuickTimeSoundData()217 wxOSXQuickTimeSoundData::~wxOSXQuickTimeSoundData()
218 {
219     if ( m_soundHandle )
220         DisposeHandle(m_soundHandle);
221 }
222 
Play(unsigned flags)223 bool wxOSXQuickTimeSoundData::Play(unsigned flags)
224 {
225     if ( m_movie )
226         Stop();
227 
228     m_flags = flags;
229 
230     if (!wxInitQT())
231         return false;
232 
233     if( m_soundHandle )
234     {
235         Handle dataRef = nil;
236         MovieImportComponent miComponent;
237         Track targetTrack = nil;
238         TimeValue addedDuration = 0;
239         long outFlags = 0;
240         OSErr err;
241         ComponentResult result;
242 
243         err = PtrToHand(&m_soundHandle, &dataRef, sizeof(Handle));
244 
245         HLock(m_soundHandle);
246         if (memcmp(&(*m_soundHandle)[8], "WAVE", 4) == 0)
247             miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeWave);
248         else if (memcmp(&(*m_soundHandle)[8], "AIFF", 4) == 0)
249             miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFF);
250         else if (memcmp(&(*m_soundHandle)[8], "AIFC", 4) == 0)
251             miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFC);
252         else
253         {
254             HUnlock(m_soundHandle);
255             wxLogSysError(wxT("wxSound - Location in memory does not contain valid data"));
256             return false;
257         }
258 
259         HUnlock(m_soundHandle);
260         m_movie = NewMovie(0);
261 
262         result = MovieImportDataRef(miComponent,                dataRef,
263                                     HandleDataHandlerSubType,   m_movie,
264                                     nil,                        &targetTrack,
265                                     nil,                        &addedDuration,
266                                     movieImportCreateTrack,     &outFlags);
267 
268         if (result != noErr)
269         {
270             wxLogSysError(wxString::Format(wxT("Couldn't import movie data\nError:%i"), (int)result));
271         }
272 
273         SetMovieVolume(m_movie, kFullVolume);
274         GoToBeginningOfMovie(m_movie);
275     }
276     else
277     {
278         OSErr err = noErr ;
279 
280         Handle dataRef = NULL;
281         OSType dataRefType;
282 
283         err = QTNewDataReferenceFromFullPathCFString(wxCFStringRef(m_sndname,wxLocale::GetSystemEncoding()),
284                                                      (UInt32)kQTNativeDefaultPathStyle, 0, &dataRef, &dataRefType);
285 
286         wxASSERT(err == noErr);
287 
288         if (NULL != dataRef || err != noErr)
289         {
290             err = NewMovieFromDataRef( &m_movie, newMovieDontAskUnresolvedDataRefs , NULL, dataRef, dataRefType );
291             wxASSERT(err == noErr);
292             DisposeHandle(dataRef);
293         }
294 
295         if (err != noErr)
296         {
297             wxLogSysError(
298                           wxString::Format(wxT("wxSound - Could not open file: %s\nError:%i"), m_sndname.c_str(), err )
299                           );
300             return false;
301         }
302     }
303 
304     //Start the m_movie!
305     StartMovie(m_movie);
306 
307     if (flags & wxSOUND_ASYNC)
308     {
309         CreateAndStartTimer();
310     }
311     else
312     {
313         wxASSERT_MSG(!(flags & wxSOUND_LOOP), wxT("Can't loop and play syncronously at the same time"));
314 
315         //Play movie until it ends, then exit
316         //Note that due to quicktime caching this may not always
317         //work 100% correctly
318         while (!IsMovieDone(m_movie))
319             MoviesTask(m_movie, 1);
320 
321         DoStop();
322     }
323 
324     return true;
325 }
326 
DoStop()327 void wxOSXQuickTimeSoundData::DoStop()
328 {
329     if( m_movie )
330     {
331         StopMovie(m_movie);
332         DisposeMovie(m_movie);
333         m_movie = NULL;
334         wxSound::SoundStopped(this);
335         wxExitQT();
336     }
337 }
338 
SoundTask()339 void wxOSXQuickTimeSoundData::SoundTask()
340 {
341     if(IsMovieDone(m_movie))
342     {
343         if (m_flags & wxSOUND_LOOP)
344         {
345             StopMovie(m_movie);
346             GoToBeginningOfMovie(m_movie);
347             StartMovie(m_movie);
348         }
349         else
350             Stop();
351     }
352     else
353         MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie
354 }
355 
Create(size_t size,const void * data)356 bool wxSound::Create(size_t size, const void* data)
357 {
358     m_data = new wxOSXQuickTimeSoundData(size,data);
359     return true;
360 }
361 
Create(const wxString & fileName,bool isResource)362 bool wxSound::Create(const wxString& fileName, bool isResource)
363 {
364     if ( isResource )
365         m_data = new wxOSXSoundManagerSoundData(fileName);
366     else
367         m_data = new wxOSXQuickTimeSoundData(fileName);
368     return true;
369 }
370 
371 #endif // wxOSX_USE_QUICKTIME
372 
373 #endif //wxUSE_SOUND
374