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"
30 /** @brief Callback to request sample data */
31 static uaudio_callback *coreaudio_callback;
33 /** @brief Userdata for @ref coreaudio_callback */
34 static void *coreaudio_userdata;
36 /** @brief Core Audio device ID */
37 static AudioDeviceID coreaudio_adid;
39 /** @brief Core Audio option names */
40 static const char *const coreaudio_options[] = {
45 /** @brief Callback from Core Audio
47 * Core Audio demands floating point samples but we provide integers.
48 * So there is a conversion step in here.
50 static OSStatus coreaudio_adioproc
51 (AudioDeviceID attribute((unused)) inDevice,
52 const AudioTimeStamp attribute((unused)) *inNow,
53 const AudioBufferList attribute((unused)) *inInputData,
54 const AudioTimeStamp attribute((unused)) *inInputTime,
55 AudioBufferList *outOutputData,
56 const AudioTimeStamp attribute((unused)) *inOutputTime,
57 void attribute((unused)) *inClientData) {
58 /* Number of buffers we must fill */
59 unsigned nbuffers = outOutputData->mNumberBuffers;
60 /* Pointer to buffer to fill */
61 AudioBuffer *ab = outOutputData->mBuffers;
64 /* Where to store converted sample data */
65 float *samples = ab->mData;
66 /* Number of samples left to fill */
67 size_t nsamples = ab->mDataByteSize / sizeof (float);
70 /* Integer-format input buffer */
71 unsigned char input[1024];
72 const size_t maxsamples = sizeof input / uaudio_sample_size;
73 /* How many samples we'll ask for */
74 const size_t ask = nsamples > maxsamples ? maxsamples : nsamples;
78 got = coreaudio_callback(input, ask, coreaudio_userdata);
79 /* Convert the samples and store in the output buffer */
82 if(uaudio_bits == 16) {
83 const int16_t *ptr = (int16_t *)input;
86 *samples++ = *ptr++ * (0.5 / 32767);
89 const int8_t *ptr = (int8_t *)input;
92 *samples++ = *ptr++ * (0.5 / 127);
96 if(uaudio_bits == 16) {
97 const uint16_t *ptr = (uint16_t *)input;
100 *samples++ = ((int)*ptr++ - 32768) * (0.5 / 32767);
103 const uint8_t *ptr = (uint8_t *)input;
106 *samples++ = ((int)*ptr++ - 128) * (0.5 / 127);
111 /* Move on to the next buffer */
118 static void coreaudio_start(uaudio_callback *callback,
122 AudioStreamBasicDescription asbd;
125 if(uaudio_bits != 8 && uaudio_bits != 16)
126 disorder_fatal(0, "asked for %d bits/channel but only support 8 and 16",
128 coreaudio_callback = callback;
129 coreaudio_userdata = userdata;
130 device = uaudio_get("device");
131 coreaudio_adid = coreaudio_getdevice(device);
132 /* Get the device properties */
133 propertySize = sizeof asbd;
134 status = AudioDeviceGetProperty(coreaudio_adid, 0, false,
135 kAudioDevicePropertyStreamFormat,
136 &propertySize, &asbd);
138 coreaudio_fatal(status, "AudioHardwareGetProperty");
139 D(("mSampleRate %f", asbd.mSampleRate));
140 D(("mFormatID %08lx", asbd.mFormatID));
141 D(("mFormatFlags %08lx", asbd.mFormatFlags));
142 D(("mBytesPerPacket %08lx", asbd.mBytesPerPacket));
143 D(("mFramesPerPacket %08lx", asbd.mFramesPerPacket));
144 D(("mBytesPerFrame %08lx", asbd.mBytesPerFrame));
145 D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame));
146 D(("mBitsPerChannel %08lx", asbd.mBitsPerChannel));
147 D(("mReserved %08lx", asbd.mReserved));
148 /* Check that everything adds up */
149 if(asbd.mFormatID != kAudioFormatLinearPCM)
150 disorder_fatal(0, "audio device does not support kAudioFormatLinearPCM");
151 if(asbd.mSampleRate != uaudio_rate
152 || asbd.mChannelsPerFrame != (unsigned)uaudio_channels) {
153 disorder_fatal(0, "want %dHz %d channels "
154 "but got %gHz %"PRIu32" channels",
157 (double)asbd.mSampleRate,
158 (uint32_t)asbd.mChannelsPerFrame);
160 /* Add a collector callback */
161 status = AudioDeviceAddIOProc(coreaudio_adid, coreaudio_adioproc, 0);
163 coreaudio_fatal(status, "AudioDeviceAddIOProc");
166 static void coreaudio_stop(void) {
169 static void coreaudio_activate(void) {
172 status = AudioDeviceStart(coreaudio_adid, coreaudio_adioproc);
174 coreaudio_fatal(status, "AudioDeviceStart");
177 static void coreaudio_deactivate(void) {
180 status = AudioDeviceStop(coreaudio_adid, coreaudio_adioproc);
182 coreaudio_fatal(status, "AudioDeviceStop");
185 const struct uaudio uaudio_coreaudio = {
187 .options = coreaudio_options,
188 .start = coreaudio_start,
189 .stop = coreaudio_stop,
190 .activate = coreaudio_activate,
191 .deactivate = coreaudio_deactivate