diff options
Diffstat (limited to 'src/audio_out')
-rw-r--r-- | src/audio_out/Makefile.am | 45 | ||||
-rw-r--r-- | src/audio_out/audio_alsa_out.c | 761 | ||||
-rw-r--r-- | src/audio_out/audio_alsa_out.h | 28 | ||||
-rw-r--r-- | src/audio_out/audio_esd_out.c | 329 | ||||
-rw-r--r-- | src/audio_out/audio_esd_out.h | 26 | ||||
-rw-r--r-- | src/audio_out/audio_oss_out.c | 452 | ||||
-rw-r--r-- | src/audio_out/audio_oss_out.h | 27 | ||||
-rw-r--r-- | src/audio_out/resample.c | 67 | ||||
-rw-r--r-- | src/audio_out/resample.h | 42 |
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(¶ms, 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(¶ms.format, &format, sizeof(format)); + + snd_pcm_playback_flush(gAudioALSA.front_handle); + + if((err = snd_pcm_channel_params(gAudioALSA.front_handle, ¶ms)) < 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 |