]>
Commit | Line | Data |
---|---|---|
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 |