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