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