diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/audio_out/Makefile.am | 8 | ||||
-rw-r--r-- | src/audio_out/audio_alsa_out.c | 1039 |
2 files changed, 403 insertions, 644 deletions
diff --git a/src/audio_out/Makefile.am b/src/audio_out/Makefile.am index c250b5aa2..e740f0bb9 100644 --- a/src/audio_out/Makefile.am +++ b/src/audio_out/Makefile.am @@ -25,14 +25,14 @@ endif # scheme "xineplug_ao_out_" # #lib_LTLIBRARIES = xineplug_ao_out_oss.la $(alsa_module) $(esd_module) -lib_LTLIBRARIES = xineplug_ao_out_oss.la +lib_LTLIBRARIES = xineplug_ao_out_oss.la $(alsa_module) 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_LIBADD = $(ALSA_LIBS) -#xineplug_ao_out_alsa_la_LDFLAGS = -avoid-version -module +xineplug_ao_out_alsa_la_SOURCES = audio_alsa_out.c resample.c +xineplug_ao_out_alsa_la_LIBADD = $(ALSA_LIBS) +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_LIBADD = $(ESD_LIBS) diff --git a/src/audio_out/audio_alsa_out.c b/src/audio_out/audio_alsa_out.c index 44dd0f2f9..0124279d0 100644 --- a/src/audio_out/audio_alsa_out.c +++ b/src/audio_out/audio_alsa_out.c @@ -17,7 +17,7 @@ * 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.3 2001/04/27 11:32:39 f1rmb Exp $ + * $Id: audio_alsa_out.c,v 1.4 2001/05/30 18:31:32 joachim_koenig Exp $ */ #ifdef HAVE_CONFIG_H @@ -25,746 +25,505 @@ #endif #include <stdio.h> -#include <stdlib.h> -#include <signal.h> -#include <string.h> #include <errno.h> - +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <math.h> #include <sys/asoundlib.h> -//#include <linux/asound.h> -//#include <linux/asequencer.h> -//#include <linux/asoundid.h> - +#include <sys/ioctl.h> #include <inttypes.h> + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) +#define error(...) do {\ + fprintf(stderr, "XINE lib %s:%d:(%s) ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, __VA_ARGS__); \ + putc('\n', stderr); \ +} while (0) +#else +#define error(args...) do {\ + fprintf(stderr, "XINE lib %s:%d:(%s) ", __FILE__, __LINE__, __FUNCTION__); \ + fprintf(stderr, ##args); \ + putc('\n', stderr); \ +} while (0) +#endif + + #include "xine_internal.h" #include "monitor.h" #include "audio_out.h" -#include "metronom.h" #include "resample.h" -#include "libac3/ac3.h" +#include "metronom.h" #include "utils.h" -#define AO_OUT_ALSA_IFACE_VERSION 1 +#ifndef AFMT_S16_NE +# if defined(sparc) || defined(__sparc__) || defined(PPC) +/* Big endian machines */ +# define AFMT_S16_NE AFMT_S16_BE +# else +# define AFMT_S16_NE AFMT_S16_LE +# endif +#endif -#define AUDIO_NUM_FRAGMENTS 15 -#define AUDIO_FRAGMENT_SIZE 8192 +#define AO_OUT_ALSA_IFACE_VERSION 1 #define GAP_TOLERANCE 15000 #define MAX_MASTER_CLOCK_DIV 5000 -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 */ +typedef struct alsa_functions_s { - int16_t *zero_space; + ao_functions_t ao_functions; - int audio_started; - int pcm_default_card; - int pcm_default_device; + metronom_t *metronom; - 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; + char audio_dev[20]; + snd_pcm_t * audio_fd; + int open_mode; -} audio_alsa_globals_t; + int32_t output_sample_rate, input_sample_rate; + int32_t output_rate_correction; + double sample_rate_factor; + uint32_t num_channels; +/* The ALSA drivers handle "frames" instead of bytes. + * So for a Stereo 16 Bit Sample, each frame would equil 4 bytes. + * For this plugin, we will use frames instead of bytes for everything. + * The term sample is also equil to frames + */ + snd_pcm_sframes_t frames_in_buffer; /* number of frames writen to audio hardware */ + uint32_t last_vpts; /* vpts at which last written package ends */ -static audio_alsa_globals_t gAudioALSA; + uint32_t sync_vpts; /* this syncpoint is used as a starting point */ + snd_pcm_sframes_t sync_frames_in_buffer; /* for vpts <-> samplecount assoc */ -/* ------------------------------------------------------------------------- */ -/* - * - */ -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; + int audio_step; /* pts per 32 768 frames (frame = #bytes/2(16 bits)/channels) */ +/* frames = pts * rate / pts_per_second */ +/* pts = frame * pts_per_second / rate */ - 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; + snd_pcm_sframes_t pts_per_second; /* pts per second */ + + int16_t *zero_space; - 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; - } + int audio_started; - 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; + int capabilities; - // 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); -} -/* ------------------------------------------------------------------------- */ +} alsa_functions_t; + + static snd_output_t *jcd_out; /* * open the audio device for writing to */ -static int ao_open(metronom_t *metronom, - 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; - } +static int ao_open(ao_functions_t *this_gen, uint32_t bits, uint32_t rate, int mode) +{ + alsa_functions_t *this = (alsa_functions_t *) this_gen; + snd_pcm_stream_t direction = SND_PCM_STREAM_PLAYBACK; + snd_pcm_hw_params_t *params; + snd_pcm_sw_params_t *swparams; + snd_pcm_sframes_t buffer_time; + snd_pcm_sframes_t period_time,tmp; + int err, step; + int open_mode=1; //NONBLOCK + //int open_mode=0; //BLOCK + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_sw_params_alloca(&swparams); - 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; + err = snd_output_stdio_attach(&jcd_out, stderr, 0); + if (((mode & AO_CAP_MODE_STEREO) == 0) && ((mode & AO_CAP_MODE_AC3) == 0)) { + error ("ALSA Driver only supports AC3/stereo output modes at the moment"); + return -1; + } else { + this->num_channels = 2; } - else { - mode = SND_PCM_MODE_BLOCK; + if (this->audio_fd != NULL) { + error ("Already open...WHY!"); + snd_pcm_close (this->audio_fd); } + this->input_sample_rate = rate; + this->frames_in_buffer = 0; + this->last_vpts = 0; + this->output_rate_correction = 0; + this->sync_vpts = 0; + this->sync_frames_in_buffer = 0; + this->audio_started = 0; + this->open_mode = mode; + /* + * open audio device + */ - 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 (metronom, 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; + err=snd_pcm_open(&this->audio_fd, this->audio_dev, direction, open_mode); + if(err <0 ) { + error("snd_pcm_open() failed: %s", snd_strerror(err)); + error(">>> Check if another program don't already use PCM <<<"); + return -1; } + /* We wanted non blocking open but now put it back to normal */ + snd_pcm_nonblock(this->audio_fd, 0); + /* + * configure audio device + */ + err = snd_pcm_hw_params_any(this->audio_fd, params); + if (err < 0) { + error("Broken configuration for this PCM: no configurations available"); + goto __close; + } + /* set interleaved access */ + err = snd_pcm_hw_params_set_access(this->audio_fd, params, + SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) { + error("Access type not available"); + goto __close; + } + err = snd_pcm_hw_params_set_format(this->audio_fd, params, bits == 16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8); + if (err < 0) { + error("Sample format non available"); + goto __close; + } + err = snd_pcm_hw_params_set_channels(this->audio_fd, params, this->num_channels); + if (err < 0) { + error("Channels count non available"); + goto __close; + } + err = snd_pcm_hw_params_set_rate_near(this->audio_fd, params, rate, 0); + if (err < 0) { + error("Rate not available"); + goto __close; + } + buffer_time = snd_pcm_hw_params_set_buffer_time_near(this->audio_fd, params, + 500000, 0); + if (buffer_time < 0) { + error("Buffer time not available"); + goto __close; + } + step = 2; + period_time = 10000 * 2; + do { + period_time /= 2; + tmp = snd_pcm_hw_params_set_period_time_near(this->audio_fd, params, + period_time, 0); + if (tmp == period_time) { + period_time /= 3; + tmp = snd_pcm_hw_params_set_period_time_near(this->audio_fd, params, + period_time, 0); + if (tmp == period_time) + period_time = 10000 * 2; + } + if (period_time < 0) { + fprintf(stderr, "Period time not available"); + goto __close; + } + } while (buffer_time == period_time && period_time > 10000); + if (buffer_time == period_time) { + error("Buffer time and period time match, could not use"); + goto __close; + } + if ((err = snd_pcm_hw_params(this->audio_fd, params)) < 0) { + error("PCM hw_params failed: %s", snd_strerror(err)); + goto __close; + } +this->output_sample_rate = this->input_sample_rate; + this->sample_rate_factor = (double) this->output_sample_rate / (double) this->input_sample_rate; + this->audio_step = (double) 90000 * (double) 32768 + / this->input_sample_rate; + this->pts_per_second = 90000; + this->metronom->set_audio_rate(this->metronom, this->audio_step); + /* + * audio buffer size handling + */ + /* Copy current parameters into swparams */ + snd_pcm_sw_params_current(this->audio_fd, swparams); + tmp=snd_pcm_sw_params_set_xfer_align(this->audio_fd, swparams, 4); + /* Install swparams into current parameters */ + snd_pcm_sw_params(this->audio_fd, swparams); - printf ("actual rate: %d\n", pcm_chan_setup.format.rate); - - alsa_set_frag(1536, 6); - - gAudioALSA.bytes_in_buffer = 0; - + // snd_pcm_dump_setup(this->audio_fd, jcd_out); return 1; +__close: + snd_pcm_close (this->audio_fd); + this->audio_fd=NULL; + 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) { +static uint32_t ao_get_current_vpts (alsa_functions_t *this) +{ + snd_pcm_sframes_t pos ; + snd_pcm_status_t *pcm_stat; + snd_pcm_sframes_t delay; + int err; + uint32_t vpts ; + snd_pcm_status_alloca(&pcm_stat); + snd_pcm_status(this->audio_fd, pcm_stat); + /* Dump ALSA info to stderr */ + /* snd_pcm_status_dump(pcm_stat, jcd_out); */ + if (this->audio_started) { + err=snd_pcm_delay( this->audio_fd, &delay); + if(err < 0) { //Hide error report - perr("snd_pcm_channel_status() failed: %s\n", snd_strerror(err)); + error("snd_pcm_delay() failed"); return 0; } - pos = pcm_stat.scount; + /* Correction factor, bigger -, sound earlier + * bigger +, sound later + * current setting for SB Live + */ + pos = this->frames_in_buffer - delay + 1500; + } else { + pos=0; } - 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); - + vpts = ((double)pos * (double)this->pts_per_second / (double)this->input_sample_rate); return vpts; } -/* ------------------------------------------------------------------------- */ -/* - * - */ -static void ao_put_samples(metronom_t *metronom, - 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); +static void ao_fill_gap (alsa_functions_t *this, uint32_t pts_len) +{ + snd_pcm_sframes_t res; + int num_frames = (double)pts_len * (double)this->input_sample_rate / (double)this->pts_per_second; + num_frames = (num_frames / 4) * 4; + this->frames_in_buffer += num_frames; + while (num_frames>0) { + if (num_frames>2048) { + res=snd_pcm_writei(this->audio_fd, this->zero_space, 2048 ); + num_frames -= 2048; + } else { + res=snd_pcm_writei(this->audio_fd, this->zero_space, num_frames ); + num_frames = 0; + } + } + this->last_vpts += pts_len; +} - if (gAudioALSA.front_handle == NULL) +static void ao_write_audio_data(ao_functions_t *this_gen, + int16_t* output_samples, uint32_t num_samples, + uint32_t pts_) +{ + + alsa_functions_t *this = (alsa_functions_t *) this_gen; + uint32_t vpts, + audio_vpts, + master_vpts; + int32_t diff, gap; + int bDropPackage; + uint16_t sample_buffer[8192]; + snd_pcm_sframes_t res = 0; + + if (this->audio_fd == NULL) { + error("Nothing open"); return; + } - // if(gAudioALSA.frag_size != num_samples) { - // alsa_set_frag(num_samples, 6); - // } - - vpts = metronom->got_audio_samples (metronom, pts_, num_samples); - + vpts = this->metronom->got_audio_samples (this->metronom, 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) { + gap = vpts - this->last_vpts ; + bDropPackage = 0; + + if (gap>GAP_TOLERANCE) { + ao_fill_gap (this, gap); + } else if (gap<-GAP_TOLERANCE) { bDropPackage = 1; } - + /* * sync on master clock */ - - audio_vpts = ao_get_current_vpts () ; - master_vpts = metronom->get_current_time (metronom); + audio_vpts = ao_get_current_vpts (this) ; + master_vpts = this->metronom->get_current_time (this->metronom); 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 */ - - /* - */ - + if (abs(diff)>5000) { + if (diff>5000) { + error("Fill Gap"); + ao_fill_gap (this,diff); + } else if (diff<-5000) { + error("Drop"); + bDropPackage = 1; + } + } else if (abs(diff)>1000) { + this->output_rate_correction = diff/10 ; + error("diff = %d => rate correction : %d", diff, this->output_rate_correction); + if ( this->output_rate_correction < -500) + this->output_rate_correction = -500; + else if ( this->output_rate_correction > 500) + this->output_rate_correction = 500; + } /* * 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 (metronom, audio_vpts); + if (abs(diff)>MAX_MASTER_CLOCK_DIV) { + error ("master clock adjust time %d -> %d (diff: %d)", master_vpts, audio_vpts, diff); + this->metronom->adjust_clock (this->metronom, 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"); - } - } - */ - + /* Multiples of xfer_align eg:- 4 */ + int num_output_samples = ((num_samples * (this->output_sample_rate + this->output_rate_correction) / this->input_sample_rate / 4) * 4)+4; + audio_out_resample_stereo (output_samples, num_samples, + sample_buffer, num_output_samples); + do { + res=snd_pcm_avail_update(this->audio_fd); + usleep(3200); + } while (res<num_output_samples+512); + /* Special note, the new ALSA outputs in counts of frames. + * A Frame is one sample for all channels, so here a Stereo 16 bits frame is 4 bytes. + */ + if ((this->open_mode & AO_CAP_MODE_AC3) == 0) { + res=snd_pcm_writei(this->audio_fd, sample_buffer, num_output_samples); + } else { + res=snd_pcm_writei(this->audio_fd, output_samples, num_samples); + } + if(res != num_output_samples) error("BUFFER MAYBE FULL!!!!!!!!!!!!"); + if (res < 0) + error("writei returned error: %s", snd_strerror(res)); /* * remember vpts */ - - gAudioALSA.sync_vpts = vpts; - gAudioALSA.sync_bytes_in_buffer = gAudioALSA.bytes_in_buffer; - + this->sync_vpts = vpts; + this->sync_frames_in_buffer = this->frames_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; + this->frames_in_buffer += num_samples ; + this->audio_started = 1; + } else { + this->sync_vpts = vpts; } - gAudioALSA.last_vpts = - vpts + num_samples * 90000 / gAudioALSA.input_sample_rate ; - + this->last_vpts = vpts + num_samples * this->pts_per_second / this->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 void ao_close(ao_functions_t *this_gen) +{ + alsa_functions_t *this = (alsa_functions_t *) this_gen; + if(this->audio_fd) snd_pcm_close(this->audio_fd); + this->audio_fd = 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; - - } +static uint32_t ao_get_capabilities (ao_functions_t *this_gen) { + alsa_functions_t *this = (alsa_functions_t *) this_gen; + return this->capabilities; +} - return 0; +static void ao_connect (ao_functions_t *this_gen, metronom_t *metronom) { + alsa_functions_t *this = (alsa_functions_t *) this_gen; + this->metronom = metronom; } -static char *ao_get_ident(void) { - return "ALSA"; +static void ao_exit(ao_functions_t *this_gen) +{ + alsa_functions_t *this = (alsa_functions_t *) this_gen; + if (this->audio_fd) snd_pcm_close(this->audio_fd); + free (this->zero_space); + free (this); } -/* ------------------------------------------------------------------------- */ -/* - * - */ -static ao_functions_t audio_alsaout = { - AO_OUT_ALSA_IFACE_VERSION, - ao_is_mode_supported, - ao_open, - ao_put_samples, - ao_close, - ao_get_ident -}; -/* ------------------------------------------------------------------------- */ /* * */ -static void sighandler(int signum) { +static int ao_get_property (ao_functions_t *this, int property) { + + /* FIXME: implement some properties + switch(property) { + case AO_PROP_MIXER_VOL: + break; + case AO_PROP_PCM_VOL: + break; + case AO_PROP_MUTE_VOL: + break; + } + */ + return 0; } -/* ------------------------------------------------------------------------- */ + /* * */ -ao_functions_t *init_audio_out_plugin (int iface, config_values_t *config) { - 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; +static int ao_set_property (ao_functions_t *this, int property, int value) { + + /* FIXME: Implement property support. + switch(property) { + case AO_PROP_MIXER_VOL: + break; + case AO_PROP_PCM_VOL: + break; + case AO_PROP_MUTE_VOL: + break; } - 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); + return ~value; +} - 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..."); +ao_functions_t *init_audio_out_plugin (config_values_t *config) { - 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); + alsa_functions_t *this; + int card; + int dev; + int err; - 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); + this = (alsa_functions_t *) malloc (sizeof (alsa_functions_t)); - 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); + strcpy(this->audio_dev,"plug:0,0"); - 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; - } + /* + * find best device driver/channel + */ + /* + * open that device + */ + card = snd_defaults_pcm_card(); + dev = snd_defaults_pcm_device(); + if (card < 0 || dev < 0) { + fprintf(stderr, "defaults are not set"); + 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); + err=snd_pcm_open(&this->audio_fd, this->audio_dev, SND_PCM_STREAM_PLAYBACK, 0); + if(err <0 ) { + error("snd_pcm_open() failed: %d", err); + error(">>> Check if another program don't already use PCM <<<"); + return NULL; } - - 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; + snd_pcm_close (this->audio_fd); + this->audio_fd=NULL; + this->output_sample_rate = 0; + this->capabilities = AO_CAP_MODE_STEREO; + if (config->lookup_int (config, "ac3_pass_through", 0)) { + this->capabilities |= AO_CAP_MODE_AC3; + strcpy(this->audio_dev,"plug:0,2"); + printf("AC3 pass through activated\n"); + } + + + this->zero_space = malloc (8192); + memset (this->zero_space, 0, 8192); + + this->ao_functions.get_capabilities = ao_get_capabilities; + this->ao_functions.get_property = ao_get_property; + this->ao_functions.set_property = ao_set_property; + this->ao_functions.connect = ao_connect; + this->ao_functions.open = ao_open; + this->ao_functions.write_audio_data = ao_write_audio_data; + this->ao_functions.close = ao_close; + this->ao_functions.exit = ao_exit; + + return &this->ao_functions; +} + +static ao_info_t ao_info_alsa = { + AUDIO_OUT_IFACE_VERSION, + "alsa", + "xine audio output plugin using alsa-compliant audio devices/drivers", + 10 +}; - return &audio_alsaout; +ao_info_t *get_audio_out_plugin_info() { + return &ao_info_alsa; } |