vdr/xine-lib-vdr/src/audio_out Makefile.am Makefile.in audio_alsa_out.c audio_arts_out.c audio_coreaudio_out.c audio_directx_out.c audio_esd_out.c audio_file_out.c audio_irixal_out.c audio_none_out.c audio_oss_out.c audio_polyp_out.c audio_sun_out.c
Darren Salt
pkg-vdr-dvb-changes@lists.alioth.debian.org
Mon, 04 Apr 2005 22:29:24 +0000
Update of /cvsroot/pkg-vdr-dvb/vdr/xine-lib-vdr/src/audio_out
In directory haydn:/tmp/cvs-serv2129/src/audio_out
Added Files:
Makefile.am Makefile.in audio_alsa_out.c audio_arts_out.c
audio_coreaudio_out.c audio_directx_out.c audio_esd_out.c
audio_file_out.c audio_irixal_out.c audio_none_out.c
audio_oss_out.c audio_polyp_out.c audio_sun_out.c
Log Message:
Import of VDR-patched xine-lib.
--- NEW FILE: audio_oss_out.c ---
/*
* Copyright (C) 2000-2003 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
*
[...1147 lines suppressed...]
this->config = xine->config;
this->xine = xine;
return this;
}
static ao_info_t ao_info_oss = {
9 /* less than alsa so xine will use alsa's native interface by default */
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_OUT_OSS_IFACE_VERSION, "oss", XINE_VERSION_CODE, &ao_info_oss, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
--- NEW FILE: audio_polyp_out.c ---
/*
* Copyright (C) 2000-2003 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
*
* $Id: audio_polyp_out.c,v 1.1 2005/04/04 22:29:22 dsalt-guest Exp $
*
* ao plugin for polypaudio:
* http://0pointer.de/lennart/projects/polypaudio/
*
* originally written for polypaudio simple api. Lennart then suggested
* using the async api for better control (such as volume), therefore, a lot
* of this code comes from Lennart's patch to mplayer.
*
*/
#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 <assert.h>
#include <pthread.h>
#include <polyp/polyplib.h>
#include <polyp/polyplib-error.h>
#include <polyp/mainloop.h>
#include "xine_internal.h"
#include "xineutils.h"
#include "audio_out.h"
#include "bswap.h"
#define GAP_TOLERANCE AO_MAX_GAP
typedef struct polyp_driver_s {
ao_driver_t ao_driver;
xine_t *xine;
/** The host to connect to */
char *host;
/** The sink to connect to */
char *sink;
/** Polypaudio playback stream object */
struct pa_stream *stream;
/** Polypaudio connection context */
struct pa_context *context;
/** Main event loop object */
struct pa_mainloop *mainloop;
pa_volume_t volume;
int capabilities;
int mode;
int32_t sample_rate;
uint32_t num_channels;
uint32_t bits_per_sample;
uint32_t bytes_per_frame;
uint32_t frames_written;
pthread_mutex_t lock;
} polyp_driver_t;
typedef struct {
audio_driver_class_t driver_class;
xine_t *xine;
} polyp_class_t;
/** Make sure that the connection context doesn't starve to death */
static void keep_alive(polyp_driver_t *this) {
assert(this->context && this->mainloop);
while (pa_mainloop_iterate(this->mainloop, 0, NULL) > 0);
}
/** Wait until no further actions are pending on the connection context */
static void wait_for_completion(polyp_driver_t *this) {
assert(this->context && this->mainloop);
while (pa_mainloop_deferred_pending(this->mainloop) || pa_context_is_pending(this->context)) {
int r = pa_mainloop_iterate(this->mainloop, 1, NULL);
assert(r >= 0);
}
}
/** Wait until the specified operation completes */
static void wait_for_operation(polyp_driver_t *this, struct pa_operation *o) {
assert(o && this->context && this->mainloop);
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
int r = pa_mainloop_iterate(this->mainloop, 1, NULL);
assert(r >= 0);
}
pa_operation_unref(o);
}
/*
* open the audio device for writing to
*/
static int ao_polyp_open(ao_driver_t *this_gen,
uint32_t bits, uint32_t rate, int mode)
{
polyp_driver_t *this = (polyp_driver_t *) this_gen;
struct pa_sample_spec ss;
struct pa_buffer_attr a;
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
"audio_polyp_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
if ( (mode & this->capabilities) == 0 ) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_polyp_out: unsupported mode %08x\n", mode);
return 0;
}
if (this->stream) {
if ( mode == this->mode && rate == this->sample_rate &&
bits == this->bits_per_sample )
return this->sample_rate;
this_gen->close(this_gen);
}
this->mode = mode;
this->sample_rate = rate;
this->bits_per_sample = bits;
this->num_channels = _x_ao_mode2channels( mode );
this->bytes_per_frame = (this->bits_per_sample*this->num_channels)/8;
ss.rate = rate;
ss.channels = this->num_channels;
switch (bits) {
case 8:
ss.format = PA_SAMPLE_U8;
break;
case 16:
#ifdef WORDS_BIGENDIAN
ss.format = PA_SAMPLE_S16BE;
#else
ss.format = PA_SAMPLE_S16LE;
#endif
break;
case 32:
ss.format = PA_SAMPLE_FLOAT32;
break;
}
pthread_mutex_lock(&this->lock);
if (!pa_sample_spec_valid(&ss)) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_polyp_out: Invalid sample spec\n");
goto fail;
}
this->mainloop = pa_mainloop_new();
assert(this->mainloop);
this->context = pa_context_new(pa_mainloop_get_api(this->mainloop), "xine");
assert(this->context);
pa_context_connect(this->context, this->host, 1, NULL);
wait_for_completion(this);
if (pa_context_get_state(this->context) != PA_CONTEXT_READY) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_polyp_out: Failed to connect to server: %s\n",
pa_strerror(pa_context_errno(this->context)));
goto fail;
}
this->stream = pa_stream_new(this->context, "audio stream", &ss);
assert(this->stream);
a.maxlength = pa_bytes_per_second(&ss)*1;
a.tlength = a.maxlength*9/10;
a.prebuf = a.tlength/2;
a.minreq = a.tlength/10;
pa_stream_connect_playback(this->stream, this->sink, &a, PA_STREAM_INTERPOLATE_LATENCY, this->volume);
wait_for_completion(this);
if (pa_stream_get_state(this->stream) != PA_STREAM_READY) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_polyp_out: Failed to connect to server: %s\n",
pa_strerror(pa_context_errno(this->context)));
goto fail;
}
pthread_mutex_unlock(&this->lock);
this->frames_written = 0;
return this->sample_rate;
fail:
pthread_mutex_unlock(&this->lock);
this_gen->close(this_gen);
return 0;
}
static int ao_polyp_num_channels(ao_driver_t *this_gen)
{
polyp_driver_t *this = (polyp_driver_t *) this_gen;
return this->num_channels;
}
static int ao_polyp_bytes_per_frame(ao_driver_t *this_gen)
{
polyp_driver_t *this = (polyp_driver_t *) this_gen;
return this->bytes_per_frame;
}
static int ao_polyp_get_gap_tolerance (ao_driver_t *this_gen)
{
return GAP_TOLERANCE;
}
static int ao_polyp_write(ao_driver_t *this_gen, int16_t *data,
uint32_t num_frames)
{
polyp_driver_t *this = (polyp_driver_t *) this_gen;
int size = num_frames * this->bytes_per_frame;
int ret = 0;
assert(this->stream && this->context);
pthread_mutex_lock(&this->lock);
if (pa_stream_get_state(this->stream) == PA_STREAM_READY) {
while (size > 0) {
size_t l;
keep_alive(this);
while (!(l = pa_stream_writable_size(this->stream))) {
pthread_mutex_unlock(&this->lock);
xine_usec_sleep (10000);
pthread_mutex_lock(&this->lock);
keep_alive(this);
}
if (l > size)
l = size;
pa_stream_write(this->stream, data, l, NULL, 0);
data = (int16_t *) ((uint8_t*) data + l);
size -= l;
wait_for_completion(this);
}
this->frames_written += num_frames;
if (pa_stream_get_state(this->stream) == PA_STREAM_READY)
ret = 1;
}
pthread_mutex_unlock(&this->lock);
return ret;
}
static int ao_polyp_delay (ao_driver_t *this_gen)
{
polyp_driver_t *this = (polyp_driver_t *) this_gen;
pa_usec_t latency;
int delay_frames;
pthread_mutex_lock(&this->lock);
keep_alive(this);
latency = pa_stream_get_interpolated_latency(this->stream, NULL);
pthread_mutex_unlock(&this->lock);
/* convert latency (us) to frame units. */
delay_frames = (int)(latency * this->sample_rate / 1000000);
if( delay_frames > this->frames_written )
return this->frames_written;
else
return delay_frames;
}
static void ao_polyp_close(ao_driver_t *this_gen)
{
polyp_driver_t *this = (polyp_driver_t *) this_gen;
pthread_mutex_lock(&this->lock);
if (this->stream) {
if (pa_stream_get_state(this->stream) == PA_STREAM_READY)
wait_for_operation(this, pa_stream_drain(this->stream, NULL, NULL));
pa_stream_disconnect(this->stream);
pa_stream_unref(this->stream);
this->stream = NULL;
}
if (this->context) {
pa_context_disconnect(this->context);
pa_context_unref(this->context);
this->context = NULL;
}
if (this->mainloop) {
pa_mainloop_free(this->mainloop);
this->mainloop = NULL;
}
pthread_mutex_unlock(&this->lock);
}
static uint32_t ao_polyp_get_capabilities (ao_driver_t *this_gen) {
polyp_driver_t *this = (polyp_driver_t *) this_gen;
return this->capabilities;
}
static void ao_polyp_exit(ao_driver_t *this_gen)
{
polyp_driver_t *this = (polyp_driver_t *) this_gen;
free (this);
}
/** A callback function that is called when the
* pa_context_get_sink_input_info() operation completes. Saves the
* volume field of the specified structure to the global variable volume. */
static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
polyp_driver_t *this = (polyp_driver_t *) userdata;
if (is_last < 0) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_polyp_out: Failed to get sink input info: %s\n",
pa_strerror(pa_context_errno(this->context)));
return;
}
if (!i)
return;
this->volume = i->volume;
}
static int ao_polyp_get_property (ao_driver_t *this_gen, int property) {
polyp_driver_t *this = (polyp_driver_t *) this_gen;
switch(property) {
case AO_PROP_PCM_VOL:
case AO_PROP_MIXER_VOL:
pthread_mutex_lock(&this->lock);
if( this->stream && this->context )
wait_for_operation(this,
pa_context_get_sink_input_info(this->context, pa_stream_get_index(this->stream), info_func, this));
pthread_mutex_unlock(&this->lock);
return (int) (pa_volume_to_user(this->volume)*100);
break;
case AO_PROP_MUTE_VOL:
break;
}
return 0;
}
static int ao_polyp_set_property (ao_driver_t *this_gen, int property, int value) {
polyp_driver_t *this = (polyp_driver_t *) this_gen;
switch(property) {
case AO_PROP_PCM_VOL:
case AO_PROP_MIXER_VOL:
pthread_mutex_lock(&this->lock);
this->volume = pa_volume_from_user((double)value/100);
if( this->stream && this->context )
wait_for_operation(this,
pa_context_set_sink_input_volume(this->context, pa_stream_get_index(this->stream),
this->volume, NULL, NULL));
pthread_mutex_unlock(&this->lock);
break;
case AO_PROP_MUTE_VOL:
break;
}
return 0;
}
static int ao_polyp_ctrl(ao_driver_t *this_gen, int cmd, ...) {
polyp_driver_t *this = (polyp_driver_t *) this_gen;
pthread_mutex_lock(&this->lock);
switch (cmd) {
case AO_CTRL_PLAY_PAUSE:
assert(this->stream && this->context );
if(pa_stream_get_state(this->stream) == PA_STREAM_READY)
wait_for_operation(this,pa_stream_cork(this->stream, 1, NULL, NULL));
break;
case AO_CTRL_PLAY_RESUME:
assert(this->stream && this->context);
if(pa_stream_get_state(this->stream) == PA_STREAM_READY) {
struct pa_operation *o2, *o1;
o1 = pa_stream_prebuf(this->stream, NULL, NULL);
o2 = pa_stream_cork(this->stream, 0, NULL, NULL);
assert(o1 && o2);
wait_for_operation(this,o1);
wait_for_operation(this,o2);
wait_for_completion(this);
}
break;
case AO_CTRL_FLUSH_BUFFERS:
assert(this->stream && this->context);
if(pa_stream_get_state(this->stream) == PA_STREAM_READY)
wait_for_operation(this,pa_stream_flush(this->stream, NULL, NULL));
this->frames_written = 0;
break;
}
pthread_mutex_unlock(&this->lock);
return 0;
}
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data) {
polyp_class_t *class = (polyp_class_t *) class_gen;
polyp_driver_t *this;
char hn[128];
char *device;
lprintf ("audio_polyp_out: open_plugin called\n");
this = (polyp_driver_t *) xine_xmalloc (sizeof (polyp_driver_t));
this->xine = class->xine;
/*
* set capabilities
*/
this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL |
AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL | AO_CAP_8BITS |
AO_CAP_16BITS | AO_CAP_FLOAT32;
this->sample_rate = 0;
this->volume = PA_VOLUME_NORM;
this->ao_driver.get_capabilities = ao_polyp_get_capabilities;
this->ao_driver.get_property = ao_polyp_get_property;
this->ao_driver.set_property = ao_polyp_set_property;
this->ao_driver.open = ao_polyp_open;
this->ao_driver.num_channels = ao_polyp_num_channels;
this->ao_driver.bytes_per_frame = ao_polyp_bytes_per_frame;
this->ao_driver.delay = ao_polyp_delay;
this->ao_driver.write = ao_polyp_write;
this->ao_driver.close = ao_polyp_close;
this->ao_driver.exit = ao_polyp_exit;
this->ao_driver.get_gap_tolerance = ao_polyp_get_gap_tolerance;
this->ao_driver.control = ao_polyp_ctrl;
device = this->xine->config->register_string(this->xine->config,
"audio.polypaudio_device",
"",
_("device used for polypaudio"),
_("use 'server[:sink]' for setting the "
"polypaudio sink device."),
10, NULL,
NULL);
if (device && strlen(device)) {
int i = strcspn(device, ":");
if (i >= sizeof(hn))
i = sizeof(hn)-1;
if (i > 0) {
strncpy(this->host = hn, device, i);
hn[i] = 0;
}
if (device[i] == ':')
this->sink = device+i+1;
}
xprintf (class->xine, XINE_VERBOSITY_DEBUG, "audio_polyp_out: host %s sink %s\n",
this->host ? this->host : "(null)", this->sink ? this->sink : "(null)");
pthread_mutex_init (&this->lock, NULL);
/* test polypaudio connection */
if( this->ao_driver.open(&this->ao_driver, 16, 44100, AO_CAP_MODE_STEREO) != 0 ) {
this->ao_driver.close(&this->ao_driver);
} else {
free(this);
xprintf (class->xine, XINE_VERBOSITY_DEBUG, "audio_polyp_out: open_plugin failed.\n");
return NULL;
}
return &this->ao_driver;
}
/*
* class functions
*/
static char* get_identifier (audio_driver_class_t *this_gen) {
return "polypaudio";
}
static char* get_description (audio_driver_class_t *this_gen) {
return _("xine audio output plugin using polypaudio sound server");
}
static void dispose_class (audio_driver_class_t *this_gen) {
polyp_class_t *this = (polyp_class_t *) this_gen;
free (this);
}
static void *init_class (xine_t *xine, void *data) {
polyp_class_t *this;
lprintf ("audio_polyp_out: init class\n");
this = (polyp_class_t *) xine_xmalloc (sizeof (polyp_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->xine = xine;
return this;
}
static ao_info_t ao_info_polyp = {
6
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, 8, "polypaudio", XINE_VERSION_CODE, &ao_info_polyp, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
--- NEW FILE: audio_sun_out.c ---
/*
* Copyright (C) 2001-2003 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
*
[...969 lines suppressed...]
this->xine = xine;
return this;
}
static ao_info_t ao_info_sun = {
10
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_SUN_IFACE_VERSION, "sun", XINE_VERSION_CODE, &ao_info_sun, ao_sun_init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
--- NEW FILE: audio_directx_out.c ---
/*
* Copyright (C) 2001-2003 the xine project
*
* This file is part of xine, a unix 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
*
* audio_directx_out.c, direct sound audio output plugin for xine
* by Matthew Grooms <elon@altavista.com>
*
* $Id: audio_directx_out.c,v 1.1 2005/04/04 22:29:22 dsalt-guest Exp $
*/
/*
* TODO:
* - stop looping on stop (requires AO_CTRL_PLAY_STOP?)
* - posibility of bad state when buffer overrun
*/
typedef unsigned char boolean;
#include <windows.h>
#include <dsound.h>
#define LOG_MODULE "audio_directx_out"
#define LOG_VERBOSE
/*
#define LOG
*/
#include "audio_out.h"
#include "xine_internal.h"
#define MAX_CHANNELS 6
#define MAX_BITS 16
#define MAX_SAMPLE_RATE 44100
#define SOUND_BUFFER_DIV 32
#define SOUND_BUFFER_MAX MAX_CHANNELS * (MAX_BITS / 8) * (((MAX_SAMPLE_RATE / SOUND_BUFFER_DIV) + 1) & ~1)
#define DSBUFF_INIT 0
#define DSBUFF_LEFT 1
#define DSBUFF_RIGHT 2
#define AO_DIRECTX_IFACE_VERSION 8
/*****************************************************************************
* DirectDraw GUIDs.
* Defining them here allows us to get rid of the dxguid library during
* the linking stage.
*****************************************************************************/
#if 1
static const GUID IID_IDirectSoundNotify = {
0xB0210783,0x89CD,0x11D0,{0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16}
};
#endif
/* -----------------------------------------
*
* ao_directx driver struct
*
* ----------------------------------------- */
typedef struct {
ao_driver_t ao_driver;
int capabilities;
xine_t *xine;
/* directx objects */
LPDIRECTSOUND dsobj;
LPDIRECTSOUNDBUFFER dsbuffer;
DSBCAPS dsbcaps;
LPDIRECTSOUNDNOTIFY notify;
DSBPOSITIONNOTIFY notify_events[ 2 ];
/* buffer vars */
long buffer_size;
int write_status;
unsigned long write_pos;
uint8_t prebuff[ SOUND_BUFFER_MAX ];
uint32_t prebuff_size;
/* current buffer properties */
int bits;
int rate;
int chnn;
int frsz;
/* current mixer settings */
int mute;
int volume;
} ao_directx_t;
typedef struct {
audio_driver_class_t driver_class;
config_values_t *config;
xine_t *xine;
} audiox_class_t;
/* -------------------------------------------
*
* BEGIN : Direct Sound and win32 handlers
* for xine audio output plugins.
*
* ------------------------------------------- */
void Error( HWND hwnd, LPSTR szfmt, ... );
boolean CreateDirectSound( ao_directx_t * ao_directx );
void DestroyDirectSound( ao_directx_t * ao_directx );
boolean CreateSoundBuffer( ao_directx_t * ao_directx );
void DestroySoundBuffer( ao_directx_t * ao_directx );
uint32_t FillSoundBuffer( ao_directx_t * ao_directx, int code, unsigned char * samples );
/* Display formatted error message in
* popup message box. */
void Error( HWND hwnd, LPSTR szfmt, ... )
{
char tempbuff[ 256 ];
*tempbuff = 0;
wvsprintf( &tempbuff[ strlen( tempbuff ) ], szfmt, ( char * )( &szfmt + 1 ) );
MessageBox( hwnd, tempbuff, "Error", MB_ICONERROR | MB_OK | MB_APPLMODAL | MB_SYSTEMMODAL );
}
/* Create our direct sound object and
* set the cooperative level. */
boolean CreateDirectSound( ao_directx_t * ao_directx )
{
DSCAPS dscaps;
HWND hxinewnd;
lprintf("CreateDirectSound(%08x) Enter\n", (unsigned long)ao_directx);
/* create direct sound object */
if( DirectSoundCreate( 0, &ao_directx->dsobj, 0 ) != DS_OK )
{
Error( 0, "DirectSoundCreate : Unable to create direct sound object" );
lprintf("CreateDirectSound() Exit! Returning False\n");
return FALSE;
}
/* try to get our current xine window */
hxinewnd = FindWindow( "xinectrlwindow", "xine" );
if( !hxinewnd )
hxinewnd = GetDesktopWindow();
/* set direct sound cooperative level */
if( IDirectSound_SetCooperativeLevel( ao_directx->dsobj, hxinewnd, DSSCL_EXCLUSIVE ) != DS_OK )
{
Error( 0, "IDirectSound_SetCooperativeLevel : could not set direct sound cooperative level" );
lprintf("CreateDirectSound() Exit! Returning False\n");
return FALSE;
}
/* get the direct sound device caps */
memset( &dscaps, 0, sizeof( dscaps ) );
dscaps.dwSize = sizeof( dscaps );
if( IDirectSound_GetCaps( ao_directx->dsobj, &dscaps ) != DS_OK )
{
Error( 0, "IDirectSound_GetCaps : Unable to get direct sound device capabilities" );
lprintf("CreateDirectSound() Exit! Returning False\n");
return FALSE;
}
lprintf("CreateDirectSound() Exit! Returning True\n");
return TRUE;
}
/* Destroy all direct sound allocated
* resources. */
void DestroyDirectSound( ao_directx_t * ao_directx )
{
lprintf("DestroyDirectSound(%08x) Enter\n", (unsigned long)ao_directx);
if( ao_directx->dsobj )
{
lprintf("IDirectSound_Release()\n");
IDirectSound_Release( ao_directx->dsobj );
ao_directx->dsobj = 0;
}
lprintf("DestroyDirectSound() Exit\n");
}
/* Used to create directx sound buffer,
* notification events, and initialize
* buffer to null sample data. */
boolean CreateSoundBuffer( ao_directx_t * ao_directx )
{
DSBUFFERDESC dsbdesc;
PCMWAVEFORMAT pcmwf;
lprintf("CreateSoundBuffer(%08x) Enter\n", (unsigned long)ao_directx);
/* calculate buffer and frame size */
ao_directx->frsz = ( ao_directx->bits / 8 ) * ao_directx->chnn;
/* buffer size, must be even and aligned to frame size */
ao_directx->buffer_size = (ao_directx->frsz * ((ao_directx->rate / SOUND_BUFFER_DIV + 1) & ~1));
/* release any existing sound buffer
* related resources */
DestroySoundBuffer( ao_directx );
/* create a secondary sound buffer */
memset( &pcmwf, 0, sizeof( PCMWAVEFORMAT ) );
pcmwf.wBitsPerSample = ( unsigned short ) ao_directx->bits;
pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM;
pcmwf.wf.nChannels = ao_directx->chnn;
pcmwf.wf.nSamplesPerSec = ao_directx->rate;
pcmwf.wf.nBlockAlign = ao_directx->frsz;
pcmwf.wf.nAvgBytesPerSec = ao_directx->rate * ao_directx->frsz;
memset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) );
dsbdesc.dwSize = sizeof( DSBUFFERDESC );
dsbdesc.dwFlags = (DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS |
DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY);
dsbdesc.dwBufferBytes = ao_directx->buffer_size;
dsbdesc.lpwfxFormat = ( LPWAVEFORMATEX ) &pcmwf;
if( IDirectSound_CreateSoundBuffer( ao_directx->dsobj, &dsbdesc,
&ao_directx->dsbuffer, 0 ) != DS_OK )
{
Error( 0, "IDirectSound_CreateSoundBuffer : Unable to create secondary sound buffer" );
return FALSE;
}
/* get the buffer capabilities */
memset( &ao_directx->dsbcaps, 0, sizeof( DSBCAPS ) );
ao_directx->dsbcaps.dwSize = sizeof( DSBCAPS );
if( IDirectSound_GetCaps( ao_directx->dsbuffer, &ao_directx->dsbcaps ) != DS_OK )
{
Error( 0, "IDirectSound_GetCaps : Unable to get secondary sound buffer capabilities" );
return FALSE;
}
/* create left side notification ( non-signaled ) */
ao_directx->notify_events[ 0 ].hEventNotify = CreateEvent( NULL, FALSE, FALSE, NULL );
/* create right side notification ( signaled ) */
ao_directx->notify_events[ 1 ].hEventNotify = CreateEvent( NULL, FALSE, FALSE, NULL );
if( !ao_directx->notify_events[ 0 ].hEventNotify || !ao_directx->notify_events[ 1 ].hEventNotify )
{
Error( 0, "CreateEvent : Unable to create sound notification events" );
return FALSE;
}
/* get the direct sound notification interface */
if( IDirectSoundBuffer_QueryInterface( ao_directx->dsbuffer,
&IID_IDirectSoundNotify,
(LPVOID *)&ao_directx->notify ) != DS_OK )
{
Error( 0, "IDirectSoundBuffer_QueryInterface : Unable to get notification interface" );
return FALSE;
}
/* set notification events */
ao_directx->notify_events[ 0 ].dwOffset = 0;
ao_directx->notify_events[ 1 ].dwOffset = ao_directx->buffer_size / 2;
if( IDirectSoundNotify_SetNotificationPositions( ao_directx->notify, 2,
ao_directx->notify_events ) != DS_OK )
{
Error( 0, "IDirectSoundNotify_SetNotificationPositions : Unable to set notification positions" );
return FALSE;
}
/* DEBUG : set sound buffer volume */
if( IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MAX ) != DS_OK )
{
Error( 0, "IDirectSoundBuffer_SetVolume : Unable to set sound buffer volume" );
return FALSE;
}
/* initialize our sound buffer */
IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN );
FillSoundBuffer( ao_directx, DSBUFF_INIT, 0 );
return TRUE;
lprintf("CreateSoundBuffer() Exit\n");
}
/* Destroy all direct sound buffer allocated
* resources. */
void DestroySoundBuffer( ao_directx_t * ao_directx )
{
lprintf("DestroySoundBuffer(%08x) Enter\n", (unsigned long)ao_directx);
/* stop our buffer and zero it out */
if( ao_directx->dsbuffer )
{
IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN );
IDirectSoundBuffer_Stop( ao_directx->dsbuffer );
FillSoundBuffer( ao_directx, DSBUFF_INIT, 0 );
}
/* release our notification events */
if( ao_directx->notify_events[ 0 ].hEventNotify )
{
CloseHandle( ao_directx->notify_events[ 0 ].hEventNotify );
ao_directx->notify_events[ 0 ].hEventNotify = 0;
}
if( ao_directx->notify_events[ 1 ].hEventNotify )
{
CloseHandle( ao_directx->notify_events[ 1 ].hEventNotify );
ao_directx->notify_events[ 1 ].hEventNotify = 0;
}
/* release our buffer notification interface */
if( ao_directx->notify )
{
IDirectSoundNotify_Release( ao_directx->notify );
ao_directx->notify = 0;
}
/* release our direct sound buffer */
if( ao_directx->dsbuffer )
{
IDirectSoundBuffer_Release( ao_directx->dsbuffer );
ao_directx->dsbuffer = 0;
}
lprintf("DestroySoundBuffer() Exit\n");
}
/* Used to fill our looping sound buffer
* with data. */
uint32_t FillSoundBuffer( ao_directx_t * ao_directx, int code, unsigned char * samples )
{
uint8_t * buff_pointer; /* pointer inside circular buffer */
DWORD buff_length; /* bytes locked by pointer */
uint32_t half_size; /* half our sound buffer size */
uint32_t result; /* error result */
#ifdef LOG
if ((void*)samples != (void*)0)
printf("audio_directx_out: FillSoundBuffer(%08x, %d, Null) Enter\n", (unsigned long)ao_directx, code);
else
printf("audio_directx_out: FillSoundBuffer(%08x, %d, Null) Enter\n", (unsigned long)ao_directx, code);
#endif
half_size = ao_directx->buffer_size / 2;
if( code == DSBUFF_INIT )
{
lprintf("FillSoundBuffer: DSBUFF_INIT\n");
/* set our new status code */
ao_directx->write_status = DSBUFF_RIGHT;
/* lock our sound buffer for write access */
result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer,
0, 0,
(LPVOID *)&buff_pointer, &buff_length,
NULL, 0, DSBLOCK_ENTIREBUFFER );
if( result != DS_OK )
{
Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" );
return 0;
}
/* clear our entire sound buffer */
memset( buff_pointer, 0, buff_length );
/* unlock our sound buffer */
if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer,
buff_pointer, buff_length,
0, 0 ) != DS_OK )
{
Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" );
return 0;
}
/* start the buffer playing */
if( IDirectSoundBuffer_Play( ao_directx->dsbuffer, 0, 0, DSBPLAY_LOOPING ) != DS_OK )
{
Error( 0, "IDirectSoundBuffer_Play : could not play sound buffer" );
return 0 ;
}
else
IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume );
}
else if( code == DSBUFF_LEFT )
{
lprintf("FillSoundBuffer: DSBUFF_LEFT\n");
/* set our new status code */
ao_directx->write_status = DSBUFF_RIGHT;
/* lock our sound buffer for write access */
result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer,
0, half_size,
(LPVOID *)&buff_pointer, &buff_length,
0, 0, 0 );
if( result != DS_OK )
{
Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" );
return 0;
}
/* write data to our sound buffer */
memcpy( buff_pointer, samples, buff_length );
/* unlock our sound buffer */
if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer,
buff_pointer, buff_length,
0, 0 ) != DS_OK )
{
Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" );
return 0;
}
}
else if( code == DSBUFF_RIGHT )
{
lprintf("FillSoundBuffer: DSBUFF_RIGHT\n");
/* set our new status code */
ao_directx->write_status = DSBUFF_LEFT;
/* lock our sound buffer for write access */
result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer,
half_size, half_size,
(LPVOID *)&buff_pointer, &buff_length,
0, 0, 0 );
if( result != DS_OK )
{
Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" );
return 0;
}
/* write data to our sound buffer */
memcpy( buff_pointer, samples, buff_length );
/* unlock our sound buffer */
if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer,
buff_pointer, buff_length,
0, 0 ) != DS_OK )
{
Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" );
return 0;
}
}
lprintf("FillSoundBuffer() Exit\n");
return buff_length;
}
/* -----------------------------------------
*
* BEGIN : Xine driver audio output plugin
* handlers.
*
* ----------------------------------------- */
static int ao_directx_control(ao_driver_t *this_gen, int cmd, ...) {
switch (cmd)
{
case AO_CTRL_PLAY_PAUSE:
break;
case AO_CTRL_PLAY_RESUME:
break;
case AO_CTRL_FLUSH_BUFFERS:
break;
}
return 0;
}
static int ao_directx_open( ao_driver_t * ao_driver, uint32_t bits, uint32_t rate, int mode )
{
ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver;
lprintf("ao_directx_open(%08x, %d, %d, %d) Enter\n", (unsigned long)ao_directx, bits, rate, mode);
/* store input rate and bits */
ao_directx->bits = bits;
ao_directx->rate = rate;
/* store channel count */
switch( mode )
{
case AO_CAP_MODE_MONO:
ao_directx->chnn = 1;
xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_MONO mode\n" );
break;
case AO_CAP_MODE_STEREO:
ao_directx->chnn = 2;
xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_STEREO mode\n" );
break;
case AO_CAP_MODE_4CHANNEL:
ao_directx->chnn = 4;
xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_4CHANNEL mode\n" );
break;
case AO_CAP_MODE_5CHANNEL:
ao_directx->chnn = 5;
xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_5CHANNEL mode\n" );
break;
case AO_CAP_MODE_5_1CHANNEL:
ao_directx->chnn = 6;
xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_5_1CHANNEL mode\n" );
break;
case AO_CAP_MODE_A52:
case AO_CAP_MODE_AC5:
return 0;
}
if (!CreateSoundBuffer( ao_directx )) return 0;
lprintf("ao_directx_open() Exit! Returning ao_directx->rate=%d\n", ao_directx->rate);
return ao_directx->rate;
}
static int ao_directx_num_channels( ao_driver_t * ao_driver )
{
ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver;
return ao_directx->chnn;
}
static int ao_directx_bytes_per_frame( ao_driver_t * ao_driver )
{
ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver;
return ao_directx->frsz;
}
static int ao_directx_get_gap_tolerance( ao_driver_t * ao_driver )
{
return 5000;
}
static int ao_directx_delay( ao_driver_t * ao_driver )
{
DWORD current_read;
DWORD bytes_left;
DWORD frames_left;
ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver;
lprintf("ao_directx_delay(%08x) Enter\n", (unsigned long)ao_directx);
IDirectSoundBuffer_GetCurrentPosition( ao_directx->dsbuffer, ¤t_read, 0 );
if( ao_directx->write_pos > current_read )
bytes_left = ( ao_directx->write_pos - current_read );
else
bytes_left = ( ao_directx->write_pos + ao_directx->buffer_size - current_read );
frames_left = ( ao_directx->prebuff_size + bytes_left ) / ao_directx->frsz;
lprintf("ao_directx_delay() Exit! Returning frames_left=%d\n", frames_left);
return frames_left;
}
static int ao_directx_write( ao_driver_t * ao_driver, int16_t * frame_buffer, uint32_t num_frames )
{
ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver;
uint32_t frame_bytes; /* how many bytes to lock */
uint32_t wrote; /* number of bytes written */
uint32_t half_size; /* half our sound buffer size */
lprintf("ao_directx_write(%08x, %08x, %d) Enter\n",
(unsigned long)ao_directx, (unsigned long)frame_buffer, num_frames);
/* zero write counter */
wrote = 0;
/* calculate how many bytes in frame_buffer */
frame_bytes = num_frames * ao_directx->frsz;
/* calculate half our buffer size */
half_size = ao_directx->buffer_size / 2;
/* fill audio prebuff */
memcpy( ao_directx->prebuff + ao_directx->prebuff_size, frame_buffer, frame_bytes );
ao_directx->prebuff_size = ao_directx->prebuff_size + frame_bytes;
/* check to see if we have enough in prebuff to
* fill half of our sound buffer */
while( ao_directx->prebuff_size >= half_size )
{
/* write to our sound buffer */
if( ao_directx->write_status == DSBUFF_LEFT )
{
/* wait for our read pointer to reach the right half
* of our sound buffer, we only want to write to the
* left side */
WaitForSingleObject( ao_directx->notify_events[ 1 ].hEventNotify, INFINITE );
/* fill left half of our buffer */
wrote = FillSoundBuffer( ao_directx, DSBUFF_LEFT, ao_directx->prebuff );
}
else if( ao_directx->write_status == DSBUFF_RIGHT )
{
/* wait for our read pointer to reach the left half,
* of our sound buffer, we only want to write to the
* right side */
WaitForSingleObject( ao_directx->notify_events[ 0 ].hEventNotify, INFINITE );
/* fill right half of our buffer */
wrote = FillSoundBuffer( ao_directx, DSBUFF_RIGHT, ao_directx->prebuff );
}
/* calc bytes written and store position for next write */
ao_directx->write_pos = ( ao_directx->write_pos + wrote ) % ao_directx->buffer_size;
/* copy remaining contents of prebuff and recalc size */
memcpy( ao_directx->prebuff, ao_directx->prebuff + wrote, ao_directx->prebuff_size - wrote );
ao_directx->prebuff_size = ao_directx->prebuff_size - wrote;
}
lprintf("ao_directx_write() Exit! Returning num_frmaes=%d\n", num_frames);
return num_frames;
}
static void ao_directx_close( ao_driver_t * ao_driver )
{
ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver;
lprintf("ao_directx_close(%08x) Enter\n", (unsigned long)ao_directx);
/* release any existing sound buffer
* related resources */
DestroySoundBuffer( ao_directx );
lprintf("ao_directx_close() Exit!\n");
}
static uint32_t ao_directx_get_capabilities( ao_driver_t * ao_driver )
{
return AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL;
}
static void ao_directx_exit( ao_driver_t * ao_driver )
{
ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver;
lprintf("ao_directx_exit(%08x) Enter\n", (unsigned long)ao_directx);
/* release any existing sound buffer
* related resources */
DestroySoundBuffer( ao_directx );
/* release any existing direct sound
* related resources */
DestroyDirectSound( ao_directx );
/* free our driver */
free( ao_directx );
lprintf("ao_directx_exit() Exit!\n");
}
static int ao_directx_get_property( ao_driver_t * ao_driver, int property )
{
return 0;
}
static int ao_directx_set_property( ao_driver_t * ao_driver, int property, int value )
{
ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver;
lprintf("ao_directx_set_property(%08x, %d, %d) Enter\n",
(unsigned long)ao_directx, property, value);
switch( property )
{
case AO_PROP_PCM_VOL:
case AO_PROP_MIXER_VOL:
lprintf("ao_directx_set_property: AO_PROP_PCM_VOL|AO_PROP_MIXER_VOL\n");
ao_directx->volume = value * ( DSBVOLUME_MIN / 100 / 3);
if( !ao_directx->mute && ao_directx->dsbuffer )
IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume );
xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG,
"ao_directx : volume set to %d - directX volume = %d\n", value, ao_directx->volume);
return value;
break;
case AO_PROP_MUTE_VOL:
lprintf("ao_directx_set_property: AO_PROP_MUTE_VOL\n");
ao_directx->mute = value;
if( !ao_directx->mute && ao_directx->dsbuffer )
IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume );
if( ao_directx->mute && ao_directx->dsbuffer )
IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN );
xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : mute toggled" );
return value;
break;
}
lprintf("ao_directx_set_property() Exit! Returning ~value=%d\n", ~value);
return ~value;
}
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data)
{
audiox_class_t *class = (audiox_class_t *) class_gen;
ao_directx_t *ao_directx;
ao_directx = ( ao_directx_t * ) xine_xmalloc( sizeof( ao_directx_t ) );
lprintf("open_plugin(%08x, %08x) Enter\n", (unsigned long)class_gen, (unsigned long)data);
lprintf("open_plugin: ao_directx=%08x\n", (unsigned long)ao_directx);
ao_directx->xine = class->xine;
ao_directx->ao_driver.get_capabilities = ao_directx_get_capabilities;
ao_directx->ao_driver.get_property = ao_directx_get_property;
ao_directx->ao_driver.set_property = ao_directx_set_property;
ao_directx->ao_driver.open = ao_directx_open;
ao_directx->ao_driver.num_channels = ao_directx_num_channels;
ao_directx->ao_driver.bytes_per_frame = ao_directx_bytes_per_frame;
ao_directx->ao_driver.delay = ao_directx_delay;
ao_directx->ao_driver.write = ao_directx_write;
ao_directx->ao_driver.close = ao_directx_close;
ao_directx->ao_driver.exit = ao_directx_exit;
ao_directx->ao_driver.get_gap_tolerance = ao_directx_get_gap_tolerance;
ao_directx->ao_driver.control = ao_directx_control;
CreateDirectSound( ao_directx );
lprintf("open_plugin() Exit! Returning ao_directx=%08x\n", (unsigned long)ao_directx);
return ( ao_driver_t * ) ao_directx;
}
static char* get_identifier (audio_driver_class_t *this_gen) {
return "DirectX";
}
static char* get_description (audio_driver_class_t *this_gen) {
return _("xine audio output plugin for win32 using directx");
}
static void dispose_class (audio_driver_class_t *this_gen) {
audiox_class_t *audiox = (audiox_class_t *) this_gen;
free (audiox);
}
static void *init_class (xine_t *xine, void *data) {
audiox_class_t *audiox;
lprintf("init_class() Enter\n");
/*
* from this point on, nothing should go wrong anymore
*/
audiox = (audiox_class_t *) xine_xmalloc (sizeof (audiox_class_t));
audiox->driver_class.open_plugin = open_plugin;
audiox->driver_class.get_identifier = get_identifier;
audiox->driver_class.get_description = get_description;
audiox->driver_class.dispose = dispose_class;
audiox->xine = xine;
audiox->config = xine->config;
lprintf("init_class() Exit! Returning audiox=%08x\n", audiox);
return audiox;
}
static ao_info_t ao_info_directx = {
1 /* priority */
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_DIRECTX_IFACE_VERSION, "directx", XINE_VERSION_CODE, &ao_info_directx, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
--- NEW FILE: audio_irixal_out.c ---
/*
* Copyright (C) 2000-2003 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
*
* $Id: audio_irixal_out.c,v 1.1 2005/04/04 22:29:22 dsalt-guest Exp $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#warning DISABLED: FIXME
#if 0
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <math.h>
#include <sys/ioctl.h>
#include <inttypes.h>
#include <dmedia/audio.h>
#include "xine_internal.h"
#include "xineutils.h"
#include "compat.h"
#include "audio_out.h"
//#ifndef AFMT_S16_NE
//# if defined(sparc) || defined(__sparc__) || defined(PPC)
///* Big endian machines */
//# define AFMT_S16_NE AFMT_S16_BE
//# else
//# define AFMT_S16_NE AFMT_S16_LE
//# endif
//#endif
#define AO_IRIXAL_IFACE_VERSION 4
#define DEFAULT_GAP_TOLERANCE 5000
typedef struct irixal_driver_s {
ao_driver_t ao_driver;
ALport port;
int capabilities;
int open_mode;
int gap_tolerance;
int32_t output_sample_rate, input_sample_rate;
uint32_t num_channels;
uint32_t bits_per_sample;
uint32_t bytes_per_frame;
stamp_t frames_in_buffer; /* number of frames writen to audio hardware */
} irixal_driver_t;
// static snd_output_t *jcd_out;
/*
* open the audio device for writing to
*/
static int ao_irixal_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode)
{
irixal_driver_t *this = (irixal_driver_t *) this_gen;
int resource;
ALconfig config;
ALpv parvalue;
/*
* Init config for audio port
*/
switch (mode) {
case AO_CAP_MODE_MONO:
this->num_channels = 1;
break;
case AO_CAP_MODE_STEREO:
this->num_channels = 2;
break;
/* not tested so far (missing an Onyx with multichannel output...) */
case AO_CAP_MODE_4CHANNEL:
this->num_channels = 4;
break;
#if 0
/* unsupported so far */
case AO_CAP_MODE_5CHANNEL:
this->num_channels = 5;
break;
case AO_CAP_MODE_5_1CHANNEL:
this->num_channels = 6;
break;
case AO_CAP_MODE_A52:
this->num_channels = 2;
break;
#endif
default:
xlerror ("irixal Driver does not support the requested mode: 0x%x",mode);
return 0;
}
if (! (config = alNewConfig ()))
{
xlerror ("cannot get new config: %s", strerror (oserror()));
return 0;
}
if ( (alSetChannels (config, this->num_channels)) == -1)
{
xlerror ("cannot set to %d channels: %s", this->num_channels, strerror (oserror()));
alFreeConfig (config);
return 0;
}
switch (bits) {
case 8:
if ( (alSetWidth (config, AL_SAMPLE_8)) == -1)
{
xlerror ("cannot set 8bit mode: %s", strerror (oserror()));
alFreeConfig (config);
return 0;
}
break;
case 16:
/* Default format is 16bit PCM */
break;
default:
xlerror ("irixal Driver does not support %dbit audio", bits);
alFreeConfig (config);
return 0;
}
printf("audio_irixal_out: channels=%d, bits=%d\n", this->num_channels, bits);
/*
* Try to open audio port
*/
if (! (this->port = alOpenPort ("xine", "w", config))) {
xlerror ("irixal Driver does not support the audio configuration");
alFreeConfig (config);
return 0;
}
alFreeConfig (config);
resource = alGetResource (this->port);
this->open_mode = mode;
this->input_sample_rate = rate;
this->bits_per_sample = bits;
/* FIXME: Can use an irixal function here ?!? */
this->bytes_per_frame = (this->bits_per_sample*this->num_channels) / 8;
this->frames_in_buffer = 0;
/* TODO: not yet settable (see alParams (3dm)): AL_INTERFACE, AL_CLOCK_GEN */
/*
* Try to adapt sample rate of audio port
*/
parvalue.param = AL_MASTER_CLOCK;
parvalue.value.i = AL_CRYSTAL_MCLK_TYPE;
if (alSetParams (resource, &parvalue, 1) == -1)
printf ("audio_irixal: FYI: cannot set audio master clock to crystal based clock\n");
parvalue.param = AL_RATE;
parvalue.value.ll = alIntToFixed (rate);
if (alSetParams (resource, &parvalue, 1) == -1)
printf ("audio_irixal: FYI: cannot set sample rate, using software resampling\n");
if (alGetParams (resource, &parvalue, 1) == -1)
{
xlerror ("cannot ask for current sample rate, assuming everything worked...");
this->output_sample_rate = this->input_sample_rate;
}
else
this->output_sample_rate = alFixedToInt (parvalue.value.ll);
if (this->input_sample_rate != this->output_sample_rate)
printf ("audio_irixal: FYI: sample_rate in %d, out %d\n",
this->input_sample_rate, this->output_sample_rate);
return this->output_sample_rate;
}
static int ao_irixal_num_channels(ao_driver_t *this_gen)
{
irixal_driver_t *this = (irixal_driver_t *) this_gen;
return this->num_channels;
}
static int ao_irixal_bytes_per_frame(ao_driver_t *this_gen)
{
irixal_driver_t *this = (irixal_driver_t *) this_gen;
return this->bytes_per_frame;
}
static int ao_irixal_get_gap_tolerance (ao_driver_t *this_gen)
{
irixal_driver_t *this = (irixal_driver_t *) this_gen;
return this->gap_tolerance;
}
static int ao_irixal_delay (ao_driver_t *this_gen)
{
irixal_driver_t *this = (irixal_driver_t *) this_gen;
stamp_t stamp, time;
int frames_left;
if (alGetFrameTime (this->port, &stamp, &time) == -1)
xlerror ("alGetFrameNumber failed");
frames_left = this->frames_in_buffer - stamp;
if (frames_left <= 0) /* buffer ran dry */
frames_left = 0;
return frames_left;
}
static int ao_irixal_write(ao_driver_t *this_gen,int16_t *data, uint32_t num_frames)
{
irixal_driver_t *this = (irixal_driver_t *) this_gen;
stamp_t stamp;
/* Grmbf. IRIX audio does not tell us, wenn we run dry.
* We have to detect this ourself. */
/* get absolute number of samples played so far
* note: this counts up when run dry as well... */
if (alGetFrameNumber (this->port, &stamp) == -1)
xlerror ("alGetFrameNumber failed");
if (this->frames_in_buffer < stamp) /* dry run */
{
if (this->frames_in_buffer > 0)
printf ("audio_irixal: audio buffer dry run detected, buffer %llu should be > %llu!\n",
this->frames_in_buffer, stamp);
this->frames_in_buffer = stamp;
}
/* FIXME: what to do when the call would block?
* We have to write things out anyway...
* alGetFillable() would tell us, whether space was available */
alWriteFrames (this->port, data, num_frames);
this->frames_in_buffer += num_frames;
return num_frames;
}
static void ao_irixal_close(ao_driver_t *this_gen)
{
irixal_driver_t *this = (irixal_driver_t *) this_gen;
if (this->port)
alClosePort (this->port);
this->port = NULL;
}
static uint32_t ao_irixal_get_capabilities (ao_driver_t *this_gen) {
irixal_driver_t *this = (irixal_driver_t *) this_gen;
return this->capabilities;
}
static void ao_irixal_exit(ao_driver_t *this_gen)
{
irixal_driver_t *this = (irixal_driver_t *) this_gen;
ao_irixal_close (this_gen);
free (this);
}
static int ao_irixal_get_property (ao_driver_t *this, int property) {
/* FIXME: implement some properties */
return 0;
}
/*
*
*/
static int ao_irixal_set_property (ao_driver_t *this, int property, int value) {
/* FIXME: Implement property support */
return ~value;
}
/*
*
*/
static int ao_irixal_ctrl(ao_driver_t *this_gen, int cmd, ...) {
irixal_driver_t *this = (irixal_driver_t *) this_gen;
switch (cmd) {
case AO_CTRL_PLAY_PAUSE:
break;
case AO_CTRL_PLAY_RESUME:
break;
case AO_CTRL_FLUSH_BUFFERS:
break;
}
return 0;
}
static void *init_audio_out_plugin (config_values_t *config)
{
irixal_driver_t *this;
ALvalue values [32];
ALpv parvalue;
char name[32];
int i, numvalues;
int useresource = -1;
printf ("audio_irixal: init...\n");
/* Check available outputs */
/* TODO: this is verbose information only right now, output is not selectable */
if ( (numvalues = alQueryValues (AL_SYSTEM, AL_DEFAULT_OUTPUT, values, 32, NULL, 0)) > 0)
{
useresource = values [0].i;
for (i = 0; i < numvalues; i++)
{
parvalue.param = AL_NAME;
parvalue.value.ptr = name;
parvalue.sizeIn = 32;
if (alGetParams (values [i].i, &parvalue, 1) != -1)
printf (" available Output: %s\n", name);
}
}
if (useresource == -1)
{
xlerror ("cannot find output resource");
return NULL;
}
#if 0
/* TODO */
device = config->lookup_str(config,"irixal_default_device", "default");
#endif
/* allocate struct */
this = (irixal_driver_t *) calloc (sizeof (irixal_driver_t), 1);
/* get capabilities */
if ( (numvalues = alQueryValues (useresource, AL_CHANNELS, values, 32, NULL, 0)) > 0)
{
for (i = 0; i < numvalues; i++)
{
switch (values[i].i) {
case 1:
this->capabilities |= AO_CAP_MODE_MONO;
break;
case 2:
this->capabilities |= AO_CAP_MODE_STEREO;
break;
/* not tested so far (missing an Onyx with multichannel output...) */
case 4:
this->capabilities |= AO_CAP_MODE_4CHANNEL;
break;
#if 0
/* unsupported so far */
case AO_CAP_MODE_5CHANNEL:
case AO_CAP_MODE_5_1CHANNEL:
case AO_CAP_MODE_A52:
#endif
default:
printf (" unsupported %d channel config available on system\n", values[i].i);
}
}
}
printf (" capabilities 0x%X\n",this->capabilities);
/* TODO: anything can change during runtime... move check to the right location */
this->gap_tolerance = config->register_range (config, "audio.device.irixal_gap_tolerance",
DEFAULT_GAP_TOLERANCE, 0, 90000,
_("irixal audio output maximum gap length"),
_("You can specify the maximum offset between audio "
"and video xine will tolerate before trying to "
"resync them.\nThe unit of this value is one PTS tick, "
"which is the 90000th part of a second."),
30, NULL, NULL);
this->ao_driver.get_capabilities = ao_irixal_get_capabilities;
this->ao_driver.get_property = ao_irixal_get_property;
this->ao_driver.set_property = ao_irixal_set_property;
this->ao_driver.open = ao_irixal_open;
this->ao_driver.num_channels = ao_irixal_num_channels;
this->ao_driver.bytes_per_frame = ao_irixal_bytes_per_frame;
this->ao_driver.delay = ao_irixal_delay;
this->ao_driver.write = ao_irixal_write;
this->ao_driver.close = ao_irixal_close;
this->ao_driver.exit = ao_irixal_exit;
this->ao_driver.get_gap_tolerance = ao_irixal_get_gap_tolerance;
this->ao_driver.control = ao_irixal_ctrl;
return this;
}
static ao_info_t ao_info_irixal = {
"xine audio output plugin using IRIX libaudio",
10
};
ao_info_t *get_audio_out_plugin_info()
{
ao_info_irixal.description = _("xine audio output plugin using IRIX libaudio");
return &ao_info_irixal;
}
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_OUT_IRIXAL_IFACE_VERSION, "irixal", XINE_VERSION_CODE, &ao_info_irixal, init_audio_out_plugin },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
#endif
--- NEW FILE: audio_esd_out.c ---
/*
* Copyright (C) 2000-2003 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
*
* $Id: audio_esd_out.c,v 1.1 2005/04/04 22:29:22 dsalt-guest Exp $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <esd.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <inttypes.h>
#include "xine_internal.h"
#include "xineutils.h"
#include "audio_out.h"
#include "metronom.h"
#define AO_OUT_ESD_IFACE_VERSION 8
#define REBLOCK 1 /* reblock output to ESD_BUF_SIZE blks */
#define GAP_TOLERANCE 5000
typedef struct esd_driver_s {
ao_driver_t ao_driver;
xine_t *xine;
int audio_fd;
int capabilities;
int mode;
char *pname; /* Player name id for esd daemon */
int32_t output_sample_rate, input_sample_rate;
int32_t output_sample_k_rate;
double sample_rate_factor;
uint32_t num_channels;
uint32_t bytes_per_frame;
uint32_t bytes_in_buffer; /* number of bytes writen to esd */
int gap_tolerance, latency;
int server_sample_rate;
struct timeval start_time;
struct {
int source_id;
int volume;
int mute;
} mixer;
#if REBLOCK
/*
* Temporary sample buffer used to reblock the sample output stream
* to writes using buffer sizes of n*ESD_BUF_SIZE bytes.
*
* The reblocking avoids a bug with esd 0.2.18 servers and reduces
* cpu load with newer versions of the esd server.
*
* The esd 0.2.18 version zero fills "partial"/"incomplete" blocks.
* esd 0.2.28+ has fixed this problem, by performing a busy polling
* loop reading from a nonblocking socket to get the remainder of
* the partial block. This is wasting a lot of cpu cycles.
*/
char reblock_buf[ESD_BUF_SIZE];
int reblock_rem;
#endif
} esd_driver_t;
typedef struct {
audio_driver_class_t driver_class;
xine_t *xine;
} esd_class_t;
/*
* connect to esd
*/
static int ao_esd_open(ao_driver_t *this_gen,
uint32_t bits, uint32_t rate, int mode)
{
esd_driver_t *this = (esd_driver_t *) this_gen;
esd_format_t format;
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
"audio_esd_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
if ( (mode & this->capabilities) == 0 ) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_esd_out: unsupported mode %08x\n", mode);
return 0;
}
if (this->audio_fd>=0) {
if ( (mode == this->mode) && (rate == this->input_sample_rate) )
return this->output_sample_rate;
esd_close (this->audio_fd);
}
this->mode = mode;
this->input_sample_rate = rate;
this->output_sample_rate = rate;
this->bytes_in_buffer = 0;
this->start_time.tv_sec = 0;
/*
* open stream to ESD server
*/
format = ESD_STREAM | ESD_PLAY | ESD_BITS16;
switch (mode) {
case AO_CAP_MODE_MONO:
format |= ESD_MONO;
this->num_channels = 1;
break;
case AO_CAP_MODE_STEREO:
format |= ESD_STEREO;
this->num_channels = 2;
break;
}
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_esd_out: %d channels output\n",this->num_channels);
this->bytes_per_frame=(bits*this->num_channels)/8;
#if ESD_RESAMPLE
/* esd resamples (only for sample rates < the esd server's sample rate) */
if (this->output_sample_rate > this->server_sample_rate)
this->output_sample_rate = this->server_sample_rate;
#else
/* use xine's resample code */
this->output_sample_rate = this->server_sample_rate;
#endif
this->output_sample_k_rate = this->output_sample_rate / 1000;
this->audio_fd = esd_play_stream(format, this->output_sample_rate, NULL, this->pname);
if (this->audio_fd < 0) {
char *server = getenv("ESPEAKER");
xprintf(this->xine, XINE_VERBOSITY_LOG,
_("audio_esd_out: connecting to ESD server %s: %s\n"),
server ? server : "<default>", strerror(errno));
return 0;
}
return this->output_sample_rate;
}
static int ao_esd_num_channels(ao_driver_t *this_gen)
{
esd_driver_t *this = (esd_driver_t *) this_gen;
return this->num_channels;
}
static int ao_esd_bytes_per_frame(ao_driver_t *this_gen)
{
esd_driver_t *this = (esd_driver_t *) this_gen;
return this->bytes_per_frame;
}
static int ao_esd_delay(ao_driver_t *this_gen)
{
esd_driver_t *this = (esd_driver_t *) this_gen;
int bytes_left;
int frames;
struct timeval tv;
if (this->start_time.tv_sec == 0)
return 0;
gettimeofday(&tv, NULL);
frames = (tv.tv_usec - this->start_time.tv_usec)
* this->output_sample_k_rate / 1000;
frames += (tv.tv_sec - this->start_time.tv_sec)
* this->output_sample_rate;
frames -= this->latency;
if (frames < 0)
frames = 0;
/* calc delay */
bytes_left = this->bytes_in_buffer - frames * this->bytes_per_frame;
if (bytes_left<=0) /* buffer ran dry */
bytes_left = 0;
return bytes_left / this->bytes_per_frame;
}
static int ao_esd_write(ao_driver_t *this_gen,
int16_t* frame_buffer, uint32_t num_frames)
{
esd_driver_t *this = (esd_driver_t *) this_gen;
int simulated_bytes_in_buffer, frames ;
struct timeval tv;
if (this->audio_fd<0)
return 1;
if (this->start_time.tv_sec == 0)
gettimeofday(&this->start_time, NULL);
/* check if simulated buffer ran dry */
gettimeofday(&tv, NULL);
frames = (tv.tv_usec - this->start_time.tv_usec)
* this->output_sample_k_rate / 1000;
frames += (tv.tv_sec - this->start_time.tv_sec)
* this->output_sample_rate;
frames -= this->latency;
if (frames < 0)
frames = 0;
/* calc delay */
simulated_bytes_in_buffer = frames * this->bytes_per_frame;
if (this->bytes_in_buffer < simulated_bytes_in_buffer)
this->bytes_in_buffer = simulated_bytes_in_buffer;
#if REBLOCK
{
struct iovec iov[2];
int iovcnt;
int num_bytes;
int nwritten;
int rem;
if (this->reblock_rem + num_frames*this->bytes_per_frame < ESD_BUF_SIZE) {
/*
* the stuff in the temporary reblocking buffer plus the new
* samples still do not give a complete ESD_BUF_SIZE block.
* just save the new samples in the reblocking buffer for later.
*/
memcpy(this->reblock_buf + this->reblock_rem,
frame_buffer,
num_frames * this->bytes_per_frame);
this->reblock_rem += num_frames * this->bytes_per_frame;
return 1;
}
/* OK, we have at least one complete ESD_BUF_SIZE block */
iovcnt = 0;
num_bytes = 0;
if (this->reblock_rem > 0) {
/* send any saved samples from the reblocking buffer first */
iov[iovcnt].iov_base = this->reblock_buf;
iov[iovcnt].iov_len = this->reblock_rem;
iovcnt++;
num_bytes += this->reblock_rem;
this->reblock_rem = 0;
}
rem = (num_bytes + num_frames * this->bytes_per_frame) % ESD_BUF_SIZE;
if (num_frames * this->bytes_per_frame > rem) {
/*
* add samples from caller, so that the total number of bytes is
* a multiple of ESD_BUF_SIZE
*/
iov[iovcnt].iov_base = frame_buffer;
iov[iovcnt].iov_len = num_frames * this->bytes_per_frame - rem;
num_bytes += num_frames * this->bytes_per_frame - rem;
iovcnt++;
}
nwritten = writev(this->audio_fd, iov, iovcnt);
if (nwritten != num_bytes) {
if (nwritten < 0)
xprintf(this->xine, XINE_VERBOSITY_DEBUG, "audio_esd_out: writev failed: %s\n", strerror(errno));
else
xprintf(this->xine, XINE_VERBOSITY_DEBUG, "audio_esd_out: warning, incomplete write: %d\n", nwritten);
}
if (nwritten > 0)
this->bytes_in_buffer += nwritten;
if (rem > 0) {
/* save the remaining bytes for the next ao_esd_write() */
memcpy(this->reblock_buf,
(char*)frame_buffer + iov[iovcnt-1].iov_len, rem);
this->reblock_rem = rem;
}
}
#else
this->bytes_in_buffer += num_frames * this->bytes_per_frame;
write(this->audio_fd, frame_buffer, num_frames * this->bytes_per_frame);
#endif
return 1;
}
static void ao_esd_close(ao_driver_t *this_gen)
{
esd_driver_t *this = (esd_driver_t *) this_gen;
esd_close(this->audio_fd);
this->audio_fd = -1;
}
static uint32_t ao_esd_get_capabilities (ao_driver_t *this_gen) {
esd_driver_t *this = (esd_driver_t *) this_gen;
return this->capabilities;
}
static int ao_esd_get_gap_tolerance (ao_driver_t *this_gen) {
/* esd_driver_t *this = (esd_driver_t *) this_gen; */
return GAP_TOLERANCE;
}
static void ao_esd_exit(ao_driver_t *this_gen)
{
esd_driver_t *this = (esd_driver_t *) this_gen;
if (this->audio_fd != -1)
esd_close(this->audio_fd);
free(this->pname);
free (this);
}
static int ao_esd_get_property (ao_driver_t *this_gen, int property) {
esd_driver_t *this = (esd_driver_t *) this_gen;
int mixer_fd;
esd_player_info_t *esd_pi;
esd_info_t *esd_i;
switch(property) {
case AO_PROP_MIXER_VOL:
if((mixer_fd = esd_open_sound(NULL)) >= 0) {
if((esd_i = esd_get_all_info(mixer_fd)) != NULL) {
for(esd_pi = esd_i->player_list; esd_pi != NULL; esd_pi = esd_pi->next) {
if(!strcmp(this->pname, esd_pi->name)) {
this->mixer.source_id = esd_pi->source_id;
if(!this->mixer.mute)
this->mixer.volume = (((esd_pi->left_vol_scale * 100) / 256) +
((esd_pi->right_vol_scale * 100) / 256)) >> 1;
}
}
esd_free_all_info(esd_i);
}
esd_close(mixer_fd);
}
return this->mixer.volume;
break;
case AO_PROP_MUTE_VOL:
return this->mixer.mute;
break;
}
return 0;
}
static int ao_esd_set_property (ao_driver_t *this_gen, int property, int value) {
esd_driver_t *this = (esd_driver_t *) this_gen;
int mixer_fd;
switch(property) {
case AO_PROP_MIXER_VOL:
if(!this->mixer.mute) {
/* need this to get source_id */
(void) ao_esd_get_property(&this->ao_driver, AO_PROP_MIXER_VOL);
if((mixer_fd = esd_open_sound(NULL)) >= 0) {
int v = (value * 256) / 100;
esd_set_stream_pan(mixer_fd, this->mixer.source_id, v, v);
if(!this->mixer.mute)
this->mixer.volume = value;
esd_close(mixer_fd);
}
}
else
this->mixer.volume = value;
return this->mixer.volume;
break;
case AO_PROP_MUTE_VOL: {
int mute = (value) ? 1 : 0;
/* need this to get source_id */
(void) ao_esd_get_property(&this->ao_driver, AO_PROP_MIXER_VOL);
if(mute) {
if((mixer_fd = esd_open_sound(NULL)) >= 0) {
int v = 0;
esd_set_stream_pan(mixer_fd, this->mixer.source_id, v, v);
esd_close(mixer_fd);
}
}
else {
if((mixer_fd = esd_open_sound(NULL)) >= 0) {
int v = (this->mixer.volume * 256) / 100;
esd_set_stream_pan(mixer_fd, this->mixer.source_id, v, v);
esd_close(mixer_fd);
}
}
this->mixer.mute = mute;
return value;
}
break;
}
return ~value;
}
static int ao_esd_ctrl(ao_driver_t *this_gen, int cmd, ...) {
/* esd_driver_t *this = (esd_driver_t *) this_gen; */
switch (cmd) {
case AO_CTRL_PLAY_PAUSE:
break;
case AO_CTRL_PLAY_RESUME:
break;
case AO_CTRL_FLUSH_BUFFERS:
break;
}
return 0;
}
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
const void *data) {
esd_class_t *class = (esd_class_t *) class_gen;
config_values_t *config = class->xine->config;
esd_driver_t *this;
int audio_fd;
int err;
esd_server_info_t *esd_svinfo;
int server_sample_rate;
sigset_t vo_mask, vo_mask_orig;
/*
* open stream to ESD server
*
* esd_open_sound needs a working SIGALRM for detecting a failed
* attempt to autostart the esd daemon; esd notifies the process that
* attempts the esd daemon autostart with a SIGALRM (SIGUSR1) signal
* about a failure to open the audio device (successful daemon startup).
*
* Temporarily release the blocked SIGALRM, while esd_open_sound is active.
* (Otherwise xine hangs in esd_open_sound on a machine without sound)
*/
sigemptyset(&vo_mask);
sigaddset(&vo_mask, SIGALRM);
if (sigprocmask(SIG_UNBLOCK, &vo_mask, &vo_mask_orig))
xprintf(class->xine, XINE_VERBOSITY_DEBUG, "audio_esd_out: cannot unblock SIGALRM: %s\n", strerror(errno));
xprintf(class->xine, XINE_VERBOSITY_LOG, _("audio_esd_out: connecting to esd server...\n"));
audio_fd = esd_open_sound(NULL);
err = errno;
if (sigprocmask(SIG_SETMASK, &vo_mask_orig, NULL))
xprintf(class->xine, XINE_VERBOSITY_DEBUG, "audio_esd_out: cannot block SIGALRM: %s\n", strerror(errno));
if(audio_fd < 0) {
char *server = getenv("ESPEAKER");
/* print a message so the user knows why ESD failed */
xprintf(class->xine, XINE_VERBOSITY_LOG,
_("audio_esd_out: can't connect to %s ESD server: %s\n"),
server ? server : "<default>", strerror(err));
return NULL;
}
esd_svinfo = esd_get_server_info(audio_fd);
if (esd_svinfo) {
server_sample_rate = esd_svinfo->rate;
esd_free_server_info(esd_svinfo);
} else
server_sample_rate = 44100;
esd_close(audio_fd);
this = (esd_driver_t *) xine_xmalloc (sizeof (esd_driver_t));
this->xine = class->xine;
this->pname = strdup("xine esd audio output plugin");
this->output_sample_rate = 0;
this->server_sample_rate = server_sample_rate;
this->audio_fd = -1;
this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_MUTE_VOL;
this->latency = config->register_range (config, "audio.device.esd_latency", 0,
-30000, 90000,
_("esd audio output latency (adjust a/v sync)"),
_("If you experience audio being not in sync "
"with the video, you can enter a fixed offset "
"here to compensate.\nThe unit of the value "
"is one PTS tick, which is the 90000th part "
"of a second."),
10, NULL, NULL);
this->ao_driver.get_capabilities = ao_esd_get_capabilities;
this->ao_driver.get_property = ao_esd_get_property;
this->ao_driver.set_property = ao_esd_set_property;
this->ao_driver.open = ao_esd_open;
this->ao_driver.num_channels = ao_esd_num_channels;
this->ao_driver.bytes_per_frame = ao_esd_bytes_per_frame;
this->ao_driver.get_gap_tolerance = ao_esd_get_gap_tolerance;
this->ao_driver.delay = ao_esd_delay;
this->ao_driver.write = ao_esd_write;
this->ao_driver.close = ao_esd_close;
this->ao_driver.exit = ao_esd_exit;
this->ao_driver.control = ao_esd_ctrl;
return &(this->ao_driver);
}
/*
* class functions
*/
static char* get_identifier (audio_driver_class_t *this_gen) {
return "esd";
}
static char* get_description (audio_driver_class_t *this_gen) {
return _("xine audio output plugin using esound");
}
static void dispose_class (audio_driver_class_t *this_gen) {
esd_class_t *this = (esd_class_t *) this_gen;
free (this);
}
static void *init_class (xine_t *xine, void *data) {
esd_class_t *this;
this = (esd_class_t *) xine_xmalloc (sizeof (esd_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->xine = xine;
return this;
}
static ao_info_t ao_info_esd = {
4
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_OUT_ESD_IFACE_VERSION, "esd", XINE_VERSION_CODE, &ao_info_esd, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
--- NEW FILE: audio_none_out.c ---
/*
* Copyright (C) 2000-2003 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
*
* $Id: audio_none_out.c,v 1.1 2005/04/04 22:29:22 dsalt-guest Exp $
*/
#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"
#define AO_OUT_NONE_IFACE_VERSION 8
#define AUDIO_NUM_FRAGMENTS 15
#define AUDIO_FRAGMENT_SIZE 8192
#define GAP_TOLERANCE AO_MAX_GAP
typedef struct none_driver_s {
ao_driver_t ao_driver;
xine_t *xine;
int capabilities;
int mode;
int32_t sample_rate;
uint32_t num_channels;
uint32_t bits_per_sample;
uint32_t bytes_per_frame;
uint32_t latency;
} none_driver_t;
typedef struct {
audio_driver_class_t driver_class;
config_values_t *config;
xine_t *xine;
} none_class_t;
/*
* open the audio device for writing to
*/
static int ao_none_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode)
{
none_driver_t *this = (none_driver_t *) this_gen;
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
"audio_none_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
this->mode = mode;
this->sample_rate = rate;
this->bits_per_sample = bits;
switch (mode) {
case AO_CAP_MODE_MONO:
this->num_channels = 1;
break;
case AO_CAP_MODE_STEREO:
this->num_channels = 2;
break;
}
return this->sample_rate;
}
static int ao_none_num_channels(ao_driver_t *this_gen)
{
none_driver_t *this = (none_driver_t *) this_gen;
return this->num_channels;
}
static int ao_none_bytes_per_frame(ao_driver_t *this_gen)
{
none_driver_t *this = (none_driver_t *) this_gen;
return this->bytes_per_frame;
}
static int ao_none_get_gap_tolerance (ao_driver_t *this_gen)
{
return GAP_TOLERANCE;
}
static int ao_none_write(ao_driver_t *this_gen, int16_t *data,
uint32_t num_frames)
{
none_driver_t *this = (none_driver_t *) this_gen;
/* take some time to pretend we are doing something.
* avoids burning cpu.
*/
if( (1000 * num_frames / this->sample_rate) > 10 )
xine_usec_sleep ((1000 * num_frames / this->sample_rate)*1000/2);
return 1;
}
static int ao_none_delay (ao_driver_t *this_gen)
{
return 0;
}
static void ao_none_close(ao_driver_t *this_gen)
{
}
static uint32_t ao_none_get_capabilities (ao_driver_t *this_gen) {
none_driver_t *this = (none_driver_t *) this_gen;
return this->capabilities;
}
static void ao_none_exit(ao_driver_t *this_gen)
{
none_driver_t *this = (none_driver_t *) this_gen;
ao_none_close(this_gen);
free (this);
}
static int ao_none_get_property (ao_driver_t *this_gen, int property) {
return 0;
}
static int ao_none_set_property (ao_driver_t *this_gen, int property, int value) {
return ~value;
}
static int ao_none_ctrl(ao_driver_t *this_gen, int cmd, ...) {
/*none_driver_t *this = (none_driver_t *) this_gen;*/
switch (cmd) {
case AO_CTRL_PLAY_PAUSE:
break;
case AO_CTRL_PLAY_RESUME:
break;
case AO_CTRL_FLUSH_BUFFERS:
break;
}
return 0;
}
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
const void *data) {
none_class_t *class = (none_class_t *) class_gen;
/* config_values_t *config = class->config; */
none_driver_t *this;
lprintf ("open_plugin called\n");
this = (none_driver_t *) xine_xmalloc (sizeof (none_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_none_get_capabilities;
this->ao_driver.get_property = ao_none_get_property;
this->ao_driver.set_property = ao_none_set_property;
this->ao_driver.open = ao_none_open;
this->ao_driver.num_channels = ao_none_num_channels;
this->ao_driver.bytes_per_frame = ao_none_bytes_per_frame;
this->ao_driver.delay = ao_none_delay;
this->ao_driver.write = ao_none_write;
this->ao_driver.close = ao_none_close;
this->ao_driver.exit = ao_none_exit;
this->ao_driver.get_gap_tolerance = ao_none_get_gap_tolerance;
this->ao_driver.control = ao_none_ctrl;
return &this->ao_driver;
}
/*
* class functions
*/
static char* get_identifier (audio_driver_class_t *this_gen) {
return "none";
}
static char* get_description (audio_driver_class_t *this_gen) {
return _("xine dummy audio output plugin");
}
static void dispose_class (audio_driver_class_t *this_gen) {
none_class_t *this = (none_class_t *) this_gen;
free (this);
}
static void *init_class (xine_t *xine, void *data) {
none_class_t *this;
lprintf ("init class\n");
this = (none_class_t *) xine_xmalloc (sizeof (none_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_none = {
-1 /* do not auto probe this one */
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_OUT_NONE_IFACE_VERSION, "none", XINE_VERSION_CODE, &ao_info_none, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
--- NEW FILE: Makefile.am ---
include $(top_srcdir)/misc/Makefile.common
AM_CFLAGS = -DXINE_COMPILE $(ALSA_CFLAGS) $(ESD_CFLAGS) $(IRIXAL_CFLAGS) $(ARTS_CFLAGS) \
$(POLYPAUDIO_CFLAGS)
EXTRA_DIST = audio_irixal_out.c
libdir = $(XINE_PLUGINDIR)
if HAVE_OSS
oss_module = xineplug_ao_out_oss.la
endif
if HAVE_ALSA
if HAVE_ALSA09
alsa_module = xineplug_ao_out_alsa.la
endif
endif
if HAVE_ESD
esd_module = xineplug_ao_out_esd.la
endif
if HAVE_SUNAUDIO
sun_module = xineplug_ao_out_sun.la
endif
#if HAVE_IRIXAL
#irixal_module = xineplug_ao_out_irixal.la
#endif
if HAVE_ARTS
arts_module = xineplug_ao_out_arts.la
endif
if HAVE_DIRECTX
directx_module = xineplug_ao_out_directx.la
endif
if HAVE_COREAUDIO
coreaudio_module = xineplug_ao_out_coreaudio.la
endif
if HAVE_POLYPAUDIO
polypaudio_module = xineplug_ao_out_polypaudio.la
endif
##
# IMPORTANT:
# ---------
# all xine audio out plugins should be named like the
# scheme "xineplug_ao_out_"
#
lib_LTLIBRARIES = xineplug_ao_out_none.la xineplug_ao_out_file.la \
$(oss_module) \
$(alsa_module) \
$(sun_module) \
$(arts_module) \
$(esd_module) \
$(directx_module) \
$(coreaudio_module) \
$(polypaudio_module)
#lib_LTLIBRARIES = \
# $(alsa_module) \
# $(arts_module) \
# $(esd_module) \
# $(irixal_module) \
# $(oss_module) \
# $(sun_module)
xineplug_ao_out_none_la_SOURCES = audio_none_out.c
xineplug_ao_out_none_la_LIBADD = $(XINE_LIB)
xineplug_ao_out_none_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_ao_out_file_la_SOURCES = audio_file_out.c
xineplug_ao_out_file_la_LIBADD = $(XINE_LIB)
xineplug_ao_out_file_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_ao_out_oss_la_SOURCES = audio_oss_out.c
xineplug_ao_out_oss_la_LIBADD = $(XINE_LIB)
xineplug_ao_out_oss_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_ao_out_alsa_la_SOURCES = audio_alsa_out.c
xineplug_ao_out_alsa_la_LIBADD = $(ALSA_LIBS) $(XINE_LIB)
xineplug_ao_out_alsa_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_ao_out_esd_la_SOURCES = audio_esd_out.c
xineplug_ao_out_esd_la_LIBADD = $(ESD_LIBS) $(XINE_LIB)
xineplug_ao_out_esd_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_ao_out_sun_la_SOURCES = audio_sun_out.c
xineplug_ao_out_sun_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
#xineplug_ao_out_irixal_la_SOURCES = audio_irixal_out.c
#xineplug_ao_out_irixal_la_LIBADD = $(IRIXAL_LIBS)
#xineplug_ao_out_irixal_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_ao_out_arts_la_SOURCES = audio_arts_out.c
xineplug_ao_out_arts_la_LIBADD = $(ARTS_LIBS)
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_CPPFLAGS = $(DIRECTX_CPPFLAGS)
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)
# The "-Wl,-framework -Wl,..." is needed for libtool versions before
# 1.5.x (1.257): the default version that ships with Mac OS X is 1.5 (1.1220)
xineplug_ao_out_coreaudio_la_LDFLAGS = \
-Wl,-framework -Wl,Cocoa -framework CoreAudio \
-Wl,-framework -Wl,AudioUnit -framework AudioUnit \
-avoid-version -module @XINE_PLUGIN_MIN_SYMS@
xineplug_ao_out_coreaudio_la_CFLAGS = -framework CoreAudio -framework AudioUnit
xineplug_ao_out_polypaudio_la_SOURCES = audio_polyp_out.c
xineplug_ao_out_polypaudio_la_LIBADD = $(POLYPAUDIO_LIBS) $(XINE_LIB)
xineplug_ao_out_polypaudio_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
--- NEW FILE: audio_alsa_out.c ---
/*
* 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
*
[...1612 lines suppressed...]
this->driver_class.dispose = dispose_class;
/* this->config = xine->config; */
this->xine = xine;
return this;
}
static ao_info_t ao_info_alsa = {
10
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_OUT_ALSA_IFACE_VERSION, "alsa", XINE_VERSION_CODE, &ao_info_alsa, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
--- NEW FILE: Makefile.in ---
# Makefile.in generated by automake 1.9.3 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
SOURCES = $(xineplug_ao_out_alsa_la_SOURCES) $(xineplug_ao_out_arts_la_SOURCES) $(xineplug_ao_out_coreaudio_la_SOURCES) $(xineplug_ao_out_directx_la_SOURCES) $(xineplug_ao_out_esd_la_SOURCES) $(xineplug_ao_out_file_la_SOURCES) $(xineplug_ao_out_none_la_SOURCES) $(xineplug_ao_out_oss_la_SOURCES) $(xineplug_ao_out_polypaudio_la_SOURCES) $(xineplug_ao_out_sun_la_SOURCES)
srcdir = @srcdir@
[...994 lines suppressed...]
uninstall-hook:
@if echo '$(libdir)' | egrep ^'$(XINE_PLUGINDIR)' >/dev/null; then \
list='$(lib_LTLIBRARIES)'; for p in $$list; do \
p="`echo $$p | sed -e 's/\.la$$/\.so/g;s|^.*/||'`"; \
echo " rm -f $(DESTDIR)$(libdir)/$$p"; \
rm -f $(DESTDIR)$(libdir)/$$p; \
done; \
fi
mostlyclean-generic:
-rm -f *~ \#* .*~ .\#*
maintainer-clean-generic:
-@echo "This command is intended for maintainers to use;"
-@echo "it deletes files that may require special tools to rebuild."
-rm -f Makefile.in
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
--- NEW FILE: audio_arts_out.c ---
/*
* Copyright (C) 2000-2003 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
*
* $Id: audio_arts_out.c,v 1.1 2005/04/04 22:29:22 dsalt-guest Exp $
*/
#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 <artsc.h>
#include "xine_internal.h"
#include "xineutils.h"
#include "audio_out.h"
#include "bswap.h"
#define AO_OUT_ARTS_IFACE_VERSION 8
#define AUDIO_NUM_FRAGMENTS 15
#define AUDIO_FRAGMENT_SIZE 8192
#define GAP_TOLERANCE AO_MAX_GAP
typedef struct arts_driver_s {
ao_driver_t ao_driver;
xine_t *xine;
arts_stream_t audio_stream;
int capabilities;
int mode;
int32_t sample_rate;
uint32_t num_channels;
uint32_t bits_per_sample;
uint32_t bytes_per_frame;
uint32_t latency;
struct {
int volume;
int mute;
int vol_scale;
int v_mixer;
} mixer;
} arts_driver_t;
typedef struct {
audio_driver_class_t driver_class;
xine_t *xine;
int inited;
} arts_class_t;
/*
* Software stereo volume control.....
* Igor Mokrushin <igor@avtomir.ru>
*/
static void ao_arts_volume(void *buffer, int length, int volume) {
int v;
short *data = (short *)buffer;
while (length--) {
v=(int) ((*(data) * volume) / 100);
*(data)=(v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
*(data)=LE_16(data);
*(data++);
}
}
/* End volume control */
/*
* open the audio device for writing to
*/
static int ao_arts_open(ao_driver_t *this_gen,
uint32_t bits, uint32_t rate, int mode)
{
arts_driver_t *this = (arts_driver_t *) this_gen;
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
"audio_arts_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
if ( (mode & this->capabilities) == 0 ) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_arts_out: unsupported mode %08x\n", mode);
return 0;
}
if (this->audio_stream) {
if ( (mode == this->mode) && (rate == this->sample_rate) )
return this->sample_rate;
sleep(2); /* arts might segfault if we are still playing */
arts_close_stream(this->audio_stream);
}
this->mode = mode;
this->sample_rate = rate;
this->bits_per_sample = bits;
switch (mode) {
case AO_CAP_MODE_MONO:
this->num_channels = 1;
break;
case AO_CAP_MODE_STEREO:
this->num_channels = 2;
break;
}
this->bytes_per_frame=(this->bits_per_sample*this->num_channels)/8;
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_arts_out: %d channels output\n", this->num_channels);
this->audio_stream=arts_play_stream(this->sample_rate, bits, this->num_channels, "xine");
this->latency = arts_stream_get (this->audio_stream, ARTS_P_TOTAL_LATENCY);
/* try to keep latency low, if we don't do this we might end
with very high latencies for low quality sound and audio_out will
try to fill gaps every time...(values in ms) */
if( this->latency > 800 )
{
this->latency = 800 - arts_stream_get (this->audio_stream, ARTS_P_SERVER_LATENCY);
if( this->latency < 100 )
this->latency = 100;
arts_stream_set( this->audio_stream, ARTS_P_BUFFER_TIME, this->latency );
this->latency = arts_stream_get (this->audio_stream, ARTS_P_TOTAL_LATENCY);
}
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_arts_out : latency %d ms\n", this->latency);
return this->sample_rate;
}
static int ao_arts_num_channels(ao_driver_t *this_gen)
{
arts_driver_t *this = (arts_driver_t *) this_gen;
return this->num_channels;
}
static int ao_arts_bytes_per_frame(ao_driver_t *this_gen)
{
arts_driver_t *this = (arts_driver_t *) this_gen;
return this->bytes_per_frame;
}
static int ao_arts_get_gap_tolerance (ao_driver_t *this_gen)
{
return GAP_TOLERANCE;
}
static int ao_arts_write(ao_driver_t *this_gen, int16_t *data,
uint32_t num_frames)
{
arts_driver_t *this = (arts_driver_t *) this_gen;
int size = num_frames * this->bytes_per_frame;
ao_arts_volume(data, num_frames * this->num_channels, this->mixer.vol_scale );
arts_write(this->audio_stream, data, size );
return 1;
}
static int ao_arts_delay (ao_driver_t *this_gen)
{
arts_driver_t *this = (arts_driver_t *) this_gen;
/* Just convert latency (ms) to frame units.
please note that there is no function in aRts C API to
get the current buffer utilization. This is, at best,
a very roughly aproximation.
*/
return this->latency * this->sample_rate / 1000;
}
static void ao_arts_close(ao_driver_t *this_gen)
{
arts_driver_t *this = (arts_driver_t *) this_gen;
if (this->audio_stream) {
sleep(2); /* arts might segfault if we are still playing */
arts_close_stream(this->audio_stream);
this->audio_stream = NULL;
}
}
static uint32_t ao_arts_get_capabilities (ao_driver_t *this_gen) {
arts_driver_t *this = (arts_driver_t *) this_gen;
return this->capabilities;
}
static void ao_arts_exit(ao_driver_t *this_gen)
{
arts_driver_t *this = (arts_driver_t *) this_gen;
ao_arts_close(this_gen);
/* FIXME: arts_free() freezes on BSD, so don't use it there */
#if !defined(__OpenBSD__) && !defined (__FreeBSD__) && !defined(__NetBSD__)
arts_free();
#endif
free (this);
}
static int ao_arts_get_property (ao_driver_t *this_gen, int property) {
arts_driver_t *this = (arts_driver_t *) this_gen;
switch(property) {
case AO_PROP_PCM_VOL:
case AO_PROP_MIXER_VOL:
if(!this->mixer.mute)
this->mixer.volume = this->mixer.vol_scale;
return this->mixer.volume;
break;
case AO_PROP_MUTE_VOL:
return this->mixer.mute;
break;
}
return 0;
}
static int ao_arts_set_property (ao_driver_t *this_gen, int property, int value) {
arts_driver_t *this = (arts_driver_t *) this_gen;
int mute = (value) ? 1 : 0;
switch(property) {
case AO_PROP_PCM_VOL:
case AO_PROP_MIXER_VOL:
if(!this->mixer.mute)
this->mixer.volume = value;
this->mixer.vol_scale = this->mixer.volume;
return this->mixer.volume;
break;
case AO_PROP_MUTE_VOL:
if(mute) {
this->mixer.v_mixer = this->mixer.volume;
this->mixer.volume = 0;
this->mixer.vol_scale = this->mixer.volume;
} else {
this->mixer.volume = this->mixer.v_mixer;
this->mixer.vol_scale = this->mixer.volume;
}
this->mixer.mute = mute;
return value;
break;
}
return ~value;
}
static int ao_arts_ctrl(ao_driver_t *this_gen, int cmd, ...) {
/*arts_driver_t *this = (arts_driver_t *) this_gen;*/
switch (cmd) {
case AO_CTRL_PLAY_PAUSE:
break;
case AO_CTRL_PLAY_RESUME:
break;
case AO_CTRL_FLUSH_BUFFERS:
break;
}
return 0;
}
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data) {
arts_class_t *class = (arts_class_t *) class_gen;
arts_driver_t *this;
int rc;
lprintf ("audio_arts_out: open_plugin called\n");
this = (arts_driver_t *) xine_xmalloc (sizeof (arts_driver_t));
if (class->inited == 0) {
rc = arts_init();
class->inited++;
} else {
xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_arts_out: not trying to initialise a second time\n");
return NULL;
}
if (rc < 0) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG,"audio_arts_out: arts_init failed: %s\n", arts_error_text(rc));
return NULL;
}
/*
* set volume control
*/
this->mixer.mute = 0;
this->mixer.vol_scale = 60;
this->mixer.v_mixer = 0;
this->xine = class->xine;
/*
* set capabilities
*/
this->capabilities = 0;
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_arts_out : supported modes are ");
this->capabilities |= AO_CAP_MODE_MONO | AO_CAP_MIXER_VOL | AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL;
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "mono ");
this->capabilities |= AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL;
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "stereo ");
this->sample_rate = 0;
this->audio_stream = NULL;
this->ao_driver.get_capabilities = ao_arts_get_capabilities;
this->ao_driver.get_property = ao_arts_get_property;
this->ao_driver.set_property = ao_arts_set_property;
this->ao_driver.open = ao_arts_open;
this->ao_driver.num_channels = ao_arts_num_channels;
this->ao_driver.bytes_per_frame = ao_arts_bytes_per_frame;
this->ao_driver.delay = ao_arts_delay;
this->ao_driver.write = ao_arts_write;
this->ao_driver.close = ao_arts_close;
this->ao_driver.exit = ao_arts_exit;
this->ao_driver.get_gap_tolerance = ao_arts_get_gap_tolerance;
this->ao_driver.control = ao_arts_ctrl;
return &this->ao_driver;
}
/*
* class functions
*/
static char* get_identifier (audio_driver_class_t *this_gen) {
return "arts";
}
static char* get_description (audio_driver_class_t *this_gen) {
return _("xine audio output plugin using kde artsd");
}
static void dispose_class (audio_driver_class_t *this_gen) {
arts_class_t *this = (arts_class_t *) this_gen;
free (this);
}
static void *init_class (xine_t *xine, void *data) {
arts_class_t *this;
lprintf ("audio_arts_out: init class\n");
this = (arts_class_t *) xine_xmalloc (sizeof (arts_class_t));
this->inited = 0;
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->xine = xine;
return this;
}
static ao_info_t ao_info_arts = {
5
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_OUT_ARTS_IFACE_VERSION, "arts", XINE_VERSION_CODE, &ao_info_arts, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
--- NEW FILE: audio_coreaudio_out.c ---
/*
* 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;
uint32_t last_block_size;
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;
}
this->last_block_size = req_size;
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;
switch (mode) {
case AO_CAP_MODE_MONO:
this->num_channels = 1;
break;
case AO_CAP_MODE_STEREO:
this->num_channels = 2;
break;
}
this->sample_rate = rate;
this->bits_per_sample = bits;
this->capabilities = AO_CAP_16BITS | AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL;
this->bytes_per_frame = this->num_channels * (bits / 8);
this->buf_readpos = 0;
this->buf_writepos = 0;
this->last_block_size = 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 converter 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.mBitsPerChannel = this->bits_per_sample;
format.mChannelsPerFrame = this->num_channels;
format.mBytesPerFrame = this->bytes_per_frame;
format.mFramesPerPacket = 1;
format.mBytesPerPacket = format.mBytesPerFrame;
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");
return 0;
}
err = AudioUnitInitialize (this->au_unit);
if (err) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
"audio_coreaudio_out: failed to AudioUnitInitialize(au_unit)\n");
return 0;
}
err = AudioOutputUnitStart (this->au_unit);
if (err) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
"audio_coreaudio_out: failed to AudioOutputUnitStart(au_unit)\n");
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->last_block_size)
/ 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);
/* contrary to some of Apple's documentation, the function to close a
* component is called CloseComponent, not CloseAComponent */
CloseComponent (this->au_unit);
this->au_unit = 0;
}
if (this->converter_unit) {
AudioUnitUninitialize (this->converter_unit);
CloseComponent (this->converter_unit);
this->converter_unit = 0;
}
if (this->au_component) {
this->au_component = NULL;
}
if (this->converter_component) {
this->converter_component = NULL;
}
pthread_mutex_destroy (&this->mutex);
}
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 * 12);
}
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 / 12.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/Mac OS X");
}
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 }
};
--- NEW FILE: audio_file_out.c ---
/*
* Copyright (C) 2000-2003 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
*
* $Id: audio_file_out.c,v 1.1 2005/04/04 22:29:22 dsalt-guest Exp $
*/
#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 "bswap.h"
#define AO_OUT_FILE_IFACE_VERSION 8
#define GAP_TOLERANCE INT_MAX
/* Taken (hStudlyCapsAndAll) from sox's wavwritehdr */
struct wavhdr {
unsigned char bRiffMagic[4]; // 'RIFF'
uint32_t wRiffLength ; // length of file minus the 8 byte riff header
unsigned char bWaveMagic[8]; // 'WAVEfmt '
uint32_t wFmtSize; // length of format chunk minus 8 byte header
uint16_t wFormatTag; // identifies PCM, ULAW etc
uint16_t wChannels;
uint32_t dwSamplesPerSecond; // samples per second per channel
uint32_t dwAvgBytesPerSec; // non-trivial for compressed formats
uint16_t wBlockAlign; // basic block size
uint16_t wBitsPerSample; // non-trivial for compressed formats
// PCM formats then go straight to the data chunk:
unsigned char bData[4]; // 'data'
unsigned long dwDataLength; // length of data chunk minus 8 byte header
};
typedef struct file_driver_s {
ao_driver_t ao_driver;
xine_t *xine;
int capabilities;
int mode;
int32_t sample_rate;
uint32_t num_channels;
uint32_t bits_per_sample;
uint32_t bytes_per_frame;
char *fname;
int fd;
size_t bytes_written;
struct timeval endtime;
} file_driver_t;
typedef struct {
audio_driver_class_t driver_class;
config_values_t *config;
xine_t *xine;
} file_class_t;
/*
* open the audio device for writing to
*/
static int ao_file_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode)
{
file_driver_t *this = (file_driver_t *) this_gen;
struct wavhdr w;
xprintf (this->xine, XINE_VERBOSITY_LOG,
"audio_file_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
this->mode = mode;
this->sample_rate = rate;
this->bits_per_sample = bits;
switch (mode) {
case AO_CAP_MODE_MONO:
this->num_channels = 1;
break;
case AO_CAP_MODE_STEREO:
this->num_channels = 2;
break;
}
this->bytes_per_frame = (this->bits_per_sample*this->num_channels) / 8;
this->fd = -1;
this->fname = getenv("XINE_WAVE_OUTPUT");
if (!this->fname)
this->fname = "xine-out.wav";
this->fd = open(this->fname, O_WRONLY|O_TRUNC|O_CREAT, 0644);
if (this->fd == -1) {
xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_file_out: Failed to open file '%s': %s\n",
this->fname, strerror(errno));
return 0;
}
w.bRiffMagic[0] = 'R';
w.bRiffMagic[1] = 'I';
w.bRiffMagic[2] = 'F';
w.bRiffMagic[3] = 'F';
w.wRiffLength = le2me_32(0x7ff00024);
w.bWaveMagic[0] = 'W';
w.bWaveMagic[1] = 'A';
w.bWaveMagic[2] = 'V';
w.bWaveMagic[3] = 'E';
w.bWaveMagic[4] = 'f';
w.bWaveMagic[5] = 'm';
w.bWaveMagic[6] = 't';
w.bWaveMagic[7] = ' ';
w.wFmtSize = le2me_32(0x10);
w.wFormatTag = le2me_16(1); // PCM;
w.wChannels = le2me_16(this->num_channels);
w.dwSamplesPerSecond = le2me_32(this->sample_rate);
w.dwAvgBytesPerSec = le2me_32(this->sample_rate * this->bytes_per_frame);
w.wBlockAlign = le2me_16(this->bytes_per_frame);
w.wBitsPerSample = le2me_16(this->bits_per_sample);
w.bData[0] = 'd';
w.bData[1] = 'a';
w.bData[2] = 't';
w.bData[3] = 'a';
w.dwDataLength = le2me_32(0x7ffff000);
this->bytes_written = 0;
if (write(this->fd, &w, sizeof(w)) != sizeof(w)) {
xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_file_out: Failed to write WAVE header to file '%s': %s\n",
this->fname, strerror(errno));
close(this->fd);
this->fd = -1;
return 0;
}
xine_monotonic_clock(&this->endtime, NULL);
return this->sample_rate;
}
static int ao_file_num_channels(ao_driver_t *this_gen)
{
file_driver_t *this = (file_driver_t *) this_gen;
return this->num_channels;
}
static int ao_file_bytes_per_frame(ao_driver_t *this_gen)
{
file_driver_t *this = (file_driver_t *) this_gen;
return this->bytes_per_frame;
}
static int ao_file_get_gap_tolerance (ao_driver_t *this_gen)
{
return GAP_TOLERANCE;
}
static int ao_file_write(ao_driver_t *this_gen, int16_t *data,
uint32_t num_frames)
{
file_driver_t *this = (file_driver_t *) this_gen;
size_t len = num_frames * this->bytes_per_frame;
unsigned long usecs;
#ifdef WORDS_BIGENDIAN
/* Eep. .WAV format is little-endian. We need to swap.
Remind me why I picked this output format again? */
if (this->bits_per_sample == 16) {
int i;
for (i=0; i<len/2; i++)
data[i] = bswap_16(data[i]);
} else if (this->bits_per_sample == 32) {
int i;
uint32_t *d32 = (void *)data;
for (i=0; i<len/4; i++)
d32[i] = bswap_16(d32[i]);
}
#endif
while(len) {
size_t thislen = write(this->fd, data, len);
if (thislen == -1) {
xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_file_out: Failed to write data to file '%s': %s\n",
this->fname, strerror(errno));
return -1;
}
len -= thislen;
this->bytes_written += thislen;
}
/* Delay for an appropriate amount of time to prevent padding */
usecs = ((10000 * num_frames / (this->sample_rate/100)));
this->endtime.tv_usec += usecs;
while (this->endtime.tv_usec > 1000000) {
this->endtime.tv_usec -= 1000000;
this->endtime.tv_sec++;
}
return 1;
}
static int ao_file_delay (ao_driver_t *this_gen)
{
file_driver_t *this = (file_driver_t *) this_gen;
struct timeval now;
unsigned long tosleep;
/* Work out how long we need to sleep for, and how much
time we've already taken */
xine_monotonic_clock(&now, NULL);
if (now.tv_sec > this->endtime.tv_sec) {
/* We slipped. Compensate */
this->endtime = now;
return 0;
}
if (now.tv_sec == this->endtime.tv_sec &&
now.tv_usec >= this->endtime.tv_usec)
return 0;
tosleep = this->endtime.tv_sec - now.tv_sec;
tosleep *= 1000000;
tosleep += this->endtime.tv_usec - now.tv_usec;
xine_usec_sleep(tosleep);
return 0;
}
static void ao_file_close(ao_driver_t *this_gen)
{
file_driver_t *this = (file_driver_t *) this_gen;
uint32_t len;
len = le2me_32(this->bytes_written);
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_file_out: Close file '%s'. %d KiB written\n",
this->fname, this->bytes_written / 1024);
if (lseek(this->fd, 40, SEEK_SET) != -1) {
write(this->fd, &len, 4);
len = le2me_32(this->bytes_written + 0x24);
if (lseek(this->fd, 4, SEEK_SET) != -1)
write(this->fd, &len, 4);
}
close(this->fd);
this->fd = -1;
}
static uint32_t ao_file_get_capabilities (ao_driver_t *this_gen) {
file_driver_t *this = (file_driver_t *) this_gen;
return this->capabilities;
}
static void ao_file_exit(ao_driver_t *this_gen)
{
file_driver_t *this = (file_driver_t *) this_gen;
if (this->fd != -1)
ao_file_close(this_gen);
free (this);
}
static int ao_file_get_property (ao_driver_t *this_gen, int property) {
return 0;
}
static int ao_file_set_property (ao_driver_t *this_gen, int property, int value) {
return ~value;
}
static int ao_file_ctrl(ao_driver_t *this_gen, int cmd, ...) {
/*file_driver_t *this = (file_driver_t *) this_gen;*/
switch (cmd) {
case AO_CTRL_PLAY_PAUSE:
break;
case AO_CTRL_PLAY_RESUME:
break;
case AO_CTRL_FLUSH_BUFFERS:
break;
}
return 0;
}
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
const void *data) {
file_class_t *class = (file_class_t *) class_gen;
/* config_values_t *config = class->config; */
file_driver_t *this;
lprintf ("open_plugin called\n");
this = (file_driver_t *) xine_xmalloc (sizeof (file_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_file_get_capabilities;
this->ao_driver.get_property = ao_file_get_property;
this->ao_driver.set_property = ao_file_set_property;
this->ao_driver.open = ao_file_open;
this->ao_driver.num_channels = ao_file_num_channels;
this->ao_driver.bytes_per_frame = ao_file_bytes_per_frame;
this->ao_driver.delay = ao_file_delay;
this->ao_driver.write = ao_file_write;
this->ao_driver.close = ao_file_close;
this->ao_driver.exit = ao_file_exit;
this->ao_driver.get_gap_tolerance = ao_file_get_gap_tolerance;
this->ao_driver.control = ao_file_ctrl;
this->fd = -1;
return &this->ao_driver;
}
/*
* class functions
*/
static char* get_identifier (audio_driver_class_t *this_gen) {
return "file";
}
static char* get_description (audio_driver_class_t *this_gen) {
return _("xine file audio output plugin");
}
static void dispose_class (audio_driver_class_t *this_gen) {
file_class_t *this = (file_class_t *) this_gen;
free (this);
}
static void *init_class (xine_t *xine, void *data) {
file_class_t *this;
lprintf ("init class\n");
this = (file_class_t *) xine_xmalloc (sizeof (file_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_file = {
-1 /* do not auto probe this one */
};
/*
* exported plugin catalog entry
*/
plugin_info_t xine_plugin_info[] = {
/* type, API, "name", version, special_info, init_function */
{ PLUGIN_AUDIO_OUT, AO_OUT_FILE_IFACE_VERSION, "file", XINE_VERSION_CODE, &ao_info_file, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};