summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac11
-rw-r--r--src/audio_out/Makefile.am12
-rw-r--r--src/audio_out/audio_coreaudio_out.c480
3 files changed, 502 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 387968558..89573c0e5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -666,6 +666,14 @@ AC_ARG_ENABLE(macosx_video, AC_HELP_STRING([--enable-macosx-video], [enable supp
AM_CONDITIONAL(HAVE_MACOSX_VIDEO, [test x"$have_macosx_video" = "xyes"])
dnl ---------------------------------------------
+dnl Check whether to build MacOSX audio output driver
+dnl ---------------------------------------------
+
+AC_ARG_ENABLE(coreaudio, AC_HELP_STRING([--enable-coreaudio], [enable support for MacOSX Coreaudio output]),
+ have_coreaudio=$enableval)
+AM_CONDITIONAL(HAVE_COREAUDIO, [test x"$have_coreaudio" = "xyes"])
+
+dnl ---------------------------------------------
dnl Find pkg-config
dnl ---------------------------------------------
@@ -2378,6 +2386,9 @@ fi
if test x"$have_directx" = "xyes"; then
echo " - directx (DirectX audio driver)"
fi
+if test x"$have_coreaudio" = "xyes"; then
+ echo " - CoreAudio (MacOSX audio driver)"
+fi
echo "---"
diff --git a/src/audio_out/Makefile.am b/src/audio_out/Makefile.am
index ab5f1dac9..7ec6e8972 100644
--- a/src/audio_out/Makefile.am
+++ b/src/audio_out/Makefile.am
@@ -36,6 +36,10 @@ if HAVE_DIRECTX
directx_module = xineplug_ao_out_directx.la
endif
+if HAVE_COREAUDIO
+coreaudio_module = xineplug_ao_out_coreaudio.la
+endif
+
##
# IMPORTANT:
# ---------
@@ -48,7 +52,8 @@ lib_LTLIBRARIES = xineplug_ao_out_none.la xineplug_ao_out_file.la \
$(sun_module) \
$(arts_module) \
$(esd_module) \
- $(directx_module)
+ $(directx_module) \
+ $(coreaudio_module)
#lib_LTLIBRARIES = \
# $(alsa_module) \
@@ -92,3 +97,8 @@ xineplug_ao_out_arts_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_ao_out_directx_la_SOURCES = audio_directx_out.c
xineplug_ao_out_directx_la_LIBADD = $(DIRECTX_AUDIO_LIBS) $(XINE_LIB)
xineplug_ao_out_directx_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+
+xineplug_ao_out_coreaudio_la_SOURCES = audio_coreaudio_out.c
+xineplug_ao_out_coreaudio_la_LIBADD = $(XINE_LIB)
+xineplug_ao_out_coreaudio_la_LDFLAGS = -framework CoreAudio -framework AudioUnit -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+xineplug_ao_out_coreaudio_la_CFLAGS = -framework CoreAudio -framework AudioUnit
diff --git a/src/audio_out/audio_coreaudio_out.c b/src/audio_out/audio_coreaudio_out.c
new file mode 100644
index 000000000..8df2e2f4c
--- /dev/null
+++ b/src/audio_out/audio_coreaudio_out.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2000-2004 the xine project
+ *
+ * This file is part of xine, a free video player.
+ *
+ * xine is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * xine is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * done by Daniel Mack <xine@zonque.org>
+ *
+ * See http://developer.apple.com/technotes/tn2002/tn2091.html
+ * and http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudio/index.html
+ * for conceptual documentation.
+ *
+ * The diffuculty here is that CoreAudio is pull-i/o while xine's internal
+ * system works on push-i/o basis. So there is need of a buffer inbetween.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "audio_out.h"
+
+#include <CoreAudio/CoreAudio.h>
+#include <CoreAudio/CoreAudioTypes.h>
+#include <AudioUnit/AUComponent.h>
+#include <AudioUnit/AudioUnitProperties.h>
+#include <AudioUnit/AudioUnitParameters.h>
+#include <AudioUnit/AudioOutputUnit.h>
+
+#define AO_OUT_COREAUDIO_IFACE_VERSION 8
+
+#define GAP_TOLERANCE AO_MAX_GAP
+#define BUFSIZE 0xffffff
+
+typedef struct coreaudio_driver_s {
+
+ ao_driver_t ao_driver;
+
+ xine_t *xine;
+
+ int capabilities;
+
+ int32_t sample_rate;
+ uint32_t num_channels;
+ uint32_t bits_per_sample;
+ uint32_t bytes_per_frame;
+
+ Component au_component;
+ Component converter_component;
+
+ AudioUnit au_unit;
+ AudioUnit converter_unit;
+
+ uint8_t buf[BUFSIZE];
+ uint32_t buf_readpos;
+ uint32_t buf_writepos;
+
+ pthread_mutex_t mutex;
+
+} coreaudio_driver_t;
+
+typedef struct {
+ audio_driver_class_t driver_class;
+
+ config_values_t *config;
+ xine_t *xine;
+} coreaudio_class_t;
+
+/* this function is called every time the CoreAudio sytem wants us to
+ * supply some data */
+static OSStatus ao_coreaudio_render_proc (coreaudio_driver_t *this,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ unsigned int inBusNumber,
+ unsigned int inNumberFrames,
+ AudioBufferList * ioData) {
+ int32_t i = 0;
+ int32_t req_size = 0;
+
+ for (i = 0; i < ioData->mNumberBuffers; i++)
+ req_size += ioData->mBuffers[i].mDataByteSize;
+
+ if (this->buf_writepos - this->buf_readpos < req_size) {
+ /* not enough data available, insert the sound of silence. */
+ for (i = 0; i < ioData->mNumberBuffers; i++)
+ memset (ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize);
+ return noErr;
+ }
+
+ pthread_mutex_lock (&this->mutex);
+
+ for (i = 0; i < ioData->mNumberBuffers; i++) {
+ memcpy (ioData->mBuffers[i].mData, &this->buf[this->buf_readpos],
+ ioData->mBuffers[i].mDataByteSize);
+ this->buf_readpos += ioData->mBuffers[i].mDataByteSize;
+ }
+
+ pthread_mutex_unlock (&this->mutex);
+
+ return noErr;
+}
+
+/*
+ * open the audio device for writing to
+ */
+static int ao_coreaudio_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode)
+{
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+ unsigned int err;
+ /* CoreAudio and AudioUnit related stuff */
+ AURenderCallbackStruct input;
+ AudioStreamBasicDescription format;
+ AudioUnitConnection connection;
+ ComponentDescription desc;
+
+ this->sample_rate = rate;
+ this->bits_per_sample = bits;
+ this->num_channels = 2;
+ this->capabilities = AO_CAP_16BITS | AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL;
+ this->bytes_per_frame = 4;
+ this->buf_readpos = 0;
+ this->buf_writepos = 0;
+ pthread_mutex_init (&this->mutex, NULL);
+
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_coreaudio_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
+
+ /* find an audio output unit */
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ this->au_component = FindNextComponent (NULL, &desc);
+
+ if (this->au_component == NULL) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_coreaudio_out: Unable to find a usable audio output unit component\n");
+ return 0;
+ }
+
+ OpenAComponent (this->au_component, &this->au_unit);
+
+ /* find a converter unit */
+ desc.componentType = kAudioUnitType_FormatConverter;
+ desc.componentSubType = kAudioUnitSubType_AUConverter;
+
+ this->converter_component = FindNextComponent (NULL, &desc);
+
+ if (this->converter_component == NULL) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_coreaudio_out: Unable to find a usable audio converter unit component\n");
+ return 0;
+ }
+
+ OpenAComponent (this->converter_component, &this->converter_unit);
+
+ /* set up the render procedure */
+ input.inputProc = (AURenderCallback) ao_coreaudio_render_proc;
+ input.inputProcRefCon = this;
+
+ AudioUnitSetProperty (this->converter_unit,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Input,
+ 0, &input, sizeof(input));
+
+ /* connect the output unit to the audio output unit */
+ connection.sourceAudioUnit = this->converter_unit;
+ connection.sourceOutputNumber = 0;
+ connection.destInputNumber = 0;
+ AudioUnitSetProperty (this->au_unit,
+ kAudioUnitProperty_MakeConnection,
+ kAudioUnitScope_Input, 0,
+ &connection, sizeof(connection));
+
+ /* set up the audio format we want to use */
+ format.mSampleRate = rate;
+ format.mFormatID = kAudioFormatLinearPCM;
+ format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
+ | kLinearPCMFormatFlagIsBigEndian
+ | kLinearPCMFormatFlagIsPacked;
+ format.mBytesPerPacket = 4;
+ format.mFramesPerPacket = 1;
+ format.mBytesPerFrame = 4;
+ format.mChannelsPerFrame = 2;
+ format.mBitsPerChannel = bits;
+
+ AudioUnitSetProperty (this->converter_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ 0, &format, sizeof (format));
+
+ /* boarding completed, now initialize and start the units... */
+ err = AudioUnitInitialize (this->converter_unit);
+ if (err) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_coreaudio_out: failed to AudioUnitInitialize(converter_unit)\n");
+ exit (1);
+ return 0;
+ }
+
+ err = AudioUnitInitialize (this->au_unit);
+ if (err) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_coreaudio_out: failed to AudioUnitInitialize(au_unit)\n");
+ exit (1);
+ return 0;
+ }
+
+ err = AudioOutputUnitStart (this->au_unit);
+ if (err) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_coreaudio_out: failed to AudioOutputUnitStart(au_unit)\n");
+ exit (1);
+ return 0;
+ }
+
+ return rate;
+}
+
+
+static int ao_coreaudio_num_channels(ao_driver_t *this_gen)
+{
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+ return this->num_channels;
+}
+
+static int ao_coreaudio_bytes_per_frame(ao_driver_t *this_gen)
+{
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+ return this->bytes_per_frame;
+}
+
+static int ao_coreaudio_get_gap_tolerance (ao_driver_t *this_gen)
+{
+ return GAP_TOLERANCE;
+}
+
+static int ao_coreaudio_write(ao_driver_t *this_gen, int16_t *data,
+ uint32_t num_frames)
+{
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+
+ pthread_mutex_lock (&this->mutex);
+
+ if (this->buf_readpos > BUFSIZE / 2) {
+ memmove (this->buf, &this->buf[this->buf_readpos],
+ (this->buf_writepos - this->buf_readpos));
+ this->buf_writepos -= this->buf_readpos;
+ this->buf_readpos = 0;
+ }
+
+ if (this->buf_writepos + (num_frames * this->bytes_per_frame) > BUFSIZE) {
+ /* buffer overflow */
+ printf ("CoreAudio: audio buffer overflow!\n");
+ pthread_mutex_unlock (&this->mutex);
+ return 1;
+ }
+
+ memcpy (&this->buf[this->buf_writepos], data, num_frames * this->bytes_per_frame);
+ this->buf_writepos += num_frames * this->bytes_per_frame;
+
+ pthread_mutex_unlock (&this->mutex);
+
+ return 1;
+}
+
+
+static int ao_coreaudio_delay (ao_driver_t *this_gen)
+{
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+ return (this->buf_writepos - this->buf_readpos) / this->bytes_per_frame;
+}
+
+static void ao_coreaudio_close(ao_driver_t *this_gen)
+{
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+
+ if (this->au_unit) {
+ AudioOutputUnitStop (this->au_unit);
+ AudioUnitUninitialize (this->au_unit);
+ this->au_unit = 0;
+ }
+
+ if (this->converter_unit) {
+ AudioUnitUninitialize (this->converter_unit);
+ this->converter_unit = 0;
+ }
+
+ if (this->au_component) {
+ CloseAComponent (this->au_component);
+ this->au_component = NULL;
+ }
+
+ if (this->converter_component) {
+ CloseAComponent (this->converter_component);
+ this->converter_component = NULL;
+ }
+}
+
+static uint32_t ao_coreaudio_get_capabilities (ao_driver_t *this_gen) {
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+ return this->capabilities;
+}
+
+static void ao_coreaudio_exit(ao_driver_t *this_gen)
+{
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+
+ ao_coreaudio_close(this_gen);
+
+ free (this);
+}
+
+static int ao_coreaudio_get_property (ao_driver_t *this_gen, int property) {
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+ Float32 val;
+
+ switch(property) {
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+ AudioUnitGetParameter (this->au_unit,
+ kHALOutputParam_Volume,
+ kAudioUnitScope_Output,
+ 0, &val);
+ return (int) (val * 100);
+ }
+
+ return 0;
+}
+
+static int ao_coreaudio_set_property (ao_driver_t *this_gen, int property, int value) {
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+ Float32 val;
+
+ switch(property) {
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+ val = value / 100.0;
+ AudioUnitSetParameter (this->au_unit,
+ kHALOutputParam_Volume,
+ kAudioUnitScope_Output,
+ 0, val, 0);
+ return value;
+ }
+
+ return ~value;
+}
+
+static int ao_coreaudio_ctrl(ao_driver_t *this_gen, int cmd, ...) {
+ coreaudio_driver_t *this = (coreaudio_driver_t *) this_gen;
+
+ switch (cmd) {
+
+ case AO_CTRL_PLAY_PAUSE:
+ AudioOutputUnitStop (this->au_unit);
+ break;
+
+ case AO_CTRL_PLAY_RESUME:
+ AudioOutputUnitStart (this->au_unit);
+ break;
+
+ case AO_CTRL_FLUSH_BUFFERS:
+ this->buf_readpos = 0;
+ this->buf_writepos = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
+ const void *data) {
+
+ coreaudio_class_t *class = (coreaudio_class_t *) class_gen;
+ /* config_values_t *config = class->config; */
+ coreaudio_driver_t *this;
+
+ lprintf ("open_plugin called\n");
+
+ this = (coreaudio_driver_t *) xine_xmalloc (sizeof (coreaudio_driver_t));
+
+ this->xine = class->xine;
+ this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO;
+
+ this->sample_rate = 0;
+
+ this->ao_driver.get_capabilities = ao_coreaudio_get_capabilities;
+ this->ao_driver.get_property = ao_coreaudio_get_property;
+ this->ao_driver.set_property = ao_coreaudio_set_property;
+ this->ao_driver.open = ao_coreaudio_open;
+ this->ao_driver.num_channels = ao_coreaudio_num_channels;
+ this->ao_driver.bytes_per_frame = ao_coreaudio_bytes_per_frame;
+ this->ao_driver.delay = ao_coreaudio_delay;
+ this->ao_driver.write = ao_coreaudio_write;
+ this->ao_driver.close = ao_coreaudio_close;
+ this->ao_driver.exit = ao_coreaudio_exit;
+ this->ao_driver.get_gap_tolerance = ao_coreaudio_get_gap_tolerance;
+ this->ao_driver.control = ao_coreaudio_ctrl;
+
+ return &this->ao_driver;
+}
+
+/*
+ * class functions
+ */
+
+static char* get_identifier (audio_driver_class_t *this_gen) {
+ return "coreaudio";
+}
+
+static char* get_description (audio_driver_class_t *this_gen) {
+ return _("xine output plugin for Coreaudio/MacOSX");
+}
+
+static void dispose_class (audio_driver_class_t *this_gen) {
+
+ coreaudio_class_t *this = (coreaudio_class_t *) this_gen;
+
+ free (this);
+}
+
+static void *init_class (xine_t *xine, void *data) {
+
+ coreaudio_class_t *this;
+
+ lprintf ("init class\n");
+
+ this = (coreaudio_class_t *) xine_xmalloc (sizeof (coreaudio_class_t));
+
+ this->driver_class.open_plugin = open_plugin;
+ this->driver_class.get_identifier = get_identifier;
+ this->driver_class.get_description = get_description;
+ this->driver_class.dispose = dispose_class;
+
+ this->config = xine->config;
+ this->xine = xine;
+
+ return this;
+}
+
+static ao_info_t ao_info_coreaudio = {
+ 1
+};
+
+/*
+ * exported plugin catalog entry
+ */
+
+plugin_info_t xine_plugin_info[] = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_AUDIO_OUT, AO_OUT_COREAUDIO_IFACE_VERSION, "coreaudio", XINE_VERSION_CODE, &ao_info_coreaudio, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
+