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