~ubuntu-branches/ubuntu/lucid/mpg123/lucid

« back to all changes in this revision

Viewing changes to src/audio_coreaudio.c

Tags: upstream-0.66
ImportĀ upstreamĀ versionĀ 0.66

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
        audio_macosx: audio output on MacOS X
 
3
 
 
4
        copyright ?-2006 by the mpg123 project - free software under the terms of the GPL 2
 
5
        see COPYING and AUTHORS files in distribution or http://mpg123.org
 
6
        initially written by Guillaume Outters
 
7
        modified by Nicholas J Humfrey to use SFIFO code
 
8
        modified by Taihei Monma to use AudioUnit and AudioConverter APIs
 
9
*/
 
10
 
 
11
 
 
12
#include "config.h"
 
13
#include "debug.h"
 
14
#include "sfifo.h"
 
15
#include "mpg123.h"
 
16
 
 
17
#include <CoreServices/CoreServices.h>
 
18
#include <AudioUnit/AudioUnit.h>
 
19
#include <AudioToolbox/AudioToolbox.h>
 
20
#include <stdio.h>
 
21
#include <stdlib.h>
 
22
#include <errno.h>
 
23
 
 
24
#define FIFO_DURATION           (0.5f)
 
25
 
 
26
 
 
27
struct anEnv
 
28
{
 
29
        AudioConverterRef converter;
 
30
        AudioUnit outputUnit;
 
31
        char play;
 
32
        int channels;
 
33
        int last_buffer;
 
34
        int play_done;
 
35
        int decode_done;
 
36
        
 
37
        /* Convertion buffer */
 
38
        unsigned char * buffer;
 
39
        size_t buffer_size;
 
40
        
 
41
        /* Ring buffer */
 
42
        sfifo_t fifo;
 
43
};
 
44
 
 
45
static struct anEnv *env=NULL;
 
46
 
 
47
 
 
48
 
 
49
static OSStatus playProc(AudioConverterRef inAudioConverter,
 
50
                                                 UInt32 *ioNumberDataPackets,
 
51
                         AudioBufferList *outOutputData,
 
52
                         AudioStreamPacketDescription **outDataPacketDescription,
 
53
                         void* inClientData)
 
54
{
 
55
        long n;
 
56
        
 
57
        if(env->last_buffer) {
 
58
                env->play_done = 1;
 
59
                return noErr;
 
60
        }
 
61
        
 
62
        for(n = 0; n < outOutputData->mNumberBuffers; n++)
 
63
        {
 
64
                unsigned int wanted = *ioNumberDataPackets * env->channels * 2;
 
65
                unsigned char *dest;
 
66
                unsigned int read;
 
67
                if(env->buffer_size < wanted) {
 
68
                        debug1("Allocating %d byte sample conversion buffer", wanted);
 
69
                        env->buffer = realloc( env->buffer, wanted);
 
70
                        env->buffer_size = wanted;
 
71
                }
 
72
                dest = env->buffer;
 
73
                
 
74
                /* Only play if we have data left */
 
75
                if ( sfifo_used( &env->fifo ) < wanted ) {
 
76
                        if(!env->decode_done) {
 
77
                                warning("Didn't have any audio data in callback (buffer underflow)");
 
78
                                return -1;
 
79
                        }
 
80
                        wanted = sfifo_used( &env->fifo );
 
81
                        env->last_buffer = 1;
 
82
                }
 
83
                
 
84
                /* Read audio from FIFO to SDL's buffer */
 
85
                read = sfifo_read( &env->fifo, dest, wanted );
 
86
                
 
87
                if (wanted!=read)
 
88
                        warning2("Error reading from the ring buffer (wanted=%u, read=%u).\n", wanted, read);
 
89
                
 
90
                outOutputData->mBuffers[n].mDataByteSize = read;
 
91
                outOutputData->mBuffers[n].mData = dest;
 
92
        }
 
93
        
 
94
        return noErr; 
 
95
}
 
96
 
 
97
static OSStatus convertProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags,
 
98
                            const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
 
99
                            UInt32 inNumFrames, AudioBufferList *ioData)
 
