chiark / gitweb /
Uniform audio command back end now rate limited.
[disorder] / lib / uaudio-coreaudio.c
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"
29
30 /** @brief Callback to request sample data */
31 static uaudio_callback *coreaudio_callback;
32
33 /** @brief Userdata for @ref coreaudio_callback */
34 static void *coreaudio_userdata;
35
36 /** @brief Core Audio device ID */
37 static AudioDeviceID coreaudio_adid;
38
39 /** @brief Core Audio option names */
40 static const char *const coreaudio_options[] = {
41   "device",
42   NULL
43 };
44
45 /** @brief Callback from Core Audio
46  *
47  * Core Audio demands floating point samples but we provide integers.
48  * So there is a conversion step in here.
49  */
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;
62   
63   while(nbuffers > 0) {
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);
68
69     while(nsamples > 0) {
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;
75       /* How many we get */
76       int got;
77
78       got = coreaudio_callback(input, ask, coreaudio_userdata);
79       /* Convert the samples and store in the output buffer */
80       nsamples -= got;
81       if(uaudio_signed) {
82         if(uaudio_bits == 16) {
83           const int16_t *ptr = (int16_t *)input;
84           while(got > 0) {
85             --got;
86             *samples++ = *ptr++ * (0.5 / 32767);
87           }
88         } else {
89           const int8_t *ptr = (int8_t *)input;
90           while(got > 0) {
91             --got;
92             *samples++ = *ptr++ * (0.5 / 127);
93           }
94         }
95       } else {
96         if(uaudio_bits == 16) {
97           const uint16_t *ptr = (uint16_t *)input;
98           while(got > 0) {
99             --got;
100             *samples++ = ((int)*ptr++ - 32768) * (0.5 / 32767);
101           }
102         } else {
103           const uint8_t *ptr = (uint8_t *)input;
104           while(got > 0) {
105             --got;
106             *samples++ = ((int)*ptr++ - 128) * (0.5 / 127);
107           }
108         }
109       }
110     }
111     /* Move on to the next buffer */
112     ++ab;
113     --nbuffers;
114   }
115   return 0;
116 }
117
118 static void coreaudio_start(uaudio_callback *callback,
119                             void *userdata) {
120   OSStatus status;
121   UInt32 propertySize;
122   AudioStreamBasicDescription asbd;
123   const char *device;
124
125   if(uaudio_bits != 8 && uaudio_bits != 16)
126     disorder_fatal(0, "asked for %d bits/channel but only support 8 and 16",
127                    uaudio_bits);
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);
137   if(status)
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",
155                    uaudio_rate,
156                    uaudio_channels,
157                    (double)asbd.mSampleRate,
158                    (uint32_t)asbd.mChannelsPerFrame);
159   }
160   /* Add a collector callback */
161   status = AudioDeviceAddIOProc(coreaudio_adid, coreaudio_adioproc, 0);
162   if(status)
163     coreaudio_fatal(status, "AudioDeviceAddIOProc");
164 }
165
166 static void coreaudio_stop(void) {
167 }
168
169 static void coreaudio_activate(void) {
170   OSStatus status;
171
172   status = AudioDeviceStart(coreaudio_adid, coreaudio_adioproc);
173   if(status)
174     coreaudio_fatal(status, "AudioDeviceStart");
175 }
176
177 static void coreaudio_deactivate(void) {
178   OSStatus status;
179
180   status = AudioDeviceStop(coreaudio_adid, coreaudio_adioproc);
181   if(status)
182     coreaudio_fatal(status, "AudioDeviceStop");
183 }
184
185 const struct uaudio uaudio_coreaudio = {
186   .name = "coreaudio",
187   .options = coreaudio_options,
188   .start = coreaudio_start,
189   .stop = coreaudio_stop,
190   .activate = coreaudio_activate,
191   .deactivate = coreaudio_deactivate
192 };
193
194 #endif
195
196 /*
197 Local Variables:
198 c-basic-offset:2
199 comment-column:40
200 fill-column:79
201 indent-tabs-mode:nil
202 End:
203 */