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