client: Fix typo in comment.
[paraslash.git] / osx_write.c
1 /*
2  * Copyright (C) 2006-2013 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;
82         size_t samples_have, samples_want = 0;
83
84         powd = wn->private_data;
85         mutex_lock(powd->mutex);
86         powd = wn->private_data;
87         if (!powd || !wn->btrn)
88                 goto out;
89         /*
90          * We fill with zeros if no data was yet written and we do not have
91          * enough to fill all buffers.
92          */
93         if (!powd->playing) {
94                 size_t want = 0, have =
95                         btr_get_input_queue_size(powd->callback_btrn);
96                 for (i = 0; i < abl->mNumberBuffers; i++)
97                         want += abl->mBuffers[i].mDataByteSize;
98                 if (have < want) {
99                         PARA_DEBUG_LOG("deferring playback (have = %zu < %zu = want)\n",
100                                 have, want);
101                         for (i = 0; i < abl->mNumberBuffers; i++)
102                                 memset(abl->mBuffers[i].mData, 0,
103                                         abl->mBuffers[i].mDataByteSize);
104                         goto out;
105                 }
106                 powd->playing = true;
107         }
108
109         for (i = 0; i < abl->mNumberBuffers; i++) {
110                 /* what we have to fill */
111                 void *dest = abl->mBuffers[i].mData;
112                 size_t sz = abl->mBuffers[i].mDataByteSize, samples, bytes;
113
114                 samples_want = sz / wn->min_iqs;
115                 while (samples_want > 0) {
116                         char *buf;
117                         btr_merge(powd->callback_btrn, wn->min_iqs);
118                         samples_have = btr_next_buffer(powd->callback_btrn, &buf) / wn->min_iqs;
119                         //PARA_INFO_LOG("i: %d want %zu samples to addr %p, have: %zu\n", i, samples_want,
120                         //      dest, samples_have);
121                         samples = PARA_MIN(samples_have, samples_want);
122                         if (samples == 0)
123                                 break;
124                         bytes = samples * wn->min_iqs;
125                         memcpy(dest, buf, bytes);
126                         btr_consume(powd->callback_btrn, bytes);
127                         samples_want -= samples;
128                         dest += bytes;
129                 }
130                 if (samples_want == 0)
131                         continue;
132                 if (btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF) >= 0)
133                         PARA_INFO_LOG("zero-padding (%zu samples)\n",
134                                 samples_want);
135                 memset(dest, 0, samples_want * wn->min_iqs);
136                 break;
137         }
138 out:
139         mutex_unlock(powd->mutex);
140         return noErr;
141 }
142
143 static int core_audio_init(struct writer_node *wn)
144 {
145         struct private_osx_write_data *powd = para_calloc(sizeof(*powd));
146         Component comp;
147         int ret;
148         int32_t val;
149         AURenderCallbackStruct input_callback;
150         ComponentDescription desc = {
151                 .componentType = kAudioUnitType_Output,
152                 .componentSubType = kAudioUnitSubType_DefaultOutput,
153                 .componentManufacturer = kAudioUnitManufacturer_Apple,
154         };
155         AudioStreamBasicDescription format = {
156                 .mFormatID = kAudioFormatLinearPCM,
157                 .mFramesPerPacket = 1,
158         };
159         struct btr_node *btrn = wn->btrn;
160         struct btr_node_description bnd;
161
162         PARA_INFO_LOG("wn: %p\n", wn);
163         ret = -E_DEFAULT_COMP;
164         comp = FindNextComponent(NULL, &desc);
165         if (!comp)
166                 goto e0;
167         ret = -E_OPEN_COMP;
168         if (OpenAComponent(comp, &powd->audio_unit))
169                 goto e0;
170         ret = -E_UNIT_INIT;
171         if (AudioUnitInitialize(powd->audio_unit))
172                 goto e1;
173         get_btr_sample_rate(btrn, &val);
174         powd->sample_rate = val;
175         get_btr_channels(btrn, &val);
176         powd->channels = val;
177         get_btr_sample_format(btrn, &val);
178         powd->sample_format = val;
179         /*
180          * Choose PCM format. We tell the Output Unit what format we're going
181          * to supply data to it. This is necessary if you're providing data
182          * through an input callback AND you want the DefaultOutputUnit to do
183          * any format conversions necessary from your format to the device's
184          * format.
185          */
186
187         format.mSampleRate = powd->sample_rate;
188         format.mChannelsPerFrame = powd->channels;
189
190         switch (powd->sample_format) {
191         case SF_S8:
192         case SF_U8:
193                 wn->min_iqs = powd->channels;
194                 format.mBitsPerChannel = 8;
195                 format.mBytesPerPacket = powd->channels;
196                 format.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
197                 break;
198         default:
199                 wn->min_iqs = powd->channels * 2;
200                 format.mBytesPerPacket = powd->channels * 2;
201                 format.mBitsPerChannel = 16;
202                 format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
203         }
204         format.mBytesPerFrame = format.mBytesPerPacket;
205
206         if (powd->sample_format == SF_S16_BE || powd->sample_format == SF_U16_BE)
207                 format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
208
209         input_callback = (AURenderCallbackStruct){osx_callback, wn};
210         ret = -E_STREAM_FORMAT;
211         if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_StreamFormat,
212                         kAudioUnitScope_Input, 0, &format, sizeof(format)))
213                 goto e2;
214         ret = -E_ADD_CALLBACK;
215         if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_SetRenderCallback,
216                         kAudioUnitScope_Input, 0, &input_callback,
217                         sizeof(input_callback)) < 0)
218                 goto e2;
219
220         ret = mutex_new();
221         if (ret < 0)
222                 goto e2;
223         powd->mutex = ret;
224         /* set up callback btr node */
225         bnd.name = "cb_node";
226         bnd.parent = btrn;
227         bnd.child = NULL;
228         bnd.handler = NULL;
229         bnd.context = powd;
230         powd->callback_btrn = btr_new_node(&bnd);
231         wn->private_data = powd;
232         return 1;
233 e2:
234         AudioUnitUninitialize(powd->audio_unit);
235 e1:
236         CloseComponent(powd->audio_unit);
237 e0:
238         free(powd);
239         wn->private_data = NULL;
240         return ret;
241 }
242
243 __malloc static void *osx_write_parse_config_or_die(int argc, char **argv)
244 {
245         struct osx_write_args_info *conf = para_calloc(sizeof(*conf));
246
247         /* exits on errors */
248         osx_write_cmdline_parser(argc, argv, conf);
249         return conf;
250 }
251
252 static void osx_free_config(void *conf)
253 {
254         osx_write_cmdline_parser_free(conf);
255 }
256
257 static void osx_write_close(struct writer_node *wn)
258 {
259         struct private_osx_write_data *powd = wn->private_data;
260
261         if (!powd)
262                 return;
263         PARA_INFO_LOG("closing writer node %p\n", wn);
264         mutex_destroy(powd->mutex);
265         free(powd);
266         wn->private_data = NULL;
267 }
268
269 /* must be called with the mutex held */
270 static inline bool need_drain_delay(struct private_osx_write_data *powd)
271 {
272         if (!powd->playing)
273                 return false;
274         return btr_get_input_queue_size(powd->callback_btrn) != 0;
275 }
276
277 static void osx_write_pre_select(struct sched *s, struct task *t)
278 {
279         struct writer_node *wn = container_of(t, struct writer_node, task);
280         struct private_osx_write_data *powd = wn->private_data;
281         int ret;
282         bool drain_delay_nec = false;
283
284         if (!powd) {
285                 ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
286                 if (ret != 0)
287                         sched_min_delay(s);
288                 return;
289         }
290
291         mutex_lock(powd->mutex);
292         ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_INTERNAL);
293         if (ret < 0)
294                 drain_delay_nec = need_drain_delay(powd);
295         mutex_unlock(powd->mutex);
296
297         if (drain_delay_nec)
298                 return sched_request_timeout_ms(50, s);
299         if (ret != 0)
300                 return sched_min_delay(s);
301         sched_request_timeout_ms(50, s);
302 }
303
304 static int osx_write_post_select(__a_unused struct sched *s, struct task *t)
305 {
306         struct writer_node *wn = container_of(t, struct writer_node, task);
307         struct private_osx_write_data *powd = wn->private_data;
308         struct btr_node *btrn = wn->btrn;
309         int ret;
310
311         ret = task_get_notification(t);
312         if (ret < 0)
313                 goto fail;
314         if (!powd) {
315                 ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
316                 if (ret == 0)
317                         return 0;
318                 if (ret < 0)
319                         goto fail;
320                 ret = core_audio_init(wn);
321                 if (ret < 0)
322                         goto fail;
323                 powd = wn->private_data;
324                 ret = -E_UNIT_START;
325                 if (AudioOutputUnitStart(powd->audio_unit) != noErr) {
326                         AudioUnitUninitialize(powd->audio_unit);
327                         CloseComponent(powd->audio_unit);
328                         btr_remove_node(&powd->callback_btrn);
329                         goto fail;
330                 }
331         }
332         mutex_lock(powd->mutex);
333         ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_INTERNAL);
334         if (ret > 0)
335                 btr_pushdown(btrn);
336         if (ret < 0 && need_drain_delay(powd))
337                 ret = 0;
338         mutex_unlock(powd->mutex);
339         if (ret >= 0)
340                 return 0;
341 fail:
342         assert(ret < 0);
343         if (powd && powd->callback_btrn) {
344                 AudioOutputUnitStop(powd->audio_unit);
345                 AudioUnitUninitialize(powd->audio_unit);
346                 CloseComponent(powd->audio_unit);
347                 btr_remove_node(&powd->callback_btrn);
348         }
349         btr_remove_node(&wn->btrn);
350         PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
351         return ret;
352 }
353
354 /**
355  * The init function of the osx writer.
356  *
357  * \param w Filled in by the function.
358  */
359 void osx_write_init(struct writer *w)
360 {
361         struct osx_write_args_info dummy;
362
363         osx_write_cmdline_parser_init(&dummy);
364         w->close = osx_write_close;
365         w->pre_select = osx_write_pre_select;
366         w->post_select = osx_write_post_select;
367         w->parse_config_or_die = osx_write_parse_config_or_die;
368         w->free_config = osx_free_config;
369         w->help = (struct ggo_help) {
370                 .short_help = osx_write_args_info_help,
371                 .detailed_help = osx_write_args_info_detailed_help
372         };
373         osx_write_cmdline_parser_free(&dummy);
374 }