73aa137732a61c57cab41a2f9f1532008ae652c9
[paraslash.git] / osx_write.c
1 /*
2 * Copyright (C) 2006-2012 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file osx_write.c paraslash's output plugin for MacOs */
8
9 /*
10 * based on mosx-mpg123, by Guillaume Outters and Steven A. Kortze
11 * <skortze@sourceforge.net>
12 */
13
14 #include <regex.h>
15 #include <sys/types.h>
16
17 #include "para.h"
18 #include "fd.h"
19 #include "string.h"
20 #include "list.h"
21 #include "sched.h"
22 #include "ggo.h"
23 #include "buffer_tree.h"
24 #include "write.h"
25 #include "write_common.h"
26 #include "osx_write.cmdline.h"
27 #include "ipc.h"
28 #include "error.h"
29
30 #include <CoreServices/CoreServices.h>
31 #include <AudioUnit/AudioUnit.h>
32 #include <AudioToolbox/AudioToolbox.h>
33
34 /** Data specific to the osx writer. */
35 struct private_osx_write_data {
36 /** The main CoreAudio handle. */
37 AudioUnit audio_unit;
38 /** True if we wrote some audio data. */
39 bool playing;
40 /** Sample rate of the current audio stream. */
41 unsigned sample_rate;
42 /** Sample format of the current audio stream */
43 unsigned sample_format;
44 /** Number of channels of the current audio stream. */
45 unsigned channels;
46 /**
47 * Serializes access to buffer tree nodes between the writer and
48 * the callback which runs in a different thread.
49 */
50 int mutex;
51 /**
52 * The btr node of the callback.
53 *
54 * Although access to the btr node is serialized between the writer and
55 * the callback via the above mutex, this does not stop other buffer
56 * tree nodes, for example the decoder, to race against the osx
57 * callback.
58 *
59 * However, since all operations on buffer tree nodes are local in the
60 * sense that they only affect one level in the buffer tree (i.e.
61 * parent or child nodes, but not the grandparent or the
62 * grandchildren), we may work around this problem by using another
63 * buffer tree node for the callback.
64 *
65 * The writer grabs the mutex in its post_select method and pushes down
66 * the buffers to the callback node.
67 */
68 struct btr_node *callback_btrn;
69 };
70
71 /* This function writes the address and the number of bytes to one end of the socket.
72 * The post_select() function then fills the buffer and notifies the callback also
73 * through the socket.
74 */
75 static OSStatus osx_callback(void *cb_arg, __a_unused AudioUnitRenderActionFlags *af,
76 __a_unused const AudioTimeStamp *ts, __a_unused UInt32 bus_number,
77 __a_unused UInt32 num_frames, AudioBufferList *abl)
78 {
79 int i;
80 struct writer_node *wn = cb_arg;
81 struct private_osx_write_data *powd = wn->private_data;
82 size_t samples_have, samples_want = 0;
83
84 mutex_lock(powd->mutex);
85 /*
86 * We fill with zeros if no data was yet written and we do not have
87 * enough to fill all buffers.
88 */
89 if (!powd->playing) {
90 size_t want = 0, have =
91 btr_get_input_queue_size(powd->callback_btrn);
92 for (i = 0; i < abl->mNumberBuffers; i++)
93 want += abl->mBuffers[i].mDataByteSize;
94 if (have < want) {
95 PARA_DEBUG_LOG("deferring playback (have = %zu < %zu = want)\n",
96 have, want);
97 for (i = 0; i < abl->mNumberBuffers; i++)
98 memset(abl->mBuffers[i].mData, 0,
99 abl->mBuffers[i].mDataByteSize);
100 goto out;
101 }
102 powd->playing = true;
103 }
104
105 for (i = 0; i < abl->mNumberBuffers; i++) {
106 /* what we have to fill */
107 void *dest = abl->mBuffers[i].mData;
108 size_t sz = abl->mBuffers[i].mDataByteSize, samples, bytes;
109
110 samples_want = sz / wn->min_iqs;
111 while (samples_want > 0) {
112 char *buf;
113 btr_merge(powd->callback_btrn, wn->min_iqs);
114 samples_have = btr_next_buffer(powd->callback_btrn, &buf) / wn->min_iqs;
115 //PARA_INFO_LOG("i: %d want %zu samples to addr %p, have: %zu\n", i, samples_want,
116 // dest, samples_have);
117 samples = PARA_MIN(samples_have, samples_want);
118 if (samples == 0)
119 break;
120 bytes = samples * wn->min_iqs;
121 memcpy(dest, buf, bytes);
122 btr_consume(powd->callback_btrn, bytes);
123 samples_want -= samples;
124 dest += bytes;
125 }
126 if (samples_want == 0)
127 continue;
128 if (btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF) >= 0)
129 PARA_INFO_LOG("zero-padding (%zu samples)\n",
130 samples_want);
131 memset(dest, 0, samples_want * wn->min_iqs);
132 break;
133 }
134 out:
135 mutex_unlock(powd->mutex);
136 return noErr;
137 }
138
139 static int core_audio_init(struct writer_node *wn)
140 {
141 struct private_osx_write_data *powd = para_calloc(sizeof(*powd));
142 Component comp;
143 int ret;
144 int32_t val;
145 AURenderCallbackStruct input_callback;
146 ComponentDescription desc = {
147 .componentType = kAudioUnitType_Output,
148 .componentSubType = kAudioUnitSubType_DefaultOutput,
149 .componentManufacturer = kAudioUnitManufacturer_Apple,
150 };
151 AudioStreamBasicDescription format = {
152 .mFormatID = kAudioFormatLinearPCM,
153 .mFramesPerPacket = 1,
154 };
155 struct btr_node *btrn = wn->btrn;
156 struct btr_node_description bnd;
157
158 PARA_INFO_LOG("wn: %p\n", wn);
159 ret = -E_DEFAULT_COMP;
160 comp = FindNextComponent(NULL, &desc);
161 if (!comp)
162 goto e0;
163 ret = -E_OPEN_COMP;
164 if (OpenAComponent(comp, &powd->audio_unit))
165 goto e0;
166 ret = -E_UNIT_INIT;
167 if (AudioUnitInitialize(powd->audio_unit))
168 goto e1;
169 get_btr_sample_rate(btrn, &val);
170 powd->sample_rate = val;
171 get_btr_channels(btrn, &val);
172 powd->channels = val;
173 get_btr_sample_format(btrn, &val);
174 powd->sample_format = val;
175 /*
176 * Choose PCM format. We tell the Output Unit what format we're going
177 * to supply data to it. This is necessary if you're providing data
178 * through an input callback AND you want the DefaultOutputUnit to do
179 * any format conversions necessary from your format to the device's
180 * format.
181 */
182
183 format.mSampleRate = powd->sample_rate;
184 format.mChannelsPerFrame = powd->channels;
185
186 switch (powd->sample_format) {
187 case SF_S8:
188 case SF_U8:
189 wn->min_iqs = powd->channels;
190 format.mBitsPerChannel = 8;
191 format.mBytesPerPacket = powd->channels;
192 format.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
193 break;
194 default:
195 wn->min_iqs = powd->channels * 2;
196 format.mBytesPerPacket = powd->channels * 2;
197 format.mBitsPerChannel = 16;
198 format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
199 }
200 format.mBytesPerFrame = format.mBytesPerPacket;
201
202 if (powd->sample_format == SF_S16_BE || powd->sample_format == SF_U16_BE)
203 format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
204
205 input_callback = (AURenderCallbackStruct){osx_callback, wn};
206 ret = -E_STREAM_FORMAT;
207 if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_StreamFormat,
208 kAudioUnitScope_Input, 0, &format, sizeof(format)))
209 goto e2;
210 ret = -E_ADD_CALLBACK;
211 if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_SetRenderCallback,
212 kAudioUnitScope_Input, 0, &input_callback,
213 sizeof(input_callback)) < 0)
214 goto e2;
215
216 ret = mutex_new();
217 if (ret < 0)
218 goto e2;
219 powd->mutex = ret;
220 /* set up callback btr node */
221 bnd.name = "cb_node";
222 bnd.parent = btrn;
223 bnd.child = NULL;
224 bnd.handler = NULL;
225 bnd.context = powd;
226 powd->callback_btrn = btr_new_node(&bnd);
227 wn->private_data = powd;
228 return 1;
229 e2:
230 AudioUnitUninitialize(powd->audio_unit);
231 e1:
232 CloseComponent(powd->audio_unit);
233 e0:
234 free(powd);
235 wn->private_data = NULL;
236 return ret;
237 }
238
239 __malloc static void *osx_write_parse_config_or_die(int argc, char **argv)
240 {
241 struct osx_write_args_info *conf = para_calloc(sizeof(*conf));
242
243 /* exits on errors */
244 osx_write_cmdline_parser(argc, argv, conf);
245 return conf;
246 }
247
248 static void osx_free_config(void *conf)
249 {
250 osx_write_cmdline_parser_free(conf);
251 }
252
253 static void osx_write_close(struct writer_node *wn)
254 {
255 struct private_osx_write_data *powd = wn->private_data;
256
257 if (!powd)
258 return;
259 PARA_INFO_LOG("closing writer node %p\n", wn);
260 mutex_destroy(powd->mutex);
261 free(powd);
262 wn->private_data = NULL;
263 }
264
265 /* must be called with the mutex held */
266 static inline bool need_drain_delay(struct private_osx_write_data *powd)
267 {
268 if (!powd->playing)
269 return false;
270 return btr_get_input_queue_size(powd->callback_btrn) != 0;
271 }
272
273 static void osx_write_pre_select(struct sched *s, struct task *t)
274 {
275 struct writer_node *wn = container_of(t, struct writer_node, task);
276 struct private_osx_write_data *powd = wn->private_data;
277 int ret;
278 bool drain_delay_nec = false;
279
280 if (!powd) {
281 ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
282 if (ret != 0)
283 sched_min_delay(s);
284 return;
285 }
286
287 mutex_lock(powd->mutex);
288 ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_INTERNAL);
289 if (ret < 0)
290 drain_delay_nec = need_drain_delay(powd);
291 mutex_unlock(powd->mutex);
292
293 if (drain_delay_nec)
294 return sched_request_timeout_ms(50, s);
295 if (ret != 0)
296 return sched_min_delay(s);
297 sched_request_timeout_ms(50, s);
298 }
299
300 static void osx_write_post_select(__a_unused struct sched *s, struct task *t)
301 {
302 struct writer_node *wn = container_of(t, struct writer_node, task);
303 struct private_osx_write_data *powd = wn->private_data;
304 struct btr_node *btrn = wn->btrn;
305 int ret;
306
307 if (!powd) {
308 ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
309 if (ret == 0)
310 return;
311 if (ret < 0)
312 goto remove_btrn;
313 ret = core_audio_init(wn);
314 if (ret < 0)
315 goto remove_btrn;
316 powd = wn->private_data;
317 AudioOutputUnitStart(powd->audio_unit);
318 }
319 mutex_lock(powd->mutex);
320 ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_INTERNAL);
321 if (ret > 0)
322 btr_pushdown(btrn);
323 if (ret < 0 && need_drain_delay(powd))
324 ret = 0;
325 mutex_unlock(powd->mutex);
326
327 if (ret >= 0)
328 goto out;
329 AudioOutputUnitStop(powd->audio_unit);
330 AudioUnitUninitialize(powd->audio_unit);
331 CloseComponent(powd->audio_unit);
332 btr_remove_node(&powd->callback_btrn);
333 remove_btrn:
334 btr_remove_node(&wn->btrn);
335 PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
336 out:
337 t->error = ret;
338 }
339
340 /**
341 * The init function of the osx writer.
342 *
343 * \param w Filled in by the function.
344 */
345 void osx_write_init(struct writer *w)
346 {
347 struct osx_write_args_info dummy;
348
349 osx_write_cmdline_parser_init(&dummy);
350 w->close = osx_write_close;
351 w->pre_select = osx_write_pre_select;
352 w->post_select = osx_write_post_select;
353 w->parse_config_or_die = osx_write_parse_config_or_die;
354 w->free_config = osx_free_config;
355 w->help = (struct ggo_help) {
356 .short_help = osx_write_args_info_help,
357 .detailed_help = osx_write_args_info_detailed_help
358 };
359 osx_write_cmdline_parser_free(&dummy);
360 }