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