chiark / gitweb /
Rewrite uaudio-schedule.c. It's now much simpler and should actually
[disorder] / lib / uaudio-coreaudio.c
CommitLineData
7a2c7068
RK
1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2009 Richard Kettlewell
4 *
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.
9 *
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.
14 *
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/>.
17 */
18/** @file lib/uaudio-coreaudio.c
19 * @brief Support for Core Audio backend */
20#include "common.h"
21
22#if HAVE_COREAUDIO_AUDIOHARDWARE_H
23
24#include "coreaudio.h"
25#include "uaudio.h"
26#include "mem.h"
27#include "log.h"
28#include "syscalls.h"
ba70caca 29#include "configuration.h"
7a2c7068
RK
30
31/** @brief Callback to request sample data */
32static uaudio_callback *coreaudio_callback;
33
34/** @brief Userdata for @ref coreaudio_callback */
35static void *coreaudio_userdata;
36
37/** @brief Core Audio device ID */
38static AudioDeviceID coreaudio_adid;
39
40/** @brief Core Audio option names */
41static const char *const coreaudio_options[] = {
42 "device",
43 NULL
44};
45
46/** @brief Callback from Core Audio
47 *
48 * Core Audio demands floating point samples but we provide integers.
49 * So there is a conversion step in here.
50 */
51static 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;
63
64 while(nbuffers > 0) {
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);
69
70 while(nsamples > 0) {
71 /* Integer-format input buffer */
4fd38868 72 unsigned char input[1024];
aba9abfa 73 const size_t maxsamples = sizeof input / uaudio_sample_size;
7a2c7068 74 /* How many samples we'll ask for */
aba9abfa 75 const size_t ask = nsamples > maxsamples ? maxsamples : nsamples;
7a2c7068
RK
76 /* How many we get */
77 int got;
78
79 got = coreaudio_callback(input, ask, coreaudio_userdata);
80 /* Convert the samples and store in the output buffer */
81 nsamples -= got;
4fd38868
RK
82 if(uaudio_signed) {
83 if(uaudio_bits == 16) {
aba9abfa 84 const int16_t *ptr = (int16_t *)input;
4fd38868
RK
85 while(got > 0) {
86 --got;
87 *samples++ = *ptr++ * (0.5 / 32767);
88 }
89 } else {
aba9abfa 90 const int8_t *ptr = (int8_t *)input;
4fd38868
RK
91 while(got > 0) {
92 --got;
93 *samples++ = *ptr++ * (0.5 / 127);
94 }
95 }
96 } else {
97 if(uaudio_bits == 16) {
aba9abfa 98 const uint16_t *ptr = (uint16_t *)input;
4fd38868
RK
99 while(got > 0) {
100 --got;
101 *samples++ = ((int)*ptr++ - 32768) * (0.5 / 32767);
102 }
103 } else {
aba9abfa 104 const uint8_t *ptr = (uint8_t *)input;
4fd38868
RK
105 while(got > 0) {
106 --got;
107 *samples++ = ((int)*ptr++ - 128) * (0.5 / 127);
108 }
109 }
7a2c7068
RK
110 }
111 }
112 /* Move on to the next buffer */
113 ++ab;
114 --nbuffers;
115 }
116 return 0;
117}
118
119static void coreaudio_start(uaudio_callback *callback,
120 void *userdata) {
121 OSStatus status;
122 UInt32 propertySize;
123 AudioStreamBasicDescription asbd;
124 const char *device;
125
4fd38868 126 if(uaudio_bits != 8 && uaudio_bits != 16)
aba9abfa 127 disorder_fatal(0, "asked for %d bits/channel but only support 8 and 16",
4fd38868 128 uaudio_bits);
7a2c7068
RK
129 coreaudio_callback = callback;
130 coreaudio_userdata = userdata;
b50cfb8a 131 device = uaudio_get("device", "default");
7a2c7068 132 coreaudio_adid = coreaudio_getdevice(device);
4fd38868 133 /* Get the device properties */
7a2c7068
RK
134 propertySize = sizeof asbd;
135 status = AudioDeviceGetProperty(coreaudio_adid, 0, false,
136 kAudioDevicePropertyStreamFormat,
137 &propertySize, &asbd);
138 if(status)
139 coreaudio_fatal(status, "AudioHardwareGetProperty");
140 D(("mSampleRate %f", asbd.mSampleRate));
141 D(("mFormatID %08lx", asbd.mFormatID));
142 D(("mFormatFlags %08lx", asbd.mFormatFlags));
143 D(("mBytesPerPacket %08lx", asbd.mBytesPerPacket));
144 D(("mFramesPerPacket %08lx", asbd.mFramesPerPacket));
145 D(("mBytesPerFrame %08lx", asbd.mBytesPerFrame));
146 D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame));
147 D(("mBitsPerChannel %08lx", asbd.mBitsPerChannel));
148 D(("mReserved %08lx", asbd.mReserved));
4fd38868 149 /* Check that everything adds up */
7a2c7068
RK
150 if(asbd.mFormatID != kAudioFormatLinearPCM)
151 disorder_fatal(0, "audio device does not support kAudioFormatLinearPCM");
4fd38868 152 if(asbd.mSampleRate != uaudio_rate
aba9abfa 153 || asbd.mChannelsPerFrame != (unsigned)uaudio_channels) {
4fd38868 154 disorder_fatal(0, "want %dHz %d channels "
aba9abfa 155 "but got %gHz %"PRIu32" channels",
4fd38868
RK
156 uaudio_rate,
157 uaudio_channels,
aba9abfa
RK
158 (double)asbd.mSampleRate,
159 (uint32_t)asbd.mChannelsPerFrame);
4fd38868
RK
160 }
161 /* Add a collector callback */
7a2c7068
RK
162 status = AudioDeviceAddIOProc(coreaudio_adid, coreaudio_adioproc, 0);
163 if(status)
164 coreaudio_fatal(status, "AudioDeviceAddIOProc");
165}
166
167static void coreaudio_stop(void) {
168}
169
170static void coreaudio_activate(void) {
171 OSStatus status;
172
173 status = AudioDeviceStart(coreaudio_adid, coreaudio_adioproc);
174 if(status)
175 coreaudio_fatal(status, "AudioDeviceStart");
176}
177
178static void coreaudio_deactivate(void) {
179 OSStatus status;
180
181 status = AudioDeviceStop(coreaudio_adid, coreaudio_adioproc);
182 if(status)
183 coreaudio_fatal(status, "AudioDeviceStop");
184}
185
ba70caca
RK
186static void coreaudio_configure(void) {
187 uaudio_set("device", config->device);
188}
189
7a2c7068
RK
190const struct uaudio uaudio_coreaudio = {
191 .name = "coreaudio",
192 .options = coreaudio_options,
193 .start = coreaudio_start,
194 .stop = coreaudio_stop,
195 .activate = coreaudio_activate,
ba70caca
RK
196 .deactivate = coreaudio_deactivate,
197 .configure = coreaudio_configure,
7a2c7068
RK
198};
199
200#endif
201
202/*
203Local Variables:
204c-basic-offset:2
205comment-column:40
206fill-column:79
207indent-tabs-mode:nil
208End:
209*/