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