]> git.saurik.com Git - wxWidgets.git/blobdiff - src/mac/carbon/sound.cpp
Performance optimization
[wxWidgets.git] / src / mac / carbon / sound.cpp
index 19cd0450911816bc589bece219fa2ce195050aa1..b3ff384fc86d9544d2b81cede45cca6357e8de31 100644 (file)
@@ -1,26 +1,30 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        sound.cpp
+// Name:        src/mac/carbon/sound.cpp
 // Purpose:     wxSound class implementation: optional
-// Author:      Ryan Norton, Stefan Csomor
-// Modified by:
+// Author:      Ryan Norton
+// Modified by: Stefan Csomor
 // Created:     1998-01-01
 // RCS-ID:      $Id$
-// Copyright:   (c) Ryan Norton, 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/log.h"
-#include "wx/file.h"
 #include "wx/sound.h"
-#include "wx/timer.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 -
 //
 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
 //
 // File:
-// 1) Obtain FSSpec
-// 2) Call OpenMovieFile
-// 3) Call NewMovieFromFile
+// 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 <Movies.h>
 #include <Gestalt.h>
 #endif
+#endif
 
-#if defined __WXMAC__ && defined __DARWIN__/*TARGET_CARBON*/
-#ifdef __APPLE_CC__
 #include <Carbon/Carbon.h>
-#else
-#include <Carbon.h>
-#endif
-#else
-#include <Sound.h>
-#endif
 
 //quicktime media layer only required for mac emulation on pc
 #ifndef __WXMAC__
 #include <qtml.h>
 #endif
 
+#ifndef __DARWIN__
 #include <QuickTimeComponents.h>
+#else
+#include <QuickTime/QuickTimeComponents.h>
+#endif
 
-//Time inbetween timer calls
+//Time between timer calls
 #define MOVIE_DELAY 100
 
+static wxTimer* lastSoundTimer=NULL;
+static bool lastSoundIsPlaying=false;
+
+#if !defined(__LP64__)
+#define USE_QUICKTIME 1
+#else
+#define USE_QUICKTIME 0
+#endif
+
+#if USE_QUICKTIME
 // ------------------------------------------------------------------
 //          wxQTTimer - Handle Asyncronous Playing
 // ------------------------------------------------------------------
 class wxQTTimer : public wxTimer
 {
 public:
-    wxQTTimer(Movie movie, bool bLoop) :
-        m_movie(movie), m_bLoop(bLoop)
+    wxQTTimer(Movie movie, bool bLoop, bool* playing) :
+        m_movie(movie), m_bLoop(bLoop), m_pbPlaying(playing)
     {
     }
 
-    ~wxQTTimer()
+    virtual ~wxQTTimer()
     {
-        Shutdown();
-    }
+        if(m_pbPlaying)
+            *m_pbPlaying = false;
 
-    void Shutdown()
-    {
         StopMovie(m_movie);
         DisposeMovie(m_movie);
-        m_movie = NULL ;
         Stop();
+
+        //Note that ExitMovies() is not necessary, but
+        //the docs are fuzzy on whether or not TerminateQTML is
+        ExitMovies();
+
+#ifndef __WXMAC__
+        TerminateQTML();
+#endif
+    }
+
+    void Shutdown()
+    {
+        delete this;
     }
 
     void Notify()
     {
+        if (m_pbPlaying && !*m_pbPlaying)
+        {
+            Shutdown();
+        }
+
         if(IsMovieDone(m_movie))
         {
             if (!m_bLoop)
@@ -99,9 +126,9 @@ public:
             else
             {
                 StopMovie(m_movie);
-                GoToBeginningOfMovie(m_movie);            
+                GoToBeginningOfMovie(m_movie);
                 StartMovie(m_movie);
-        }
+            }
         }
         else
             MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie
@@ -109,56 +136,73 @@ public:
 
 
     Movie& GetMovie() {return m_movie;}
+
 protected:
     Movie m_movie;
     bool m_bLoop;
+
+public:
+    bool* m_pbPlaying;
+
 };
 
 
 class wxSMTimer : public wxTimer
 {
 public:
-    wxSMTimer(void* hSnd, void* pSndChannel, const bool& bLoop)
-       : m_hSnd(hSnd), m_pSndChannel(pSndChannel), m_bLoop(bLoop)
-     {
-     }
+    wxSMTimer(void* hSnd, void* pSndChannel, bool bLoop, bool* playing)
+        : m_hSnd(hSnd), m_pSndChannel(pSndChannel), m_bLoop(bLoop), m_pbPlaying(playing)
+    {
+    }
 
-    ~wxSMTimer()
-        {
-           Shutdown();
-        }
+    virtual ~wxSMTimer()
+    {
+        if(m_pbPlaying)
+            *m_pbPlaying = false;
+        SndDisposeChannel((SndChannelPtr)m_pSndChannel, TRUE);
+        Stop();
+    }
 
     void Notify()
+    {
+        if (m_pbPlaying && !*m_pbPlaying)
         {
-       SCStatus stat;
+            Shutdown();
+        }
+
+        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)
-           {
-               if (m_bLoop)
-               {
-                       if(SndPlay((SndChannelPtr)m_pSndChannel, (SndListHandle) m_hSnd, true) != noErr)
-                           Shutdown();
-               }
-               else
-                   Shutdown();
+            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)
+        {
+            if (m_bLoop)
+            {
+                if(SndPlay((SndChannelPtr)m_pSndChannel, (SndListHandle) m_hSnd, true) != noErr)
+                    Shutdown();
+            }
+            else
+                Shutdown();
         }
     }
 
     void Shutdown()
     {
-        SndDisposeChannel((SndChannelPtr)m_pSndChannel, TRUE);
-       Stop();
+        delete this;
     }
