• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2012 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
PrintStreamDesc(AudioStreamBasicDescription * inDesc)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 
AudioFilePlayer_SetDestination(AudioFilePlayer * afp,AudioUnit * inDestUnit)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 
AudioFilePlayer_SetNotifier(AudioFilePlayer * afp,AudioFilePlayNotifier inNotifier,void * inRefCon)100 static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon)
101 {
102     afp->mNotifier = inNotifier;
103     afp->mRefCon = inRefCon;
104 }
105 
AudioFilePlayer_IsConnected(AudioFilePlayer * afp)106 static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp)
107 {
108     return afp->mConnected;
109 }
110 
AudioFilePlayer_GetDestUnit(AudioFilePlayer * afp)111 static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp)
112 {
113    return afp->mPlayUnit;
114 }
115 
AudioFilePlayer_Print(AudioFilePlayer * afp)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 
AudioFilePlayer_SetStartFrame(AudioFilePlayer * afp,int frame)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 
AudioFilePlayer_GetCurrentFrame(AudioFilePlayer * afp)133 static int    AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp)
134 {
135     return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352);
136 }
137 
AudioFilePlayer_SetStopFrame(AudioFilePlayer * afp,int frame)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 
delete_AudioFilePlayer(AudioFilePlayer * afp)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 
AudioFilePlayer_Connect(AudioFilePlayer * afp)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 */
AudioFilePlayer_DoNotification(AudioFilePlayer * afp,OSStatus inStatus)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 
AudioFilePlayer_Disconnect(AudioFilePlayer * afp)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 
AudioFilePlayer_OpenFile(AudioFilePlayer * afp,const FSRef * inRef,SInt64 * outFileDataSize)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 
new_AudioFilePlayer(const FSRef * inFileRef)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 
361