1 /* AudioCore sound handler
2 *
3 * Copyright 2008 Chris Bagwell And Sundry Contributors
4 */
5
6 #include "sox_i.h"
7
8 #include <CoreAudio/CoreAudio.h>
9 #include <pthread.h>
10
11 #define Buffactor 4
12
13 typedef struct {
14 AudioDeviceID adid;
15 pthread_mutex_t mutex;
16 pthread_cond_t cond;
17 int device_started;
18 size_t bufsize;
19 size_t bufrd;
20 size_t bufwr;
21 size_t bufrdavail;
22 float *buf;
23 } priv_t;
24
PlaybackIOProc(AudioDeviceID inDevice UNUSED,const AudioTimeStamp * inNow UNUSED,const AudioBufferList * inInputData UNUSED,const AudioTimeStamp * inInputTime UNUSED,AudioBufferList * outOutputData,const AudioTimeStamp * inOutputTime UNUSED,void * inClientData)25 static OSStatus PlaybackIOProc(AudioDeviceID inDevice UNUSED,
26 const AudioTimeStamp *inNow UNUSED,
27 const AudioBufferList *inInputData UNUSED,
28 const AudioTimeStamp *inInputTime UNUSED,
29 AudioBufferList *outOutputData,
30 const AudioTimeStamp *inOutputTime UNUSED,
31 void *inClientData)
32 {
33 priv_t *ac = (priv_t*)((sox_format_t*)inClientData)->priv;
34 AudioBuffer *buf;
35 size_t copylen, avail;
36
37 pthread_mutex_lock(&ac->mutex);
38
39 for(buf = outOutputData->mBuffers;
40 buf != outOutputData->mBuffers + outOutputData->mNumberBuffers;
41 buf++){
42
43 copylen = buf->mDataByteSize / sizeof(float);
44 if(copylen > ac->bufrdavail)
45 copylen = ac->bufrdavail;
46
47 avail = ac->bufsize - ac->bufrd;
48 if(buf->mData == NULL){
49 /*do nothing-hardware can't play audio*/
50 }else if(copylen > avail){
51 memcpy(buf->mData, ac->buf + ac->bufrd, avail * sizeof(float));
52 memcpy((float*)buf->mData + avail, ac->buf, (copylen - avail) * sizeof(float));
53 }else{
54 memcpy(buf->mData, ac->buf + ac->bufrd, copylen * sizeof(float));
55 }
56
57 buf->mDataByteSize = copylen * sizeof(float);
58 ac->bufrd += copylen;
59 if(ac->bufrd >= ac->bufsize)
60 ac->bufrd -= ac->bufsize;
61 ac->bufrdavail -= copylen;
62 }
63
64 pthread_cond_signal(&ac->cond);
65 pthread_mutex_unlock(&ac->mutex);
66
67 return kAudioHardwareNoError;
68 }
69
RecIOProc(AudioDeviceID inDevice UNUSED,const AudioTimeStamp * inNow UNUSED,const AudioBufferList * inInputData,const AudioTimeStamp * inInputTime UNUSED,AudioBufferList * outOutputData UNUSED,const AudioTimeStamp * inOutputTime UNUSED,void * inClientData)70 static OSStatus RecIOProc(AudioDeviceID inDevice UNUSED,
71 const AudioTimeStamp *inNow UNUSED,
72 const AudioBufferList *inInputData,
73 const AudioTimeStamp *inInputTime UNUSED,
74 AudioBufferList *outOutputData UNUSED,
75 const AudioTimeStamp *inOutputTime UNUSED,
76 void *inClientData)
77 {
78 priv_t *ac = (priv_t *)((sox_format_t*)inClientData)->priv;
79 AudioBuffer const *buf;
80 size_t nfree, copylen, avail;
81
82 pthread_mutex_lock(&ac->mutex);
83
84 for(buf = inInputData->mBuffers;
85 buf != inInputData->mBuffers + inInputData->mNumberBuffers;
86 buf++){
87
88 if(buf->mData == NULL)
89 continue;
90
91 copylen = buf->mDataByteSize / sizeof(float);
92 nfree = ac->bufsize - ac->bufrdavail - 1;
93 if(nfree == 0)
94 lsx_warn("coreaudio: unhandled buffer overrun. Data discarded.");
95
96 if(copylen > nfree)
97 copylen = nfree;
98
99 avail = ac->bufsize - ac->bufwr;
100 if(copylen > avail){
101 memcpy(ac->buf + ac->bufwr, buf->mData, avail * sizeof(float));
102 memcpy(ac->buf, (float*)buf->mData + avail, (copylen - avail) * sizeof(float));
103 }else{
104 memcpy(ac->buf + ac->bufwr, buf->mData, copylen * sizeof(float));
105 }
106
107 ac->bufwr += copylen;
108 if(ac->bufwr >= ac->bufsize)
109 ac->bufwr -= ac->bufsize;
110 ac->bufrdavail += copylen;
111 }
112
113 pthread_cond_signal(&ac->cond);
114 pthread_mutex_unlock(&ac->mutex);
115
116 return kAudioHardwareNoError;
117 }
118
setup(sox_format_t * ft,int is_input)119 static int setup(sox_format_t *ft, int is_input)
120 {
121 priv_t *ac = (priv_t *)ft->priv;
122 OSStatus status;
123 UInt32 property_size;
124 struct AudioStreamBasicDescription stream_desc;
125 int32_t buf_size;
126 int rc;
127
128 if (strncmp(ft->filename, "default", (size_t)7) == 0)
129 {
130 property_size = sizeof(ac->adid);
131 if (is_input)
132 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &property_size, &ac->adid);
133 else
134 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &property_size, &ac->adid);
135 }
136 else
137 {
138 Boolean is_writable;
139 status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &property_size, &is_writable);
140
141 if (status == noErr)
142 {
143 int device_count = property_size/sizeof(AudioDeviceID);
144 AudioDeviceID *devices;
145
146 devices = malloc(property_size);
147 status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &property_size, devices);
148
149 if (status == noErr)
150 {
151 int i;
152 for (i = 0; i < device_count; i++)
153 {
154 char name[256];
155 status = AudioDeviceGetProperty(devices[i],0,false,kAudioDevicePropertyDeviceName,&property_size,&name);
156
157 lsx_report("Found Audio Device \"%s\"\n",name);
158
159 /* String returned from OS is truncated so only compare
160 * as much as returned.
161 */
162 if (strncmp(name,ft->filename,strlen(name)) == 0)
163 {
164 ac->adid = devices[i];
165 break;
166 }
167 }
168 }
169 free(devices);
170 }
171 }
172
173 if (status || ac->adid == kAudioDeviceUnknown)
174 {
175 lsx_fail_errno(ft, SOX_EPERM, "can not open audio device");
176 return SOX_EOF;
177 }
178
179 /* Query device to get initial values */
180 property_size = sizeof(struct AudioStreamBasicDescription);
181 status = AudioDeviceGetProperty(ac->adid, 0, is_input,
182 kAudioDevicePropertyStreamFormat,
183 &property_size, &stream_desc);
184 if (status)
185 {
186 lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
187 return SOX_EOF;
188 }
189
190 if (!(stream_desc.mFormatFlags & kLinearPCMFormatFlagIsFloat))
191 {
192 lsx_fail_errno(ft, SOX_EPERM, "audio device does not accept floats");
193 return SOX_EOF;
194 }
195
196 /* OS X effectively only supports these values. */
197 ft->signal.channels = 2;
198 ft->signal.rate = 44100;
199 ft->encoding.bits_per_sample = 32;
200
201 /* TODO: My limited experience with hardware can only get floats working
202 * withh a fixed sample rate and stereo. I know that is a limitiation of
203 * audio device I have so this may not be standard operating orders.
204 * If some hardware supports setting sample rates and channel counts
205 * then should do that over resampling and mixing.
206 */
207 #if 0
208 stream_desc.mSampleRate = ft->signal.rate;
209 stream_desc.mChannelsPerFrame = ft->signal.channels;
210
211 /* Write them back */
212 property_size = sizeof(struct AudioStreamBasicDescription);
213 status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
214 kAudioDevicePropertyStreamFormat,
215 property_size, &stream_desc);
216 if (status)
217 {
218 lsx_fail_errno(ft, SOX_EPERM, "can not set audio device properties");
219 return SOX_EOF;
220 }
221
222 /* Query device to see if it worked */
223 property_size = sizeof(struct AudioStreamBasicDescription);
224 status = AudioDeviceGetProperty(ac->adid, 0, is_input,
225 kAudioDevicePropertyStreamFormat,
226 &property_size, &stream_desc);
227
228 if (status)
229 {
230 lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
231 return SOX_EOF;
232 }
233 #endif
234
235 if (stream_desc.mChannelsPerFrame != ft->signal.channels)
236 {
237 lsx_debug("audio device did not accept %d channels. Use %d channels instead.", (int)ft->signal.channels,
238 (int)stream_desc.mChannelsPerFrame);
239 ft->signal.channels = stream_desc.mChannelsPerFrame;
240 }
241
242 if (stream_desc.mSampleRate != ft->signal.rate)
243 {
244 lsx_debug("audio device did not accept %d sample rate. Use %d instead.", (int)ft->signal.rate,
245 (int)stream_desc.mSampleRate);
246 ft->signal.rate = stream_desc.mSampleRate;
247 }
248
249 ac->bufsize = sox_globals.bufsiz / sizeof(sox_sample_t) * Buffactor;
250 ac->bufrd = 0;
251 ac->bufwr = 0;
252 ac->bufrdavail = 0;
253 ac->buf = lsx_malloc(ac->bufsize * sizeof(float));
254
255 buf_size = sox_globals.bufsiz / sizeof(sox_sample_t) * sizeof(float);
256 property_size = sizeof(buf_size);
257 status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
258 kAudioDevicePropertyBufferSize,
259 property_size, &buf_size);
260
261 rc = pthread_mutex_init(&ac->mutex, NULL);
262 if (rc)
263 {
264 lsx_fail_errno(ft, SOX_EPERM, "failed initializing mutex");
265 return SOX_EOF;
266 }
267
268 rc = pthread_cond_init(&ac->cond, NULL);
269 if (rc)
270 {
271 lsx_fail_errno(ft, SOX_EPERM, "failed initializing condition");
272 return SOX_EOF;
273 }
274
275 ac->device_started = 0;
276
277 /* Registers callback with the device without activating it. */
278 if (is_input)
279 status = AudioDeviceAddIOProc(ac->adid, RecIOProc, (void *)ft);
280 else
281 status = AudioDeviceAddIOProc(ac->adid, PlaybackIOProc, (void *)ft);
282
283 return SOX_SUCCESS;
284 }
285
startread(sox_format_t * ft)286 static int startread(sox_format_t *ft)
287 {
288 return setup(ft, 1);
289 }
290
read_samples(sox_format_t * ft,sox_sample_t * buf,size_t nsamp)291 static size_t read_samples(sox_format_t *ft, sox_sample_t *buf, size_t nsamp)
292 {
293 priv_t *ac = (priv_t *)ft->priv;
294 size_t len;
295 SOX_SAMPLE_LOCALS;
296
297 if (!ac->device_started) {
298 AudioDeviceStart(ac->adid, RecIOProc);
299 ac->device_started = 1;
300 }
301
302 pthread_mutex_lock(&ac->mutex);
303
304 /* Wait until input buffer has been filled by device driver */
305 while (ac->bufrdavail == 0)
306 pthread_cond_wait(&ac->cond, &ac->mutex);
307
308 len = 0;
309 while(len < nsamp && ac->bufrdavail > 0){
310 buf[len] = SOX_FLOAT_32BIT_TO_SAMPLE(ac->buf[ac->bufrd], ft->clips);
311 len++;
312 ac->bufrd++;
313 if(ac->bufrd == ac->bufsize)
314 ac->bufrd = 0;
315 ac->bufrdavail--;
316 }
317
318 pthread_mutex_unlock(&ac->mutex);
319
320 return len;
321 }
322
stopread(sox_format_t * ft)323 static int stopread(sox_format_t * ft)
324 {
325 priv_t *ac = (priv_t *)ft->priv;
326
327 AudioDeviceStop(ac->adid, RecIOProc);
328 AudioDeviceRemoveIOProc(ac->adid, RecIOProc);
329 pthread_cond_destroy(&ac->cond);
330 pthread_mutex_destroy(&ac->mutex);
331 free(ac->buf);
332
333 return SOX_SUCCESS;
334 }
335
startwrite(sox_format_t * ft)336 static int startwrite(sox_format_t * ft)
337 {
338 return setup(ft, 0);
339 }
340
write_samples(sox_format_t * ft,const sox_sample_t * buf,size_t nsamp)341 static size_t write_samples(sox_format_t *ft, const sox_sample_t *buf, size_t nsamp)
342 {
343 priv_t *ac = (priv_t *)ft->priv;
344 size_t i;
345
346 SOX_SAMPLE_LOCALS;
347
348 pthread_mutex_lock(&ac->mutex);
349
350 /* Wait to start until mutex is locked to help prevent callback
351 * getting zero samples.
352 */
353 if(!ac->device_started){
354 if(AudioDeviceStart(ac->adid, PlaybackIOProc)){
355 pthread_mutex_unlock(&ac->mutex);
356 return SOX_EOF;
357 }
358 ac->device_started = 1;
359 }
360
361 /* globals.bufsize is in samples
362 * buf_offset is in bytes
363 * buf_size is in bytes
364 */
365 for(i = 0; i < nsamp; i++){
366 while(ac->bufrdavail == ac->bufsize - 1)
367 pthread_cond_wait(&ac->cond, &ac->mutex);
368
369 ac->buf[ac->bufwr] = SOX_SAMPLE_TO_FLOAT_32BIT(buf[i], ft->clips);
370 ac->bufwr++;
371 if(ac->bufwr == ac->bufsize)
372 ac->bufwr = 0;
373 ac->bufrdavail++;
374 }
375
376 pthread_mutex_unlock(&ac->mutex);
377 return nsamp;
378 }
379
380
stopwrite(sox_format_t * ft)381 static int stopwrite(sox_format_t * ft)
382 {
383 priv_t *ac = (priv_t *)ft->priv;
384
385 if(ac->device_started){
386 pthread_mutex_lock(&ac->mutex);
387
388 while (ac->bufrdavail > 0)
389 pthread_cond_wait(&ac->cond, &ac->mutex);
390
391 pthread_mutex_unlock(&ac->mutex);
392
393 AudioDeviceStop(ac->adid, PlaybackIOProc);
394 }
395
396 AudioDeviceRemoveIOProc(ac->adid, PlaybackIOProc);
397 pthread_cond_destroy(&ac->cond);
398 pthread_mutex_destroy(&ac->mutex);
399 free(ac->buf);
400
401 return SOX_SUCCESS;
402 }
403
LSX_FORMAT_HANDLER(coreaudio)404 LSX_FORMAT_HANDLER(coreaudio)
405 {
406 static char const *const names[] = { "coreaudio", NULL };
407 static unsigned const write_encodings[] = {
408 SOX_ENCODING_FLOAT, 32, 0,
409 0};
410 static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
411 "Mac AudioCore device driver",
412 names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
413 startread, read_samples, stopread,
414 startwrite, write_samples, stopwrite,
415 NULL, write_encodings, NULL, sizeof(priv_t)
416 };
417 return &handler;
418 }
419