summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFrantišek Dvořák <valtri@users.sourceforge.net>2005-02-04 22:31:29 +0000
committerFrantišek Dvořák <valtri@users.sourceforge.net>2005-02-04 22:31:29 +0000
commit5c34ff37bb30b68aecf985e77cc06ff8541597e1 (patch)
treedb9969d8827a29f4f2ffe628207b11c4022c0b1e /src
parent484a83b43658b3b0a6e06bc97eec9395e8917322 (diff)
downloadxine-lib-5c34ff37bb30b68aecf985e77cc06ff8541597e1.tar.gz
xine-lib-5c34ff37bb30b68aecf985e77cc06ff8541597e1.tar.bz2
**BUGFIX**
Long awaited new version of DirectX audio output plugin. (very stable IMHO, so marked for 1.0 branch too) CVS patchset: 7382 CVS date: 2005/02/04 22:31:29
Diffstat (limited to 'src')
-rw-r--r--src/audio_out/Makefile.am9
-rw-r--r--src/audio_out/audio_directx2_out.c1030
2 files changed, 1038 insertions, 1 deletions
diff --git a/src/audio_out/Makefile.am b/src/audio_out/Makefile.am
index 13b36b36f..5206e805f 100644
--- a/src/audio_out/Makefile.am
+++ b/src/audio_out/Makefile.am
@@ -35,6 +35,7 @@ endif
if HAVE_DIRECTX
directx_module = xineplug_ao_out_directx.la
+directx2_module = xineplug_ao_out_directx2.la
endif
if HAVE_COREAUDIO
@@ -59,7 +60,8 @@ lib_LTLIBRARIES = xineplug_ao_out_none.la xineplug_ao_out_file.la \
$(esd_module) \
$(directx_module) \
$(coreaudio_module) \
- $(polypaudio_module)
+ $(polypaudio_module) \
+ $(directx2_module)
#lib_LTLIBRARIES = \
# $(alsa_module) \
@@ -118,3 +120,8 @@ xineplug_ao_out_coreaudio_la_CFLAGS = -framework CoreAudio -framework AudioUnit
xineplug_ao_out_polypaudio_la_SOURCES = audio_polyp_out.c
xineplug_ao_out_polypaudio_la_LIBADD = $(POLYPAUDIO_LIBS) $(XINE_LIB)
xineplug_ao_out_polypaudio_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
+
+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)
+xineplug_ao_out_directx2_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@
diff --git a/src/audio_out/audio_directx2_out.c b/src/audio_out/audio_directx2_out.c
new file mode 100644
index 000000000..48d45cec3
--- /dev/null
+++ b/src/audio_out/audio_directx2_out.c
@@ -0,0 +1,1030 @@
+/*
+ * Copyright (C) 2004-2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * $Id: audio_directx2_out.c,v 1.1 2005/02/04 22:31:32 valtri Exp $
+ *
+ *
+ * xine audio output plugin using DirectX
+ *
+ * Implementation:
+ * - this version contains service thread which starts and stops playback
+ * according to the data availability
+ * - it uses the ring buffer offered by DirectSound API
+ * - formula for volume level is deduced according to authors ears :-)
+ *
+ * Hacker notes:
+ * - always lock the mutex before calling audio_* functions
+ *
+ * Authors:
+ * - Frantisek Dvorak <valtri@atlas.cz>
+ *
+ * Inspiration:
+ * - mplayer for workarounding -lguid idea
+ * - DirectX 7 documentation
+ *
+ * License:
+ * - dual GPL/LGPL (LGPL for non xine-specific part)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <math.h>
+#include <windows.h>
+#include <dsound.h>
+
+
+#define LOG_MODULE "audio_directx2_out"
+#define LOG_VERBOSE
+/*
+#define LOG
+*/
+
+#include "xine_internal.h"
+#include "audio_out.h"
+
+
+#define AO_OUT_DIRECTX2_IFACE_VERSION 8
+
+/*
+ * 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
+ */
+#define PARTS 3
+
+/*
+ * base power factor for volume remapping
+ */
+#define FACTOR 60.0
+
+
+/* experiments */
+/*#define EXACT_WAIT*/
+/*#define EXACT_SLEEP*/
+/*#define PANIC_OVERRUN*/
+
+#define STATUS_START 0
+#define STATUS_WAIT 1
+#define STATUS_RUNNING 2
+
+
+#define PRIdword "lu"
+#define PRIsizet "u"
+
+
+typedef struct {
+ audio_driver_class_t driver_class;
+ xine_t *xine;
+} dx2_class_t;
+
+
+typedef struct {
+ ao_driver_t ao_driver;
+ dx2_class_t *class;
+
+ 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 */
+
+ uint32_t bits;
+ uint32_t rate;
+ uint32_t frame_size;
+ uint32_t capabilities;
+ int channels;
+ 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; /* number of current free parts */
+
+ 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 */
+} dx2_driver_t;
+
+
+/*****************************************************************************
+ * DirectDraw GUIDs.
+ * 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 int buffer_ready(dx2_driver_t *this);
+
+
+/* popup a dialog with error */
+static void error_message(const char *fmt, ...) {
+ char message[256];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+
+ MessageBox(0, message, _("Error"), MB_ICONERROR | MB_OK | MB_APPLMODAL);
+}
+
+
+/* description of given error */
+static char *dsound_strerror(HRESULT err) {
+ switch(err) {
+ case DS_OK: return _("success");
+#ifdef DSERR_ACCESSDENIED
+ case DSERR_ACCESSDENIED: return _("access denied");
+#endif
+ case DSERR_ALLOCATED: return _("resource is already in use");
+ case DSERR_ALREADYINITIALIZED: return _("object was already initialized");
+ case DSERR_BADFORMAT: return _("specified wave format is not supported");
+ case DSERR_BUFFERLOST: return _("memory buffer has been lost and must be restored");
+ case DSERR_CONTROLUNAVAIL: return _("requested buffer control is not available");
+ case DSERR_GENERIC: return _("undetermined error inside DirectSound subsystem");
+#ifdef HWUNAVAIL
+ case DSERR_HWUNAVAIL: return _("DirectSound hardware device is unavailable");
+#endif
+ case DSERR_INVALIDCALL: return _("function is not valid for the current state of the object");
+ case DSERR_INVALIDPARAM: return _("invalid parameter was passed");
+ case DSERR_NOAGGREGATION: return _("object doesn't support aggregation");
+ case DSERR_NODRIVER: return _("no sound driver available for use");
+ case DSERR_NOINTERFACE: return _("requested COM interface not available");
+ case DSERR_OTHERAPPHASPRIO: return _("another application has a higher priority level");
+ case DSERR_OUTOFMEMORY: return _("insufficient memory");
+ case DSERR_PRIOLEVELNEEDED: return _("low priority level for this function");
+ case DSERR_UNINITIALIZED: return _("DirectSound wasn't initialized");
+ case DSERR_UNSUPPORTED: return _("function is not supported");
+ default: return _("unknown error");
+ }
+}
+
+
+/* create direct sound object */
+static LPDIRECTSOUND dsound_create() {
+ LPDIRECTSOUND ds;
+
+ if (DirectSoundCreate(NULL, &ds, NULL) != DS_OK) {
+ error_message(_("Unable to create direct sound object."));
+ return NULL;
+ }
+
+ if (IDirectSound_SetCooperativeLevel(ds, GetDesktopWindow(), DSSCL_PRIORITY) != DS_OK) {
+ IDirectSound_Release(ds);
+ error_message(_("Could not set direct sound cooperative level."));
+ return NULL;
+ }
+
+ return ds;
+}
+
+
+/* destroy direct sound object */
+static void dsound_destroy(LPDIRECTSOUND ds) {
+ IDirectSound_Release(ds);
+}
+
+
+/* fill out wave format header */
+static void dsound_fill_wfx(WAVEFORMATEX *wfx, uint32_t bits, uint32_t rate, int channels, size_t frame_size) {
+ memset(wfx, 0, sizeof(wfx));
+ wfx->wFormatTag = WAVE_FORMAT_PCM;
+ wfx->nChannels = channels;
+ wfx->nSamplesPerSec = rate;
+ wfx->wBitsPerSample = (WORD)bits;
+ wfx->nBlockAlign = frame_size;
+ wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
+}
+
+
+/* fill out buffer description structure */
+static void dsound_fill_desc(DSBUFFERDESC *desc, DWORD flags, DWORD buffer_size, WAVEFORMATEX *wfx) {
+ memset(desc, 0, sizeof(DSBUFFERDESC));
+ desc->dwSize = sizeof(DSBUFFERDESC);
+ desc->dwFlags = flags;
+ desc->dwBufferBytes = buffer_size;
+ desc->lpwfxFormat = wfx;
+}
+
+
+/* send exit signal to the audio thread */
+static void audio_thread_exit(dx2_driver_t *this) {
+ this->finished = 1;
+ pthread_cond_signal(&this->data_cond);
+}
+
+
+/* check for error, log it and pop up dialog */
+static void audio_error(dx2_driver_t *this, HRESULT err, char *msg) {
+ xine_log(this->class->xine, XINE_LOG_MSG, LOG_MODULE ": %s: %s\n", msg, dsound_strerror(err));
+ if (!this->failed) {
+ error_message("%s: %s", msg, dsound_strerror(err));
+ this->failed = 1;
+ }
+ if (this->status != STATUS_START) audio_thread_exit(this);
+}
+
+
+/* create direct sound buffer */
+static int audio_create_buffers(dx2_driver_t *this) {
+ DSBUFFERDESC desc;
+ WAVEFORMATEX wfx;
+ DWORD flags;
+ HRESULT err;
+ size_t buffer_size;
+
+ buffer_size = this->rate * this->frame_size * BUFFER_MS / 1000;
+ 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;
+
+ flags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
+ dsound_fill_wfx(&wfx, this->bits, this->rate, this->channels, this->frame_size);
+ dsound_fill_desc(&desc, flags, this->buffer_size, &wfx);
+
+ if ((err = IDirectSound_CreateSoundBuffer(this->ds, &desc, &this->dsbuffer, NULL)) != DS_OK) {
+ audio_error(this, err, _("Unable to create secondary direct sound buffer"));
+ return 0;
+ }
+
+ lprintf("created direct sound buffer, size = %u, part = %u\n", this->buffer_size, this->part_size);
+ return 1;
+}
+
+
+/* destroy the sound buffer */
+static void audio_destroy_buffers(dx2_driver_t *this) {
+ IDirectSoundBuffer_Release(this->dsbuffer);
+}
+
+
+/* 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;
+
+ if ((err = IDirectSoundBuffer_Play(this->dsbuffer, 0, 0, DSBPLAY_LOOPING)) != DS_OK) {
+ audio_error(this, err, _("Couldn't play sound buffer"));
+ return 0;
+ }
+ return 1;
+}
+
+
+/* stop playback */
+static int audio_stop(dx2_driver_t *this) {
+ HRESULT err;
+
+ if ((err = IDirectSoundBuffer_Stop(this->dsbuffer)) != DS_OK) {
+ audio_error(this, err, _("Couldn't stop sound buffer"));
+ return 0;
+ }
+ return 1;
+}
+
+
+/* get current playback position in the ring buffer */
+static int audio_tell(dx2_driver_t *this, size_t *pos) {
+ DWORD err;
+ DWORD play_pos;
+
+ if ((err = IDirectSoundBuffer_GetCurrentPosition(this->dsbuffer, &play_pos, NULL)) != DS_OK) {
+ audio_error(this, err, _("Can't get buffer position"));
+ return 0;
+ }
+ *pos = play_pos;
+
+ return 1;
+}
+
+
+/* set playback position in the ring buffer */
+static int audio_seek(dx2_driver_t *this, size_t pos) {
+ DWORD err;
+
+ if ((err = IDirectSoundBuffer_SetCurrentPosition(this->dsbuffer, pos)) != DS_OK) {
+ audio_error(this, err, _("Can't set buffer position"));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* flush audio buffers */
+static int audio_flush(dx2_driver_t *this) {
+ this->status = STATUS_WAIT;
+ this->count = 0;
+ this->read_size = 0;
+ return audio_seek(this, 0);
+}
+
+
+/*
+ * set the volume
+ *
+ * DirecSound can only lower the volume by software way.
+ * Unit is dB, value is always negative or zero.
+ */
+static int audio_set_volume(dx2_driver_t *this, int volume) {
+ HRESULT err;
+ LONG value;
+
+ value = DSBVOLUME_MIN * (pow(FACTOR, 1 - volume / 100.0) - 1) / (FACTOR - 1);
+ if (value < DSBVOLUME_MIN) value = DSBVOLUME_MIN;
+ else if (value > DSBVOLUME_MAX) value = DSBVOLUME_MAX;
+ lprintf("Setting sound to %d%% (%ld dB)\n", volume, value);
+ if ((err = IDirectSoundBuffer_SetVolume(this->dsbuffer, value) != DS_OK)) {
+ audio_error(this, err, _("Can't set sound volume"));
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* add given data into the ring buffer */
+static int audio_fill(dx2_driver_t *this, char *data, size_t size) {
+ DWORD size1, size2;
+ void *ptr1, *ptr2;
+ 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);
+ /* try to restore the buffer, if necessary */
+ if (err == DSERR_BUFFERLOST) {
+ xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": buffer lost, try 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); }
+ if (err != DS_OK) {
+ audio_error(this, err, _("Couldn't lock direct sound buffer"));
+ return 0;
+ }
+
+ _x_assert(size == size1 + size2);
+ if (ptr1 && size1) xine_fast_memcpy(ptr1, data, size1);
+ if (ptr2 && size2) xine_fast_memcpy(ptr2, data + size1, size2);
+
+ this->read_size += size;
+
+ 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;
+}
+
+
+/* transform given mode the number of channels */
+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;
+ }
+
+ return channels;
+}
+
+
+/* test the capability on given buffer */
+static int test_capability(LPDIRECTSOUNDBUFFER buffer, uint32_t bits, uint32_t rate, int mode) {
+ WAVEFORMATEX wfx;
+ int channels;
+
+ channels = mode2channels(mode);
+ if (!channels) return 0;
+
+ dsound_fill_wfx(&wfx, bits, rate, channels, (bits >> 3) * channels);
+ if (IDirectSoundBuffer_SetFormat(buffer, &wfx) != DS_OK) {
+ lprintf("mode %d, bits %" PRIu32 " not supported\n", mode, bits);
+ return 0;
+ }
+
+ lprintf("mode %d, bits %" PRIu32 " supported\n", mode, bits);
+
+ return 1;
+}
+
+
+/*
+ * test capabilities of driver before opening
+ *
+ * Passed only 8 bit and 16 bit with mono or stereo.
+ */
+static int test_capabilities(dx2_driver_t *this) {
+ struct {
+ uint32_t bits;
+ uint32_t rate;
+ uint32_t mode;
+ uint32_t caps;
+ } tests[] = {
+ {8, 44100, AO_CAP_MODE_MONO, AO_CAP_8BITS | AO_CAP_MODE_MONO},
+ {8, 44100, AO_CAP_MODE_STEREO, AO_CAP_8BITS | AO_CAP_MODE_STEREO},
+ {16, 44100, AO_CAP_MODE_MONO, AO_CAP_16BITS | AO_CAP_MODE_MONO},
+ {16, 44100, AO_CAP_MODE_STEREO, AO_CAP_16BITS | AO_CAP_MODE_STEREO},
+ {16, 44100, AO_CAP_MODE_4CHANNEL, AO_CAP_16BITS | AO_CAP_MODE_4CHANNEL},
+ {16, 44100, AO_CAP_MODE_5CHANNEL, AO_CAP_16BITS | AO_CAP_MODE_5CHANNEL},
+ {16, 44100, AO_CAP_MODE_5_1CHANNEL, AO_CAP_16BITS | AO_CAP_MODE_5_1CHANNEL},
+ {24, 44100, AO_CAP_MODE_STEREO, AO_CAP_24BITS | AO_CAP_MODE_STEREO},
+ {32, 44100, AO_CAP_MODE_STEREO, AO_CAP_FLOAT32 | AO_CAP_MODE_STEREO},
+ {0, 0, 0, 0},
+ };
+ LPDIRECTSOUNDBUFFER buffer;
+ DSBUFFERDESC desc;
+ int i;
+
+ /* create temporary primary sound buffer */
+ dsound_fill_desc(&desc, DSBCAPS_PRIMARYBUFFER, 0, NULL);
+ if (IDirectSound_CreateSoundBuffer(this->ds, &desc, &buffer, NULL) != DS_OK) {
+ error_message(_("Unable to create primary direct sound buffer."));
+ return 0;
+ }
+
+ /* test capabilities */
+ this->capabilities = 0;
+ i = 0;
+ while (tests[i].bits) {
+ if (test_capability(buffer, tests[i].bits, tests[i].rate, tests[i].mode)) this->capabilities |= tests[i].caps;
+ i++;
+ }
+ lprintf("result capabilities: 0x08%" PRIX32 "\n", this->capabilities);
+
+ IDirectSoundBuffer_Release(buffer);
+ return 1;
+}
+
+
+/* size of free space in the ring buffer */
+static size_t buffer_free_size(dx2_driver_t *this) {
+ size_t used_full_size;
+
+ 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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
+
+ /* prepare empty buffer */
+ audio_flush(this);
+
+ for (i = 0; i < PARTS; i++) handles[i] = this->events[i].hEventNotify;
+
+ /* 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;
+ 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
+ }
+#endif
+ }
+
+ return NULL;
+
+fail:
+ this->finished = 1;
+finished:
+ pthread_mutex_unlock(&this->data_mutex);
+ return NULL;
+}
+
+
+/* ---- driver functions ---- */
+
+static uint32_t ao_dx2_get_capabilities(ao_driver_t *this_gen) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ return this->capabilities;
+}
+
+
+static int ao_dx2_get_property(ao_driver_t *this_gen, int property) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ switch(property) {
+
+ case AO_PROP_MIXER_VOL:
+ case AO_PROP_PCM_VOL:
+ return this->volume;
+
+ case AO_PROP_MUTE_VOL:
+ return this->muted;
+
+ default:
+ return 0;
+
+ }
+}
+
+
+static int ao_dx2_set_property(ao_driver_t *this_gen, int property, int value) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ 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_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;
+
+ }
+
+ return value;
+}
+
+
+static int ao_dx2_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ lprintf("bits=%" PRIu32 ", rate=%" PRIu32 ", mode=%d\n", bits, rate, mode);
+
+ if (rate < DSBFREQUENCY_MIN) rate = DSBFREQUENCY_MIN;
+ if (rate > DSBFREQUENCY_MAX) rate = DSBFREQUENCY_MAX;
+
+ this->bits = bits;
+ this->rate = rate;
+ if ((this->channels = mode2channels(mode)) == 0) return 0;
+ this->frame_size = (this->bits >> 3) * this->channels;
+
+ this->paused = 0;
+ this->finished = 0;
+ 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 (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;
+ }
+ 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) {
+ xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": can't create buffer pthread: %s\n"), strerror(errno));
+ goto fail_mutex;
+ }
+ pthread_cond_wait(&this->data_cond, &this->data_mutex);
+ pthread_mutex_unlock(&this->data_mutex);
+
+ return rate;
+
+fail_mutex:
+ pthread_mutex_unlock(&this->data_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;
+}
+
+
+static int ao_dx2_num_channels(ao_driver_t *this_gen) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ lprintf("channels=%d\n", this->channels);
+
+ return this->channels;
+}
+
+
+static int ao_dx2_bytes_per_frame(ao_driver_t *this_gen) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ lprintf("frame_size=%d\n", this->frame_size);
+
+ return this->frame_size;
+}
+
+
+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;
+
+ 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;
+ 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);
+#endif
+
+ return frames;
+}
+
+
+static int ao_dx2_write(ao_driver_t *this_gen, int16_t* audio_data, uint32_t num_samples) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+ size_t input_size; /* used size of input data */
+ size_t free_size; /* size of the free space in the ring buffer */
+ size_t size; /* current block size */
+ size_t read_pos; /* position in the input */
+
+ input_size = this->frame_size * num_samples;
+ read_pos = 0;
+
+ while (input_size && !this->finished) {
+ pthread_mutex_lock(&this->data_mutex);
+ 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);
+ pthread_mutex_lock(&this->data_mutex);
+ }
+ if (free_size >= input_size) size = input_size;
+ else size = free_size;
+
+ if (!audio_fill(this, ((char *)audio_data) + read_pos, size)) {
+ audio_thread_exit(this);
+ pthread_mutex_unlock(&this->data_mutex);
+ return 0;
+ }
+ pthread_mutex_unlock(&this->data_mutex);
+ read_pos += size;
+ input_size -= size;
+ }
+
+ return 1;
+}
+
+
+static void ao_dx2_close(ao_driver_t *this_gen) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ lprintf("close plugin\n");
+
+ pthread_mutex_lock(&this->data_mutex);
+ audio_thread_exit(this);
+ pthread_mutex_unlock(&this->data_mutex);
+ if (pthread_join(this->buffer_service, NULL) != 0) {
+ xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": can't destroy buffer pthread: %s\n"), strerror(errno));
+ return;
+ }
+ lprintf("pthread joined\n");
+ pthread_mutex_unlock(&this->data_mutex);
+
+ if (pthread_cond_destroy(&this->data_cond) != 0) {
+ xine_log(this->class->xine, XINE_LOG_MSG, _(LOG_MODULE ": can't destroy pthread condition: %s\n"), strerror(errno));
+ }
+ 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);
+}
+
+
+static void ao_dx2_exit(ao_driver_t *this_gen) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ lprintf("exit instance\n");
+
+ dsound_destroy(this->ds);
+ free(this);
+}
+
+
+/*
+ * 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;
+}
+
+
+static int ao_dx2_control(ao_driver_t *this_gen, int cmd, ...) {
+ dx2_driver_t *this = (dx2_driver_t *)this_gen;
+
+ 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_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);
+
+ }
+
+ return 0;
+}
+
+
+/* ---- class functions ---- */
+
+static ao_driver_t *open_plugin(audio_driver_class_t *class_gen, const void *data) {
+ dx2_class_t *class = (dx2_class_t *)class_gen;
+ dx2_driver_t *this;
+
+ lprintf("open plugin called\n");
+
+ this = (dx2_driver_t *)xine_xmalloc(sizeof(dx2_driver_t));
+ this->class = class;
+
+ this->ao_driver.get_capabilities = ao_dx2_get_capabilities;
+ this->ao_driver.get_property = ao_dx2_get_property;
+ this->ao_driver.set_property = ao_dx2_set_property;
+ this->ao_driver.open = ao_dx2_open;
+ this->ao_driver.num_channels = ao_dx2_num_channels;
+ this->ao_driver.bytes_per_frame = ao_dx2_bytes_per_frame;
+ this->ao_driver.delay = ao_dx2_delay;
+ this->ao_driver.write = ao_dx2_write;
+ this->ao_driver.close = ao_dx2_close;
+ this->ao_driver.exit = ao_dx2_exit;
+ this->ao_driver.get_gap_tolerance = ao_dx2_get_gap_tolerance;
+ this->ao_driver.control = ao_dx2_control;
+
+ this->volume = 100;
+ this->muted = 0;
+
+ this->failed = 0;
+
+ if ((this->ds = dsound_create()) == NULL) {
+ free(this);
+ return NULL;
+ }
+ test_capabilities(this);
+
+ return (ao_driver_t *)this;
+}
+
+
+static char* get_identifier(audio_driver_class_t *this_gen) {
+ return "directx2";
+}
+
+
+static char *get_description(audio_driver_class_t *this_gen) {
+ return _("second xine audio output plugin using directx");
+}
+
+
+static void dispose_class(audio_driver_class_t *this_gen) {
+ free(this_gen);
+}
+
+
+static void *init_class(xine_t *xine, void *data) {
+ dx2_class_t *this;
+
+ lprintf("init class\n");
+
+ this = (dx2_class_t *)xine_xmalloc(sizeof(dx2_class_t));
+
+ 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 ao_info_t ao_info_directx2 = {
+ 10
+};
+
+plugin_info_t xine_plugin_info[] = {
+ { PLUGIN_AUDIO_OUT, AO_OUT_DIRECTX2_IFACE_VERSION, "directx2", XINE_VERSION_CODE, &ao_info_directx2, init_class },
+ { PLUGIN_NONE, 0, "", 0, NULL, NULL }
+};