X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/315ebf688c0e69bed47cd3149158198b781b5f65..3e637936e59be12938e4b020798bc1e4474cb4ba:/src/mac/carbon/sound.cpp diff --git a/src/mac/carbon/sound.cpp b/src/mac/carbon/sound.cpp index d6d13bb595..e346f5c9d4 100644 --- a/src/mac/carbon/sound.cpp +++ b/src/mac/carbon/sound.cpp @@ -1,11 +1,11 @@ ///////////////////////////////////////////////////////////////////////////// // Name: sound.cpp // Purpose: wxSound class implementation: optional -// Author: Stefan Csomor +// Author: Ryan Norton // Modified by: // Created: 1998-01-01 // RCS-ID: $Id$ -// Copyright: (c) Stefan Csomor +// Copyright: (c) Ryan Norton, Stefan Csomor // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// @@ -15,229 +15,464 @@ #include "wx/object.h" #include "wx/string.h" +#include "wx/log.h" +#include "wx/file.h" #include "wx/sound.h" +#include "wx/timer.h" #if wxUSE_SOUND +// 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) Obtain FSSpec +// 2) Call OpenMovieFile +// 3) Call NewMovieFromFile +// 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" -#ifndef __DARWIN__ +#include +#include +#endif + +#if defined __WXMAC__ && defined __DARWIN__/*TARGET_CARBON*/ +#ifdef __APPLE_CC__ +#include +#else +#include +#endif +#else #include #endif + +//quicktime media layer only required for mac emulation on pc +#ifndef __WXMAC__ +#include #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) +//Time inbetween timer calls +#define MOVIE_DELAY 100 + +// ------------------------------------------------------------------ +// wxQTTimer - Handle Asyncronous Playing +// ------------------------------------------------------------------ +class wxQTTimer : public wxTimer { - Create(sFileName, isResource); -} +public: + wxQTTimer(Movie movie, bool bLoop, bool& playing) : + m_movie(movie), m_bLoop(bLoop), m_pbPlaying(&playing) + { + } + ~wxQTTimer() + { + if(m_pbPlaying) + *m_pbPlaying = false; -wxSound::~wxSound() -{ - FreeData(); -} + StopMovie(m_movie); + DisposeMovie(m_movie); + Stop(); -wxSound::wxSound(int size, const wxByte* data) - : m_sndChan(0), m_hSnd(NULL), m_waveLength(0), m_isResource(false) -{ - //TODO convert data -} + //Note that ExitMovies() is not neccessary, but + //the docs are fuzzy on whether or not TerminateQTML is + ExitMovies(); -bool wxSound::Create(const wxString& fileName, bool isResource) -{ - bool ret = false; - m_sndname = fileName; - m_isResource = isResource; +#ifndef __WXMAC__ + TerminateQTML(); +#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); + void Shutdown() + { + delete this; + } + + void Notify() + { + if (m_pbPlaying && !*m_pbPlaying) + { + Shutdown(); } - if (!lpSnd) - return true; + if(IsMovieDone(m_movie)) + { + 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 + } - if (_access(lpSnd,0)) // no file, no service - return false; - // Allocate SndChannel - sndChan = (SndChannelPtr) malloc (sizeof(SndChannel)); + Movie& GetMovie() {return m_movie;} - if (!sndChan) - return false; +protected: + Movie m_movie; + bool m_bLoop; - sndChan->qLength = 128; +public: + bool* m_pbPlaying; - if (noErr != SndNewChannel (&sndChan, sampledSynth, initMono | initNoInterp, 0)) - { - free(sndChan); - sndChan = 0; - return false; - } +}; - if (!(SndRefNum = MacOpenSndFile ((char *)lpSnd))) - { - SndDisposeChannel(sndChan, TRUE); - free(sndChan); - sndChan = 0; - return false; +class wxSMTimer : public wxTimer +{ +public: + wxSMTimer(void* hSnd, void* pSndChannel, const bool& bLoop, bool& playing) + : m_hSnd(hSnd), m_pSndChannel(pSndChannel), m_bLoop(bLoop), m_pbPlaying(&playing) + { + } + + ~wxSMTimer() + { + if(m_pbPlaying) + *m_pbPlaying = false; + SndDisposeChannel((SndChannelPtr)m_pSndChannel, TRUE); + Stop(); + } + + void Notify() + { + if (m_pbPlaying && !*m_pbPlaying) + { + Shutdown(); } - bool async = false; + SCStatus stat; - if (fdwSound & SND_ASYNC) - async = true; + if (SndChannelStatus((SndChannelPtr)m_pSndChannel, sizeof(SCStatus), &stat) != 0) + Shutdown(); - if (SndStartFilePlay(sndChan, SndRefNum, 0, 81920, 0, 0, 0, async) != noErr) + //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; - return false; + if (m_bLoop) + { + if(SndPlay((SndChannelPtr)m_pSndChannel, (SndListHandle) m_hSnd, true) != noErr) + Shutdown(); + } + else + Shutdown(); } + } - if (async) - { // haven't finish yet - timerID = SetTimer(0, 0, 250, TimerCallBack); - } - else - { - FSClose (SndRefNum); - SndRefNum = 0; - SndDisposeChannel (sndChan, TRUE); - free (sndChan); - sndChan = 0; - }*/ + void Shutdown() + { + delete this; } - return ret; -} + void* GetChannel() {return m_pSndChannel;} +protected: + void* m_hSnd; + void* m_pSndChannel; + bool m_bLoop; -//don't know what to do with looped, wth -bool wxSound::DoPlay(unsigned flags) const +public: + bool* m_pbPlaying; +}; + +// ------------------------------------------------------------------ +// wxSound +// ------------------------------------------------------------------ +wxTimer* lastSoundTimer=NULL; +bool lastSoundIsPlaying=false; + +//Determines whether version 4 of QT is installed +Boolean wxIsQuickTime4Installed (void) { - bool ret = false; +#ifdef __WXMAC__ + short error; + long result; - if (m_isResource) + error = Gestalt (gestaltQuickTime, &result); + return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400); +#else + return true; +#endif +} + +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("Couldn't Initialize Quicktime-%i", nError)); + #endif + EnterMovies(); + return true; + } + else + { + wxLogSysError("Quicktime is not installed, or Your Version of Quicktime is <= 4."); + return false; + } +} - hSnd = (SndListHandle) GetNamedResource('snd ', snd); +wxSound::wxSound() +: m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE) +{ +} - if ((hSnd != NULL) && (SndPlay((SndChannelPtr)m_sndChan, (SndListHandle) hSnd, (flags & wxSOUND_ASYNC)) == noErr)) - ret = true; - } +wxSound::wxSound(const wxString& sFileName, bool isResource) +: m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE) +{ + Create(sFileName, isResource); +} - return ret; +wxSound::wxSound(int size, const wxByte* data) +: m_hSnd((char*)data), m_waveLength(size), m_pTimer(NULL), m_type(wxSound_MEMORY) +{ } +wxSound::~wxSound() +{ + if(lastSoundIsPlaying) + { + if(m_type == wxSound_RESOURCE) + ((wxSMTimer*)lastSoundTimer)->m_pbPlaying = NULL; + else + ((wxQTTimer*)lastSoundTimer)->m_pbPlaying = NULL; + } +} -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 = lpSnd; + 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; } - -//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 - -/*static short MacOpenSndFile (char * path) +bool wxSound::DoPlay(unsigned flags) const { - VolumeParam vp; - FSSpec fspec; - Str255 name; - char *c; + Stop(); + + Movie movie; - // 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, ':')) + switch(m_type) { - c++; - *c = '\0'; - } + 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("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; - c2pstr ((char *) name); - vp.ioCompletion = 0; - vp.ioVolIndex = -1; - vp.ioNamePtr = name; - vp.ioVRefNum = 0; + ParseSndHeader((SndListHandle)m_hSnd, &data, &numframes, &offset); + //m_waveLength = numFrames * data.numChannels; - if (PBGetVInfo((ParamBlockRec *)&vp, 0) != noErr) - return 0; + SndChannelPtr pSndChannel; + SndNewChannel(&pSndChannel, sampledSynth, + initNoInterp + + (data.numChannels == 1 ? initMono : initStereo), NULL); - // next, buld an FSSpec for the file - strcpy ((char *) name, path); - c2pstr ((char *) name); - if (FSMakeFSSpec (vp.ioVRefNum, 0, name, &fspec) != noErr) - return 0; + if(SndPlay(pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr) + return false; - short frefnum; - // now open the file, and return it's reference number - if (FSpOpenDF(&fspec, fsRdPerm, &frefnum) != noErr) - return 0; + if (flags & wxSOUND_ASYNC) + { + lastSoundTimer = ((wxSMTimer*&)m_pTimer) + = new wxSMTimer(pSndChannel, m_hSnd, flags & wxSOUND_LOOP ? 1 : 0, + lastSoundIsPlaying=true); - return frefnum; -} + ((wxTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); + } + else + SndDisposeChannel(pSndChannel, TRUE); + return true; + } + break; + case wxSound_FILE: + { + if (!wxInitQT()) + return false; -void TimerCallBack(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime) -{ - if(!sndChan) + short movieResFile; + FSSpec sfFile; + +#ifdef __WXMAC__ + wxMacFilename2FSSpec( m_sndname , &sfFile ) ; +#else + int nError; + if ((nError = NativePathNameToFSSpec ((char*) m_sndname.c_str(), &sfFile, 0)) != noErr) + { + wxLogSysError(wxString::Format(wxT("File:%s does not exist\nError:%i"), + m_sndname.c_str(), nError)); + return false; + } +#endif + + if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr) + { + wxLogSysError(wxT("Quicktime couldn't open the file")); + return false; + } + + + short movieResID = 0; + Str255 movieName; + OSErr err; + + err = NewMovieFromFile ( + &movie, + movieResFile, + &movieResID, + movieName, + newMovieActive, + NULL); //wasChanged + + CloseMovieFile (movieResFile); + + 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_SYNC) { - KillTimer(0,timerID); - return; + wxASSERT_MSG(!(flags & wxSOUND_LOOP), "Can't loop and play syncronously at the same time"); + + //Play movie until it ends, then exit + while (!IsMovieDone(movie)) + MoviesTask(movie, 0); + + DisposeMovie(movie); + } + else + { + //Start timer and play movie asyncronously + lastSoundTimer = ((wxQTTimer*&)m_pTimer) = new wxQTTimer(movie, flags & wxSOUND_LOOP ? 1 : 0,lastSoundIsPlaying=true); + ((wxQTTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); } - SCStatus scstat; + return true; +} - if (noErr == SndChannelStatus (sndChan, sizeof (SCStatus), &scstat)) { - if (scstat.scChannelPaused || scstat.scChannelBusy) - return; // not done yet +bool wxSound::IsPlaying() +{ + return lastSoundIsPlaying; +} + +void wxSound::Stop() +{ + if(lastSoundIsPlaying) + { + delete (wxTimer*&) lastSoundTimer; + lastSoundIsPlaying = false; } +} - // either error or done. - FSClose (SndRefNum); - SndRefNum = 0; - SndDisposeChannel (sndChan, TRUE); - free (sndChan); - sndChan = 0; - KillTimer(0,timerID); -}*/ +void* wxSound::GetHandle() +{ + if(m_type == wxSound_RESOURCE) + return (void*) ((wxSMTimer*)m_pTimer)->GetChannel(); + return (void*) ((wxQTTimer*) m_pTimer)->GetMovie(); +} -#endif +#endif //wxUSE_SOUND