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