1 /* AudioCore sound handler
3 * Copyright 2008 Chris Bagwell And Sundry Contributors
8 #include <CoreAudio/CoreAudio.h>
15 pthread_mutex_t mutex
;
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
,
33 priv_t
*ac
= (priv_t
*)((sox_format_t
*)inClientData
)->priv
;
35 size_t copylen
, avail
;
37 pthread_mutex_lock(&ac
->mutex
);
39 for(buf
= outOutputData
->mBuffers
;
40 buf
!= outOutputData
->mBuffers
+ outOutputData
->mNumberBuffers
;
43 copylen
= buf
->mDataByteSize
/ sizeof(float);
44 if(copylen
> ac
->bufrdavail
)
45 copylen
= ac
->bufrdavail
;
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));
54 memcpy(buf
->mData
, ac
->buf
+ ac
->bufrd
, copylen
* sizeof(float));
57 buf
->mDataByteSize
= copylen
* sizeof(float);
59 if(ac
->bufrd
>= ac
->bufsize
)
60 ac
->bufrd
-= ac
->bufsize
;
61 ac
->bufrdavail
-= copylen
;
64 pthread_cond_signal(&ac
->cond
);
65 pthread_mutex_unlock(&ac
->mutex
);
67 return kAudioHardwareNoError
;
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
,
78 priv_t
*ac
= (priv_t
*)((sox_format_t
*)inClientData
)->priv
;
79 AudioBuffer
const *buf
;
80 size_t nfree
, copylen
, avail
;
82 pthread_mutex_lock(&ac
->mutex
);
84 for(buf
= inInputData
->mBuffers
;
85 buf
!= inInputData
->mBuffers
+ inInputData
->mNumberBuffers
;
88 if(buf
->mData
== NULL
)
91 copylen
= buf
->mDataByteSize
/ sizeof(float);
92 nfree
= ac
->bufsize
- ac
->bufrdavail
- 1;
94 lsx_warn("coreaudio: unhandled buffer overrun. Data discarded.");
99 avail
= ac
->bufsize
- ac
->bufwr
;
101 memcpy(ac
->buf
+ ac
->bufwr
, buf
->mData
, avail
* sizeof(float));
102 memcpy(ac
->buf
, (float*)buf
->mData
+ avail
, (copylen
- avail
) * sizeof(float));
104 memcpy(ac
->buf
+ ac
->bufwr
, buf
->mData
, copylen
* sizeof(float));
107 ac
->bufwr
+= copylen
;
108 if(ac
->bufwr
>= ac
->bufsize
)
109 ac
->bufwr
-= ac
->bufsize
;
110 ac
->bufrdavail
+= copylen
;
113 pthread_cond_signal(&ac
->cond
);
114 pthread_mutex_unlock(&ac
->mutex
);
116 return kAudioHardwareNoError
;
119 static int setup(sox_format_t
*ft
, int is_input
)
121 priv_t
*ac
= (priv_t
*)ft
->priv
;
123 UInt32 property_size
;
124 struct AudioStreamBasicDescription stream_desc
;
128 if (strncmp(ft
->filename
, "default", (size_t)7) == 0)
130 property_size
= sizeof(ac
->adid
);
132 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice
, &property_size
, &ac
->adid
);
134 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice
, &property_size
, &ac
->adid
);
139 status
= AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices
, &property_size
, &is_writable
);
143 int device_count
= property_size
/sizeof(AudioDeviceID
);
144 AudioDeviceID
*devices
;
146 devices
= malloc(property_size
);
147 status
= AudioHardwareGetProperty(kAudioHardwarePropertyDevices
, &property_size
, devices
);
152 for (i
= 0; i
< device_count
; i
++)
155 status
= AudioDeviceGetProperty(devices
[i
],0,false,kAudioDevicePropertyDeviceName
,&property_size
,&name
);
157 lsx_report("Found Audio Device \"%s\"\n",name
);
159 /* String returned from OS is truncated so only compare
160 * as much as returned.
162 if (strncmp(name
,ft
->filename
,strlen(name
)) == 0)
164 ac
->adid
= devices
[i
];
173 if (status
|| ac
->adid
== kAudioDeviceUnknown
)
175 lsx_fail_errno(ft
, SOX_EPERM
, "can not open audio device");
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
);
186 lsx_fail_errno(ft
, SOX_EPERM
, "can not get audio device properties");
190 if (!(stream_desc
.mFormatFlags
& kLinearPCMFormatFlagIsFloat
))
192 lsx_fail_errno(ft
, SOX_EPERM
, "audio device does not accept floats");
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;
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.
208 stream_desc
.mSampleRate
= ft
->signal
.rate
;
209 stream_desc
.mChannelsPerFrame
= ft
->signal
.channels
;
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
);
218 lsx_fail_errno(ft
, SOX_EPERM
, "can not set audio device properties");
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
);
230 lsx_fail_errno(ft
, SOX_EPERM
, "can not get audio device properties");
235 if (stream_desc
.mChannelsPerFrame
!= ft
->signal
.channels
)
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
;
242 if (stream_desc
.mSampleRate
!= ft
->signal
.rate
)
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
;
249 ac
->bufsize
= sox_globals
.bufsiz
/ sizeof(sox_sample_t
) * Buffactor
;
253 ac
->buf
= lsx_malloc(ac
->bufsize
* sizeof(float));
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
);
261 rc
= pthread_mutex_init(&ac
->mutex
, NULL
);
264 lsx_fail_errno(ft
, SOX_EPERM
, "failed initializing mutex");
268 rc
= pthread_cond_init(&ac
->cond
, NULL
);
271 lsx_fail_errno(ft
, SOX_EPERM
, "failed initializing condition");
275 ac
->device_started
= 0;
277 /* Registers callback with the device without activating it. */
279 status
= AudioDeviceAddIOProc(ac
->adid
, RecIOProc
, (void *)ft
);
281 status
= AudioDeviceAddIOProc(ac
->adid
, PlaybackIOProc
, (void *)ft
);
286 static int startread(sox_format_t
*ft
)
291 static size_t read_samples(sox_format_t
*ft
, sox_sample_t
*buf
, size_t nsamp
)
293 priv_t
*ac
= (priv_t
*)ft
->priv
;
297 if (!ac
->device_started
) {
298 AudioDeviceStart(ac
->adid
, RecIOProc
);
299 ac
->device_started
= 1;
302 pthread_mutex_lock(&ac
->mutex
);
304 /* Wait until input buffer has been filled by device driver */
305 while (ac
->bufrdavail
== 0)
306 pthread_cond_wait(&ac
->cond
, &ac
->mutex
);
309 while(len
< nsamp
&& ac
->bufrdavail
> 0){
310 buf
[len
] = SOX_FLOAT_32BIT_TO_SAMPLE(ac
->buf
[ac
->bufrd
], ft
->clips
);
313 if(ac
->bufrd
== ac
->bufsize
)
318 pthread_mutex_unlock(&ac
->mutex
);
323 static int stopread(sox_format_t
* ft
)
325 priv_t
*ac
= (priv_t
*)ft
->priv
;
327 AudioDeviceStop(ac
->adid
, RecIOProc
);
328 AudioDeviceRemoveIOProc(ac
->adid
, RecIOProc
);
329 pthread_cond_destroy(&ac
->cond
);
330 pthread_mutex_destroy(&ac
->mutex
);
336 static int startwrite(sox_format_t
* ft
)
341 static size_t write_samples(sox_format_t
*ft
, const sox_sample_t
*buf
, size_t nsamp
)
343 priv_t
*ac
= (priv_t
*)ft
->priv
;
348 pthread_mutex_lock(&ac
->mutex
);
350 /* Wait to start until mutex is locked to help prevent callback
351 * getting zero samples.
353 if(!ac
->device_started
){
354 if(AudioDeviceStart(ac
->adid
, PlaybackIOProc
)){
355 pthread_mutex_unlock(&ac
->mutex
);
358 ac
->device_started
= 1;
361 /* globals.bufsize is in samples
362 * buf_offset is in bytes
363 * buf_size is in bytes
365 for(i
= 0; i
< nsamp
; i
++){
366 while(ac
->bufrdavail
== ac
->bufsize
- 1)
367 pthread_cond_wait(&ac
->cond
, &ac
->mutex
);
369 ac
->buf
[ac
->bufwr
] = SOX_SAMPLE_TO_FLOAT_32BIT(buf
[i
], ft
->clips
);
371 if(ac
->bufwr
== ac
->bufsize
)
376 pthread_mutex_unlock(&ac
->mutex
);
381 static int stopwrite(sox_format_t
* ft
)
383 priv_t
*ac
= (priv_t
*)ft
->priv
;
385 if(ac
->device_started
){
386 pthread_mutex_lock(&ac
->mutex
);
388 while (ac
->bufrdavail
> 0)
389 pthread_cond_wait(&ac
->cond
, &ac
->mutex
);
391 pthread_mutex_unlock(&ac
->mutex
);
393 AudioDeviceStop(ac
->adid
, PlaybackIOProc
);
396 AudioDeviceRemoveIOProc(ac
->adid
, PlaybackIOProc
);
397 pthread_cond_destroy(&ac
->cond
);
398 pthread_mutex_destroy(&ac
->mutex
);
404 LSX_FORMAT_HANDLER(coreaudio
)
406 static char const *const names
[] = { "coreaudio", NULL
};
407 static unsigned const write_encodings
[] = {
408 SOX_ENCODING_FLOAT
, 32, 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
)