summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio_out/Makefile.am8
-rw-r--r--src/audio_out/audio_alsa_out.c1039
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(&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;
+ 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(&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;
- }
+ 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(&params);
+ 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;
}