2 * This file is part of DisOrder.
3 * Copyright (C) 2009 Richard Kettlewell
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 /** @file lib/uaudio-coreaudio.c
19 * @brief Support for Core Audio backend */
22 #if HAVE_COREAUDIO_AUDIOHARDWARE_H
24 #include "coreaudio.h"
29 #include "configuration.h"
31 /** @brief Callback to request sample data */
32 static uaudio_callback
*coreaudio_callback
;
34 /** @brief Userdata for @ref coreaudio_callback */
35 static void *coreaudio_userdata
;
37 /** @brief Core Audio device ID */
38 static AudioDeviceID coreaudio_adid
;
40 /** @brief Core Audio option names */
41 static const char *const coreaudio_options
[] = {
46 /** @brief Callback from Core Audio
48 * Core Audio demands floating point samples but we provide integers.
49 * So there is a conversion step in here.
51 static OSStatus coreaudio_adioproc
52 (AudioDeviceID
attribute((unused
)) inDevice
,
53 const AudioTimeStamp
attribute((unused
)) *inNow
,
54 const AudioBufferList
attribute((unused
)) *inInputData
,
55 const AudioTimeStamp
attribute((unused
)) *inInputTime
,
56 AudioBufferList
*outOutputData
,
57 const AudioTimeStamp
attribute((unused
)) *inOutputTime
,
58 void attribute((unused
)) *inClientData
) {
59 /* Number of buffers we must fill */
60 unsigned nbuffers
= outOutputData
->mNumberBuffers
;
61 /* Pointer to buffer to fill */
62 AudioBuffer
*ab
= outOutputData
->mBuffers
;
65 /* Where to store converted sample data */
66 float *samples
= ab
->mData
;
67 /* Number of samples left to fill */
68 size_t nsamples
= ab
->mDataByteSize
/ sizeof (float);
71 /* Integer-format input buffer */
72 unsigned char input
[1024];
73 const size_t maxsamples
= sizeof input
/ uaudio_sample_size
;
74 /* How many samples we'll ask for */
75 const size_t ask
= nsamples
> maxsamples ? maxsamples
: nsamples
;
79 got
= coreaudio_callback(input
, ask
, coreaudio_userdata
);
80 /* Convert the samples and store in the output buffer */
83 if(uaudio_bits
== 16) {
84 const int16_t *ptr
= (int16_t *)input
;
87 *samples
++ = *ptr
++ * (0.5 / 32767);
90 const int8_t *ptr
= (int8_t *)input
;
93 *samples
++ = *ptr
++ * (0.5 / 127);
97 if(uaudio_bits
== 16) {
98 const uint16_t *ptr
= (uint16_t *)input
;
101 *samples
++ = ((int)*ptr
++ - 32768) * (0.5 / 32767);
104 const uint8_t *ptr
= (uint8_t *)input
;
107 *samples
++ = ((int)*ptr
++ - 128) * (0.5 / 127);
112 /* Move on to the next buffer */
119 static void coreaudio_start(uaudio_callback
*callback
,
123 AudioStreamBasicDescription asbd
;
126 if(uaudio_bits
!= 8 && uaudio_bits
!= 16)
127 disorder_fatal(0, "asked for %d bits/channel but only support 8 and 16",
129 coreaudio_callback
= callback
;
130 coreaudio_userdata
= userdata
;
131 device
= uaudio_get("device", "default");
132 coreaudio_adid
= coreaudio_getdevice(device
);
133 /* Get the device properties */
134 propertySize
= sizeof asbd
;
135 status
= AudioDeviceGetProperty(coreaudio_adid
, 0, false,
136 kAudioDevicePropertyStreamFormat
,
137 &propertySize
, &asbd
);
139 coreaudio_fatal(status
, "AudioHardwareGetProperty");
140 D(("mSampleRate %f", asbd
.mSampleRate
));
141 D(("mFormatID %08"PRIx32
, (uint32_t)asbd
.mFormatID
));
142 D(("mFormatFlags %08"PRIx32
, (uint32_t)asbd
.mFormatFlags
));
143 D(("mBytesPerPacket %08"PRIx32
, (uint32_t)asbd
.mBytesPerPacket
));
144 D(("mFramesPerPacket %08"PRIx32
, (uint32_t)asbd
.mFramesPerPacket
));
145 D(("mBytesPerFrame %08"PRIx32
, (uint32_t)asbd
.mBytesPerFrame
));
146 D(("mChannelsPerFrame %08"PRIx32
, (uint32_t)asbd
.mChannelsPerFrame
));
147 D(("mBitsPerChannel %08"PRIx32
, (uint32_t)asbd
.mBitsPerChannel
));
148 D(("mReserved %08"PRIx32
, (uint32_t)asbd
.mReserved
));
149 /* Check that everything adds up */
150 if(asbd
.mFormatID
!= kAudioFormatLinearPCM
)
151 disorder_fatal(0, "audio device does not support kAudioFormatLinearPCM");
152 if(asbd
.mSampleRate
!= uaudio_rate
153 || asbd
.mChannelsPerFrame
!= (unsigned)uaudio_channels
) {
154 disorder_fatal(0, "want %dHz %d channels "
155 "but got %gHz %"PRIu32
" channels",
158 (double)asbd
.mSampleRate
,
159 (uint32_t)asbd
.mChannelsPerFrame
);
161 /* Add a collector callback */
162 status
= AudioDeviceAddIOProc(coreaudio_adid
, coreaudio_adioproc
, 0);
164 coreaudio_fatal(status
, "AudioDeviceAddIOProc");
167 static void coreaudio_stop(void) {
170 static void coreaudio_activate(void) {
173 status
= AudioDeviceStart(coreaudio_adid
, coreaudio_adioproc
);
175 coreaudio_fatal(status
, "AudioDeviceStart");
178 static void coreaudio_deactivate(void) {
181 status
= AudioDeviceStop(coreaudio_adid
, coreaudio_adioproc
);
183 coreaudio_fatal(status
, "AudioDeviceStop");
186 static void coreaudio_configure(void) {
187 uaudio_set("device", config
->device
);
190 const struct uaudio uaudio_coreaudio
= {
192 .options
= coreaudio_options
,
193 .start
= coreaudio_start
,
194 .stop
= coreaudio_stop
,
195 .activate
= coreaudio_activate
,
196 .deactivate
= coreaudio_deactivate
,
197 .configure
= coreaudio_configure
,