README.osx wasn't easily readable in Finder. Revert back to
[sox.git] / src / coreaudio.c
blob8de17e3753d3c11a949051d3ba97e4ce73c26c0c
1 /* AudioCore sound handler
3 * Copyright 2008 Chris Bagwell And Sundry Contributors
4 */
6 #include "sox_i.h"
8 #include <CoreAudio/CoreAudio.h>
9 #include <pthread.h>
11 typedef struct {
12 AudioDeviceID adid;
13 pthread_mutex_t mutex;
14 pthread_cond_t cond;
15 int device_started;
16 size_t buf_size;
17 size_t buf_offset;
18 float *buffer;
19 } priv_t;
21 static OSStatus PlaybackIOProc(AudioDeviceID inDevice UNUSED,
22 const AudioTimeStamp *inNow UNUSED,
23 const AudioBufferList *inInputData UNUSED,
24 const AudioTimeStamp *inInputTime UNUSED,
25 AudioBufferList *outOutputData,
26 const AudioTimeStamp *inOutputTime UNUSED,
27 void *inClientData)
29 sox_format_t *ft = (sox_format_t *)inClientData;
30 priv_t *ac = (priv_t *)ft->priv;
31 char *buf = outOutputData->mBuffers[0].mData;
32 unsigned int len, output_len;
34 if (outOutputData->mNumberBuffers != 1)
36 lsx_warn("coreaudio: unhandled extra buffer. Data discarded.");
37 return kAudioHardwareNoError;
40 buf = (char *)outOutputData->mBuffers[0].mData;
41 output_len = outOutputData->mBuffers[0].mDataByteSize;
43 pthread_mutex_lock(&ac->mutex);
45 len = (ac->buf_offset < output_len) ? ac->buf_offset : output_len;
47 /* Make sure to write 2 (stereo) floats at a time */
48 if (len % 8)
49 len -= len % 8;
51 memcpy(buf, ac->buffer, len);
53 /* Fill partial output buffers with silence */
54 if (len < output_len)
55 memset(buf+len, 0, output_len-len);
57 ac->buf_offset -= len;
59 pthread_mutex_unlock(&ac->mutex);
60 pthread_cond_signal(&ac->cond);
62 return kAudioHardwareNoError;
65 static OSStatus RecIOProc(AudioDeviceID inDevice UNUSED,
66 const AudioTimeStamp *inNow UNUSED,
67 const AudioBufferList *inInputData,
68 const AudioTimeStamp *inInputTime UNUSED,
69 AudioBufferList *outOutputData UNUSED,
70 const AudioTimeStamp *inOutputTime UNUSED,
71 void *inClientData)
73 sox_format_t *ft = (sox_format_t *)inClientData;
74 priv_t *ac = (priv_t *)ft->priv;
75 size_t len, output_len;
76 char *destbuf;
77 char *buf;
78 int i;
80 pthread_mutex_lock(&ac->mutex);
82 if (inInputData->mNumberBuffers != 1)
84 lsx_warn("coreaudio: unhandled extra buffer. Data discarded.");
85 return kAudioHardwareNoError;
88 destbuf = ((char *)ac->buffer + ac->buf_offset);
89 buf = inInputData->mBuffers[0].mData;
90 output_len = inInputData->mBuffers[0].mDataByteSize;
92 /* mDataByteSize may be non-zero even when mData is NULL, but that is
93 * not an error.
95 if (buf == NULL)
96 return kAudioHardwareNoError;
98 len = ac->buf_size - ac->buf_offset;
100 /* Make sure to read 2 (stereo) floats at a time */
101 if (len % 8)
102 len -= len % 8;
104 if (len > output_len)
105 len = output_len;
107 /* FIXME: Handle buffer overrun. */
108 if (len < output_len)
109 lsx_warn("coreaudio: unhandled buffer overrun. Data discarded.");
111 memcpy(destbuf, buf, len);
112 ac->buf_offset += len;
114 pthread_mutex_unlock(&ac->mutex);
115 pthread_cond_signal(&ac->cond);
117 return kAudioHardwareNoError;
120 static int setup(sox_format_t *ft, int is_input)
122 priv_t *ac = (priv_t *)ft->priv;
123 OSStatus status;
124 UInt32 property_size;
125 struct AudioStreamBasicDescription stream_desc;
126 int32_t buf_size;
127 int rc;
129 if (strncmp(ft->filename, "default", (size_t)7) == 0)
131 property_size = sizeof(ac->adid);
132 if (is_input)
133 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &property_size, &ac->adid);
134 else
135 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &property_size, &ac->adid);
137 else
139 Boolean is_writable;
140 status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &property_size, &is_writable);
142 if (status == noErr)
144 int device_count = property_size/sizeof(AudioDeviceID);
145 AudioDeviceID *devices;
147 devices = malloc(property_size);
148 status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &property_size, devices);
150 if (status == noErr)
152 int i;
153 for (i = 0; i < property_size/sizeof(AudioDeviceID); i++)
155 char name[256];
156 status = AudioDeviceGetProperty(devices[i],0,false,kAudioDevicePropertyDeviceName,&property_size,&name);
158 lsx_report("Found Audio Device \"%s\"\n",name);
160 /* String returned from OS is truncated so only compare
161 * as much as returned.
163 if (strncmp(name,ft->filename,strlen(name)) == 0)
165 ac->adid = devices[i];
166 break;
170 free(devices);
174 if (status || ac->adid == kAudioDeviceUnknown)
176 lsx_fail_errno(ft, SOX_EPERM, "can not open audio device");
177 return SOX_EOF;
180 /* Query device to get initial values */
181 property_size = sizeof(struct AudioStreamBasicDescription);
182 status = AudioDeviceGetProperty(ac->adid, 0, is_input,
183 kAudioDevicePropertyStreamFormat,
184 &property_size, &stream_desc);
185 if (status)
187 lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
188 return SOX_EOF;
191 if (!(stream_desc.mFormatFlags & kLinearPCMFormatFlagIsFloat))
193 lsx_fail_errno(ft, SOX_EPERM, "audio device does not accept floats");
194 return SOX_EOF;
197 /* OS X effectively only supports these values. */
198 ft->signal.channels = 2;
199 ft->signal.rate = 44100;
200 ft->encoding.bits_per_sample = 32;
202 /* TODO: My limited experience with hardware can only get floats working
203 * withh a fixed sample rate and stereo. I know that is a limitiation of
204 * audio device I have so this may not be standard operating orders.
205 * If some hardware supports setting sample rates and channel counts
206 * then should do that over resampling and mixing.
208 #if 0
209 stream_desc.mSampleRate = ft->signal.rate;
210 stream_desc.mChannelsPerFrame = ft->signal.channels;
212 /* Write them back */
213 property_size = sizeof(struct AudioStreamBasicDescription);
214 status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
215 kAudioDevicePropertyStreamFormat,
216 property_size, &stream_desc);
217 if (status)
219 lsx_fail_errno(ft, SOX_EPERM, "can not set audio device properties");
220 return SOX_EOF;
223 /* Query device to see if it worked */
224 property_size = sizeof(struct AudioStreamBasicDescription);
225 status = AudioDeviceGetProperty(ac->adid, 0, is_input,
226 kAudioDevicePropertyStreamFormat,
227 &property_size, &stream_desc);
229 if (status)
231 lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
232 return SOX_EOF;
234 #endif
236 if (stream_desc.mChannelsPerFrame != ft->signal.channels)
238 lsx_debug("audio device did not accept %d channels. Use %d channels instead.", (int)ft->signal.channels,
239 (int)stream_desc.mChannelsPerFrame);
240 ft->signal.channels = stream_desc.mChannelsPerFrame;
243 if (stream_desc.mSampleRate != ft->signal.rate)
245 lsx_debug("audio device did not accept %d sample rate. Use %d instead.", (int)ft->signal.rate,
246 (int)stream_desc.mSampleRate);
247 ft->signal.rate = stream_desc.mSampleRate;
250 ac->buf_size = sox_globals.bufsiz * sizeof(float);
251 ac->buf_offset = 0;
252 ac->buffer = lsx_malloc(ac->buf_size);
254 buf_size = ac->buf_size;
255 property_size = sizeof(buf_size);
256 status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
257 kAudioDevicePropertyBufferSize,
258 property_size, &buf_size);
260 rc = pthread_mutex_init(&ac->mutex, NULL);
261 if (rc)
263 lsx_fail_errno(ft, SOX_EPERM, "failed initializing mutex");
264 return SOX_EOF;
267 rc = pthread_cond_init(&ac->cond, NULL);
268 if (rc)
270 lsx_fail_errno(ft, SOX_EPERM, "failed initializing condition");
271 return SOX_EOF;
274 ac->device_started = 0;
276 /* Registers callback with the device without activating it. */
277 if (is_input)
278 status = AudioDeviceAddIOProc(ac->adid, RecIOProc, (void *)ft);
279 else
280 status = AudioDeviceAddIOProc(ac->adid, PlaybackIOProc, (void *)ft);
282 return SOX_SUCCESS;
285 static int startread(sox_format_t *ft)
287 return setup(ft, 1);
290 static size_t read_samples(sox_format_t *ft, sox_sample_t *buf, size_t nsamp)
292 priv_t *ac = (priv_t *)ft->priv;
293 size_t len = nsamp;
294 size_t samp_left;
295 OSStatus status;
296 float *p;
297 SOX_SAMPLE_LOCALS;
299 if (!ac->device_started)
301 status = AudioDeviceStart(ac->adid, RecIOProc);
302 ac->device_started = 1;
305 pthread_mutex_lock(&ac->mutex);
307 /* Wait until input buffer has been filled by device driver */
308 while (ac->buf_offset < ac->buf_size)
309 pthread_cond_wait(&ac->cond, &ac->mutex);
311 len = ac->buf_offset / sizeof(float);
312 for (p = ac->buffer, samp_left = len; samp_left > 0; samp_left--, buf++, p++)
313 *buf = SOX_FLOAT_32BIT_TO_SAMPLE(*p, ft->clips);
314 ac->buf_offset = 0;
316 pthread_mutex_unlock(&ac->mutex);
318 return len;
321 static int stopread(sox_format_t * ft)
323 priv_t *ac = (priv_t *)ft->priv;
325 AudioDeviceStop(ac->adid, RecIOProc);
326 AudioDeviceRemoveIOProc(ac->adid, RecIOProc);
327 pthread_cond_destroy(&ac->cond);
328 pthread_mutex_destroy(&ac->mutex);
330 return SOX_SUCCESS;
333 static int startwrite(sox_format_t * ft)
335 return setup(ft, 0);
338 static size_t write_samples(sox_format_t *ft, const sox_sample_t *buf, size_t nsamp)
340 priv_t *ac = (priv_t *)ft->priv;
341 size_t len, written = 0;
342 size_t samp_left;
343 OSStatus status;
344 float *p;
345 SOX_SAMPLE_LOCALS;
347 pthread_mutex_lock(&ac->mutex);
349 /* Wait to start until mutex is locked to help prevent callback
350 * getting zero samples.
352 if (!ac->device_started)
354 status = AudioDeviceStart(ac->adid, PlaybackIOProc);
355 if (status)
357 pthread_mutex_unlock(&ac->mutex);
358 return SOX_EOF;
360 ac->device_started = 1;
363 /* globals.bufsize is in samples
364 * buf_offset is in bytes
365 * buf_size is in bytes
367 do {
368 while (ac->buf_offset >= ac->buf_size)
369 pthread_cond_wait(&ac->cond, &ac->mutex);
371 len = nsamp - written;
372 if (len > (ac->buf_size - ac->buf_offset) / sizeof(float))
373 len = (ac->buf_size - ac->buf_offset) / sizeof(float);
374 samp_left = len;
376 p = ((unsigned char *)ac->buffer) + ac->buf_offset;
378 while (samp_left--)
379 *p++ = SOX_SAMPLE_TO_FLOAT_32BIT(*buf++, ft->clips);
381 ac->buf_offset += len * sizeof(float);
382 written += len;
383 } while (written < nsamp);
385 pthread_mutex_unlock(&ac->mutex);
387 return written;
391 static int stopwrite(sox_format_t * ft)
393 priv_t *ac = (priv_t *)ft->priv;
395 if (!ac->device_started)
397 pthread_mutex_lock(&ac->mutex);
399 while (ac->buf_offset)
400 pthread_cond_wait(&ac->cond, &ac->mutex);
402 pthread_mutex_unlock(&ac->mutex);
404 AudioDeviceStop(ac->adid, PlaybackIOProc);
407 AudioDeviceRemoveIOProc(ac->adid, PlaybackIOProc);
408 pthread_cond_destroy(&ac->cond);
409 pthread_mutex_destroy(&ac->mutex);
411 return SOX_SUCCESS;
414 LSX_FORMAT_HANDLER(coreaudio)
416 static char const *const names[] = { "coreaudio", NULL };
417 static unsigned const write_encodings[] = {
418 SOX_ENCODING_FLOAT, 32, 0,
420 static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
421 "Mac AudioCore device driver",
422 names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
423 startread, read_samples, stopread,
424 startwrite, write_samples, stopwrite,
425 NULL, write_encodings, NULL, sizeof(priv_t)
427 return &handler;