SDL-1.2.14
[sdl_omap.git] / src / cdrom / macosx / AudioFilePlayer.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 Sam Lantinga
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14
15     You should have received a copy of the GNU Library General Public
16     License along with this library; if not, write to the Free
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19     Sam Lantinga
20     slouken@libsdl.org
21
22     This file based on Apple sample code. We haven't changed the file name, 
23     so if you want to see the original search for it on apple.com/developer
24 */
25 #include "SDL_config.h"
26 #include "SDL_endian.h"
27
28 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
29     AudioFilePlayer.cpp
30 */
31 #include "AudioFilePlayer.h"
32
33 /*
34 void ThrowResult (OSStatus result, const char* str)
35 {
36     SDL_SetError ("Error: %s %d", str, result);
37     throw result;
38 }
39 */
40
41 #if DEBUG
42 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
43 {
44     if (!inDesc) {
45         printf ("Can't print a NULL desc!\n");
46         return;
47     }
48     
49     printf ("- - - - - - - - - - - - - - - - - - - -\n");
50     printf ("  Sample Rate:%f\n", inDesc->mSampleRate);
51     printf ("  Format ID:%s\n", (char*)&inDesc->mFormatID);
52     printf ("  Format Flags:%lX\n", inDesc->mFormatFlags);
53     printf ("  Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
54     printf ("  Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
55     printf ("  Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
56     printf ("  Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
57     printf ("  Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
58     printf ("- - - - - - - - - - - - - - - - - - - -\n");
59 }
60 #endif
61
62
63 static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit  *inDestUnit)
64 {
65     /*if (afp->mConnected) throw static_cast<OSStatus>(-1);*/ /* can't set dest if already engaged */
66     if (afp->mConnected)
67         return 0 ;
68
69     SDL_memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit));
70
71     OSStatus result = noErr;
72     
73
74         /* we can "down" cast a component instance to a component */
75     ComponentDescription desc;
76     result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0);
77     if (result) return 0; /*THROW_RESULT("GetComponentInfo")*/
78         
79         /* we're going to use this to know which convert routine to call
80            a v1 audio unit will have a type of 'aunt'
81            a v2 audio unit will have one of several different types. */
82     if (desc.componentType != kAudioUnitType_Output) {
83         result = badComponentInstance;
84         /*THROW_RESULT("BAD COMPONENT")*/
85         if (result) return 0;
86     }
87
88     /* Set the input format of the audio unit. */
89     result = AudioUnitSetProperty (*inDestUnit,
90                                kAudioUnitProperty_StreamFormat,
91                                kAudioUnitScope_Input,
92                                0,
93                                &afp->mFileDescription,
94                                sizeof (afp->mFileDescription));
95         /*THROW_RESULT("AudioUnitSetProperty")*/
96     if (result) return 0;
97     return 1;
98 }
99
100 static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon)
101 {
102     afp->mNotifier = inNotifier;
103     afp->mRefCon = inRefCon;
104 }
105
106 static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp)
107 {
108     return afp->mConnected;
109 }
110
111 static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp)
112 {
113    return afp->mPlayUnit;
114 }
115
116 static void AudioFilePlayer_Print(AudioFilePlayer *afp)
117 {
118 #if DEBUG    
119     printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
120     printf ("- - - - - - - - - - - - - - \n");
121 #endif
122 }
123
124 static void    AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame)
125 {
126     SInt64 position = frame * 2352;
127
128     afp->mStartFrame = frame;
129     afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position);
130 }
131
132     
133 static int    AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp)
134 {
135     return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352);
136 }
137     
138 static void    AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame)
139 {
140     SInt64 position  = frame * 2352;
141     
142     afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position);
143 }
144     
145 void delete_AudioFilePlayer(AudioFilePlayer *afp)
146 {
147     if (afp != NULL)
148     {
149         afp->Disconnect(afp);
150         
151         if (afp->mAudioFileManager) {
152             delete_AudioFileManager(afp->mAudioFileManager);
153             afp->mAudioFileManager = 0;
154         }
155     
156         if (afp->mForkRefNum) {
157             FSCloseFork (afp->mForkRefNum);
158             afp->mForkRefNum = 0;
159         }
160         SDL_free(afp);
161     }
162 }
163
164 static int    AudioFilePlayer_Connect(AudioFilePlayer *afp)
165 {
166 #if DEBUG
167     printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0));
168 #endif
169     if (!afp->mConnected)
170     {           
171         if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
172             return 0;
173
174         /* set the render callback for the file data to be supplied to the sound converter AU */
175         afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc;
176         afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager;
177
178         OSStatus result = AudioUnitSetProperty (afp->mPlayUnit, 
179                             kAudioUnitProperty_SetRenderCallback,
180                             kAudioUnitScope_Input, 
181                             0,
182                             &afp->mInputCallback, 
183                             sizeof(afp->mInputCallback));
184         if (result) return 0;  /*THROW_RESULT("AudioUnitSetProperty")*/
185         afp->mConnected = 1;
186     }
187
188     return 1;
189 }
190
191 /* warning noted, now please go away ;-) */
192 /* #warning This should redirect the calling of notification code to some other thread */
193 static void    AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus)
194 {
195     if (afp->mNotifier) {
196         (*afp->mNotifier) (afp->mRefCon, inStatus);
197     } else {
198         SDL_SetError ("Notification posted with no notifier in place");
199         
200         if (inStatus == kAudioFilePlay_FileIsFinished)
201             afp->Disconnect(afp);
202         else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
203             afp->Disconnect(afp);
204     }
205 }
206
207 static void    AudioFilePlayer_Disconnect (AudioFilePlayer *afp)
208 {
209 #if DEBUG
210     printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0));
211 #endif
212     if (afp->mConnected)
213     {
214         afp->mConnected = 0;
215             
216         afp->mInputCallback.inputProc = 0;
217         afp->mInputCallback.inputProcRefCon = 0;
218         OSStatus result = AudioUnitSetProperty (afp->mPlayUnit, 
219                                         kAudioUnitProperty_SetRenderCallback,
220                                         kAudioUnitScope_Input, 
221                                         0,
222                                         &afp->mInputCallback, 
223                                         sizeof(afp->mInputCallback));
224         if (result) 
225             SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
226
227         afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
228     }
229 }
230
231 typedef struct {
232     UInt32 offset;
233     UInt32 blockSize;
234 } SSNDData;
235
236 static int    AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize)
237 {
238     ContainerChunk chunkHeader;
239     ChunkHeader chunk;
240     SSNDData ssndData;
241
242     OSErr result;
243     HFSUniStr255 dfName;
244     ByteCount actual;
245     SInt64 offset;
246
247     /* Open the data fork of the input file */
248     result = FSGetDataForkName(&dfName);
249        if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")*/
250
251     result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum);
252        if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")*/
253  
254     /* Read the file header, and check if it's indeed an AIFC file */
255     result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual);
256        if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
257
258     if (SDL_SwapBE32(chunkHeader.ckID) != 'FORM') {
259         result = -1;
260         if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");*/
261     }
262
263     if (SDL_SwapBE32(chunkHeader.formType) != 'AIFC') {
264         result = -1;
265         if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");*/
266     }
267
268     /* Search for the SSND chunk. We ignore all compression etc. information
269        in other chunks. Of course that is kind of evil, but for now we are lazy
270        and rely on the cdfs to always give us the same fixed format.
271        TODO: Parse the COMM chunk we currently skip to fill in mFileDescription.
272     */
273     offset = 0;
274     do {
275         result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual);
276         if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
277
278         chunk.ckID = SDL_SwapBE32(chunk.ckID);
279         chunk.ckSize = SDL_SwapBE32(chunk.ckSize);
280
281         /* Skip the chunk data */
282         offset = chunk.ckSize;
283     } while (chunk.ckID != 'SSND');
284
285     /* Read the header of the SSND chunk. After this, we are positioned right
286        at the start of the audio data. */
287     result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual);
288     if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
289
290     ssndData.offset = SDL_SwapBE32(ssndData.offset);
291
292     result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset);
293     if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")*/
294
295     /* Data size */
296     *outFileDataSize = chunk.ckSize - ssndData.offset - 8;
297
298     /* File format */
299     afp->mFileDescription.mSampleRate = 44100;
300     afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
301     afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
302     afp->mFileDescription.mBytesPerPacket = 4;
303     afp->mFileDescription.mFramesPerPacket = 1;
304     afp->mFileDescription.mBytesPerFrame = 4;
305     afp->mFileDescription.mChannelsPerFrame = 2;
306     afp->mFileDescription.mBitsPerChannel = 16;
307
308     return 1;
309 }
310
311 AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef)
312 {
313     SInt64 fileDataSize  = 0;
314
315     AudioFilePlayer *afp = (AudioFilePlayer *) SDL_malloc(sizeof (AudioFilePlayer));
316     if (afp == NULL)
317         return NULL;
318     SDL_memset(afp, '\0', sizeof (*afp));
319
320     #define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
321     SET_AUDIOFILEPLAYER_METHOD(SetDestination);
322     SET_AUDIOFILEPLAYER_METHOD(SetNotifier);
323     SET_AUDIOFILEPLAYER_METHOD(SetStartFrame);
324     SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame);
325     SET_AUDIOFILEPLAYER_METHOD(SetStopFrame);
326     SET_AUDIOFILEPLAYER_METHOD(Connect);
327     SET_AUDIOFILEPLAYER_METHOD(Disconnect);
328     SET_AUDIOFILEPLAYER_METHOD(DoNotification);
329     SET_AUDIOFILEPLAYER_METHOD(IsConnected);
330     SET_AUDIOFILEPLAYER_METHOD(GetDestUnit);
331     SET_AUDIOFILEPLAYER_METHOD(Print);
332     SET_AUDIOFILEPLAYER_METHOD(OpenFile);
333     #undef SET_AUDIOFILEPLAYER_METHOD
334
335     if (!afp->OpenFile (afp, inFileRef, &fileDataSize))
336     {
337         SDL_free(afp);
338         return NULL;
339     }
340         
341     /* we want about 4 seconds worth of data for the buffer */
342     int bytesPerSecond = (UInt32) (4 * afp->mFileDescription.mSampleRate * afp->mFileDescription.mBytesPerFrame);
343     
344 #if DEBUG
345     printf("File format:\n");
346     PrintStreamDesc (&afp->mFileDescription);
347 #endif
348     
349     afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
350                                                   fileDataSize,
351                                                   bytesPerSecond);
352     if (afp->mAudioFileManager == NULL)
353     {
354         delete_AudioFilePlayer(afp);
355         return NULL;
356     }
357
358     return afp;
359 }
360