1 /* AudioCore sound handler
3 * Copyright 2008 Chris Bagwell And Sundry Contributors
8 #include <CoreAudio/CoreAudio.h>
13 pthread_mutex_t mutex
;
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
,
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 */
51 memcpy(buf
, ac
->buffer
, len
);
53 /* Fill partial output buffers with silence */
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
,
73 sox_format_t
*ft
= (sox_format_t
*)inClientData
;
74 priv_t
*ac
= (priv_t
*)ft
->priv
;
75 size_t len
, output_len
;
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
96 return kAudioHardwareNoError
;
98 len
= ac
->buf_size
- ac
->buf_offset
;
100 /* Make sure to read 2 (stereo) floats at a time */
104 if (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
;
124 UInt32 property_size
;
125 struct AudioStreamBasicDescription stream_desc
;
129 if (strncmp(ft
->filename
, "default", (size_t)7) == 0)
131 property_size
= sizeof(ac
->adid
);
133 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
, &property_size
, &ac
->adid
);
135 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
, &property_size
, &ac
->adid
);
140 status
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
, &property_size
, &is_writable
);
144 int device_count
= property_size
/sizeof(AudioDeviceID
);
145 AudioDeviceID
*devices
;
147 devices
= malloc(property_size
);
148 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDevices
, &property_size
, devices
);
153 for (i
= 0; i
< property_size
/sizeof(AudioDeviceID
); i
++)
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
];
174 if (status
|| ac
->adid
== kAudioDeviceUnknown
)
176 lsx_fail_errno(ft
, SOX_EPERM
, "can not open audio device");
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
);
187 lsx_fail_errno(ft
, SOX_EPERM
, "can not get audio device properties");
191 if (!(stream_desc
.mFormatFlags
& kLinearPCMFormatFlagIsFloat
))
193 lsx_fail_errno(ft
, SOX_EPERM
, "audio device does not accept floats");
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.
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
);
219 lsx_fail_errno(ft
, SOX_EPERM
, "can not set audio device properties");
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
);
231 lsx_fail_errno(ft
, SOX_EPERM
, "can not get audio device properties");
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);
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
);
263 lsx_fail_errno(ft
, SOX_EPERM
, "failed initializing mutex");
267 rc
= pthread_cond_init(&ac
->cond
, NULL
);
270 lsx_fail_errno(ft
, SOX_EPERM
, "failed initializing condition");
274 ac
->device_started
= 0;
276 /* Registers callback with the device without activating it. */
278 status
= AudioDeviceAddIOProc(ac
->adid
, RecIOProc
, (void *)ft
);
280 status
= AudioDeviceAddIOProc(ac
->adid
, PlaybackIOProc
, (void *)ft
);
285 static int startread(sox_format_t
*ft
)
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
;
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
);
316 pthread_mutex_unlock(&ac
->mutex
);
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
);
333 static int startwrite(sox_format_t
* ft
)
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;
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
);
357 pthread_mutex_unlock(&ac
->mutex
);
360 ac
->device_started
= 1;
363 /* globals.bufsize is in samples
364 * buf_offset is in bytes
365 * buf_size is in bytes
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);
376 p
= ((unsigned char *)ac
->buffer
) + ac
->buf_offset
;
379 *p
++ = SOX_SAMPLE_TO_FLOAT_32BIT(*buf
++, ft
->clips
);
381 ac
->buf_offset
+= len
* sizeof(float);
383 } while (written
< nsamp
);
385 pthread_mutex_unlock(&ac
->mutex
);
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
);
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
)