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