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