100
{
 
101
        OSStatus err= noErr;
 
102
        void *inInputDataProcUserData=NULL;
 
103
        AudioStreamPacketDescription* outPacketDescription =NULL;
 
104
        
 
105
        err = AudioConverterFillComplexBuffer(env->converter, playProc, inInputDataProcUserData, &inNumFrames, ioData, outPacketDescription);
 
106
        
 
107
        return err;
 
108
}
 
109
 
 
110
 
 
111
int audio_open(struct audio_info_struct *ai)
 
112
{
 
113
        UInt32 size;
 
114
        ComponentDescription desc;
 
115
        Component comp;
 
116
        AudioStreamBasicDescription inFormat;
 
117
        AudioStreamBasicDescription outFormat;
 
118
        AURenderCallbackStruct  renderCallback;
 
119
        Boolean outWritable;
 
120
        
 
121
        /* Allocate memory for data structure */
 
122
        if (!env) {
 
123
                env = (struct anEnv*)malloc( sizeof( struct anEnv ) );
 
124
                if (!env) {
 
125
                        error("failed to malloc memory for 'struct anEnv'");
 
126
                        return -1;
 
127
                }
 
128
        }
 
129
 
 
130
        /* Initialize our environment */
 
131
        env->play = 0;
 
132
        env->buffer = NULL;
 
133
        env->buffer_size = 0;
 
134
        env->last_buffer = 0;
 
135
        env->play_done = 0;
 
136
        env->decode_done = 0;
 
137
 
 
138
        
 
139
        /* Get the default audio output unit */
 
140
        desc.componentType = kAudioUnitType_Output; 
 
141
        desc.componentSubType = kAudioUnitSubType_DefaultOutput;
 
142
        desc.componentManufacturer = kAudioUnitManufacturer_Apple;
 
143
        desc.componentFlags = 0;
 
144
        desc.componentFlagsMask = 0;
 
145
        comp = FindNextComponent(NULL, &desc);
 
146
        if(comp == NULL) {
 
147
                error("FindNextComponent failed");
 
148
                return(-1);
 
149
        }
 
150
        
 
151
        if(OpenAComponent(comp, &(env->outputUnit)))  {
 
152
                error("OpenAComponent failed");
 
153
                return (-1);
 
154
        }
 
155
        
 
156
        if(AudioUnitInitialize(env->outputUnit)) {
 
157
                error("AudioUnitInitialize failed");
 
158
                return (-1);
 
159
        }
 
160
        
 
161
        /* Specify the output PCM format */
 
162
        AudioUnitGetPropertyInfo(env->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &size, &outWritable);
 
163
        if(AudioUnitGetProperty(env->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &outFormat, &size)) {
 
164
                error("AudioUnitGetProperty(kAudioUnitProperty_StreamFormat) failed");
 
165
                return (-1);
 
166
        }
 
167
        
 
168
        if(AudioUnitSetProperty(env->outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outFormat, size)) {
 
169
                error("AudioUnitSetProperty(kAudioUnitProperty_StreamFormat) failed");
 
170
                return (-1);
 
171
        }
 
172
        
 
173
        /* Specify the input PCM format */
 
174
        env->channels = ai->channels;
 
175
        inFormat.mSampleRate = ai->rate;
 
176
        inFormat.mChannelsPerFrame = ai->channels;
 
177
        inFormat.mBitsPerChannel = 16;
 
178
        inFormat.mBytesPerPacket = 2*inFormat.mChannelsPerFrame;
 
179
        inFormat.mFramesPerPacket = 1;
 
180
        inFormat.mBytesPerFrame = 2*inFormat.mChannelsPerFrame;
 
181
        inFormat.mFormatID = kAudioFormatLinearPCM;
 
182
#ifdef _BIG_ENDIAN
 
183
        inFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
 
184
#else
 
185
        inFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
 
186
#endif
 
187
        
 
188
                
 
189
        /* Add our callback - but don't start it yet */
 
190
        memset(&renderCallback, 0, sizeof(AURenderCallbackStruct));
 
191
        renderCallback.inputProc = convertProc;
 
192
        renderCallback.inputProcRefCon = 0;
 
193
        if(AudioUnitSetProperty(env->outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct))) {
 
194
                error("AudioUnitSetProperty(kAudioUnitProperty_SetRenderCallback) failed");
 
195
                return(-1);
 
196
        }
 
