chiark / gitweb /
129a66e793a4fcf2d9b5b35be3c28e2633ba4f84
[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       int16_t input[1024], *ptr = input;
72       /* How many samples we'll ask for */
73       const int ask = nsamples > 1024 ? 1024 : (int)nsamples;
74       /* How many we get */
75       int got;
76
77       got = coreaudio_callback(input, ask, coreaudio_userdata);
78       /* Convert the samples and store in the output buffer */
79       nsamples -= got;
80       while(got > 0) {
81         --got;
82         *samples++ = *ptr++ * (0.5 / 32767);
83       }
84     }
85     /* Move on to the next buffer */
86     ++ab;
87     --nbuffers;
88   }
89   return 0;
90 }
91
92 static void coreaudio_start(uaudio_callback *callback,
93                             void *userdata) {
94   OSStatus status;
95   UInt32 propertySize;
96   AudioStreamBasicDescription asbd;
97   const char *device;
98
99   coreaudio_callback = callback;
100   coreaudio_userdata = userdata;
101   device = uaudio_get("device");
102   coreaudio_adid = coreaudio_getdevice(device);
103   propertySize = sizeof asbd;
104   status = AudioDeviceGetProperty(coreaudio_adid, 0, false,
105                                   kAudioDevicePropertyStreamFormat,
106                                   &propertySize, &asbd);
107   if(status)
108     coreaudio_fatal(status, "AudioHardwareGetProperty");
109   D(("mSampleRate       %f", asbd.mSampleRate));
110   D(("mFormatID         %08lx", asbd.mFormatID));
111   D(("mFormatFlags      %08lx", asbd.mFormatFlags));
112   D(("mBytesPerPacket   %08lx", asbd.mBytesPerPacket));
113   D(("mFramesPerPacket  %08lx", asbd.mFramesPerPacket));
114   D(("mBytesPerFrame    %08lx", asbd.mBytesPerFrame));
115   D(("mChannelsPerFrame %08lx", asbd.mChannelsPerFrame));
116   D(("mBitsPerChannel   %08lx", asbd.mBitsPerChannel));
117   D(("mReserved         %08lx", asbd.mReserved));
118   if(asbd.mFormatID != kAudioFormatLinearPCM)
119     disorder_fatal(0, "audio device does not support kAudioFormatLinearPCM");
120   status = AudioDeviceAddIOProc(coreaudio_adid, coreaudio_adioproc, 0);
121   if(status)
122     coreaudio_fatal(status, "AudioDeviceAddIOProc");
123 }
124
125 static void coreaudio_stop(void) {
126 }
127
128 static void coreaudio_activate(void) {
129   OSStatus status;
130
131   status = AudioDeviceStart(coreaudio_adid, coreaudio_adioproc);
132   if(status)
133     coreaudio_fatal(status, "AudioDeviceStart");
134 }
135
136 static void coreaudio_deactivate(void) {
137   OSStatus status;
138
139   status = AudioDeviceStop(coreaudio_adid, coreaudio_adioproc);
140   if(status)
141     coreaudio_fatal(status, "AudioDeviceStop");
142 }
143
144 const struct uaudio uaudio_coreaudio = {
145   .name = "coreaudio",
146   .options = coreaudio_options,
147   .start = coreaudio_start,
148   .stop = coreaudio_stop,
149   .activate = coreaudio_activate,
150   .deactivate = coreaudio_deactivate
151 };
152
153 #endif
154
155 /*
156 Local Variables:
157 c-basic-offset:2
158 comment-column:40
159 fill-column:79
160 indent-tabs-mode:nil
161 End:
162 */