2 * Copyright (C) 2006-2012 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file osx_write.c paraslash's output plugin for MacOs */
10 * based on mosx-mpg123, by Guillaume Outters and Steven A. Kortze
11 * <skortze@sourceforge.net>
15 #include <sys/types.h>
23 #include "buffer_tree.h"
25 #include "write_common.h"
26 #include "osx_write.cmdline.h"
30 #include <CoreServices/CoreServices.h>
31 #include <AudioUnit/AudioUnit.h>
32 #include <AudioToolbox/AudioToolbox.h>
34 /** Data specific to the osx writer. */
35 struct private_osx_write_data
{
36 /** The main CoreAudio handle. */
38 /** True if we wrote some audio data. */
40 /** Sample rate of the current audio stream. */
42 /** Sample format of the current audio stream */
43 unsigned sample_format
;
44 /** Number of channels of the current audio stream. */
47 * Serializes access to buffer tree nodes between the writer and
48 * the callback which runs in a different thread.
52 * The btr node of the callback.
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
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.
65 * The writer grabs the mutex in its post_select method and pushes down
66 * the buffers to the callback node.
68 struct btr_node
*callback_btrn
;
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
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
)
80 struct writer_node
*wn
= cb_arg
;
81 struct private_osx_write_data
*powd
;
82 size_t samples_have
, samples_want
= 0;
84 powd
= wn
->private_data
;
85 mutex_lock(powd
->mutex
);
86 powd
= wn
->private_data
;
87 if (!powd
|| !wn
->btrn
)
90 * We fill with zeros if no data was yet written and we do not have
91 * enough to fill all buffers.
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
;
99 PARA_DEBUG_LOG("deferring playback (have = %zu < %zu = want)\n",
101 for (i
= 0; i
< abl
->mNumberBuffers
; i
++)
102 memset(abl
->mBuffers
[i
].mData
, 0,
103 abl
->mBuffers
[i
].mDataByteSize
);
106 powd
->playing
= true;
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
;
114 samples_want
= sz
/ wn
->min_iqs
;
115 while (samples_want
> 0) {
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
);
124 bytes
= samples
* wn
->min_iqs
;
125 memcpy(dest
, buf
, bytes
);
126 btr_consume(powd
->callback_btrn
, bytes
);
127 samples_want
-= samples
;
130 if (samples_want
== 0)
132 if (btr_node_status(wn
->btrn
, wn
->min_iqs
, BTR_NT_LEAF
) >= 0)
133 PARA_INFO_LOG("zero-padding (%zu samples)\n",
135 memset(dest
, 0, samples_want
* wn
->min_iqs
);
139 mutex_unlock(powd
->mutex
);
143 static int core_audio_init(struct writer_node
*wn
)
145 struct private_osx_write_data
*powd
= para_calloc(sizeof(*powd
));
149 AURenderCallbackStruct input_callback
;
150 ComponentDescription desc
= {
151 .componentType
= kAudioUnitType_Output
,
152 .componentSubType
= kAudioUnitSubType_DefaultOutput
,
153 .componentManufacturer
= kAudioUnitManufacturer_Apple
,
155 AudioStreamBasicDescription format
= {
156 .mFormatID
= kAudioFormatLinearPCM
,
157 .mFramesPerPacket
= 1,
159 struct btr_node
*btrn
= wn
->btrn
;
160 struct btr_node_description bnd
;
162 PARA_INFO_LOG("wn: %p\n", wn
);
163 ret
= -E_DEFAULT_COMP
;
164 comp
= FindNextComponent(NULL
, &desc
);
168 if (OpenAComponent(comp
, &powd
->audio_unit
))
171 if (AudioUnitInitialize(powd
->audio_unit
))
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
;
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
187 format
.mSampleRate
= powd
->sample_rate
;
188 format
.mChannelsPerFrame
= powd
->channels
;
190 switch (powd
->sample_format
) {
193 wn
->min_iqs
= powd
->channels
;
194 format
.mBitsPerChannel
= 8;
195 format
.mBytesPerPacket
= powd
->channels
;
196 format
.mFormatFlags
|= kLinearPCMFormatFlagIsPacked
;
199 wn
->min_iqs
= powd
->channels
* 2;
200 format
.mBytesPerPacket
= powd
->channels
* 2;
201 format
.mBitsPerChannel
= 16;
202 format
.mFormatFlags
|= kLinearPCMFormatFlagIsSignedInteger
;
204 format
.mBytesPerFrame
= format
.mBytesPerPacket
;
206 if (powd
->sample_format
== SF_S16_BE
|| powd
->sample_format
== SF_U16_BE
)
207 format
.mFormatFlags
|= kLinearPCMFormatFlagIsBigEndian
;
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
)))
214 ret
= -E_ADD_CALLBACK
;
215 if (AudioUnitSetProperty(powd
->audio_unit
, kAudioUnitProperty_SetRenderCallback
,
216 kAudioUnitScope_Input
, 0, &input_callback
,
217 sizeof(input_callback
)) < 0)
224 /* set up callback btr node */
225 bnd
.name
= "cb_node";
230 powd
->callback_btrn
= btr_new_node(&bnd
);
231 wn
->private_data
= powd
;
234 AudioUnitUninitialize(powd
->audio_unit
);
236 CloseComponent(powd
->audio_unit
);
239 wn
->private_data
= NULL
;
243 __malloc
static void *osx_write_parse_config_or_die(int argc
, char **argv
)
245 struct osx_write_args_info
*conf
= para_calloc(sizeof(*conf
));
247 /* exits on errors */
248 osx_write_cmdline_parser(argc
, argv
, conf
);
252 static void osx_free_config(void *conf
)
254 osx_write_cmdline_parser_free(conf
);
257 static void osx_write_close(struct writer_node
*wn
)
259 struct private_osx_write_data
*powd
= wn
->private_data
;
263 PARA_INFO_LOG("closing writer node %p\n", wn
);
264 mutex_destroy(powd
->mutex
);
266 wn
->private_data
= NULL
;
269 /* must be called with the mutex held */
270 static inline bool need_drain_delay(struct private_osx_write_data
*powd
)
274 return btr_get_input_queue_size(powd
->callback_btrn
) != 0;
277 static void osx_write_pre_select(struct sched
*s
, struct task
*t
)
279 struct writer_node
*wn
= container_of(t
, struct writer_node
, task
);
280 struct private_osx_write_data
*powd
= wn
->private_data
;
282 bool drain_delay_nec
= false;
285 ret
= btr_node_status(wn
->btrn
, wn
->min_iqs
, BTR_NT_LEAF
);
291 mutex_lock(powd
->mutex
);
292 ret
= btr_node_status(wn
->btrn
, wn
->min_iqs
, BTR_NT_INTERNAL
);
294 drain_delay_nec
= need_drain_delay(powd
);
295 mutex_unlock(powd
->mutex
);
298 return sched_request_timeout_ms(50, s
);
300 return sched_min_delay(s
);
301 sched_request_timeout_ms(50, s
);
304 static void osx_write_post_select(__a_unused
struct sched
*s
, struct task
*t
)
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
;
312 ret
= btr_node_status(btrn
, wn
->min_iqs
, BTR_NT_LEAF
);
317 ret
= core_audio_init(wn
);
320 powd
= wn
->private_data
;
321 AudioOutputUnitStart(powd
->audio_unit
);
323 mutex_lock(powd
->mutex
);
324 ret
= btr_node_status(btrn
, wn
->min_iqs
, BTR_NT_INTERNAL
);
327 if (ret
< 0 && need_drain_delay(powd
))
330 mutex_unlock(powd
->mutex
);
333 AudioOutputUnitStop(powd
->audio_unit
);
334 AudioUnitUninitialize(powd
->audio_unit
);
335 CloseComponent(powd
->audio_unit
);
336 btr_remove_node(&powd
->callback_btrn
);
337 mutex_unlock(powd
->mutex
);
339 btr_remove_node(&wn
->btrn
);
340 PARA_NOTICE_LOG("%s\n", para_strerror(-ret
));
345 * The init function of the osx writer.
347 * \param w Filled in by the function.
349 void osx_write_init(struct writer
*w
)
351 struct osx_write_args_info dummy
;
353 osx_write_cmdline_parser_init(&dummy
);
354 w
->close
= osx_write_close
;
355 w
->pre_select
= osx_write_pre_select
;
356 w
->post_select
= osx_write_post_select
;
357 w
->parse_config_or_die
= osx_write_parse_config_or_die
;
358 w
->free_config
= osx_free_config
;
359 w
->help
= (struct ggo_help
) {
360 .short_help
= osx_write_args_info_help
,
361 .detailed_help
= osx_write_args_info_detailed_help
363 osx_write_cmdline_parser_free(&dummy
);