chiark / gitweb /
Fix mis-merged trackdb_open().
[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 #include "configuration.h"
30
31 /** @brief Callback to request sample data */
32 static uaudio_callback *coreaudio_callback;
33
34 /** @brief Userdata for @ref coreaudio_callback */
35 static void *coreaudio_userdata;
36
37 /** @brief Core Audio device ID */
38 static AudioDeviceID coreaudio_adid;
39
40 /** @brief Core Audio option names */
41 static 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  */
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;
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 */
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;
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;
82       if(uaudio_signed) {
83         if(uaudio_bits == 16) {
84           const int16_t *ptr = (int16_t *)input;
85           while(got > 0) {
86             --got;
87             *samples++ = *ptr++ * (0.5 / 32767);
88           }
89         } else {
90           const int8_t *ptr = (int8_t *)input;
91           while(got > 0) {
92             --got;
93             *samples++ = *ptr++ * (0.5 / 127);
94           }
95         }
96       } else {
97         if(uaudio_bits == 16) {
98           const uint16_t *ptr = (uint16_t *)input;
99           while(got > 0) {
100             --got;
101             *samples++ = ((int)*ptr++ - 32768) * (0.5 / 32767);
102           }
103         } else {
104           const uint8_t *ptr = (uint8_t *)input;
105           while(got > 0) {
106             --got;
107             *samples++ = ((int)*ptr++ - 128) * (0.5 / 127);
108           }
109         }
110       }
111     }
112     /* Move on to the next buffer */
113     ++ab;
114     --nbuffers;
115   }
116   return 0;
117 }
118
119 static void coreaudio_start(uaudio_callback *callback,
120                             void *userdata) {
121   OSStatus status;
122   UInt32 propertySize;
123   AudioStreamBasicDescription asbd;
124   const char *device;
125
126   if(uaudio_bits != 8 && uaudio_bits != 16)
127     disorder_fatal(0, "asked for %d bits/channel but only support 8 and 16",
128                    uaudio_bits);
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);
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));
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",
156                    uaudio_rate,
157                    uaudio_channels,
158                    (double)asbd.mSampleRate,
159                    (uint32_t)asbd.mChannelsPerFrame);
160   }
161   /* Add a collector callback */
162   status = AudioDeviceAddIOProc(coreaudio_adid, coreaudio_adioproc, 0);
163   if(status)
164     coreaudio_fatal(status, "AudioDeviceAddIOProc");
165 }
166
167 static void coreaudio_stop(void) {
168 }
169
170 static 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
178 static 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
186 static void coreaudio_configure(void) {
187   uaudio_set("device", config->device);
188 }
189
190 const struct uaudio uaudio_coreaudio = {
191   .name = "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,
198 };
199
200 #endif
201
202 /*
203 Local Variables:
204 c-basic-offset:2
205 comment-column:40
206 fill-column:79
207 indent-tabs-mode:nil
208 End:
209 */