197
        
 
198
        
 
199
        /* Open an audio I/O stream and create converter */
 
200
        if (ai->rate > 0 && ai->channels >0 ) {
 
201
                int ringbuffer_len;
 
202
 
 
203
                if(AudioConverterNew(&inFormat, &outFormat, &(env->converter))) {
 
204
                        error("AudioConverterNew failed");
 
205
                        return(-1);
 
206
                }
 
207
                if(ai->channels == 1) {
 
208
                        SInt32 channelMap[2] = { 0, 0 };
 
209
                        if(AudioConverterSetProperty(env->converter, kAudioConverterChannelMap, sizeof(channelMap), channelMap)) {
 
210
                                error("AudioConverterSetProperty(kAudioConverterChannelMap) failed");
 
211
                                return(-1);
 
212
                        }
 
213
                }
 
214
                
 
215
                /* Initialise FIFO */
 
216
                ringbuffer_len = ai->rate * FIFO_DURATION * sizeof(short) *ai->channels;
 
217
                debug2( "Allocating %d byte ring-buffer (%f seconds)", ringbuffer_len, (float)FIFO_DURATION);
 
218
                sfifo_init( &env->fifo, ringbuffer_len );
 
219
                                                                           
 
220
        }
 
221
        
 
222
        return(0);
 
223
}
 
224
 
 
225
 
 
226
int audio_get_formats(struct audio_info_struct *ai)
 
227
{
 
228
        /* Only support Signed 16-bit output */
 
229
        return AUDIO_FORMAT_SIGNED_16;
 
230
}
 
231
 
 
232
 
 
233
int audio_play_samples(struct audio_info_struct *ai, unsigned char *buf, int len)
 
234
{
 
235
        int written;
 
236
 
 
237
        /* If there is no room, then sleep for half the length of the FIFO */
 
238
        while (sfifo_space( &env->fifo ) < len ) {
 
239
                usleep( (FIFO_DURATION/2) * 1000000 );
 
240
        }
 
241
        
 
242
        /* Store converted audio in ring buffer */
 
243
        written = sfifo_write( &env->fifo, (char*)buf, len);
 
244
        if (written != len) {
 
245
                warning( "Failed to write audio to ring buffer" );
 
246
                return -1;
 
247
        }
 
248
        
 
249
        /* Start playback now that we have something to play */
 
250
        if(!env->play)
 
251
        {
 
252
                if(AudioOutputUnitStart(env->outputUnit)) {
 
253
                        error("AudioOutputUnitStart failed");
 
254
                        return(-1);
 
255
                }
 
256
                env->play = 1;
 
257
        }
 
258
        
 
259
        return len;
 
260
}
 
261
 
 
262
int audio_close(struct audio_info_struct *ai)
 
263
{
 
264
        if (env) {
 
265
                env->decode_done = 1;
 
266
                while(!env->play_done && env->play) usleep(10000);
 
267
                
 
268
                /* No matter the error code, we want to close it (by brute force if necessary) */
 
269
                AudioConverterDispose(env->converter);
 
270
                AudioOutputUnitStop(env->outputUnit);
 
271
                AudioUnitUninitialize(env->outputUnit);
 
272
                CloseComponent(env->outputUnit);
 
273
        
 
274
            /* Free the ring buffer */
 
275
                sfifo_close( &env->fifo );
 
276
                
 
277
                /* Free the conversion buffer */
 
278
                if (env->buffer) free( env->buffer );
 
279
                
 
280
                /* Free environment data structure */
 
281
                free(env);
 
282
                env=NULL;
 
283
        }
 
284
        
 
285
        return 0;
 
286
}
 
287
 
 
288
void audio_queueflush(struct audio_info_struct *ai)
 
289
{
 
290
 
 
291
        /* Stop playback */
 
292
        if(AudioOutputUnitStop(env->outputUnit)) {
 
293
                error("AudioOutputUnitStop failed");
 
294
        }
 
295
        env->play=0;
 
296
        
 
297
        /* Empty out the ring buffer */
 
298
        sfifo_flush( &env->fifo );      
 
299
}