summaryrefslogtreecommitdiff
path: root/src/xine-engine/audio_out.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/xine-engine/audio_out.c')
-rw-r--r--src/xine-engine/audio_out.c336
1 files changed, 336 insertions, 0 deletions
diff --git a/src/xine-engine/audio_out.c b/src/xine-engine/audio_out.c
new file mode 100644
index 000000000..19273fdfd
--- /dev/null
+++ b/src/xine-engine/audio_out.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2000, 2001 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 self program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: audio_out.c,v 1.3 2001/08/21 19:39:50 jcdutton Exp $
+ *
+ * 20-8-2001 First implementation of Audio sync and Audio driver separation.
+ * Copyright (C) 2001 James Courtier-Dutton James@superbug.demon.co.uk
+ *
+ * General Programming Guidelines: -
+ * New concept of an "audio_frame".
+ * An audio_frame consists of all the samples required to fill every audio channel to a full amount of bits.
+ * So, it does not mater how many bits per sample, or how many audio channels are being used, the number of audio_frames is the same.
+ * E.g. 16 bit stereo is 4 bytes, but one frame.
+ * 16 bit 5.1 surround is 12 bytes, but one frame.
+ * The purpose of this is to make the audio_sync code a lot more readable, rather than having to multiply by the amount of channels all the time
+ * when dealing with audio_bytes instead of audio_frames.
+ *
+ * The number of samples passed to/from the audio driver is also sent in units of audio_frames.
+ */
+
+/* required for swab() */
+#define _XOPEN_SOURCE 500
+/* required for FNDELAY decl */
+#define _BSD_SOURCE 1
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include <unistd.h>
+#if defined(__OpenBSD__)
+#include <soundcard.h>
+#elif defined(__FreeBSD__)
+#include <machine/soundcard.h>
+#else
+#if defined(__linux__)
+#include <linux/config.h> /* Check for DEVFS */
+#endif
+#include <sys/soundcard.h>
+#endif
+#include <sys/ioctl.h>
+#include <inttypes.h>
+
+#include "xine_internal.h"
+#include "monitor.h"
+#include "audio_out.h"
+#include "resample.h"
+#include "metronom.h"
+#include "utils.h"
+
+#ifndef AFMT_S16_NE
+# if defined(sparc) || defined(__sparc__) || defined(PPC)
+/* Big endian machines */
+# define AFMT_S16_NE AFMT_S16_BE
+# else
+# define AFMT_S16_NE AFMT_S16_LE
+# endif
+#endif
+
+#ifndef AFMT_AC3
+# define AFMT_AC3 0x00000400 /* Dolby Digital AC3 */
+#endif
+
+#define AO_OUT_OSS_IFACE_VERSION 1
+
+#define AUDIO_NUM_FRAGMENTS 15
+#define AUDIO_FRAGMENT_SIZE 8192
+
+/* bufsize must be a multiple of 3 and 5 for 5.0 and 5.1 channel playback! */
+#define ZERO_BUF_SIZE 15360
+
+#define GAP_TOLERANCE 5000
+#define MAX_GAP 90000
+
+#ifdef CONFIG_DEVFS_FS
+#define DSP_TEMPLATE "/dev/sound/dsp%d"
+#else
+#define DSP_TEMPLATE "/dev/dsp%d"
+#endif
+
+/*
+ * open the audio device for writing to
+ */
+static int ao_open(ao_instance_t *self,
+ uint32_t bits, uint32_t rate, int mode)
+{
+ int result;
+ if(result=self->driver->open(self->driver,bits,rate,mode)<0) {
+ printf("open failed!\n");
+ return -1;
+ };
+// self->frame_rate_factor = (double) self->output_frame_rate / (double) self->input_frame_rate;
+ self->mode = mode;
+ self->input_frame_rate = rate;
+ self->frames_in_buffer = 0;
+ self->audio_started = 0;
+ self->last_audio_vpts = 0;
+
+ self->output_frame_rate=rate;
+ self->num_channels = self->driver->num_channels(self->driver);
+
+ self->frame_rate_factor = (double) 1 / (double) 1;
+ self->audio_step = (uint32_t) 90000 * (uint32_t) 32768
+ / self->input_frame_rate;
+ self->frames_per_kpts = self->output_frame_rate * self->num_channels * 2 * 1024 / 90000;
+ xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 frames\n", self->audio_step);
+
+ self->metronom->set_audio_rate(self->metronom, self->audio_step);
+
+
+ return 1;
+}
+
+static void ao_fill_gap (ao_instance_t *self, uint32_t pts_len) {
+
+ int num_bytes ;
+ xprintf (VERBOSE|AUDIO, "audio_out : fill_gap\n");
+
+ if (pts_len > MAX_GAP)
+ pts_len = MAX_GAP;
+ num_bytes = pts_len * self->frames_per_kpts / 1024;
+ num_bytes = (num_bytes / (2*self->num_channels)) * (2*self->num_channels);
+
+ if(self->mode == AO_CAP_MODE_AC3) return; /* FIXME */
+
+ printf ("audio_out: inserting %d 0-bytes to fill a gap of %d pts\n",num_bytes, pts_len);
+
+ self->frames_in_buffer += num_bytes;
+
+ while (num_bytes > 0) {
+ if (num_bytes > ZERO_BUF_SIZE) {
+ self->driver->write(self->driver, self->zero_space, ZERO_BUF_SIZE);
+ num_bytes -= ZERO_BUF_SIZE;
+ } else {
+ self->driver->write(self->driver, self->zero_space, num_bytes);
+ num_bytes = 0;
+ }
+ }
+}
+
+
+
+static int ao_write(ao_instance_t *self,
+ int16_t* output_frames, uint32_t num_frames,
+ uint32_t pts_)
+{
+ uint32_t vpts, buffer_vpts;
+ int32_t gap;
+ int bDropPackage;
+ int pos;
+
+ if (self->driver<0)
+ return 1;
+
+ vpts = self->metronom->got_audio_samples (self->metronom, pts_, num_frames);
+
+ xprintf (VERBOSE|AUDIO, "audio_out: got %d frames, vpts=%d pts=%d\n",
+ num_frames, vpts,pts_);
+
+ if (vpts<self->last_audio_vpts) {
+ /* reject self */
+ xprintf (VERBOSE|AUDIO, "audio_out: rejected frame vpts=%d, last_audio_vpts=%d\n", vpts,self->last_audio_vpts)
+
+ return 1;
+ }
+
+ self->last_audio_vpts = vpts;
+
+ bDropPackage = 0;
+
+ if ( self->audio_has_realtime || !self->audio_started ) {
+
+ /*
+ * where, in the timeline is the "end" of the audio buffer at the moment?
+ */
+
+ buffer_vpts = self->metronom->get_current_time (self->metronom);
+
+ if (self->audio_started) {
+ pos = self->driver->delay(self->driver);
+ } else
+ pos = 0;
+ if ( (self->mode==AO_CAP_MODE_AC3) && (pos>10) ) pos-=10; /* External AC3 decoder delay correction */
+
+ if (pos>self->frames_in_buffer) /* buffer ran dry */
+ self->frames_in_buffer = pos;
+
+ buffer_vpts += (self->frames_in_buffer - pos) * 1024 / self->frames_per_kpts;
+
+ /*
+ * calculate gap:
+ */
+
+ gap = vpts - buffer_vpts;
+ xprintf (VERBOSE|AUDIO, "audio_out: buff=%d pos=%d buf_vpts=%d gap=%d\n",
+ self->frames_in_buffer, pos,buffer_vpts,gap);
+
+ if (gap>GAP_TOLERANCE) {
+ ao_fill_gap (self, gap);
+
+ /* keep xine responsive */
+
+ if (gap>MAX_GAP)
+ return 0;
+
+ } else if (gap<-GAP_TOLERANCE) {
+ bDropPackage = 1;
+ xprintf (VERBOSE|AUDIO, "audio_out: audio package (vpts = %d %d)"
+ "dropped\n", vpts, gap);
+ }
+
+ } /* has realtime */
+
+ /*
+ * resample and output frames
+ */
+ if(self->mode == AO_CAP_MODE_AC3) bDropPackage=0;
+
+ if (!bDropPackage) {
+ int num_output_frames = num_frames * (self->output_frame_rate) / self->input_frame_rate;
+
+ if ((!self->do_resample) && (self->mode != AO_CAP_MODE_AC3)) {
+ xprintf (VERBOSE|AUDIO, "audio_out: writing without resampling\n");
+ self->driver->write(self->driver, output_frames,
+ num_output_frames );
+ } else switch (self->mode) {
+ case AO_CAP_MODE_MONO:
+ audio_out_resample_mono (output_frames, num_frames,
+ self->frame_buffer, num_output_frames);
+ self->driver->write(self->driver, self->frame_buffer, num_output_frames);
+ break;
+ case AO_CAP_MODE_STEREO:
+ audio_out_resample_stereo (output_frames, num_frames,
+ self->frame_buffer, num_output_frames);
+ self->driver->write(self->driver, self->frame_buffer, num_output_frames);
+ break;
+ case AO_CAP_MODE_4CHANNEL:
+ audio_out_resample_4channel (output_frames, num_frames,
+ self->frame_buffer, num_output_frames);
+ self->driver->write(self->driver, self->frame_buffer, num_output_frames);
+ break;
+ case AO_CAP_MODE_5CHANNEL:
+ audio_out_resample_5channel (output_frames, num_frames,
+ self->frame_buffer, num_output_frames);
+ self->driver->write(self->driver, self->frame_buffer, num_output_frames);
+ break;
+ case AO_CAP_MODE_5_1CHANNEL:
+ audio_out_resample_6channel (output_frames, num_frames,
+ self->frame_buffer, num_output_frames);
+ self->driver->write(self->driver, self->frame_buffer, num_output_frames);
+ break;
+ case AO_CAP_MODE_AC3:
+ num_output_frames = (num_frames+8)/4;
+ self->frame_buffer[0] = 0xf872; //spdif syncword
+ self->frame_buffer[1] = 0x4e1f; // .............
+ self->frame_buffer[2] = 0x0001; // AC3 data
+ self->frame_buffer[3] = num_frames * 8;
+ self->frame_buffer[4] = 0x0b77; // AC3 syncwork already in output_frames
+
+ // ac3 seems to be swabbed data
+ swab(output_frames,self->frame_buffer+4, num_frames );
+ self->driver->write(self->driver, self->zero_space, 2); /* Prevents crackle at start. */
+ self->driver->write(self->driver, self->frame_buffer, num_output_frames);
+ self->driver->write(self->driver, self->zero_space, 1534-num_output_frames);
+ num_output_frames=num_output_frames;
+ break;
+ }
+
+ xprintf (AUDIO|VERBOSE, "audio_out :audio package written\n");
+
+ /*
+ * step values
+ */
+
+ self->frames_in_buffer += num_output_frames ;
+ self->audio_started = 1;
+ }
+
+ return 1;
+
+}
+
+
+static void ao_close(ao_instance_t *self)
+{
+ self->driver->close(self->driver);
+}
+
+static uint32_t ao_get_capabilities (ao_instance_t *self) {
+ uint32_t result;
+ result=self->driver->get_capabilities(self->driver);
+ return result;
+}
+
+ao_instance_t *ao_new_instance (ao_driver_t *driver, metronom_t *metronom) {
+
+ ao_instance_t *self;
+
+ self = xmalloc (sizeof (ao_instance_t)) ;
+ self->driver = driver;
+ self->metronom = metronom;
+
+ self->open = ao_open;
+ self->write = ao_write;
+ self->close = ao_close;
+ self->get_capabilities = ao_get_capabilities;
+ self->audio_loop_running = 0;
+ self->frame_buffer = malloc (40000);
+ memset (self->frame_buffer, 0, 40000);
+ self->zero_space = malloc (ZERO_BUF_SIZE);
+ memset (self->zero_space, 0, ZERO_BUF_SIZE);
+ return self;
+}
+