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