]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/sound.cpp
bug fix for MacTel builds, using the same code on PPC as well (wxMask)
[wxWidgets.git] / src / mac / carbon / sound.cpp
CommitLineData
e9576ca5 1/////////////////////////////////////////////////////////////////////////////
315ebf68
VS
2// Name: sound.cpp
3// Purpose: wxSound class implementation: optional
4b430ee1 4// Author: Ryan Norton
bc57786b 5// Modified by: Stefan Csomor
a31a5f85 6// Created: 1998-01-01
e9576ca5 7// RCS-ID: $Id$
bc57786b 8// Copyright: (c) Ryan Norton
65571936 9// Licence: wxWindows licence
e9576ca5
SC
10/////////////////////////////////////////////////////////////////////////////
11
3d1a4878
SC
12#include "wx/wxprec.h"
13
e9576ca5
SC
14#include "wx/object.h"
15#include "wx/string.h"
625d14ab
SC
16#include "wx/log.h"
17#include "wx/file.h"
315ebf68 18#include "wx/sound.h"
625d14ab 19#include "wx/timer.h"
c9528e9d 20#include "wx/intl.h"
e9576ca5 21
315ebf68 22#if wxUSE_SOUND
4a69b060 23
625d14ab
SC
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
76a5e5d2 42#ifdef __WXMAC__
a2b77260 43#include "wx/mac/uma.h"
768c6e8b 44#ifndef __DARWIN__
625d14ab
SC
45#include <Movies.h>
46#include <Gestalt.h>
47#endif
768c6e8b 48#endif
625d14ab
SC
49
50#if defined __WXMAC__ && defined __DARWIN__/*TARGET_CARBON*/
51#ifdef __APPLE_CC__
52#include <Carbon/Carbon.h>
53#else
54#include <Carbon.h>
66a09d47 55#endif
625d14ab
SC
56#else
57#include <Sound.h>
76a5e5d2
SC
58#endif
59
625d14ab
SC
60//quicktime media layer only required for mac emulation on pc
61#ifndef __WXMAC__
62#include <qtml.h>
63#endif
e9576ca5 64
768c6e8b 65#ifndef __DARWIN__
625d14ab 66#include <QuickTimeComponents.h>
768c6e8b
SC
67#else
68#include <QuickTime/QuickTimeComponents.h>
69#endif
e9576ca5 70
c22edec9 71//Time between timer calls
625d14ab 72#define MOVIE_DELAY 100
e9576ca5 73
c22edec9
RD
74static wxTimer* lastSoundTimer=NULL;
75static bool lastSoundIsPlaying=false;
76
625d14ab
SC
77// ------------------------------------------------------------------
78// wxQTTimer - Handle Asyncronous Playing
79// ------------------------------------------------------------------
80class wxQTTimer : public wxTimer
e9576ca5 81{
625d14ab 82public:
c22edec9
RD
83 wxQTTimer(Movie movie, bool bLoop, bool* playing) :
84 m_movie(movie), m_bLoop(bLoop), m_pbPlaying(playing)
625d14ab
SC
85 {
86 }
e9576ca5 87
625d14ab
SC
88 ~wxQTTimer()
89 {
4b430ee1
DS
90 if(m_pbPlaying)
91 *m_pbPlaying = false;
5b781a67 92
625d14ab
SC
93 StopMovie(m_movie);
94 DisposeMovie(m_movie);
625d14ab 95 Stop();
9e783cc0 96
3103e8a9 97 //Note that ExitMovies() is not necessary, but
9e783cc0
RD
98 //the docs are fuzzy on whether or not TerminateQTML is
99 ExitMovies();
100
4b430ee1 101#ifndef __WXMAC__
9e783cc0 102 TerminateQTML();
4b430ee1
DS
103#endif
104 }
105
106 void Shutdown()
107 {
108 delete this;
625d14ab 109 }
e40298d5 110
625d14ab
SC
111 void Notify()
112 {
4b430ee1
DS
113 if (m_pbPlaying && !*m_pbPlaying)
114 {
115 Shutdown();
116 }
117
625d14ab
SC
118 if(IsMovieDone(m_movie))
119 {
120 if (!m_bLoop)
121 Shutdown();
122 else
123 {
124 StopMovie(m_movie);
4b430ee1 125 GoToBeginningOfMovie(m_movie);
625d14ab 126 StartMovie(m_movie);
4b430ee1 127 }
625d14ab
SC
128 }
129 else
130 MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie
131 }
e40298d5 132
e40298d5 133
625d14ab 134 Movie& GetMovie() {return m_movie;}
4b430ee1 135
625d14ab
SC
136protected:
137 Movie m_movie;
138 bool m_bLoop;
4b430ee1
DS
139
140public:
141 bool* m_pbPlaying;
142
625d14ab 143};
e40298d5 144
e40298d5 145
625d14ab
SC
146class wxSMTimer : public wxTimer
147{
148public:
c22edec9
RD
149 wxSMTimer(void* hSnd, void* pSndChannel, bool bLoop, bool* playing)
150 : m_hSnd(hSnd), m_pSndChannel(pSndChannel), m_bLoop(bLoop), m_pbPlaying(playing)
4b430ee1
DS
151 {
152 }
e40298d5 153
625d14ab 154 ~wxSMTimer()
4b430ee1
DS
155 {
156 if(m_pbPlaying)
157 *m_pbPlaying = false;
158 SndDisposeChannel((SndChannelPtr)m_pSndChannel, TRUE);
159 Stop();
160 }
e40298d5 161
625d14ab 162 void Notify()
4b430ee1
DS
163 {
164 if (m_pbPlaying && !*m_pbPlaying)
e40298d5 165 {
4b430ee1
DS
166 Shutdown();
167 }
168
169 SCStatus stat;
625d14ab
SC
170
171 if (SndChannelStatus((SndChannelPtr)m_pSndChannel, sizeof(SCStatus), &stat) != 0)
4b430ee1
DS
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();
e40298d5 185 }
625d14ab 186 }
e40298d5 187
625d14ab
SC
188 void Shutdown()
189 {
4b430ee1 190 delete this;
625d14ab 191 }
4b430ee1
DS
192
193 void* GetChannel() {return m_pSndChannel;}
194
625d14ab
SC
195protected:
196 void* m_hSnd;
197 void* m_pSndChannel;
198 bool m_bLoop;
4b430ee1
DS
199
200public:
201 bool* m_pbPlaying;
625d14ab
SC
202};
203
204// ------------------------------------------------------------------
205// wxSound
206// ------------------------------------------------------------------
207
208//Determines whether version 4 of QT is installed
209Boolean wxIsQuickTime4Installed (void)
210{
211#ifdef __WXMAC__
212 short error;
213 long result;
e40298d5 214
625d14ab 215 error = Gestalt (gestaltQuickTime, &result);
9e783cc0 216 return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
625d14ab
SC
217#else
218 return true;
219#endif
220}
e40298d5 221
625d14ab
SC
222inline bool wxInitQT ()
223{
224 if (wxIsQuickTime4Installed())
225 {
226 #ifndef __WXMAC__
227 int nError;
228 //-2093 no dll
229 if ((nError = InitializeQTML(0)) != noErr)
6d3ef225 230 wxLogSysError(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError));
4b430ee1 231 #endif
625d14ab
SC
232 EnterMovies();
233 return true;
234 }
235 else
4b430ee1 236 {
7f0b95bb 237 wxLogSysError(wxT("Quicktime is not installed, or Your Version of Quicktime is <= 4."));
4b430ee1
DS
238 return false;
239 }
625d14ab 240}
e40298d5 241
625d14ab
SC
242wxSound::wxSound()
243: m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE)
244{
245}
246
247wxSound::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
253wxSound::wxSound(int size, const wxByte* data)
254: m_hSnd((char*)data), m_waveLength(size), m_pTimer(NULL), m_type(wxSound_MEMORY)
255{
625d14ab
SC
256}
257
258wxSound::~wxSound()
259{
625d14ab
SC
260}
261
262bool wxSound::Create(const wxString& fileName, bool isResource)
263{
4b430ee1 264 Stop();
2116a0d1 265
625d14ab
SC
266 if (isResource)
267 {
268#ifdef __WXMAC__
269 m_type = wxSound_RESOURCE;
270
271 Str255 lpSnd ;
272
273 wxMacStringToPascal( fileName , lpSnd ) ;
274
7f0b95bb 275 m_sndname = fileName;
625d14ab
SC
276 m_hSnd = (char*) GetNamedResource('snd ', (const unsigned char *) lpSnd);
277#else
278 return false;
279#endif
4b430ee1
DS
280 }
281 else
282 {
625d14ab
SC
283 m_type = wxSound_FILE;
284 m_sndname = fileName;
e40298d5
JS
285 }
286
625d14ab 287 return true;
e9576ca5
SC
288}
289
315ebf68 290bool wxSound::DoPlay(unsigned flags) const
e9576ca5 291{
4b430ee1 292 Stop();
4a69b060 293
625d14ab
SC
294 Movie movie;
295
296 switch(m_type)
297 {
298 case wxSound_MEMORY:
299 {
4b430ee1
DS
300 if (!wxInitQT())
301 return false;
625d14ab 302 Handle myHandle, dataRef = nil;
4b430ee1 303 MovieImportComponent miComponent;
625d14ab
SC
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);
4b430ee1 311
625d14ab
SC
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
4b430ee1 323 {
7f0b95bb 324 wxLogSysError(wxT("wxSound - Location in memory does not contain valid data"));
625d14ab
SC
325 return false;
326 }
327
328 movie = NewMovie(0);
4a69b060 329
625d14ab
SC
330 result = MovieImportDataRef(miComponent, dataRef,
331 HandleDataHandlerSubType, movie,
332 nil, &targetTrack,
333 nil, &addedDuration,
334 movieImportCreateTrack, &outFlags);
e9576ca5 335
625d14ab
SC
336 if (result != noErr)
337 {
338 wxLogSysError(wxString::Format(wxT("Couldn't import movie data\nError:%i"), (int)result));
4b430ee1 339 }
4a69b060 340
625d14ab
SC
341 SetMovieVolume(movie, kFullVolume);
342 GoToBeginningOfMovie(movie);
9e783cc0
RD
343
344 DisposeHandle(myHandle);
625d14ab
SC
345 }
346 break;
347 case wxSound_RESOURCE:
348 {
349 SoundComponentData data;
350 unsigned long numframes, offset;
e9576ca5 351
625d14ab
SC
352 ParseSndHeader((SndListHandle)m_hSnd, &data, &numframes, &offset);
353 //m_waveLength = numFrames * data.numChannels;
5b781a67 354
625d14ab
SC
355 SndChannelPtr pSndChannel;
356 SndNewChannel(&pSndChannel, sampledSynth,
4b430ee1
DS
357 initNoInterp
358 + (data.numChannels == 1 ? initMono : initStereo), NULL);
e40298d5 359
625d14ab 360 if(SndPlay(pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr)
4b430ee1 361 return false;
625d14ab
SC
362
363 if (flags & wxSOUND_ASYNC)
4b430ee1
DS
364 {
365 lastSoundTimer = ((wxSMTimer*&)m_pTimer)
366 = new wxSMTimer(pSndChannel, m_hSnd, flags & wxSOUND_LOOP ? 1 : 0,
c22edec9
RD
367 &lastSoundIsPlaying);
368 lastSoundIsPlaying = true;
4b430ee1
DS
369
370 ((wxTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
371 }
372 else
625d14ab
SC
373 SndDisposeChannel(pSndChannel, TRUE);
374
375 return true;
376 }
377 break;
378 case wxSound_FILE:
379 {
4b430ee1
DS
380 if (!wxInitQT())
381 return false;
382
a2b77260 383 OSErr err = noErr ;
bc57786b
RN
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'
a2b77260
SC
395#if defined( __WXMAC__ ) && TARGET_API_MAC_OSX && ( MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2 )
396 if ( UMAGetSystemVersion() >= 0x1030 )
4b430ee1 397 {
a2b77260
SC
398 Handle dataRef = NULL;
399 OSType dataRefType;
400
401 err = QTNewDataReferenceFromFullPathCFString(wxMacCFStringHolder(m_sndname,wxLocale::GetSystemEncoding()),
bc57786b
RN
402 //FIXME: Why does this have to be casted?
403 (unsigned int)kQTNativeDefaultPathStyle,
404 //FIXME: End
405 0, &dataRef, &dataRefType);
a2b77260 406
bc57786b
RN
407 wxASSERT(err == noErr);
408
409 if (NULL != dataRef || err != noErr)
a2b77260
SC
410 {
411 err = NewMovieFromDataRef( &movie, newMovieDontAskUnresolvedDataRefs , NULL, dataRef, dataRefType );
bc57786b 412 wxASSERT(err == noErr);
a2b77260
SC
413 DisposeHandle(dataRef);
414 }
4b430ee1 415 }
a2b77260 416 else
625d14ab 417#endif
625d14ab 418 {
a2b77260
SC
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 {
65442ab6 427/*
a2b77260
SC
428 wxLogSysError(wxString::Format(wxT("File:%s does not exist\nError:%i"),
429 m_sndname.c_str(), nError));
65442ab6 430*/
a2b77260
SC
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);
625d14ab 451 }
a2b77260 452
625d14ab 453 if (err != noErr)
4b430ee1 454 {
625d14ab
SC
455 wxLogSysError(
456 wxString::Format(wxT("wxSound - Could not open file: %s\nError:%i"), m_sndname.c_str(), err )
457 );
458 return false;
4b430ee1 459 }
625d14ab
SC
460 }
461 break;
462 default:
463 return false;
464 }//end switch(m_type)
e40298d5 465
625d14ab
SC
466 //Start the movie!
467 StartMovie(movie);
e40298d5 468
c22edec9
RD
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
625d14ab 479 {
7f0b95bb 480 wxASSERT_MSG(!(flags & wxSOUND_LOOP), wxT("Can't loop and play syncronously at the same time"));
e40298d5 481
625d14ab 482 //Play movie until it ends, then exit
dcb68102
RN
483 //Note that due to quicktime caching this may not always
484 //work 100% correctly
625d14ab 485 while (!IsMovieDone(movie))
dcb68102 486 MoviesTask(movie, 1);
e40298d5 487
625d14ab
SC
488 DisposeMovie(movie);
489 }
625d14ab
SC
490
491 return true;
5b781a67
SC
492}
493
4b430ee1 494bool wxSound::IsPlaying()
625d14ab 495{
4b430ee1 496 return lastSoundIsPlaying;
625d14ab 497}
5b781a67 498
4b430ee1 499void wxSound::Stop()
5b781a67 500{
c22edec9 501 if (lastSoundIsPlaying)
e40298d5 502 {
4b430ee1
DS
503 delete (wxTimer*&) lastSoundTimer;
504 lastSoundIsPlaying = false;
c22edec9 505 lastSoundTimer = NULL;
e40298d5 506 }
625d14ab 507}
9e783cc0 508
4b430ee1
DS
509void* wxSound::GetHandle()
510{
511 if(m_type == wxSound_RESOURCE)
512 return (void*) ((wxSMTimer*)m_pTimer)->GetChannel();
9e783cc0 513
4b430ee1
DS
514 return (void*) ((wxQTTimer*) m_pTimer)->GetMovie();
515}
9e783cc0 516
4b430ee1 517#endif //wxUSE_SOUND