summaryrefslogtreecommitdiff
path: root/src/audio_out
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_out')
-rw-r--r--src/audio_out/Makefile.am17
-rw-r--r--src/audio_out/audio_alsa_out.c48
-rw-r--r--src/audio_out/audio_arts_out.c4
-rw-r--r--src/audio_out/audio_coreaudio_out.c4
-rw-r--r--src/audio_out/audio_directx2_out.c470
-rw-r--r--src/audio_out/audio_directx_out.c10
-rw-r--r--src/audio_out/audio_esd_out.c4
-rw-r--r--src/audio_out/audio_file_out.c4
-rw-r--r--src/audio_out/audio_fusionsound_out.c6
-rw-r--r--src/audio_out/audio_jack_out.c1144
-rw-r--r--src/audio_out/audio_none_out.c4
-rw-r--r--src/audio_out/audio_oss_out.c81
-rw-r--r--src/audio_out/audio_pulse_out.c771
-rw-r--r--src/audio_out/audio_sndio_out.c410
-rw-r--r--src/audio_out/audio_sun_out.c4
15 files changed, 2061 insertions, 920 deletions
diff --git a/src/audio_out/Makefile.am b/src/audio_out/Makefile.am
index b0c3d292e..07ad19c8d 100644
--- a/src/audio_out/Makefile.am
+++ b/src/audio_out/Makefile.am
@@ -1,3 +1,4 @@
+include $(top_builddir)/misc/Makefile.plugins
include $(top_srcdir)/misc/Makefile.common
AM_CPPFLAGS = -DXINE_COMPILE
@@ -49,6 +50,10 @@ if HAVE_JACK
jack_module = xineplug_ao_out_jack.la
endif
+if HAVE_SNDIO
+sndio_module = xineplug_ao_out_sndio.la
+endif
+
##
# IMPORTANT:
# ---------
@@ -66,7 +71,8 @@ xineplug_LTLIBRARIES = xineplug_ao_out_none.la xineplug_ao_out_file.la \
$(pulseaudio_module) \
$(directx2_module) \
$(fusionsound_module) \
- $(jack_module)
+ $(jack_module) \
+ $(sndio_module)
xineplug_ao_out_none_la_SOURCES = audio_none_out.c
xineplug_ao_out_none_la_LIBADD = $(XINE_LIB) $(LTLIBINTL)
@@ -110,7 +116,7 @@ xineplug_ao_out_arts_la_LDFLAGS = -avoid-version -module
xineplug_ao_out_directx_la_SOURCES = audio_directx_out.c
xineplug_ao_out_directx_la_CPPFLAGS = $(DIRECTX_CPPFLAGS)
-xineplug_ao_out_directx_la_LIBADD = $(XINE_LIB) $(DIRECTX_AUDIO_LIBS)
+xineplug_ao_out_directx_la_LIBADD = $(XINE_LIB) $(DIRECTX_AUDIO_LIBS) $(LTLIBINTL)
xineplug_ao_out_directx_la_CFLAGS = $(VISIBILITY_FLAG)
xineplug_ao_out_directx_la_LDFLAGS = -avoid-version -module
@@ -131,7 +137,7 @@ xineplug_ao_out_pulseaudio_la_LDFLAGS = -avoid-version -module
xineplug_ao_out_directx2_la_SOURCES = audio_directx2_out.c
xineplug_ao_out_directx2_la_CPPFLAGS = $(DIRECTX_CPPFLAGS)
-xineplug_ao_out_directx2_la_LIBADD = $(XINE_LIB) $(DIRECTX_AUDIO_LIBS) $(PTHREAD_LIBS)
+xineplug_ao_out_directx2_la_LIBADD = $(XINE_LIB) $(DIRECTX_AUDIO_LIBS) $(PTHREAD_LIBS) $(LTLIBINTL)
xineplug_ao_out_directx2_la_CFLAGS = $(VISIBILITY_FLAG)
xineplug_ao_out_directx2_la_LDFLAGS = -avoid-version -module
@@ -144,3 +150,8 @@ xineplug_ao_out_jack_la_SOURCES = audio_jack_out.c
xineplug_ao_out_jack_la_LIBADD = $(XINE_LIB) $(JACK_LIBS) $(LTLIBINTL)
xineplug_ao_out_jack_la_CFLAGS = $(VISIBILITY_FLAG) $(JACK_CFLAGS)
xineplug_ao_out_jack_la_LDFLAGS = -avoid-version -module
+
+xineplug_ao_out_sndio_la_SOURCES = audio_sndio_out.c
+xineplug_ao_out_sndio_la_LIBADD = $(XINE_LIB) $(SNDIO_LIBS)
+xineplug_ao_out_sndio_la_CFLAGS = $(VISIBILITY_FLAG) $(SNDIO_CFLAGS)
+xineplug_ao_out_sndio_la_LDFLAGS = -avoid-version -module
diff --git a/src/audio_out/audio_alsa_out.c b/src/audio_out/audio_alsa_out.c
index 4ce2b1be3..470cd953a 100644
--- a/src/audio_out/audio_alsa_out.c
+++ b/src/audio_out/audio_alsa_out.c
@@ -1350,7 +1350,7 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
#define A52_PASSTHRU 12
int speakers;
- this = (alsa_driver_t *) xine_xmalloc (sizeof (alsa_driver_t));
+ this = calloc(1, sizeof (alsa_driver_t));
if (!this)
return NULL;
@@ -1497,70 +1497,73 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
"formats you want to play to your sound card's digital output."),
0, alsa_speaker_arrangement_cb, this);
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("audio_alsa_out : supported modes are "));
+ char *logmsg = strdup (_("audio_alsa_out : supported modes are"));
+
if (!(snd_pcm_hw_params_test_format(this->audio_fd, params, SND_PCM_FORMAT_U8))) {
this->capabilities |= AO_CAP_8BITS;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("8bit "));
+ xine_strcat_realloc (&logmsg, _(" 8bit"));
}
/* ALSA automatically appends _LE or _BE depending on the CPU */
if (!(snd_pcm_hw_params_test_format(this->audio_fd, params, SND_PCM_FORMAT_S16))) {
this->capabilities |= AO_CAP_16BITS;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("16bit "));
+ xine_strcat_realloc (&logmsg, _(" 16bit"));
}
if (!(snd_pcm_hw_params_test_format(this->audio_fd, params, SND_PCM_FORMAT_S24))) {
this->capabilities |= AO_CAP_24BITS;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("24bit "));
+ xine_strcat_realloc (&logmsg, _(" 24bit"));
}
if (!(snd_pcm_hw_params_test_format(this->audio_fd, params, SND_PCM_FORMAT_FLOAT))) {
this->capabilities |= AO_CAP_FLOAT32;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("32bit "));
+ xine_strcat_realloc (&logmsg, _(" 32bit"));
}
if (0 == (this->capabilities & (AO_CAP_FLOAT32 | AO_CAP_24BITS | AO_CAP_16BITS | AO_CAP_8BITS))) {
- xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
- "\naudio_alsa_out: no supported PCM format found\n");
+ xprintf(class->xine, XINE_VERBOSITY_LOG, "%s\n", logmsg);
+ free (logmsg);
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG,
+ "audio_alsa_out: no supported PCM format found\n");
snd_pcm_close(this->audio_fd);
free(this);
return NULL;
}
if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 1))) {
this->capabilities |= AO_CAP_MODE_MONO;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("mono "));
+ xine_strcat_realloc (&logmsg, _(" mono"));
}
if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 2))) {
this->capabilities |= AO_CAP_MODE_STEREO;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("stereo "));
+ xine_strcat_realloc (&logmsg, _(" stereo"));
}
if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 4)) &&
( speakers == SURROUND4 )) {
this->capabilities |= AO_CAP_MODE_4CHANNEL;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("4-channel "));
+ xine_strcat_realloc (&logmsg, _(" 4-channel"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("(4-channel not enabled in xine config) "));
+ xine_strcat_realloc (&logmsg, _(" (4-channel not enabled in xine config)"));
if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 6)) &&
( speakers == SURROUND41 )) {
this->capabilities |= AO_CAP_MODE_4_1CHANNEL;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("4.1-channel "));
+ xine_strcat_realloc (&logmsg, _(" 4.1-channel"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("(4.1-channel not enabled in xine config) "));
+ xine_strcat_realloc (&logmsg, _(" (4.1-channel not enabled in xine config)"));
if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 6)) &&
( speakers == SURROUND5 )) {
this->capabilities |= AO_CAP_MODE_5CHANNEL;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("5-channel "));
+ xine_strcat_realloc (&logmsg, _(" 5-channel"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("(5-channel not enabled in xine config) "));
+ xine_strcat_realloc (&logmsg, _(" (5-channel not enabled in xine config)"));
if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 6)) &&
( speakers >= SURROUND51 )) {
this->capabilities |= AO_CAP_MODE_5_1CHANNEL;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("5.1-channel "));
+ xine_strcat_realloc (&logmsg, _(" 5.1-channel"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("(5.1-channel not enabled in xine config) "));
+ xine_strcat_realloc (&logmsg, _(" (5.1-channel not enabled in xine config)"));
this->has_pause_resume = 0; /* This is checked at open time instead */
this->is_paused = 0;
@@ -1583,10 +1586,13 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
if ( speakers == A52_PASSTHRU ) {
this->capabilities |= AO_CAP_MODE_A52;
this->capabilities |= AO_CAP_MODE_AC5;
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("a/52 and DTS pass-through\n"));
+ xine_strcat_realloc (&logmsg, _(" a/52 and DTS pass-through"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_LOG, _("(a/52 and DTS pass-through not enabled in xine config)\n"));
+ xine_strcat_realloc (&logmsg, _(" (a/52 and DTS pass-through not enabled in xine config)"));
+
+ xprintf(class->xine, XINE_VERBOSITY_LOG, "%s\n", logmsg);
+ free (logmsg);
/* printf("audio_alsa_out: capabilities 0x%X\n",this->capabilities); */
@@ -1682,7 +1688,7 @@ static void *init_class (xine_t *xine, void *data) {
alsa_class_t *this;
- this = (alsa_class_t *) xine_xmalloc (sizeof (alsa_class_t));
+ this = calloc(1, sizeof (alsa_class_t));
if (!this)
return NULL;
diff --git a/src/audio_out/audio_arts_out.c b/src/audio_out/audio_arts_out.c
index 0f0467b4a..14bf5c965 100644
--- a/src/audio_out/audio_arts_out.c
+++ b/src/audio_out/audio_arts_out.c
@@ -303,7 +303,7 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
lprintf ("audio_arts_out: open_plugin called\n");
- this = (arts_driver_t *) xine_xmalloc (sizeof (arts_driver_t));
+ this = calloc(1, sizeof (arts_driver_t));
if (!this)
return NULL;
@@ -384,7 +384,7 @@ static void *init_class (xine_t *xine, void *data) {
lprintf ("audio_arts_out: init class\n");
- this = (arts_class_t *) xine_xmalloc (sizeof (arts_class_t));
+ this = calloc(1, sizeof (arts_class_t));
if (!this)
return NULL;
diff --git a/src/audio_out/audio_coreaudio_out.c b/src/audio_out/audio_coreaudio_out.c
index eecab8f95..642f920c7 100644
--- a/src/audio_out/audio_coreaudio_out.c
+++ b/src/audio_out/audio_coreaudio_out.c
@@ -530,7 +530,7 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
lprintf ("open_plugin called\n");
- this = (coreaudio_driver_t *) xine_xmalloc (sizeof (coreaudio_driver_t));
+ this = calloc(1, sizeof (coreaudio_driver_t));
if (!this)
return NULL;
@@ -582,7 +582,7 @@ static void *init_class (xine_t *xine, void *data) {
lprintf ("init class\n");
- this = (coreaudio_class_t *) xine_xmalloc (sizeof (coreaudio_class_t));
+ this = calloc(1, sizeof (coreaudio_class_t));
if (!this)
return NULL;
diff --git a/src/audio_out/audio_directx2_out.c b/src/audio_out/audio_directx2_out.c
index d9688b647..6bee2066c 100644
--- a/src/audio_out/audio_directx2_out.c
+++ b/src/audio_out/audio_directx2_out.c
@@ -31,6 +31,9 @@
*
* Authors:
* - Frantisek Dvorak <valtri@atlas.cz>
+ * - Original version with slotted ring buffer
+ * - Matthias Ringald <mringwal@inf.ethz.ch>
+ * - non-slotted simpler version for ring buffer handling
*
* Inspiration:
* - mplayer for workarounding -lguid idea
@@ -55,8 +58,8 @@
#define LOG_MODULE "audio_directx2_out"
#define LOG_VERBOSE
/*
-#define LOG
-*/
+ #define LOG
+ */
#include "xine_internal.h"
#include "audio_out.h"
@@ -64,29 +67,33 @@
#define AO_OUT_DIRECTX2_IFACE_VERSION 8
+
+/*
+ * If GAP_TOLERANCE is lower than AO_MAX_GAP, xine will
+ * try to smooth playback without skipping frames or
+ * inserting silence.
+ */
+#define GAP_TOLERANCE (AO_MAX_GAP/3)
+
/*
* buffer size in miliseconds
* (one second takes 11-192 KB)
*/
#define BUFFER_MS 1000
-/*
- * number of parts in the buffer,
- * one is always locked for playing
+/*
+ * buffer below this threshold is considered a buffer underrun
*/
-#define PARTS 3
+#define BUFFER_MIN_MS 200
/*
* base power factor for volume remapping
*/
#define FACTOR 60.0
-
-/* experiments */
-/*#define EXACT_WAIT*/
-/*#define EXACT_SLEEP*/
-/*#define PANIC_OVERRUN*/
-
+/*
+ * buffer handler status
+ */
#define STATUS_START 0
#define STATUS_WAIT 1
#define STATUS_RUNNING 2
@@ -108,12 +115,14 @@ typedef struct {
LPDIRECTSOUND ds; /* DirectSound device */
LPDIRECTSOUNDBUFFER dsbuffer; /* DirectSound buffer */
- DSBPOSITIONNOTIFY events[PARTS]; /* position events */
- LPDIRECTSOUNDNOTIFY notify; /* notify interface */
size_t buffer_size; /* size of the buffer */
- size_t part_size; /* second half of buffer */
- size_t read_size; /* size of prepared data */
+ size_t write_pos; /* positition in ring buffer for writing*/
+
+ int status; /* current status of the driver */
+ int paused; /* paused mode */
+ int finished; /* driver finished */
+ int failed; /* don't open modal dialog again */
uint32_t bits;
uint32_t rate;
@@ -123,12 +132,6 @@ typedef struct {
int volume;
int muted;
- int status; /* current status of the driver */
- int paused; /* paused mode */
- int finished; /* driver finished */
- int failed; /* don't open modal dialog again */
- int count; /* current free part number */
-
pthread_t buffer_service; /* service thread for operating with DSB */
pthread_cond_t data_cond; /* signals on data */
pthread_mutex_t data_mutex; /* data lock */
@@ -140,17 +143,19 @@ typedef struct {
* Defining them here allows us to get rid of the dxguid library during
* the linking stage.
*****************************************************************************/
-static const GUID IID_IDirectSoundNotify = {
- 0xB0210783, 0x89CD, 0x11D0, {0xAF, 0x08, 0x00, 0xA0, 0xC9, 0x25, 0xCD, 0x16}
+static const GUID xine_IID_IDirectSoundNotify = {
+0xB0210783, 0x89CD, 0x11D0, {0xAF, 0x08, 0x00, 0xA0, 0xC9, 0x25, 0xCD, 0x16}
};
+#ifdef IID_IDirectSoundNotify
+# undef IID_IDirectSoundNotify
+#endif
+#define IID_IDirectSoundNotify xine_IID_IDirectSoundNotify
-static int buffer_ready(dx2_driver_t *this);
-
/* popup a dialog with error */
static void XINE_FORMAT_PRINTF(1, 2)
- error_message(const char *fmt, ...) {
+error_message(const char *fmt, ...) {
char message[256];
va_list ap;
@@ -214,7 +219,7 @@ static LPDIRECTSOUND dsound_create() {
/* destroy direct sound object */
static void dsound_destroy(LPDIRECTSOUND ds) {
- IDirectSound_Release(ds);
+ IDirectSound_Release(ds);
}
@@ -266,12 +271,10 @@ static int audio_create_buffers(dx2_driver_t *this) {
HRESULT err;
size_t buffer_size;
- buffer_size = this->rate * this->frame_size * BUFFER_MS / 1000;
+ buffer_size = this->rate * BUFFER_MS / 1000 * this->frame_size;
if (buffer_size > DSBSIZE_MAX) buffer_size = DSBSIZE_MAX;
if (buffer_size < DSBSIZE_MIN) buffer_size = DSBSIZE_MIN;
- this->part_size = (buffer_size / PARTS / this->frame_size) * this->frame_size;
- if (!this->part_size) this->part_size = this->frame_size;
- this->buffer_size = this->part_size * PARTS;
+ this->buffer_size = buffer_size;
flags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
dsound_fill_wfx(&wfx, this->bits, this->rate, this->channels, this->frame_size);
@@ -282,7 +285,7 @@ static int audio_create_buffers(dx2_driver_t *this) {
return 0;
}
- lprintf("created direct sound buffer, size = %u, part = %u\n", this->buffer_size, this->part_size);
+ lprintf("created direct sound buffer, size = %u\n", this->buffer_size);
return 1;
}
@@ -293,43 +296,6 @@ static void audio_destroy_buffers(dx2_driver_t *this) {
}
-/* create position events */
-static int audio_create_events(dx2_driver_t *this) {
- HANDLE handle[PARTS];
- HRESULT err;
- int i;
-
- for (i = 0; i < PARTS; i++) {
- handle[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (!handle[i]) {
- error_message(_("Unable to create buffer position events."));
- return 0;
- }
- this->events[i].dwOffset = i * this->part_size;
- this->events[i].hEventNotify = handle[i];
- }
-
- if ((err = IDirectSoundBuffer_QueryInterface(this->dsbuffer, &IID_IDirectSoundNotify, (void **)&this->notify)) != DS_OK) {
- audio_error(this, err, _("Unable to get notification interface"));
- return 0;
- }
-
- if ((err = IDirectSoundNotify_SetNotificationPositions(this->notify, PARTS, this->events)) != DS_OK) {
- audio_error(this, err, _("Unable to set notification positions"));
- IDirectSoundNotify_Release(this->notify);
- return 0;
- }
-
- return 1;
-}
-
-
-/* destroy notification interface */
-static void audio_destroy_events(dx2_driver_t *this) {
- IDirectSoundNotify_Release(this->notify);
-}
-
-
/* start playback */
static int audio_play(dx2_driver_t *this) {
HRESULT err;
@@ -385,8 +351,7 @@ static int audio_seek(dx2_driver_t *this, size_t pos) {
/* flush audio buffers */
static int audio_flush(dx2_driver_t *this) {
this->status = STATUS_WAIT;
- this->count = 0;
- this->read_size = 0;
+ this->write_pos = 0;
return audio_seek(this, 0);
}
@@ -421,12 +386,12 @@ static int audio_fill(dx2_driver_t *this, char *data, size_t size) {
HRESULT err;
/* lock a part of the buffer, begin position on free space */
- err = IDirectSoundBuffer_Lock(this->dsbuffer, (this->count * this->part_size + this->read_size) % this->buffer_size, size, &ptr1, &size1, &ptr2, &size2, 0);
+ err = IDirectSoundBuffer_Lock(this->dsbuffer, this->write_pos, size, &ptr1, &size1, &ptr2, &size2, 0);
/* try to restore the buffer, if necessary */
if (err == DSERR_BUFFERLOST) {
xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": buffer lost, tryig to restore\n"));
IDirectSoundBuffer_Restore(this->dsbuffer);
- err = IDirectSoundBuffer_Lock(this->dsbuffer, (this->count * this->part_size + this->read_size) % this->buffer_size, size, &ptr1, &size1, &ptr2, &size2, 0); }
+ err = IDirectSoundBuffer_Lock(this->dsbuffer, this->write_pos, size, &ptr1, &size1, &ptr2, &size2, 0); }
if (err != DS_OK) {
audio_error(this, err, _("Couldn't lock direct sound buffer"));
return 0;
@@ -436,19 +401,15 @@ static int audio_fill(dx2_driver_t *this, char *data, size_t size) {
if (ptr1 && size1) xine_fast_memcpy(ptr1, data, size1);
if (ptr2 && size2) xine_fast_memcpy(ptr2, data + size1, size2);
- this->read_size += size;
+ // this->read_size += size;
+ this->write_pos = (this->write_pos + size ) % this->buffer_size;
+ lprintf("size %u, write_pos %u\n", size, this->write_pos);
if ((err = IDirectSoundBuffer_Unlock(this->dsbuffer, ptr1, size1, ptr2, size2)) != DS_OK) {
audio_error(this, err, _("Couldn't unlock direct sound buffer"));
return 0;
}
- /* signal, if are waiting and need wake up */
- if ((this->status == STATUS_WAIT) && buffer_ready(this)) {
- lprintf("buffer ready, waking up\n");
- pthread_cond_signal(&this->data_cond);
- }
-
return 1;
}
@@ -458,28 +419,28 @@ static int mode2channels(uint32_t mode) {
int channels;
switch(mode) {
- case AO_CAP_MODE_MONO:
- channels = 1;
- break;
-
- case AO_CAP_MODE_STEREO:
- channels = 2;
- break;
-
- case AO_CAP_MODE_4CHANNEL:
- channels = 4;
- break;
-
- case AO_CAP_MODE_5CHANNEL:
- channels = 5;
- break;
-
- case AO_CAP_MODE_5_1CHANNEL:
- channels = 6;
- break;
-
- default:
- return 0;
+ case AO_CAP_MODE_MONO:
+ channels = 1;
+ break;
+
+ case AO_CAP_MODE_STEREO:
+ channels = 2;
+ break;
+
+ case AO_CAP_MODE_4CHANNEL:
+ channels = 4;
+ break;
+
+ case AO_CAP_MODE_5CHANNEL:
+ channels = 5;
+ break;
+
+ case AO_CAP_MODE_5_1CHANNEL:
+ channels = 6;
+ break;
+
+ default:
+ return 0;
}
return channels;
@@ -556,109 +517,94 @@ static int test_capabilities(dx2_driver_t *this) {
/* size of free space in the ring buffer */
static size_t buffer_free_size(dx2_driver_t *this) {
- size_t used_full_size;
+
+ int ret;
+ size_t play_pos;
+ size_t free_space;
- used_full_size = this->read_size + ((this->status != STATUS_WAIT) ? this->part_size : 0);
- _x_assert(used_full_size <= this->buffer_size);
- return this->buffer_size - used_full_size;
+ // get current play pos
+ ret = audio_tell(this, &play_pos);
+ if (!ret)
+ return 0;
+
+ // calc free space (-1)
+ free_space = (this->buffer_size + play_pos - this->write_pos - 1) % this->buffer_size;
+
+ return free_space;
}
-/* enough data in the ring buffer for playing next part? */
-static int buffer_ready(dx2_driver_t *this) {
- return this->read_size >= this->part_size;
+/* size of occupied space in the ring buffer */
+static size_t buffer_occupied_size(dx2_driver_t *this) {
+ int ret;
+ size_t play_pos;
+ size_t used_space;
+
+ // get current play pos
+ ret = audio_tell(this, &play_pos);
+ if (!ret) return 0;
+
+ // calc used space
+ used_space = (this->buffer_size + this->write_pos - play_pos) % this->buffer_size;
+
+ return used_space;
}
/* service thread working with direct sound buffer */
static void *buffer_service(void *data) {
dx2_driver_t *this = (dx2_driver_t *)data;
- HANDLE handles[PARTS];
- DWORD ret;
- int i;
-#if defined(EXACT_SLEEP) || defined(EXACT_WAIT)
- size_t play_pos, req_delay;
-#endif
+ size_t buffer_min;
+ size_t data_in_buffer;
/* prepare empty buffer */
audio_flush(this);
- for (i = 0; i < PARTS; i++) handles[i] = this->events[i].hEventNotify;
-
+ /* prepare min buffer fill */
+ buffer_min = BUFFER_MIN_MS * this->rate / 1000 * this->frame_size;
+
/* we live! */
pthread_mutex_lock(&this->data_mutex);
pthread_cond_signal(&this->data_cond);
pthread_mutex_unlock(&this->data_mutex);
while (!this->finished) {
+
pthread_mutex_lock(&this->data_mutex);
- if (!buffer_ready(this)) {
- if (!audio_stop(this)) goto fail;
- lprintf("no data (count=%d,free=%" PRIsizet ",avail=%" PRIsizet "), sleeping...\n", this->count, buffer_free_size(this), this->read_size);
- this->status = STATUS_WAIT;
- pthread_cond_wait(&this->data_cond, &this->data_mutex);
- lprintf("wake up (count=%d,free=%" PRIsizet "--,avail=%" PRIsizet ")\n", this->count, buffer_free_size(this), this->read_size);
- if (this->finished) goto finished;
- if (!audio_seek(this, this->count * this->part_size)) goto fail;
- if (!this->paused) {
- if (!audio_play(this)) goto fail;
- }
- this->status = STATUS_RUNNING;
- this->count = (this->count + 1) % PARTS;
- this->read_size -= this->part_size;
- pthread_mutex_unlock(&this->data_mutex);
- lprintf("wait for playback (newcount=%d,free=%" PRIsizet ",avail=%" PRIsizet ")\n", this->count, buffer_free_size(this), this->read_size);
- do {
- ret = WaitForMultipleObjects(PARTS, handles, FALSE, 250) - WAIT_OBJECT_0;
+ switch( this->status){
+
+ case STATUS_WAIT:
+
+ // pre: stop/buffer flushed
+ lprintf("no data, sleeping...\n");
+ pthread_cond_wait(&this->data_cond, &this->data_mutex);
+ lprintf("woke up (write_pos=%d,free=%" PRIsizet")\n", this->write_pos, buffer_free_size(this));
if (this->finished) goto finished;
- } while (ret > PARTS);
- lprintf("playback started (newcount=%d,ev=%d,free=%" PRIsizet ",avail=%" PRIsizet ")\n", this->count, ret, buffer_free_size(this), this->read_size);
- _x_assert(ret == ((PARTS + this->count - 1) % PARTS));
- } else {
- this->count = (this->count + 1) % PARTS;
- this->read_size -= this->part_size;
- pthread_mutex_unlock(&this->data_mutex);
- }
- lprintf("waiting for sound event(count=%d,free=%" PRIsizet ",avail=%" PRIsizet ")...\n", this->count, buffer_free_size(this), this->read_size);
- do {
- ret = WaitForMultipleObjects(PARTS, handles, FALSE, 250) - WAIT_OBJECT_0;
- if (this->finished) goto finished;
- } while (ret > PARTS);
- lprintf("end wait(ev=%" PRIdword ",count=%d,free=%" PRIsizet ",avail=%" PRIsizet ")\n", ret, this->count, buffer_free_size(this), this->read_size);
-#ifdef PANIC_OVERRUN
- _x_abort(ret == this->count);
-#else
- if (ret != this->count) {
- xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": play cursor overran, flushing buffers\n"));
- pthread_mutex_lock(&this->data_mutex);
- if (!audio_stop(this)) goto fail;
- if (!audio_flush(this)) goto fail;
- pthread_mutex_unlock(&this->data_mutex);
- }
-#endif
-
-#if defined(EXACT_SLEEP) || defined(EXACT_WAIT)
- /* ugly hack: wait for right time, + little over for sure */
- pthread_mutex_lock(&this->data_mutex);
- if (!audio_tell(this, &play_pos)) goto fail;
- req_delay = (this->buffer_size + play_pos - this->count * this->part_size) % this->buffer_size;
- pthread_mutex_unlock(&this->data_mutex);
- if (req_delay > (this->buffer_size >> 1)) {
- long delay;
-
- delay = 1000 * (this->buffer_size - req_delay) / (this->frame_size * this->rate) + 1 + BUFFER_MS / PARTS / 4;
- xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": delayed by %ld msec\n"), delay);
- printf("should be delayed %ld msec\n", delay);
-#ifdef EXACT_SLEEP
- xine_usec_sleep(delay * 1000);
-#endif
-#ifdef EXACT_WAIT
- WaitForMultipleObjects(PARTS, handles, FALSE, delay);
-#endif
+ if (!audio_seek(this, 0)) goto fail;
+ if (!this->paused) {
+ if (!audio_play(this)) goto fail;
+ }
+ this->status = STATUS_RUNNING;
+ pthread_mutex_unlock(&this->data_mutex);
+ break;
+
+ case STATUS_RUNNING:
+
+ // check for buffer underrun
+ data_in_buffer = buffer_occupied_size(this);
+ if ( data_in_buffer < buffer_min){
+ xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": play cursor overran (data %u, min %u), flushing buffers\n"),
+ data_in_buffer, buffer_min);
+ if (!audio_stop(this)) goto fail;
+ if (!audio_flush(this)) goto fail;
+ }
+ pthread_mutex_unlock(&this->data_mutex);
+
+ // just wait BUFFER_MIN_MS before next check
+ xine_usec_sleep(BUFFER_MIN_MS * 1000);
+ break;
}
-#endif
}
-
return NULL;
fail:
@@ -683,15 +629,15 @@ static int ao_dx2_get_property(ao_driver_t *this_gen, int property) {
switch(property) {
- case AO_PROP_MIXER_VOL:
- case AO_PROP_PCM_VOL:
- return this->volume;
+ case AO_PROP_MIXER_VOL:
+ case AO_PROP_PCM_VOL:
+ return this->volume;
- case AO_PROP_MUTE_VOL:
- return this->muted;
+ case AO_PROP_MUTE_VOL:
+ return this->muted;
- default:
- return 0;
+ default:
+ return 0;
}
}
@@ -702,26 +648,26 @@ static int ao_dx2_set_property(ao_driver_t *this_gen, int property, int value) {
switch(property) {
- case AO_PROP_MIXER_VOL:
- case AO_PROP_PCM_VOL:
- lprintf("set volume to %d\n", value);
- pthread_mutex_lock(&this->data_mutex);
- if (!this->muted) {
- if (this->dsbuffer && !audio_set_volume(this, value)) return ~value;
- }
- this->volume = value;
- pthread_mutex_unlock(&this->data_mutex);
- break;
+ case AO_PROP_MIXER_VOL:
+ case AO_PROP_PCM_VOL:
+ lprintf("set volume to %d\n", value);
+ pthread_mutex_lock(&this->data_mutex);
+ if (!this->muted) {
+ if (this->dsbuffer && !audio_set_volume(this, value)) return ~value;
+ }
+ this->volume = value;
+ pthread_mutex_unlock(&this->data_mutex);
+ break;
- case AO_PROP_MUTE_VOL:
- pthread_mutex_lock(&this->data_mutex);
- if (this->dsbuffer && !audio_set_volume(this, value ? 0 : this->volume)) return ~value;
- this->muted = value;
- pthread_mutex_unlock(&this->data_mutex);
- break;
+ case AO_PROP_MUTE_VOL:
+ pthread_mutex_lock(&this->data_mutex);
+ if (this->dsbuffer && !audio_set_volume(this, value ? 0 : this->volume)) return ~value;
+ this->muted = value;
+ pthread_mutex_unlock(&this->data_mutex);
+ break;
- default:
- return ~value;
+ default:
+ return ~value;
}
@@ -747,18 +693,17 @@ static int ao_dx2_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int
this->status = STATUS_START;
if (!audio_create_buffers(this)) return 0;
- if (!audio_create_events(this)) goto fail_buffers;
- if (!audio_set_volume(this, this->volume)) goto fail_events;
+ if (!audio_set_volume(this, this->volume)) goto fail_buffers;
if (pthread_cond_init(&this->data_cond, NULL) != 0) {
xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": can't create pthread condition: %s\n"), strerror(errno));
- goto fail_events;
+ goto fail_buffers;
}
if (pthread_mutex_init(&this->data_mutex, NULL) != 0) {
xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": can't create pthread mutex: %s\n"), strerror(errno));
goto fail_cond;
}
-
+
/* creating the service thread and waiting for its signal */
pthread_mutex_lock(&this->data_mutex);
if (pthread_create(&this->buffer_service, NULL, buffer_service, this) != 0) {
@@ -775,8 +720,6 @@ fail_mutex:
pthread_mutex_destroy(&this->data_mutex);
fail_cond:
pthread_cond_destroy(&this->data_cond);
-fail_events:
- audio_destroy_events(this);
fail_buffers:
audio_destroy_buffers(this);
return 0;
@@ -803,22 +746,26 @@ static int ao_dx2_bytes_per_frame(ao_driver_t *this_gen) {
static int ao_dx2_delay(ao_driver_t *this_gen) {
dx2_driver_t *this = (dx2_driver_t *)this_gen;
- int frames;
- size_t final_pos, play_pos;
-
- if (this->status != STATUS_RUNNING) return this->read_size / this->frame_size;
+ int frames = 0;
+ int ret;
+ size_t play_pos;
pthread_mutex_lock(&this->data_mutex);
- if (!audio_tell(this, &play_pos)) {
- pthread_mutex_unlock(&this->data_mutex);
- return 0;
- }
- final_pos = this->read_size + (((PARTS + this->count - 1) % PARTS) + 1) * this->part_size - 1;
- frames = (this->buffer_size + final_pos - play_pos) % this->buffer_size / this->frame_size;
+
+ if (this->status != STATUS_RUNNING){
+ frames = this->write_pos / this->frame_size;
+ } else {
+ ret = audio_tell(this, &play_pos);
+ if (ret){
+ frames = buffer_occupied_size(this) / this->frame_size;
+ }
+ }
+
pthread_mutex_unlock(&this->data_mutex);
#ifdef LOG
- if ((rand() % 10) == 0) lprintf("frames=%d, play_pos=%" PRIdword ", block=%" PRIsizet "..%" PRIsizet "\n", frames, play_pos, final_pos - this->part_size + 1, final_pos);
+ if ((rand() % 10) == 0)
+ lprintf("frames=%d, play_pos=%" PRIdword ", write_pos=%u\n", frames, play_pos, this->write_pos);
#endif
return frames;
@@ -840,7 +787,7 @@ static int ao_dx2_write(ao_driver_t *this_gen, int16_t* audio_data, uint32_t num
while (((free_size = buffer_free_size(this)) == 0) && !this->finished) {
lprintf("buffer full, waiting\n");
pthread_mutex_unlock(&this->data_mutex);
- xine_usec_sleep(1000 * BUFFER_MS / PARTS / 5);
+ xine_usec_sleep(1000 * BUFFER_MS / 10);
pthread_mutex_lock(&this->data_mutex);
}
if (free_size >= input_size) size = input_size;
@@ -856,6 +803,12 @@ static int ao_dx2_write(ao_driver_t *this_gen, int16_t* audio_data, uint32_t num
input_size -= size;
}
+ /* signal, if are waiting and need wake up */
+ if ((this->status == STATUS_WAIT) && (buffer_occupied_size(this) > BUFFER_MIN_MS * this->rate / 1000 * this->frame_size)) {
+ lprintf("buffer ready, waking up\n");
+ pthread_cond_signal(&this->data_cond);
+ }
+
return 1;
}
@@ -881,7 +834,6 @@ static void ao_dx2_close(ao_driver_t *this_gen) {
if (pthread_mutex_destroy(&this->data_mutex) != 0) {
xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": can't destroy pthread mutex: %s\n"), strerror(errno));
}
- audio_destroy_events(this);
audio_destroy_buffers(this);
}
@@ -896,12 +848,8 @@ static void ao_dx2_exit(ao_driver_t *this_gen) {
}
-/*
- * TODO: check
- */
static int ao_dx2_get_gap_tolerance(ao_driver_t *this_gen) {
- /* half of part of the buffer in pts (1 msec = 90 pts) */
- return (90 * (BUFFER_MS / PARTS)) >> 1;
+ return GAP_TOLERANCE;
}
@@ -910,36 +858,36 @@ static int ao_dx2_control(ao_driver_t *this_gen, int cmd, ...) {
switch(cmd) {
- case AO_CTRL_PLAY_PAUSE:
- lprintf("control pause\n");
- pthread_mutex_lock(&this->data_mutex);
- if (!this->paused) {
- audio_stop(this);
- this->paused = 1;
- }
- pthread_mutex_unlock(&this->data_mutex);
- break;
+ case AO_CTRL_PLAY_PAUSE:
+ lprintf("control pause\n");
+ pthread_mutex_lock(&this->data_mutex);
+ if (!this->paused) {
+ audio_stop(this);
+ this->paused = 1;
+ }
+ pthread_mutex_unlock(&this->data_mutex);
+ break;
- case AO_CTRL_PLAY_RESUME:
- lprintf("control resume\n");
- pthread_mutex_lock(&this->data_mutex);
- if (this->paused) {
- if (this->status != STATUS_WAIT) audio_play(this);
- this->paused = 0;
- }
- pthread_mutex_unlock(&this->data_mutex);
- break;
-
- case AO_CTRL_FLUSH_BUFFERS:
- lprintf("control flush\n");
- pthread_mutex_lock(&this->data_mutex);
- audio_stop(this);
- audio_flush(this);
- pthread_mutex_unlock(&this->data_mutex);
- break;
+ case AO_CTRL_PLAY_RESUME:
+ lprintf("control resume\n");
+ pthread_mutex_lock(&this->data_mutex);
+ if (this->paused) {
+ if (this->status != STATUS_WAIT) audio_play(this);
+ this->paused = 0;
+ }
+ pthread_mutex_unlock(&this->data_mutex);
+ break;
+
+ case AO_CTRL_FLUSH_BUFFERS:
+ lprintf("control flush\n");
+ pthread_mutex_lock(&this->data_mutex);
+ audio_stop(this);
+ audio_flush(this);
+ pthread_mutex_unlock(&this->data_mutex);
+ break;
- default:
- xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": unknown control command %d\n"), cmd);
+ default:
+ xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": unknown control command %d\n"), cmd);
}
@@ -955,7 +903,7 @@ static ao_driver_t *open_plugin(audio_driver_class_t *class_gen, const void *dat
lprintf("open plugin called\n");
- this = (dx2_driver_t *)xine_xmalloc(sizeof(dx2_driver_t));
+ this = calloc(1, sizeof(dx2_driver_t));
if (!this)
return NULL;
@@ -1009,7 +957,7 @@ static void *init_class(xine_t *xine, void *data) {
lprintf("init class\n");
- this = (dx2_class_t *)xine_xmalloc(sizeof(dx2_class_t));
+ this = calloc(1, sizeof(dx2_class_t));
if (!this)
return NULL;
diff --git a/src/audio_out/audio_directx_out.c b/src/audio_out/audio_directx_out.c
index 77e3e15b9..21ba23d91 100644
--- a/src/audio_out/audio_directx_out.c
+++ b/src/audio_out/audio_directx_out.c
@@ -60,9 +60,13 @@ typedef unsigned char boolean;
* the linking stage.
*****************************************************************************/
#if 1
-static const GUID IID_IDirectSoundNotify = {
+static const GUID xine_IID_IDirectSoundNotify = {
0xB0210783,0x89CD,0x11D0,{0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16}
};
+#ifdef IID_IDirectSoundNotify
+# undef IID_IDirectSoundNotify
+#endif
+#define IID_IDirectSoundNotify xine_IID_IDirectSoundNotify
#endif
@@ -790,7 +794,7 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
audiox_class_t *class = (audiox_class_t *) class_gen;
ao_directx_t *ao_directx;
- ao_directx = ( ao_directx_t * ) xine_xmalloc( sizeof( ao_directx_t ) );
+ ao_directx = calloc(1, sizeof(ao_directx_t));
if (!ao_directx)
return NULL;
@@ -841,7 +845,7 @@ static void *init_class (xine_t *xine, void *data) {
/*
* from this point on, nothing should go wrong anymore
*/
- audiox = (audiox_class_t *) xine_xmalloc (sizeof (audiox_class_t));
+ audiox = calloc(1, sizeof (audiox_class_t));
if (!audiox)
return NULL;
diff --git a/src/audio_out/audio_esd_out.c b/src/audio_out/audio_esd_out.c
index 4b209bea1..63c54b546 100644
--- a/src/audio_out/audio_esd_out.c
+++ b/src/audio_out/audio_esd_out.c
@@ -521,7 +521,7 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
esd_close(audio_fd);
- this = (esd_driver_t *) xine_xmalloc (sizeof (esd_driver_t));
+ this = calloc(1, sizeof (esd_driver_t));
if (!this)
return NULL;
this->xine = class->xine;
@@ -583,7 +583,7 @@ static void *init_class (xine_t *xine, void *data) {
esd_class_t *this;
- this = (esd_class_t *) xine_xmalloc (sizeof (esd_class_t));
+ this = calloc(1, sizeof (esd_class_t));
if (!this)
return NULL;
diff --git a/src/audio_out/audio_file_out.c b/src/audio_out/audio_file_out.c
index 225d64d0e..699a5cf54 100644
--- a/src/audio_out/audio_file_out.c
+++ b/src/audio_out/audio_file_out.c
@@ -323,7 +323,7 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
lprintf ("open_plugin called\n");
- this = (file_driver_t *) xine_xmalloc (sizeof (file_driver_t));
+ this = calloc(1, sizeof (file_driver_t));
if (!this)
return NULL;
@@ -375,7 +375,7 @@ static void *init_class (xine_t *xine, void *data) {
lprintf ("init class\n");
- this = (file_class_t *) xine_xmalloc (sizeof (file_class_t));
+ this = calloc(1, sizeof (file_class_t));
if (!this)
return NULL;
diff --git a/src/audio_out/audio_fusionsound_out.c b/src/audio_out/audio_fusionsound_out.c
index 92eadc6cf..6c3a0ae33 100644
--- a/src/audio_out/audio_fusionsound_out.c
+++ b/src/audio_out/audio_fusionsound_out.c
@@ -389,11 +389,11 @@ static ao_driver_t* open_plugin(audio_driver_class_t *ao_class,
fusionsound_class_t *class = (fusionsound_class_t *) ao_class;
fusionsound_driver_t *this;
const char *args[] = { "xine", "--dfb:no-sighandler", "--fs:no-banner" };
- int argn = sizeof(args) / sizeof(args[0]);
+ const size_t argn = sizeof(args) / sizeof(args[0]);
char **argp = (char **) args;
DFBResult ret;
- this = (fusionsound_driver_t *) xine_xmalloc (sizeof(fusionsound_driver_t));
+ this = calloc(1, sizeof(fusionsound_driver_t));
if (!this) {
xprintf (class->xine, XINE_VERBOSITY_LOG,
"audio_fusionsound_out: driver interface allocation failed!\n");
@@ -460,7 +460,7 @@ static void* init_class(xine_t *xine, void *data) {
return NULL;
}
- class = (fusionsound_class_t *) xine_xmalloc (sizeof( fusionsound_class_t));
+ class = calloc(1, sizeof( fusionsound_class_t));
if (!class) {
xprintf (xine, XINE_VERBOSITY_LOG,
"audio_fusionsound_out: class interface allocation failed!\n");
diff --git a/src/audio_out/audio_jack_out.c b/src/audio_out/audio_jack_out.c
index 1bc070527..10c58c774 100644
--- a/src/audio_out/audio_jack_out.c
+++ b/src/audio_out/audio_jack_out.c
@@ -1,3 +1,6 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
#include <stdio.h>
#include <errno.h>
@@ -17,431 +20,942 @@
#define AO_OUT_JACK_IFACE_VERSION 8
#define GAP_TOLERANCE AO_MAX_GAP
-#define BUFSIZE 81920
+/* maximum number of channels supported, avoids lots of mallocs */
+#define MAX_CHANS 6
-typedef struct jack_driver_s {
+typedef struct jack_driver_s
+{
+ ao_driver_t ao_driver;
+ xine_t *xine;
+
+ int capabilities;
+ int mode;
+ int paused;
+ int underrun;
+
+ int32_t output_sample_rate, input_sample_rate;
+ uint32_t num_channels;
+ uint32_t bits_per_sample;
+ uint32_t bytes_per_frame;
+ uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */
+ uint32_t fragment_size;
+
+ jack_client_t *client;
+ jack_port_t *ports[MAX_CHANS];
+
+ /*! buffer for audio data */
+ unsigned char *buffer;
+
+ /*! buffer read position, may only be modified by playback thread or while it is stopped */
+ uint32_t read_pos;
+ /*! buffer write position, may only be modified by MPlayer's thread */
+ uint32_t write_pos;
+
+ struct
+ {
+ int volume;
+ int mute;
+ } mixer;
- ao_driver_t ao_driver;
- xine_t *xine;
+} jack_driver_t;
- int capabilities;
+typedef struct
+{
+ audio_driver_class_t driver_class;
+ config_values_t *config;
+ xine_t *xine;
+} jack_class_t;
- int32_t sample_rate;
- uint32_t num_channels;
- uint32_t bits_per_sample;
- uint32_t bytes_per_frame;
- jack_client_t *client;
- jack_port_t *port_1;
- jack_port_t *port_2;
- float buf_1[BUFSIZE];
- float buf_2[BUFSIZE];
- uint32_t buf_read;
- uint32_t buf_write;
+/**************************************************************
+ *
+ * Simple ringbuffer implementation
+ * Lifted from mplayer ao_jack.c
+ *
+**************************************************************/
+
+
+/*! size of one chunk, if this is too small Xine will start to "stutter" */
+/*! after a short time of playback */
+#define CHUNK_SIZE (16 * 1024)
+/*! number of "virtual" chunks the buffer consists of */
+#define NUM_CHUNKS 8
+/* This type of ring buffer may never fill up completely, at least */
+/* one byte must always be unused. */
+/* For performance reasons (alignment etc.) one whole chunk always stays */
+/* empty, not only one byte. */
+#define BUFFSIZE ((NUM_CHUNKS + 1) * CHUNK_SIZE)
+
+/**
+ * \brief get the number of free bytes in the buffer
+ * \return number of free bytes in buffer
+ *
+ * may only be called by Xine's thread
+ * return value may change between immediately following two calls,
+ * and the real number of free bytes might be larger!
+ */
+static int buf_free (jack_driver_t *this)
+{
+ int free = this->read_pos - this->write_pos - CHUNK_SIZE;
+ if (free < 0)
+ free += BUFFSIZE;
+ return free;
+}
- uint32_t volume;
- uint32_t mute;
+/**
+ * \brief get amount of data available in the buffer
+ * \return number of bytes available in buffer
+ *
+ * may only be called by the playback thread
+ * return value may change between immediately following two calls,
+ * and the real number of buffered bytes might be larger!
+ */
+static int buf_used (jack_driver_t *this)
+{
+ int used = this->write_pos - this->read_pos;
+ if (used < 0)
+ used += BUFFSIZE;
+ return used;
+}
-} jack_driver_t;
+/**
+ * \brief insert len bytes into buffer
+ * \param data data to insert
+ * \param len length of data
+ * \return number of bytes inserted into buffer
+ *
+ * If there is not enough room, the buffer is filled up
+ *
+ * TODO: Xine should really pass data as float, perhaps in V1.2?
+ */
+static int write_buffer_32 (jack_driver_t *this, unsigned char *data, int len)
+{
+ int first_len = BUFFSIZE - this->write_pos;
+ int free = buf_free (this);
+ if (len > free)
+ len = free;
+ if (first_len > len)
+ first_len = len;
+
+ /* copy from current write_pos to end of buffer */
+ memcpy (&(this->buffer[this->write_pos]), data, first_len);
+ if (len > first_len) { /* we have to wrap around */
+ /* remaining part from beginning of buffer */
+ memcpy (this->buffer, &data[first_len], len - first_len);
+ }
+ this->write_pos = (this->write_pos + len) % BUFFSIZE;
+
+ return len;
+}
+
+static int write_buffer_16 (jack_driver_t *this, unsigned char *data, int len)
+{
+ int samples_free = buf_free (this) / (sizeof (float));
+ int samples = len / 2;
+ if (samples > samples_free)
+ samples = samples_free;
+
+ /* Rename some pointers so that the next bit of gymnastics is easier to read */
+ uint32_t write_pos = this->write_pos;
+ float *p_write;
+ int16_t *p_read = (int16_t *) data;
+ int i;
+ for (i = 0; i < samples; i++) {
+ /* Read in 16bits, write out floats */
+ p_write = (float *) (&(this->buffer[write_pos]));
+ *p_write = ((float) (p_read[i])) / 32767.0f;
+ write_pos = (write_pos + sizeof (float)) % BUFFSIZE;
+ }
+ this->write_pos = write_pos;
+
+ return samples * 2;
+}
-typedef struct {
- audio_driver_class_t driver_class;
- config_values_t *config;
- xine_t *xine;
-} jack_class_t;
-static int jack_process(jack_nframes_t nframes, void *arg)
+/**
+ * \brief read data from buffer and splitting it into channels
+ * \param bufs num_bufs float buffers, each will contain the data of one channel
+ * \param cnt number of samples to read per channel
+ * \param num_bufs number of channels to split the data into
+ * \return number of samples read per channel, equals cnt unless there was too
+ * little data in the buffer
+ *
+ * Assumes the data in the buffer is of type float, the number of bytes
+ * read is res * num_bufs * sizeof(float), where res is the return value.
+ * If there is not enough data in the buffer remaining parts will be filled
+ * with silence.
+ */
+static int read_buffer (jack_driver_t *this, float **bufs, int cnt,
+ int num_bufs, float gain)
{
- jack_driver_t *this = (jack_driver_t *)arg;
- uint32_t local_buf_read = this->buf_read;
- uint32_t local_buf_write = this->buf_write;
- uint32_t src_channel, target_channel;
- uint32_t frame;
- float *buf, *out;
- float gain = 0;
-
- if (!this->client) return 0;
-
- if (!this->mute) {
- gain = (float)this->volume / 100.0;
+ int buffered = buf_used (this);
+ int i, j;
+ int orig_cnt = cnt;
+ if (cnt * sizeof (float) * num_bufs > buffered)
+ cnt = buffered / (sizeof (float) * num_bufs);
+
+ uint32_t read_pos = this->read_pos;
+ unsigned char *buffer = this->buffer;
+ for (i = 0; i < cnt; i++) {
+ for (j = 0; j < num_bufs; j++) {
+ bufs[j][i] = *((float *) (&(buffer[read_pos]))) * gain;
+ read_pos = (read_pos + sizeof (float)) % BUFFSIZE;
}
+ }
+ this->read_pos = read_pos;
+ for (i = cnt; i < orig_cnt; i++)
+ for (j = 0; j < num_bufs; j++)
+ bufs[j][i] = 0;
- for (target_channel = 0; target_channel < 2; ++target_channel) {
-
- if (target_channel < this->num_channels) src_channel = target_channel;
- else src_channel = 0;
+ return cnt;
+}
- jack_port_t *port = (target_channel ? this->port_2 : this->port_1);
- if (!port) continue;
+/**
+ * \brief fill the buffers with silence
+ * \param bufs num_bufs float buffers, each will contain the data of one channel
+ * \param cnt number of samples in each buffer
+ * \param num_bufs number of buffers
+ */
+static void silence (float **bufs, int cnt, int num_bufs)
+{
+ int i, j;
+ for (i = 0; i < cnt; i++)
+ for (j = 0; j < num_bufs; j++)
+ bufs[j][i] = 0;
+}
- buf = (src_channel ? this->buf_2 : this->buf_1);
- out = (float *)jack_port_get_buffer(port, nframes);
- local_buf_read = this->buf_read;
- frame = 0;
+/**************************************************************
+ *
+ * Jack interface functions
+ *
+**************************************************************/
- while (frame < nframes && local_buf_read != local_buf_write) {
- // local_buf_write doesn't change during this process,
- // so we can safely defer updating buf_read until after
+/**
+ * \brief stop playing and empty buffers (for seeking/pause)
+ */
+static void jack_reset (jack_driver_t *this)
+{
+ this->paused = 1;
+ this->read_pos = this->write_pos = 0;
+ this->paused = 0;
+}
- out[frame++] = buf[local_buf_read] * gain;
- if (++local_buf_read == BUFSIZE) local_buf_read = 0;
- }
+static int jack_callback (jack_nframes_t nframes, void *arg)
+{
+ jack_driver_t *this = (jack_driver_t *) arg;
- if (frame < nframes) {
-// printf("jack_process: underrun: %u required, %u available\n",
-// nframes, frame);
- while (frame < nframes) {
- out[frame++] = 0.0f;
- }
- }
+ if (!this->client) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_callback: called without a client parameter? silently trying to continue...\n");
+ return 0;
+ }
+
+ float gain = 0;
+ if (!this->mixer.mute) {
+ gain = (float) this->mixer.volume / 100.0;
+ gain *= gain; /* experiment with increasing volume range */
+ }
+
+ float *bufs[MAX_CHANS];
+ int i;
+ for (i = 0; i < this->num_channels; i++)
+ bufs[i] = jack_port_get_buffer (this->ports[i], nframes);
+
+ if (this->paused || this->underrun) {
+ silence (bufs, nframes, this->num_channels);
+ } else {
+ int frames_read = read_buffer (this, bufs, nframes, this->num_channels, gain);
+ if (frames_read < nframes) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_callback: underrun - frames read: %d\n", frames_read);
+ this->underrun = 1;
}
+ }
- this->buf_read = local_buf_read;
+ return 0;
+}
-// printf("jack_process: buf_read %u, buf_write %u\n", this->buf_read, this->buf_write);
+static void jack_shutdown (void *arg)
+{
+ jack_driver_t *this = (jack_driver_t *) arg;
+ this->client = NULL;
+}
- return 0;
+/*
+ * Open the Jack audio device
+ * Return 1 on success, 0 on failure
+ * All error handling rests with the caller, we just try to open the device here
+ */
+static int jack_open_device (ao_driver_t *this_gen, char *jack_device,
+ int32_t *poutput_sample_rate, int num_channels)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ const char **matching_ports = NULL;
+ char *port_name = NULL;
+ jack_client_t *client = this->client;
+
+ int port_flags = JackPortIsInput;
+ int i;
+ int num_ports;
+
+ if (num_channels > MAX_CHANS) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_open_device: Invalid number of channels: %i\n",
+ num_channels);
+ goto err_out;
+ }
+ /* Try to create a client called "xine" */
+ if ((client = jack_client_new ("xine")) == 0) {
+ /* If that doesn't work it could be because running two copies of xine - try using a unique name */
+ char client_name[20];
+ sprintf (client_name, "xine (%d)", (int) getpid ());
+
+ if ((client = jack_client_new (client_name)) == 0) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "\njack_open_device: Error: Failed to connect to JACK server\n");
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_open_device: (did you start 'jackd' server?)\n");
+ goto err_out;
+ }
+ }
+
+ /* Save the new client */
+ this->client = client;
+
+ jack_reset (this);
+ jack_set_process_callback (client, jack_callback, this);
+
+ /* list matching ports */
+ if (!jack_device)
+ port_flags |= JackPortIsPhysical;
+ matching_ports = jack_get_ports (client, jack_device, NULL, port_flags);
+ for (num_ports = 0; matching_ports && matching_ports[num_ports];
+ num_ports++);
+ if (!num_ports) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_open_device: no physical ports available\n");
+ goto err_out;
+ }
+ if (num_ports < num_channels) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_open_device: not enough physical ports available\n");
+ goto err_out;
+ }
+
+ /* create output ports */
+ for (i = 0; i < num_channels; i++) {
+ char pname[50];
+ snprintf (pname, 50, "out_%d", i);
+ this->ports[i] =
+ jack_port_register (client, pname, JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsOutput, 0);
+ if (!this->ports[i]) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_open_device: could not create output ports? Why not?\n");
+ goto err_out;
+ }
+ }
+ if (jack_activate (client)) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_open_device: jack_activate() failed\n");
+ goto err_out;
+ }
+ for (i = 0; i < num_channels; i++) {
+ if (jack_connect
+ (client, jack_port_name (this->ports[i]), matching_ports[i])) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "jack_open_device: jack_connect() failed\n");
+ goto err_out;
+ }
+ }
+ *poutput_sample_rate = jack_get_sample_rate (client);
+
+ free (matching_ports);
+ return 1;
+
+err_out:
+ free (matching_ports);
+ if (client) {
+ jack_client_close (client);
+ this->client = NULL;
+ }
+ return 0;
}
-
-static void jack_shutdown(void *arg)
+
+/**************************************************************
+ *
+ * Xine interface functions
+ *
+**************************************************************/
+
+
+/**
+ * close the device and reset the play position
+ */
+static void ao_jack_close (ao_driver_t *this_gen)
{
- jack_driver_t *this = (jack_driver_t *)arg;
- this->client = 0;
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "ao_jack_close: closing\n");
+
+ jack_reset (this);
+ if (this->client) {
+ jack_client_close (this->client);
+ this->client = NULL;
+ }
}
/*
* open the audio device for writing to
*/
-static int ao_jack_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode)
+static int ao_jack_open (ao_driver_t *this_gen, uint32_t bits, uint32_t rate,
+ int mode)
{
- jack_driver_t *this = (jack_driver_t *) this_gen;
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ config_values_t *config = this->xine->config;
+ char *jack_device;
- if (bits != 16) {
- fprintf(stderr, "ao_jack_open: bits=%u expected %u\n", bits, 16);
- return 0;
- }
+ jack_device =
+ config->lookup_entry (config, "audio.device.jack_device_name")->str_value;
- rate = jack_get_sample_rate(this->client);
- fprintf(stderr, "ao_jack_open: JACK sample rate is %u\n", rate);
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "ao_jack_open: ao_open rate=%d, mode=%d, bits=%d dev=%s\n", rate,
+ mode, bits, jack_device);
- switch (mode) {
- case AO_CAP_MODE_MONO:
- this->num_channels = 1;
- break;
- case AO_CAP_MODE_STEREO:
- this->num_channels = 2;
- break;
+ if ((bits != 16) && (bits != 32)) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "ao_jack_open: bits=%u expected 16 or 32bit only\n", bits);
+ return 0;
+ }
+
+ if ((mode & this->capabilities) == 0) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "ao_jack_open: unsupported mode %08x\n", mode);
+ return 0;
+ }
+
+ /* If device open already then either re-use it or close it */
+ if (this->client) {
+ if ((mode == this->mode) && (rate == this->input_sample_rate)) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "ao_jack_open: device already open, reusing it\n");
+ return this->output_sample_rate;
}
- this->buf_read = this->buf_write = 0;
- this->sample_rate = rate;
- this->bits_per_sample = bits;
- this->capabilities = AO_CAP_16BITS | AO_CAP_MODE_MONO | \
- AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_MUTE_VOL;
- this->bytes_per_frame = this->num_channels * (bits / 8);
+ ao_jack_close (this_gen);
+ }
+
+
+ this->mode = mode;
+ this->input_sample_rate = rate;
+ this->bits_per_sample = bits;
+ this->bytes_in_buffer = 0;
+ this->read_pos = this->write_pos = 0;
+ this->paused = 0;
+ this->underrun = 0;
+
+ /*
+ * set number of channels / a52 passthrough
+ */
+ switch (mode) {
+ case AO_CAP_MODE_MONO:
+ this->num_channels = 1;
+ break;
+ case AO_CAP_MODE_STEREO:
+ this->num_channels = 2;
+ break;
+ case AO_CAP_MODE_4CHANNEL:
+ this->num_channels = 4;
+ break;
+ case AO_CAP_MODE_4_1CHANNEL:
+ case AO_CAP_MODE_5CHANNEL:
+ case AO_CAP_MODE_5_1CHANNEL:
+ this->num_channels = 6;
+ break;
+ case AO_CAP_MODE_A52:
+ case AO_CAP_MODE_AC5:
+ /* FIXME: Is this correct...? */
+ this->num_channels = 2;
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "ao_jack_open: AO_CAP_MODE_A52\n");
+ break;
+ default:
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "ao_jack_open: JACK Driver does not support the requested mode: 0x%X\n",
+ mode);
+ return 0;
+ }
+
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "ao_jack_open: %d channels output\n", this->num_channels);
+ this->bytes_per_frame = (this->bits_per_sample * this->num_channels) / 8;
- fprintf(stderr, "ao_jack_open: bits=%d rate=%d, mode=%d OK\n", bits, rate, mode);
+ /*
+ * open audio device
+ */
+ if (!jack_open_device (this_gen, jack_device, &(this->output_sample_rate),
+ this->num_channels))
+ return 0;
- return rate;
+ if (this->input_sample_rate != this->output_sample_rate) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "ao_jack_open: audio rate : %d requested, %d provided by device\n",
+ this->input_sample_rate, this->output_sample_rate);
+ }
+
+ return this->output_sample_rate;
}
-static int ao_jack_num_channels(ao_driver_t *this_gen)
+static int ao_jack_num_channels (ao_driver_t *this_gen)
{
- jack_driver_t *this = (jack_driver_t *) this_gen;
- return this->num_channels;
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ return this->num_channels;
}
-static int ao_jack_bytes_per_frame(ao_driver_t *this_gen)
+static int ao_jack_bytes_per_frame (ao_driver_t *this_gen)
{
- jack_driver_t *this = (jack_driver_t *) this_gen;
- return this->bytes_per_frame;
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ return this->bytes_per_frame;
}
static int ao_jack_get_gap_tolerance (ao_driver_t *this_gen)
{
- return GAP_TOLERANCE;
+ return GAP_TOLERANCE;
}
-static int last_write_space = 0;
-
-static int ao_jack_write(ao_driver_t *this_gen, int16_t *data,
- uint32_t num_frames)
+/*
+ * Return the number of outstanding frames in all output buffers
+ * need to account for ring buffer plus Jack, plus soundcard
+ */
+static int ao_jack_delay (ao_driver_t *this_gen)
{
- jack_driver_t *this = (jack_driver_t *) this_gen;
- uint32_t frame, channel;
-
- uint32_t local_buf_read = this->buf_read;
- uint32_t local_buf_write = this->buf_write;
- uint32_t space = (local_buf_read + BUFSIZE - local_buf_write - 1) % BUFSIZE;
- uint32_t first_frame = 0;
-
- int c = 0;
- while (space < num_frames) {
- if (++c == 10) return 0;
- usleep(10000);
- local_buf_read = this->buf_read;
- space = (local_buf_read + BUFSIZE - local_buf_write - 1) % BUFSIZE;
- }
-
-// if (space < num_frames) return 0;
-
-// printf("ao_jack_write: %u frames on %u channels, space is %u\n", num_frames, this->num_channels, space);
- last_write_space = space;
-
- for (frame = first_frame; frame < num_frames; ++frame) {
- for (channel = 0; channel < this->num_channels; ++channel) {
- float *buf = (channel ? this->buf_2 : this->buf_1);
- int16_t sample = data[frame * this->num_channels + channel];
- buf[local_buf_write] = ((float)sample) / 32767.0f;
-// printf("%6f ", buf[local_buf_write]);
-// if (++c == 8) { printf("\n"); c = 0; }
- }
- if (++local_buf_write == BUFSIZE) local_buf_write = 0;
- }
-
- this->buf_write = local_buf_write;
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ int frames_played = jack_frames_since_cycle_start (this->client);
-// printf("ao_jack_write: buf_read %u, buf_write %u\n", this->buf_read, this->buf_write);
+ int delay = 0;
+ /* Ring Buffer always stores floats */
+ /* TODO: Unsure if the delay should be fragment_size*2 or *3? */
+ delay = buf_used (this) / (sizeof (float) * this->num_channels) +
+ this->fragment_size * 3 - frames_played;
- return 1;
+ return delay;
}
-static int ao_jack_delay (ao_driver_t *this_gen)
+ /* Write audio samples
+ * num_frames is the number of audio frames present
+ * audio frames are equivalent one sample on each channel.
+ * I.E. Stereo 16 bits audio frames are 4 bytes.
+ * MUST SIMULATE BLOCKING WRITES
+ */
+static int ao_jack_write (ao_driver_t *this_gen, int16_t *frame_buffer,
+ uint32_t num_frames)
{
- jack_driver_t *this = (jack_driver_t *) this_gen;
-
- uint32_t local_buf_read = this->buf_read;
- uint32_t local_buf_write = this->buf_write;
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ int written = 0;
+ int num_bytes = num_frames * this->bytes_per_frame;
+
+ /* First try and write all the bytes in one go */
+ this->underrun = 0;
+ /* TODO: In the future Xine should pass only floats to us, so no conversion needed */
+ if (this->bits_per_sample == 16)
+ written = write_buffer_16 (this, (char *) frame_buffer, num_bytes);
+ else if (this->bits_per_sample == 32)
+ written = write_buffer_32 (this, (char *) frame_buffer, num_bytes);
+
+ /* If this fails then need to spin and keep trying until everything written */
+ int spin_count = 0;
+ while ((written < num_bytes) && (spin_count < 40)) {
+ num_bytes -= written;
+ frame_buffer += written / 2;
+
+ /* Sleep to save CPU */
+ int until_callback =
+ this->fragment_size - jack_frames_since_cycle_start (this->client);
+ if ((until_callback < 0) || (until_callback > this->fragment_size)) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "ao_jack_write: Invalid until_callback %d\n", until_callback);
+ until_callback = this->fragment_size;
+ }
+ xine_usec_sleep (((until_callback +
+ 100) * 1000.0 * 1000.0) / this->output_sample_rate);
- int delay = 0;
+ if (this->bits_per_sample == 16)
+ written = write_buffer_16 (this, (char *) frame_buffer, num_bytes);
+ else if (this->bits_per_sample == 32)
+ written = write_buffer_32 (this, (char *) frame_buffer, num_bytes);
- if (local_buf_write > local_buf_read) {
- delay = local_buf_write - local_buf_read;
- } else {
- delay = ((local_buf_write + BUFSIZE - local_buf_read) % BUFSIZE);
- }
+ if (written == 0)
+ spin_count++;
+ else
+ spin_count = 0;
- return delay;// - jack_get_buffer_size(this->client);
-}
+ if (written == 0)
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "ao_jack_write: unusual, couldn't write anything\n");
+ };
-static void ao_jack_close(ao_driver_t *this_gen)
-{
- // nothing
-}
+ if (spin_count)
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "Nonzero spin_count...%d\n", spin_count);
-static uint32_t ao_jack_get_capabilities (ao_driver_t *this_gen) {
- jack_driver_t *this = (jack_driver_t *) this_gen;
- return this->capabilities;
+ return spin_count ? 0 : 1; /* return 1 on success, 0 if we got stuck for some reason */
}
-static void ao_jack_exit(ao_driver_t *this_gen)
+static uint32_t ao_jack_get_capabilities (ao_driver_t *this_gen)
{
- jack_driver_t *this = (jack_driver_t *) this_gen;
- jack_client_t *client = this->client;
- ao_jack_close(this_gen);
- this->client = 0;
- if (client) jack_client_close(client);
- free (this);
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+ return this->capabilities;
}
-static int ao_jack_get_property (ao_driver_t *this_gen, int property) {
- jack_driver_t *this = (jack_driver_t *) this_gen;
-
- switch(property) {
- case AO_PROP_PCM_VOL:
- case AO_PROP_MIXER_VOL:
-// printf("ao_jack_get_property(AO_PROP_MIXER_VOL): %u\n", this->volume);
- return this->volume;
- break;
- case AO_PROP_MUTE_VOL:
-// printf("ao_jack_get_property(AO_PROP_MUTE_VOL): %u\n", this->mute);
- return this->mute;
- break;
- }
+static void ao_jack_exit (ao_driver_t *this_gen)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
- return 0;
+ ao_jack_close (this_gen);
+ if (this->buffer)
+ free (this->buffer);
+ free (this);
}
-static int ao_jack_set_property (ao_driver_t *this_gen, int property, int value) {
- jack_driver_t *this = (jack_driver_t *) this_gen;
-
- switch(property) {
- case AO_PROP_PCM_VOL:
- case AO_PROP_MIXER_VOL:
-// printf("ao_jack_set_property(AO_PROP_MIXER_VOL): %u\n", value);
- this->volume = value;
- break;
- case AO_PROP_MUTE_VOL:
-// printf("ao_jack_get_property(AO_PROP_MUTE_VOL): %u\n", value);
- this->mute = value;
- break;
- }
+static int ao_jack_get_property (ao_driver_t *this_gen, int property)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+
+ switch (property) {
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+ return this->mixer.volume;
+ break;
+ case AO_PROP_MUTE_VOL:
+ return this->mixer.mute;
+ break;
+ }
+
+ return 0;
+}
- return ~value;
+static int ao_jack_set_property (ao_driver_t *this_gen, int property, int value)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
+
+ switch (property) {
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+ this->mixer.volume = value;
+ return value;
+ break;
+ case AO_PROP_MUTE_VOL:
+ this->mixer.mute = value;
+ return value;
+ break;
+ }
+
+ return -1;
}
-static int ao_jack_ctrl(ao_driver_t *this_gen, int cmd, ...) {
- jack_driver_t *this = (jack_driver_t *) this_gen;
+static int ao_jack_ctrl (ao_driver_t *this_gen, int cmd, ...)
+{
+ jack_driver_t *this = (jack_driver_t *) this_gen;
- switch (cmd) {
+ switch (cmd) {
- case AO_CTRL_PLAY_PAUSE:
- break;
+ case AO_CTRL_PLAY_PAUSE:
+ this->paused = 1;
+ break;
- case AO_CTRL_PLAY_RESUME:
- break;
+ case AO_CTRL_PLAY_RESUME:
+ this->paused = 0;
+ break;
- case AO_CTRL_FLUSH_BUFFERS:
-// fprintf(stderr, "ao_jack_ctrl(AO_CTRL_FLUSH_BUFFERS)\n");
- this->buf_write = this->buf_read = 0;
- break;
- }
+ case AO_CTRL_FLUSH_BUFFERS:
+ jack_reset (this);
+ break;
+ }
- return 0;
+ return 0;
}
+static void jack_speaker_arrangement_cb (void *user_data,
+ xine_cfg_entry_t *entry);
+
static ao_driver_t *open_jack_plugin (audio_driver_class_t *class_gen,
const void *data)
{
- jack_class_t *class = (jack_class_t *) class_gen;
- jack_driver_t *this;
- jack_client_t *client;
- uint32_t rate;
- const char **port_names;
- int i;
-
- if ((client = jack_client_new("xine")) == 0) {
-
- char name[20];
- sprintf(name, "xine (%d)", (int)getpid());
-
- if ((client = jack_client_new(name)) == 0) {
- fprintf(stderr, "\nopen_jack_plugin: Error: Failed to connect to JACK server\n");
- fprintf(stderr, "open_jack_plugin: (did you start 'jackd' server?)\n");
- return 0;
- }
- }
-
- this = (jack_driver_t *) xine_xmalloc (sizeof (jack_driver_t));
-
- this->client = client;
-
- jack_set_process_callback(client, jack_process, this);
- jack_on_shutdown(client, jack_shutdown, this);
-
- rate = jack_get_sample_rate(client);
- fprintf(stderr, "open_jack_plugin: JACK sample rate is %u\n", rate);
-
- // We support up to 2-channel output
-
- for (i = 0; i < 2; ++i) {
- jack_port_t *port = jack_port_register
- (client, (i ? "out_r" : "out_l"),
- JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
- if (!port) {
- fprintf(stderr, "ao_jack_open: failed to register port %u\n", i);
- }
- if (i) this->port_2 = port;
- else this->port_1 = port;
- }
-
- this->buf_read = this->buf_write = 0;
- this->volume = 100;
- this->mute = 0;
-
- if (jack_activate(client)) {
- fprintf(stderr, "ao_jack_open: failed to activate JACK client\n");
- return 0;
- }
-
- if ((port_names = jack_get_ports(client, NULL, NULL,
- JackPortIsPhysical | JackPortIsInput)) != NULL) {
- if (port_names[0]) {
- jack_connect(client, jack_port_name(this->port_1), port_names[0]);
- }
- if (port_names[1] && this->port_2) {
- jack_connect(client, jack_port_name(this->port_2), port_names[1]);
- }
- free(port_names);
+ jack_class_t *class = (jack_class_t *) class_gen;
+ config_values_t *config = class->config;
+ jack_driver_t *this;
+
+ jack_client_t *client;
+ uint32_t rate;
+ char *jack_device;
+ const char **matching_ports = NULL;
+ const char **port_names;
+
+ /* for usability reasons, keep this in sync with audio_oss_out.c */
+ static char *speaker_arrangement[] = {
+ "Mono 1.0", "Stereo 2.0", "Headphones 2.0", "Stereo 2.1",
+ "Surround 3.0", "Surround 4.0", "Surround 4.1", "Surround 5.0",
+ "Surround 5.1", "Surround 6.0",
+ "Surround 6.1", "Surround 7.1", "Pass Through", NULL
+ };
+#define MONO 0
+#define STEREO 1
+#define HEADPHONES 2
+#define SURROUND21 3
+#define SURROUND3 4
+#define SURROUND4 5
+#define SURROUND41 6
+#define SURROUND5 7
+#define SURROUND51 8
+#define SURROUND6 9
+#define SURROUND61 10
+#define SURROUND71 11
+#define A52_PASSTHRU 12
+ int speakers;
+
+ /* Try to create a client called "xine" */
+ if ((client = jack_client_new ("xine")) == 0) {
+ /* If that doesn't work it could be because running two copies of xine - try using a unique name */
+ char name[20];
+ sprintf (name, "xine (%d)", (int) getpid ());
+
+ if ((client = jack_client_new (name)) == 0) {
+ xprintf (class->xine, XINE_VERBOSITY_LOG,
+ "\nopen_jack_plugin: Error: Failed to connect to JACK server\n");
+ xprintf (class->xine, XINE_VERBOSITY_LOG,
+ "open_jack_plugin: (did you start 'jackd' server?)\n");
+ return 0;
}
+ }
+
+ this = calloc(1, sizeof (jack_driver_t));
+
+ rate = jack_get_sample_rate (client);
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG,
+ "open_jack_plugin: JACK sample rate is %u\n", rate);
+
+ /* devname_val is offset used to select auto, /dev/dsp, or /dev/sound/dsp */
+ jack_device = config->register_string (config,
+ "audio.device.jack_device_name",
+ "",
+ _("JACK audio device name"),
+ _("Specifies the jack audio device name, "
+ "leave blank for the default physical output port."),
+ 10, NULL, NULL);
+
+ this->capabilities = 0;
+
+
+ /* for usability reasons, keep this in sync with audio_alsa_out.c */
+ speakers =
+ config->register_enum (config, "audio.output.speaker_arrangement",
+ STEREO, speaker_arrangement,
+ _("speaker arrangement"),
+ _("Select how your speakers are arranged, "
+ "this determines which speakers xine uses for sound output. "
+ "The individual values are:\n\n"
+ "Mono 1.0: You have only one speaker.\n"
+ "Stereo 2.0: You have two speakers for left and right channel.\n"
+ "Headphones 2.0: You use headphones.\n"
+ "Stereo 2.1: You have two speakers for left and right channel, and one "
+ "subwoofer for the low frequencies.\n"
+ "Surround 3.0: You have three speakers for left, right and rear channel.\n"
+ "Surround 4.0: You have four speakers for front left and right and rear "
+ "left and right channels.\n"
+ "Surround 4.1: You have four speakers for front left and right and rear "
+ "left and right channels, and one subwoofer for the low frequencies.\n"
+ "Surround 5.0: You have five speakers for front left, center and right and "
+ "rear left and right channels.\n"
+ "Surround 5.1: You have five speakers for front left, center and right and "
+ "rear left and right channels, and one subwoofer for the low frequencies.\n"
+ "Surround 6.0: You have six speakers for front left, center and right and "
+ "rear left, center and right channels.\n"
+ "Surround 6.1: You have six speakers for front left, center and right and "
+ "rear left, center and right channels, and one subwoofer for the low frequencies.\n"
+ "Surround 7.1: You have seven speakers for front left, center and right, "
+ "left and right and rear left and right channels, and one subwoofer for the "
+ "low frequencies.\n"
+ "Pass Through: Your sound system will receive undecoded digital sound from xine. "
+ "You need to connect a digital surround decoder capable of decoding the "
+ "formats you want to play to your sound card's digital output."),
+ 0, jack_speaker_arrangement_cb, this);
+
+ int port_flags = JackPortIsInput;
+ int num_ports;
+ /* list matching ports */
+ if (!jack_device)
+ port_flags |= JackPortIsPhysical;
+ /* Find all the ports matching either the desired device regexp or physical output ports */
+ matching_ports = jack_get_ports (client, jack_device, NULL, port_flags);
+ /* Count 'em */
+ for (num_ports = 0; matching_ports && matching_ports[num_ports];
+ num_ports++)
+ /**/;
+ if (!num_ports) {
+ xprintf (this->xine, XINE_VERBOSITY_LOG,
+ "open_jack_plugin: no physical ports available\n");
+ goto err_out;
+ }
+
+
+/* TODO: We deliberately don't offer mono, let Xine upsample instead? */
+/* if (num_ports >= 1) { */
+/* this->capabilities |= AO_CAP_MODE_MONO; */
+/* xprintf(class->xine, XINE_VERBOSITY_DEBUG, "mono "); */
+/* } */
+
+ if (num_ports >= 2) {
+ this->capabilities |= AO_CAP_MODE_STEREO;
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG, "stereo ");
+ }
+
+ if (num_ports >= 4) {
+ if (speakers == SURROUND4) {
+ this->capabilities |= AO_CAP_MODE_4CHANNEL;
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG, "4-channel ");
+ } else
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG,
+ "(4-channel not enabled in xine config) ");
+ }
+
+ if (num_ports >= 5) {
+ if (speakers == SURROUND5) {
+ this->capabilities |= AO_CAP_MODE_5CHANNEL;
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG, "5-channel ");
+ } else
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG,
+ "(5-channel not enabled in xine config) ");
+ }
+
+ if (num_ports >= 6) {
+ if (speakers == SURROUND51) {
+ this->capabilities |= AO_CAP_MODE_5_1CHANNEL;
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG, "5.1-channel ");
+ } else
+ xprintf (class->xine, XINE_VERBOSITY_DEBUG,
+ "(5.1-channel not enabled in xine config) ");
+ }
+
+ this->buffer = (unsigned char *) malloc (BUFFSIZE);
+ jack_reset (this);
+
+ this->capabilities |= AO_CAP_MIXER_VOL;
+ this->capabilities |= AO_CAP_MUTE_VOL;
+ /* TODO: Currently not respected by Xine, perhaps v1.2? */
+ this->capabilities |= AO_CAP_FLOAT32;
+
+
+ this->mixer.mute = 0;
+ this->mixer.volume = 100;
+
+ this->output_sample_rate = jack_get_sample_rate (client);
+ this->fragment_size = jack_get_buffer_size (client);
+
+ /* Close our JACK client */
+ jack_client_close (client);
+
+ this->xine = class->xine;
+
+ this->ao_driver.get_capabilities = ao_jack_get_capabilities;
+ this->ao_driver.get_property = ao_jack_get_property;
+ this->ao_driver.set_property = ao_jack_set_property;
+ this->ao_driver.open = ao_jack_open;
+ this->ao_driver.num_channels = ao_jack_num_channels;
+ this->ao_driver.bytes_per_frame = ao_jack_bytes_per_frame;
+ this->ao_driver.delay = ao_jack_delay;
+ this->ao_driver.write = ao_jack_write;
+ this->ao_driver.close = ao_jack_close;
+ this->ao_driver.exit = ao_jack_exit;
+ this->ao_driver.get_gap_tolerance = ao_jack_get_gap_tolerance;
+ this->ao_driver.control = ao_jack_ctrl;
+
+ return &this->ao_driver;
+
+err_out:
+ free (matching_ports);
+ if (client)
+ jack_client_close (client);
+ return 0;
+}
- this->sample_rate = rate;
-
- this->xine = class->xine;
- this->capabilities = AO_CAP_FLOAT32 | AO_CAP_MODE_MONO |
- AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_MUTE_VOL;
-
- this->ao_driver.get_capabilities = ao_jack_get_capabilities;
- this->ao_driver.get_property = ao_jack_get_property;
- this->ao_driver.set_property = ao_jack_set_property;
- this->ao_driver.open = ao_jack_open;
- this->ao_driver.num_channels = ao_jack_num_channels;
- this->ao_driver.bytes_per_frame = ao_jack_bytes_per_frame;
- this->ao_driver.delay = ao_jack_delay;
- this->ao_driver.write = ao_jack_write;
- this->ao_driver.close = ao_jack_close;
- this->ao_driver.exit = ao_jack_exit;
- this->ao_driver.get_gap_tolerance = ao_jack_get_gap_tolerance;
- this->ao_driver.control = ao_jack_ctrl;
-
- fprintf(stderr, "jack open_jack_plugin returning %p\n", (void *)(&this->ao_driver));
- return &this->ao_driver;
+static void jack_speaker_arrangement_cb (void *user_data,
+ xine_cfg_entry_t *entry)
+{
+ jack_driver_t *this = (jack_driver_t *) user_data;
+ int32_t value = entry->num_value;
+ if (value == SURROUND4) {
+ this->capabilities |= AO_CAP_MODE_4CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_4CHANNEL;
+ }
+ if (value == SURROUND41) {
+ this->capabilities |= AO_CAP_MODE_4_1CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_4_1CHANNEL;
+ }
+ if (value == SURROUND5) {
+ this->capabilities |= AO_CAP_MODE_5CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_5CHANNEL;
+ }
+ if (value >= SURROUND51) {
+ this->capabilities |= AO_CAP_MODE_5_1CHANNEL;
+ } else {
+ this->capabilities &= ~AO_CAP_MODE_5_1CHANNEL;
+ }
}
/*
* class functions
*/
-static char* get_identifier (audio_driver_class_t *this_gen) {
- return "jack";
+static char *get_identifier (audio_driver_class_t *this_gen)
+{
+ return "jack";
}
-static char* get_description (audio_driver_class_t *this_gen) {
- return _("xine output plugin for JACK Audio Connection Kit");
+static char *get_description (audio_driver_class_t *this_gen)
+{
+ return _("xine output plugin for JACK Audio Connection Kit");
}
-static void dispose_class (audio_driver_class_t *this_gen) {
-
- jack_class_t *this = (jack_class_t *) this_gen;
- free (this);
+static void dispose_class (audio_driver_class_t *this_gen)
+{
+ jack_class_t *this = (jack_class_t *) this_gen;
+ free (this);
}
-static void *init_class (xine_t *xine, void *data) {
-
- jack_class_t *this;
-
- this = (jack_class_t *) xine_xmalloc (sizeof (jack_class_t));
+static void *init_class (xine_t *xine, void *data)
+{
+ jack_class_t *this;
- this->driver_class.open_plugin = open_jack_plugin;
- this->driver_class.get_identifier = get_identifier;
- this->driver_class.get_description = get_description;
- this->driver_class.dispose = dispose_class;
+ this = calloc(1, sizeof (jack_class_t));
- this->config = xine->config;
- this->xine = xine;
+ this->driver_class.open_plugin = open_jack_plugin;
+ this->driver_class.get_identifier = get_identifier;
+ this->driver_class.get_description = get_description;
+ this->driver_class.dispose = dispose_class;
- fprintf(stderr, "jack init_class returning %p\n", (void *)this);
+ this->config = xine->config;
+ this->xine = xine;
- return this;
+ return this;
}
-static ao_info_t ao_info_jack = {
- 6
-};
+static ao_info_t ao_info_jack = { 6 };
/*
* exported plugin catalog entry
*/
const plugin_info_t xine_plugin_info[] EXPORTED = {
- /* type, API, "name", version, special_info, init_function */
- { PLUGIN_AUDIO_OUT, AO_OUT_JACK_IFACE_VERSION, "jack", XINE_VERSION_CODE /* XINE_VERSION_CODE */, &ao_info_jack, init_class },
- { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_AUDIO_OUT, AO_OUT_JACK_IFACE_VERSION, "jack", XINE_VERSION_CODE,
+ &ao_info_jack, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
-
diff --git a/src/audio_out/audio_none_out.c b/src/audio_out/audio_none_out.c
index a815d645a..9ba9a00ca 100644
--- a/src/audio_out/audio_none_out.c
+++ b/src/audio_out/audio_none_out.c
@@ -186,7 +186,7 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen,
lprintf ("open_plugin called\n");
- this = (none_driver_t *) xine_xmalloc (sizeof (none_driver_t));
+ this = calloc(1, sizeof (none_driver_t));
if (!this)
return NULL;
@@ -236,7 +236,7 @@ static void *init_class (xine_t *xine, void *data) {
lprintf ("init class\n");
- this = (none_class_t *) xine_xmalloc (sizeof (none_class_t));
+ this = calloc(1, sizeof (none_class_t));
if (!this)
return NULL;
diff --git a/src/audio_out/audio_oss_out.c b/src/audio_out/audio_oss_out.c
index a6e0fe494..ba1044ed4 100644
--- a/src/audio_out/audio_oss_out.c
+++ b/src/audio_out/audio_oss_out.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2000-2003 the xine project
+ * Copyright (C) 2000-2008 the xine project
*
* This file is part of xine, a free video player.
*
@@ -405,6 +405,18 @@ static int ao_oss_delay(ao_driver_t *this_gen) {
if (bytes_left<=0) /* buffer ran dry */
bytes_left = 0;
break;
+ case OSS_SYNC_GETODELAY:
+#ifdef SNDCTL_DSP_GETODELAY
+ if (ioctl (this->audio_fd, SNDCTL_DSP_GETODELAY, &bytes_left)) {
+ perror ("audio_oss_out: DSP_GETODELAY ioctl():");
+ }
+ if (bytes_left<0)
+ bytes_left = 0;
+
+ lprintf ("%d bytes left\n", bytes_left);
+
+ break;
+#endif
case OSS_SYNC_GETOPTR:
if (ioctl (this->audio_fd, SNDCTL_DSP_GETOPTR, &info)) {
perror ("audio_oss_out: SNDCTL_DSP_GETOPTR failed:");
@@ -424,16 +436,6 @@ static int ao_oss_delay(ao_driver_t *this_gen) {
}
this->last_getoptr = info.bytes;
break;
- case OSS_SYNC_GETODELAY:
- if (ioctl (this->audio_fd, SNDCTL_DSP_GETODELAY, &bytes_left)) {
- perror ("audio_oss_out: DSP_GETODELAY ioctl():");
- }
- if (bytes_left<0)
- bytes_left = 0;
-
- lprintf ("%d bytes left\n", bytes_left);
-
- break;
}
return bytes_left / this->bytes_per_frame;
@@ -727,7 +729,7 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
int speakers;
- this = (oss_driver_t *) xine_xmalloc (sizeof (oss_driver_t));
+ this = calloc(1, sizeof (oss_driver_t));
if (!this)
return NULL;
@@ -840,10 +842,13 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
* check if SNDCTL_DSP_GETODELAY works. if so, using it is preferred.
*/
+#ifdef SNDCTL_DSP_GETODELAY
if (ioctl(audio_fd, SNDCTL_DSP_GETODELAY, &info) != -1) {
xprintf(class->xine, XINE_VERBOSITY_DEBUG, "audio_oss_out: using SNDCTL_DSP_GETODELAY\n");
this->sync_method = OSS_SYNC_GETODELAY;
- } else if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &info) != -1) {
+ } else
+#endif
+ if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &info) != -1) {
xprintf(class->xine, XINE_VERBOSITY_DEBUG, "audio_oss_out: using SNDCTL_DSP_GETOPTR\n");
this->sync_method = OSS_SYNC_GETOPTR;
} else {
@@ -872,15 +877,10 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
if (this->sync_method == OSS_SYNC_PROBEBUFFER) {
char *buf;
int c;
-
- xprintf(class->xine, XINE_VERBOSITY_LOG,
- _("audio_oss_out: Audio driver realtime sync disabled...\n"
- "audio_oss_out: ...probing output buffer size: "));
+
this->buffer_size = 0;
- if( (buf=malloc(1024)) != NULL ) {
- memset(buf,0,1024);
-
+ if( (buf=calloc(1, 1024)) != NULL ) {
do {
c = write(audio_fd,buf,1024);
if( c != -1 )
@@ -891,7 +891,8 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
}
close(audio_fd);
xprintf(class->xine, XINE_VERBOSITY_LOG,
- _("%d bytes\naudio_oss_out: ...there may be audio/video synchronization issues\n"), this->buffer_size);
+ _("audio_oss_out: Audio driver realtime sync disabled...\n"
+ "audio_oss_out: ...probing output buffer size: %d bytes\naudio_oss_out: ...there may be audio/video synchronization issues\n"), this->buffer_size);
audio_fd=open(this->audio_dev, O_WRONLY|O_NONBLOCK);
@@ -954,48 +955,48 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
0, oss_speaker_arrangement_cb, this);
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "audio_oss_out: supported modes are ");
+ char *logmsg = strdup (_("audio_oss_out: supported modes are"));
num_channels = 1;
status = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &num_channels);
if ( (status != -1) && (num_channels==1) ) {
this->capabilities |= AO_CAP_MODE_MONO;
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "mono ");
+ xine_strcat_realloc (&logmsg, _(" mono"));
}
num_channels = 2;
status = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &num_channels);
if ( (status != -1) && (num_channels==2) ) {
this->capabilities |= AO_CAP_MODE_STEREO;
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "stereo ");
+ xine_strcat_realloc (&logmsg, _(" stereo"));
}
num_channels = 4;
status = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &num_channels);
if ( (status != -1) && (num_channels==4) ) {
if ( speakers == SURROUND4 ) {
this->capabilities |= AO_CAP_MODE_4CHANNEL;
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "4-channel ");
+ xine_strcat_realloc (&logmsg, _(" 4-channel"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "(4-channel not enabled in xine config) " );
+ xine_strcat_realloc (&logmsg, _(" (4-channel not enabled in xine config)"));
}
num_channels = 5;
status = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &num_channels);
if ( (status != -1) && (num_channels==5) ) {
if ( speakers == SURROUND5 ) {
this->capabilities |= AO_CAP_MODE_5CHANNEL;
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "5-channel ");
+ xine_strcat_realloc (&logmsg, _(" 5-channel"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "(5-channel not enabled in xine config) " );
+ xine_strcat_realloc (&logmsg, _(" (5-channel not enabled in xine config)"));
}
num_channels = 6;
status = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &num_channels);
if ( (status != -1) && (num_channels==6) ) {
if ( speakers == SURROUND51 ) {
this->capabilities |= AO_CAP_MODE_5_1CHANNEL;
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "5.1-channel ");
+ xine_strcat_realloc (&logmsg, _(" 5.1-channel"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "(5.1-channel not enabled in xine config) " );
+ xine_strcat_realloc (&logmsg, _(" (5.1-channel not enabled in xine config)"));
}
ioctl(audio_fd,SNDCTL_DSP_GETFMTS,&caps);
@@ -1005,10 +1006,13 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
if ( speakers == A52_PASSTHRU ) {
this->capabilities |= AO_CAP_MODE_A52;
this->capabilities |= AO_CAP_MODE_AC5;
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "a/52-pass-through ");
+ xine_strcat_realloc (&logmsg, _(" a/52 pass-through"));
}
else
- xprintf(class->xine, XINE_VERBOSITY_DEBUG, "(a/52-pass-through not enabled in xine config)");
+ xine_strcat_realloc (&logmsg, _(" (a/52 pass-through not enabled in xine config)"));
+
+ xprintf(class->xine, XINE_VERBOSITY_DEBUG, "%s\n", logmsg);
+ free (logmsg);
/*
* mixer initialisation.
@@ -1035,20 +1039,17 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
if ((parse = strstr(mixer_name, "dsp"))) {
parse[0] = '\0';
parse += 3;
- this->mixer.name = (char *)malloc(strlen(mixer_name) + sizeof("mixer") + 2);
if (devname_val == 0)
- sprintf(this->mixer.name, "%smixer%s", mixer_name, parse);
+ asprintf(&(this->mixer.name), "%smixer%s", mixer_name, parse);
else {
if (mixer_num == -1)
- sprintf(this->mixer.name, "%smixer", mixer_name);
+ asprintf(&(this->mixer.name), "%smixer", mixer_name);
else
- sprintf(this->mixer.name, "%smixer%d", mixer_name, mixer_num);
+ asprintf(&(this->mixer.name), "%smixer%d", mixer_name, mixer_num);
}
} else {
- this->mixer.name = (char *)malloc(1);
- this->mixer.name[0] = '\0';
+ _x_abort();
}
- _x_assert(this->mixer.name[0] != '\0');
this->mixer.fd = open(this->mixer.name, O_RDONLY);
@@ -1165,7 +1166,7 @@ static void *init_class (xine_t *xine, void *data) {
oss_class_t *this;
- this = (oss_class_t *) xine_xmalloc (sizeof (oss_class_t));
+ this = calloc(1, sizeof (oss_class_t));
if (!this)
return NULL;
diff --git a/src/audio_out/audio_pulse_out.c b/src/audio_out/audio_pulse_out.c
index 26d121bf6..4b66fbaed 100644
--- a/src/audio_out/audio_pulse_out.c
+++ b/src/audio_out/audio_pulse_out.c
@@ -1,28 +1,28 @@
-/*
- * Copyright (C) 2000-2007 the xine project
- *
+/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
+
+/*
+ * Copyright (C) 2000-2008 the xine project
+ *
* This file is part of xine, a free 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
*
- * ao plugin for pulseaudio (rename of polypaudio):
+ * ao plugin for PulseAudio:
* http://0pointer.de/lennart/projects/pulsaudio/
*
- * originally written for polypaudio simple api. Lennart then suggested
- * using the async api for better control (such as volume), therefore, a lot
- * of this code comes from Lennart's patch to mplayer.
+ * Diego Petteno, Lennart Poettering
*/
#ifdef HAVE_CONFIG_H
@@ -48,15 +48,9 @@
#define GAP_TOLERANCE AO_MAX_GAP
-/* CHECKME: should this be conditional on autotools? */
-extern const char *__progname;
-
typedef struct {
audio_driver_class_t driver_class;
xine_t *xine;
-
- struct pa_context *context; /*< Pulseaudio connection context */
- struct pa_threaded_mainloop *mainloop; /*< Main event loop object */
} pulse_class_t;
typedef struct pulse_driver_s {
@@ -67,11 +61,13 @@ typedef struct pulse_driver_s {
char *host; /*< The host to connect to */
char *sink; /*< The sink to connect to */
- struct pa_stream *stream; /*< Pulseaudio playback stream object */
- pthread_mutex_t info_mutex; /**< Mutex for info callback signaling */
+ pa_threaded_mainloop *mainloop; /*< Main event loop object */
+ pa_context *context; /*< Pulseaudio connection context */
+ pa_stream *stream; /*< Pulseaudio playback stream object */
pa_volume_t swvolume;
+ int muted;
pa_cvolume cvolume;
int capabilities;
@@ -82,67 +78,117 @@ typedef struct pulse_driver_s {
uint32_t bits_per_sample;
uint32_t bytes_per_frame;
- uint32_t frames_written;
-
} pulse_driver_t;
/**
- * @brief Callback function called when a stream operation succeed
- * @param stream Stream which operation has succeeded
- * @param success The success value for the operation (ignored)
- * @param this_Gen pulse_driver_t pointer for the PulseAudio output
- * instance.
+ * @brief Callback function called when the state of the context is changed
+ * @param c Context which changed status
+ * @param this_gen pulse_class_t pointer for the PulseAudio output class
*/
-static void __xine_pa_stream_success_callback(pa_stream *const stream, const int success,
- void *const mutex_gen)
+static void __xine_pa_context_state_callback(pa_context *c, void *this_gen)
{
- pthread_mutex_t *const completion_mutex = (pthread_mutex_t*)mutex_gen;
+ pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+ switch (pa_context_get_state(c)) {
- pthread_mutex_unlock(completion_mutex);
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(this->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ }
}
/**
- * @brief Callback function called when the state of the context is changed
- * @param ctx Context which operation has succeeded
+ * @brief Callback function called when the state of the stream is changed
+ * @param s Stream that changed status
* @param this_gen pulse_driver_t pointer for the PulseAudio output
* instance.
*/
-static void __xine_pa_context_status_callback(pa_context *const ctx, void *const this_gen)
+static void __xine_pa_stream_state_callback(pa_stream *s, void *this_gen)
{
- pulse_driver_t *const this = (pulse_driver_t*)this_gen;
-
- switch (pa_context_get_state(ctx)) {
- case PA_CONTEXT_READY:
- case PA_CONTEXT_TERMINATED:
- case PA_CONTEXT_FAILED:
- pa_threaded_mainloop_signal(this->pa_class->mainloop, 0);
- break;
-
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_UNCONNECTED:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
+ pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+ switch (pa_stream_get_state(s)) {
+
+ case PA_STREAM_READY:
+ case PA_STREAM_TERMINATED:
+ case PA_STREAM_FAILED:
+ pa_threaded_mainloop_signal(this->mainloop, 0);
+ break;
+
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
}
}
/**
- * @brief Callback function called when a context operation succeed
+ * @brief Callback function called when PA asks for more audio data.
+ * @param s Stream on which data is requested
+ * @param nbytes the number of bytes PA requested
+ * @param this_gen pulse_driver_t pointer for the PulseAudio output
+ * instance.
+ */
+static void __xine_pa_stream_request_callback(pa_stream *s, size_t nbytes, void *this_gen)
+{
+ pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+ pa_threaded_mainloop_signal(this->mainloop, 0);
+}
+
+/**
+ * @brief Callback function called when PA notifies about something
+ * @param s Stream on which the notification happened
+ * @param this_gen pulse_driver_t pointer for the PulseAudio output
+ * instance.
+ */
+static void __xine_pa_stream_notify_callback(pa_stream *s, void *this_gen)
+{
+ pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+ pa_threaded_mainloop_signal(this->mainloop, 0);
+}
+
+/**
+ * @brief Callback function called when PA completed an operation
* @param ctx Context which operation has succeeded
- * @param success The success value for the operation (ignored)
+ * @param nbytes the number of bytes PA requested
+ * @param this_gen pulse_driver_t pointer for the PulseAudio output
+ * instance.
+ */
+static void __xine_pa_stream_success_callback(pa_stream *s, int success, void *this_gen)
+{
+ pulse_driver_t * this = (pulse_driver_t*) this_gen;
+
+ if (!success)
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: stream operation failed: %s\n", pa_strerror(pa_context_errno(this->context)));
+
+ pa_threaded_mainloop_signal(this->mainloop, 0);
+}
+
+/**
+ * @brief Callback function called when PA completed an operation
+ * @param c Context on which operation has succeeded
+ * @param nbytes the number of bytes PA requested
* @param this_gen pulse_driver_t pointer for the PulseAudio output
* instance.
*/
-static void __xine_pa_context_success_callback(pa_context *const ctx, const int success,
- void *const this_gen)
+static void __xine_pa_context_success_callback(pa_context *c, int success, void *this_gen)
{
- pulse_driver_t *const this = (pulse_driver_t*)this_gen;
+ pulse_driver_t *this = (pulse_driver_t*) this_gen;
- _x_assert(ctx); _x_assert(this);
- _x_assert(ctx == this->pa_class->context);
+ if (!success)
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: context operation failed: %s\n", pa_strerror(pa_context_errno(this->context)));
- pa_threaded_mainloop_signal(this->pa_class->mainloop, 0);
+ pa_threaded_mainloop_signal(this->mainloop, 0);
}
/**
@@ -156,14 +202,14 @@ static void __xine_pa_context_success_callback(pa_context *const ctx, const int
* This function saves the volume field of the passed structure to the
* @c cvolume variable of the output instance.
*/
-static void __xine_pa_sink_info_callback(pa_context *const ctx, const pa_sink_input_info *const info,
- const int is_last, void *const userdata) {
+static void __xine_pa_sink_info_callback(pa_context *c, const pa_sink_input_info *info,
+ int is_last, void *userdata) {
pulse_driver_t *const this = (pulse_driver_t *) userdata;
if (is_last < 0) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to get sink input info: %s\n",
- pa_strerror(pa_context_errno(this->pa_class->context)));
+ pa_strerror(pa_context_errno(this->context)));
return;
}
@@ -171,36 +217,95 @@ static void __xine_pa_sink_info_callback(pa_context *const ctx, const pa_sink_in
return;
this->cvolume = info->volume;
+ this->swvolume = pa_cvolume_avg(&info->volume);
+#if PA_PROTOCOL_VERSION >= 11
+ /* PulseAudio 0.9.7 and newer */
+ this->muted = info->mute;
+#else
+ this->muted = pa_cvolume_is_muted (&this->cvolume);
+#endif
+}
+
+static int connect_context(pulse_driver_t *this) {
+
+ if (this->context && (pa_context_get_state(this->context) == PA_CONTEXT_FAILED ||
+ pa_context_get_state(this->context) == PA_CONTEXT_TERMINATED)) {
+ pa_context_unref(this->context);
+ this->context = NULL;
+ }
+
+ if (!this->context) {
+ char fn[PATH_MAX], *p;
+
+ if (pa_get_binary_name(fn, sizeof(fn)))
+ p = pa_path_get_filename(fn);
+ else
+ p = "Xine";
- pthread_mutex_unlock(&this->info_mutex);
+ this->context = pa_context_new(pa_threaded_mainloop_get_api(this->mainloop), p);
+ _x_assert(this->context);
+
+ pa_context_set_state_callback(this->context, __xine_pa_context_state_callback, this);
+ }
+
+ if (pa_context_get_state(this->context) == PA_CONTEXT_UNCONNECTED) {
+
+ if (pa_context_connect(this->context, this->host, 0, NULL) < 0) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: failed to connect context object %s\n", pa_strerror(pa_context_errno(this->context)));
+ return -1;
+ }
+ }
+
+ for (;;) {
+ pa_context_state_t state = pa_context_get_state(this->context);
+
+ if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: failed to connect context object: %s\n", pa_strerror(pa_context_errno(this->context)));
+ return -1;
+ }
+
+ if (state == PA_CONTEXT_READY)
+ break;
+
+ pa_threaded_mainloop_wait(this->mainloop);
+ }
+
+ return 0;
}
/*
* open the audio device for writing to
*/
static int ao_pulse_open(ao_driver_t *this_gen,
- uint32_t bits, uint32_t rate, int mode)
+ uint32_t bits, uint32_t rate, int mode)
{
pulse_driver_t *this = (pulse_driver_t *) this_gen;
- struct pa_sample_spec ss;
- struct pa_buffer_attr a;
- pa_stream_state_t streamstate;
+ pa_sample_spec ss;
+ pa_channel_map cm;
+ int r;
xprintf (this->xine, XINE_VERBOSITY_DEBUG,
- "audio_pulse_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
+ "audio_pulse_out: ao_open bits=%d rate=%d, mode=%d\n", bits, rate, mode);
if ( (mode & this->capabilities) == 0 ) {
xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: unsupported mode %08x\n", mode);
return 0;
}
+ pa_threaded_mainloop_lock(this->mainloop);
+
if (this->stream) {
- if ( mode == this->mode && rate == this->sample_rate &&
- bits == this->bits_per_sample )
+ if (mode == this->mode && rate == this->sample_rate &&
+ bits == this->bits_per_sample) {
+
+ pa_threaded_mainloop_unlock(this->mainloop);
return this->sample_rate;
+ }
- this_gen->close(this_gen);
+ pa_stream_disconnect(this->stream);
+ pa_stream_unref(this->stream);
+ this->stream = NULL;
}
this->mode = mode;
@@ -221,6 +326,8 @@ static int ao_pulse_open(ao_driver_t *this_gen,
case 32:
ss.format = PA_SAMPLE_FLOAT32NE;
break;
+ default:
+ _x_assert(!"Should not be reached");
}
if (!pa_sample_spec_valid(&ss)) {
@@ -228,69 +335,86 @@ static int ao_pulse_open(ao_driver_t *this_gen,
goto fail;
}
- if ( this->pa_class->context && pa_context_get_state(this->pa_class->context) > PA_CONTEXT_READY ) {
- pa_context_unref(this->pa_class->context);
- this->pa_class->context = NULL;
- }
-
- if ( this->pa_class->context == NULL ) {
- this->pa_class->context = pa_context_new(pa_threaded_mainloop_get_api(this->pa_class->mainloop),
- __progname);
- }
-
- pa_context_ref(this->pa_class->context);
+ cm.channels = ss.channels;
- if ( pa_context_get_state(this->pa_class->context) == PA_CONTEXT_UNCONNECTED ) {
- int ret;
+ switch (mode) {
+ case AO_CAP_MODE_MONO:
+ cm.map[0] = PA_CHANNEL_POSITION_MONO;
+ _x_assert(cm.channels == 1);
+ break;
- pa_threaded_mainloop_lock(this->pa_class->mainloop);
- ret = pa_context_connect(this->pa_class->context, this->host, 1, NULL);
- if ( ret < 0 )
- goto fail;
+ case AO_CAP_MODE_STEREO:
+ cm.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ cm.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ _x_assert(cm.channels == 2);
+ break;
- pa_context_set_state_callback(this->pa_class->context, __xine_pa_context_status_callback, this);
+ case AO_CAP_MODE_4CHANNEL:
+ cm.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ cm.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ cm.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ cm.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ _x_assert(cm.channels == 4);
+ break;
- pa_threaded_mainloop_wait(this->pa_class->mainloop);
- pa_threaded_mainloop_unlock(this->pa_class->mainloop);
+ case AO_CAP_MODE_4_1CHANNEL:
+ case AO_CAP_MODE_5CHANNEL:
+ case AO_CAP_MODE_5_1CHANNEL:
+ cm.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
+ cm.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
+ cm.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
+ cm.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
+ cm.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
+ cm.map[5] = PA_CHANNEL_POSITION_LFE;
+ cm.channels = 6;
+ break;
+ default:
+ _x_assert(!"Should not be reached");
}
- if (pa_context_get_state(this->pa_class->context) != PA_CONTEXT_READY) {
- xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Failed to connect to server: %s\n",
- pa_strerror(pa_context_errno(this->pa_class->context)));
+ if (!pa_channel_map_valid(&cm)) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: Invalid channel map\n");
goto fail;
}
- this->stream = pa_stream_new(this->pa_class->context, "audio stream", &ss, NULL);
+ if (connect_context(this) < 0)
+ goto fail;
+
+ _x_assert(!this->stream);
+ this->stream = pa_stream_new(this->context, "Audio Stream", &ss, &cm);
_x_assert(this->stream);
- a.maxlength = pa_bytes_per_second(&ss)*1;
- a.tlength = a.maxlength*9/10;
- a.prebuf = a.tlength/2;
- a.minreq = a.tlength/10;
+ pa_stream_set_state_callback(this->stream, __xine_pa_stream_state_callback, this);
+ pa_stream_set_write_callback(this->stream, __xine_pa_stream_request_callback, this);
+ pa_stream_set_latency_update_callback(this->stream, __xine_pa_stream_notify_callback, this);
- pa_stream_connect_playback(this->stream, this->sink, &a,
- PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE,
+ r = pa_stream_connect_playback(this->stream, this->sink, NULL,
+ PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE,
NULL, NULL);
- do {
- xine_usec_sleep (100);
+ for (;;) {
+ pa_context_state_t cstate = pa_context_get_state(this->context);
+ pa_stream_state_t sstate = pa_stream_get_state(this->stream);
- streamstate = pa_stream_get_state(this->stream);
- } while (streamstate < PA_STREAM_READY);
-
- if (streamstate != PA_STREAM_READY) {
- xprintf (this->xine, XINE_VERBOSITY_LOG, "audio_pulse_out: Failed to connect to server: %s\n",
- pa_strerror(pa_context_errno(this->pa_class->context)));
- goto fail;
+ if (cstate == PA_CONTEXT_FAILED || cstate == PA_CONTEXT_TERMINATED ||
+ sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: failed to connect context object: %s\n", pa_strerror(pa_context_errno(this->context)));
+ goto fail;
+ }
+
+ if (sstate == PA_STREAM_READY)
+ break;
+
+ pa_threaded_mainloop_wait(this->mainloop);
}
- this->frames_written = 0;
- this->ao_driver.set_property(this, AO_PROP_PCM_VOL, 100);
+ pa_threaded_mainloop_unlock(this->mainloop);
return this->sample_rate;
-fail:
- pa_threaded_mainloop_unlock(this->pa_class->mainloop);
+ fail:
+
+ pa_threaded_mainloop_unlock(this->mainloop);
this_gen->close(this_gen);
return 0;
}
@@ -318,217 +442,367 @@ static int ao_pulse_write(ao_driver_t *this_gen, int16_t *data,
{
pulse_driver_t *this = (pulse_driver_t *) this_gen;
size_t size = num_frames * this->bytes_per_frame;
- int ret = 0;
-
- if ( !this->stream || !this->pa_class->context)
- return -1;
+ int ret = -1;
+ size_t done = 0;
- switch( pa_stream_get_state(this->stream) ) {
- case PA_STREAM_READY:
- while (size > 0) {
- size_t l;
+ pa_threaded_mainloop_lock(this->mainloop);
- while (!(l = pa_stream_writable_size(this->stream))) {
- xine_usec_sleep (10000);
- }
+ while (size > 0) {
+ size_t l;
- if (l > size)
- l = size;
-
- pa_stream_write(this->stream, data, l, NULL, 0, PA_SEEK_RELATIVE);
- data = (int16_t *) ((uint8_t*) data + l);
- size -= l;
- }
+ for (;;) {
+
+ if (!this->stream ||
+ !this->context ||
+ pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+ pa_stream_get_state(this->stream) != PA_STREAM_READY)
+ goto finish;
- this->frames_written += num_frames;
+ if ((l = pa_stream_writable_size(this->stream)) == (size_t) -1)
+ goto finish;
- if (pa_stream_get_state(this->stream) == PA_STREAM_READY)
- ret = 1;
+ if (l > 0)
+ break;
- break;
+ pa_threaded_mainloop_wait(this->mainloop);
+ }
+
+ if (l > size)
+ l = size;
+
+ pa_stream_write(this->stream, data, l, NULL, 0, PA_SEEK_RELATIVE);
+ data = (int16_t *) ((uint8_t*) data + l);
+ size -= l;
+ done += l;
}
+ ret = done;
+
+finish:
+
+ pa_threaded_mainloop_unlock(this->mainloop);
+
+/* fprintf(stderr, "write-out\n"); */
+
return ret;
-}
+}
static int ao_pulse_delay (ao_driver_t *this_gen)
{
pulse_driver_t *this = (pulse_driver_t *) this_gen;
- pa_usec_t latency = 0;
- unsigned int delay_frames;
+ int ret = 0;
- if ( ! this->stream ) return this->frames_written;
+/* fprintf(stderr, "delay-in\n"); */
- if (pa_stream_get_latency(this->stream, &latency, NULL) < 0) {
- pa_context_unref(this->pa_class->context);
- this->pa_class->context = NULL;
+ pa_threaded_mainloop_lock(this->mainloop);
- pa_stream_disconnect(this->stream);
- pa_stream_unref(this->stream);
- this->stream = NULL;
+ for (;;) {
+ pa_usec_t latency = 0;
- return 0;
+ if (!this->stream ||
+ !this->context ||
+ pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+ pa_stream_get_state(this->stream) != PA_STREAM_READY)
+ goto finish;
+
+ if (pa_stream_get_latency(this->stream, &latency, NULL) >= 0) {
+ ret = (int) ((latency * this->sample_rate) / 1000000);
+ goto finish;
+ }
+
+ if (pa_context_errno(this->context) != PA_ERR_NODATA) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: failed to query latency: %s\n", pa_strerror(pa_context_errno(this->context)));
+ goto finish;
+ }
+
+ pa_threaded_mainloop_wait(this->mainloop);
}
- /* convert latency (us) to frame units. */
- delay_frames = (int)(latency * this->sample_rate / 1000000);
+finish:
+
+ pa_threaded_mainloop_unlock(this->mainloop);
- if( delay_frames > this->frames_written )
- return this->frames_written;
- else
- return delay_frames;
+ return ret;
}
static void ao_pulse_close(ao_driver_t *this_gen)
{
pulse_driver_t *this = (pulse_driver_t *) this_gen;
-
- if (this->stream) {
- if (pa_stream_get_state(this->stream) == PA_STREAM_READY) {
- pthread_mutex_t completion_callback = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&completion_callback);
- pa_stream_drain(this->stream, __xine_pa_stream_success_callback, &completion_callback);
- pthread_mutex_lock(&completion_callback);
- pthread_mutex_destroy(&completion_callback);
- }
+ pa_threaded_mainloop_lock(this->mainloop);
+ if (this->stream) {
pa_stream_disconnect(this->stream);
pa_stream_unref(this->stream);
this->stream = NULL;
-
- pa_context_unref(this->pa_class->context);
}
+
+ pa_threaded_mainloop_unlock(this->mainloop);
}
static uint32_t ao_pulse_get_capabilities (ao_driver_t *this_gen) {
pulse_driver_t *this = (pulse_driver_t *) this_gen;
+
return this->capabilities;
}
-static void ao_pulse_exit(ao_driver_t *this_gen)
-{
+static void ao_pulse_exit(ao_driver_t *this_gen) {
pulse_driver_t *this = (pulse_driver_t *) this_gen;
- free (this);
+ ao_pulse_close(this_gen);
+
+ pa_threaded_mainloop_lock(this->mainloop);
+
+ if (this->context) {
+ pa_context_disconnect(this->context);
+ pa_context_unref(this->context);
+ }
+
+ pa_threaded_mainloop_unlock(this->mainloop);
+
+ pa_threaded_mainloop_free(this->mainloop);
+
+ free(this->host);
+ free(this->sink);
+ free(this);
+}
+
+static int wait_for_operation(pulse_driver_t *this, pa_operation *o) {
+
+ for (;;) {
+
+ if (!this->stream ||
+ !this->context ||
+ pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+ pa_stream_get_state(this->stream) != PA_STREAM_READY)
+ return -1;
+
+ if (pa_operation_get_state(o) != PA_OPERATION_RUNNING)
+ return 0;
+
+ pa_threaded_mainloop_wait(this->mainloop);
+ }
}
static int ao_pulse_get_property (ao_driver_t *this_gen, int property) {
pulse_driver_t *this = (pulse_driver_t *) this_gen;
int result = 0;
+ pa_operation *o = NULL;
- if ( ! this->stream || ! this->pa_class->context )
+ pa_threaded_mainloop_lock(this->mainloop);
+
+ if (!this->stream ||
+ !this->context ||
+ pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+ pa_stream_get_state(this->stream) != PA_STREAM_READY) {
+ pa_threaded_mainloop_unlock(this->mainloop);
return 0;
+ }
switch(property) {
- case AO_PROP_PCM_VOL:
- case AO_PROP_MIXER_VOL:
- {
- pthread_mutex_lock(&this->info_mutex);
- pa_operation *o = pa_context_get_sink_input_info(this->pa_class->context,
- pa_stream_get_index(this->stream),
- __xine_pa_sink_info_callback, this);
- if ( ! o ) return 0;
- pthread_mutex_lock(&this->info_mutex); pthread_mutex_unlock(&this->info_mutex);
-
- result = (pa_sw_volume_to_linear(this->swvolume)*100);
- }
- break;
- case AO_PROP_MUTE_VOL:
- result = pa_cvolume_is_muted(&this->cvolume);
- break;
+ case AO_PROP_MUTE_VOL:
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+
+ o = pa_context_get_sink_input_info(this->context, pa_stream_get_index(this->stream),
+ __xine_pa_sink_info_callback, this);
+
+ break;
+ }
+
+ if (o) {
+ wait_for_operation(this, o);
+ pa_operation_unref(o);
+ }
+
+ switch(property) {
+
+ case AO_PROP_MUTE_VOL:
+ result = this->muted;
+ break;
+
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+ result = (int) (pa_sw_volume_to_linear(this->swvolume)*100);
+ break;
}
-
+
+ pa_threaded_mainloop_unlock(this->mainloop);
+
return result;
}
static int ao_pulse_set_property (ao_driver_t *this_gen, int property, int value) {
pulse_driver_t *this = (pulse_driver_t *) this_gen;
int result = ~value;
+ pa_operation *o = NULL;
- if ( ! this->stream || ! this->pa_class->context )
- return result;
+ pa_threaded_mainloop_lock(this->mainloop);
+
+ if (!this->stream ||
+ !this->context ||
+ pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+ pa_stream_get_state(this->stream) != PA_STREAM_READY) {
+ pa_threaded_mainloop_unlock(this->mainloop);
+ return 0;
+ }
switch(property) {
- case AO_PROP_PCM_VOL:
- case AO_PROP_MIXER_VOL:
- this->swvolume = pa_sw_volume_from_linear((double)value/100.0);
- pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume);
+ case AO_PROP_PCM_VOL:
+ case AO_PROP_MIXER_VOL:
+
+ this->swvolume = pa_sw_volume_from_linear((double)value/100.0);
+ pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume);
- pa_context_set_sink_input_volume(this->pa_class->context, pa_stream_get_index(this->stream),
- &this->cvolume, __xine_pa_context_success_callback, this);
+ o = pa_context_set_sink_input_volume(this->context, pa_stream_get_index(this->stream),
+ &this->cvolume, __xine_pa_context_success_callback, this);
- result = value;
- break;
+ result = value;
+ break;
- case AO_PROP_MUTE_VOL:
- if ( value )
- pa_cvolume_mute(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels);
- else
- pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume);
+ case AO_PROP_MUTE_VOL:
- pa_context_set_sink_input_volume(this->pa_class->context, pa_stream_get_index(this->stream),
- &this->cvolume, __xine_pa_context_success_callback, this);
-
- result = value;
- break;
+ this->muted = value;
+
+#if PA_PROTOCOL_VERSION >= 11
+ /* PulseAudio 0.9.7 and newer */
+ o = pa_context_set_sink_input_mute(this->context, pa_stream_get_index(this->stream),
+ value, __xine_pa_context_success_callback, this);
+#else
+ /* Get the current volume, so we can restore it properly. */
+ o = pa_context_get_sink_input_info(this->context, pa_stream_get_index(this->stream),
+ __xine_pa_sink_info_callback, this);
+
+ if (o) {
+ wait_for_operation(this, o);
+ pa_operation_unref(o);
+ }
+
+ if ( value )
+ pa_cvolume_mute(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels);
+ else
+ pa_cvolume_set(&this->cvolume, pa_stream_get_sample_spec(this->stream)->channels, this->swvolume);
+
+ o = pa_context_set_sink_input_volume(this->context, pa_stream_get_index(this->stream),
+ &this->cvolume, __xine_pa_context_success_callback, this);
+#endif
+ result = value;
}
-
+
+ if (o) {
+ wait_for_operation(this, o);
+ pa_operation_unref(o);
+ }
+
+ pa_threaded_mainloop_unlock(this->mainloop);
+
return result;
}
static int ao_pulse_ctrl(ao_driver_t *this_gen, int cmd, ...) {
pulse_driver_t *this = (pulse_driver_t *) this_gen;
+ pa_operation *o = NULL;
- if ( ! this->stream ) return 0;
+ pa_threaded_mainloop_lock(this->mainloop);
+
+ if (!this->stream ||
+ !this->context ||
+ pa_context_get_state(this->context) != PA_CONTEXT_READY ||
+ pa_stream_get_state(this->stream) != PA_STREAM_READY) {
+ pa_threaded_mainloop_unlock(this->mainloop);
+ return 0;
+ }
switch (cmd) {
- case AO_CTRL_FLUSH_BUFFERS:
- _x_assert(this->stream && this->pa_class->context);
+ case AO_CTRL_FLUSH_BUFFERS:
- if(pa_stream_get_state(this->stream) == PA_STREAM_READY) {
- pthread_mutex_t completion_callback = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&completion_callback);
- pa_stream_flush(this->stream, __xine_pa_stream_success_callback, &completion_callback);
+ o = pa_stream_flush(this->stream, __xine_pa_stream_success_callback, this);
+ break;
- pthread_mutex_lock(&completion_callback);
- pthread_mutex_destroy(&completion_callback);
- }
+ case AO_CTRL_PLAY_RESUME:
+ case AO_CTRL_PLAY_PAUSE:
- this->frames_written = 0;
+ o = pa_stream_cork(this->stream, cmd == AO_CTRL_PLAY_PAUSE, __xine_pa_stream_success_callback, this);
+ break;
+ }
- break;
+ if (o) {
+ wait_for_operation(this, o);
+ pa_operation_unref(o);
}
+ pa_threaded_mainloop_unlock(this->mainloop);
+
return 0;
}
static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data) {
pulse_class_t *class = (pulse_class_t *) class_gen;
pulse_driver_t *this;
- char *device;
+ const char* device;
+ int r;
lprintf ("audio_pulse_out: open_plugin called\n");
- this = (pulse_driver_t *) xine_xmalloc (sizeof (pulse_driver_t));
+ this = calloc(1, sizeof (pulse_driver_t));
if (!this)
return NULL;
+
this->xine = class->xine;
+ this->host = NULL;
+ this->sink = NULL;
+ this->context = NULL;
+ this->mainloop = NULL;
+
+ device = class->xine->config->register_string(class->xine->config,
+ "audio.pulseaudio_device",
+ "",
+ _("device used for pulseaudio"),
+ _("use 'server[:sink]' for setting the "
+ "pulseaudio sink device."),
+ 10, NULL,
+ NULL);
+
+ if (device && *device) {
+ char *sep = strrchr(device, ':');
+ if ( sep ) {
+ if (!(this->host = strndup(device, sep-device))) {
+ free(this);
+ return NULL;
+ }
+
+ if (!(this->sink = strdup(sep+1))) {
+ free(this->host);
+ free(this);
+ return NULL;
+ }
+ } else {
+
+ if (!(this->host = strdup(device))) {
+ free(this);
+ return NULL;
+ }
+ }
+ }
+
+ this->mainloop = pa_threaded_mainloop_new();
+ _x_assert(this->mainloop);
+ pa_threaded_mainloop_start(this->mainloop);
/*
* set capabilities
*/
- this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO | AO_CAP_MODE_4CHANNEL |
- AO_CAP_MODE_4_1CHANNEL | AO_CAP_MODE_5CHANNEL |
- AO_CAP_MODE_5_1CHANNEL | AO_CAP_MIXER_VOL |
- AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL | AO_CAP_8BITS |
- AO_CAP_16BITS | AO_CAP_FLOAT32;
+ this->capabilities =
+ AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO | AO_CAP_MODE_4CHANNEL |
+ AO_CAP_MODE_4_1CHANNEL | AO_CAP_MODE_5CHANNEL | AO_CAP_MODE_5_1CHANNEL |
+ AO_CAP_MIXER_VOL | AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL |
+ AO_CAP_8BITS | AO_CAP_16BITS | AO_CAP_FLOAT32;
this->sample_rate = 0;
- this->host = NULL;
- this->sink = NULL;
-
+
this->ao_driver.get_capabilities = ao_pulse_get_capabilities;
this->ao_driver.get_property = ao_pulse_get_property;
this->ao_driver.set_property = ao_pulse_set_property;
@@ -540,33 +814,22 @@ static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *da
this->ao_driver.close = ao_pulse_close;
this->ao_driver.exit = ao_pulse_exit;
this->ao_driver.get_gap_tolerance = ao_pulse_get_gap_tolerance;
- this->ao_driver.control = ao_pulse_ctrl;
-
- device = this->xine->config->register_string(this->xine->config,
- "audio.pulseaudio_device",
- "",
- _("device used for pulseaudio"),
- _("use 'server[:sink]' for setting the "
- "pulseaudio sink device."),
- 10, NULL,
- NULL);
-
- if (device && *device) {
- char *sep = strchr(device, ':');
- if ( sep ) {
- this->host = strndup(device, sep-device);
- this->sink = strdup(&sep[1]);
- } else
- this->host = strdup(device);
- }
-
- pthread_mutex_init(&this->info_mutex, NULL);
+ this->ao_driver.control = ao_pulse_ctrl;
xprintf (class->xine, XINE_VERBOSITY_DEBUG, "audio_pulse_out: host %s sink %s\n",
this->host ? this->host : "(null)", this->sink ? this->sink : "(null)");
this->pa_class = class;
+ pa_threaded_mainloop_lock(this->mainloop);
+ r = connect_context(this);
+ pa_threaded_mainloop_unlock(this->mainloop);
+
+ if (r < 0) {
+ ao_pulse_exit((ao_driver_t *) this);
+ return NULL;
+ }
+
return &this->ao_driver;
}
@@ -586,13 +849,7 @@ static void dispose_class (audio_driver_class_t *this_gen) {
pulse_class_t *this = (pulse_class_t *) this_gen;
- if ( this->context )
- pa_context_unref(this->context);
-
- pa_threaded_mainloop_stop(this->mainloop);
- pa_threaded_mainloop_free(this->mainloop);
-
- free (this);
+ free(this);
}
static void *init_class (xine_t *xine, void *data) {
@@ -601,29 +858,21 @@ static void *init_class (xine_t *xine, void *data) {
lprintf ("audio_pulse_out: init class\n");
- this = (pulse_class_t *) xine_xmalloc (sizeof (pulse_class_t));
+ this = calloc(1, sizeof (pulse_class_t));
if (!this)
return NULL;
+ this->xine = xine;
this->driver_class.open_plugin = open_plugin;
this->driver_class.get_identifier = get_identifier;
this->driver_class.get_description = get_description;
this->driver_class.dispose = dispose_class;
- this->xine = xine;
-
- this->mainloop = pa_threaded_mainloop_new();
- _x_assert(this->mainloop);
-
- pa_threaded_mainloop_start(this->mainloop);
-
- this->context = NULL;
-
return this;
}
static const ao_info_t ao_info_pulse = {
- 6
+ 12
};
/*
@@ -635,5 +884,3 @@ const plugin_info_t xine_plugin_info[] EXPORTED = {
{ PLUGIN_AUDIO_OUT, 8, "pulseaudio", XINE_VERSION_CODE, &ao_info_pulse, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};
-
-
diff --git a/src/audio_out/audio_sndio_out.c b/src/audio_out/audio_sndio_out.c
new file mode 100644
index 000000000..7c3040e6e
--- /dev/null
+++ b/src/audio_out/audio_sndio_out.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2008 Brad Smith <brad@comstyle.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* ao plugin for sndio by Brad Smith <brad@comstyle.com>. */
+
+#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>
+#include <inttypes.h>
+#include <pthread.h>
+
+#include <sndio.h>
+
+#include "xine_internal.h"
+#include "xineutils.h"
+#include "audio_out.h"
+#include "bswap.h"
+
+#define GAP_TOLERANCE AO_MAX_GAP
+#define PCT_TO_MIDI(p) (((p) * SIO_MAXVOL + 50) / 100)
+
+typedef struct {
+ audio_driver_class_t driver_class;
+ xine_t *xine;
+} sndio_class_t;
+
+typedef struct sndio_driver_s {
+ ao_driver_t ao_driver;
+ xine_t *xine;
+
+ struct sio_hdl *hdl;
+ long long realpos, playpos;
+ int capabilities;
+
+ int num_channels;
+ u_int32_t bits_per_sample;
+ u_int32_t bytes_per_frame;
+
+ struct {
+ int volume;
+ int mute;
+ } mixer;
+} sndio_driver_t;
+
+/*
+ * Callback to notify of frames processed by the hw. It is
+ * called from the mail loop called from sio_write().
+ */
+static void ao_sndio_onmove_cb(void *addr, int delta)
+{
+ sndio_driver_t *this = (sndio_driver_t *)addr;
+
+ this->realpos += delta;
+}
+
+/*
+ * Open the audio device for writing to.
+ */
+static int ao_sndio_open(ao_driver_t *this_gen,
+ uint32_t bits, uint32_t rate, int mode)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+ struct sio_par par;
+
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_open bits=%d rate=%d, mode=%d\n",
+ bits, rate, mode);
+
+ if (this->hdl != NULL) {
+ sio_close (this->hdl);
+ this->hdl = NULL;
+ }
+
+ this->hdl = sio_open(NULL, SIO_PLAY, 0);
+ if (this->hdl == NULL)
+ goto bad;
+
+ sio_initpar(&par);
+
+ switch (mode) {
+ case AO_CAP_MODE_MONO:
+ par.pchan = 1;
+ break;
+ case AO_CAP_MODE_STEREO:
+ par.pchan = 2;
+ break;
+ case AO_CAP_MODE_4CHANNEL:
+ par.pchan = 4;
+ break;
+ case AO_CAP_MODE_4_1CHANNEL:
+ case AO_CAP_MODE_5CHANNEL:
+ case AO_CAP_MODE_5_1CHANNEL:
+ par.pchan = 6;
+ break;
+ default:
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_open does not support the requested mode: 0x%X\n",
+ mode);
+ goto bad;
+ }
+
+ switch (bits) {
+ case 8:
+ par.bits = 8;
+ par.sig = 0;
+ break;
+ case 16:
+ par.bits = 16;
+ par.sig = 1;
+ break;
+ default:
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_open bits per sample not supported: %d\n", bits);
+ goto bad;
+ }
+
+ par.rate = rate;
+ par.appbufsz = par.rate * 250 / 1000; /* 250ms buffer */
+
+ if (!sio_setpar(this->hdl, &par)) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_open could not set params\n");
+ goto bad;
+ }
+
+ if (!sio_getpar(this->hdl, &par)) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_open could not get params\n");
+ goto bad;
+ }
+
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_open %d channels output\n",
+ par.pchan);
+
+ this->num_channels = par.pchan;
+ this->bytes_per_frame = par.bps * par.pchan;
+ this->playpos = 0;
+ this->realpos = 0;
+ sio_onmove(this->hdl, ao_sndio_onmove_cb, this);
+
+ if (!sio_start(this->hdl)) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_open could not start\n");
+ goto bad;
+ }
+
+ return par.rate;
+
+bad:
+ if (this->hdl != NULL)
+ sio_close(this->hdl);
+ return 0;
+}
+
+static int ao_sndio_num_channels(ao_driver_t *this_gen)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+
+ return this->num_channels;
+}
+
+static int ao_sndio_bytes_per_frame(ao_driver_t *this_gen)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+
+ return this->bytes_per_frame;
+}
+
+static int ao_sndio_get_gap_tolerance (ao_driver_t *this_gen)
+{
+ return GAP_TOLERANCE;
+}
+
+static int ao_sndio_write(ao_driver_t *this_gen, int16_t *data,
+ uint32_t num_frames)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+ size_t ret, size = num_frames * this->bytes_per_frame;
+
+ ret = sio_write(this->hdl, data, size);
+ if (ret == 0)
+ return 0;
+
+ this->playpos += num_frames;
+
+ return 1;
+}
+
+static int ao_sndio_delay (ao_driver_t *this_gen)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+ int bufused;
+
+ if (this->realpos < 0)
+ bufused = this->playpos;
+ else
+ bufused = this->playpos - this->realpos;
+
+ return bufused;
+}
+
+static void ao_sndio_close(ao_driver_t *this_gen)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_close called\n");
+
+ if (!sio_stop(this->hdl)) {
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_close could not stop\n");
+ }
+
+ sio_close(this->hdl);
+ this->hdl = NULL;
+}
+
+static uint32_t ao_sndio_get_capabilities (ao_driver_t *this_gen)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+
+ return this->capabilities;
+}
+
+static void ao_sndio_exit(ao_driver_t *this_gen)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+
+ xprintf (this->xine, XINE_VERBOSITY_DEBUG,
+ "audio_sndio_out: ao_sndio_exit called\n");
+
+ if (this->hdl != NULL)
+ sio_close(this->hdl);
+}
+
+static int ao_sndio_get_property (ao_driver_t *this_gen, int property)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+
+ switch (property) {
+ case AO_PROP_MIXER_VOL:
+ return this->mixer.volume;
+ break;
+ case AO_PROP_MUTE_VOL:
+ return this->mixer.mute;
+ break;
+ }
+
+ return 0;
+}
+
+static int ao_sndio_set_property (ao_driver_t *this_gen, int property, int value)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+ int vol;
+
+ if (this->hdl == NULL)
+ return 0;
+
+ switch(property) {
+ case AO_PROP_MIXER_VOL:
+ this->mixer.volume = value;
+ if (!this->mixer.mute)
+ sio_setvol(this->hdl, PCT_TO_MIDI(this->mixer.volume));
+ return this->mixer.volume;
+ break;
+
+ case AO_PROP_MUTE_VOL:
+ this->mixer.mute = (value) ? 1 : 0;
+ vol = 0;
+ if (!this->mixer.mute)
+ vol = PCT_TO_MIDI(this->mixer.volume);
+ sio_setvol(this->hdl, vol);
+ return value;
+ break;
+ }
+
+ return value;
+}
+
+/*
+ * pause, resume, flush buffers
+ */
+static int ao_sndio_ctrl(ao_driver_t *this_gen, int cmd, ...)
+{
+ sndio_driver_t *this = (sndio_driver_t *) this_gen;
+
+ /*
+ * sndio pauses automatically if there are no more samples to play
+ * and resumes when there are samples again. So we leave this empty
+ * for the moment.
+ */
+
+ return 0;
+}
+
+static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data)
+{
+ sndio_class_t *class = (sndio_class_t *) class_gen;
+ sndio_driver_t *this;
+
+ lprintf ("audio_sndio_out: open_plugin called\n");
+
+ this = calloc(1, sizeof (sndio_driver_t));
+ if (!this)
+ return NULL;
+
+ this->xine = class->xine;
+
+ /*
+ * Set capabilities
+ */
+ this->capabilities = AO_CAP_MODE_MONO | AO_CAP_MODE_STEREO |
+ AO_CAP_MODE_4CHANNEL | AO_CAP_MODE_4_1CHANNEL |
+ AO_CAP_MODE_5CHANNEL | AO_CAP_MODE_5_1CHANNEL |
+ AO_CAP_MIXER_VOL | AO_CAP_MUTE_VOL | AO_CAP_8BITS |
+ AO_CAP_16BITS;
+
+ this->ao_driver.get_capabilities = ao_sndio_get_capabilities;
+ this->ao_driver.get_property = ao_sndio_get_property;
+ this->ao_driver.set_property = ao_sndio_set_property;
+ this->ao_driver.open = ao_sndio_open;
+ this->ao_driver.num_channels = ao_sndio_num_channels;
+ this->ao_driver.bytes_per_frame = ao_sndio_bytes_per_frame;
+ this->ao_driver.delay = ao_sndio_delay;
+ this->ao_driver.write = ao_sndio_write;
+ this->ao_driver.close = ao_sndio_close;
+ this->ao_driver.exit = ao_sndio_exit;
+ this->ao_driver.get_gap_tolerance = ao_sndio_get_gap_tolerance;
+ this->ao_driver.control = ao_sndio_ctrl;
+
+ return &this->ao_driver;
+}
+
+/*
+ * class functions
+ */
+
+static char* get_identifier (audio_driver_class_t *this_gen)
+{
+ return "sndio";
+}
+
+static char* get_description (audio_driver_class_t *this_gen)
+{
+ return _("xine audio output plugin using sndio audio devices/drivers ");
+}
+
+static void dispose_class (audio_driver_class_t *this_gen)
+{
+ sndio_class_t *this = (sndio_class_t *) this_gen;
+
+ free(this);
+}
+
+static void *init_class (xine_t *xine, void *data)
+{
+ sndio_class_t *this;
+
+ lprintf ("audio_sndio_out: init class\n");
+
+ this = calloc(1, sizeof (sndio_class_t));
+ if (!this)
+ return NULL;
+
+ this->driver_class.open_plugin = open_plugin;
+ this->driver_class.get_identifier = get_identifier;
+ this->driver_class.get_description = get_description;
+ this->driver_class.dispose = dispose_class;
+
+ this->xine = xine;
+
+ return this;
+}
+
+static const ao_info_t ao_info_sndio = {
+ 12
+};
+
+/*
+ * exported plugin catalog entry
+ */
+
+const plugin_info_t xine_plugin_info[] EXPORTED = {
+ /* type, API, "name", version, special_info, init_function */
+ { PLUGIN_AUDIO_OUT, 8, "sndio", XINE_VERSION_CODE, &ao_info_sndio, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};
diff --git a/src/audio_out/audio_sun_out.c b/src/audio_out/audio_sun_out.c
index b23955b77..048db6b86 100644
--- a/src/audio_out/audio_sun_out.c
+++ b/src/audio_out/audio_sun_out.c
@@ -909,7 +909,7 @@ static ao_driver_t *ao_sun_open_plugin (audio_driver_class_t *class_gen, const v
int status;
audio_info_t info;
- this = (sun_driver_t *) xine_xmalloc (sizeof (sun_driver_t));
+ this = calloc(1, sizeof (sun_driver_t));
if (!this)
return NULL;
@@ -1032,7 +1032,7 @@ static void ao_sun_dispose_class (audio_driver_class_t *this_gen) {
static void *ao_sun_init_class (xine_t *xine, void *data) {
sun_class_t *this;
- this = (sun_class_t *) xine_xmalloc (sizeof (sun_class_t));
+ this = calloc(1, sizeof (sun_class_t));
if (!this)
return NULL;