X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/655719367ac5e131d9642e5783f3ecf64d1a3385..8064223b7b1b3657363b7a635c381b9269d95e55:/src/mac/carbon/sound.cpp?ds=inline diff --git a/src/mac/carbon/sound.cpp b/src/mac/carbon/sound.cpp index d6d13bb595..b3ff384fc8 100644 --- a/src/mac/carbon/sound.cpp +++ b/src/mac/carbon/sound.cpp @@ -1,243 +1,476 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: sound.cpp +// Name: src/mac/carbon/sound.cpp // Purpose: wxSound class implementation: optional -// Author: Stefan Csomor -// Modified by: +// Author: Ryan Norton +// Modified by: Stefan Csomor // Created: 1998-01-01 // RCS-ID: $Id$ -// Copyright: (c) Stefan Csomor -// Licence: wxWindows licence +// Copyright: (c) Ryan Norton +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "sound.h" -#endif +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#if wxUSE_SOUND -#include "wx/object.h" -#include "wx/string.h" #include "wx/sound.h" -#if wxUSE_SOUND +#ifndef WX_PRECOMP + #include "wx/object.h" + #include "wx/string.h" + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/timer.h" +#endif + +#include "wx/file.h" + +// Carbon QT Implementation Details - +// +// Memory: +// 1) OpenDefaultComponent(MovieImportType, kQTFileTypeWave); +// 2) NewMovie(0); +// 3) MovieImportDataRef() //Pass Memory Location to this +// 4) PlayMovie(); +// 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime +// +// File: +// 1) Path as CFString +// 2) Call QTNewDataReferenceFromFullPathCFString +// 3) Call NewMovieFromDataRef +// 4) Call CloseMovieFile +// 4) PlayMovie(); +// 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime +// #ifdef __WXMAC__ -#include "wx/mac/private.h" +#include "wx/mac/uma.h" #ifndef __DARWIN__ -#include +#include +#include #endif #endif -wxSound::wxSound() - : m_sndChan(0), m_hSnd(NULL), m_waveLength(0), m_isResource(true) -{ -} +#include -wxSound::wxSound(const wxString& sFileName, bool isResource) - : m_sndChan(0), m_hSnd(NULL), m_waveLength(0), m_isResource(true) -{ - Create(sFileName, isResource); -} +//quicktime media layer only required for mac emulation on pc +#ifndef __WXMAC__ +#include +#endif +#ifndef __DARWIN__ +#include +#else +#include +#endif -wxSound::~wxSound() -{ - FreeData(); -} +//Time between timer calls +#define MOVIE_DELAY 100 -wxSound::wxSound(int size, const wxByte* data) - : m_sndChan(0), m_hSnd(NULL), m_waveLength(0), m_isResource(false) -{ - //TODO convert data -} +static wxTimer* lastSoundTimer=NULL; +static bool lastSoundIsPlaying=false; -bool wxSound::Create(const wxString& fileName, bool isResource) -{ - bool ret = false; - m_sndname = fileName; - m_isResource = isResource; +#if !defined(__LP64__) +#define USE_QUICKTIME 1 +#else +#define USE_QUICKTIME 0 +#endif - if (m_isResource) - ret = true; - else - { /* - if (sndChan) - { // we're playing - FSClose(SndRefNum); - SndRefNum = 0; - SndDisposeChannel(sndChan, TRUE); - free(sndChan); - sndChan = 0; - KillTimer(0,timerID); - } +#if USE_QUICKTIME +// ------------------------------------------------------------------ +// wxQTTimer - Handle Asyncronous Playing +// ------------------------------------------------------------------ +class wxQTTimer : public wxTimer +{ +public: + wxQTTimer(Movie movie, bool bLoop, bool* playing) : + m_movie(movie), m_bLoop(bLoop), m_pbPlaying(playing) + { + } - if (!lpSnd) - return true; + virtual ~wxQTTimer() + { + if(m_pbPlaying) + *m_pbPlaying = false; - if (_access(lpSnd,0)) // no file, no service - return false; + StopMovie(m_movie); + DisposeMovie(m_movie); + Stop(); - // Allocate SndChannel - sndChan = (SndChannelPtr) malloc (sizeof(SndChannel)); + //Note that ExitMovies() is not necessary, but + //the docs are fuzzy on whether or not TerminateQTML is + ExitMovies(); - if (!sndChan) - return false; +#ifndef __WXMAC__ + TerminateQTML(); +#endif + } - sndChan->qLength = 128; + void Shutdown() + { + delete this; + } - if (noErr != SndNewChannel (&sndChan, sampledSynth, initMono | initNoInterp, 0)) + void Notify() + { + if (m_pbPlaying && !*m_pbPlaying) { - free(sndChan); - sndChan = 0; - return false; + Shutdown(); } - if (!(SndRefNum = MacOpenSndFile ((char *)lpSnd))) + if(IsMovieDone(m_movie)) { - SndDisposeChannel(sndChan, TRUE); - free(sndChan); - sndChan = 0; - - return false; + if (!m_bLoop) + Shutdown(); + else + { + StopMovie(m_movie); + GoToBeginningOfMovie(m_movie); + StartMovie(m_movie); + } } + else + MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie + } + + + Movie& GetMovie() {return m_movie;} - bool async = false; +protected: + Movie m_movie; + bool m_bLoop; - if (fdwSound & SND_ASYNC) - async = true; +public: + bool* m_pbPlaying; - if (SndStartFilePlay(sndChan, SndRefNum, 0, 81920, 0, 0, 0, async) != noErr) +}; + + +class wxSMTimer : public wxTimer +{ +public: + wxSMTimer(void* hSnd, void* pSndChannel, bool bLoop, bool* playing) + : m_hSnd(hSnd), m_pSndChannel(pSndChannel), m_bLoop(bLoop), m_pbPlaying(playing) + { + } + + virtual ~wxSMTimer() + { + if(m_pbPlaying) + *m_pbPlaying = false; + SndDisposeChannel((SndChannelPtr)m_pSndChannel, TRUE); + Stop(); + } + + void Notify() + { + if (m_pbPlaying && !*m_pbPlaying) { - FSClose (SndRefNum); - SndRefNum = 0; - SndDisposeChannel (sndChan, TRUE); - free (sndChan); - sndChan = 0; - return false; + Shutdown(); } - if (async) - { // haven't finish yet - timerID = SetTimer(0, 0, 250, TimerCallBack); - } - else + SCStatus stat; + + if (SndChannelStatus((SndChannelPtr)m_pSndChannel, sizeof(SCStatus), &stat) != 0) + Shutdown(); + + //if the sound isn't playing anymore, see if it's looped, + //and if so play it again, otherwise close things up + if (stat.scChannelBusy == FALSE) { - FSClose (SndRefNum); - SndRefNum = 0; - SndDisposeChannel (sndChan, TRUE); - free (sndChan); - sndChan = 0; - }*/ + if (m_bLoop) + { + if(SndPlay((SndChannelPtr)m_pSndChannel, (SndListHandle) m_hSnd, true) != noErr) + Shutdown(); + } + else + Shutdown(); + } } - return ret; -} + void Shutdown() + { + delete this; + } + void* GetChannel() {return m_pSndChannel;} -//don't know what to do with looped, wth -bool wxSound::DoPlay(unsigned flags) const +protected: + void* m_hSnd; + void* m_pSndChannel; + bool m_bLoop; + +public: + bool* m_pbPlaying; +}; + +// ------------------------------------------------------------------ +// wxSound +// ------------------------------------------------------------------ + +//Determines whether version 4 of QT is installed +Boolean wxIsQuickTime4Installed (void) { - bool ret = false; +#ifdef __WXMAC__ + short error; + long result; + + error = Gestalt (gestaltQuickTime, &result); + return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400); +#else + return true; +#endif +} - if (m_isResource) +inline bool wxInitQT () +{ + if (wxIsQuickTime4Installed()) { - Str255 snd ; - wxMacStringToPascal( m_sndname , snd ) ; - SndListHandle hSnd; + #ifndef __WXMAC__ + int nError; + //-2093 no dll + if ((nError = InitializeQTML(0)) != noErr) + wxLogSysError(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError)); + #endif + EnterMovies(); + return true; + } + else + { + wxLogSysError(wxT("Quicktime is not installed, or Your Version of Quicktime is <= 4.")); + return false; + } +} - hSnd = (SndListHandle) GetNamedResource('snd ', snd); +#endif - if ((hSnd != NULL) && (SndPlay((SndChannelPtr)m_sndChan, (SndListHandle) hSnd, (flags & wxSOUND_ASYNC)) == noErr)) - ret = true; - } +wxSound::wxSound() +: m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE) +{ +} - return ret; +wxSound::wxSound(const wxString& sFileName, bool isResource) +: m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE) +{ + Create(sFileName, isResource); } +wxSound::wxSound(int size, const wxByte* data) +: m_hSnd((char*)data), m_waveLength(size), m_pTimer(NULL), m_type(wxSound_MEMORY) +{ +} + +wxSound::~wxSound() +{ +} -bool wxSound::FreeData() +bool wxSound::Create(const wxString& fileName, bool isResource) { - bool ret = false; + Stop(); - if (m_isResource) + if (isResource) { - m_sndname.Empty(); - ret = true; +#ifdef __WXMAC__ + m_type = wxSound_RESOURCE; + + Str255 lpSnd ; + + wxMacStringToPascal( fileName , lpSnd ) ; + + m_sndname = fileName; + m_hSnd = (char*) GetNamedResource('snd ', (const unsigned char *) lpSnd); +#else + return false; +#endif } else { - //TODO, + m_type = wxSound_FILE; + m_sndname = fileName; } - return ret; + return true; } +bool wxSound::DoPlay(unsigned flags) const +{ + Stop(); -//code below is from an old implementation used for telinfo with MSVC crossplatform support -//technology proceeds, so it would be the wisest to drop this code, but it's left here just -//for the sake of a reference. BTW: Wave files can now be played with QT, starting from V3 +#if USE_QUICKTIME -/*static short MacOpenSndFile (char * path) -{ - VolumeParam vp; - FSSpec fspec; - Str255 name; - char *c; - - // first, get the volume reference number for the file. Start by - // making a Pstring with just the volume name - strcpy ((char *) name, path); - if (c = strchr ((char *) name, ':')) - { - c++; - *c = '\0'; - } + Movie movie; - c2pstr ((char *) name); - vp.ioCompletion = 0; - vp.ioVolIndex = -1; - vp.ioNamePtr = name; - vp.ioVRefNum = 0; + switch(m_type) + { + case wxSound_MEMORY: + { + if (!wxInitQT()) + return false; + Handle myHandle, dataRef = nil; + MovieImportComponent miComponent; + Track targetTrack = nil; + TimeValue addedDuration = 0; + long outFlags = 0; + OSErr err; + ComponentResult result; + + myHandle = NewHandleClear((Size)m_waveLength); + + BlockMove(m_hSnd, *myHandle, m_waveLength); + + err = PtrToHand(&myHandle, &dataRef, sizeof(Handle)); + + if (memcmp(&m_hSnd[8], "WAVE", 4) == 0) + miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeWave); + else if (memcmp(&m_hSnd[8], "AIFF", 4) == 0) + miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFF); + else if (memcmp(&m_hSnd[8], "AIFC", 4) == 0) + miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFC); + else + { + wxLogSysError(wxT("wxSound - Location in memory does not contain valid data")); + return false; + } + + movie = NewMovie(0); + + result = MovieImportDataRef(miComponent, dataRef, + HandleDataHandlerSubType, movie, + nil, &targetTrack, + nil, &addedDuration, + movieImportCreateTrack, &outFlags); + + if (result != noErr) + { + wxLogSysError(wxString::Format(wxT("Couldn't import movie data\nError:%i"), (int)result)); + } + + SetMovieVolume(movie, kFullVolume); + GoToBeginningOfMovie(movie); + + DisposeHandle(myHandle); + } + break; + case wxSound_RESOURCE: + { + SoundComponentData data; + unsigned long numframes, offset; - if (PBGetVInfo((ParamBlockRec *)&vp, 0) != noErr) - return 0; + ParseSndHeader((SndListHandle)m_hSnd, &data, &numframes, &offset); + //m_waveLength = numFrames * data.numChannels; - // next, buld an FSSpec for the file - strcpy ((char *) name, path); - c2pstr ((char *) name); - if (FSMakeFSSpec (vp.ioVRefNum, 0, name, &fspec) != noErr) - return 0; + SndChannelPtr pSndChannel; + SndNewChannel(&pSndChannel, sampledSynth, + initNoInterp + + (data.numChannels == 1 ? initMono : initStereo), NULL); - short frefnum; - // now open the file, and return it's reference number - if (FSpOpenDF(&fspec, fsRdPerm, &frefnum) != noErr) - return 0; + if(SndPlay(pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr) + return false; - return frefnum; -} + if (flags & wxSOUND_ASYNC) + { + lastSoundTimer = ((wxSMTimer*&)m_pTimer) + = new wxSMTimer(pSndChannel, m_hSnd, flags & wxSOUND_LOOP ? 1 : 0, + &lastSoundIsPlaying); + lastSoundIsPlaying = true; + ((wxTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); + } + else + SndDisposeChannel(pSndChannel, TRUE); -void TimerCallBack(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime) -{ - if(!sndChan) + return true; + } + break; + case wxSound_FILE: + { + if (!wxInitQT()) + return false; + + OSErr err = noErr ; + + Handle dataRef = NULL; + OSType dataRefType; + + err = QTNewDataReferenceFromFullPathCFString(wxCFStringRef(m_sndname,wxLocale::GetSystemEncoding()), + (UInt32)kQTNativeDefaultPathStyle, 0, &dataRef, &dataRefType); + + wxASSERT(err == noErr); + + if (NULL != dataRef || err != noErr) + { + err = NewMovieFromDataRef( &movie, newMovieDontAskUnresolvedDataRefs , NULL, dataRef, dataRefType ); + wxASSERT(err == noErr); + DisposeHandle(dataRef); + } + + if (err != noErr) + { + wxLogSysError( + wxString::Format(wxT("wxSound - Could not open file: %s\nError:%i"), m_sndname.c_str(), err ) + ); + return false; + } + } + break; + default: + return false; + }//end switch(m_type) + + //Start the movie! + StartMovie(movie); + + if (flags & wxSOUND_ASYNC) { - KillTimer(0,timerID); - return; + //Start timer and play movie asyncronously + lastSoundTimer = ((wxQTTimer*&)m_pTimer) = + new wxQTTimer(movie, flags & wxSOUND_LOOP ? 1 : 0, + &lastSoundIsPlaying); + lastSoundIsPlaying = true; + ((wxQTTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); } + else + { + wxASSERT_MSG(!(flags & wxSOUND_LOOP), wxT("Can't loop and play syncronously at the same time")); - SCStatus scstat; + //Play movie until it ends, then exit + //Note that due to quicktime caching this may not always + //work 100% correctly + while (!IsMovieDone(movie)) + MoviesTask(movie, 1); - if (noErr == SndChannelStatus (sndChan, sizeof (SCStatus), &scstat)) { - if (scstat.scChannelPaused || scstat.scChannelBusy) - return; // not done yet + DisposeMovie(movie); } +#endif + + return true; +} + +bool wxSound::IsPlaying() +{ + return lastSoundIsPlaying; +} - // either error or done. - FSClose (SndRefNum); - SndRefNum = 0; - SndDisposeChannel (sndChan, TRUE); - free (sndChan); - sndChan = 0; - KillTimer(0,timerID); -}*/ +void wxSound::Stop() +{ + if (lastSoundIsPlaying) + { + delete (wxTimer*&) lastSoundTimer; + lastSoundIsPlaying = false; + lastSoundTimer = NULL; + } +} +void* wxSound::GetHandle() +{ +#if USE_QUICKTIME + if(m_type == wxSound_RESOURCE) + return (void*) ((wxSMTimer*)m_pTimer)->GetChannel(); + return (void*) ((wxQTTimer*) m_pTimer)->GetMovie(); #endif + return NULL; +} + +#endif //wxUSE_SOUND