]>
Commit | Line | Data |
---|---|---|
e9576ca5 | 1 | ///////////////////////////////////////////////////////////////////////////// |
8e3f3880 | 2 | // Name: src/mac/carbon/sound.cpp |
315ebf68 | 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 |
8e3f3880 | 9 | // Licence: wxWindows licence |
e9576ca5 SC |
10 | ///////////////////////////////////////////////////////////////////////////// |
11 | ||
8e3f3880 | 12 | // For compilers that support precompilation, includes "wx.h". |
3d1a4878 SC |
13 | #include "wx/wxprec.h" |
14 | ||
8e3f3880 WS |
15 | #if wxUSE_SOUND |
16 | ||
df91131c WS |
17 | #include "wx/sound.h" |
18 | ||
8e3f3880 WS |
19 | #ifndef WX_PRECOMP |
20 | #include "wx/object.h" | |
df91131c | 21 | #include "wx/string.h" |
88a7a4e1 | 22 | #include "wx/intl.h" |
e4db172a | 23 | #include "wx/log.h" |
c0badb70 | 24 | #include "wx/timer.h" |
8e3f3880 WS |
25 | #endif |
26 | ||
625d14ab | 27 | #include "wx/file.h" |
e9576ca5 | 28 | |
625d14ab SC |
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: | |
fe8712b5 SC |
39 | // 1) Path as CFString |
40 | // 2) Call QTNewDataReferenceFromFullPathCFString | |
41 | // 3) Call NewMovieFromDataRef | |
625d14ab SC |
42 | // 4) Call CloseMovieFile |
43 | // 4) PlayMovie(); | |
44 | // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime | |
45 | // | |
46 | ||
76a5e5d2 | 47 | #ifdef __WXMAC__ |
a2b77260 | 48 | #include "wx/mac/uma.h" |
768c6e8b | 49 | #ifndef __DARWIN__ |
625d14ab SC |
50 | #include <Movies.h> |
51 | #include <Gestalt.h> | |
52 | #endif | |
768c6e8b | 53 | #endif |
625d14ab SC |
54 | |
55 | #if defined __WXMAC__ && defined __DARWIN__/*TARGET_CARBON*/ | |
56 | #ifdef __APPLE_CC__ | |
57 | #include <Carbon/Carbon.h> | |
58 | #else | |
59 | #include <Carbon.h> | |
66a09d47 | 60 | #endif |
625d14ab SC |
61 | #else |
62 | #include <Sound.h> | |
76a5e5d2 SC |
63 | #endif |
64 | ||
625d14ab SC |
65 | //quicktime media layer only required for mac emulation on pc |
66 | #ifndef __WXMAC__ | |
67 | #include <qtml.h> | |
68 | #endif | |
e9576ca5 | 69 | |
768c6e8b | 70 | #ifndef __DARWIN__ |
625d14ab | 71 | #include <QuickTimeComponents.h> |
768c6e8b SC |
72 | #else |
73 | #include <QuickTime/QuickTimeComponents.h> | |
74 | #endif | |
e9576ca5 | 75 | |
c22edec9 | 76 | //Time between timer calls |
625d14ab | 77 | #define MOVIE_DELAY 100 |
e9576ca5 | 78 | |
c22edec9 RD |
79 | static wxTimer* lastSoundTimer=NULL; |
80 | static bool lastSoundIsPlaying=false; | |
81 | ||
6239ee05 SC |
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 | |
625d14ab SC |
89 | // ------------------------------------------------------------------ |
90 | // wxQTTimer - Handle Asyncronous Playing | |
91 | // ------------------------------------------------------------------ | |
92 | class wxQTTimer : public wxTimer | |
e9576ca5 | 93 | { |
625d14ab | 94 | public: |
c22edec9 RD |
95 | wxQTTimer(Movie movie, bool bLoop, bool* playing) : |
96 | m_movie(movie), m_bLoop(bLoop), m_pbPlaying(playing) | |
625d14ab SC |
97 | { |
98 | } | |
e9576ca5 | 99 | |
d3c7fc99 | 100 | virtual ~wxQTTimer() |
625d14ab | 101 | { |
4b430ee1 DS |
102 | if(m_pbPlaying) |
103 | *m_pbPlaying = false; | |
5b781a67 | 104 | |
625d14ab SC |
105 | StopMovie(m_movie); |
106 | DisposeMovie(m_movie); | |
625d14ab | 107 | Stop(); |
9e783cc0 | 108 | |
3103e8a9 | 109 | //Note that ExitMovies() is not necessary, but |
9e783cc0 RD |
110 | //the docs are fuzzy on whether or not TerminateQTML is |
111 | ExitMovies(); | |
112 | ||
4b430ee1 | 113 | #ifndef __WXMAC__ |
9e783cc0 | 114 | TerminateQTML(); |
4b430ee1 DS |
115 | #endif |
116 | } | |
117 | ||
118 | void Shutdown() | |
119 | { | |
120 | delete this; | |
625d14ab | 121 | } |
e40298d5 | 122 | |
625d14ab SC |
123 | void Notify() |
124 | { | |
4b430ee1 DS |
125 | if (m_pbPlaying && !*m_pbPlaying) |
126 | { | |
127 | Shutdown(); | |
128 | } | |
129 | ||
625d14ab SC |
130 | if(IsMovieDone(m_movie)) |
131 | { | |
132 | if (!m_bLoop) | |
133 | Shutdown(); | |
134 | else | |
135 | { | |
136 | StopMovie(m_movie); | |
4b430ee1 | 137 | GoToBeginningOfMovie(m_movie); |
625d14ab | 138 | StartMovie(m_movie); |
4b430ee1 | 139 | } |
625d14ab SC |
140 | } |
141 | else | |
142 | MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie | |
143 | } | |
e40298d5 | 144 | |
e40298d5 | 145 | |
625d14ab | 146 | Movie& GetMovie() {return m_movie;} |
4b430ee1 | 147 | |
625d14ab SC |
148 | protected: |
149 | Movie m_movie; | |
150 | bool m_bLoop; | |
4b430ee1 DS |
151 | |
152 | public: | |
153 | bool* m_pbPlaying; | |
154 | ||
625d14ab | 155 | }; |
e40298d5 | 156 | |
e40298d5 | 157 | |
625d14ab SC |
158 | class wxSMTimer : public wxTimer |
159 | { | |
160 | public: | |
c22edec9 RD |
161 | wxSMTimer(void* hSnd, void* pSndChannel, bool bLoop, bool* playing) |
162 | : m_hSnd(hSnd), m_pSndChannel(pSndChannel), m_bLoop(bLoop), m_pbPlaying(playing) | |
4b430ee1 DS |
163 | { |
164 | } | |
e40298d5 | 165 | |
d3c7fc99 | 166 | virtual ~wxSMTimer() |
4b430ee1 DS |
167 | { |
168 | if(m_pbPlaying) | |
169 | *m_pbPlaying = false; | |
170 | SndDisposeChannel((SndChannelPtr)m_pSndChannel, TRUE); | |
171 | Stop(); | |
172 | } | |
e40298d5 | 173 | |
625d14ab | 174 | void Notify() |
4b430ee1 DS |
175 | { |
176 | if (m_pbPlaying && !*m_pbPlaying) | |
e40298d5 | 177 | { |
4b430ee1 DS |
178 | Shutdown(); |
179 | } | |
180 | ||
181 | SCStatus stat; | |
625d14ab SC |
182 | |
183 | if (SndChannelStatus((SndChannelPtr)m_pSndChannel, sizeof(SCStatus), &stat) != 0) | |
4b430ee1 DS |
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(); | |
e40298d5 | 197 | } |
625d14ab | 198 | } |
e40298d5 | 199 | |
625d14ab SC |
200 | void Shutdown() |
201 | { | |
4b430ee1 | 202 | delete this; |
625d14ab | 203 | } |
4b430ee1 DS |
204 | |
205 | void* GetChannel() {return m_pSndChannel;} | |
206 | ||
625d14ab SC |
207 | protected: |
208 | void* m_hSnd; | |
209 | void* m_pSndChannel; | |
210 | bool m_bLoop; | |
4b430ee1 DS |
211 | |
212 | public: | |
213 | bool* m_pbPlaying; | |
625d14ab SC |
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; | |
e40298d5 | 226 | |
625d14ab | 227 | error = Gestalt (gestaltQuickTime, &result); |
9e783cc0 | 228 | return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400); |
625d14ab SC |
229 | #else |
230 | return true; | |
231 | #endif | |
232 | } | |
e40298d5 | 233 | |
625d14ab SC |
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) | |
6d3ef225 | 242 | wxLogSysError(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError)); |
4b430ee1 | 243 | #endif |
625d14ab SC |
244 | EnterMovies(); |
245 | return true; | |
246 | } | |
247 | else | |
4b430ee1 | 248 | { |
7f0b95bb | 249 | wxLogSysError(wxT("Quicktime is not installed, or Your Version of Quicktime is <= 4.")); |
4b430ee1 DS |
250 | return false; |
251 | } | |
625d14ab | 252 | } |
e40298d5 | 253 | |
6239ee05 SC |
254 | #endif |
255 | ||
625d14ab SC |
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 | { | |
625d14ab SC |
270 | } |
271 | ||
272 | wxSound::~wxSound() | |
273 | { | |
625d14ab SC |
274 | } |
275 | ||
276 | bool wxSound::Create(const wxString& fileName, bool isResource) | |
277 | { | |
4b430ee1 | 278 | Stop(); |
2116a0d1 | 279 | |
625d14ab SC |
280 | if (isResource) |
281 | { | |
282 | #ifdef __WXMAC__ | |
283 | m_type = wxSound_RESOURCE; | |
284 | ||
285 | Str255 lpSnd ; | |
286 | ||
287 | wxMacStringToPascal( fileName , lpSnd ) ; | |
288 | ||
7f0b95bb | 289 | m_sndname = fileName; |
625d14ab SC |
290 | m_hSnd = (char*) GetNamedResource('snd ', (const unsigned char *) lpSnd); |
291 | #else | |
292 | return false; | |
293 | #endif | |
4b430ee1 DS |
294 | } |
295 | else | |
296 | { | |
625d14ab SC |
297 | m_type = wxSound_FILE; |
298 | m_sndname = fileName; | |
e40298d5 JS |
299 | } |
300 | ||
625d14ab | 301 | return true; |
e9576ca5 SC |
302 | } |
303 | ||
315ebf68 | 304 | bool wxSound::DoPlay(unsigned flags) const |
e9576ca5 | 305 | { |
4b430ee1 | 306 | Stop(); |
4a69b060 | 307 | |
6239ee05 SC |
308 | #if USE_QUICKTIME |
309 | ||
625d14ab SC |
310 | Movie movie; |
311 | ||
312 | switch(m_type) | |
313 | { | |
314 | case wxSound_MEMORY: | |
315 | { | |
4b430ee1 DS |
316 | if (!wxInitQT()) |
317 | return false; | |
625d14ab | 318 | Handle myHandle, dataRef = nil; |
4b430ee1 | 319 | MovieImportComponent miComponent; |
625d14ab SC |
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); | |
4b430ee1 | 327 | |
625d14ab SC |
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 | |
4b430ee1 | 339 | { |
7f0b95bb | 340 | wxLogSysError(wxT("wxSound - Location in memory does not contain valid data")); |
625d14ab SC |
341 | return false; |
342 | } | |
343 | ||
344 | movie = NewMovie(0); | |
4a69b060 | 345 | |
625d14ab SC |
346 | result = MovieImportDataRef(miComponent, dataRef, |
347 | HandleDataHandlerSubType, movie, | |
348 | nil, &targetTrack, | |
349 | nil, &addedDuration, | |
350 | movieImportCreateTrack, &outFlags); | |
e9576ca5 | 351 | |
625d14ab SC |
352 | if (result != noErr) |
353 | { | |
354 | wxLogSysError(wxString::Format(wxT("Couldn't import movie data\nError:%i"), (int)result)); | |
4b430ee1 | 355 | } |
4a69b060 | 356 | |
625d14ab SC |
357 | SetMovieVolume(movie, kFullVolume); |
358 | GoToBeginningOfMovie(movie); | |
9e783cc0 RD |
359 | |
360 | DisposeHandle(myHandle); | |
625d14ab SC |
361 | } |
362 | break; | |
363 | case wxSound_RESOURCE: | |
364 | { | |
365 | SoundComponentData data; | |
366 | unsigned long numframes, offset; | |
e9576ca5 | 367 | |
625d14ab SC |
368 | ParseSndHeader((SndListHandle)m_hSnd, &data, &numframes, &offset); |
369 | //m_waveLength = numFrames * data.numChannels; | |
5b781a67 | 370 | |
625d14ab SC |
371 | SndChannelPtr pSndChannel; |
372 | SndNewChannel(&pSndChannel, sampledSynth, | |
4b430ee1 DS |
373 | initNoInterp |
374 | + (data.numChannels == 1 ? initMono : initStereo), NULL); | |
e40298d5 | 375 | |
625d14ab | 376 | if(SndPlay(pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr) |
4b430ee1 | 377 | return false; |
625d14ab SC |
378 | |
379 | if (flags & wxSOUND_ASYNC) | |
4b430ee1 DS |
380 | { |
381 | lastSoundTimer = ((wxSMTimer*&)m_pTimer) | |
382 | = new wxSMTimer(pSndChannel, m_hSnd, flags & wxSOUND_LOOP ? 1 : 0, | |
c22edec9 RD |
383 | &lastSoundIsPlaying); |
384 | lastSoundIsPlaying = true; | |
4b430ee1 DS |
385 | |
386 | ((wxTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); | |
387 | } | |
388 | else | |
625d14ab SC |
389 | SndDisposeChannel(pSndChannel, TRUE); |
390 | ||
391 | return true; | |
392 | } | |
393 | break; | |
394 | case wxSound_FILE: | |
395 | { | |
4b430ee1 DS |
396 | if (!wxInitQT()) |
397 | return false; | |
398 | ||
a2b77260 | 399 | OSErr err = noErr ; |
8e3f3880 | 400 | |
fe8712b5 SC |
401 | Handle dataRef = NULL; |
402 | OSType dataRefType; | |
a2b77260 | 403 | |
fe8712b5 SC |
404 | err = QTNewDataReferenceFromFullPathCFString(wxMacCFStringHolder(m_sndname,wxLocale::GetSystemEncoding()), |
405 | (UInt32)kQTNativeDefaultPathStyle, 0, &dataRef, &dataRefType); | |
8e3f3880 | 406 | |
fe8712b5 SC |
407 | wxASSERT(err == noErr); |
408 | ||
409 | if (NULL != dataRef || err != noErr) | |
625d14ab | 410 | { |
fe8712b5 SC |
411 | err = NewMovieFromDataRef( &movie, newMovieDontAskUnresolvedDataRefs , NULL, dataRef, dataRefType ); |
412 | wxASSERT(err == noErr); | |
413 | DisposeHandle(dataRef); | |
625d14ab | 414 | } |
8e3f3880 | 415 | |
625d14ab | 416 | if (err != noErr) |
4b430ee1 | 417 | { |
625d14ab SC |
418 | wxLogSysError( |
419 | wxString::Format(wxT("wxSound - Could not open file: %s\nError:%i"), m_sndname.c_str(), err ) | |
420 | ); | |
421 | return false; | |
4b430ee1 | 422 | } |
625d14ab SC |
423 | } |
424 | break; | |
425 | default: | |
426 | return false; | |
427 | }//end switch(m_type) | |
e40298d5 | 428 | |
625d14ab SC |
429 | //Start the movie! |
430 | StartMovie(movie); | |
e40298d5 | 431 | |
c22edec9 RD |
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 | |
625d14ab | 442 | { |
7f0b95bb | 443 | wxASSERT_MSG(!(flags & wxSOUND_LOOP), wxT("Can't loop and play syncronously at the same time")); |
e40298d5 | 444 | |
625d14ab | 445 | //Play movie until it ends, then exit |
8e3f3880 | 446 | //Note that due to quicktime caching this may not always |
dcb68102 | 447 | //work 100% correctly |
625d14ab | 448 | while (!IsMovieDone(movie)) |
dcb68102 | 449 | MoviesTask(movie, 1); |
e40298d5 | 450 | |
625d14ab SC |
451 | DisposeMovie(movie); |
452 | } | |
6239ee05 | 453 | #endif |
625d14ab SC |
454 | |
455 | return true; | |
5b781a67 SC |
456 | } |
457 | ||
4b430ee1 | 458 | bool wxSound::IsPlaying() |
625d14ab | 459 | { |
4b430ee1 | 460 | return lastSoundIsPlaying; |
625d14ab | 461 | } |
5b781a67 | 462 | |
4b430ee1 | 463 | void wxSound::Stop() |
5b781a67 | 464 | { |
c22edec9 | 465 | if (lastSoundIsPlaying) |
e40298d5 | 466 | { |
4b430ee1 DS |
467 | delete (wxTimer*&) lastSoundTimer; |
468 | lastSoundIsPlaying = false; | |
c22edec9 | 469 | lastSoundTimer = NULL; |
e40298d5 | 470 | } |
625d14ab | 471 | } |
9e783cc0 | 472 | |
4b430ee1 DS |
473 | void* wxSound::GetHandle() |
474 | { | |
6239ee05 | 475 | #if USE_QUICKTIME |
4b430ee1 DS |
476 | if(m_type == wxSound_RESOURCE) |
477 | return (void*) ((wxSMTimer*)m_pTimer)->GetChannel(); | |
9e783cc0 | 478 | |
4b430ee1 | 479 | return (void*) ((wxQTTimer*) m_pTimer)->GetMovie(); |
6239ee05 SC |
480 | #endif |
481 | return NULL; | |
4b430ee1 | 482 | } |
9e783cc0 | 483 | |
4b430ee1 | 484 | #endif //wxUSE_SOUND |