From e241843bec22600ab4ef98e7a085e82aac73fc93 Mon Sep 17 00:00:00 2001 From: Eugene Sandulenko Date: Sun, 15 Feb 2009 11:39:07 +0000 Subject: - Remove some unneeded files - Mass rename .c to .cpp svn-id: r38227 --- engines/sci/sfx/mixer/dc.c | 329 ------------- engines/sci/sfx/mixer/dc.cpp | 329 +++++++++++++ engines/sci/sfx/mixer/mixers.c | 55 --- engines/sci/sfx/mixer/mixers.cpp | 55 +++ engines/sci/sfx/mixer/soft.c | 988 --------------------------------------- engines/sci/sfx/mixer/soft.cpp | 988 +++++++++++++++++++++++++++++++++++++++ engines/sci/sfx/mixer/test.c | 351 -------------- engines/sci/sfx/mixer/test.cpp | 351 ++++++++++++++ 8 files changed, 1723 insertions(+), 1723 deletions(-) delete mode 100644 engines/sci/sfx/mixer/dc.c create mode 100644 engines/sci/sfx/mixer/dc.cpp delete mode 100644 engines/sci/sfx/mixer/mixers.c create mode 100644 engines/sci/sfx/mixer/mixers.cpp delete mode 100644 engines/sci/sfx/mixer/soft.c create mode 100644 engines/sci/sfx/mixer/soft.cpp delete mode 100644 engines/sci/sfx/mixer/test.c create mode 100644 engines/sci/sfx/mixer/test.cpp (limited to 'engines/sci/sfx/mixer') diff --git a/engines/sci/sfx/mixer/dc.c b/engines/sci/sfx/mixer/dc.c deleted file mode 100644 index 52b3ab5cbb..0000000000 --- a/engines/sci/sfx/mixer/dc.c +++ /dev/null @@ -1,329 +0,0 @@ -/*************************************************************************** - dc.c Copyright (C) 2005 Walter van Niftrik - - - This program may be modified and copied freely according to the terms of - the GNU general public license (GPL), as long as the above copyright - notice and the licensing information contained herein are preserved. - - Please refer to www.gnu.org for licensing details. - - This work is provided AS IS, without warranty of any kind, expressed or - implied, including but not limited to the warranties of merchantibility, - noninfringement, and fitness for a specific purpose. The author will not - be held liable for any damage caused by this work or derivatives of it. - - By using this source code, you agree to the licensing terms as stated - above. - - - Please contact the maintainer for bug reports or inquiries. - - Current Maintainer: - - Walter van Niftrik - -***************************************************************************/ - -#include "../mixer.h" -#include -#include -#include -#include - -#define FEED_MODE_ALIVE 0 -#define FEED_MODE_IDLE 1 -#define FEED_MODE_DIEING 2 -#define FEED_MODE_DEAD 3 -#define FEED_MODE_RESTART 4 - -typedef struct feed_state { - /* Queue entry. */ - TAILQ_ENTRY(feed_state) entry; - - /* Whether feed is alive or dead. */ - int mode; - - /* Blank gap in frames. */ - int gap; - - /* Stream handle. */ - snd_stream_hnd_t handle; - - /* Feed. */ - sfx_pcm_feed_t *feed; - - /* Timestamp of next frame requested by stream driver. */ - sfx_timestamp_t time; -} feed_state_t; - -TAILQ_HEAD(feed_list, feed_state) feeds; - -/* Buffer size in samples. */ -#define BUF_SIZE 0x4000 - -static char buf[BUF_SIZE * 2]; - -static feed_state_t * -find_feed_state(snd_stream_hnd_t hnd) -{ - feed_state_t *state; - TAILQ_FOREACH(state, &feeds, entry) { - if (state->handle == hnd) - return state; - } - - return NULL; -} - -static void -query_timestamp(feed_state_t *state) -{ - sfx_pcm_feed_t *feed = state->feed; - - if (feed->get_timestamp) { - sfx_timestamp_t stamp; - int val = feed->get_timestamp(feed, &stamp); - - switch (val) { - case PCM_FEED_TIMESTAMP: - state->gap = sfx_timestamp_frame_diff(stamp, state->time); - - if (state->gap >= 0) - state->mode = FEED_MODE_ALIVE; - else { - long secs, usecs; - - state->mode = FEED_MODE_RESTART; - sci_gettime(&secs, &usecs); - state->time = sfx_new_timestamp(secs, usecs, feed->conf.rate); - state->gap = sfx_timestamp_frame_diff(stamp, state->time); - - if (state->gap < 0) - state->gap = 0; - } - break; - case PCM_FEED_IDLE: - state->mode = FEED_MODE_IDLE; - break; - case PCM_FEED_EMPTY: - state->mode = FEED_MODE_DIEING; - state->gap = BUF_SIZE; - } - } else { - state->mode = FEED_MODE_DIEING; - state->gap = BUF_SIZE; - } -} - -void -U8_to_S16(char *buf, int frames, int stereo) -{ - int samples = frames * (stereo ? 2 : 1); - int i; - - for (i = samples - 1; i >= 0; i--) { - buf[i * 2 + 1] = (unsigned char) buf[i] - 128; - buf[i * 2] = 0; - } -} - -static void * -callback(snd_stream_hnd_t hnd, sfx_timestamp_t timestamp, int bytes_req, int *bytes_recv) -{ - feed_state_t *state = find_feed_state(hnd); - sfx_pcm_feed_t *feed; - int channels, frames_req; - int frames_recv = 0; - - assert(state); - - state->time = timestamp; - feed = state->feed; - channels = feed->conf.stereo == SFX_PCM_MONO ? 1 : 2; - frames_req = bytes_req / 2 / channels; - - while (frames_req != frames_recv) { - int frames_left = frames_req - frames_recv; - char *buf_pos = buf + frames_recv * channels * 2; - - if (state->mode == FEED_MODE_IDLE) - query_timestamp(state); - - if (state->mode == FEED_MODE_IDLE) { - memset(buf_pos, 0, frames_left * channels * 2); - - state->time = sfx_timestamp_add(state->time, frames_left); - break; - } - - if (state->gap) { - int frames = state->gap; - - if (frames > frames_left) - frames = frames_left; - - memset(buf_pos, 0, frames * channels * 2); - - state->gap -= frames; - frames_recv += frames; - state->time = sfx_timestamp_add(state->time, frames); - if (!state->gap && state->mode == FEED_MODE_DIEING) { - state->mode = FEED_MODE_DEAD; - break; - } - } else { - int frames = feed->poll(feed, buf_pos, frames_left); - - if (feed->conf.format == SFX_PCM_FORMAT_U8) - U8_to_S16(buf_pos, frames, feed->conf.stereo != SFX_PCM_MONO); - - frames_recv += frames; - state->time = sfx_timestamp_add(state->time, frames); - - if (frames < frames_left) - query_timestamp(state); - } - } - - *bytes_recv = bytes_req; - return buf; -} - -static int -mix_init(sfx_pcm_mixer_t *self, sfx_pcm_device_t *device) -{ - if (snd_stream_init() < 0) { - fprintf(stderr, "[dc-mixer] Failed to initialize streaming sound driver\n"); - return SFX_ERROR; - } - - TAILQ_INIT(&feeds); - - return SFX_OK; -} - -static void -mix_subscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) -{ - feed_state_t *state = sci_malloc(sizeof(feed_state_t)); - long secs, usecs; - - if ((feed->conf.format != SFX_PCM_FORMAT_S16_LE) && - (feed->conf.format != SFX_PCM_FORMAT_U8)) { - fprintf(stderr, "[dc-mixer] Unsupported feed format\n"); - feed->destroy(feed); - return; - } - - state->handle = snd_stream_alloc(callback, BUF_SIZE); - - if (state->handle == SND_STREAM_INVALID) { - fprintf(stderr, "[dc-mixer] Failed to allocate stream handle\n"); - feed->destroy(feed); - return; - } - - feed->frame_size = SFX_PCM_FRAME_SIZE(feed->conf); - state->mode = FEED_MODE_ALIVE; - state->feed = feed; - state->gap = 0; - - TAILQ_INSERT_TAIL(&feeds, state, entry); - - sci_gettime(&secs, &usecs); - state->time = sfx_new_timestamp(secs, usecs, feed->conf.rate); - snd_stream_start(state->handle, feed->conf.rate, - feed->conf.stereo != SFX_PCM_MONO); -} - -static void -mix_exit(sfx_pcm_mixer_t *self) -{ - snd_stream_shutdown(); -} - -static int -mix_process(sfx_pcm_mixer_t *self) -{ - feed_state_t *state, *state_next; - - TAILQ_FOREACH(state, &feeds, entry) { - snd_stream_poll(state->handle); - } - - state = TAILQ_FIRST(&feeds); - while (state) { - state_next = TAILQ_NEXT(state, entry); - if (state->mode == FEED_MODE_DEAD) { - snd_stream_stop(state->handle); - snd_stream_destroy(state->handle); - state->feed->destroy(state->feed); - TAILQ_REMOVE(&feeds, state, entry); - } - else if (state->mode == FEED_MODE_RESTART) { - snd_stream_stop(state->handle); - snd_stream_start(state->handle, state->feed->conf.rate, - state->feed->conf.stereo != SFX_PCM_MONO); - state->mode = FEED_MODE_ALIVE; - } - state = state_next; - } - - return SFX_OK; -} - -static void -mix_pause(sfx_pcm_mixer_t *self) -{ -} - -static void -mix_resume(sfx_pcm_mixer_t *self) -{ -} - -static int -pcm_init(sfx_pcm_device_t *self) -{ - return SFX_OK; -} - -static void -pcm_exit(sfx_pcm_device_t *self) -{ -} - -sfx_pcm_device_t sfx_pcm_driver_dc = { - "dc", - "0.1", - - pcm_init, - pcm_exit, - NULL, - NULL, - NULL, - - {0, 0, 0}, - 0, - NULL, - NULL -}; - -sfx_pcm_mixer_t sfx_pcm_mixer_dc = { - "dc", - "0.1", - - mix_init, - mix_exit, - mix_subscribe, - mix_pause, - mix_resume, - mix_process, - - 0, - 0, - NULL, - NULL, - NULL -}; diff --git a/engines/sci/sfx/mixer/dc.cpp b/engines/sci/sfx/mixer/dc.cpp new file mode 100644 index 0000000000..52b3ab5cbb --- /dev/null +++ b/engines/sci/sfx/mixer/dc.cpp @@ -0,0 +1,329 @@ +/*************************************************************************** + dc.c Copyright (C) 2005 Walter van Niftrik + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Walter van Niftrik + +***************************************************************************/ + +#include "../mixer.h" +#include +#include +#include +#include + +#define FEED_MODE_ALIVE 0 +#define FEED_MODE_IDLE 1 +#define FEED_MODE_DIEING 2 +#define FEED_MODE_DEAD 3 +#define FEED_MODE_RESTART 4 + +typedef struct feed_state { + /* Queue entry. */ + TAILQ_ENTRY(feed_state) entry; + + /* Whether feed is alive or dead. */ + int mode; + + /* Blank gap in frames. */ + int gap; + + /* Stream handle. */ + snd_stream_hnd_t handle; + + /* Feed. */ + sfx_pcm_feed_t *feed; + + /* Timestamp of next frame requested by stream driver. */ + sfx_timestamp_t time; +} feed_state_t; + +TAILQ_HEAD(feed_list, feed_state) feeds; + +/* Buffer size in samples. */ +#define BUF_SIZE 0x4000 + +static char buf[BUF_SIZE * 2]; + +static feed_state_t * +find_feed_state(snd_stream_hnd_t hnd) +{ + feed_state_t *state; + TAILQ_FOREACH(state, &feeds, entry) { + if (state->handle == hnd) + return state; + } + + return NULL; +} + +static void +query_timestamp(feed_state_t *state) +{ + sfx_pcm_feed_t *feed = state->feed; + + if (feed->get_timestamp) { + sfx_timestamp_t stamp; + int val = feed->get_timestamp(feed, &stamp); + + switch (val) { + case PCM_FEED_TIMESTAMP: + state->gap = sfx_timestamp_frame_diff(stamp, state->time); + + if (state->gap >= 0) + state->mode = FEED_MODE_ALIVE; + else { + long secs, usecs; + + state->mode = FEED_MODE_RESTART; + sci_gettime(&secs, &usecs); + state->time = sfx_new_timestamp(secs, usecs, feed->conf.rate); + state->gap = sfx_timestamp_frame_diff(stamp, state->time); + + if (state->gap < 0) + state->gap = 0; + } + break; + case PCM_FEED_IDLE: + state->mode = FEED_MODE_IDLE; + break; + case PCM_FEED_EMPTY: + state->mode = FEED_MODE_DIEING; + state->gap = BUF_SIZE; + } + } else { + state->mode = FEED_MODE_DIEING; + state->gap = BUF_SIZE; + } +} + +void +U8_to_S16(char *buf, int frames, int stereo) +{ + int samples = frames * (stereo ? 2 : 1); + int i; + + for (i = samples - 1; i >= 0; i--) { + buf[i * 2 + 1] = (unsigned char) buf[i] - 128; + buf[i * 2] = 0; + } +} + +static void * +callback(snd_stream_hnd_t hnd, sfx_timestamp_t timestamp, int bytes_req, int *bytes_recv) +{ + feed_state_t *state = find_feed_state(hnd); + sfx_pcm_feed_t *feed; + int channels, frames_req; + int frames_recv = 0; + + assert(state); + + state->time = timestamp; + feed = state->feed; + channels = feed->conf.stereo == SFX_PCM_MONO ? 1 : 2; + frames_req = bytes_req / 2 / channels; + + while (frames_req != frames_recv) { + int frames_left = frames_req - frames_recv; + char *buf_pos = buf + frames_recv * channels * 2; + + if (state->mode == FEED_MODE_IDLE) + query_timestamp(state); + + if (state->mode == FEED_MODE_IDLE) { + memset(buf_pos, 0, frames_left * channels * 2); + + state->time = sfx_timestamp_add(state->time, frames_left); + break; + } + + if (state->gap) { + int frames = state->gap; + + if (frames > frames_left) + frames = frames_left; + + memset(buf_pos, 0, frames * channels * 2); + + state->gap -= frames; + frames_recv += frames; + state->time = sfx_timestamp_add(state->time, frames); + if (!state->gap && state->mode == FEED_MODE_DIEING) { + state->mode = FEED_MODE_DEAD; + break; + } + } else { + int frames = feed->poll(feed, buf_pos, frames_left); + + if (feed->conf.format == SFX_PCM_FORMAT_U8) + U8_to_S16(buf_pos, frames, feed->conf.stereo != SFX_PCM_MONO); + + frames_recv += frames; + state->time = sfx_timestamp_add(state->time, frames); + + if (frames < frames_left) + query_timestamp(state); + } + } + + *bytes_recv = bytes_req; + return buf; +} + +static int +mix_init(sfx_pcm_mixer_t *self, sfx_pcm_device_t *device) +{ + if (snd_stream_init() < 0) { + fprintf(stderr, "[dc-mixer] Failed to initialize streaming sound driver\n"); + return SFX_ERROR; + } + + TAILQ_INIT(&feeds); + + return SFX_OK; +} + +static void +mix_subscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) +{ + feed_state_t *state = sci_malloc(sizeof(feed_state_t)); + long secs, usecs; + + if ((feed->conf.format != SFX_PCM_FORMAT_S16_LE) && + (feed->conf.format != SFX_PCM_FORMAT_U8)) { + fprintf(stderr, "[dc-mixer] Unsupported feed format\n"); + feed->destroy(feed); + return; + } + + state->handle = snd_stream_alloc(callback, BUF_SIZE); + + if (state->handle == SND_STREAM_INVALID) { + fprintf(stderr, "[dc-mixer] Failed to allocate stream handle\n"); + feed->destroy(feed); + return; + } + + feed->frame_size = SFX_PCM_FRAME_SIZE(feed->conf); + state->mode = FEED_MODE_ALIVE; + state->feed = feed; + state->gap = 0; + + TAILQ_INSERT_TAIL(&feeds, state, entry); + + sci_gettime(&secs, &usecs); + state->time = sfx_new_timestamp(secs, usecs, feed->conf.rate); + snd_stream_start(state->handle, feed->conf.rate, + feed->conf.stereo != SFX_PCM_MONO); +} + +static void +mix_exit(sfx_pcm_mixer_t *self) +{ + snd_stream_shutdown(); +} + +static int +mix_process(sfx_pcm_mixer_t *self) +{ + feed_state_t *state, *state_next; + + TAILQ_FOREACH(state, &feeds, entry) { + snd_stream_poll(state->handle); + } + + state = TAILQ_FIRST(&feeds); + while (state) { + state_next = TAILQ_NEXT(state, entry); + if (state->mode == FEED_MODE_DEAD) { + snd_stream_stop(state->handle); + snd_stream_destroy(state->handle); + state->feed->destroy(state->feed); + TAILQ_REMOVE(&feeds, state, entry); + } + else if (state->mode == FEED_MODE_RESTART) { + snd_stream_stop(state->handle); + snd_stream_start(state->handle, state->feed->conf.rate, + state->feed->conf.stereo != SFX_PCM_MONO); + state->mode = FEED_MODE_ALIVE; + } + state = state_next; + } + + return SFX_OK; +} + +static void +mix_pause(sfx_pcm_mixer_t *self) +{ +} + +static void +mix_resume(sfx_pcm_mixer_t *self) +{ +} + +static int +pcm_init(sfx_pcm_device_t *self) +{ + return SFX_OK; +} + +static void +pcm_exit(sfx_pcm_device_t *self) +{ +} + +sfx_pcm_device_t sfx_pcm_driver_dc = { + "dc", + "0.1", + + pcm_init, + pcm_exit, + NULL, + NULL, + NULL, + + {0, 0, 0}, + 0, + NULL, + NULL +}; + +sfx_pcm_mixer_t sfx_pcm_mixer_dc = { + "dc", + "0.1", + + mix_init, + mix_exit, + mix_subscribe, + mix_pause, + mix_resume, + mix_process, + + 0, + 0, + NULL, + NULL, + NULL +}; diff --git a/engines/sci/sfx/mixer/mixers.c b/engines/sci/sfx/mixer/mixers.c deleted file mode 100644 index 997525f173..0000000000 --- a/engines/sci/sfx/mixer/mixers.c +++ /dev/null @@ -1,55 +0,0 @@ -/*************************************************************************** - mixers.c Copyright (C) 2003 Christoph Reichenbach - - - This program may be modified and copied freely according to the terms of - the GNU general public license (GPL), as long as the above copyright - notice and the licensing information contained herein are preserved. - - Please refer to www.gnu.org for licensing details. - - This work is provided AS IS, without warranty of any kind, expressed or - implied, including but not limited to the warranties of merchantibility, - noninfringement, and fitness for a specific purpose. The author will not - be held liable for any damage caused by this work or derivatives of it. - - By using this source code, you agree to the licensing terms as stated - above. - - - Please contact the maintainer for bug reports or inquiries. - - Current Maintainer: - - Christoph Reichenbach (CR) - -***************************************************************************/ - -#include "../mixer.h" -#include "sci/include/resource.h" - -extern sfx_pcm_mixer_t sfx_pcm_mixer_soft_linear; - -#ifdef _DREAMCAST -extern sfx_pcm_mixer_t sfx_pcm_mixer_dc; -#endif - -static sfx_pcm_mixer_t *mixers[] = { -#ifdef _DREAMCAST - &sfx_pcm_mixer_dc, -#endif - &sfx_pcm_mixer_soft_linear, - NULL -}; - -sfx_pcm_mixer_t * -sfx_pcm_find_mixer(char *name) -{ - int i = 0; - - if (name) - while (mixers[i] && strcmp(name, mixers[i]->name)) - ++i; - - return mixers[i]; -} diff --git a/engines/sci/sfx/mixer/mixers.cpp b/engines/sci/sfx/mixer/mixers.cpp new file mode 100644 index 0000000000..997525f173 --- /dev/null +++ b/engines/sci/sfx/mixer/mixers.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** + mixers.c Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include "../mixer.h" +#include "sci/include/resource.h" + +extern sfx_pcm_mixer_t sfx_pcm_mixer_soft_linear; + +#ifdef _DREAMCAST +extern sfx_pcm_mixer_t sfx_pcm_mixer_dc; +#endif + +static sfx_pcm_mixer_t *mixers[] = { +#ifdef _DREAMCAST + &sfx_pcm_mixer_dc, +#endif + &sfx_pcm_mixer_soft_linear, + NULL +}; + +sfx_pcm_mixer_t * +sfx_pcm_find_mixer(char *name) +{ + int i = 0; + + if (name) + while (mixers[i] && strcmp(name, mixers[i]->name)) + ++i; + + return mixers[i]; +} diff --git a/engines/sci/sfx/mixer/soft.c b/engines/sci/sfx/mixer/soft.c deleted file mode 100644 index d71567a209..0000000000 --- a/engines/sci/sfx/mixer/soft.c +++ /dev/null @@ -1,988 +0,0 @@ -/*************************************************************************** - mixer.c Copyright (C) 2003 Christoph Reichenbach - - - This program may be modified and copied freely according to the terms of - the GNU general public license (GPL), as long as the above copyright - notice and the licensing information contained herein are preserved. - - Please refer to www.gnu.org for licensing details. - - This work is provided AS IS, without warranty of any kind, expressed or - implied, including but not limited to the warranties of merchantibility, - noninfringement, and fitness for a specific purpose. The author will not - be held liable for any damage caused by this work or derivatives of it. - - By using this source code, you agree to the licensing terms as stated - above. - - - Please contact the maintainer for bug reports or inquiries. - - Current Maintainer: - - Christoph Reichenbach (CR) - -***************************************************************************/ - -#include "../mixer.h" -#include "sci/include/sci_memory.h" - -/* Max. number of microseconds in difference allowed between independent audio streams */ -#define TIMESTAMP_MAX_ALLOWED_DELTA 2000 - -/*#define DEBUG 3*/ -/* Set DEBUG to one of the following: -** anything -- high-level debugging (feed subscriptions/deletions etc.) -** >= 1 -- rough input and output analysis (once per call) -** >= 2 -- more detailed input analysis (once per call and feed) -** >= 3 -- fully detailed input and output analysis (once per frame and feed) -*/ - -/*#define DEBUG 0*/ - -#define MIN_DELTA_OBSERVATIONS 100 /* Number of times the mixer is called before it starts trying to improve latency */ -#define MAX_DELTA_OBSERVATIONS 1000000 /* Number of times the mixer is called before we assume we truly understand timing */ - -static int diagnosed_too_slow = 0; - -static volatile int mixer_lock = 0; - -/*#define DEBUG_LOCKS*/ -#ifdef DEBUG_LOCKS -# define DEBUG_ACQUIRE fprintf(stderr, "[ -LOCK -] ACKQ %d: %d\n", __LINE__, mixer_lock) -# define DEBUG_WAIT fprintf(stderr, "[ -LOCK -] WAIT %d: %d\n", __LINE__, mixer_lock); -# define DEBUG_RELEASE ; fprintf(stderr, "[ -LOCK -] REL %d: %d\n", __LINE__, mixer_lock); -#else -# define DEBUG_ACQUIRE -# define DEBUG_WAIT -# define DEBUG_RELEASE -#endif - -#define ACQUIRE_LOCK() ++mixer_lock; while (mixer_lock != 1) { DEBUG_WAIT sci_sched_yield(); } DEBUG_ACQUIRE -#define RELEASE_LOCK() --mixer_lock DEBUG_RELEASE - -struct mixer_private { - byte *outbuf; /* Output buffer to write to the PCM device next time */ - sfx_timestamp_t outbuf_timestamp; /* Timestamp associated with the output buffer */ - int have_outbuf_timestamp; /* Whether we really _have_ an associated timestamp */ - byte *writebuf; /* Buffer we're supposed to write to */ - gint32 *compbuf_l, *compbuf_r; /* Intermediate buffers for computation */ - int lastbuf_len; /* Number of frames stored in the last buffer */ - - long skew; /* Millisecond relative to which we compute time. This is the millisecond - ** part of the first time we emitted sound, to simplify some computations. */ - long lsec; /* Last point in time we updated buffers, if any (seconds since the epoch) */ - int played_this_second; /* Number of frames emitted so far in second lsec */ - - int max_delta; /* maximum observed time delta (using 'frames' as a metric unit) */ - int delta_observations; /* Number of times we played; confidence measure for max_delta */ - - /* Pause data */ - int paused; -}; - -#define P ((struct mixer_private *)(self->private_bits)) - - -static int -mix_init(sfx_pcm_mixer_t *self, sfx_pcm_device_t *device) -{ - self->dev = device; - self->private_bits /* = P */ = sci_malloc(sizeof(struct mixer_private)); - P->outbuf = P->writebuf = NULL; - P->lastbuf_len = 0; - P->compbuf_l = (gint32*)sci_malloc(sizeof(gint32) * device->buf_size); - P->compbuf_r = (gint32*)sci_malloc(sizeof(gint32) * device->buf_size); - P->played_this_second = 0; - P->paused = 0; -#ifdef DEBUG - sciprintf("[soft-mixer] Initialised device %s v%s (%d Hz, %d/%x)\n", - device->name, device->version, - device->conf.rate, device->conf.stereo, device->conf.format); -#endif - return SFX_OK; -} - -static inline unsigned int -gcd(unsigned int a, unsigned int b) -{ - if (a == b) - return a; - - if (a < b) { - unsigned int c = b % a; - - if (!c) - return a; - - return gcd(c, a); - } else - return gcd(b, a); -} - -static sfx_pcm_urat_t -urat(unsigned int nom, unsigned int denom) -{ - sfx_pcm_urat_t rv; - unsigned int g; - - rv.val = nom / denom; - nom -= rv.val * denom; - if (nom == 0) - g = 1; - else - g = gcd(nom, denom); - - rv.nom = nom / g; - rv.den = denom / g; - - return rv; -} - -static void -mix_subscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) -{ - sfx_pcm_feed_state_t *fs; - ACQUIRE_LOCK(); - if (!self->feeds) { - self->feeds_allocd = 2; - self->feeds = (sfx_pcm_feed_state_t*)sci_malloc(sizeof(sfx_pcm_feed_state_t) - * self->feeds_allocd); - } else if (self->feeds_allocd == self->feeds_nr) { - self->feeds_allocd += 2; - self->feeds = (sfx_pcm_feed_state_t*)sci_realloc(self->feeds, - sizeof(sfx_pcm_feed_state_t) - * self->feeds_allocd); - } - - fs = self->feeds + self->feeds_nr++; - fs->feed = feed; - - feed->frame_size = SFX_PCM_FRAME_SIZE(feed->conf); - - /* fs->buf_size = (self->dev->buf_size - * (feed->conf.rate - + self->dev->conf.rate - 1)) - / self->dev->conf.rate; - */ - /* For the sake of people without 64 bit CPUs: */ - fs->buf_size = 2 + /* Additional safety */ - (self->dev->buf_size * - (1 + (feed->conf.rate / self->dev->conf.rate))); -fprintf(stderr, " ---> %d/%d/%d/%d = %d\n", - self->dev->buf_size, - feed->conf.rate, - self->dev->conf.rate, - feed->frame_size, - fs->buf_size); - - fs->buf = (byte*)sci_malloc(fs->buf_size * feed->frame_size); -fprintf(stderr, " ---> --> %d for %p at %p\n", fs->buf_size * feed->frame_size, (void *)fs, (void *)fs->buf); -{int i; for (i = 0; i < fs->buf_size * feed->frame_size; i++) -fs->buf[i] = 0xa5; } - fs->scount = urat(0, 1); - fs->spd = urat(feed->conf.rate, self->dev->conf.rate); - fs->scount.den = fs->spd.den; - fs->ch_old.left = 0; - fs->ch_old.right = 0; - fs->ch_new.left = 0; - fs->ch_new.right = 0; - fs->mode = SFX_PCM_FEED_MODE_ALIVE; - - /* If the feed can't provide us with timestamps, we don't need to wait for it to do so */ - fs->pending_review = (feed->get_timestamp)? 1 : 0; - - fs->frame_bufstart = 0; - -#ifdef DEBUG - sciprintf("[soft-mixer] Subscribed %s-%x (%d Hz, %d/%x) at %d+%d/%d, buffer size %d\n", - feed->debug_name, feed->debug_nr, feed->conf.rate, feed->conf.stereo, feed->conf.format, - fs->spd.val, fs->spd.nom, fs->spd.den, fs->buf_size); -#endif - RELEASE_LOCK(); -} - - -static void -_mix_unsubscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) -{ - int i; -#ifdef DEBUG - sciprintf("[soft-mixer] Unsubscribing %s-%x\n", feed->debug_name, feed->debug_nr); -#endif - for (i = 0; i < self->feeds_nr; i++) { - sfx_pcm_feed_state_t *fs = self->feeds + i; - - if (fs->feed == feed) { - feed->destroy(feed); - - if (fs->buf) - sci_free(fs->buf); - - self->feeds_nr--; - - /* Copy topmost into deleted so that we don't have any holes */ - if (i != self->feeds_nr) - self->feeds[i] = self->feeds[self->feeds_nr]; - - if (self->feeds_allocd > 8 && self->feeds_allocd > (self->feeds_nr << 1)) { - /* Limit memory waste */ - self->feeds_allocd >>= 1; - self->feeds - = (sfx_pcm_feed_state_t*)sci_realloc(self->feeds, - sizeof(sfx_pcm_feed_state_t) - * self->feeds_allocd); - } - - for (i = 0; i < self->feeds_nr; i++) - fprintf(stderr, " Feed #%d: %s-%x\n", - i, self->feeds[i].feed->debug_name, - self->feeds[i].feed->debug_nr); - - return; - } - } - - fprintf(stderr, "[sfx-mixer] Assertion failed: Deleting invalid feed %p out of %d\n", - (void *)feed, self->feeds_nr); - - BREAKPOINT(); -} - -static void -mix_unsubscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) -{ - ACQUIRE_LOCK(); - _mix_unsubscribe(self, feed); - RELEASE_LOCK(); -} - - -static void -mix_exit(sfx_pcm_mixer_t *self) -{ - ACQUIRE_LOCK(); - while (self->feeds_nr) - _mix_unsubscribe(self, self->feeds[0].feed); - - if (P->outbuf) - sci_free(P->outbuf); - if (P->writebuf) - sci_free(P->writebuf); - - if (P->compbuf_l) - sci_free(P->compbuf_l); - if (P->compbuf_l) - sci_free(P->compbuf_r); - - sci_free(P); - self->private_bits /* = P */ = NULL; - RELEASE_LOCK(); - -#ifdef DEBUG - sciprintf("[soft-mixer] Uninitialising mixer\n"); -#endif -} - - -#define LIMIT_16_BITS(v) \ - if (v < -32767) \ - v = -32768; \ - else if (v > 32766) \ - v = 32767 - -static inline void -mix_compute_output(sfx_pcm_mixer_t *self, int outplen) -{ - int frame_i; - sfx_pcm_config_t conf = self->dev->conf; - int use_16 = conf.format & SFX_PCM_FORMAT_16; - int bias = conf.format & ~SFX_PCM_FORMAT_LMASK; - byte *lchan, *rchan = NULL; - /* Don't see how this could possibly wind up being - ** used w/o initialisation, but you never know... */ - gint32 *lsrc = P->compbuf_l; - gint32 *rsrc = P->compbuf_r; - int frame_size = SFX_PCM_FRAME_SIZE(conf); - - - if (!P->writebuf) - P->writebuf = (byte*)sci_malloc(self->dev->buf_size * frame_size + 4); - - if (conf.stereo) { - if (conf.stereo == SFX_PCM_STEREO_RL) { - lchan = P->writebuf + ((use_16)? 2 : 1); - rchan = P->writebuf; - } else { - lchan = P->writebuf; - rchan = P->writebuf + ((use_16)? 2 : 1); - } - } else - lchan = P->writebuf; - - - for (frame_i = 0; frame_i < outplen; frame_i++) { - int left = *lsrc++; - int right = *rsrc++; - - if (conf.stereo) { - LIMIT_16_BITS(left); - LIMIT_16_BITS(right); - - if (!use_16) { - left >>= 8; - right >>= 8; - } - - left += bias; - right += bias; - - if (use_16) { - if (SFX_PCM_FORMAT_LE == (conf.format & SFX_PCM_FORMAT_ENDIANNESS)) { - lchan[0] = left & 0xff; - lchan[1] = (left >> 8) & 0xff; - rchan[0] = right & 0xff; - rchan[1] = (right >> 8) & 0xff; - } else { - lchan[1] = left & 0xff; - lchan[0] = (left >> 8) & 0xff; - rchan[1] = right & 0xff; - rchan[0] = (right >> 8) & 0xff; - } - - lchan += 4; - rchan += 4; - } else { - *lchan = left & 0xff; - *rchan = right & 0xff; - - lchan += 2; - rchan += 2; - } - - } else { - left += right; - left >>= 1; - LIMIT_16_BITS(left); - if (!use_16) - left >>= 8; - - left += bias; - - if (use_16) { - if (SFX_PCM_FORMAT_LE == (conf.format & SFX_PCM_FORMAT_ENDIANNESS)) { - lchan[0] = left & 0xff; - lchan[1] = (left >> 8) & 0xff; - } else { - lchan[1] = left & 0xff; - lchan[0] = (left >> 8) & 0xff; - } - - lchan += 2; - } else { - *lchan = left & 0xff; - lchan += 1; - } - } - } -} - -static inline void -mix_swap_buffers(sfx_pcm_mixer_t *self) -{ /* Swap buffers */ - byte *tmp = P->outbuf; - P->outbuf = P->writebuf; - P->writebuf = tmp; -} - - -#define FRAME_OFFSET(usecs) \ - ((usecs >> 7) /* approximate, since uint32 is too small */ \ - * ((long) self->dev->conf.rate)) \ - / (1000000L >> 7) - -static inline int -mix_compute_buf_len(sfx_pcm_mixer_t *self, int *skip_frames) - /* Computes the number of frames we ought to write. It tries to minimise the number, - ** in order to reduce latency. */ - /* It sets 'skip_frames' to the number of frames to assume lost by latency, effectively - ** skipping them. */ -{ - int free_frames; - int played_frames = 0; /* since the last call */ - long secs, usecs; - int frame_pos; - int result_frames; - - sci_gettime(&secs, &usecs); - - if (!P->outbuf) { - /* Called for the first time ever? */ - P->skew = usecs; - P->lsec = secs; - P->max_delta = 0; - P->delta_observations = 0; - P->played_this_second = 0; - *skip_frames = 0; - return self->dev->buf_size; - } - - /* fprintf(stderr, "[%d:%d]S%d ", secs, usecs, P->skew);*/ - - if (P->skew > usecs) { - secs--; - usecs += (1000000 - P->skew); - } - else - usecs -= P->skew; - - frame_pos = FRAME_OFFSET(usecs); - - played_frames = frame_pos - P->played_this_second - + ((secs - P->lsec) * self->dev->conf.rate); - /* - fprintf(stderr, "%d:%d - %d:%d => %d\n", secs, frame_pos, - P->lsec, P->played_this_second, played_frames); - */ - - if (played_frames > self->dev->buf_size) - played_frames = self->dev->buf_size; - - /* - fprintf(stderr, "Between %d:? offset=%d and %d:%d offset=%d: Played %d at %d\n", P->lsec, P->played_this_second, - secs, usecs, frame_pos, played_frames, self->dev->conf.rate); - */ - - - if (played_frames > P->max_delta) - P->max_delta = played_frames; - - free_frames = played_frames; - - if (free_frames > self->dev->buf_size) { - if (!diagnosed_too_slow) { - sciprintf("[sfx-mixer] Your timer is too slow for your PCM output device (%d/%d), free=%d.\n" - "[sfx-mixer] You might want to try changing the device, timer, or mixer, if possible.\n", - played_frames, self->dev->buf_size, free_frames); - } - diagnosed_too_slow = 1; - - *skip_frames = free_frames - self->dev->buf_size; - free_frames = self->dev->buf_size; - } else - *skip_frames = 0; - - ++P->delta_observations; - if (P->delta_observations > MAX_DELTA_OBSERVATIONS) - P->delta_observations = MAX_DELTA_OBSERVATIONS; - -/* /\* Disabled, broken *\/ */ -/* if (0 && P->delta_observations > MIN_DELTA_OBSERVATIONS) { /\* Start improving after a while *\/ */ -/* int diff = self->dev->conf.rate - P->max_delta; */ - -/* /\* log-approximate P->max_delta over time *\/ */ -/* recommended_frames = P->max_delta + */ -/* ((diff * MIN_DELTA_OBSERVATIONS) / P->delta_observations); */ -/* /\* WTF? *\/ */ -/* } else */ -/* recommended_frames = self->dev->buf_size; /\* Initially, keep the buffer full *\/ */ - -#if (DEBUG >= 1) - sciprintf("[soft-mixer] played since last time: %d, recommended: %d, free: %d\n", - played_frames, recommended_frames, free_frames); -#endif - - result_frames = free_frames; - - if (result_frames < 0) - result_frames = 0; - - P->played_this_second += result_frames; - while (P->played_this_second >= self->dev->conf.rate) { - /* Won't normally happen more than once */ - P->played_this_second -= self->dev->conf.rate; - P->lsec++; - } - - if (result_frames > self->dev->buf_size) { - fprintf(stderr, "[soft-mixer] Internal assertion failed: frames-to-write %d > %d\n", - result_frames, self->dev->buf_size); - } - return result_frames; -} - - - -#define READ_NEW_VALUES() \ - if (frames_left > 0) { \ - if (bias) { /* unsigned data */ \ - if (!use_16) { \ - c_new.left = (*lsrc) << 8; \ - c_new.right = (*rsrc) << 8; \ - } else { \ - if (conf.format & SFX_PCM_FORMAT_LE) { \ - c_new.left = lsrc[0] | lsrc[1] << 8; \ - c_new.right = rsrc[0] | rsrc[1] << 8; \ - } else { \ - c_new.left = lsrc[1] | lsrc[0] << 8; \ - c_new.right = rsrc[1] | rsrc[0] << 8; \ - } \ - } \ - } else { /* signed data */ \ - if (!use_16) { \ - c_new.left = (*((signed char *)lsrc)) << 8; \ - c_new.right = (*((signed char *)rsrc)) << 8; \ - } else { \ - if (conf.format & SFX_PCM_FORMAT_LE) { \ - c_new.left = lsrc[0] | ((signed char *)lsrc)[1] << 8; \ - c_new.right = rsrc[0] | ((signed char *)rsrc)[1] << 8; \ - } else { \ - c_new.left = lsrc[1] | ((signed char *)lsrc)[0] << 8; \ - c_new.right = rsrc[1] | ((signed char *)rsrc)[0] << 8; \ - } \ - } \ - } \ - \ - c_new.left -= bias; \ - c_new.right -= bias; \ - \ - lsrc += frame_size; \ - rsrc += frame_size; \ - } else { \ - c_new.left = c_new.right = 0; \ - break; \ - } - - -static volatile int xx_offset; -static volatile int xx_size; - -static void -mix_compute_input_linear(sfx_pcm_mixer_t *self, int add_result, - int len, sfx_timestamp_t *ts, sfx_timestamp_t base_ts) - /* if add_result is non-zero, P->outbuf should be added to rather than overwritten. */ - /* base_ts is the timestamp for the first frame */ -{ - sfx_pcm_feed_state_t *fs = self->feeds + add_result; - sfx_pcm_feed_t *f = fs->feed; - sfx_pcm_config_t conf = f->conf; - int use_16 = conf.format & SFX_PCM_FORMAT_16; - gint32 *lchan = P->compbuf_l; - gint32 *rchan = P->compbuf_r; - int frame_size = f->frame_size; - byte *wr_dest = fs->buf + (frame_size * fs->frame_bufstart); - byte *lsrc = fs->buf; - byte *rsrc = fs->buf; - /* Location to write to */ - int frames_nr; - int bias = (conf.format & ~SFX_PCM_FORMAT_LMASK)? 0x8000 : 0; - /* We use this only on a 16 bit level here */ - - /* The two most extreme source frames we consider for a - ** destination frame */ - struct twochannel_data c_old = fs->ch_old; - struct twochannel_data c_new = fs->ch_new; - - int frames_read = 0; - int frames_left; - int write_offset; /* Iterator for translation */ - int delay_frames = 0; /* Number of frames (dest buffer) at the beginning we skip */ - - /* First, compute the number of frames we want to retreive */ - frames_nr = fs->spd.val * len; - /* A little complicated since we must consider partial frames */ - frames_nr += (fs->spd.nom * len - + (fs->scount.den - fs->scount.nom) /* remember that we may have leftovers */ - + (fs->spd.den - 1 /* round up */) - ) - / fs->spd.den; - - ts->secs = -1; - - if (frames_nr > fs->buf_size) { - fprintf(stderr, "%d (%d*%d + somethign) bytes, but only %d allowed!!!!!\n", - frames_nr * f->frame_size, - fs->spd.val, len, - fs->buf_size); - BREAKPOINT(); - } - - if (fs->pending_review) { - int newmode = PCM_FEED_EMPTY; /* empty unless a get_timestamp() tells otherwise */ - - RELEASE_LOCK(); - /* Retrieve timestamp */ - if (f->get_timestamp) - newmode = f->get_timestamp(f, ts); - ACQUIRE_LOCK(); - - fs = self->feeds + add_result; - /* Reset in case of status update */ - - switch (newmode) { - - case PCM_FEED_TIMESTAMP: { - /* Compute the number of frames the returned timestamp is in the future: */ - delay_frames = - sfx_timestamp_frame_diff(sfx_timestamp_renormalise(*ts, base_ts.frame_rate), - base_ts); - - if (delay_frames <= 0) - /* Start ASAP, even if it's too late */ - delay_frames = 0; - else - if (delay_frames > len) - delay_frames = len; - fs->pending_review = 0; - } - break; - - case PCM_FEED_EMPTY: - fs->mode = SFX_PCM_FEED_MODE_DEAD; - - /* ...fall through... */ - - case PCM_FEED_IDLE: - /* Clear audio buffer, if neccessary, and return */ - if (!add_result) { - memset(P->compbuf_l, 0, sizeof(gint32) * len); - memset(P->compbuf_r, 0, sizeof(gint32) * len); - } - return; - - default: - fprintf(stderr, "[soft-mixer] Fatal: Invalid mode returned by PCM feed %s-%d's get_timestamp(): %d\n", - f->debug_name, f->debug_nr, newmode); - exit(1); - } - } - - RELEASE_LOCK(); - /* Make sure we have sufficient information */ - if (frames_nr > delay_frames + fs->frame_bufstart) - frames_read = - f->poll(f, wr_dest, - frames_nr - - delay_frames - - fs->frame_bufstart); - - ACQUIRE_LOCK(); - fs = self->feeds + add_result; - - frames_read += fs->frame_bufstart; - frames_left = frames_read; - - /* Reset in case of status update */ - - /* Skip at the beginning: */ - if (delay_frames) { - if (!add_result) { - memset(lchan, 0, sizeof(gint32) * delay_frames); - memset(rchan, 0, sizeof(gint32) * delay_frames); - } - lchan += delay_frames; - rchan += delay_frames; - - len -= delay_frames; - } - - -#if (DEBUG >= 2) - sciprintf("[soft-mixer] Examining %s-%x (frame size %d); read %d/%d/%d, re-using %d frames\n", - f->debug_name, f->debug_nr, frame_size, frames_read, frames_nr, - fs->buf_size, fs->frame_bufstart); -#endif - - - if (conf.stereo == SFX_PCM_STEREO_LR) - rsrc += (use_16)? 2 : 1; - else if (conf.stereo == SFX_PCM_STEREO_RL) - lsrc += (use_16)? 2 : 1; - /* Otherwise, we let both point to the same place */ - -#if (DEBUG >= 2) - sciprintf("[soft-mixer] Stretching theoretical %d (physical %d) results to %d\n", frames_nr, frames_left, len); -#endif - for (write_offset = 0; write_offset < len; write_offset++) { - int leftsum = 0; /* Sum of any complete frames we used */ - int rightsum = 0; - - int left; /* Sum of the two most extreme source frames - ** we considered, i.e. the oldest and newest - ** one corresponding to the output frame we are - ** computing */ - int right; - - int frame_steps = fs->spd.val; - int j; - - if (fs->scount.nom >= fs->scount.den) { - fs->scount.nom -= fs->scount.den; /* Ensure fractional part < 1 */ - ++frame_steps; - } - if (frame_steps) - c_old = c_new; - -#if 0 - if (write_offset == 0) { - READ_NEW_VALUES(); - --frames_left; -#if (DEBUG >= 3) - sciprintf("[soft-mixer] Initial read %d:%d\n", c_new.left, c_new.right); -#endif - c_old = c_new; - } -#endif - - for (j = 0; j < frame_steps; j++) { - READ_NEW_VALUES(); - --frames_left; -#if (DEBUG >= 3) - sciprintf("[soft-mixer] Step %d/%d made %d:%d\n", j, frame_steps, c_new.left, c_new.right); -#endif - - /* The last frame will be subject to the fractional - ** part analysis, so we add it to 'left' and 'right' - ** later-- all others are added to (leftsum, rightsum). - */ - if (j+1 < frame_steps) { - leftsum += c_new.left; - rightsum += c_new.right; - } - } - - left = c_new.left * fs->scount.nom - + c_old.left * (fs->scount.den - fs->scount.nom); - right = c_new.right * fs->scount.nom - + c_old.right * (fs->scount.den - fs->scount.nom); - - /* Normalise */ - left /= fs->spd.den; - right /= fs->spd.den; - - - leftsum += left; - rightsum += right; - - - /* Make sure to divide by the number of frames we added here */ - if (frame_steps > 1) { - leftsum /= (frame_steps); - rightsum /= (frame_steps); - } - - -#if (DEBUG >= 3) - sciprintf("[soft-mixer] Ultimate result: %d:%d (frac %d:%d)\n", leftsum, rightsum, left, right); -#endif - - if (add_result) { - *(lchan++) += leftsum; - *(rchan++) += rightsum; - } else { - *(lchan++) = leftsum; - *(rchan++) = rightsum; - } - - fs->scount.nom += fs->spd.nom; /* Count up fractional part */ - } - - fs->ch_old = c_old; - fs->ch_new = c_new; - - /* If neccessary, zero out the rest */ - if (write_offset < len && !add_result) { - memset(lchan, 0, sizeof(gint32) * (len - write_offset)); - memset(rchan, 0, sizeof(gint32) * (len - write_offset)); - } - - /* Save whether we have a partial frame still stored */ - fs->frame_bufstart = frames_left; - - if (frames_left) { - xx_offset = ((frames_read - frames_left) * f->frame_size); - xx_size = frames_left * f->frame_size; - if (xx_offset + xx_size - >= fs->buf_size * f->frame_size) { - fprintf(stderr, "offset %d >= max %d!\n", - (xx_offset + xx_size), fs->buf_size * f->frame_size); - BREAKPOINT(); - } - - memmove(fs->buf, - fs->buf + ((frames_read - frames_left) * f->frame_size), - frames_left * f->frame_size); - } -#if (DEBUG >= 2) - sciprintf("[soft-mixer] Leaving %d over\n", fs->frame_bufstart); -#endif - - if (frames_read + delay_frames < frames_nr) { - if (f->get_timestamp) /* Can resume? */ - fs->pending_review = 1; - else - fs->mode = SFX_PCM_FEED_MODE_DEAD; /* Done. */ - } -} - -static int -mix_process_linear(sfx_pcm_mixer_t *self) -{ -ACQUIRE_LOCK(); -{ - int src_i; /* source feed index counter */ - int frames_skip; /* Number of frames to discard, rather than to emit */ - int buflen = mix_compute_buf_len(self, &frames_skip); /* Compute # of frames we must compute and write */ - int fake_buflen; - int timestamp_max_delta = 0; - int have_timestamp = 0; - sfx_timestamp_t start_timestamp; /* The timestamp at which the first frame will be played */ - sfx_timestamp_t min_timestamp; - sfx_timestamp_t timestamp; - - if (self->dev->get_output_timestamp) - start_timestamp = self->dev->get_output_timestamp(self->dev); - else { - long sec, usec; - sci_gettime(&sec, &usec); - start_timestamp = sfx_new_timestamp(sec, usec, self->dev->conf.rate); - } - - if ((P->outbuf) && (P->lastbuf_len)) { - sfx_timestamp_t ts; - int rv; - - if (P->have_outbuf_timestamp) { - ts = sfx_timestamp_renormalise(P->outbuf_timestamp, self->dev->conf.rate); - } - - rv = self->dev->output(self->dev, P->outbuf, - P->lastbuf_len, - (P->have_outbuf_timestamp)? &ts : NULL); - - if (rv == SFX_ERROR) { - RELEASE_LOCK(); - return rv; /* error */ - } - } - -#if (DEBUG >= 1) - if (self->feeds_nr) - sciprintf("[soft-mixer] Mixing %d output frames on %d input feeds\n", buflen, self->feeds_nr); -#endif - if (self->feeds_nr && !P->paused) { - /* Below, we read out all feeds in case we have to skip frames first, then get the - ** most current sound. 'fake_buflen' is either the actual buflen (for the last iteration) - ** or a fraction of the buf length to discard. */ - do { - if (frames_skip) { - if (frames_skip > self->dev->buf_size) - fake_buflen = self->dev->buf_size; - else - fake_buflen = frames_skip; - - frames_skip -= fake_buflen; - } else { - fake_buflen = buflen; - frames_skip = -1; /* Mark us as being completely done */ - } - - for (src_i = 0; src_i < self->feeds_nr; src_i++) { - mix_compute_input_linear(self, src_i, - fake_buflen, ×tamp, - start_timestamp); - - if (timestamp.secs >= 0) { - if (have_timestamp) { - int diff = sfx_timestamp_usecs_diff(min_timestamp, timestamp); - if (diff > 0) { - /* New earlier timestamp */ - timestamp = min_timestamp; - timestamp_max_delta += diff; - } else if (diff > timestamp_max_delta) - timestamp_max_delta = diff; - /* New max delta for timestamp */ - } else { - min_timestamp = timestamp; - have_timestamp = 1; - } - } - } - /* Destroy all feeds we finished */ - for (src_i = 0; src_i < self->feeds_nr; src_i++) - if (self->feeds[src_i].mode == SFX_PCM_FEED_MODE_DEAD) - _mix_unsubscribe(self, self->feeds[src_i].feed); - } while (frames_skip >= 0); - - } else { /* Zero it out */ - memset(P->compbuf_l, 0, sizeof(gint32) * buflen); - memset(P->compbuf_r, 0, sizeof(gint32) * buflen); - } - -#if (DEBUG >= 1) - if (self->feeds_nr) - sciprintf("[soft-mixer] Done mixing for this session, the result will be our next output buffer\n"); -#endif - -#if (DEBUG >= 3) - if (self->feeds_nr) { - int i; - sciprintf("[soft-mixer] Intermediate representation:\n"); - for (i = 0; i < buflen; i++) - sciprintf("[soft-mixer] Offset %d:\t[%04x:%04x]\t%d:%d\n", i, - P->compbuf_l[i] & 0xffff, P->compbuf_r[i] & 0xffff, - P->compbuf_l[i], P->compbuf_r[i]); - } -#endif - - if (timestamp_max_delta > TIMESTAMP_MAX_ALLOWED_DELTA) - sciprintf("[soft-mixer] Warning: Difference in timestamps between audio feeds is %d us\n", timestamp_max_delta); - - mix_compute_output(self, buflen); - P->lastbuf_len = buflen; - - /* Finalize */ - mix_swap_buffers(self); - if (have_timestamp) - P->outbuf_timestamp = sfx_timestamp_add(min_timestamp, - timestamp_max_delta >> 1); - P->have_outbuf_timestamp = have_timestamp; - -} RELEASE_LOCK(); - return SFX_OK; -} - -static void -mix_pause(sfx_pcm_mixer_t *self) -{ - ACQUIRE_LOCK(); - P->paused = 1; - RELEASE_LOCK(); -} - -static void -mix_resume(sfx_pcm_mixer_t *self) -{ - ACQUIRE_LOCK(); - P->paused = 0; - RELEASE_LOCK(); -} - -sfx_pcm_mixer_t sfx_pcm_mixer_soft_linear = { - "soft-linear", - "0.1", - - mix_init, - mix_exit, - mix_subscribe, - mix_pause, - mix_resume, - mix_process_linear, - - 0, - 0, - NULL, - NULL, - NULL -}; diff --git a/engines/sci/sfx/mixer/soft.cpp b/engines/sci/sfx/mixer/soft.cpp new file mode 100644 index 0000000000..d71567a209 --- /dev/null +++ b/engines/sci/sfx/mixer/soft.cpp @@ -0,0 +1,988 @@ +/*************************************************************************** + mixer.c Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ + +#include "../mixer.h" +#include "sci/include/sci_memory.h" + +/* Max. number of microseconds in difference allowed between independent audio streams */ +#define TIMESTAMP_MAX_ALLOWED_DELTA 2000 + +/*#define DEBUG 3*/ +/* Set DEBUG to one of the following: +** anything -- high-level debugging (feed subscriptions/deletions etc.) +** >= 1 -- rough input and output analysis (once per call) +** >= 2 -- more detailed input analysis (once per call and feed) +** >= 3 -- fully detailed input and output analysis (once per frame and feed) +*/ + +/*#define DEBUG 0*/ + +#define MIN_DELTA_OBSERVATIONS 100 /* Number of times the mixer is called before it starts trying to improve latency */ +#define MAX_DELTA_OBSERVATIONS 1000000 /* Number of times the mixer is called before we assume we truly understand timing */ + +static int diagnosed_too_slow = 0; + +static volatile int mixer_lock = 0; + +/*#define DEBUG_LOCKS*/ +#ifdef DEBUG_LOCKS +# define DEBUG_ACQUIRE fprintf(stderr, "[ -LOCK -] ACKQ %d: %d\n", __LINE__, mixer_lock) +# define DEBUG_WAIT fprintf(stderr, "[ -LOCK -] WAIT %d: %d\n", __LINE__, mixer_lock); +# define DEBUG_RELEASE ; fprintf(stderr, "[ -LOCK -] REL %d: %d\n", __LINE__, mixer_lock); +#else +# define DEBUG_ACQUIRE +# define DEBUG_WAIT +# define DEBUG_RELEASE +#endif + +#define ACQUIRE_LOCK() ++mixer_lock; while (mixer_lock != 1) { DEBUG_WAIT sci_sched_yield(); } DEBUG_ACQUIRE +#define RELEASE_LOCK() --mixer_lock DEBUG_RELEASE + +struct mixer_private { + byte *outbuf; /* Output buffer to write to the PCM device next time */ + sfx_timestamp_t outbuf_timestamp; /* Timestamp associated with the output buffer */ + int have_outbuf_timestamp; /* Whether we really _have_ an associated timestamp */ + byte *writebuf; /* Buffer we're supposed to write to */ + gint32 *compbuf_l, *compbuf_r; /* Intermediate buffers for computation */ + int lastbuf_len; /* Number of frames stored in the last buffer */ + + long skew; /* Millisecond relative to which we compute time. This is the millisecond + ** part of the first time we emitted sound, to simplify some computations. */ + long lsec; /* Last point in time we updated buffers, if any (seconds since the epoch) */ + int played_this_second; /* Number of frames emitted so far in second lsec */ + + int max_delta; /* maximum observed time delta (using 'frames' as a metric unit) */ + int delta_observations; /* Number of times we played; confidence measure for max_delta */ + + /* Pause data */ + int paused; +}; + +#define P ((struct mixer_private *)(self->private_bits)) + + +static int +mix_init(sfx_pcm_mixer_t *self, sfx_pcm_device_t *device) +{ + self->dev = device; + self->private_bits /* = P */ = sci_malloc(sizeof(struct mixer_private)); + P->outbuf = P->writebuf = NULL; + P->lastbuf_len = 0; + P->compbuf_l = (gint32*)sci_malloc(sizeof(gint32) * device->buf_size); + P->compbuf_r = (gint32*)sci_malloc(sizeof(gint32) * device->buf_size); + P->played_this_second = 0; + P->paused = 0; +#ifdef DEBUG + sciprintf("[soft-mixer] Initialised device %s v%s (%d Hz, %d/%x)\n", + device->name, device->version, + device->conf.rate, device->conf.stereo, device->conf.format); +#endif + return SFX_OK; +} + +static inline unsigned int +gcd(unsigned int a, unsigned int b) +{ + if (a == b) + return a; + + if (a < b) { + unsigned int c = b % a; + + if (!c) + return a; + + return gcd(c, a); + } else + return gcd(b, a); +} + +static sfx_pcm_urat_t +urat(unsigned int nom, unsigned int denom) +{ + sfx_pcm_urat_t rv; + unsigned int g; + + rv.val = nom / denom; + nom -= rv.val * denom; + if (nom == 0) + g = 1; + else + g = gcd(nom, denom); + + rv.nom = nom / g; + rv.den = denom / g; + + return rv; +} + +static void +mix_subscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) +{ + sfx_pcm_feed_state_t *fs; + ACQUIRE_LOCK(); + if (!self->feeds) { + self->feeds_allocd = 2; + self->feeds = (sfx_pcm_feed_state_t*)sci_malloc(sizeof(sfx_pcm_feed_state_t) + * self->feeds_allocd); + } else if (self->feeds_allocd == self->feeds_nr) { + self->feeds_allocd += 2; + self->feeds = (sfx_pcm_feed_state_t*)sci_realloc(self->feeds, + sizeof(sfx_pcm_feed_state_t) + * self->feeds_allocd); + } + + fs = self->feeds + self->feeds_nr++; + fs->feed = feed; + + feed->frame_size = SFX_PCM_FRAME_SIZE(feed->conf); + + /* fs->buf_size = (self->dev->buf_size + * (feed->conf.rate + + self->dev->conf.rate - 1)) + / self->dev->conf.rate; + */ + /* For the sake of people without 64 bit CPUs: */ + fs->buf_size = 2 + /* Additional safety */ + (self->dev->buf_size * + (1 + (feed->conf.rate / self->dev->conf.rate))); +fprintf(stderr, " ---> %d/%d/%d/%d = %d\n", + self->dev->buf_size, + feed->conf.rate, + self->dev->conf.rate, + feed->frame_size, + fs->buf_size); + + fs->buf = (byte*)sci_malloc(fs->buf_size * feed->frame_size); +fprintf(stderr, " ---> --> %d for %p at %p\n", fs->buf_size * feed->frame_size, (void *)fs, (void *)fs->buf); +{int i; for (i = 0; i < fs->buf_size * feed->frame_size; i++) +fs->buf[i] = 0xa5; } + fs->scount = urat(0, 1); + fs->spd = urat(feed->conf.rate, self->dev->conf.rate); + fs->scount.den = fs->spd.den; + fs->ch_old.left = 0; + fs->ch_old.right = 0; + fs->ch_new.left = 0; + fs->ch_new.right = 0; + fs->mode = SFX_PCM_FEED_MODE_ALIVE; + + /* If the feed can't provide us with timestamps, we don't need to wait for it to do so */ + fs->pending_review = (feed->get_timestamp)? 1 : 0; + + fs->frame_bufstart = 0; + +#ifdef DEBUG + sciprintf("[soft-mixer] Subscribed %s-%x (%d Hz, %d/%x) at %d+%d/%d, buffer size %d\n", + feed->debug_name, feed->debug_nr, feed->conf.rate, feed->conf.stereo, feed->conf.format, + fs->spd.val, fs->spd.nom, fs->spd.den, fs->buf_size); +#endif + RELEASE_LOCK(); +} + + +static void +_mix_unsubscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) +{ + int i; +#ifdef DEBUG + sciprintf("[soft-mixer] Unsubscribing %s-%x\n", feed->debug_name, feed->debug_nr); +#endif + for (i = 0; i < self->feeds_nr; i++) { + sfx_pcm_feed_state_t *fs = self->feeds + i; + + if (fs->feed == feed) { + feed->destroy(feed); + + if (fs->buf) + sci_free(fs->buf); + + self->feeds_nr--; + + /* Copy topmost into deleted so that we don't have any holes */ + if (i != self->feeds_nr) + self->feeds[i] = self->feeds[self->feeds_nr]; + + if (self->feeds_allocd > 8 && self->feeds_allocd > (self->feeds_nr << 1)) { + /* Limit memory waste */ + self->feeds_allocd >>= 1; + self->feeds + = (sfx_pcm_feed_state_t*)sci_realloc(self->feeds, + sizeof(sfx_pcm_feed_state_t) + * self->feeds_allocd); + } + + for (i = 0; i < self->feeds_nr; i++) + fprintf(stderr, " Feed #%d: %s-%x\n", + i, self->feeds[i].feed->debug_name, + self->feeds[i].feed->debug_nr); + + return; + } + } + + fprintf(stderr, "[sfx-mixer] Assertion failed: Deleting invalid feed %p out of %d\n", + (void *)feed, self->feeds_nr); + + BREAKPOINT(); +} + +static void +mix_unsubscribe(sfx_pcm_mixer_t *self, sfx_pcm_feed_t *feed) +{ + ACQUIRE_LOCK(); + _mix_unsubscribe(self, feed); + RELEASE_LOCK(); +} + + +static void +mix_exit(sfx_pcm_mixer_t *self) +{ + ACQUIRE_LOCK(); + while (self->feeds_nr) + _mix_unsubscribe(self, self->feeds[0].feed); + + if (P->outbuf) + sci_free(P->outbuf); + if (P->writebuf) + sci_free(P->writebuf); + + if (P->compbuf_l) + sci_free(P->compbuf_l); + if (P->compbuf_l) + sci_free(P->compbuf_r); + + sci_free(P); + self->private_bits /* = P */ = NULL; + RELEASE_LOCK(); + +#ifdef DEBUG + sciprintf("[soft-mixer] Uninitialising mixer\n"); +#endif +} + + +#define LIMIT_16_BITS(v) \ + if (v < -32767) \ + v = -32768; \ + else if (v > 32766) \ + v = 32767 + +static inline void +mix_compute_output(sfx_pcm_mixer_t *self, int outplen) +{ + int frame_i; + sfx_pcm_config_t conf = self->dev->conf; + int use_16 = conf.format & SFX_PCM_FORMAT_16; + int bias = conf.format & ~SFX_PCM_FORMAT_LMASK; + byte *lchan, *rchan = NULL; + /* Don't see how this could possibly wind up being + ** used w/o initialisation, but you never know... */ + gint32 *lsrc = P->compbuf_l; + gint32 *rsrc = P->compbuf_r; + int frame_size = SFX_PCM_FRAME_SIZE(conf); + + + if (!P->writebuf) + P->writebuf = (byte*)sci_malloc(self->dev->buf_size * frame_size + 4); + + if (conf.stereo) { + if (conf.stereo == SFX_PCM_STEREO_RL) { + lchan = P->writebuf + ((use_16)? 2 : 1); + rchan = P->writebuf; + } else { + lchan = P->writebuf; + rchan = P->writebuf + ((use_16)? 2 : 1); + } + } else + lchan = P->writebuf; + + + for (frame_i = 0; frame_i < outplen; frame_i++) { + int left = *lsrc++; + int right = *rsrc++; + + if (conf.stereo) { + LIMIT_16_BITS(left); + LIMIT_16_BITS(right); + + if (!use_16) { + left >>= 8; + right >>= 8; + } + + left += bias; + right += bias; + + if (use_16) { + if (SFX_PCM_FORMAT_LE == (conf.format & SFX_PCM_FORMAT_ENDIANNESS)) { + lchan[0] = left & 0xff; + lchan[1] = (left >> 8) & 0xff; + rchan[0] = right & 0xff; + rchan[1] = (right >> 8) & 0xff; + } else { + lchan[1] = left & 0xff; + lchan[0] = (left >> 8) & 0xff; + rchan[1] = right & 0xff; + rchan[0] = (right >> 8) & 0xff; + } + + lchan += 4; + rchan += 4; + } else { + *lchan = left & 0xff; + *rchan = right & 0xff; + + lchan += 2; + rchan += 2; + } + + } else { + left += right; + left >>= 1; + LIMIT_16_BITS(left); + if (!use_16) + left >>= 8; + + left += bias; + + if (use_16) { + if (SFX_PCM_FORMAT_LE == (conf.format & SFX_PCM_FORMAT_ENDIANNESS)) { + lchan[0] = left & 0xff; + lchan[1] = (left >> 8) & 0xff; + } else { + lchan[1] = left & 0xff; + lchan[0] = (left >> 8) & 0xff; + } + + lchan += 2; + } else { + *lchan = left & 0xff; + lchan += 1; + } + } + } +} + +static inline void +mix_swap_buffers(sfx_pcm_mixer_t *self) +{ /* Swap buffers */ + byte *tmp = P->outbuf; + P->outbuf = P->writebuf; + P->writebuf = tmp; +} + + +#define FRAME_OFFSET(usecs) \ + ((usecs >> 7) /* approximate, since uint32 is too small */ \ + * ((long) self->dev->conf.rate)) \ + / (1000000L >> 7) + +static inline int +mix_compute_buf_len(sfx_pcm_mixer_t *self, int *skip_frames) + /* Computes the number of frames we ought to write. It tries to minimise the number, + ** in order to reduce latency. */ + /* It sets 'skip_frames' to the number of frames to assume lost by latency, effectively + ** skipping them. */ +{ + int free_frames; + int played_frames = 0; /* since the last call */ + long secs, usecs; + int frame_pos; + int result_frames; + + sci_gettime(&secs, &usecs); + + if (!P->outbuf) { + /* Called for the first time ever? */ + P->skew = usecs; + P->lsec = secs; + P->max_delta = 0; + P->delta_observations = 0; + P->played_this_second = 0; + *skip_frames = 0; + return self->dev->buf_size; + } + + /* fprintf(stderr, "[%d:%d]S%d ", secs, usecs, P->skew);*/ + + if (P->skew > usecs) { + secs--; + usecs += (1000000 - P->skew); + } + else + usecs -= P->skew; + + frame_pos = FRAME_OFFSET(usecs); + + played_frames = frame_pos - P->played_this_second + + ((secs - P->lsec) * self->dev->conf.rate); + /* + fprintf(stderr, "%d:%d - %d:%d => %d\n", secs, frame_pos, + P->lsec, P->played_this_second, played_frames); + */ + + if (played_frames > self->dev->buf_size) + played_frames = self->dev->buf_size; + + /* + fprintf(stderr, "Between %d:? offset=%d and %d:%d offset=%d: Played %d at %d\n", P->lsec, P->played_this_second, + secs, usecs, frame_pos, played_frames, self->dev->conf.rate); + */ + + + if (played_frames > P->max_delta) + P->max_delta = played_frames; + + free_frames = played_frames; + + if (free_frames > self->dev->buf_size) { + if (!diagnosed_too_slow) { + sciprintf("[sfx-mixer] Your timer is too slow for your PCM output device (%d/%d), free=%d.\n" + "[sfx-mixer] You might want to try changing the device, timer, or mixer, if possible.\n", + played_frames, self->dev->buf_size, free_frames); + } + diagnosed_too_slow = 1; + + *skip_frames = free_frames - self->dev->buf_size; + free_frames = self->dev->buf_size; + } else + *skip_frames = 0; + + ++P->delta_observations; + if (P->delta_observations > MAX_DELTA_OBSERVATIONS) + P->delta_observations = MAX_DELTA_OBSERVATIONS; + +/* /\* Disabled, broken *\/ */ +/* if (0 && P->delta_observations > MIN_DELTA_OBSERVATIONS) { /\* Start improving after a while *\/ */ +/* int diff = self->dev->conf.rate - P->max_delta; */ + +/* /\* log-approximate P->max_delta over time *\/ */ +/* recommended_frames = P->max_delta + */ +/* ((diff * MIN_DELTA_OBSERVATIONS) / P->delta_observations); */ +/* /\* WTF? *\/ */ +/* } else */ +/* recommended_frames = self->dev->buf_size; /\* Initially, keep the buffer full *\/ */ + +#if (DEBUG >= 1) + sciprintf("[soft-mixer] played since last time: %d, recommended: %d, free: %d\n", + played_frames, recommended_frames, free_frames); +#endif + + result_frames = free_frames; + + if (result_frames < 0) + result_frames = 0; + + P->played_this_second += result_frames; + while (P->played_this_second >= self->dev->conf.rate) { + /* Won't normally happen more than once */ + P->played_this_second -= self->dev->conf.rate; + P->lsec++; + } + + if (result_frames > self->dev->buf_size) { + fprintf(stderr, "[soft-mixer] Internal assertion failed: frames-to-write %d > %d\n", + result_frames, self->dev->buf_size); + } + return result_frames; +} + + + +#define READ_NEW_VALUES() \ + if (frames_left > 0) { \ + if (bias) { /* unsigned data */ \ + if (!use_16) { \ + c_new.left = (*lsrc) << 8; \ + c_new.right = (*rsrc) << 8; \ + } else { \ + if (conf.format & SFX_PCM_FORMAT_LE) { \ + c_new.left = lsrc[0] | lsrc[1] << 8; \ + c_new.right = rsrc[0] | rsrc[1] << 8; \ + } else { \ + c_new.left = lsrc[1] | lsrc[0] << 8; \ + c_new.right = rsrc[1] | rsrc[0] << 8; \ + } \ + } \ + } else { /* signed data */ \ + if (!use_16) { \ + c_new.left = (*((signed char *)lsrc)) << 8; \ + c_new.right = (*((signed char *)rsrc)) << 8; \ + } else { \ + if (conf.format & SFX_PCM_FORMAT_LE) { \ + c_new.left = lsrc[0] | ((signed char *)lsrc)[1] << 8; \ + c_new.right = rsrc[0] | ((signed char *)rsrc)[1] << 8; \ + } else { \ + c_new.left = lsrc[1] | ((signed char *)lsrc)[0] << 8; \ + c_new.right = rsrc[1] | ((signed char *)rsrc)[0] << 8; \ + } \ + } \ + } \ + \ + c_new.left -= bias; \ + c_new.right -= bias; \ + \ + lsrc += frame_size; \ + rsrc += frame_size; \ + } else { \ + c_new.left = c_new.right = 0; \ + break; \ + } + + +static volatile int xx_offset; +static volatile int xx_size; + +static void +mix_compute_input_linear(sfx_pcm_mixer_t *self, int add_result, + int len, sfx_timestamp_t *ts, sfx_timestamp_t base_ts) + /* if add_result is non-zero, P->outbuf should be added to rather than overwritten. */ + /* base_ts is the timestamp for the first frame */ +{ + sfx_pcm_feed_state_t *fs = self->feeds + add_result; + sfx_pcm_feed_t *f = fs->feed; + sfx_pcm_config_t conf = f->conf; + int use_16 = conf.format & SFX_PCM_FORMAT_16; + gint32 *lchan = P->compbuf_l; + gint32 *rchan = P->compbuf_r; + int frame_size = f->frame_size; + byte *wr_dest = fs->buf + (frame_size * fs->frame_bufstart); + byte *lsrc = fs->buf; + byte *rsrc = fs->buf; + /* Location to write to */ + int frames_nr; + int bias = (conf.format & ~SFX_PCM_FORMAT_LMASK)? 0x8000 : 0; + /* We use this only on a 16 bit level here */ + + /* The two most extreme source frames we consider for a + ** destination frame */ + struct twochannel_data c_old = fs->ch_old; + struct twochannel_data c_new = fs->ch_new; + + int frames_read = 0; + int frames_left; + int write_offset; /* Iterator for translation */ + int delay_frames = 0; /* Number of frames (dest buffer) at the beginning we skip */ + + /* First, compute the number of frames we want to retreive */ + frames_nr = fs->spd.val * len; + /* A little complicated since we must consider partial frames */ + frames_nr += (fs->spd.nom * len + + (fs->scount.den - fs->scount.nom) /* remember that we may have leftovers */ + + (fs->spd.den - 1 /* round up */) + ) + / fs->spd.den; + + ts->secs = -1; + + if (frames_nr > fs->buf_size) { + fprintf(stderr, "%d (%d*%d + somethign) bytes, but only %d allowed!!!!!\n", + frames_nr * f->frame_size, + fs->spd.val, len, + fs->buf_size); + BREAKPOINT(); + } + + if (fs->pending_review) { + int newmode = PCM_FEED_EMPTY; /* empty unless a get_timestamp() tells otherwise */ + + RELEASE_LOCK(); + /* Retrieve timestamp */ + if (f->get_timestamp) + newmode = f->get_timestamp(f, ts); + ACQUIRE_LOCK(); + + fs = self->feeds + add_result; + /* Reset in case of status update */ + + switch (newmode) { + + case PCM_FEED_TIMESTAMP: { + /* Compute the number of frames the returned timestamp is in the future: */ + delay_frames = + sfx_timestamp_frame_diff(sfx_timestamp_renormalise(*ts, base_ts.frame_rate), + base_ts); + + if (delay_frames <= 0) + /* Start ASAP, even if it's too late */ + delay_frames = 0; + else + if (delay_frames > len) + delay_frames = len; + fs->pending_review = 0; + } + break; + + case PCM_FEED_EMPTY: + fs->mode = SFX_PCM_FEED_MODE_DEAD; + + /* ...fall through... */ + + case PCM_FEED_IDLE: + /* Clear audio buffer, if neccessary, and return */ + if (!add_result) { + memset(P->compbuf_l, 0, sizeof(gint32) * len); + memset(P->compbuf_r, 0, sizeof(gint32) * len); + } + return; + + default: + fprintf(stderr, "[soft-mixer] Fatal: Invalid mode returned by PCM feed %s-%d's get_timestamp(): %d\n", + f->debug_name, f->debug_nr, newmode); + exit(1); + } + } + + RELEASE_LOCK(); + /* Make sure we have sufficient information */ + if (frames_nr > delay_frames + fs->frame_bufstart) + frames_read = + f->poll(f, wr_dest, + frames_nr + - delay_frames + - fs->frame_bufstart); + + ACQUIRE_LOCK(); + fs = self->feeds + add_result; + + frames_read += fs->frame_bufstart; + frames_left = frames_read; + + /* Reset in case of status update */ + + /* Skip at the beginning: */ + if (delay_frames) { + if (!add_result) { + memset(lchan, 0, sizeof(gint32) * delay_frames); + memset(rchan, 0, sizeof(gint32) * delay_frames); + } + lchan += delay_frames; + rchan += delay_frames; + + len -= delay_frames; + } + + +#if (DEBUG >= 2) + sciprintf("[soft-mixer] Examining %s-%x (frame size %d); read %d/%d/%d, re-using %d frames\n", + f->debug_name, f->debug_nr, frame_size, frames_read, frames_nr, + fs->buf_size, fs->frame_bufstart); +#endif + + + if (conf.stereo == SFX_PCM_STEREO_LR) + rsrc += (use_16)? 2 : 1; + else if (conf.stereo == SFX_PCM_STEREO_RL) + lsrc += (use_16)? 2 : 1; + /* Otherwise, we let both point to the same place */ + +#if (DEBUG >= 2) + sciprintf("[soft-mixer] Stretching theoretical %d (physical %d) results to %d\n", frames_nr, frames_left, len); +#endif + for (write_offset = 0; write_offset < len; write_offset++) { + int leftsum = 0; /* Sum of any complete frames we used */ + int rightsum = 0; + + int left; /* Sum of the two most extreme source frames + ** we considered, i.e. the oldest and newest + ** one corresponding to the output frame we are + ** computing */ + int right; + + int frame_steps = fs->spd.val; + int j; + + if (fs->scount.nom >= fs->scount.den) { + fs->scount.nom -= fs->scount.den; /* Ensure fractional part < 1 */ + ++frame_steps; + } + if (frame_steps) + c_old = c_new; + +#if 0 + if (write_offset == 0) { + READ_NEW_VALUES(); + --frames_left; +#if (DEBUG >= 3) + sciprintf("[soft-mixer] Initial read %d:%d\n", c_new.left, c_new.right); +#endif + c_old = c_new; + } +#endif + + for (j = 0; j < frame_steps; j++) { + READ_NEW_VALUES(); + --frames_left; +#if (DEBUG >= 3) + sciprintf("[soft-mixer] Step %d/%d made %d:%d\n", j, frame_steps, c_new.left, c_new.right); +#endif + + /* The last frame will be subject to the fractional + ** part analysis, so we add it to 'left' and 'right' + ** later-- all others are added to (leftsum, rightsum). + */ + if (j+1 < frame_steps) { + leftsum += c_new.left; + rightsum += c_new.right; + } + } + + left = c_new.left * fs->scount.nom + + c_old.left * (fs->scount.den - fs->scount.nom); + right = c_new.right * fs->scount.nom + + c_old.right * (fs->scount.den - fs->scount.nom); + + /* Normalise */ + left /= fs->spd.den; + right /= fs->spd.den; + + + leftsum += left; + rightsum += right; + + + /* Make sure to divide by the number of frames we added here */ + if (frame_steps > 1) { + leftsum /= (frame_steps); + rightsum /= (frame_steps); + } + + +#if (DEBUG >= 3) + sciprintf("[soft-mixer] Ultimate result: %d:%d (frac %d:%d)\n", leftsum, rightsum, left, right); +#endif + + if (add_result) { + *(lchan++) += leftsum; + *(rchan++) += rightsum; + } else { + *(lchan++) = leftsum; + *(rchan++) = rightsum; + } + + fs->scount.nom += fs->spd.nom; /* Count up fractional part */ + } + + fs->ch_old = c_old; + fs->ch_new = c_new; + + /* If neccessary, zero out the rest */ + if (write_offset < len && !add_result) { + memset(lchan, 0, sizeof(gint32) * (len - write_offset)); + memset(rchan, 0, sizeof(gint32) * (len - write_offset)); + } + + /* Save whether we have a partial frame still stored */ + fs->frame_bufstart = frames_left; + + if (frames_left) { + xx_offset = ((frames_read - frames_left) * f->frame_size); + xx_size = frames_left * f->frame_size; + if (xx_offset + xx_size + >= fs->buf_size * f->frame_size) { + fprintf(stderr, "offset %d >= max %d!\n", + (xx_offset + xx_size), fs->buf_size * f->frame_size); + BREAKPOINT(); + } + + memmove(fs->buf, + fs->buf + ((frames_read - frames_left) * f->frame_size), + frames_left * f->frame_size); + } +#if (DEBUG >= 2) + sciprintf("[soft-mixer] Leaving %d over\n", fs->frame_bufstart); +#endif + + if (frames_read + delay_frames < frames_nr) { + if (f->get_timestamp) /* Can resume? */ + fs->pending_review = 1; + else + fs->mode = SFX_PCM_FEED_MODE_DEAD; /* Done. */ + } +} + +static int +mix_process_linear(sfx_pcm_mixer_t *self) +{ +ACQUIRE_LOCK(); +{ + int src_i; /* source feed index counter */ + int frames_skip; /* Number of frames to discard, rather than to emit */ + int buflen = mix_compute_buf_len(self, &frames_skip); /* Compute # of frames we must compute and write */ + int fake_buflen; + int timestamp_max_delta = 0; + int have_timestamp = 0; + sfx_timestamp_t start_timestamp; /* The timestamp at which the first frame will be played */ + sfx_timestamp_t min_timestamp; + sfx_timestamp_t timestamp; + + if (self->dev->get_output_timestamp) + start_timestamp = self->dev->get_output_timestamp(self->dev); + else { + long sec, usec; + sci_gettime(&sec, &usec); + start_timestamp = sfx_new_timestamp(sec, usec, self->dev->conf.rate); + } + + if ((P->outbuf) && (P->lastbuf_len)) { + sfx_timestamp_t ts; + int rv; + + if (P->have_outbuf_timestamp) { + ts = sfx_timestamp_renormalise(P->outbuf_timestamp, self->dev->conf.rate); + } + + rv = self->dev->output(self->dev, P->outbuf, + P->lastbuf_len, + (P->have_outbuf_timestamp)? &ts : NULL); + + if (rv == SFX_ERROR) { + RELEASE_LOCK(); + return rv; /* error */ + } + } + +#if (DEBUG >= 1) + if (self->feeds_nr) + sciprintf("[soft-mixer] Mixing %d output frames on %d input feeds\n", buflen, self->feeds_nr); +#endif + if (self->feeds_nr && !P->paused) { + /* Below, we read out all feeds in case we have to skip frames first, then get the + ** most current sound. 'fake_buflen' is either the actual buflen (for the last iteration) + ** or a fraction of the buf length to discard. */ + do { + if (frames_skip) { + if (frames_skip > self->dev->buf_size) + fake_buflen = self->dev->buf_size; + else + fake_buflen = frames_skip; + + frames_skip -= fake_buflen; + } else { + fake_buflen = buflen; + frames_skip = -1; /* Mark us as being completely done */ + } + + for (src_i = 0; src_i < self->feeds_nr; src_i++) { + mix_compute_input_linear(self, src_i, + fake_buflen, ×tamp, + start_timestamp); + + if (timestamp.secs >= 0) { + if (have_timestamp) { + int diff = sfx_timestamp_usecs_diff(min_timestamp, timestamp); + if (diff > 0) { + /* New earlier timestamp */ + timestamp = min_timestamp; + timestamp_max_delta += diff; + } else if (diff > timestamp_max_delta) + timestamp_max_delta = diff; + /* New max delta for timestamp */ + } else { + min_timestamp = timestamp; + have_timestamp = 1; + } + } + } + /* Destroy all feeds we finished */ + for (src_i = 0; src_i < self->feeds_nr; src_i++) + if (self->feeds[src_i].mode == SFX_PCM_FEED_MODE_DEAD) + _mix_unsubscribe(self, self->feeds[src_i].feed); + } while (frames_skip >= 0); + + } else { /* Zero it out */ + memset(P->compbuf_l, 0, sizeof(gint32) * buflen); + memset(P->compbuf_r, 0, sizeof(gint32) * buflen); + } + +#if (DEBUG >= 1) + if (self->feeds_nr) + sciprintf("[soft-mixer] Done mixing for this session, the result will be our next output buffer\n"); +#endif + +#if (DEBUG >= 3) + if (self->feeds_nr) { + int i; + sciprintf("[soft-mixer] Intermediate representation:\n"); + for (i = 0; i < buflen; i++) + sciprintf("[soft-mixer] Offset %d:\t[%04x:%04x]\t%d:%d\n", i, + P->compbuf_l[i] & 0xffff, P->compbuf_r[i] & 0xffff, + P->compbuf_l[i], P->compbuf_r[i]); + } +#endif + + if (timestamp_max_delta > TIMESTAMP_MAX_ALLOWED_DELTA) + sciprintf("[soft-mixer] Warning: Difference in timestamps between audio feeds is %d us\n", timestamp_max_delta); + + mix_compute_output(self, buflen); + P->lastbuf_len = buflen; + + /* Finalize */ + mix_swap_buffers(self); + if (have_timestamp) + P->outbuf_timestamp = sfx_timestamp_add(min_timestamp, + timestamp_max_delta >> 1); + P->have_outbuf_timestamp = have_timestamp; + +} RELEASE_LOCK(); + return SFX_OK; +} + +static void +mix_pause(sfx_pcm_mixer_t *self) +{ + ACQUIRE_LOCK(); + P->paused = 1; + RELEASE_LOCK(); +} + +static void +mix_resume(sfx_pcm_mixer_t *self) +{ + ACQUIRE_LOCK(); + P->paused = 0; + RELEASE_LOCK(); +} + +sfx_pcm_mixer_t sfx_pcm_mixer_soft_linear = { + "soft-linear", + "0.1", + + mix_init, + mix_exit, + mix_subscribe, + mix_pause, + mix_resume, + mix_process_linear, + + 0, + 0, + NULL, + NULL, + NULL +}; diff --git a/engines/sci/sfx/mixer/test.c b/engines/sci/sfx/mixer/test.c deleted file mode 100644 index 20b3e952e1..0000000000 --- a/engines/sci/sfx/mixer/test.c +++ /dev/null @@ -1,351 +0,0 @@ -/*************************************************************************** - test.c Copyright (C) 2003 Christoph Reichenbach - - - This program may be modified and copied freely according to the terms of - the GNU general public license (GPL), as long as the above copyright - notice and the licensing information contained herein are preserved. - - Please refer to www.gnu.org for licensing details. - - This work is provided AS IS, without warranty of any kind, expressed or - implied, including but not limited to the warranties of merchantibility, - noninfringement, and fitness for a specific purpose. The author will not - be held liable for any damage caused by this work or derivatives of it. - - By using this source code, you agree to the licensing terms as stated - above. - - - Please contact the maintainer for bug reports or inquiries. - - Current Maintainer: - - Christoph Reichenbach (CR) - -***************************************************************************/ -/* Mixer inspection/test program */ - - -#include "../mixer.h" -#include - -#if 0 -sfx_pcm_mixer_t *mix; - -int dev_init(sfx_pcm_device_t *self); -void dev_exit(sfx_pcm_device_t *self); -int dev_option(sfx_pcm_device_t *self, char *name, char *value); -int dev_output(sfx_pcm_device_t *self, byte *buf, int count); - -#define MIN_OUTPUT 128 -/* Min amount of output to compute */ - -#define DEVICES_NR 10 - -sfx_pcm_device_t devices[DEVICES_NR] = { - { "test-1", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U8 }, 1024, NULL }, -#if (DEVICES_NR > 1) - { "test-2", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_U8 }, 1024, NULL }, - { "test-3", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_STEREO_RL, SFX_PCM_FORMAT_U8 }, 1024, NULL }, - { "test-4", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S8 }, 1024, NULL }, - { "test-5", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U16_LE }, 1024, NULL }, - { "test-6", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U16_BE }, 1024, NULL }, - { "test-7", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_LE }, 1024, NULL }, - { "test-8", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_BE }, 1024, NULL }, - { "test-9", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_STEREO_RL, SFX_PCM_FORMAT_S16_LE }, 1024, NULL }, - { "test-10", "0", dev_init, dev_exit, dev_option, dev_output, - { 200, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_U16_BE }, 1024, NULL } -#endif -}; - -int output_count; - -int dev_init(sfx_pcm_device_t *self) -{ - output_count = 0; - - fprintf(stderr, "[DEV] Initialised device %p as follows:\n" - "\trate = %d\n" - "\tstereo = %s\n" - "\tbias = %x\n" - "\tbytes/sample = %d\n" - "\tendianness = %s\n", - self, - self->conf.rate, - self->conf.stereo? ((self->conf.stereo == SFX_PCM_STEREO_LR)? "Left, Right" : "Right, Left") : "No", - self->conf.format & ~SFX_PCM_FORMAT_LMASK, - (self->conf.format & SFX_PCM_FORMAT_16)? 2 : 1, - ((self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_BE)? "big" : "little"); - return 0; -} - -void dev_exit(sfx_pcm_device_t *self) -{ - fprintf(stderr, "[DEV] Uninitialising device\n"); -} - -int dev_option(sfx_pcm_device_t *self, char *name, char *value) -{ - fprintf(stderr, "[DEV] Set option '%s' to '%s'\n", name, value); - return 0; -} - -int dev_output_enabled = 0; - -int dev_output(sfx_pcm_device_t *self, byte *buf, int count) -{ - int mono_sample_size = ((self->conf.format & SFX_PCM_FORMAT_16)? 2 : 1); - int sample_size = (self->conf.stereo? 2 : 1) * mono_sample_size; - int bias = self->conf.format & ~SFX_PCM_FORMAT_LMASK; - int is_bigendian = (self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_BE; - byte *left_channel = buf; - byte *right_channel = buf; - - if (!dev_output_enabled) - return 0; - - if (self->conf.format & SFX_PCM_FORMAT_16) - bias <<= 8; - - if (self->conf.stereo == SFX_PCM_STEREO_LR) - right_channel += mono_sample_size; - if (self->conf.stereo == SFX_PCM_STEREO_RL) - left_channel += mono_sample_size; - - while (count--) { - int right = right_channel[0]; - int left = left_channel[0]; - int second_byte = ((self->conf.format & SFX_PCM_FORMAT_16)? 1 : 0); - - if (second_byte) { - - if (is_bigendian) { - left = left << 8 | left_channel[1]; - right = right << 8 | right_channel[1]; - } else { - left = left | left_channel[1] << 8; - right = right | right_channel[1] << 8; - } - } - - left -= bias; - right -= bias; - - if (!second_byte) { - left <<= 8; - right <<= 8; - } - - fprintf(stderr, "[DEV] %p play %04x:\t%04x %04x\n", self, output_count++, left & 0xffff, right & 0xffff); - - left_channel += sample_size; - right_channel += sample_size; - } - return 0; -} - -/* Feeds for debugging */ - -typedef struct { - int i; -} int_struct; - -int feed_poll(sfx_pcm_feed_t *self, byte *dest, int size); -void feed_destroy(sfx_pcm_feed_t *self); - -int_struct private_bits[10] = { - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0}, - {0} -}; - - -typedef struct { - int start; - int samples_nr; - byte *data; -} sample_feed_t; - -#define FEEDS_NR 4 - -sfx_pcm_feed_t feeds[FEEDS_NR] = { - { feed_poll, feed_destroy, &(private_bits[0]), - { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S8 }, "test-feed", 0, 0} -#if FEEDS_NR > 1 - ,{ feed_poll, feed_destroy, &(private_bits[1]), - { 400, SFX_PCM_MONO, SFX_PCM_FORMAT_U8 }, "test-feed", 1, 0} -#endif -#if FEEDS_NR > 2 - ,{ feed_poll, feed_destroy, &(private_bits[2]), - { 20, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_LE }, "test-feed", 2, 0} -#endif -#if FEEDS_NR > 3 - ,{ feed_poll, feed_destroy, &(private_bits[3]), - { 150, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_S8 }, "test-feed", 3, 0} -#endif - /* - ,{ feed_poll, feed_destroy, &(private_bits[4]), - {}, "test-feed", 4, 0} - ,{ feed_poll, feed_destroy, &(private_bits[5]), - {}, "test-feed", 5, 0} - */ -}; - -byte feed_data_0[] = {0xfd, 0xfe, 0xff, 0, 1, 2, 3, 4, 5, 6}; -byte feed_data_1[] = {0x80, 0x90, 0xA0, 0xB0, 0xC0, - 0xD0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80}; -byte feed_data_2[] = {0x00, 0x00, - 0x00, 0x80, - 0xe8, 0x03}; -byte feed_data_3[] = {0x00, 0x10, - 0x01, 0x20, - 0x02, 0x30}; - -sample_feed_t sample_feeds[FEEDS_NR] = { - { 1, 10, feed_data_0 } -#if FEEDS_NR > 1 - ,{ 21, 12, feed_data_1 } -#endif -#if FEEDS_NR > 2 - ,{ 0, 3, feed_data_2 } -#endif -#if FEEDS_NR > 3 - ,{ 40, 3, feed_data_3 } -#endif -}; - -void -feed_destroy(sfx_pcm_feed_t *self) -{ - int_struct *s = (int_struct *) self->internal; - s->i = 0; /* reset */ -} - - -int -feed_poll(sfx_pcm_feed_t *self, byte *dest, int size) -{ - int_struct *s = (int_struct *) self->internal; - int sample_size = self->sample_size; - sample_feed_t *data = &(sample_feeds[self->debug_nr]); - int bias = self->conf.format & ~SFX_PCM_FORMAT_LMASK; - byte neutral[4] = {0, 0, 0, 0}; - int i; -fprintf(stderr, "[feed] Asked for %d at %p, ss=%d\n", size, dest, sample_size); - if (bias) { - byte first = bias >> 8; - byte second = bias & 0xff; - - if ((self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_LE) { - int t = first; - first = second; - second = t; - } - - if (self->conf.format & SFX_PCM_FORMAT_16) { - neutral[0] = first; - neutral[1] = second; - neutral[2] = first; - neutral[3] = second; - } else { - neutral[0] = bias; - neutral[1] = bias; - } - } - - for (i = 0; i < size; i++) { - int t = s->i - data->start; - - if (t >= data->samples_nr) - return i; - - if (t >= 0) - memcpy(dest, data->data + t * sample_size, sample_size); - else - memcpy(dest, neutral, sample_size); - - dest += sample_size; - s->i++; - } - return size; -} - - - - -extern FILE *con_file; - -#define DELAY usleep((rand() / (RAND_MAX / 250L))) - - -int -main(int argc, char **argv) -{ - int dev_nr; - - mix = sfx_pcm_find_mixer(NULL); - - if (!mix) { - fprintf(stderr, "Error: Could not find a mixer!\n"); - return 1; - } else { - fprintf(stderr, "Running %s, v%s\n", - mix->name, mix->version); - } - con_file = stderr; - - srand(time(NULL)); - - for (dev_nr = 0; dev_nr < DEVICES_NR; dev_nr++) { - sfx_pcm_device_t *dev = &(devices[dev_nr++]); - int j; - dev->init(dev); - mix->init(mix, dev); - - dev_output_enabled = 0; - /* Prime it to our timing */ - for (j = 0; j < 250; j++) { - DELAY; - mix->process(mix); - } - dev_output_enabled = 1; - - fprintf(stderr, "[test] Subscribing...\n"); - - for (j = 0; j < FEEDS_NR; j++) - mix->subscribe(mix, &(feeds[j])); - - fprintf(stderr, "[test] Subscribed %d feeds.\n", - FEEDS_NR); - - while (output_count < MIN_OUTPUT) { - DELAY; - mix->process(mix); - fprintf(stderr, "\n"); - } - - fprintf(stderr, "[test] Preparing finalisation\n"); - mix->exit(mix); - fprintf(stderr, "[test] Mixer uninitialised\n"); - } -} - -#else -int main() {} -#endif diff --git a/engines/sci/sfx/mixer/test.cpp b/engines/sci/sfx/mixer/test.cpp new file mode 100644 index 0000000000..20b3e952e1 --- /dev/null +++ b/engines/sci/sfx/mixer/test.cpp @@ -0,0 +1,351 @@ +/*************************************************************************** + test.c Copyright (C) 2003 Christoph Reichenbach + + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) + +***************************************************************************/ +/* Mixer inspection/test program */ + + +#include "../mixer.h" +#include + +#if 0 +sfx_pcm_mixer_t *mix; + +int dev_init(sfx_pcm_device_t *self); +void dev_exit(sfx_pcm_device_t *self); +int dev_option(sfx_pcm_device_t *self, char *name, char *value); +int dev_output(sfx_pcm_device_t *self, byte *buf, int count); + +#define MIN_OUTPUT 128 +/* Min amount of output to compute */ + +#define DEVICES_NR 10 + +sfx_pcm_device_t devices[DEVICES_NR] = { + { "test-1", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U8 }, 1024, NULL }, +#if (DEVICES_NR > 1) + { "test-2", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_U8 }, 1024, NULL }, + { "test-3", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_STEREO_RL, SFX_PCM_FORMAT_U8 }, 1024, NULL }, + { "test-4", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S8 }, 1024, NULL }, + { "test-5", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U16_LE }, 1024, NULL }, + { "test-6", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_U16_BE }, 1024, NULL }, + { "test-7", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_LE }, 1024, NULL }, + { "test-8", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_BE }, 1024, NULL }, + { "test-9", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_STEREO_RL, SFX_PCM_FORMAT_S16_LE }, 1024, NULL }, + { "test-10", "0", dev_init, dev_exit, dev_option, dev_output, + { 200, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_U16_BE }, 1024, NULL } +#endif +}; + +int output_count; + +int dev_init(sfx_pcm_device_t *self) +{ + output_count = 0; + + fprintf(stderr, "[DEV] Initialised device %p as follows:\n" + "\trate = %d\n" + "\tstereo = %s\n" + "\tbias = %x\n" + "\tbytes/sample = %d\n" + "\tendianness = %s\n", + self, + self->conf.rate, + self->conf.stereo? ((self->conf.stereo == SFX_PCM_STEREO_LR)? "Left, Right" : "Right, Left") : "No", + self->conf.format & ~SFX_PCM_FORMAT_LMASK, + (self->conf.format & SFX_PCM_FORMAT_16)? 2 : 1, + ((self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_BE)? "big" : "little"); + return 0; +} + +void dev_exit(sfx_pcm_device_t *self) +{ + fprintf(stderr, "[DEV] Uninitialising device\n"); +} + +int dev_option(sfx_pcm_device_t *self, char *name, char *value) +{ + fprintf(stderr, "[DEV] Set option '%s' to '%s'\n", name, value); + return 0; +} + +int dev_output_enabled = 0; + +int dev_output(sfx_pcm_device_t *self, byte *buf, int count) +{ + int mono_sample_size = ((self->conf.format & SFX_PCM_FORMAT_16)? 2 : 1); + int sample_size = (self->conf.stereo? 2 : 1) * mono_sample_size; + int bias = self->conf.format & ~SFX_PCM_FORMAT_LMASK; + int is_bigendian = (self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_BE; + byte *left_channel = buf; + byte *right_channel = buf; + + if (!dev_output_enabled) + return 0; + + if (self->conf.format & SFX_PCM_FORMAT_16) + bias <<= 8; + + if (self->conf.stereo == SFX_PCM_STEREO_LR) + right_channel += mono_sample_size; + if (self->conf.stereo == SFX_PCM_STEREO_RL) + left_channel += mono_sample_size; + + while (count--) { + int right = right_channel[0]; + int left = left_channel[0]; + int second_byte = ((self->conf.format & SFX_PCM_FORMAT_16)? 1 : 0); + + if (second_byte) { + + if (is_bigendian) { + left = left << 8 | left_channel[1]; + right = right << 8 | right_channel[1]; + } else { + left = left | left_channel[1] << 8; + right = right | right_channel[1] << 8; + } + } + + left -= bias; + right -= bias; + + if (!second_byte) { + left <<= 8; + right <<= 8; + } + + fprintf(stderr, "[DEV] %p play %04x:\t%04x %04x\n", self, output_count++, left & 0xffff, right & 0xffff); + + left_channel += sample_size; + right_channel += sample_size; + } + return 0; +} + +/* Feeds for debugging */ + +typedef struct { + int i; +} int_struct; + +int feed_poll(sfx_pcm_feed_t *self, byte *dest, int size); +void feed_destroy(sfx_pcm_feed_t *self); + +int_struct private_bits[10] = { + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0} +}; + + +typedef struct { + int start; + int samples_nr; + byte *data; +} sample_feed_t; + +#define FEEDS_NR 4 + +sfx_pcm_feed_t feeds[FEEDS_NR] = { + { feed_poll, feed_destroy, &(private_bits[0]), + { 200, SFX_PCM_MONO, SFX_PCM_FORMAT_S8 }, "test-feed", 0, 0} +#if FEEDS_NR > 1 + ,{ feed_poll, feed_destroy, &(private_bits[1]), + { 400, SFX_PCM_MONO, SFX_PCM_FORMAT_U8 }, "test-feed", 1, 0} +#endif +#if FEEDS_NR > 2 + ,{ feed_poll, feed_destroy, &(private_bits[2]), + { 20, SFX_PCM_MONO, SFX_PCM_FORMAT_S16_LE }, "test-feed", 2, 0} +#endif +#if FEEDS_NR > 3 + ,{ feed_poll, feed_destroy, &(private_bits[3]), + { 150, SFX_PCM_STEREO_LR, SFX_PCM_FORMAT_S8 }, "test-feed", 3, 0} +#endif + /* + ,{ feed_poll, feed_destroy, &(private_bits[4]), + {}, "test-feed", 4, 0} + ,{ feed_poll, feed_destroy, &(private_bits[5]), + {}, "test-feed", 5, 0} + */ +}; + +byte feed_data_0[] = {0xfd, 0xfe, 0xff, 0, 1, 2, 3, 4, 5, 6}; +byte feed_data_1[] = {0x80, 0x90, 0xA0, 0xB0, 0xC0, + 0xD0, 0xD0, 0xC0, 0xB0, 0xA0, 0x90, 0x80}; +byte feed_data_2[] = {0x00, 0x00, + 0x00, 0x80, + 0xe8, 0x03}; +byte feed_data_3[] = {0x00, 0x10, + 0x01, 0x20, + 0x02, 0x30}; + +sample_feed_t sample_feeds[FEEDS_NR] = { + { 1, 10, feed_data_0 } +#if FEEDS_NR > 1 + ,{ 21, 12, feed_data_1 } +#endif +#if FEEDS_NR > 2 + ,{ 0, 3, feed_data_2 } +#endif +#if FEEDS_NR > 3 + ,{ 40, 3, feed_data_3 } +#endif +}; + +void +feed_destroy(sfx_pcm_feed_t *self) +{ + int_struct *s = (int_struct *) self->internal; + s->i = 0; /* reset */ +} + + +int +feed_poll(sfx_pcm_feed_t *self, byte *dest, int size) +{ + int_struct *s = (int_struct *) self->internal; + int sample_size = self->sample_size; + sample_feed_t *data = &(sample_feeds[self->debug_nr]); + int bias = self->conf.format & ~SFX_PCM_FORMAT_LMASK; + byte neutral[4] = {0, 0, 0, 0}; + int i; +fprintf(stderr, "[feed] Asked for %d at %p, ss=%d\n", size, dest, sample_size); + if (bias) { + byte first = bias >> 8; + byte second = bias & 0xff; + + if ((self->conf.format & SFX_PCM_FORMAT_ENDIANNESS) == SFX_PCM_FORMAT_LE) { + int t = first; + first = second; + second = t; + } + + if (self->conf.format & SFX_PCM_FORMAT_16) { + neutral[0] = first; + neutral[1] = second; + neutral[2] = first; + neutral[3] = second; + } else { + neutral[0] = bias; + neutral[1] = bias; + } + } + + for (i = 0; i < size; i++) { + int t = s->i - data->start; + + if (t >= data->samples_nr) + return i; + + if (t >= 0) + memcpy(dest, data->data + t * sample_size, sample_size); + else + memcpy(dest, neutral, sample_size); + + dest += sample_size; + s->i++; + } + return size; +} + + + + +extern FILE *con_file; + +#define DELAY usleep((rand() / (RAND_MAX / 250L))) + + +int +main(int argc, char **argv) +{ + int dev_nr; + + mix = sfx_pcm_find_mixer(NULL); + + if (!mix) { + fprintf(stderr, "Error: Could not find a mixer!\n"); + return 1; + } else { + fprintf(stderr, "Running %s, v%s\n", + mix->name, mix->version); + } + con_file = stderr; + + srand(time(NULL)); + + for (dev_nr = 0; dev_nr < DEVICES_NR; dev_nr++) { + sfx_pcm_device_t *dev = &(devices[dev_nr++]); + int j; + dev->init(dev); + mix->init(mix, dev); + + dev_output_enabled = 0; + /* Prime it to our timing */ + for (j = 0; j < 250; j++) { + DELAY; + mix->process(mix); + } + dev_output_enabled = 1; + + fprintf(stderr, "[test] Subscribing...\n"); + + for (j = 0; j < FEEDS_NR; j++) + mix->subscribe(mix, &(feeds[j])); + + fprintf(stderr, "[test] Subscribed %d feeds.\n", + FEEDS_NR); + + while (output_count < MIN_OUTPUT) { + DELAY; + mix->process(mix); + fprintf(stderr, "\n"); + } + + fprintf(stderr, "[test] Preparing finalisation\n"); + mix->exit(mix); + fprintf(stderr, "[test] Mixer uninitialised\n"); + } +} + +#else +int main() {} +#endif -- cgit v1.2.3