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