+
+    void* GetChannel() {return m_pSndChannel;}
+
 protected:
     void* m_hSnd;
     void* m_pSndChannel;
     bool m_bLoop;
+
+public:
+    bool* m_pbPlaying;
 };
 
 // ------------------------------------------------------------------
@@ -173,7 +217,7 @@ Boolean wxIsQuickTime4Installed (void)
     long result;
 
     error = Gestalt (gestaltQuickTime, &result);
-    return (error == noErr) && (result >= 4);  //result >= 4 correct?
+    return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
 #else
     return true;
 #endif
@@ -187,18 +231,20 @@ inline bool wxInitQT ()
         int nError;
         //-2093 no dll
             if ((nError = InitializeQTML(0)) != noErr)
-                wxLogSysError(wxString::Format("Couldn't Initialize Quicktime-%i", nError));
-        #endif 
+                wxLogSysError(wxString::Format(wxT("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;
-        }
+    {
+        wxLogSysError(wxT("Quicktime is not installed, or Your Version of Quicktime is <= 4."));
+        return false;
+    }
 }
 
+#endif
+
 wxSound::wxSound()
 : m_hSnd(NULL), m_waveLength(0), m_pTimer(NULL), m_type(wxSound_NONE)
 {
@@ -213,19 +259,15 @@ wxSound::wxSound(const wxString& sFileName, bool isResource)
 wxSound::wxSound(int size, const wxByte* data)
 : m_hSnd((char*)data), m_waveLength(size), m_pTimer(NULL), m_type(wxSound_MEMORY)
 {
-    if (!wxInitQT())
-        m_type = wxSound_NONE;
 }
 
 wxSound::~wxSound()
 {
-    FreeData();
 }
 
 bool wxSound::Create(const wxString& fileName, bool isResource)
 {
-    if(!wxInitQT())
-        return false;
+    Stop();
 
     if (isResource)
     {
@@ -236,14 +278,14 @@ bool wxSound::Create(const wxString& fileName, bool isResource)
 
         wxMacStringToPascal( fileName , lpSnd ) ;
 
-               m_sndname = lpSnd;
+        m_sndname = fileName;
         m_hSnd = (char*) GetNamedResource('snd ', (const unsigned char *) lpSnd);
 #else
         return false;
 #endif
-        }
-        else
-        {
+    }
+    else
+    {
         m_type = wxSound_FILE;
         m_sndname = fileName;
     }
@@ -253,7 +295,9 @@ bool wxSound::Create(const wxString& fileName, bool isResource)
 
 bool wxSound::DoPlay(unsigned flags) const
 {
-    wxASSERT(m_pTimer == NULL || !((wxTimer*)m_pTimer)->IsRunning() );
+    Stop();
+
+#if USE_QUICKTIME
 
     Movie movie;
 
@@ -261,8 +305,10 @@ bool wxSound::DoPlay(unsigned flags) const
     {
     case wxSound_MEMORY:
         {
+            if (!wxInitQT())
+                return false;
             Handle myHandle, dataRef = nil;
-            MovieImportComponent miComponent; 
+            MovieImportComponent miComponent;
             Track targetTrack = nil;
             TimeValue addedDuration = 0;
             long outFlags = 0;
@@ -270,7 +316,7 @@ bool wxSound::DoPlay(unsigned flags) const
             ComponentResult result;
 
             myHandle = NewHandleClear((Size)m_waveLength);
-            
+
             BlockMove(m_hSnd, *myHandle, m_waveLength);
 
             err = PtrToHand(&myHandle, &dataRef, sizeof(Handle));
@@ -282,8 +328,8 @@ bool wxSound::DoPlay(unsigned flags) const
             else if (memcmp(&m_hSnd[8], "AIFC", 4) == 0)
                 miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFC);
             else
-    {
-                wxLogSysError("wxSound - Location in memory does not contain valid data");
+            {
+                wxLogSysError(wxT("wxSound - Location in memory does not contain valid data"));
                 return false;
             }
 
@@ -298,10 +344,12 @@ bool wxSound::DoPlay(unsigned flags) const
             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:
@@ -314,19 +362,22 @@ bool wxSound::DoPlay(unsigned flags) const
 
             SndChannelPtr pSndChannel;
             SndNewChannel(&pSndChannel, sampledSynth,
-                                   initNoInterp + 
-                        (data.numChannels == 1 ? initMono : initStereo), NULL);
+                initNoInterp
+                + (data.numChannels == 1 ? initMono : initStereo), NULL);
 
             if(SndPlay(pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr)
-                   return false;
+                return false;
 
             if (flags & wxSOUND_ASYNC)
-    {
-                ((wxSMTimer*&)m_pTimer) = new wxSMTimer(pSndChannel, m_hSnd, flags & wxSOUND_LOOP ? 1 : 0);
-                   ((wxTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS); 
-    }
-    else
+            {
+                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);
 
             return true;
@@ -334,99 +385,92 @@ bool wxSound::DoPlay(unsigned flags) const
         break;
     case wxSound_FILE:
         {
-            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));
+            if (!wxInitQT())
                 return false;
-    }
-#endif
 
-            if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr)
-            {
-                wxLogSysError(wxT("Quicktime couldn't open the file"));
-                return false;
-            }
+            OSErr err = noErr ;
 
+            Handle dataRef = NULL;
+            OSType dataRefType;
 
-            short movieResID = 0;
-            Str255 movieName;
-            OSErr err;
+            err = QTNewDataReferenceFromFullPathCFString(wxCFStringRef(m_sndname,wxLocale::GetSystemEncoding()),
+                (UInt32)kQTNativeDefaultPathStyle, 0, &dataRef, &dataRefType);
 
-            err = NewMovieFromFile (
-            &movie,
-            movieResFile,
-            &movieResID,
-            movieName,
-            newMovieActive,
-            NULL); //wasChanged
+            wxASSERT(err == noErr);
 
-            CloseMovieFile (movieResFile);
+            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_SYNC)
+    if (flags & wxSOUND_ASYNC)
     {
-        wxASSERT_MSG(!(flags & wxSOUND_LOOP), "Can't loop and play syncronously at the same time");
+        //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"));
 
         //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, 0);
+            MoviesTask(movie, 1);
 
         DisposeMovie(movie);
     }
-    else
-    {
-        //Start timer and play movie asyncronously
-        ((wxQTTimer*&)m_pTimer) = new wxQTTimer(movie, flags & wxSOUND_LOOP ? 1 : 0);
-        ((wxQTTimer*)m_pTimer)->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
-    }
+#endif
 
     return true;
 }
 
-void* wxSound::GetHandle() 
+bool wxSound::IsPlaying()
 {
-    return (void*) ((wxQTTimer*) m_pTimer)->GetMovie();
+    return lastSoundIsPlaying;
 }
 
-bool wxSound::FreeData()
+void wxSound::Stop()
 {
-    if (m_pTimer != NULL)
+    if (lastSoundIsPlaying)
     {
-        delete (wxQTTimer*) m_pTimer;
-        m_pTimer = NULL;
+        delete (wxTimer*&) lastSoundTimer;
+        lastSoundIsPlaying = false;
+        lastSoundTimer = NULL;
     }
+}
 
-    //Note that ExitMovies() is not neccessary, but
-    //the docs are fuzzy on whether or not TerminateQTML is
-    ExitMovies();
+void* wxSound::GetHandle()
+{
+#if USE_QUICKTIME
+    if(m_type == wxSound_RESOURCE)
+        return (void*)  ((wxSMTimer*)m_pTimer)->GetChannel();
 
-#ifndef __WXMAC__
-    TerminateQTML();
+    return (void*) ((wxQTTimer*) m_pTimer)->GetMovie();
 #endif
-    return true;
+    return NULL;
 }
+
 #endif //wxUSE_SOUND