oss_cppflags := @oss_cppflags@
mp4v2_cppflags := @mp4v2_cppflags@
-clock_gettime_ldflags := @clock_gettime_ldflags@
id3tag_ldflags := @id3tag_ldflags@
ogg_ldflags := @ogg_ldflags@
vorbis_ldflags := @vorbis_ldflags@
samplerate_ldflags := @samplerate_ldflags@
osl_ldflags := @osl_ldflags@
curses_ldflags := @curses_ldflags@
-core_audio_ldflags := @core_audio_ldflags@
crypto_ldflags := @crypto_ldflags@
iconv_ldflags := @iconv_ldflags@
mp4v2_ldflags := @mp4v2_ldflags@
STRICT_CFLAGS += -Wmissing-format-attribute
STRICT_CFLAGS += -Wdeclaration-after-statement
-LDFLAGS += $(clock_gettime_ldflags)
-
ifeq ($(uname_s),Linux)
# these cause warnings on *BSD
CPPFLAGS += -Wunused-macros
para_recv para_afh para_play para_server: LDFLAGS += $(id3tag_ldflags)
para_write para_play para_audiod \
-: LDFLAGS += $(ao_ldflags) $(pthread_ldflags) $(core_audio_ldflags)
+: LDFLAGS += $(ao_ldflags) $(pthread_ldflags)
para_client para_audioc para_play : LDFLAGS += $(readline_ldflags)
para_server: LDFLAGS += $(osl_ldflags)
para_gui: LDFLAGS += $(curses_ldflags)
NEWS
====
+------------------------------------
+0.6.0 (to be announced) "fuzzy flux"
+------------------------------------
+- Support for Mac OS X has been removed.
+- On Linux systems, glibc-2.17 or newer is required to build the
+ source tree.
+
+Downloads:
+[tarball](./releases/paraslash-git.tar.bz2),
+
-------------------------------------
0.5.7 (2016-12-31) "semantic density"
-------------------------------------
AC_PROG_CPP
executables="recv filter audioc write afh play"
-################################################################## clock_gettime
-clock_gettime_lib=
-AC_CHECK_LIB([c], [clock_gettime], [clock_gettime_lib=c], [
- AC_CHECK_LIB([rt], [clock_gettime], [clock_gettime_lib=rt], [], [])
-])
-if test -n "$clock_gettime_lib"; then
- AC_DEFINE(HAVE_CLOCK_GETTIME, 1, [
- define to 1 if clock_gettime() is supported])
-fi
-if test "$clock_gettime_lib" = "rt"; then
- AC_SUBST(clock_gettime_ldflags, -lrt)
-fi
-
########################################################################### osl
STASH_FLAGS
LIB_ARG_WITH([osl], [-losl])
if test ${have_ip_mreqn} = yes; then
AC_DEFINE(HAVE_IP_MREQN, 1, define to 1 you have struct ip_mreqn)
fi
-########################################################################### osx
-
-AC_MSG_CHECKING(for CoreAudio (MacOs))
-AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- #include <CoreAudio/CoreAudio.h>
-]], [[
- AudioDeviceID id;
-]])],[have_core_audio=yes],[have_core_audio=no])
-AC_MSG_RESULT($have_core_audio)
-if test ${have_core_audio} = yes; then
- f1="-framework CoreAudio"
- f2="-framework AudioToolbox"
- f3="-framework AudioUnit"
- f4="-framework CoreServices"
- core_audio_ldflags="$f1 $f2 $f3 $f4"
- AC_SUBST(core_audio_ldflags)
- AC_DEFINE(HAVE_CORE_AUDIO, 1, define to 1 on Mac Os X)
-fi
########################################################################### ogg
STASH_FLAGS
LIB_ARG_WITH([ogg], [-logg])
else
audiod_errlist_objs="$audiod_errlist_objs gcrypt"
fi
- if test "$have_core_audio" = "yes"; then
- audiod_errlist_objs="$audiod_errlist_objs osx_write ipc"
- audiod_cmdline_objs="$audiod_cmdline_objs osx_write"
- fi
NEED_VORBIS_OBJECTS && {
audiod_errlist_objs="$audiod_errlist_objs oggdec_filter"
audiod_audio_formats="$audiod_audio_formats ogg"
play
sync_filter
"
-if test "$have_core_audio" = "yes"; then
- play_errlist_objs="$play_errlist_objs osx_write ipc"
- play_cmdline_objs="$play_cmdline_objs osx_write"
-fi
NEED_OGG_OBJECTS && play_errlist_objs="$play_errlist_objs ogg_afh_common"
NEED_VORBIS_OBJECTS && {
play_errlist_objs="$play_errlist_objs oggdec_filter ogg_afh"
writers="file"
default_writer="FILE_WRITE"
-if test "$have_core_audio" = "yes"; then
- write_errlist_objs="$write_errlist_objs osx_write ipc"
- write_cmdline_objs="$write_cmdline_objs osx_write"
- writers="$writers osx"
- default_writer="OSX_WRITE"
-fi
NEED_AO_OBJECTS && {
write_errlist_objs="$write_errlist_objs ao_write"
write_cmdline_objs="$write_cmdline_objs ao_write"
PARA_ERROR(AACDEC_INIT, "failed to init aac decoder"), \
PARA_ERROR(AAC_DECODE, "aac decode error"), \
PARA_ERROR(ACL_PERM, "access denied by acl"), \
- PARA_ERROR(ADD_CALLBACK, "can not add callback"), \
PARA_ERROR(ADDRESS_LOOKUP, "can not resolve requested address"),\
PARA_ERROR(AFH_RECV_BAD_FILENAME, "no file name given"), \
PARA_ERROR(AFS_SHORT_READ, "short read from afs socket"), \
PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
PARA_ERROR(DECRYPT, "decrypt error"), \
- PARA_ERROR(DEFAULT_COMP, "can not find default audio output component"), \
PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \
PARA_ERROR(DUP_PIPE, "exec error: can not create pipe"), \
PARA_ERROR(EMPTY, "file is empty"), \
PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
- PARA_ERROR(OPEN_COMP, "OpenAComponent() error"), \
PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
PARA_ERROR(OPUS_DECODE, "opus decode error"), \
PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
PARA_ERROR(STAT_ITEM_PARSE, "failed to parse status item"), \
PARA_ERROR(STATUS_TIMEOUT, "status item timeout"), \
PARA_ERROR(STCO, "did not find stco atom"), \
- PARA_ERROR(STREAM_FORMAT, "could not set stream format"), \
PARA_ERROR(STREAM_PACKETIN, "ogg stream packet-in error"), \
PARA_ERROR(STREAM_PACKETOUT, "ogg stream packet-out error"), \
PARA_ERROR(STREAM_PAGEIN, "ogg stream page-in error"), \
PARA_ERROR(TOO_MANY_CLIENTS, "maximal number of stat clients exceeded"), \
PARA_ERROR(UCRED_PERM, "permission denied"), \
PARA_ERROR(UDP_OVERRUN, "output buffer overrun"), \
- PARA_ERROR(UNIT_INIT, "AudioUnitInitialize() error"), \
- PARA_ERROR(UNIT_START, "AudioUnitStart() error"), \
PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \
PARA_ERROR(UNSUPPORTED_AUDIO_FORMAT, "given audio format not supported"), \
PARA_ERROR(UNSUPPORTED_FILTER, "given filter not supported"), \
static void wipe_bottom_line(void)
{
- char x[] = " ";
- int n = i9ep->num_columns;
-
- /*
- * For reasons beyond my understanding, writing more than 68 characters
- * here causes MacOS to mess up the terminal. Writing a line of spaces
- * in smaller chunks works fine though. Weird.
- */
- fprintf(i9ep->stderr_stream, "\r");
- while (n > 0) {
- if (n >= sizeof(x)) {
- fprintf(i9ep->stderr_stream, "%s", x);
- n -= sizeof(x);
- continue;
- }
- x[n] = '\0';
- fprintf(i9ep->stderr_stream, "%s", x);
- break;
- }
- fprintf(i9ep->stderr_stream, "\r");
+ fprintf(i9ep->stderr_stream, "\r%s\r", i9ep->empty_line);
}
#ifndef RL_FREE_KEYMAP_DECLARED
+++ /dev/null
-args "--no-version --no-help"
-
-purpose "Output plugin for Mac OS coreaudio"
-
-section "osx options"
-#####################
-
-option "numbuffers" n
-#~~~~~~~~~~~~~~~~~~~~~
-
-"number of audio buffers to allocate (increase if
-you get buffer underruns)"
-
- int typestr="num"
- default="20"
- optional
- details = ""
+++ /dev/null
-/*
- * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
- *
- * 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
- * <skortze@sourceforge.net>
- */
-
-#include <regex.h>
-#include <sys/types.h>
-
-#include "para.h"
-#include "fd.h"
-#include "string.h"
-#include "list.h"
-#include "sched.h"
-#include "ggo.h"
-#include "buffer_tree.h"
-#include "write.h"
-#include "write_common.h"
-#include "osx_write.cmdline.h"
-#include "ipc.h"
-#include "error.h"
-
-#include <CoreServices/CoreServices.h>
-#include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/AudioToolbox.h>
-
-/** 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;
-}
-
-__malloc static void *osx_write_parse_config_or_die(int argc, char **argv)
-{
- struct osx_write_args_info *conf = para_calloc(sizeof(*conf));
-
- /* exits on errors */
- osx_write_cmdline_parser(argc, argv, conf);
- return conf;
-}
-
-static void osx_free_config(void *conf)
-{
- osx_write_cmdline_parser_free(conf);
-}
-
-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;
-}
-
-/**
- * The init function of the osx writer.
- *
- * \param w Filled in by the function.
- */
-void osx_write_init(struct writer *w)
-{
- struct osx_write_args_info dummy;
-
- osx_write_cmdline_parser_init(&dummy);
- w->close = osx_write_close;
- w->pre_select = osx_write_pre_select;
- w->post_select = osx_write_post_select;
- w->parse_config_or_die = osx_write_parse_config_or_die;
- w->free_config = osx_free_config;
- w->help = (struct ggo_help)DEFINE_GGO_HELP(osx_write);
- osx_write_cmdline_parser_free(&dummy);
-}
struct timeval *clock_get_realtime(struct timeval *tv)
{
static struct timeval user_friendly;
+ struct timespec t;
+ int ret;
if (!tv)
tv = &user_friendly;
-#ifdef HAVE_CLOCK_GETTIME
- {
- struct timespec t;
- int ret;
-
- ret = clock_gettime(CLOCK_REALTIME, &t);
- assert(ret == 0);
- tv->tv_sec = t.tv_sec;
- tv->tv_usec = t.tv_nsec / 1000;
- }
-#else
- #include <sys/time.h>
- gettimeofday(tv, NULL);
-#endif /* HAVE_CLOCK_GETTIME */
+ ret = clock_gettime(CLOCK_REALTIME, &t);
+ assert(ret == 0);
+ tv->tv_sec = t.tv_sec;
+ tv->tv_usec = t.tv_nsec / 1000;
return tv;
}
systems. It is written in C and released under the GPLv2.
<ul>
- <li> Runs on Linux, Mac OS, FreeBSD, NetBSD </li>
+ <li> Runs on Linux, FreeBSD, NetBSD </li>
<li> Mp3, ogg/vorbis, ogg/speex, aac (m4a), wma, flac and ogg/opus support </li>
<li> http, dccp and udp network streaming </li>
<li> Stand-alone decoder, player, tagger </li>
### para_write ###
A modular audio stream writer. It supports a simple file writer
-output plug-in and optional WAV/raw players for ALSA (Linux) and for
-coreaudio (Mac OS). para_write can also be used as a stand-alone WAV
-or raw audio player.
+output plug-in and optional WAV/raw players for ALSA (Linux) and OSS.
+para_write can also be used as a stand-alone WAV or raw audio player.
### para_play ###
also limited. For example only one application can open the device
at any time. The OSS writer is activated by default on BSD Systems.
-- *OSX*. Mac OS X has yet another API called CoreAudio. The OSX writer
-for this API is only compiled in on such systems and is of course
-the default there.
-
- *FILE*. The file writer allows to capture the audio stream and
write the PCM data to a file on the file system rather than playing
it through a sound device. It is supported on all platforms and is
* \param wn The writer node.
*
* If arg is \p NULL, the OS-dependent default writer is used with no
- * arguments. The default writers are alsa for Linux, osx for OS X, oss for
- * *BSD, and the file writer if the default writer is not supported.
+ * arguments. The default writers are alsa for Linux and oss for *BSD, and the
+ * file writer if neither is supported.
*
* Once the writer configuration has been retrieved from the ->parse_config
* callback a writer node is created, its buffer tree node is added to the