diff options
-rw-r--r-- | configure.ac | 11 | ||||
-rw-r--r-- | src/audio_out/Makefile.am | 12 | ||||
-rw-r--r-- | src/audio_out/audio_coreaudio_out.c | 480 |
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 } +}; + |