]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/sound.cpp
forward port from 2.8
[wxWidgets.git] / src / mac / carbon / sound.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/mac/carbon/sound.cpp
3 // Purpose: wxSound class implementation: optional
4 // Author: Ryan Norton
5 // Modified by: Stefan Csomor
6 // Created: 1998-01-01
7 // RCS-ID: $Id$
8 // Copyright: (c) Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #if wxUSE_SOUND
16
17 #include "wx/sound.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/object.h"
21 #include "wx/string.h"
22 #include "wx/intl.h"
23 #include "wx/log.h"
24 #include "wx/timer.h"
25 #endif
26
27 #include "wx/file.h"
28
29 // Carbon QT Implementation Details -
30 //
31 // Memory:
32 // 1) OpenDefaultComponent(MovieImportType, kQTFileTypeWave);
33 // 2) NewMovie(0);
34 // 3) MovieImportDataRef() //Pass Memory Location to this
35 // 4) PlayMovie();
36 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
37 //
38 // File:
39 // 1) Path as CFString
40 // 2) Call QTNewDataReferenceFromFullPathCFString
41 // 3) Call NewMovieFromDataRef
42 // 4) Call CloseMovieFile
43 // 4) PlayMovie();
44 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
45 //
46
47 #ifdef __WXMAC__
48 #include "wx/mac/uma.h"
49 #ifndef __DARWIN__
50 #include <Movies.h>
51 #include <Gestalt.h>
52 #endif
53 #endif
54
55 #if defined __WXMAC__ && defined __DARWIN__/*TARGET_CARBON*/
56 #ifdef __APPLE_CC__
57 #include <Carbon/Carbon.h>
58 #else
59 #include <Carbon.h>
60 #endif
61 #else
62 #include <Sound.h>
63 #endif
64
65 //quicktime media layer only required for mac emulation on pc
66 #ifndef __WXMAC__
67 #include <qtml.h>
68 #endif
69
70 #ifndef __DARWIN__
71 #include <QuickTimeComponents.h>
72 #else
73 #include <QuickTime/QuickTimeComponents.h>
74 #endif
75
76 //Time between timer calls
77 #define MOVIE_DELAY 100
78
79 static wxTimer* lastSoundTimer=NULL;
80 static bool lastSoundIsPlaying=false;
81
82 #if !defined(__DARWIN__) || !defined(__LP64__)
83 #define USE_QUICKTIME 1
84 #else
85 #define USE_QUICKTIME 0
86 #endif
87
88 #if USE_QUICKTIME
89 // ------------------------------------------------------------------
90 // wxQTTimer - Handle Asyncronous Playing
91 // ------------------------------------------------------------------
92 class wxQTTimer : public wxTimer
93 {
94 public:
95 wxQTTimer(Movie movie, bool bLoop, bool* playing) :
96 m_movie(movie), m_bLoop(bLoop), m_pbPlaying(playing)
97 {
98 }
99
100 virtual ~wxQTTimer()
101 {
102 if(m_pbPlaying)
103 *m_pbPlaying = false;
104
105 StopMovie(m_movie);
106 DisposeMovie(m_movie);
107 Stop();
108
109 //Note that ExitMovies() is not necessary, but
110 //the docs are fuzzy on whether or not TerminateQTML is
111 ExitMovies();
112
113 #ifndef __WXMAC__
114 TerminateQTML();
115 #endif
116 }
117
118 void Shutdown()
119 {
120 delete this;
121 }
122
123 void Notify()
124 {
125 if (m_pbPlaying && !*m_pbPlaying)
126 {
127 Shutdown();
128 }
129
130 if(IsMovieDone(m_movie))
131 {
132 if (!m_bLoop)
133 Shutdown();
134 else
135 {
136 StopMovie(m_movie);
137 GoToBeginningOfMovie(m_movie);
138 StartMovie(m_movie);
139 }
140 }
141 else
142 MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie
143 }
144
145
146 Movie& GetMovie() {return m_movie;}
147
148 protected:
149 Movie m_movie;
150 bool m_bLoop;
151
152 public:
153 bool* m_pbPlaying;
154
155 };
156
157
158 class wxSMTimer : public wxTimer
159 {
160 public:
161 wxSMTimer(void* hSnd, void* pSndChannel, bool bLoop, bool* playing)
162 : m_hSnd(hSnd), m_pSndChannel(pSndChannel), m_bLoop(bLoop), m_pbPlaying(playing)
163 {
164 }
165
166 virtual ~wxSMTimer()
167 {
168 if(m_pbPlaying)
169 *m_pbPlaying = false;
170 SndDisposeChannel((SndChannelPtr)m_pSndChannel, TRUE);
171 Stop();
172 }
173
174 void Notify()
175 {
176 if (m_pbPlaying && !*m_pbPlaying)
177 {
178 Shutdown();
179 }
180
181 SCStatus stat;
182
183 if (SndChannelStatus((SndChannelPtr)m_pSndChannel, sizeof(SCStatus), &stat) != 0)
184 Shutdown();
185
186 //if the sound isn't playing anymore, see if it's looped,
187 //and if so play it again, otherwise close things up
188 if (stat.scChannelBusy == FALSE)
189 {
190 if (m_bLoop)
191 {
192 if(SndPlay((SndChannelPtr)m_pSndChannel, (SndListHandle) m_hSnd, true) != noErr)
193 Shutdown();
194 }
195 else
196 Shutdown();
197 }
198 }
199
200 void Shutdown()
201 {
202 delete this;
203 }
204
205 void* GetChannel() {return m_pSndChannel;}
206
207 protected:
208 void* m_hSnd;
209 void* m_pSndChannel;
210 bool m_bLoop;
211
212 public:
213 bool* m_pbPlaying;
214 };
215
216 // ------------------------------------------------------------------
217 // wxSound
218 // ------------------------------------------------------------------
219
220 //Determines whether version 4 of QT is installed
221 Boolean wxIsQuickTime4Installed (void)
222 {
223 #ifdef __WXMAC__
224 short error;
225 long result;
226
227 error = Gestalt (gestaltQuickTime, &result);
228 return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
229 #else
230 return true;
231 #endif
232 }
233
234 inline bool wxInitQT ()
235 {
236 if (wxIsQuickTime4Installed())
237 {
238 #ifndef __WXMAC__
239 int nError;
240 //-2093 no dll
241 if ((nError = InitializeQTML(0)) != noErr)
242 wxLogSysError(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError));
243 #endif
244 EnterMovies();
245 return true;
246 }
247 else
248 {
249 wxLogSysError(wxT("Quicktime is not installed, or Your Version of Quicktime is <= 4."));
250 return false;
251 }
252 }
253
254 #endif
255
256 wxSound::wxSound()
257 : m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE)
258 {
259 }
260
261 wxSound::wxSound(const wxString& sFileName, bool isResource)
262 : m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE)
263 {
264 Create(sFileName, isResource);
265 }
266
267 wxSound::wxSound(int size, const wxByte* data)
268 : m_hSnd((char*)data), m_waveLength(size), m_pTimer(NULL), m_type(wxSound_MEMORY)
269 {
270 }
271
272 wxSound::~wxSound()
273 {
274 }
275
276 bool wxSound::Create(const wxString& fileName, bool isResource)
277 {
278 Stop();
279
280 if (isResource)
281 {
282 #ifdef __WXMAC__
283 m_type = wxSound_RESOURCE;
284
285 Str255 lpSnd ;
286
287 wxMacStringToPascal( fileName , lpSnd ) ;
288
289 m_sndname = fileName;
290 m_hSnd = (char*) GetNamedResource('snd ', (const unsigned char *) lpSnd);
291 #else
292 return false;
293 #endif
294 }
295 else
296 {
297 m_type = wxSound_FILE;
298 m_sndname = fileName;
299 }
300
301 return true;
302 }
303
304 bool wxSound::DoPlay(unsigned flags) const
305 {
306 Stop();
307
308 #if USE_QUICKTIME
309
310 Movie movie;
311
312 switch(m_type)
313 {
314 case wxSound_MEMORY:
315 {
316 if (!wxInitQT())
317 return false;
318 Handle myHandle, dataRef = nil;
319 MovieImportComponent miComponent;
320 Track targetTrack = nil;
321 TimeValue addedDuration = 0;
322 long outFlags = 0;
323 OSErr err;
324 ComponentResult result;
325
326 myHandle = NewHandleClear((Size)m_waveLength);
327
328 BlockMove(m_hSnd, *myHandle, m_waveLength);
329
330 err = PtrToHand(&myHandle, &dataRef, sizeof(Handle));
331
332 if (memcmp(&m_hSnd[8], "WAVE", 4) == 0)
333 miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeWave);
334 else if (memcmp(&m_hSnd[8], "AIFF", 4) == 0)
335 miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFF);
336 else if (memcmp(&m_hSnd[8], "AIFC", 4) == 0)
337 miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFC);
338 else
339 {
340 wxLogSysError(wxT("wxSound - Location in memory does not contain valid data"));
341 return false;
342 }
343
344 movie = NewMovie(0);
345
346 result = MovieImportDataRef(miComponent, dataRef,
347 HandleDataHandlerSubType, movie,
348 nil, &targetTrack,
349 nil, &addedDuration,
350 movieImportCreateTrack, &outFlags);
351
352 if (result != noErr)
353 {
354 wxLogSysError(wxString::Format(wxT("Couldn't import movie data\nError:%i"), (int)result));
355 }
356
357 SetMovieVolume(movie, kFullVolume);
358 GoToBeginningOfMovie(movie);
359
360 DisposeHandle(myHandle);
361 }
362 break;
363 case wxSound_RESOURCE:
364 {
365 SoundComponentData data;
366 unsigned long numframes, offset;
367
368 ParseSndHeader((SndListHandle)m_hSnd, &data, &numframes, &offset);
369 //m_waveLength = numFrames * data.numChannels;
370
371 SndChannelPtr pSndChannel;
372 SndNewChannel(&pSndChannel, sampledSynth,
373 initNoInterp
374 + (data.numChannels == 1 ? initMono : initStereo), NULL);
375
376 if(SndPlay(pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr)
377 return false;
378
379 if (flags & wxSOUND_ASYNC)
380 {
381 lastSoundTimer = ((wxSMTimer*&)m_pTimer)
382 = new wxSMTimer(pSndChannel, m_hSnd, flags & wxSOUND_LOOP ? 1 : 0,
383 &lastSoundIsPlaying);
384 lastSoundIsPlaying = true;
385
386 ((wxTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
387 }
388 else
389 SndDisposeChannel(pSndChannel, TRUE);
390
391 return true;
392 }
393 break;
394 case wxSound_FILE:
395 {
396 if (!wxInitQT())
397 return false;
398
399 OSErr err = noErr ;
400
401 Handle dataRef = NULL;
402 OSType dataRefType;
403
404 err = QTNewDataReferenceFromFullPathCFString(wxMacCFStringHolder(m_sndname,wxLocale::GetSystemEncoding()),
405 (UInt32)kQTNativeDefaultPathStyle, 0, &dataRef, &dataRefType);
406
407 wxASSERT(err == noErr);
408
409 if (NULL != dataRef || err != noErr)
410 {
411 err = NewMovieFromDataRef( &movie, newMovieDontAskUnresolvedDataRefs , NULL, dataRef, dataRefType );
412 wxASSERT(err == noErr);
413 DisposeHandle(dataRef);
414 }
415
416 if (err != noErr)
417 {
418 wxLogSysError(
419 wxString::Format(wxT("wxSound - Could not open file: %s\nError:%i"), m_sndname.c_str(), err )
420 );
421 return false;
422 }
423 }
424 break;
425 default:
426 return false;
427 }//end switch(m_type)
428
429 //Start the movie!
430 StartMovie(movie);
431
432 if (flags & wxSOUND_ASYNC)
433 {
434 //Start timer and play movie asyncronously
435 lastSoundTimer = ((wxQTTimer*&)m_pTimer) =
436 new wxQTTimer(movie, flags & wxSOUND_LOOP ? 1 : 0,
437 &lastSoundIsPlaying);
438 lastSoundIsPlaying = true;
439 ((wxQTTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
440 }
441 else
442 {
443 wxASSERT_MSG(!(flags & wxSOUND_LOOP), wxT("Can't loop and play syncronously at the same time"));
444
445 //Play movie until it ends, then exit
446 //Note that due to quicktime caching this may not always
447 //work 100% correctly
448 while (!IsMovieDone(movie))
449 MoviesTask(movie, 1);
450
451 DisposeMovie(movie);
452 }
453 #endif
454
455 return true;
456 }
457
458 bool wxSound::IsPlaying()
459 {
460 return lastSoundIsPlaying;
461 }
462
463 void wxSound::Stop()
464 {
465 if (lastSoundIsPlaying)
466 {
467 delete (wxTimer*&) lastSoundTimer;
468 lastSoundIsPlaying = false;
469 lastSoundTimer = NULL;
470 }
471 }
472
473 void* wxSound::GetHandle()
474 {
475 #if USE_QUICKTIME
476 if(m_type == wxSound_RESOURCE)
477 return (void*) ((wxSMTimer*)m_pTimer)->GetChannel();
478
479 return (void*) ((wxQTTimer*) m_pTimer)->GetMovie();
480 #endif
481 return NULL;
482 }
483
484 #endif //wxUSE_SOUND