diff options
-rw-r--r-- | ChangeLog | 1 | ||||
-rw-r--r-- | src/audio_out/Makefile.am | 9 | ||||
-rw-r--r-- | src/audio_out/audio_directx2_out.c | 1030 | ||||
-rw-r--r-- | win32/xine.dsw | 18 | ||||
-rw-r--r-- | win32/xineplug_ao_out_directx2.dsp | 109 |
5 files changed, 1166 insertions, 1 deletions
@@ -21,6 +21,7 @@ xine-lib (1.0.1) * fixed build on solaris and other platforms [bugs #1062987, #1114677 and #1115001] * published documentation about Win32 platform + * brand new DirectX audio output plugin for Windows xine-lib (1.0) * unbreak DXR3 plugin 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 } +}; diff --git a/win32/xine.dsw b/win32/xine.dsw index 4616d28e7..684e2ca20 100644 --- a/win32/xine.dsw +++ b/win32/xine.dsw @@ -87,6 +87,24 @@ Package=<4> ###############################################################################
+Project: "xineplug_ao_out_directx2"=".\xineplug_ao_out_directx2.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+ Begin Project Dependency
+ Project_Dep_Name libxine
+ End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name libxinesuppt
+ End Project Dependency
+}}}
+
+###############################################################################
+
Project: "xineplug_decode_dts"=".\xineplug_decode_dts.dsp" - Package Owner=<4>
Package=<5>
diff --git a/win32/xineplug_ao_out_directx2.dsp b/win32/xineplug_ao_out_directx2.dsp new file mode 100644 index 000000000..29e640742 --- /dev/null +++ b/win32/xineplug_ao_out_directx2.dsp @@ -0,0 +1,109 @@ +# Microsoft Developer Studio Project File - Name="xineplug_ao_out_directx2" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=xineplug_ao_out_directx2 - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "xineplug_ao_out_directx2.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "xineplug_ao_out_directx2.mak" CFG="xineplug_ao_out_directx2 - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "xineplug_ao_out_directx2 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "xineplug_ao_out_directx2 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "xineplug_ao_out_directx2 - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release/xineplug_ao_out_directx2"
+# PROP Intermediate_Dir "Release/xineplug_ao_out_directx2"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+LIB32=link.exe
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XINEPLUG_AO_OUT_DIRECTX_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../lib" /I "../include" /I "include" /I "include/msvc" /I "contrib/pthreads" /I "../src" /I "../src/xine-engine" /I "../src/xine-utils" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XINEPLUG_AO_OUT_DIRECTX_EXPORTS" /D "XINE_COMPILE" /D "HAVE_CONFIG_H" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib dsound.lib dxguid.lib /nologo /dll /machine:I386 /out:"Release/bin/plugins/xineplug_ao_out_directx2.dll"
+
+!ELSEIF "$(CFG)" == "xineplug_ao_out_directx2 - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug/xineplug_ao_out_directx2"
+# PROP Intermediate_Dir "Debug/xineplug_ao_out_directx2"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+LIB32=link.exe
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XINEPLUG_AO_OUT_DIRECTX_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../lib" /I "../include" /I "include" /I "include/msvc" /I "contrib/pthreads" /I "../src" /I "../src/xine-engine" /I "../src/xine-utils" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "XINEPLUG_AO_OUT_DIRECTX_EXPORTS" /D "XINE_COMPILE" /D "HAVE_CONFIG_H" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib dsound.lib dxguid.lib /nologo /dll /debug /machine:I386 /out:"Debug/bin/plugins/xineplug_ao_out_directx2.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "xineplug_ao_out_directx2 - Win32 Release"
+# Name "xineplug_ao_out_directx2 - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\src\audio_out\audio_directx2_out.c
+# End Source File
+# End Group
+# Begin Group "DLL Defs"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\xine_plugin.def
+# End Source File
+# End Group
+# End Target
+# End Project
|