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.c166
1 files changed, 111 insertions, 55 deletions
diff --git a/src/xine-engine/audio_out.c b/src/xine-engine/audio_out.c
index 89fbdde1a..5162c1883 100644
--- a/src/xine-engine/audio_out.c
+++ b/src/xine-engine/audio_out.c
@@ -15,17 +15,19 @@
*
* 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
+ */
+
+/**
+ * @file
+ * @brief xine-lib audio output implementation
+ *
+ * @date 2001-08-20 First implementation of Audio sync and Audio driver separation.
+ * (c) 2001 James Courtier-Dutton <james@superbug.demon.co.uk>
+ * @date 2001-08-22 James imported some useful AC3 sections from the previous
+ * ALSA driver. (c) 2001 Andy Lo A Foe <andy@alsaplayer.org>
*
- * $Id: audio_out.c,v 1.210 2007/04/01 00:52:36 dgp85 Exp $
*
- * 22-8-2001 James imported some useful AC3 sections from the previous alsa driver.
- * (c) 2001 Andy Lo A Foe <andy@alsaplayer.org>
- * 20-8-2001 First implementation of Audio sync and Audio driver separation.
- * (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
@@ -53,7 +55,6 @@
* Copyright (C) 2002 Felipe Rivera <liebremx at users sourceforge net>
*
* heavily modified by guenter bartsch 2003 for use in libxine
- *
*/
#ifndef __sun
@@ -88,11 +89,11 @@
#define LOG_RESAMPLE_SYNC 0
-#include "xine_internal.h"
-#include "xineutils.h"
-#include "audio_out.h"
-#include "resample.h"
-#include "metronom.h"
+#include <xine/xine_internal.h>
+#include <xine/xineutils.h>
+#include <xine/audio_out.h>
+#include <xine/resample.h>
+#include <xine/metronom.h>
#define NUM_AUDIO_BUFFERS 32
@@ -174,7 +175,7 @@ typedef struct {
}sXYData;
-static sIIRCoefficients iir_cf[] = {
+static const sIIRCoefficients iir_cf[] = {
/* 31 Hz*/
{ EQ_REAL(9.9691562441e-01), EQ_REAL(1.5421877947e-03), EQ_REAL(1.9968961468e+00) },
/* 62 Hz*/
@@ -251,6 +252,7 @@ typedef struct {
* sure nobody will change speed without going through xine.c:set_speed_internal */
int slow_fast_audio; /* play audio even on slow/fast speeds */
+ int16_t last_sample[RESAMPLE_MAX_CHANNELS];
audio_buffer_t *frame_buf[2]; /* two buffers for "stackable" conversions */
int16_t *zero_space;
@@ -289,6 +291,7 @@ struct audio_fifo_s {
int num_buffers;
};
+static int ao_set_property (xine_audio_port_t *this_gen, int property, int value);
static audio_fifo_t *fifo_new (xine_t *xine) {
@@ -339,9 +342,7 @@ static void fifo_append (audio_fifo_t *fifo,
pthread_mutex_unlock (&fifo->mutex);
}
-static audio_buffer_t *fifo_remove_int (audio_fifo_t *fifo, int blocking) {
- audio_buffer_t *buf;
-
+static audio_buffer_t *fifo_peek_int (audio_fifo_t *fifo, int blocking) {
while (!fifo->first) {
pthread_cond_signal (&fifo->empty);
if (blocking)
@@ -356,25 +357,38 @@ static audio_buffer_t *fifo_remove_int (audio_fifo_t *fifo, int blocking) {
return NULL;
}
}
+ return fifo->first;
+}
- buf = fifo->first;
-
- if (buf) {
- fifo->first = buf->next;
+static audio_buffer_t *fifo_remove_int (audio_fifo_t *fifo, int blocking) {
+ audio_buffer_t *buf = fifo_peek_int(fifo, blocking);
+ if (!buf)
+ return NULL;
- if (!fifo->first) {
+ fifo->first = buf->next;
- fifo->last = NULL;
- fifo->num_buffers = 0;
- pthread_cond_signal (&fifo->empty);
+ if (!fifo->first) {
- } else
- fifo->num_buffers--;
+ fifo->last = NULL;
+ fifo->num_buffers = 0;
+ pthread_cond_signal (&fifo->empty);
- }
+ } else
+ fifo->num_buffers--;
buf->next = NULL;
-
+
+ return buf;
+}
+
+static audio_buffer_t *fifo_peek (audio_fifo_t *fifo) {
+
+ audio_buffer_t *buf;
+
+ pthread_mutex_lock (&fifo->mutex);
+ buf = fifo_peek_int(fifo, 1);
+ pthread_mutex_unlock (&fifo->mutex);
+
return buf;
}
@@ -576,18 +590,16 @@ static void audio_filter_compress (aos_t *this, int16_t *mem, int num_frames) {
}
static void audio_filter_amp (aos_t *this, void *buf, int num_frames) {
-
- int i;
- int num_channels;
double amp_factor;
-
- num_channels = _x_ao_mode2channels (this->input.mode);
- if (!num_channels)
+ int i;
+ const int total_frames = num_frames * _x_ao_mode2channels (this->input.mode);
+
+ if (!total_frames)
return;
amp_factor=this->amp_factor;
if (this->amp_mute || amp_factor == 0) {
- memset (buf, 0, num_frames * num_channels * (this->input.bits / 8));
+ memset (buf, 0, total_frames * (this->input.bits / 8));
return;
}
@@ -595,7 +607,7 @@ static void audio_filter_amp (aos_t *this, void *buf, int num_frames) {
int16_t test;
int8_t *mem = (int8_t *) buf;
- for (i=0; i<num_frames*num_channels; i++) {
+ for (i=0; i<total_frames; i++) {
test = mem[i] * amp_factor;
/* Force limit on amp_factor to prevent clipping */
if (test < INT8_MIN) {
@@ -612,7 +624,7 @@ static void audio_filter_amp (aos_t *this, void *buf, int num_frames) {
int32_t test;
int16_t *mem = (int16_t *) buf;
- for (i=0; i<num_frames*num_channels; i++) {
+ for (i=0; i<total_frames; i++) {
test = mem[i] * amp_factor;
/* Force limit on amp_factor to prevent clipping */
if (test < INT16_MIN) {
@@ -746,19 +758,19 @@ static audio_buffer_t* prepare_samples( aos_t *this, audio_buffer_t *buf) {
switch (this->input.mode) {
case AO_CAP_MODE_MONO:
ensure_buffer_size(this->frame_buf[1], (this->output.bits>>3), num_output_frames);
- _x_audio_out_resample_mono (buf->mem, buf->num_frames,
+ _x_audio_out_resample_mono (this->last_sample, buf->mem, buf->num_frames,
this->frame_buf[1]->mem, num_output_frames);
buf = swap_frame_buffers(this);
break;
case AO_CAP_MODE_STEREO:
ensure_buffer_size(this->frame_buf[1], (this->output.bits>>3)*2, num_output_frames);
- _x_audio_out_resample_stereo (buf->mem, buf->num_frames,
+ _x_audio_out_resample_stereo (this->last_sample, buf->mem, buf->num_frames,
this->frame_buf[1]->mem, num_output_frames);
buf = swap_frame_buffers(this);
break;
case AO_CAP_MODE_4CHANNEL:
ensure_buffer_size(this->frame_buf[1], (this->output.bits>>3)*4, num_output_frames);
- _x_audio_out_resample_4channel (buf->mem, buf->num_frames,
+ _x_audio_out_resample_4channel (this->last_sample, buf->mem, buf->num_frames,
this->frame_buf[1]->mem, num_output_frames);
buf = swap_frame_buffers(this);
break;
@@ -766,7 +778,7 @@ static audio_buffer_t* prepare_samples( aos_t *this, audio_buffer_t *buf) {
case AO_CAP_MODE_5CHANNEL:
case AO_CAP_MODE_5_1CHANNEL:
ensure_buffer_size(this->frame_buf[1], (this->output.bits>>3)*6, num_output_frames);
- _x_audio_out_resample_6channel (buf->mem, buf->num_frames,
+ _x_audio_out_resample_6channel (this->last_sample, buf->mem, buf->num_frames,
this->frame_buf[1]->mem, num_output_frames);
buf = swap_frame_buffers(this);
break;
@@ -775,6 +787,25 @@ static audio_buffer_t* prepare_samples( aos_t *this, audio_buffer_t *buf) {
/* pass-through modes: no resampling */
break;
}
+ } else {
+ /* maintain last_sample in case we need it */
+ switch (this->input.mode) {
+ case AO_CAP_MODE_MONO:
+ memcpy (this->last_sample, &buf->mem[buf->num_frames - 1], sizeof (this->last_sample[0]));
+ break;
+ case AO_CAP_MODE_STEREO:
+ memcpy (this->last_sample, &buf->mem[(buf->num_frames - 1) * 2], 2 * sizeof (this->last_sample[0]));
+ break;
+ case AO_CAP_MODE_4CHANNEL:
+ memcpy (this->last_sample, &buf->mem[(buf->num_frames - 1) * 4], 4 * sizeof (this->last_sample[0]));
+ break;
+ case AO_CAP_MODE_4_1CHANNEL:
+ case AO_CAP_MODE_5CHANNEL:
+ case AO_CAP_MODE_5_1CHANNEL:
+ memcpy (this->last_sample, &buf->mem[(buf->num_frames - 1) * 6], 6 * sizeof (this->last_sample[0]));
+ break;
+ default:;
+ }
}
/* mode conversion */
@@ -980,7 +1011,7 @@ static void *ao_loop (void *this_gen) {
if (!in_buf) {
lprintf ("loop: get buf from fifo\n");
- in_buf = fifo_remove (this->out_fifo);
+ in_buf = fifo_peek (this->out_fifo);
bufs_since_sync++;
lprintf ("got a buffer\n");
}
@@ -993,6 +1024,7 @@ static void *ao_loop (void *this_gen) {
}
if (this->discard_buffers) {
+ fifo_remove (this->out_fifo);
if (in_buf->stream)
_x_refcounter_dec(in_buf->stream->refcounter);
fifo_append (this->free_fifo, in_buf);
@@ -1019,6 +1051,7 @@ static void *ao_loop (void *this_gen) {
cur_time = this->clock->get_current_time (this->clock);
if (in_buf->vpts < cur_time ) {
lprintf ("loop: next fifo\n");
+ fifo_remove (this->out_fifo);
if (in_buf->stream)
_x_refcounter_dec(in_buf->stream->refcounter);
fifo_append (this->free_fifo, in_buf);
@@ -1143,6 +1176,7 @@ static void *ao_loop (void *this_gen) {
/* drop package */
lprintf ("loop: drop package, next fifo\n");
+ fifo_remove (this->out_fifo);
if (in_buf->stream)
_x_refcounter_dec(in_buf->stream->refcounter);
fifo_append (this->free_fifo, in_buf);
@@ -1209,15 +1243,30 @@ static void *ao_loop (void *this_gen) {
} else {
result = 0;
}
+ fifo_remove (this->out_fifo);
if( result < 0 ) {
- /* FIXME: USB device unplugged.
- * We should get the card into a closed state here, that involves closing
- * the PCM as well as the MIXER.
- * Maybe we should pause the stream until the USB device is plugged in again.
- * Return values 0 happen even if usb not unplugged, so needs further investigation.
- */
- xprintf(this->xine, XINE_VERBOSITY_LOG, _("write to sound card failed. Was a USB device unplugged ?\n"));
+ /* device unplugged. */
+ xprintf(this->xine, XINE_VERBOSITY_LOG, _("write to sound card failed. Assuming the device was unplugged.\n"));
+ _x_message (in_buf->stream, XINE_MSG_AUDIO_OUT_UNAVAILABLE, NULL);
+
+ pthread_mutex_lock( &this->driver_lock );
+ if(this->driver_open) {
+ this->driver->close(this->driver);
+ this->driver_open = 0;
+ this->driver->exit(this->driver);
+ this->driver = _x_load_audio_output_plugin (this->xine, "none");
+ if (this->driver && !in_buf->stream->emergency_brake &&
+ ao_change_settings(this,
+ in_buf->format.bits,
+ in_buf->format.rate,
+ in_buf->format.mode) == 0) {
+ in_buf->stream->emergency_brake = 1;
+ _x_message (in_buf->stream, XINE_MSG_AUDIO_OUT_UNAVAILABLE, NULL);
+ }
+ }
+ pthread_mutex_unlock( &this->driver_lock );
+ /* closing the driver will result in XINE_MSG_AUDIO_OUT_UNAVAILABLE to be emitted */
}
lprintf ("loop: next buf from fifo\n");
@@ -1387,7 +1436,7 @@ static int ao_change_settings(aos_t *this, uint32_t bits, uint32_t rate, int mod
_("stereo not supported by driver, converting to mono.\n"));
}
- output_sample_rate=this->driver->open(this->driver,bits,(this->force_rate ? this->force_rate : rate),mode);
+ output_sample_rate=(this->driver->open) (this->driver,bits,(this->force_rate ? this->force_rate : rate),mode);
} else
output_sample_rate = this->input.rate;
@@ -1565,6 +1614,11 @@ static void ao_close(xine_audio_port_t *this_gen, xine_stream_t *stream) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_out: no streams left, closing driver\n");
if (this->audio_loop_running) {
+ if (this->clock->speed == XINE_SPEED_PAUSE ||
+ (this->clock->speed != XINE_FINE_SPEED_NORMAL && !this->slow_fast_audio)) {
+ /* discard buffers, otherwise we'll wait forever */
+ ao_set_property(this_gen, AO_PROP_DISCARD_BUFFERS, 1);
+ }
/* make sure there are no more buffers on queue */
fifo_wait_empty(this->out_fifo);
}
@@ -1991,8 +2045,8 @@ xine_audio_port_t *_x_ao_new_port (xine_t *xine, ao_driver_t *driver,
int i, err;
pthread_attr_t pth_attrs;
pthread_mutexattr_t attr;
- static const char* resample_modes[] = {"auto", "off", "on", NULL};
- static const char* av_sync_methods[] = {"metronom feedback", "resample", NULL};
+ static const char *const resample_modes[] = {"auto", "off", "on", NULL};
+ static const char *const av_sync_methods[] = {"metronom feedback", "resample", NULL};
this = xine_xmalloc (sizeof (aos_t)) ;
@@ -2146,6 +2200,8 @@ xine_audio_port_t *_x_ao_new_port (xine_t *xine, ao_driver_t *driver,
fifo_append (this->free_fifo, buf);
}
+
+ memset (this->last_sample, 0, sizeof (this->last_sample));
/* buffers used for audio conversions */
for (i=0; i<2; i++) {