1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxSound class implementation: optional
5 // Modified by: Stefan Csomor
8 // Copyright: (c) Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "sound.h"
16 #include "wx/wxprec.h"
18 #include "wx/object.h"
19 #include "wx/string.h"
28 // Carbon QT Implementation Details -
31 // 1) OpenDefaultComponent(MovieImportType, kQTFileTypeWave);
33 // 3) MovieImportDataRef() //Pass Memory Location to this
35 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
39 // 2) Call OpenMovieFile
40 // 3) Call NewMovieFromFile
41 // 4) Call CloseMovieFile
43 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
47 #include "wx/mac/uma.h"
52 #if defined __WXMAC__ && defined __DARWIN__/*TARGET_CARBON*/
54 #include <Carbon/Carbon.h>
62 //quicktime media layer only required for mac emulation on pc
67 #include <QuickTimeComponents.h>
69 //Time between timer calls
70 #define MOVIE_DELAY 100
72 static wxTimer
* lastSoundTimer
=NULL
;
73 static bool lastSoundIsPlaying
=false;
75 // ------------------------------------------------------------------
76 // wxQTTimer - Handle Asyncronous Playing
77 // ------------------------------------------------------------------
78 class wxQTTimer
: public wxTimer
81 wxQTTimer(Movie movie
, bool bLoop
, bool* playing
) :
82 m_movie(movie
), m_bLoop(bLoop
), m_pbPlaying(playing
)
92 DisposeMovie(m_movie
);
95 //Note that ExitMovies() is not necessary, but
96 //the docs are fuzzy on whether or not TerminateQTML is
111 if (m_pbPlaying
&& !*m_pbPlaying
)
116 if(IsMovieDone(m_movie
))
123 GoToBeginningOfMovie(m_movie
);
128 MoviesTask(m_movie
, MOVIE_DELAY
); //Give QT time to play movie
132 Movie
& GetMovie() {return m_movie
;}
144 class wxSMTimer
: public wxTimer
147 wxSMTimer(void* hSnd
, void* pSndChannel
, bool bLoop
, bool* playing
)
148 : m_hSnd(hSnd
), m_pSndChannel(pSndChannel
), m_bLoop(bLoop
), m_pbPlaying(playing
)
155 *m_pbPlaying
= false;
156 SndDisposeChannel((SndChannelPtr
)m_pSndChannel
, TRUE
);
162 if (m_pbPlaying
&& !*m_pbPlaying
)
169 if (SndChannelStatus((SndChannelPtr
)m_pSndChannel
, sizeof(SCStatus
), &stat
) != 0)
172 //if the sound isn't playing anymore, see if it's looped,
173 //and if so play it again, otherwise close things up
174 if (stat
.scChannelBusy
== FALSE
)
178 if(SndPlay((SndChannelPtr
)m_pSndChannel
, (SndListHandle
) m_hSnd
, true) != noErr
)
191 void* GetChannel() {return m_pSndChannel
;}
202 // ------------------------------------------------------------------
204 // ------------------------------------------------------------------
206 //Determines whether version 4 of QT is installed
207 Boolean
wxIsQuickTime4Installed (void)
213 error
= Gestalt (gestaltQuickTime
, &result
);
214 return (error
== noErr
) && (((result
>> 16) & 0xffff) >= 0x0400);
220 inline bool wxInitQT ()
222 if (wxIsQuickTime4Installed())
227 if ((nError
= InitializeQTML(0)) != noErr
)
228 wxLogSysError(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError
));
235 wxLogSysError(wxT("Quicktime is not installed, or Your Version of Quicktime is <= 4."));
241 : m_hSnd(NULL
), m_waveLength(0), m_pTimer(NULL
), m_type(wxSound_NONE
)
245 wxSound::wxSound(const wxString
& sFileName
, bool isResource
)
246 : m_hSnd(NULL
), m_waveLength(0), m_pTimer(NULL
), m_type(wxSound_NONE
)
248 Create(sFileName
, isResource
);
251 wxSound::wxSound(int size
, const wxByte
* data
)
252 : m_hSnd((char*)data
), m_waveLength(size
), m_pTimer(NULL
), m_type(wxSound_MEMORY
)
260 bool wxSound::Create(const wxString
& fileName
, bool isResource
)
267 m_type
= wxSound_RESOURCE
;
271 wxMacStringToPascal( fileName
, lpSnd
) ;
273 m_sndname
= fileName
;
274 m_hSnd
= (char*) GetNamedResource('snd ', (const unsigned char *) lpSnd
);
281 m_type
= wxSound_FILE
;
282 m_sndname
= fileName
;
288 bool wxSound::DoPlay(unsigned flags
) const
300 Handle myHandle
, dataRef
= nil
;
301 MovieImportComponent miComponent
;
302 Track targetTrack
= nil
;
303 TimeValue addedDuration
= 0;
306 ComponentResult result
;
308 myHandle
= NewHandleClear((Size
)m_waveLength
);
310 BlockMove(m_hSnd
, *myHandle
, m_waveLength
);
312 err
= PtrToHand(&myHandle
, &dataRef
, sizeof(Handle
));
314 if (memcmp(&m_hSnd
[8], "WAVE", 4) == 0)
315 miComponent
= OpenDefaultComponent(MovieImportType
, kQTFileTypeWave
);
316 else if (memcmp(&m_hSnd
[8], "AIFF", 4) == 0)
317 miComponent
= OpenDefaultComponent(MovieImportType
, kQTFileTypeAIFF
);
318 else if (memcmp(&m_hSnd
[8], "AIFC", 4) == 0)
319 miComponent
= OpenDefaultComponent(MovieImportType
, kQTFileTypeAIFC
);
322 wxLogSysError(wxT("wxSound - Location in memory does not contain valid data"));
328 result
= MovieImportDataRef(miComponent
, dataRef
,
329 HandleDataHandlerSubType
, movie
,
332 movieImportCreateTrack
, &outFlags
);
336 wxLogSysError(wxString::Format(wxT("Couldn't import movie data\nError:%i"), (int)result
));
339 SetMovieVolume(movie
, kFullVolume
);
340 GoToBeginningOfMovie(movie
);
342 DisposeHandle(myHandle
);
345 case wxSound_RESOURCE
:
347 SoundComponentData data
;
348 unsigned long numframes
, offset
;
350 ParseSndHeader((SndListHandle
)m_hSnd
, &data
, &numframes
, &offset
);
351 //m_waveLength = numFrames * data.numChannels;
353 SndChannelPtr pSndChannel
;
354 SndNewChannel(&pSndChannel
, sampledSynth
,
356 + (data
.numChannels
== 1 ? initMono
: initStereo
), NULL
);
358 if(SndPlay(pSndChannel
, (SndListHandle
) m_hSnd
, flags
& wxSOUND_ASYNC
? 1 : 0) != noErr
)
361 if (flags
& wxSOUND_ASYNC
)
363 lastSoundTimer
= ((wxSMTimer
*&)m_pTimer
)
364 = new wxSMTimer(pSndChannel
, m_hSnd
, flags
& wxSOUND_LOOP
? 1 : 0,
365 &lastSoundIsPlaying
);
366 lastSoundIsPlaying
= true;
368 ((wxTimer
*)m_pTimer
)->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
);
371 SndDisposeChannel(pSndChannel
, TRUE
);
382 //NB: RN: Stefan - I think the 10.3 path functions are broken if kQTNativeDefaultPathStyle is
383 //going to trigger a warning every time it is used - where its _supposed to be used_!!
384 //(kQTNativePathStyle is negative but the function argument is unsigned!)
385 //../src/mac/carbon/sound.cpp: In member function `virtual bool
386 // wxSound::DoPlay(unsigned int) const':
387 //../src/mac/carbon/sound.cpp:387: warning: passing negative value `
388 // kQTNativeDefaultPathStyle' for argument passing 2 of `OSErr
389 // QTNewDataReferenceFromFullPathCFString(const __CFString*, long unsigned int,
390 // long unsigned int, char***, OSType*)'
391 //../src/mac/carbon/sound.cpp:387: warning: argument of negative value `
392 // kQTNativeDefaultPathStyle' to `long unsigned int'
393 #if defined( __WXMAC__ ) && TARGET_API_MAC_OSX && ( MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2 )
394 if ( UMAGetSystemVersion() >= 0x1030 )
396 Handle dataRef
= NULL
;
399 err
= QTNewDataReferenceFromFullPathCFString(wxMacCFStringHolder(m_sndname
,wxLocale::GetSystemEncoding()),
400 //FIXME: Why does this have to be casted?
401 (unsigned int)kQTNativeDefaultPathStyle
,
403 0, &dataRef
, &dataRefType
);
405 wxASSERT(err
== noErr
);
407 if (NULL
!= dataRef
|| err
!= noErr
)
409 err
= NewMovieFromDataRef( &movie
, newMovieDontAskUnresolvedDataRefs
, NULL
, dataRef
, dataRefType
);
410 wxASSERT(err
== noErr
);
411 DisposeHandle(dataRef
);
420 wxMacFilename2FSSpec( m_sndname
, &sfFile
) ;
423 if ((nError
= NativePathNameToFSSpec ((char*) m_sndname
.c_str(), &sfFile
, 0)) != noErr
)
426 wxLogSysError(wxString::Format(wxT("File:%s does not exist\nError:%i"),
427 m_sndname.c_str(), nError));
432 if (OpenMovieFile (&sfFile
, &movieResFile
, fsRdPerm
) != noErr
)
434 wxLogSysError(wxT("Quicktime couldn't open the file"));
437 short movieResID
= 0;
440 err
= NewMovieFromFile (
448 CloseMovieFile (movieResFile
);
454 wxString::Format(wxT("wxSound - Could not open file: %s\nError:%i"), m_sndname
.c_str(), err
)
462 }//end switch(m_type)
467 if (flags
& wxSOUND_ASYNC
)
469 //Start timer and play movie asyncronously
470 lastSoundTimer
= ((wxQTTimer
*&)m_pTimer
) =
471 new wxQTTimer(movie
, flags
& wxSOUND_LOOP
? 1 : 0,
472 &lastSoundIsPlaying
);
473 lastSoundIsPlaying
= true;
474 ((wxQTTimer
*)m_pTimer
)->Start(MOVIE_DELAY
, wxTIMER_CONTINUOUS
);
478 wxASSERT_MSG(!(flags
& wxSOUND_LOOP
), wxT("Can't loop and play syncronously at the same time"));
480 //Play movie until it ends, then exit
481 //Note that due to quicktime caching this may not always
482 //work 100% correctly
483 while (!IsMovieDone(movie
))
484 MoviesTask(movie
, 1);
492 bool wxSound::IsPlaying()
494 return lastSoundIsPlaying
;
499 if (lastSoundIsPlaying
)
501 delete (wxTimer
*&) lastSoundTimer
;
502 lastSoundIsPlaying
= false;
503 lastSoundTimer
= NULL
;
507 void* wxSound::GetHandle()
509 if(m_type
== wxSound_RESOURCE
)
510 return (void*) ((wxSMTimer
*)m_pTimer
)->GetChannel();
512 return (void*) ((wxQTTimer
*) m_pTimer
)->GetMovie();