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, &current_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 }
};