summaryrefslogtreecommitdiff
path: root/src/audio_out
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_out')
-rw-r--r--src/audio_out/Makefile.am45
-rw-r--r--src/audio_out/audio_alsa_out.c761
-rw-r--r--src/audio_out/audio_alsa_out.h28
-rw-r--r--src/audio_out/audio_esd_out.c329
-rw-r--r--src/audio_out/audio_esd_out.h26
-rw-r--r--src/audio_out/audio_oss_out.c452
-rw-r--r--src/audio_out/audio_oss_out.h27
-rw-r--r--src/audio_out/resample.c67
-rw-r--r--src/audio_out/resample.h42
9 files changed, 1777 insertions, 0 deletions
diff --git a/src/audio_out/Makefile.am b/src/audio_out/Makefile.am
new file mode 100644
index 000000000..220f5991d
--- /dev/null
+++ b/src/audio_out/Makefile.am
@@ -0,0 +1,45 @@
+CFLAGS = @GLOBAL_CFLAGS@ -DXINE_COMPILE
+
+EXTRA_DIST = audio_alsa_out.c audio_esd_out.c
+
+if HAVE_ALSA
+alsa_module = xineplug_ao_out_alsa.la
+endif
+
+if HAVE_ESD
+esd_module = xineplug_ao_out_esd.la
+endif
+
+##
+# IMPORTANT:
+# ---------
+# All of xine audio out plugins should be named like the
+# scheme "xineplug_ao_out_"
+#
+##lib_LTLIBRARIES = xineplug_ao_out_oss.la $(alsa_module) $(esd_module)
+lib_LTLIBRARIES =
+
+##xineplug_ao_out_oss_la_SOURCES = audio_oss_out.c resample.c
+##xineplug_ao_out_oss_la_LDFLAGS = -avoid-version -module
+
+##xineplug_ao_out_alsa_la_SOURCES = audio_alsa_out.c resample.c
+##xineplug_ao_out_alsa_la_LDFLAGS = -avoid-version -module
+
+##xineplug_ao_out_esd_la_SOURCES = audio_esd_out.c resample.c
+##xineplug_ao_out_esd_la_LDFLAGS = -avoid-version -module
+
+noinst_HEADERS = audio_oss_out.h audio_alsa_out.h audio_esd_out.h resample.h
+
+
+debug:
+ $(MAKE) CFLAGS="$(DEBUG_CFLAGS)"
+
+
+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
diff --git a/src/audio_out/audio_alsa_out.c b/src/audio_out/audio_alsa_out.c
new file mode 100644
index 000000000..95405511f
--- /dev/null
+++ b/src/audio_out/audio_alsa_out.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) 2000 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
+ *
+ * $Id: audio_alsa_out.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/asoundlib.h>
+//#include <linux/asound.h>
+//#include <linux/asequencer.h>
+//#include <linux/asoundid.h>
+
+#include <inttypes.h>
+#include "xine/xine.h"
+#include "xine/monitor.h"
+#include "xine/audio_out.h"
+#include "xine/metronom.h"
+#include "resample.h"
+#include "xine/ac3.h"
+#include "xine/utils.h"
+
+#define AUDIO_NUM_FRAGMENTS 15
+#define AUDIO_FRAGMENT_SIZE 8192
+
+#define GAP_TOLERANCE 15000
+#define MAX_MASTER_CLOCK_DIV 5000
+
+extern uint32_t xine_debug;
+
+
+typedef struct _audio_alsa_globals {
+
+ snd_pcm_t *front_handle;
+
+ int32_t output_sample_rate, input_sample_rate;
+ uint32_t num_channels;
+
+ uint32_t bytes_in_buffer; /* number of bytes written to audio hardware */
+ uint32_t last_vpts; /* vpts at which last written package ends */
+
+ uint32_t sync_vpts; /* this syncpoint is used as a starting point */
+ uint32_t sync_bytes_in_buffer; /* for vpts <-> samplecount assoc */
+
+ int audio_step; /* pts per 32 768 samples (sample = #bytes/2) */
+ int32_t bytes_per_kpts; /* bytes per 1024/90000 sec */
+
+ int16_t *zero_space;
+
+ int audio_started;
+ int pcm_default_card;
+ int pcm_default_device;
+
+ int direction;
+ int mode;
+ int start_mode;
+ int stop_mode;
+ int format;
+ int rate;
+ int voices;
+ int interleave;
+ int frag_size;
+ int frag_count;
+ int pcm_len;
+
+} audio_alsa_globals_t;
+
+static audio_alsa_globals_t gAudioALSA;
+
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static void alsa_set_frag(int fragment_size, int fragment_count) {
+ snd_pcm_channel_params_t params;
+ snd_pcm_channel_setup_t setup;
+ snd_pcm_format_t format;
+ int err;
+
+ memset(&params, 0, sizeof(params));
+
+ params.mode = gAudioALSA.mode;
+ params.channel = gAudioALSA.direction;
+ params.start_mode = gAudioALSA.start_mode;
+ params.stop_mode = gAudioALSA.stop_mode;
+ params.buf.block.frag_size = fragment_size;
+ params.buf.block.frags_max = fragment_count;
+ params.buf.block.frags_min = 1;
+
+ memset(&format, 0, sizeof(format));
+ format.format = gAudioALSA.format;
+ format.rate = gAudioALSA.rate;
+ format.voices = gAudioALSA.voices;
+ format.interleave = gAudioALSA.interleave;
+ memcpy(&params.format, &format, sizeof(format));
+
+ snd_pcm_playback_flush(gAudioALSA.front_handle);
+
+ if((err = snd_pcm_channel_params(gAudioALSA.front_handle, &params)) < 0) {
+ perr("snd_pcm_channel_params() failed: %s\n", snd_strerror(err));
+ return;
+ }
+ if((err = snd_pcm_playback_prepare(gAudioALSA.front_handle)) < 0) {
+ perr("snd_pcm_channel_prepare() failed: %s\n", snd_strerror(err));
+ return;
+ }
+
+ memset(&setup, 0, sizeof(setup));
+ setup.mode = gAudioALSA.mode;
+ setup.channel = gAudioALSA.direction;
+ if((err = snd_pcm_channel_setup(gAudioALSA.front_handle, &setup)) < 0) {
+ perr("snd_pcm_channel_setup() failed: %s\n", snd_strerror(err));
+ return;
+ }
+
+ gAudioALSA.frag_size = fragment_size;
+ gAudioALSA.frag_count = fragment_count;
+
+ gAudioALSA.pcm_len = fragment_size *
+ (snd_pcm_format_width(gAudioALSA.format) / 8) *
+ gAudioALSA.voices;
+
+ // perr("PCM len = %d\n", gAudioALSA.pcm_len);
+ if(gAudioALSA.zero_space)
+ free(gAudioALSA.zero_space);
+
+ gAudioALSA.zero_space = (int16_t *) malloc(gAudioALSA.frag_size);
+ memset(gAudioALSA.zero_space,
+ (int16_t) snd_pcm_format_silence(gAudioALSA.format),
+ gAudioALSA.frag_size);
+}
+/* ------------------------------------------------------------------------- */
+/*
+ * open the audio device for writing to
+ */
+static int ao_open(uint32_t bits, uint32_t rate, int ao_mode) {
+ int channels;
+ int subdevice = 0;
+ int direction = SND_PCM_OPEN_PLAYBACK;
+ snd_pcm_format_t pcm_format;
+ snd_pcm_channel_setup_t pcm_chan_setup;
+ snd_pcm_channel_params_t pcm_chan_params;
+ snd_pcm_channel_info_t pcm_chan_info;
+ int err;
+ int mode;
+
+
+ switch (ao_mode) {
+
+ case AO_MODE_STEREO:
+ case AO_MODE_AC3:
+ channels = 2;
+ break;
+
+ case AO_MODE_MONO:
+ channels = 1;
+ break;
+
+ default:
+ return 0;
+ break;
+ }
+
+ xprintf (VERBOSE|AUDIO, "bits = %d, rate = %d, channels = %d\n",
+ bits, rate, channels);
+
+#warning "FIXME in libAC3"
+ if(!rate)
+ return 0;
+
+ if(gAudioALSA.front_handle != NULL) {
+
+ if(rate == gAudioALSA.input_sample_rate)
+ return 1;
+
+ snd_pcm_close(gAudioALSA.front_handle);
+ }
+
+ gAudioALSA.input_sample_rate = rate;
+ gAudioALSA.bytes_in_buffer = 0;
+ gAudioALSA.last_vpts = 0;
+ gAudioALSA.sync_vpts = 0;
+ gAudioALSA.sync_bytes_in_buffer = 0;
+ gAudioALSA.audio_started = 0;
+ gAudioALSA.direction = SND_PCM_CHANNEL_PLAYBACK;
+
+ if (ao_mode == AO_MODE_AC3) {
+ gAudioALSA.pcm_default_device = 2;
+ mode = SND_PCM_MODE_BLOCK;
+ }
+ else {
+ mode = SND_PCM_MODE_BLOCK;
+ }
+
+ gAudioALSA.mode = mode;
+
+ if((err = snd_pcm_open_subdevice(&gAudioALSA.front_handle,
+ gAudioALSA.pcm_default_card,
+ gAudioALSA.pcm_default_device,
+ subdevice, direction
+// | SND_PCM_OPEN_NONBLOCK)) < 0) {
+ )) < 0) {
+ perr("snd_pcm_open_subdevice() failed: %s\n", snd_strerror(err));
+ return 0;
+ }
+
+ memset(&pcm_chan_info, 0, sizeof(snd_pcm_channel_info_t));
+ if((err = snd_pcm_channel_info(gAudioALSA.front_handle,
+ &pcm_chan_info)) < 0) {
+ perr("snd_pcm_channel_info() failed: %s\n", snd_strerror(err));
+ return 0;
+ }
+
+ memset(&pcm_chan_params, 0, sizeof(snd_pcm_channel_params_t));
+ memset(&pcm_format, 0, sizeof(snd_pcm_format_t));
+ /* set sample size */
+ switch(bits) {
+ case 8:
+ pcm_format.format = SND_PCM_SFMT_S8;
+ break;
+
+ case 16:
+ pcm_format.format = SND_PCM_SFMT_S16;
+ break;
+
+ case 24:
+ pcm_format.format = SND_PCM_SFMT_S24;
+ break;
+
+ case 32:
+ pcm_format.format = SND_PCM_SFMT_S32;
+ break;
+
+ default:
+ perr("sample format %d unsupported\n", bits);
+ break;
+ }
+ gAudioALSA.format = pcm_format.format;
+
+ xprintf (VERBOSE|AUDIO, "format name = '%s'\n",
+ snd_pcm_get_format_name(pcm_format.format));
+
+
+ pcm_format.voices = gAudioALSA.voices = channels;
+ pcm_format.rate = gAudioALSA.rate = rate;
+ pcm_format.interleave = gAudioALSA.interleave = 1;
+
+ gAudioALSA.num_channels = channels;
+
+ xprintf (VERBOSE|AUDIO, "audio channels = %d ao_mode = %d\n",
+ gAudioALSA.num_channels,ao_mode);
+
+ if(rate > pcm_chan_info.max_rate)
+ gAudioALSA.output_sample_rate = pcm_chan_info.max_rate;
+ else
+ gAudioALSA.output_sample_rate = gAudioALSA.input_sample_rate;
+
+ gAudioALSA.audio_step = (uint32_t) 90000
+ * (uint32_t) 32768 / gAudioALSA.input_sample_rate;
+
+ gAudioALSA.bytes_per_kpts = gAudioALSA.output_sample_rate
+ * gAudioALSA.num_channels * 2 * 1024 / 90000;
+
+ xprintf (VERBOSE|AUDIO, "%d input samples/sec %d output samples/sec\n",
+ rate, gAudioALSA.output_sample_rate);
+ xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 samples\n",
+ gAudioALSA.audio_step);
+
+ metronom_set_audio_rate (gAudioALSA.audio_step);
+
+ memcpy(&pcm_chan_params.format, &pcm_format, sizeof(snd_pcm_format_t));
+
+ pcm_chan_params.mode = mode;
+ pcm_chan_params.channel = gAudioALSA.direction;
+
+ pcm_chan_params.start_mode = SND_PCM_START_FULL;
+ //pcm_chan_params.start_mode = SND_PCM_START_DATA;
+ //pcm_chan_params.stop_mode = SND_PCM_STOP_STOP;
+ pcm_chan_params.stop_mode = SND_PCM_STOP_ROLLOVER;
+
+ gAudioALSA.start_mode = pcm_chan_params.start_mode;
+ gAudioALSA.stop_mode = pcm_chan_params.stop_mode;
+
+ if (ao_mode == AO_MODE_AC3) {
+ pcm_chan_params.digital.dig_valid = 1;
+ pcm_chan_params.digital.dig_status[0] = SND_PCM_DIG0_NONAUDIO;
+ pcm_chan_params.digital.dig_status[0] |= SND_PCM_DIG0_PROFESSIONAL;
+ pcm_chan_params.digital.dig_status[0] |= SND_PCM_DIG0_PRO_FS_48000;
+ pcm_chan_params.digital.dig_status[3] = SND_PCM_DIG3_CON_FS_48000;
+ }
+
+ snd_pcm_playback_flush(gAudioALSA.front_handle);
+ if((err = snd_pcm_channel_params(gAudioALSA.front_handle,
+ &pcm_chan_params)) < 0) {
+ perr("snd_pcm_channel_params() failed: %s\n", snd_strerror(err));
+ return 0;
+ }
+ if((err = snd_pcm_playback_prepare(gAudioALSA.front_handle)) < 0) {
+ perr("snd_pcm_channel_prepare() failed: %s\n", snd_strerror(err));
+ return 0;
+ }
+
+ pcm_chan_setup.mode = mode;
+ pcm_chan_setup.channel = gAudioALSA.direction;
+
+ if((err = snd_pcm_channel_setup(gAudioALSA.front_handle,
+ &pcm_chan_setup)) < 0) {
+ perr("snd_pcm_channel_setup() failed: %s\n", snd_strerror(err));
+ return 0;
+ }
+
+ printf ("actual rate: %d\n", pcm_chan_setup.format.rate);
+
+ alsa_set_frag(1536, 6);
+
+ gAudioALSA.bytes_in_buffer = 0;
+
+ return 1;
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static void ao_fill_gap (uint32_t pts_len) {
+ int num_bytes = pts_len * gAudioALSA.bytes_per_kpts / 1024;
+
+ num_bytes = (num_bytes / 4) * 4;
+
+ gAudioALSA.bytes_in_buffer += num_bytes;
+
+ printf ("audio_alsa_out: inserting %d 0-bytes to fill a gap of %d pts\n",
+ num_bytes, pts_len);
+
+ while (num_bytes>0) {
+ if (num_bytes>gAudioALSA.frag_size) {
+ snd_pcm_write(gAudioALSA.front_handle, gAudioALSA.zero_space,
+ gAudioALSA.frag_size);
+ num_bytes -= gAudioALSA.frag_size;
+ } else {
+ int old_frag_size = gAudioALSA.frag_size;
+
+ alsa_set_frag(num_bytes, 6);
+
+ snd_pcm_write(gAudioALSA.front_handle, gAudioALSA.zero_space, num_bytes);
+
+ alsa_set_frag(old_frag_size, 6);
+
+ num_bytes = 0;
+ }
+ }
+
+ gAudioALSA.last_vpts += pts_len ;
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static uint32_t ao_get_current_vpts (void) {
+ int pos;
+ snd_pcm_channel_status_t pcm_stat;
+ int err;
+ int32_t diff;
+ uint32_t vpts;
+
+
+ if (gAudioALSA.audio_started) {
+ memset(&pcm_stat, 0, sizeof(snd_pcm_channel_status_t));
+ pcm_stat.channel = SND_PCM_CHANNEL_PLAYBACK;
+ if((err = snd_pcm_channel_status(gAudioALSA.front_handle,
+ &pcm_stat)) < 0) {
+ //Hide error report
+ perr("snd_pcm_channel_status() failed: %s\n", snd_strerror(err));
+ return 0;
+ }
+ pos = pcm_stat.scount;
+ }
+ else {
+ pos = 0;
+ }
+
+ diff = gAudioALSA.sync_bytes_in_buffer - pos;
+
+ vpts = gAudioALSA.sync_vpts - diff * 1024 / gAudioALSA.bytes_per_kpts;
+
+ xprintf (AUDIO|VERBOSE,"audio_alsa_out: get_current_vpts pos=%d diff=%d "
+ "vpts=%d sync_vpts=%d sync_bytes_in_buffer %d\n", pos, diff,
+ vpts, gAudioALSA.sync_vpts,gAudioALSA.sync_bytes_in_buffer);
+
+ return vpts;
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static void ao_put_samples(int16_t* output_samples,
+ uint32_t num_samples, uint32_t pts_) {
+ uint32_t vpts;
+ uint32_t audio_vpts;
+ uint32_t master_vpts;
+ int32_t diff, gap;
+ int bDropPackage = 0;
+ snd_pcm_channel_status_t status_front;
+ int err;
+ uint16_t sample_buffer[gAudioALSA.frag_size];
+
+
+ xprintf(VERBOSE|AUDIO, "Audio : play %d samples at pts %d pos %d \n",
+ num_samples, pts_, gAudioALSA.bytes_in_buffer);
+
+ if (gAudioALSA.front_handle == NULL)
+ return;
+
+ // if(gAudioALSA.frag_size != num_samples) {
+ // alsa_set_frag(num_samples, 6);
+ // }
+
+ vpts = metronom_got_audio_samples (pts_, num_samples);
+
+ /*
+ * check if these samples "fit" in the audio output buffer
+ * or do we have an audio "gap" here?
+ */
+
+ gap = vpts - gAudioALSA.last_vpts;
+
+ xprintf (VERBOSE|AUDIO, "audio_alsa_out: got %d samples, vpts=%d, "
+ "last_vpts=%d\n", num_samples, vpts, gAudioALSA.last_vpts);
+
+ if (gap > GAP_TOLERANCE) {
+ // ao_fill_gap (gap);
+ }
+ else if (gap < -GAP_TOLERANCE) {
+ bDropPackage = 1;
+ }
+
+ /*
+ * sync on master clock
+ */
+
+ audio_vpts = ao_get_current_vpts () ;
+ master_vpts = metronom_get_current_time ();
+ diff = audio_vpts - master_vpts;
+
+ xprintf (VERBOSE|AUDIO,"audio_alsa_out: syncing on master clock: "
+ "audio_vpts=%d master_vpts=%d\n", audio_vpts, master_vpts);
+
+ /*
+ * method 1 : resampling
+ */
+
+ /*
+ */
+
+ /*
+ * method 2: adjust master clock
+ */
+
+
+ if (abs(diff) > MAX_MASTER_CLOCK_DIV) {
+ printf ("master clock adjust time %d -> %d\n", master_vpts, audio_vpts);
+ metronom_adjust_clock (audio_vpts);
+ }
+
+ /*
+ * resample and output samples
+ */
+ if (!bDropPackage) {
+ int num_output_samples =
+ num_samples
+ * gAudioALSA.output_sample_rate
+ / gAudioALSA.input_sample_rate;
+
+ if(num_output_samples != gAudioALSA.frag_size)
+ alsa_set_frag(num_output_samples, 6);
+
+ if (num_output_samples != num_samples ) {
+ audio_out_resample_stereo (output_samples, num_samples,
+ sample_buffer, num_output_samples);
+ snd_pcm_write(gAudioALSA.front_handle, (void*)sample_buffer,
+ num_output_samples * 2 * gAudioALSA.num_channels);
+ }
+ else {
+ snd_pcm_write(gAudioALSA.front_handle, (void*)output_samples,
+ num_samples * 2 * gAudioALSA.num_channels);
+ }
+
+ memset(&status_front, 0, sizeof(snd_pcm_channel_status_t));
+ if((err = snd_pcm_channel_status(gAudioALSA.front_handle,
+ &status_front)) < 0) {
+ perr("snd_pcm_channel_status() failed: %s\n", snd_strerror(err));
+ }
+
+ /* Hummm, this seems made mistakes (flushing isnt good here). */
+ /*
+ if(status_front.underrun) {
+ perr("underrun, resetting front channel\n");
+ snd_pcm_channel_flush(gAudioALSA.front_handle, channel);
+ snd_pcm_playback_prepare(gAudioALSA.front_handle);
+ snd_pcm_write(gAudioALSA.front_handle, output_samples, num_samples<<1);
+ if((err = snd_pcm_channel_status(gAudioALSA.front_handle,
+ &status_front)) < 0) {
+ perr("snd_pcm_channel_status() failed: %s", snd_strerror(err));
+ }
+ if(status_front.underrun) {
+ perr("front write error, giving up\n");
+ }
+ }
+ */
+
+ /*
+ * remember vpts
+ */
+
+ gAudioALSA.sync_vpts = vpts;
+ gAudioALSA.sync_bytes_in_buffer = gAudioALSA.bytes_in_buffer;
+
+ /*
+ * step values
+ */
+ gAudioALSA.bytes_in_buffer +=
+ num_output_samples * 2 * gAudioALSA.num_channels;
+
+ gAudioALSA.audio_started = 1;
+ }
+ else {
+ printf ("audio_alsa_out: audio package (vpts = %d) dropped\n", vpts);
+ gAudioALSA.sync_vpts = vpts;
+ }
+
+ gAudioALSA.last_vpts =
+ vpts + num_samples * 90000 / gAudioALSA.input_sample_rate ;
+
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static void ao_close(void) {
+ int err;
+
+ if(gAudioALSA.front_handle) {
+ if((err = snd_pcm_playback_flush(gAudioALSA.front_handle)) < 0) {
+ perr("snd_pcm_channel_flush() failed: %s\n", snd_strerror(err));
+ }
+
+ if((err = snd_pcm_close(gAudioALSA.front_handle)) < 0) {
+ perr("snd_pcm_close() failed: %s\n", snd_strerror(err));
+ }
+
+ gAudioALSA.front_handle = NULL;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static int ao_is_mode_supported (int mode) {
+
+ switch (mode) {
+
+ case AO_MODE_STEREO:
+ case AO_MODE_AC3:
+ /*case AO_MODE_MONO: FIXME */
+ return 1;
+
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static ao_functions_t audio_alsaout = {
+ ao_is_mode_supported,
+ ao_open,
+ ao_put_samples,
+ ao_close
+};
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+static void sighandler(int signum) {
+}
+/* ------------------------------------------------------------------------- */
+/*
+ *
+ */
+ao_functions_t *audio_alsaout_init(void) {
+ int best_rate;
+ int devnum;
+ int err;
+ int direction = SND_PCM_OPEN_PLAYBACK;
+ int snd_default_card;
+ int snd_default_mixer_card;
+ int snd_default_mixer_device;
+ snd_pcm_info_t pcm_info;
+ snd_pcm_channel_info_t pcm_chan_info;
+ struct sigaction action;
+
+ /* Check if, at least, one card is installed */
+ if((devnum = snd_cards()) == 0) {
+ return NULL;
+ }
+ else {
+ snd_default_card = snd_defaults_card();
+ if((err = snd_card_load(snd_default_card)) < 0) {
+ perr("snd_card_load() failed: %s\n", snd_strerror(err));
+ }
+ xprintf (VERBOSE|AUDIO, "%d card(s) installed. Default = %d\n",
+ devnum, snd_default_card);
+
+ if((snd_default_mixer_card = snd_defaults_mixer_card()) < 0) {
+ perr("snd_defaults_mixer_card() failed: %s\n",
+ snd_strerror(snd_default_mixer_card));
+ }
+ xprintf (VERBOSE|AUDIO, "default mixer card = %d\n",
+ snd_default_mixer_card);
+
+ if((snd_default_mixer_device = snd_defaults_mixer_device()) < 0) {
+ perr("snd_defaults_mixer_device() failed: %s\n",
+ snd_strerror(snd_default_mixer_device));
+ }
+ xprintf (VERBOSE|AUDIO, "default mixer device = %d\n",
+ snd_default_mixer_device);
+ }
+
+ xprintf (VERBOSE|AUDIO, "Opening audio device...");
+
+ if((gAudioALSA.pcm_default_card = snd_defaults_pcm_card()) < 0) {
+ perr("There is no default pcm card.\n");
+ exit(1);
+ }
+ xprintf (VERBOSE|AUDIO, "snd_defaults_pcm_card() return %d\n",
+ gAudioALSA.pcm_default_card);
+
+ if((gAudioALSA.pcm_default_device = snd_defaults_pcm_device()) < 0) {
+ perr("There is no default pcm device.\n");
+ exit(1);
+ }
+ xprintf (VERBOSE|AUDIO, "snd_defaults_pcm_device() return %d\n",
+ gAudioALSA.pcm_default_device);
+
+ action.sa_handler = sighandler;
+ sigemptyset(&(action.sa_mask));
+ action.sa_flags = 0;
+ if(sigaction(SIGALRM, &action, NULL) != 0) {
+ perr("sigaction(SIGALRM) failed: %s\n", strerror(errno));
+ }
+ alarm(2);
+
+ if((err = snd_pcm_open(&gAudioALSA.front_handle, gAudioALSA.pcm_default_card,
+ gAudioALSA.pcm_default_device, direction)) < 0) {
+ perr("snd_pcm_open() failed: %s\n", snd_strerror(err));
+ perr(">>> Check if another program don't already use PCM <<<\n");
+ return NULL;
+ }
+
+ memset(&pcm_info, 0, sizeof(snd_pcm_info_t));
+ if((err = snd_pcm_info(gAudioALSA.front_handle, &pcm_info)) < 0) {
+ perr("snd_pcm_info() failed: %s\n", snd_strerror(err));
+ exit(1);
+ }
+
+ xprintf (VERBOSE|AUDIO, "snd_pcm_info():\n");
+ xprintf (VERBOSE|AUDIO, "---------------\n");
+ xprintf (VERBOSE|AUDIO, "type = 0x%x\n", pcm_info.type);
+ xprintf (VERBOSE|AUDIO, "flags = 0x%x\n", pcm_info.flags);
+ xprintf (VERBOSE|AUDIO, "id = '%s'\n", pcm_info.id);
+ xprintf (VERBOSE|AUDIO, "name = '%s'\n", pcm_info.name);
+ xprintf (VERBOSE|AUDIO, "playback = %d\n", pcm_info.playback);
+ xprintf (VERBOSE|AUDIO, "capture = %d\n", pcm_info.capture);
+
+ memset(&pcm_chan_info, 0, sizeof(snd_pcm_channel_info_t));
+ pcm_chan_info.channel = SND_PCM_CHANNEL_PLAYBACK;
+ if((err = snd_pcm_channel_info(gAudioALSA.front_handle,
+ &pcm_chan_info)) < 0) {
+ perr("snd_pcm_channel_info() failed: %s\n", snd_strerror(err));
+ exit(1);
+ }
+
+ best_rate = pcm_chan_info.rates;
+
+ xprintf (VERBOSE|AUDIO, "best_rate = %d\n", best_rate);
+ xprintf (VERBOSE|AUDIO, "snd_pcm_channel_info(PLAYBACK):\n");
+ xprintf (VERBOSE|AUDIO, "-------------------------------\n");
+ xprintf (VERBOSE|AUDIO, "subdevice = %d\n",
+ pcm_chan_info.subdevice);
+ xprintf (VERBOSE|AUDIO, "subname = %s\n",
+ pcm_chan_info.subname);
+ xprintf (VERBOSE|AUDIO, "channel = %d\n",
+ pcm_chan_info.channel);
+ xprintf (VERBOSE|AUDIO, "mode = %d\n",
+ pcm_chan_info.mode);
+ xprintf (VERBOSE|AUDIO, "flags = 0x%x\n",
+ pcm_chan_info.flags);
+ xprintf (VERBOSE|AUDIO, "formats = %d\n",
+ pcm_chan_info.formats);
+ xprintf (VERBOSE|AUDIO, "rates = %d\n",
+ pcm_chan_info.rates);
+ xprintf (VERBOSE|AUDIO, "min_rate = %d\n",
+ pcm_chan_info.min_rate);
+ xprintf (VERBOSE|AUDIO, "max_rate = %d\n",
+ pcm_chan_info.max_rate);
+ xprintf (VERBOSE|AUDIO, "min_voices = %d\n",
+ pcm_chan_info.min_voices);
+ xprintf (VERBOSE|AUDIO, "max_voices = %d\n",
+ pcm_chan_info.max_voices);
+ xprintf (VERBOSE|AUDIO, "buffer_size = %d\n",
+ pcm_chan_info.buffer_size);
+ xprintf (VERBOSE|AUDIO, "min_fragment_size = %d\n",
+ pcm_chan_info.min_fragment_size);
+ xprintf (VERBOSE|AUDIO, "max_fragment_size = %d\n",
+ pcm_chan_info.max_fragment_size);
+ xprintf (VERBOSE|AUDIO, "fragment_align = %d\n",
+ pcm_chan_info.fragment_align);
+ xprintf (VERBOSE|AUDIO, "fifo_size = %d\n",
+ pcm_chan_info.fifo_size);
+ xprintf (VERBOSE|AUDIO, "transfer_block_size = %d\n",
+ pcm_chan_info.transfer_block_size);
+ xprintf (VERBOSE|AUDIO, "mmap_size = %ld\n",
+ pcm_chan_info.mmap_size);
+ xprintf (VERBOSE|AUDIO, "mixer_device = %d\n",
+ pcm_chan_info.mixer_device);
+
+ snd_pcm_close (gAudioALSA.front_handle);
+ gAudioALSA.front_handle = NULL;
+
+ return &audio_alsaout;
+}
diff --git a/src/audio_out/audio_alsa_out.h b/src/audio_out/audio_alsa_out.h
new file mode 100644
index 000000000..123282262
--- /dev/null
+++ b/src/audio_out/audio_alsa_out.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2000 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
+ *
+ * $Id: audio_alsa_out.h,v 1.1 2001/04/24 20:53:00 f1rmb Exp $
+ */
+#ifndef _AUDIO_ALSA_OUT_H_
+#define _AUDIO_ALSA_OUT_H_ 1
+
+ao_functions_t *audio_alsaout_init(void);
+
+#endif
+
diff --git a/src/audio_out/audio_esd_out.c b/src/audio_out/audio_esd_out.c
new file mode 100644
index 000000000..259364a55
--- /dev/null
+++ b/src/audio_out/audio_esd_out.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2000 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
+ */
+
+#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 <sys/time.h>
+#include <inttypes.h>
+
+#include "xine/xine.h"
+#include "xine/monitor.h"
+#include "xine/audio_out.h"
+#include "resample.h"
+#include "xine/metronom.h"
+#include "xine/ac3.h"
+#include "xine/utils.h"
+
+#define GAP_TOLERANCE 15000
+#define MAX_MASTER_CLOCK_DIV 5000
+
+extern uint32_t xine_debug;
+
+typedef struct _audio_esd_globals {
+
+ int audio_fd;
+
+ int32_t output_sample_rate, input_sample_rate;
+ int32_t output_rate_correction;
+ double sample_rate_factor;
+ uint32_t num_channels;
+
+ uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */
+ uint32_t last_vpts; /* vpts at which last written package ends */
+
+ uint32_t sync_vpts; /* this syncpoint is used as a starting point */
+ uint32_t sync_bytes_in_buffer; /* for vpts <-> samplecount assoc */
+
+ int audio_step; /* pts per 32 768 samples (sample = #bytes/2) */
+ int32_t bytes_per_kpts; /* bytes per 1024/90000 sec */
+
+ int16_t *zero_space;
+
+ int audio_started;
+
+} audio_esd_globals_t;
+
+static audio_esd_globals_t gAudioESD;
+
+/*
+ * open the audio device for writing to
+ */
+static int ao_open(uint32_t bits, uint32_t rate, int mode)
+{
+ esd_format_t format;
+
+ printf ("audio_esd_out: ao_open rate=%d, mode=%d\n", rate, mode);
+
+ if ((mode != AO_MODE_STEREO) && (mode != AO_MODE_MONO)) {
+ printf ("ESD Driver only supports mono/stereo output modes at the moment\n");
+ return -1;
+ }
+
+ if (gAudioESD.audio_fd > -1) {
+
+ if (rate == gAudioESD.input_sample_rate)
+ return 1;
+
+ close (gAudioESD.audio_fd);
+ }
+
+ gAudioESD.input_sample_rate = rate;
+ gAudioESD.bytes_in_buffer = 0;
+ gAudioESD.last_vpts = 0;
+ gAudioESD.output_rate_correction = 0;
+ gAudioESD.sync_vpts = 0;
+ gAudioESD.sync_bytes_in_buffer = 0;
+ gAudioESD.audio_started = 0;
+
+ /*
+ * open stream to ESD server
+ */
+
+ format = ESD_STREAM | ESD_PLAY | ESD_BITS16;
+ if (mode == AO_MODE_STEREO) {
+ format |= ESD_STEREO;
+ gAudioESD.num_channels = 2;
+ } else {
+ format |= ESD_MONO;
+ gAudioESD.num_channels = 1;
+ }
+ gAudioESD.output_sample_rate = gAudioESD.input_sample_rate;
+ if (gAudioESD.output_sample_rate > 44100)
+ gAudioESD.output_sample_rate = 44100;
+
+ gAudioESD.audio_fd=esd_play_stream(format, gAudioESD.output_sample_rate, NULL, NULL);
+ if(gAudioESD.audio_fd < 0) {
+ printf("audio_esd_out: Connecting to ESD server %s: %s\n",
+ getenv("ESPEAKER"), strerror(errno));
+ return -1;
+ }
+
+ xprintf (VERBOSE|AUDIO, "audio_esd_out: %d channels\n",gAudioESD.num_channels);
+
+ gAudioESD.sample_rate_factor = (double) gAudioESD.output_sample_rate / (double) gAudioESD.input_sample_rate;
+ gAudioESD.audio_step = (uint32_t) 90000 * (uint32_t) 32768
+ / gAudioESD.input_sample_rate;
+ gAudioESD.bytes_per_kpts = gAudioESD.output_sample_rate * gAudioESD.num_channels * 2 * 1024 / 90000;
+
+ xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 samples\n", gAudioESD.audio_step);
+
+ metronom_set_audio_rate (gAudioESD.audio_step);
+
+ return 1;
+}
+
+static uint32_t ao_get_current_vpts (void) {
+
+ int32_t diff ;
+ uint32_t vpts ;
+
+ if (gAudioESD.audio_started)
+ diff = 0;
+ else
+ diff = gAudioESD.sync_bytes_in_buffer;
+
+ vpts = gAudioESD.sync_vpts - diff * 1024 / gAudioESD.bytes_per_kpts;
+
+// xprintf (AUDIO|VERBOSE,"audio_esd_out: get_current_vpts pos=%d diff=%d vpts=%d sync_vpts=%d\n",
+// pos, diff, vpts, gAudioESD.sync_vpts);
+
+ return vpts;
+}
+
+static void ao_fill_gap (uint32_t pts_len) {
+
+ int num_bytes = pts_len * gAudioESD.bytes_per_kpts / 1024;
+
+ num_bytes = (num_bytes / 4) * 4;
+
+ printf ("audio_esd_out: inserting %d 0-bytes to fill a gap of %d pts\n",num_bytes, pts_len);
+
+ gAudioESD.bytes_in_buffer += num_bytes;
+
+ while (num_bytes>0) {
+ if (num_bytes>8192) {
+ write(gAudioESD.audio_fd, gAudioESD.zero_space, 8192);
+ num_bytes -= 8192;
+ } else {
+ write(gAudioESD.audio_fd, gAudioESD.zero_space, num_bytes);
+ num_bytes = 0;
+ }
+ }
+
+ gAudioESD.last_vpts += pts_len;
+}
+
+static void ao_write_audio_data(int16_t* output_samples, uint32_t num_samples,
+ uint32_t pts_)
+{
+
+ uint32_t vpts,
+ audio_vpts,
+ master_vpts;
+ int32_t diff, gap;
+ int bDropPackage;
+ uint16_t sample_buffer[8192];
+
+
+ if (gAudioESD.audio_fd<0)
+ return;
+
+ vpts = metronom_got_audio_samples (pts_, num_samples);
+
+ xprintf (VERBOSE|AUDIO, "audio_esd_out: got %d samples, vpts=%d, last_vpts=%d\n",
+ num_samples, vpts, gAudioESD.last_vpts);
+
+ /*
+ * check if these samples "fit" in the audio output buffer
+ * or do we have an audio "gap" here?
+ */
+
+ gap = vpts - gAudioESD.last_vpts ;
+
+ /*
+ printf ("audio_esd_out: gap = %d - %d + %d = %d\n",
+ vpts, gAudioESD.last_vpts, diff, gap);
+ */
+
+ bDropPackage = 0;
+
+ if (gap>GAP_TOLERANCE) {
+ ao_fill_gap (gap);
+ } else if (gap<-GAP_TOLERANCE) {
+ bDropPackage = 1;
+ }
+
+ /*
+ * sync on master clock
+ */
+
+ audio_vpts = ao_get_current_vpts () ;
+ master_vpts = metronom_get_current_time ();
+ diff = audio_vpts - master_vpts;
+
+ xprintf (AUDIO|VERBOSE, "audio_esd_out: syncing on master clock: audio_vpts=%d master_vpts=%d\n",
+ audio_vpts, master_vpts);
+ /*
+ printf ("audio_esd_out: audio_vpts=%d <=> master_vpts=%d (diff=%d)\n",
+ audio_vpts, master_vpts, diff);
+ */
+
+ /*
+ * adjust master clock
+ */
+
+ if (abs(diff)>MAX_MASTER_CLOCK_DIV) {
+ printf ("master clock adjust time %d -> %d (diff: %d)\n", master_vpts, audio_vpts, diff);
+ metronom_adjust_clock (audio_vpts);
+ }
+
+ /*
+ * resample and output samples
+ */
+
+ if (!bDropPackage) {
+ int num_output_samples = num_samples * (gAudioESD.output_sample_rate + gAudioESD.output_rate_correction) / gAudioESD.input_sample_rate;
+
+
+ audio_out_resample_stereo (output_samples, num_samples,
+ sample_buffer, num_output_samples);
+
+ write(gAudioESD.audio_fd, sample_buffer, num_output_samples * 2 * gAudioESD.num_channels);
+
+ xprintf (AUDIO|VERBOSE, "audio_esd_out :audio package written\n");
+
+ /*
+ * remember vpts
+ */
+
+ gAudioESD.sync_vpts = vpts;
+ gAudioESD.sync_bytes_in_buffer = gAudioESD.bytes_in_buffer;
+
+ /*
+ * step values
+ */
+
+ gAudioESD.bytes_in_buffer += num_output_samples * 2 * gAudioESD.num_channels;
+ gAudioESD.audio_started = 1;
+ } else {
+ printf ("audio_esd_out: audio package (vpts = %d) dropped\n", vpts);
+ gAudioESD.sync_vpts = vpts;
+ }
+
+ gAudioESD.last_vpts = vpts + num_samples * 90000 / gAudioESD.input_sample_rate ;
+}
+
+
+static void ao_close(void)
+{
+ close(gAudioESD.audio_fd);
+ gAudioESD.audio_fd = -1;
+}
+
+static int ao_is_mode_supported (int mode) {
+ return ((mode == AO_MODE_STEREO) || (mode == AO_MODE_MONO));
+}
+
+static ao_functions_t audio_esdout = {
+ ao_is_mode_supported,
+ ao_open,
+ ao_write_audio_data,
+ ao_close,
+};
+
+
+ao_functions_t *audio_esdout_init (void)
+{
+ int audio_fd;
+
+ /*
+ * open stream to ESD server
+ */
+
+ xprintf(VERBOSE|AUDIO, "Connecting to ESD server...");
+ audio_fd = esd_open_sound(NULL);
+
+ if(audio_fd < 0)
+ {
+ char *server = getenv("ESPEAKER");
+
+ // print a message so the user knows why ESD failed
+ printf("Can't connect to %s ESD server: %s\n",
+ server ? server : "local", strerror(errno));
+
+ return NULL;
+ } // else
+// xprintf(VERBOSE|AUDIO, " %s\n", gAudioESD.audio_dev);
+
+ close(audio_fd);
+
+ gAudioESD.output_sample_rate = 0;
+ gAudioESD.zero_space = xmalloc (8192);
+
+ return &audio_esdout;
+}
diff --git a/src/audio_out/audio_esd_out.h b/src/audio_out/audio_esd_out.h
new file mode 100644
index 000000000..a98dcc34f
--- /dev/null
+++ b/src/audio_out/audio_esd_out.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2000 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
+ */
+
+#ifndef _AUDIO_ESD_OUT_H_
+#define _AUDIO_ESD_OUT_H_ 1
+
+ao_functions_t *audio_esdout_init(void);
+
+#endif
diff --git a/src/audio_out/audio_oss_out.c b/src/audio_out/audio_oss_out.c
new file mode 100644
index 000000000..18d11ff5d
--- /dev/null
+++ b/src/audio_out/audio_oss_out.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2000 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
+ *
+ * $Id: audio_oss_out.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+#if defined(__OpenBSD__)
+#include <soundcard.h>
+#elif defined(__FreeBSD__)
+#include <machine/soundcard.h>
+#else
+#include <sys/soundcard.h>
+#endif
+#include <sys/ioctl.h>
+#include <inttypes.h>
+
+#include "xine/xine.h"
+#include "xine/monitor.h"
+#include "xine/audio_out.h"
+#include "resample.h"
+#include "xine/metronom.h"
+#include "xine/ac3.h"
+#include "xine/utils.h"
+
+#define AUDIO_NUM_FRAGMENTS 15
+#define AUDIO_FRAGMENT_SIZE 8192
+
+#define GAP_TOLERANCE 15000
+#define MAX_MASTER_CLOCK_DIV 5000
+
+extern uint32_t xine_debug;
+
+#define DSP_TEMPLATE "/dev/dsp%d"
+
+typedef struct _audio_oss_globals {
+
+ char audio_dev[20];
+ int audio_fd;
+
+ int32_t output_sample_rate, input_sample_rate;
+ int32_t output_rate_correction;
+ double sample_rate_factor;
+ uint32_t num_channels;
+
+ uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */
+ uint32_t last_vpts; /* vpts at which last written package ends */
+
+ uint32_t sync_vpts; /* this syncpoint is used as a starting point */
+ uint32_t sync_bytes_in_buffer; /* for vpts <-> samplecount assoc */
+
+ int audio_step; /* pts per 32 768 samples (sample = #bytes/2) */
+ int32_t bytes_per_kpts; /* bytes per 1024/90000 sec */
+
+ int16_t *zero_space;
+
+ int audio_started;
+
+} audio_oss_globals_t;
+
+static audio_oss_globals_t gAudioOSS;
+
+/*
+ * open the audio device for writing to
+ */
+static int ao_open(uint32_t bits, uint32_t rate, int mode)
+{
+ int tmp;
+ int fsize;
+
+ printf ("audio_oss_out: ao_open rate=%d, mode=%d\n", rate, mode);
+
+ if ((mode != AO_MODE_STEREO) && (mode != AO_MODE_MONO)) {
+ printf ("OSS Driver only supports mono/stereo output modes at the moment\n");
+ return -1;
+ }
+
+ if (gAudioOSS.audio_fd > -1) {
+
+ if (rate == gAudioOSS.input_sample_rate)
+ return 1;
+
+ close (gAudioOSS.audio_fd);
+ }
+
+ gAudioOSS.input_sample_rate = rate;
+ gAudioOSS.bytes_in_buffer = 0;
+ gAudioOSS.last_vpts = 0;
+ gAudioOSS.output_rate_correction = 0;
+ gAudioOSS.sync_vpts = 0;
+ gAudioOSS.sync_bytes_in_buffer = 0;
+ gAudioOSS.audio_started = 0;
+
+ /*
+ * open audio device
+ */
+
+ gAudioOSS.audio_fd=open(gAudioOSS.audio_dev,O_WRONLY|O_NDELAY);
+ if(gAudioOSS.audio_fd < 0) {
+ printf("audio_oss_out: Opening audio device %s: %s\n",
+ gAudioOSS.audio_dev, strerror(errno));
+ return -1;
+ }
+
+ /* We wanted non blocking open but now put it back to normal */
+ fcntl(gAudioOSS.audio_fd, F_SETFL, fcntl(gAudioOSS.audio_fd, F_GETFL)&~FNDELAY);
+
+ /*
+ * configure audio device
+ */
+
+ tmp = (mode == AO_MODE_STEREO) ? 1 : 0;
+ ioctl(gAudioOSS.audio_fd,SNDCTL_DSP_STEREO,&tmp);
+
+ gAudioOSS.num_channels = tmp+1;
+ xprintf (VERBOSE|AUDIO, "audio_oss_out: %d channels\n",gAudioOSS.num_channels);
+
+ tmp = bits;
+ ioctl(gAudioOSS.audio_fd,SNDCTL_DSP_SAMPLESIZE,&tmp);
+
+ tmp = gAudioOSS.input_sample_rate;
+ ioctl(gAudioOSS.audio_fd,SNDCTL_DSP_SPEED, &tmp);
+ gAudioOSS.output_sample_rate = tmp;
+
+ xprintf (VERBOSE|AUDIO, "audio_oss_out: audio rate : %d requested, %d provided by device/sec\n",
+ gAudioOSS.input_sample_rate, gAudioOSS.output_sample_rate);
+
+ gAudioOSS.sample_rate_factor = (double) gAudioOSS.output_sample_rate / (double) gAudioOSS.input_sample_rate;
+ gAudioOSS.audio_step = (uint32_t) 90000 * (uint32_t) 32768
+ / gAudioOSS.input_sample_rate;
+ gAudioOSS.bytes_per_kpts = gAudioOSS.output_sample_rate * gAudioOSS.num_channels * 2 * 1024 / 90000;
+
+ xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 samples\n", gAudioOSS.audio_step);
+
+ metronom_set_audio_rate (gAudioOSS.audio_step);
+
+ /*
+ * audio buffer size handling
+ */
+
+ tmp=0 ;
+ fsize = AUDIO_FRAGMENT_SIZE;
+ while (fsize>0) {
+ fsize /=2;
+ tmp++;
+ }
+ tmp--;
+
+ tmp = (AUDIO_NUM_FRAGMENTS << 16) | tmp ;
+
+ xprintf (VERBOSE|AUDIO, "Audio buffer fragment info : %x\n",tmp);
+
+ ioctl(gAudioOSS.audio_fd,SNDCTL_DSP_SETFRAGMENT,&tmp);
+
+
+ return 1;
+}
+
+static uint32_t ao_get_current_vpts (void) {
+
+ int pos ;
+ int32_t diff ;
+ uint32_t vpts ;
+
+ count_info info;
+
+ if (gAudioOSS.audio_started) {
+ ioctl (gAudioOSS.audio_fd, SNDCTL_DSP_GETOPTR, &info);
+
+ pos = info.bytes;
+
+ } else
+ pos = 0;
+
+ diff = gAudioOSS.sync_bytes_in_buffer - pos;
+
+ vpts = gAudioOSS.sync_vpts - diff * 1024 / gAudioOSS.bytes_per_kpts;
+
+ xprintf (AUDIO|VERBOSE,"audio_oss_out: get_current_vpts pos=%d diff=%d vpts=%d sync_vpts=%d\n",
+ pos, diff, vpts, gAudioOSS.sync_vpts);
+
+ return vpts;
+}
+
+static void ao_fill_gap (uint32_t pts_len) {
+
+ int num_bytes = pts_len * gAudioOSS.bytes_per_kpts / 1024;
+
+ num_bytes = (num_bytes / 4) * 4;
+
+ printf ("audio_oss_out: inserting %d 0-bytes to fill a gap of %d pts\n",num_bytes, pts_len);
+
+ gAudioOSS.bytes_in_buffer += num_bytes;
+
+ while (num_bytes>0) {
+ if (num_bytes>8192) {
+ write(gAudioOSS.audio_fd, gAudioOSS.zero_space, 8192);
+ num_bytes -= 8192;
+ } else {
+ write(gAudioOSS.audio_fd, gAudioOSS.zero_space, num_bytes);
+ num_bytes = 0;
+ }
+ }
+
+ gAudioOSS.last_vpts += pts_len;
+}
+
+static void ao_write_audio_data(int16_t* output_samples, uint32_t num_samples,
+ uint32_t pts_)
+{
+
+ uint32_t vpts,
+ audio_vpts,
+ master_vpts;
+ int32_t diff, gap;
+ int bDropPackage;
+ uint16_t sample_buffer[8192];
+
+
+ if (gAudioOSS.audio_fd<0)
+ return;
+
+ vpts = metronom_got_audio_samples (pts_, num_samples);
+
+ xprintf (VERBOSE|AUDIO, "audio_oss_out: got %d samples, vpts=%d, last_vpts=%d\n",
+ num_samples, vpts, gAudioOSS.last_vpts);
+
+ /*
+ * check if these samples "fit" in the audio output buffer
+ * or do we have an audio "gap" here?
+ */
+
+ gap = vpts - gAudioOSS.last_vpts ;
+
+ /*
+ printf ("audio_oss_out: gap = %d - %d + %d = %d\n",
+ vpts, gAudioOSS.last_vpts, diff, gap);
+ */
+
+ bDropPackage = 0;
+
+ if (gap>GAP_TOLERANCE) {
+ ao_fill_gap (gap);
+ } else if (gap<-GAP_TOLERANCE) {
+ bDropPackage = 1;
+ }
+
+ /*
+ * sync on master clock
+ */
+
+ audio_vpts = ao_get_current_vpts () ;
+ master_vpts = metronom_get_current_time ();
+ diff = audio_vpts - master_vpts;
+
+ xprintf (AUDIO|VERBOSE, "audio_oss_out: syncing on master clock: audio_vpts=%d master_vpts=%d\n",
+ audio_vpts, master_vpts);
+ /*
+ printf ("audio_oss_out: audio_vpts=%d <=> master_vpts=%d (diff=%d)\n",
+ audio_vpts, master_vpts, diff);
+ */
+ /*
+ * method 1 : resampling
+ */
+
+ /*
+ if (abs(diff)>5000) {
+
+ if (diff>5000) {
+ ao_fill_gap (diff);
+ } else if (diff<-5000) {
+ bDropPackage = 1;
+ }
+
+ } else if (abs(diff)>1000) {
+ gAudioOSS.output_rate_correction = diff/10 ;
+
+ printf ("audio_oss_out: diff = %d => rate correction : %d\n", diff, gAudioOSS.output_rate_correction);
+
+ if ( gAudioOSS.output_rate_correction < -500)
+ gAudioOSS.output_rate_correction = -500;
+ else if ( gAudioOSS.output_rate_correction > 500)
+ gAudioOSS.output_rate_correction = 500;
+ }
+ */
+
+ /*
+ * method 2: adjust master clock
+ */
+
+
+ if (abs(diff)>MAX_MASTER_CLOCK_DIV) {
+ printf ("master clock adjust time %d -> %d (diff: %d)\n", master_vpts, audio_vpts, diff);
+ metronom_adjust_clock (audio_vpts);
+ }
+
+
+ /*
+ * resample and output samples
+ */
+
+ if (!bDropPackage) {
+ int num_output_samples = num_samples * (gAudioOSS.output_sample_rate + gAudioOSS.output_rate_correction) / gAudioOSS.input_sample_rate;
+
+
+ audio_out_resample_stereo (output_samples, num_samples,
+ sample_buffer, num_output_samples);
+
+ write(gAudioOSS.audio_fd, sample_buffer, num_output_samples * 2 * gAudioOSS.num_channels);
+
+ xprintf (AUDIO|VERBOSE, "audio_oss_out :audio package written\n");
+
+ /*
+ * remember vpts
+ */
+
+ gAudioOSS.sync_vpts = vpts;
+ gAudioOSS.sync_bytes_in_buffer = gAudioOSS.bytes_in_buffer;
+
+ /*
+ * step values
+ */
+
+ gAudioOSS.bytes_in_buffer += num_output_samples * 2 * gAudioOSS.num_channels;
+ gAudioOSS.audio_started = 1;
+ } else {
+ printf ("audio_oss_out: audio package (vpts = %d) dropped\n", vpts);
+ gAudioOSS.sync_vpts = vpts;
+ }
+
+ gAudioOSS.last_vpts = vpts + num_samples * 90000 / gAudioOSS.input_sample_rate ;
+}
+
+
+static void ao_close(void)
+{
+ close(gAudioOSS.audio_fd);
+ gAudioOSS.audio_fd = -1;
+}
+
+static int ao_is_mode_supported (int mode) {
+ return ((mode == AO_MODE_STEREO) || (mode == AO_MODE_MONO));
+}
+
+static ao_functions_t audio_ossout = {
+ ao_is_mode_supported,
+ ao_open,
+ ao_write_audio_data,
+ ao_close,
+};
+
+
+ao_functions_t *audio_ossout_init (void)
+{
+ int caps;
+ char devname[] = "/dev/dsp\0\0\0";
+ int best_rate;
+ int rate ;
+ int devnum;
+ int audio_fd;
+
+ /*
+ * find best device driver/channel
+ */
+
+ xprintf (VERBOSE|AUDIO, "Opening audio device...");
+ devnum = 0;
+ best_rate = 0;
+ while (devnum<16) {
+ audio_fd=open(devname,O_WRONLY|O_NDELAY);
+
+ if (audio_fd>0) {
+
+ /* test bitrate capability */
+
+ rate = 48000;
+ ioctl(audio_fd,SNDCTL_DSP_SPEED, &rate);
+ if (rate>best_rate) {
+ strncpy (gAudioOSS.audio_dev, devname, 19);
+ best_rate = rate;
+ }
+
+ close (audio_fd);
+ }
+
+ sprintf(devname, DSP_TEMPLATE, devnum);
+ devnum++;
+ }
+
+ /*
+ * open that device
+ */
+
+ audio_fd=open(gAudioOSS.audio_dev, O_WRONLY|O_NDELAY);
+
+ if(audio_fd < 0)
+ {
+ xprintf(VERBOSE|AUDIO, "%s: Opening audio device %s\n",
+ strerror(errno), gAudioOSS.audio_dev);
+
+ return NULL;
+
+ } else
+ xprintf (VERBOSE|AUDIO, " %s\n", gAudioOSS.audio_dev);
+
+ ioctl (audio_fd, SNDCTL_DSP_GETCAPS, &caps);
+
+ if ((caps & DSP_CAP_REALTIME) > 0) {
+ xprintf (VERBOSE|AUDIO, "audio_out : realtime check: passed :-)\n");
+ } else {
+ xprintf (VERBOSE|AUDIO, "audio_out : realtime check: *FAILED* :-(((((\n\n");
+ }
+
+ if ((caps & DSP_CAP_TRIGGER) > 0) {
+ xprintf (VERBOSE|AUDIO, "audio_out : trigger check : passed :-)\n");
+ } else {
+ xprintf (VERBOSE|AUDIO, "audio_out : trigger check : *FAILED* :-(((((\n");
+ }
+
+ close (audio_fd);
+
+ gAudioOSS.output_sample_rate = 0;
+
+ gAudioOSS.zero_space = xmalloc (8192);
+
+ return &audio_ossout;
+}
diff --git a/src/audio_out/audio_oss_out.h b/src/audio_out/audio_oss_out.h
new file mode 100644
index 000000000..2618acbf9
--- /dev/null
+++ b/src/audio_out/audio_oss_out.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2000 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
+ *
+ * $Id: audio_oss_out.h,v 1.1 2001/04/24 20:53:00 f1rmb Exp $
+ */
+#ifndef _AUDIO_OSS_OUT_H_
+#define _AUDIO_OSS_OUT_H_ 1
+
+ao_functions_t *audio_ossout_init(void);
+
+#endif
diff --git a/src/audio_out/resample.c b/src/audio_out/resample.c
new file mode 100644
index 000000000..9f464b276
--- /dev/null
+++ b/src/audio_out/resample.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2000 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
+ *
+ * $Id: resample.c,v 1.1 2001/04/24 20:53:00 f1rmb Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <inttypes.h>
+
+/* contributed by paul flinders */
+
+void audio_out_resample_stereo(int16_t* input_samples, uint32_t in_samples,
+ int16_t* output_samples, uint32_t out_samples)
+{
+ int osample;
+ /* 16+16 fixed point math */
+ uint32_t isample = 0;
+ uint32_t istep = (in_samples << 16)/out_samples;
+
+#ifdef VERBOSE
+ printf ("Audio : resample %d samples to %d\n",
+ in_samples, out_samples);
+#endif
+
+ for (osample = 0; osample < out_samples - 1; osample++) {
+ int s1;
+ int s2;
+ int16_t os;
+ uint32_t t = isample&0xffff;
+
+ /* don't "optimize" the (isample >> 16)*2 to (isample >> 15) */
+ s1 = input_samples[(isample >> 16)*2];
+ s2 = input_samples[(isample >> 16)*2+2];
+
+ os = (s1 * (0x10000-t)+ s2 * t) >> 16;
+ output_samples[osample * 2] = os;
+
+ s1 = input_samples[(isample >> 16)*2+1];
+ s2 = input_samples[(isample >> 16)*2+3];
+
+ os = (s1 * (0x10000-t)+ s2 * t) >> 16;
+ output_samples[(osample * 2 )+1] = os;
+ isample += istep;
+ }
+ output_samples[out_samples*2-2] = input_samples[in_samples*2-2];
+ output_samples[out_samples*2-1] = input_samples[in_samples*2-1];
+}
+
diff --git a/src/audio_out/resample.h b/src/audio_out/resample.h
new file mode 100644
index 000000000..11c9e4a45
--- /dev/null
+++ b/src/audio_out/resample.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2000 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
+ *
+ * $Id: resample.h,v 1.1 2001/04/24 20:53:00 f1rmb Exp $
+ *
+ * utilitiy functions for audio drivers
+ *
+ * FIXME: not all of them are implemented yet
+ */
+
+#ifndef HAVE_RESAMPLE_H
+#define HAVE_RESAMPLE_H
+
+void audio_out_resample_stereo(int16_t* input_samples, uint32_t in_samples,
+ int16_t* output_samples, uint32_t out_samples);
+
+void audio_out_resample_mono(int16_t* input_samples, uint32_t in_samples,
+ int16_t* output_samples, uint32_t out_samples);
+
+void audio_out_resample_4channel(int16_t* input_samples, uint32_t in_samples,
+ int16_t* output_samples, uint32_t out_samples);
+
+void audio_out_resample_5channel(int16_t* input_samples, uint32_t in_samples,
+ int16_t* output_samples, uint32_t out_samples);
+
+#endif