X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=osx_write.c;fp=osx_write.c;h=0000000000000000000000000000000000000000;hp=0517892e8d991d877bd8a0a18c46caf72bf38fb9;hb=7d9e345416f87866bea1e0179641d31091b97ffb;hpb=11279193cca5441c834eb6f08c65ae82c9974307 diff --git a/osx_write.c b/osx_write.c deleted file mode 100644 index 0517892e..00000000 --- a/osx_write.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright (C) 2006 Andre Noll - * - * Licensed under the GPL v2. For licencing details see COPYING. - */ - -/** \file osx_write.c paraslash's output plugin for MacOs */ - -/* - * based on mosx-mpg123, by Guillaume Outters and Steven A. Kortze - * - */ - -#include -#include -#include - -#include "write_cmd.lsg.h" -#include "para.h" -#include "fd.h" -#include "string.h" -#include "list.h" -#include "sched.h" -#include "buffer_tree.h" -#include "write.h" -#include "ipc.h" -#include "error.h" - -#include -#include -#include - -/** Data specific to the osx writer. */ -struct private_osx_write_data { - /** The main CoreAudio handle. */ - AudioUnit audio_unit; - /** True if we wrote some audio data. */ - bool playing; - /** Sample rate of the current audio stream. */ - unsigned sample_rate; - /** Sample format of the current audio stream */ - unsigned sample_format; - /** Number of channels of the current audio stream. */ - unsigned channels; - /** - * Serializes access to buffer tree nodes between the writer and - * the callback which runs in a different thread. - */ - int mutex; - /** - * The btr node of the callback. - * - * Although access to the btr node is serialized between the writer and - * the callback via the above mutex, this does not stop other buffer - * tree nodes, for example the decoder, to race against the osx - * callback. - * - * However, since all operations on buffer tree nodes are local in the - * sense that they only affect one level in the buffer tree (i.e. - * parent or child nodes, but not the grandparent or the - * grandchildren), we may work around this problem by using another - * buffer tree node for the callback. - * - * The writer grabs the mutex in its post_select method and pushes down - * the buffers to the callback node. - */ - struct btr_node *callback_btrn; -}; - -/* This function writes the address and the number of bytes to one end of the socket. - * The post_select() function then fills the buffer and notifies the callback also - * through the socket. - */ -static OSStatus osx_callback(void *cb_arg, __a_unused AudioUnitRenderActionFlags *af, - __a_unused const AudioTimeStamp *ts, __a_unused UInt32 bus_number, - __a_unused UInt32 num_frames, AudioBufferList *abl) -{ - int i; - struct writer_node *wn = cb_arg; - struct private_osx_write_data *powd; - size_t samples_have, samples_want = 0; - - powd = wn->private_data; - mutex_lock(powd->mutex); - powd = wn->private_data; - if (!powd || !wn->btrn) - goto out; - /* - * We fill with zeros if no data was yet written and we do not have - * enough to fill all buffers. - */ - if (!powd->playing) { - size_t want = 0, have = - btr_get_input_queue_size(powd->callback_btrn); - for (i = 0; i < abl->mNumberBuffers; i++) - want += abl->mBuffers[i].mDataByteSize; - if (have < want) { - PARA_DEBUG_LOG("deferring playback (have = %zu < %zu = want)\n", - have, want); - for (i = 0; i < abl->mNumberBuffers; i++) - memset(abl->mBuffers[i].mData, 0, - abl->mBuffers[i].mDataByteSize); - goto out; - } - powd->playing = true; - } - - for (i = 0; i < abl->mNumberBuffers; i++) { - /* what we have to fill */ - void *dest = abl->mBuffers[i].mData; - size_t sz = abl->mBuffers[i].mDataByteSize, samples, bytes; - - samples_want = sz / wn->min_iqs; - while (samples_want > 0) { - char *buf; - btr_merge(powd->callback_btrn, wn->min_iqs); - samples_have = btr_next_buffer(powd->callback_btrn, &buf) / wn->min_iqs; - //PARA_INFO_LOG("i: %d want %zu samples to addr %p, have: %zu\n", i, samples_want, - // dest, samples_have); - samples = PARA_MIN(samples_have, samples_want); - if (samples == 0) - break; - bytes = samples * wn->min_iqs; - memcpy(dest, buf, bytes); - btr_consume(powd->callback_btrn, bytes); - samples_want -= samples; - dest += bytes; - } - if (samples_want == 0) - continue; - if (btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF) >= 0) - PARA_INFO_LOG("zero-padding (%zu samples)\n", - samples_want); - memset(dest, 0, samples_want * wn->min_iqs); - break; - } -out: - mutex_unlock(powd->mutex); - return noErr; -} - -static int core_audio_init(struct writer_node *wn) -{ - struct private_osx_write_data *powd = para_calloc(sizeof(*powd)); - Component comp; - int ret; - int32_t val; - AURenderCallbackStruct input_callback; - ComponentDescription desc = { - .componentType = kAudioUnitType_Output, - .componentSubType = kAudioUnitSubType_DefaultOutput, - .componentManufacturer = kAudioUnitManufacturer_Apple, - }; - AudioStreamBasicDescription format = { - .mFormatID = kAudioFormatLinearPCM, - .mFramesPerPacket = 1, - }; - struct btr_node *btrn = wn->btrn; - struct btr_node_description bnd; - - PARA_INFO_LOG("wn: %p\n", wn); - ret = -E_DEFAULT_COMP; - comp = FindNextComponent(NULL, &desc); - if (!comp) - goto e0; - ret = -E_OPEN_COMP; - if (OpenAComponent(comp, &powd->audio_unit)) - goto e0; - ret = -E_UNIT_INIT; - if (AudioUnitInitialize(powd->audio_unit)) - goto e1; - get_btr_sample_rate(btrn, &val); - powd->sample_rate = val; - get_btr_channels(btrn, &val); - powd->channels = val; - get_btr_sample_format(btrn, &val); - powd->sample_format = val; - /* - * Choose PCM format. We tell the Output Unit what format we're going - * to supply data to it. This is necessary if you're providing data - * through an input callback AND you want the DefaultOutputUnit to do - * any format conversions necessary from your format to the device's - * format. - */ - - format.mSampleRate = powd->sample_rate; - format.mChannelsPerFrame = powd->channels; - - switch (powd->sample_format) { - case SF_S8: - case SF_U8: - wn->min_iqs = powd->channels; - format.mBitsPerChannel = 8; - format.mBytesPerPacket = powd->channels; - format.mFormatFlags |= kLinearPCMFormatFlagIsPacked; - break; - default: - wn->min_iqs = powd->channels * 2; - format.mBytesPerPacket = powd->channels * 2; - format.mBitsPerChannel = 16; - format.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; - } - format.mBytesPerFrame = format.mBytesPerPacket; - - if (powd->sample_format == SF_S16_BE || powd->sample_format == SF_U16_BE) - format.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; - - input_callback = (AURenderCallbackStruct){osx_callback, wn}; - ret = -E_STREAM_FORMAT; - if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, &format, sizeof(format))) - goto e2; - ret = -E_ADD_CALLBACK; - if (AudioUnitSetProperty(powd->audio_unit, kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, &input_callback, - sizeof(input_callback)) < 0) - goto e2; - - ret = mutex_new(); - if (ret < 0) - goto e2; - powd->mutex = ret; - /* set up callback btr node */ - bnd.name = "cb_node"; - bnd.parent = btrn; - bnd.child = NULL; - bnd.handler = NULL; - bnd.context = powd; - powd->callback_btrn = btr_new_node(&bnd); - wn->private_data = powd; - return 1; -e2: - AudioUnitUninitialize(powd->audio_unit); -e1: - CloseComponent(powd->audio_unit); -e0: - free(powd); - wn->private_data = NULL; - return ret; -} - -static void osx_write_close(struct writer_node *wn) -{ - struct private_osx_write_data *powd = wn->private_data; - - if (!powd) - return; - PARA_INFO_LOG("closing writer node %p\n", wn); - mutex_destroy(powd->mutex); - free(powd); - wn->private_data = NULL; -} - -/* must be called with the mutex held */ -static inline bool need_drain_delay(struct private_osx_write_data *powd) -{ - if (!powd->playing) - return false; - return btr_get_input_queue_size(powd->callback_btrn) != 0; -} - -static void osx_write_pre_select(struct sched *s, void *context) -{ - struct writer_node *wn = context; - struct private_osx_write_data *powd = wn->private_data; - int ret; - bool drain_delay_nec = false; - - if (!powd) { - ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - if (ret != 0) - sched_min_delay(s); - return; - } - - mutex_lock(powd->mutex); - ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_INTERNAL); - if (ret < 0) - drain_delay_nec = need_drain_delay(powd); - mutex_unlock(powd->mutex); - - if (drain_delay_nec) - return sched_request_timeout_ms(50, s); - if (ret != 0) - return sched_min_delay(s); - sched_request_timeout_ms(50, s); -} - -static int osx_write_post_select(__a_unused struct sched *s, void *context) -{ - struct writer_node *wn = context; - struct private_osx_write_data *powd = wn->private_data; - struct btr_node *btrn = wn->btrn; - int ret; - - ret = task_get_notification(wn->task); - if (ret < 0) - goto fail; - if (!powd) { - ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF); - if (ret == 0) - return 0; - if (ret < 0) - goto fail; - ret = core_audio_init(wn); - if (ret < 0) - goto fail; - powd = wn->private_data; - ret = -E_UNIT_START; - if (AudioOutputUnitStart(powd->audio_unit) != noErr) { - AudioUnitUninitialize(powd->audio_unit); - CloseComponent(powd->audio_unit); - btr_remove_node(&powd->callback_btrn); - goto fail; - } - } - mutex_lock(powd->mutex); - ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_INTERNAL); - if (ret > 0) - btr_pushdown(btrn); - if (ret < 0 && need_drain_delay(powd)) - ret = 0; - mutex_unlock(powd->mutex); - if (ret >= 0) - return 0; -fail: - assert(ret < 0); - if (powd && powd->callback_btrn) { - AudioOutputUnitStop(powd->audio_unit); - AudioUnitUninitialize(powd->audio_unit); - CloseComponent(powd->audio_unit); - btr_remove_node(&powd->callback_btrn); - } - btr_remove_node(&wn->btrn); - PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); - return ret; -} - -struct writer lsg_write_cmd_com_osx_user_data = { - .close = osx_write_close, - .pre_select = osx_write_pre_select, - .post_select = osx_write_post_select, -};