]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/sound.cpp
Refresh is necessary in earlier systems, no penalty when doing in 10.3+
[wxWidgets.git] / src / mac / carbon / sound.cpp
CommitLineData
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// ------------------------------------------------------------------
72class wxQTTimer : public wxTimer
e9576ca5 73{
625d14ab
SC
74public:
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;}
120protected:
121 Movie m_movie;
122 bool m_bLoop;
123};
e40298d5 124
e40298d5 125
625d14ab
SC
126class wxSMTimer : public wxTimer
127{
128public:
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
166protected:
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
177Boolean 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
190inline 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
210wxSound::wxSound()
211: m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE)
212{
213}
214
215wxSound::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
221wxSound::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
228wxSound::~wxSound()
229{
625d14ab
SC
230}
231
232bool 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 263bool 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
422void* wxSound::GetHandle()
423{
424 return (void*) ((wxQTTimer*) m_pTimer)->GetMovie();
425}
5b781a67 426
625d14ab 427bool 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