1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/carbon/sound.cpp
3 // Purpose: wxSound class implementation: optional
4 // Author: Ryan Norton
5 // Modified by: Stefan Csomor
6 // Created: 1998-01-01
7 // Copyright: (c) Ryan Norton
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #if wxUSE_SOUND
15
16 #if wxOSX_USE_QUICKTIME
17
18 #include "wx/sound.h"
19
20 #ifndef WX_PRECOMP
21 #include "wx/object.h"
22 #include "wx/string.h"
23 #include "wx/intl.h"
24 #include "wx/log.h"
25 #include "wx/timer.h"
26 #endif
27
28 #include "wx/file.h"
29
30 // Carbon QT Implementation Details -
31 //
32 // Memory:
33 // 1) OpenDefaultComponent(MovieImportType, kQTFileTypeWave);
34 // 2) NewMovie(0);
35 // 3) MovieImportDataRef() //Pass Memory Location to this
36 // 4) PlayMovie();
37 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
38 //
39 // File:
40 // 1) Path as CFString
41 // 2) Call QTNewDataReferenceFromFullPathCFString
42 // 3) Call NewMovieFromDataRef
43 // 4) Call CloseMovieFile
44 // 4) PlayMovie();
45 // 5) IsMovieDone(), MoviesTask() //2nd param is minimum wait time to allocate to quicktime
46 //
47
48 #ifdef __WXMAC__
49 #include "wx/osx/private.h"
50 #if wxOSX_USE_COCOA_OR_CARBON
51 #include <QuickTime/QuickTimeComponents.h>
52 #endif
53 #else
54 #include <qtml.h>
55 #endif
56
57 #define MOVIE_DELAY 100
58
59 // ------------------------------------------------------------------
60 // SoundManager
61 // ------------------------------------------------------------------
62
63 class wxOSXSoundManagerSoundData : public wxSoundData
64 {
65 public:
66 wxOSXSoundManagerSoundData(const wxString& fileName);
67 ~wxOSXSoundManagerSoundData();
68
69 virtual bool Play(unsigned flags);
70 virtual void SoundTask();
71
72 void DoStop();
73 protected:
74 SndListHandle m_hSnd;
75 SndChannelPtr m_pSndChannel;
76 };
77
wxOSXSoundManagerSoundData(const wxString & fileName)78 wxOSXSoundManagerSoundData::wxOSXSoundManagerSoundData(const wxString& fileName) :
79 m_pSndChannel(NULL)
80 {
81 Str255 lpSnd ;
82
83 wxMacStringToPascal( fileName , lpSnd ) ;
84
85 m_hSnd = (SndListHandle) GetNamedResource('snd ', (const unsigned char *) lpSnd);
86 }
87
~wxOSXSoundManagerSoundData()88 wxOSXSoundManagerSoundData::~wxOSXSoundManagerSoundData()
89 {
90 DoStop();
91 ReleaseResource((Handle)m_hSnd);
92 }
93
DoStop()94 void wxOSXSoundManagerSoundData::DoStop()
95 {
96 if ( m_pSndChannel )
97 {
98 SndDisposeChannel(m_pSndChannel, TRUE /* stop immediately, not after playing */);
99 m_pSndChannel = NULL;
100 wxSound::SoundStopped(this);
101 }
102
103 if (IsMarkedForDeletion())
104 delete this;
105 }
106
Play(unsigned flags)107 bool wxOSXSoundManagerSoundData::Play(unsigned flags)
108 {
109 Stop();
110
111 m_flags = flags;
112
113 SoundComponentData data;
114 unsigned long numframes, offset;
115
116 ParseSndHeader((SndListHandle)m_hSnd, &data, &numframes, &offset);
117
118 SndNewChannel(&m_pSndChannel, sampledSynth,
119 initNoInterp
120 + (data.numChannels == 1 ? initMono : initStereo), NULL);
121
122 if(SndPlay(m_pSndChannel, (SndListHandle) m_hSnd, flags & wxSOUND_ASYNC ? 1 : 0) != noErr)
123 return false;
124
125 if (flags & wxSOUND_ASYNC)
126 CreateAndStartTimer();
127 else
128 DoStop();
129
130 return true;
131 }
132
SoundTask()133 void wxOSXSoundManagerSoundData::SoundTask()
134 {
135 SCStatus stat;
136
137 if (SndChannelStatus((SndChannelPtr)m_pSndChannel, sizeof(SCStatus), &stat) != 0)
138 Stop();
139
140 //if the sound isn't playing anymore, see if it's looped,
141 //and if so play it again, otherwise close things up
142 if (stat.scChannelBusy == FALSE)
143 {
144 if (m_flags & wxSOUND_LOOP)
145 {
146 if(SndPlay((SndChannelPtr)m_pSndChannel, (SndListHandle) m_hSnd, true) != noErr)
147 Stop();
148 }
149 else
150 Stop();
151 }
152 }
153
154 // ------------------------------------------------------------------
155 // QuickTime
156 // ------------------------------------------------------------------
157
158 bool wxInitQT();
wxInitQT()159 bool wxInitQT()
160 {
161 #ifndef __WXMAC__
162 int nError;
163 //-2093 no dll
164 if ((nError = InitializeQTML(0)) != noErr)
165 {
166 wxLogSysError(wxString::Format(wxT("Couldn't Initialize Quicktime-%i"), nError));
167 return false;
168 }
169 #endif
170 EnterMovies();
171 return true;
172 }
173
174 void wxExitQT();
wxExitQT()175 void wxExitQT()
176 {
177 //Note that ExitMovies() is not necessary, but
178 //the docs are fuzzy on whether or not TerminateQTML is
179 ExitMovies();
180
181 #ifndef __WXMAC__
182 TerminateQTML();
183 #endif
184 }
185
186 class wxOSXQuickTimeSoundData : public wxSoundData
187 {
188 public:
189 wxOSXQuickTimeSoundData(const wxString& fileName);
190 wxOSXQuickTimeSoundData(size_t size, const void* data);
191 ~wxOSXQuickTimeSoundData();
192
193 virtual bool Play(unsigned flags);
194 virtual void SoundTask();
195 virtual void DoStop();
196 protected:
197 Movie m_movie;
198
199 wxString m_sndname; //file path
200 Handle m_soundHandle;
201 };
202
203
wxOSXQuickTimeSoundData(const wxString & fileName)204 wxOSXQuickTimeSoundData::wxOSXQuickTimeSoundData(const wxString& fileName) :
205 m_movie(NULL), m_soundHandle(NULL)
206 {
207 m_sndname = fileName;
208 }
209
wxOSXQuickTimeSoundData(size_t size,const void * data)210 wxOSXQuickTimeSoundData::wxOSXQuickTimeSoundData(size_t size, const void* data) :
211 m_movie(NULL)
212 {
213 m_soundHandle = NewHandleClear((Size)size);
214 BlockMove(data, *m_soundHandle, size);
215 }
216
~wxOSXQuickTimeSoundData()217 wxOSXQuickTimeSoundData::~wxOSXQuickTimeSoundData()
218 {
219 if ( m_soundHandle )
220 DisposeHandle(m_soundHandle);
221 }
222
Play(unsigned flags)223 bool wxOSXQuickTimeSoundData::Play(unsigned flags)
224 {
225 if ( m_movie )
226 Stop();
227
228 m_flags = flags;
229
230 if (!wxInitQT())
231 return false;
232
233 if( m_soundHandle )
234 {
235 Handle dataRef = nil;
236 MovieImportComponent miComponent;
237 Track targetTrack = nil;
238 TimeValue addedDuration = 0;
239 long outFlags = 0;
240 OSErr err;
241 ComponentResult result;
242
243 err = PtrToHand(&m_soundHandle, &dataRef, sizeof(Handle));
244
245 HLock(m_soundHandle);
246 if (memcmp(&(*m_soundHandle)[8], "WAVE", 4) == 0)
247 miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeWave);
248 else if (memcmp(&(*m_soundHandle)[8], "AIFF", 4) == 0)
249 miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFF);
250 else if (memcmp(&(*m_soundHandle)[8], "AIFC", 4) == 0)
251 miComponent = OpenDefaultComponent(MovieImportType, kQTFileTypeAIFC);
252 else
253 {
254 HUnlock(m_soundHandle);
255 wxLogSysError(wxT("wxSound - Location in memory does not contain valid data"));
256 return false;
257 }
258
259 HUnlock(m_soundHandle);
260 m_movie = NewMovie(0);
261
262 result = MovieImportDataRef(miComponent, dataRef,
263 HandleDataHandlerSubType, m_movie,
264 nil, &targetTrack,
265 nil, &addedDuration,
266 movieImportCreateTrack, &outFlags);
267
268 if (result != noErr)
269 {
270 wxLogSysError(wxString::Format(wxT("Couldn't import movie data\nError:%i"), (int)result));
271 }
272
273 SetMovieVolume(m_movie, kFullVolume);
274 GoToBeginningOfMovie(m_movie);
275 }
276 else
277 {
278 OSErr err = noErr ;
279
280 Handle dataRef = NULL;
281 OSType dataRefType;
282
283 err = QTNewDataReferenceFromFullPathCFString(wxCFStringRef(m_sndname,wxLocale::GetSystemEncoding()),
284 (UInt32)kQTNativeDefaultPathStyle, 0, &dataRef, &dataRefType);
285
286 wxASSERT(err == noErr);
287
288 if (NULL != dataRef || err != noErr)
289 {
290 err = NewMovieFromDataRef( &m_movie, newMovieDontAskUnresolvedDataRefs , NULL, dataRef, dataRefType );
291 wxASSERT(err == noErr);
292 DisposeHandle(dataRef);
293 }
294
295 if (err != noErr)
296 {
297 wxLogSysError(
298 wxString::Format(wxT("wxSound - Could not open file: %s\nError:%i"), m_sndname.c_str(), err )
299 );
300 return false;
301 }
302 }
303
304 //Start the m_movie!
305 StartMovie(m_movie);
306
307 if (flags & wxSOUND_ASYNC)
308 {
309 CreateAndStartTimer();
310 }
311 else
312 {
313 wxASSERT_MSG(!(flags & wxSOUND_LOOP), wxT("Can't loop and play syncronously at the same time"));
314
315 //Play movie until it ends, then exit
316 //Note that due to quicktime caching this may not always
317 //work 100% correctly
318 while (!IsMovieDone(m_movie))
319 MoviesTask(m_movie, 1);
320
321 DoStop();
322 }
323
324 return true;
325 }
326
DoStop()327 void wxOSXQuickTimeSoundData::DoStop()
328 {
329 if( m_movie )
330 {
331 StopMovie(m_movie);
332 DisposeMovie(m_movie);
333 m_movie = NULL;
334 wxSound::SoundStopped(this);
335 wxExitQT();
336 }
337 }
338
SoundTask()339 void wxOSXQuickTimeSoundData::SoundTask()
340 {
341 if(IsMovieDone(m_movie))
342 {
343 if (m_flags & wxSOUND_LOOP)
344 {
345 StopMovie(m_movie);
346 GoToBeginningOfMovie(m_movie);
347 StartMovie(m_movie);
348 }
349 else
350 Stop();
351 }
352 else
353 MoviesTask(m_movie, MOVIE_DELAY); //Give QT time to play movie
354 }
355
Create(size_t size,const void * data)356 bool wxSound::Create(size_t size, const void* data)
357 {
358 m_data = new wxOSXQuickTimeSoundData(size,data);
359 return true;
360 }
361
Create(const wxString & fileName,bool isResource)362 bool wxSound::Create(const wxString& fileName, bool isResource)
363 {
364 if ( isResource )
365 m_data = new wxOSXSoundManagerSoundData(fileName);
366 else
367 m_data = new wxOSXQuickTimeSoundData(fileName);
368 return true;
369 }
370
371 #endif // wxOSX_USE_QUICKTIME
372
373 #endif //wxUSE_SOUND
374