diff options
Diffstat (limited to 'src/emu/streams.c')
-rw-r--r-- | src/emu/streams.c | 881 |
1 files changed, 881 insertions, 0 deletions
diff --git a/src/emu/streams.c b/src/emu/streams.c new file mode 100644 index 00000000000..e8b5d753f7f --- /dev/null +++ b/src/emu/streams.c @@ -0,0 +1,881 @@ +/*************************************************************************** + + streams.c + + Handle general purpose audio streams + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + Streaming works as follows: + + Each stream can have 'n' inputs and 'm' outputs. The inputs for a + stream are the outputs from another stream. Note that for tracking + purposes, each stream tracks its input streams directly, but does + not explicitly track its output streams. Instead, each output simply + tracks the number of dependent input streams. + + Each stream has a sample rate. This rate controls the sample rate + of the outputs. All outputs on a stream output at the same sample + rate. + + Each stream also has a callback function. This function is called + with an array of input sample streams, and an array out output + sample streams. The input sample streams are automatically resampled + by the streaming engine to match the stream's current sample rate. + The output sample streams are expected to be generated at the + stream's current sample rate. + + Before the callback can be invoked, all the inputs that flow into it + must be updated as well. However, each stream can have an independent + sample rate, so this isn't as easy as it sounds. + + To update a stream, the engine must iterate over all the inputs. For + each input, it requests that input to update to the current time. + Then it resamples the input data into a local resample buffer at the + stream's sample rate. Once all inputs are up-to-date, it calls the + callback with the array of resampled input sample buffers. The + callback is expected to fill in the array of output sample buffers. + These sample buffers can then be further resampled and passed to + other streams, or output as desired. + +***************************************************************************/ + +#include "driver.h" +#include "streams.h" +#include <math.h> + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define VERBOSE (0) + +#if VERBOSE +#define VPRINTF(x) mame_printf_debug x +#else +#define VPRINTF(x) +#endif + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define OUTPUT_BUFFER_UPDATES (5) + +#define FRAC_BITS 22 +#define FRAC_ONE (1 << FRAC_BITS) +#define FRAC_MASK (FRAC_ONE - 1) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _stream_input stream_input; +typedef struct _stream_output stream_output; + +struct _stream_input +{ + /* linking information */ + sound_stream * owner; /* pointer to the owning stream */ + stream_output * source; /* pointer to the sound_output for this source */ + + /* resample buffer */ + stream_sample_t * resample; /* buffer for resampling to the stream's sample rate */ + UINT32 bufsize; /* size of output buffer, in samples */ + UINT32 bufalloc; /* allocated size of output buffer, in samples */ + + /* resampling information */ + attoseconds_t latency_attoseconds; /* latency between this stream and the input stream */ + INT16 gain; /* gain to apply to this input */ +}; + + +struct _stream_output +{ + /* linking information */ + sound_stream * owner; /* pointer to the owning stream */ + + /* output buffer */ + stream_sample_t * buffer; /* output buffer */ + + /* output buffer position */ + int dependents; /* number of dependents */ + INT16 gain; /* gain to apply to the output */ +}; + + +struct _sound_stream +{ + /* linking information */ + sound_stream * next; /* next stream in the chain */ + void * tag; /* tag (used for identification) */ + int index; /* index for save states */ + + /* general information */ + UINT32 sample_rate; /* sample rate of this stream */ + UINT32 new_sample_rate; /* newly-set sample rate for the stream */ + + /* timing information */ + attoseconds_t attoseconds_per_sample; /* number of attoseconds per sample */ + INT32 max_samples_per_update; /* maximum samples per update */ + + /* input information */ + int inputs; /* number of inputs */ + stream_input * input; /* list of streams we directly depend upon */ + stream_sample_t ** input_array; /* array of inputs for passing to the callback */ + + /* resample buffer information */ + UINT32 resample_bufalloc; /* allocated size of each resample buffer */ + + /* output information */ + int outputs; /* number of outputs */ + stream_output * output; /* list of streams which directly depend upon us */ + stream_sample_t ** output_array; /* array of outputs for passing to the callback */ + + /* output buffer information */ + UINT32 output_bufalloc; /* allocated size of each output buffer */ + INT32 output_sampindex; /* current position within each output buffer */ + INT32 output_update_sampindex;/* position at time of last global update */ + INT32 output_base_sampindex; /* sample at base of buffer, relative to the current emulated second */ + + /* callback information */ + stream_callback callback; /* callback function */ + void * param; /* callback function parameter */ +}; + + +struct _streams_private +{ + sound_stream * stream_head; /* pointer to first stream */ + sound_stream ** stream_tailptr; /* pointer to pointer to last stream */ + void * current_tag; /* current tag to assign to new streams */ + int stream_index; /* index of the current stream */ + attoseconds_t update_attoseconds; /* attoseconds between global updates */ + attotime last_update; /* last update time */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void stream_postload(void *param); +static void allocate_resample_buffers(streams_private *strdata, sound_stream *stream); +static void allocate_output_buffers(streams_private *strdata, sound_stream *stream); +static void recompute_sample_rate_data(streams_private *strdata, sound_stream *stream); +static void generate_samples(sound_stream *stream, int samples); +static stream_sample_t *generate_resampled_data(stream_input *input, UINT32 numsamples); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + time_to_sampindex - convert an absolute + time to a sample index in a given stream +-------------------------------------------------*/ + +INLINE INT32 time_to_sampindex(const streams_private *strdata, const sound_stream *stream, attotime time) +{ + /* determine the number of samples since the start of this second */ + INT32 sample = (INT32)(time.attoseconds / stream->attoseconds_per_sample); + + /* if we're ahead of the last update, then adjust upwards */ + if (time.seconds > strdata->last_update.seconds) + { + assert(time.seconds == strdata->last_update.seconds + 1); + sample += stream->sample_rate; + } + + /* if we're behind the last update, then adjust downwards */ + if (time.seconds < strdata->last_update.seconds) + { + assert(time.seconds == strdata->last_update.seconds - 1); + sample -= stream->sample_rate; + } + return sample; +} + + + +/*************************************************************************** + CORE IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + streams_init - initialize the streams engine +-------------------------------------------------*/ + +void streams_init(running_machine *machine, attoseconds_t update_attoseconds) +{ + streams_private *strdata; + + /* allocate memory for our private data */ + strdata = auto_malloc(sizeof(*strdata)); + memset(strdata, 0, sizeof(*strdata)); + + /* reset globals */ + strdata->stream_tailptr = &strdata->stream_head; + strdata->update_attoseconds = update_attoseconds; + + /* set the global pointer */ + machine->streams_data = strdata; + + /* register global states */ + state_save_register_global(strdata->last_update.seconds); + state_save_register_global(strdata->last_update.attoseconds); +} + + +/*------------------------------------------------- + streams_update - update all the streams + periodically +-------------------------------------------------*/ + +void streams_update(running_machine *machine) +{ + streams_private *strdata = machine->streams_data; + attotime curtime = timer_get_time(); + int second_tick = FALSE; + sound_stream *stream; + + VPRINTF(("streams_update\n")); + + /* see if we ticked over to the next second */ + if (curtime.seconds != strdata->last_update.seconds) + { + assert(curtime.seconds == strdata->last_update.seconds + 1); + second_tick = TRUE; + } + + /* iterate over all the streams */ + for (stream = strdata->stream_head; stream != NULL; stream = stream->next) + { + INT32 output_bufindex = stream->output_sampindex - stream->output_base_sampindex; + int outputnum; + + /* make sure this stream is up-to-date */ + stream_update(stream); + + /* if we've ticked over another second, adjust all the counters that are relative to + the current second */ + if (second_tick) + { + stream->output_sampindex -= stream->sample_rate; + stream->output_base_sampindex -= stream->sample_rate; + } + + /* note our current output sample */ + stream->output_update_sampindex = stream->output_sampindex; + + /* if we don't have enough output buffer space to hold two updates' worth of samples, + we need to shuffle things down */ + if (stream->output_bufalloc - output_bufindex < 2 * stream->max_samples_per_update) + { + INT32 samples_to_lose = output_bufindex - stream->max_samples_per_update; + if (samples_to_lose > 0) + { + /* if we have samples to move, do so for each output */ + if (output_bufindex > 0) + for (outputnum = 0; outputnum < stream->outputs; outputnum++) + { + stream_output *output = &stream->output[outputnum]; + memmove(&output->buffer[0], &output->buffer[samples_to_lose], sizeof(output->buffer[0]) * (output_bufindex - samples_to_lose)); + } + + /* update the base position */ + stream->output_base_sampindex += samples_to_lose; + } + } + } + + /* remember the update time */ + strdata->last_update = curtime; + + /* update sample rates if they have changed */ + for (stream = strdata->stream_head; stream != NULL; stream = stream->next) + if (stream->new_sample_rate != 0) + { + UINT32 old_rate = stream->sample_rate; + int outputnum; + + /* update to the new rate and remember the old rate */ + stream->sample_rate = stream->new_sample_rate; + stream->new_sample_rate = 0; + + /* recompute all the data */ + recompute_sample_rate_data(strdata, stream); + + /* reset our sample indexes to the current time */ + stream->output_sampindex = (INT64)stream->output_sampindex * (INT64)stream->sample_rate / old_rate; + stream->output_update_sampindex = (INT64)stream->output_update_sampindex * (INT64)stream->sample_rate / old_rate; + stream->output_base_sampindex = stream->output_sampindex - stream->max_samples_per_update; + + /* clear out the buffer */ + for (outputnum = 0; outputnum < stream->outputs; outputnum++) + memset(stream->output[outputnum].buffer, 0, stream->max_samples_per_update * sizeof(stream->output[outputnum].buffer[0])); + } +} + + +/*------------------------------------------------- + streams_set_tag - set the tag to be associated + with all streams allocated from now on +-------------------------------------------------*/ + +void streams_set_tag(running_machine *machine, void *streamtag) +{ + streams_private *strdata = machine->streams_data; + strdata->current_tag = streamtag; +} + + +/*------------------------------------------------- + stream_create - create a new stream +-------------------------------------------------*/ + +sound_stream *stream_create(int inputs, int outputs, int sample_rate, void *param, stream_callback callback) +{ + streams_private *strdata = Machine->streams_data; + int inputnum, outputnum; + sound_stream *stream; + char statetag[30]; + + /* allocate memory */ + stream = auto_malloc(sizeof(*stream)); + memset(stream, 0, sizeof(*stream)); + + VPRINTF(("stream_create(%d, %d, %d) => %p\n", inputs, outputs, sample_rate, stream)); + + /* fill in the data */ + stream->tag = strdata->current_tag; + stream->index = strdata->stream_index++; + stream->sample_rate = sample_rate; + stream->inputs = inputs; + stream->outputs = outputs; + stream->callback = callback; + stream->param = param; + + /* create a unique tag for saving */ + sprintf(statetag, "stream.%d", stream->index); + state_save_register_item(statetag, 0, stream->sample_rate); + state_save_register_func_postload_ptr(stream_postload, stream); + + /* allocate space for the inputs */ + if (inputs > 0) + { + stream->input = auto_malloc(inputs * sizeof(*stream->input)); + memset(stream->input, 0, inputs * sizeof(*stream->input)); + stream->input_array = auto_malloc(inputs * sizeof(*stream->input_array)); + memset(stream->input_array, 0, inputs * sizeof(*stream->input_array)); + } + + /* initialize the state of each input */ + for (inputnum = 0; inputnum < inputs; inputnum++) + { + stream->input[inputnum].owner = stream; + stream->input[inputnum].gain = 0x100; + state_save_register_item(statetag, inputnum, stream->input[inputnum].gain); + } + + /* allocate space for the outputs */ + if (outputs > 0) + { + stream->output = auto_malloc(outputs * sizeof(*stream->output)); + memset(stream->output, 0, outputs * sizeof(*stream->output)); + stream->output_array = auto_malloc(outputs * sizeof(*stream->output_array)); + memset(stream->output_array, 0, outputs * sizeof(*stream->output_array)); + } + + /* initialize the state of each output */ + for (outputnum = 0; outputnum < outputs; outputnum++) + { + stream->output[outputnum].owner = stream; + stream->output[outputnum].gain = 0x100; + state_save_register_item(statetag, outputnum, stream->output[outputnum].gain); + } + + /* hook us into the master stream list */ + *strdata->stream_tailptr = stream; + strdata->stream_tailptr = &stream->next; + + /* force an update to the sample rates; this will cause everything to be recomputed + and will generate the initial resample buffers for our inputs */ + recompute_sample_rate_data(strdata, stream); + + /* set up the initial output buffer positions now that we have data */ + stream->output_base_sampindex = -stream->max_samples_per_update; + + return stream; +} + + +/*------------------------------------------------- + stream_set_input - configure a stream's input +-------------------------------------------------*/ + +void stream_set_input(sound_stream *stream, int index, sound_stream *input_stream, int output_index, float gain) +{ + stream_input *input; + + VPRINTF(("stream_set_input(%p, %d, %p, %d, %f)\n", stream, index, input_stream, output_index, gain)); + + /* make sure it's a valid input */ + if (index >= stream->inputs) + fatalerror("Fatal error: stream_set_input attempted to configure non-existant input %d (%d max)", index, stream->inputs); + + /* make sure it's a valid output */ + if (input_stream != NULL && output_index >= input_stream->outputs) + fatalerror("Fatal error: stream_set_input attempted to use a non-existant output %d (%d max)", output_index, input_stream->outputs); + + /* if this input is already wired, update the dependent info */ + input = &stream->input[index]; + if (input->source != NULL) + input->source->dependents--; + + /* wire it up */ + input->source = (input_stream != NULL) ? &input_stream->output[output_index] : NULL; + input->gain = (int)(0x100 * gain); + + /* update the dependent info */ + if (input->source != NULL) + input->source->dependents++; + + /* update sample rates now that we know the input */ + recompute_sample_rate_data(Machine->streams_data, stream); +} + + +/*------------------------------------------------- + stream_update - force a stream to update to + the current emulated time +-------------------------------------------------*/ + +void stream_update(sound_stream *stream) +{ + streams_private *strdata = Machine->streams_data; + INT32 update_sampindex = time_to_sampindex(strdata, stream, timer_get_time()); + + /* generate samples to get us up to the appropriate time */ + assert(stream->output_sampindex - stream->output_base_sampindex >= 0); + assert(update_sampindex - stream->output_base_sampindex <= stream->output_bufalloc); + generate_samples(stream, update_sampindex - stream->output_sampindex); + + /* remember this info for next time */ + stream->output_sampindex = update_sampindex; +} + + +/*------------------------------------------------- + stream_find_by_tag - find a stream using a + tag and index +-------------------------------------------------*/ + +sound_stream *stream_find_by_tag(void *streamtag, int streamindex) +{ + streams_private *strdata = Machine->streams_data; + sound_stream *stream; + + /* scan the list looking for the nth stream that matches the tag */ + for (stream = strdata->stream_head; stream; stream = stream->next) + if (stream->tag == streamtag && streamindex-- == 0) + return stream; + return NULL; +} + + +/*------------------------------------------------- + stream_get_inputs - return the number of + inputs for a given stream +-------------------------------------------------*/ + +int stream_get_inputs(sound_stream *stream) +{ + return stream->inputs; +} + + +/*------------------------------------------------- + stream_get_outputs - return the number of + outputs for a given stream +-------------------------------------------------*/ + +int stream_get_outputs(sound_stream *stream) +{ + return stream->outputs; +} + + +/*------------------------------------------------- + stream_set_input_gain - set the input gain on + a given stream +-------------------------------------------------*/ + +void stream_set_input_gain(sound_stream *stream, int input, float gain) +{ + stream_update(stream); + stream->input[input].gain = (int)(0x100 * gain); +} + + +/*------------------------------------------------- + stream_set_output_gain - set the output gain on + a given stream +-------------------------------------------------*/ + +void stream_set_output_gain(sound_stream *stream, int output, float gain) +{ + stream_update(stream); + stream->output[output].gain = (int)(0x100 * gain); +} + + +/*------------------------------------------------- + stream_set_sample_rate - set the sample rate + on a given stream +-------------------------------------------------*/ + +void stream_set_sample_rate(sound_stream *stream, int sample_rate) +{ + /* we will update this on the next global update */ + if ((stream->new_sample_rate == 0 && sample_rate != stream->sample_rate) || (stream->new_sample_rate != 0 && sample_rate != stream->new_sample_rate)) + stream->new_sample_rate = sample_rate; +} + + +/*------------------------------------------------- + stream_get_output_since_last_update - return a + pointer to the output buffer and the number of + samples since the last global update +-------------------------------------------------*/ + +const stream_sample_t *stream_get_output_since_last_update(sound_stream *stream, int outputnum, int *numsamples) +{ + stream_output *output = &stream->output[outputnum]; + + /* force an update on the stream */ + stream_update(stream); + + /* compute the number of samples and a pointer to the output buffer */ + *numsamples = stream->output_sampindex - stream->output_update_sampindex; + return output->buffer + (stream->output_update_sampindex - stream->output_base_sampindex); +} + + + +/*************************************************************************** + STREAM BUFFER MAINTENANCE +***************************************************************************/ + +/*------------------------------------------------- + stream_postload - save/restore callback +-------------------------------------------------*/ + +static void stream_postload(void *param) +{ + streams_private *strdata = Machine->streams_data; + sound_stream *stream = param; + int outputnum; + + /* recompute the same rate information */ + recompute_sample_rate_data(strdata, stream); + + /* make sure our output buffers are fully cleared */ + for (outputnum = 0; outputnum < stream->outputs; outputnum++) + memset(stream->output[outputnum].buffer, 0, stream->output_bufalloc * sizeof(stream->output[outputnum].buffer[0])); + + /* recompute the sample indexes to make sense */ + stream->output_sampindex = strdata->last_update.attoseconds / stream->attoseconds_per_sample; + stream->output_update_sampindex = stream->output_sampindex; + stream->output_base_sampindex = stream->output_sampindex - stream->max_samples_per_update; +} + + +/*------------------------------------------------- + allocate_resample_buffers - recompute the + resample buffer sizes and expand if necessary +-------------------------------------------------*/ + +static void allocate_resample_buffers(streams_private *strdata, sound_stream *stream) +{ + /* compute the target number of samples */ + INT32 bufsize = 2 * stream->max_samples_per_update; + + /* if we don't have enough room, allocate more */ + if (stream->resample_bufalloc < bufsize) + { + int inputnum; + + /* this becomes the new allocation size */ + stream->resample_bufalloc = bufsize; + + /* iterate over outputs and realloc their buffers */ + for (inputnum = 0; inputnum < stream->inputs; inputnum++) + { + stream_input *input = &stream->input[inputnum]; + input->resample = auto_realloc(input->resample, stream->resample_bufalloc * sizeof(input->resample[0])); + } + } +} + + +/*------------------------------------------------- + allocate_output_buffers - recompute the + output buffer sizes and expand if necessary +-------------------------------------------------*/ + +static void allocate_output_buffers(streams_private *strdata, sound_stream *stream) +{ + /* compute the target number of samples */ + INT32 bufsize = OUTPUT_BUFFER_UPDATES * stream->max_samples_per_update; + + /* if we don't have enough room, allocate more */ + if (stream->output_bufalloc < bufsize) + { + int outputnum; + int oldsize; + + /* this becomes the new allocation size */ + oldsize = stream->output_bufalloc; + stream->output_bufalloc = bufsize; + + /* iterate over outputs and realloc their buffers */ + for (outputnum = 0; outputnum < stream->outputs; outputnum++) + { + stream_output *output = &stream->output[outputnum]; + output->buffer = auto_realloc(output->buffer, stream->output_bufalloc * sizeof(output->buffer[0])); + memset(&output->buffer[oldsize], 0, (stream->output_bufalloc - oldsize) * sizeof(output->buffer[0])); + } + } +} + + +/*------------------------------------------------- + recompute_sample_rate_data - recompute sample + rate data, and all streams that are affected + by this stream +-------------------------------------------------*/ + +static void recompute_sample_rate_data(streams_private *strdata, sound_stream *stream) +{ + int inputnum; + + /* recompute the timing parameters */ + stream->attoseconds_per_sample = ATTOSECONDS_PER_SECOND / stream->sample_rate; + stream->max_samples_per_update = (strdata->update_attoseconds + stream->attoseconds_per_sample - 1) / stream->attoseconds_per_sample; + + /* update resample and output buffer sizes */ + allocate_resample_buffers(strdata, stream); + allocate_output_buffers(strdata, stream); + + /* iterate over each input */ + for (inputnum = 0; inputnum < stream->inputs; inputnum++) + { + stream_input *input = &stream->input[inputnum]; + + /* if we have a source, see if its sample rate changed */ + if (input->source != NULL) + { + sound_stream *input_stream = input->source->owner; + attoseconds_t new_attosecs_per_sample = ATTOSECONDS_PER_SECOND / input_stream->sample_rate; + attoseconds_t latency; + + /* okay, we have a new sample rate; recompute the latency to be the maximum + sample period between us and our input */ + latency = MAX(new_attosecs_per_sample, stream->attoseconds_per_sample); + + /* if the input stream's sample rate is lower, we will use linear interpolation */ + /* this requires an extra sample from the source */ + if (input_stream->sample_rate < stream->sample_rate) + latency += new_attosecs_per_sample; + + /* if our sample rates match exactly, we don't need any latency */ + else if (input_stream->sample_rate == stream->sample_rate) + latency = 0; + + /* we generally don't want to tweak the latency, so we just keep the greatest + one we've computed thus far */ + input->latency_attoseconds = MAX(input->latency_attoseconds, latency); + assert(input->latency_attoseconds < strdata->update_attoseconds); + } + } +} + + + +/*************************************************************************** + SOUND GENERATION +***************************************************************************/ + +/*------------------------------------------------- + generate_samples - generate the requested + number of samples for a stream, making sure + all inputs have the appropriate number of + samples generated +-------------------------------------------------*/ + +static void generate_samples(sound_stream *stream, int samples) +{ + int inputnum, outputnum; + + /* if we're already there, skip it */ + if (samples <= 0) + return; + + VPRINTF(("generate_samples(%p, %d)\n", stream, samples)); + + /* ensure all inputs are up to date and generate resampled data */ + for (inputnum = 0; inputnum < stream->inputs; inputnum++) + { + stream_input *input = &stream->input[inputnum]; + + /* update the stream to the current time */ + if (input->source != NULL) + stream_update(input->source->owner); + + /* generate the resampled data */ + stream->input_array[inputnum] = generate_resampled_data(input, samples); + } + + /* loop over all outputs and compute the output pointer */ + for (outputnum = 0; outputnum < stream->outputs; outputnum++) + { + stream_output *output = &stream->output[outputnum]; + stream->output_array[outputnum] = output->buffer + (stream->output_sampindex - stream->output_base_sampindex); + } + + /* run the callback */ + VPRINTF((" callback(%p, %d)\n", stream, samples)); + (*stream->callback)(stream->param, stream->input_array, stream->output_array, samples); + VPRINTF((" callback done\n")); +} + + +/*------------------------------------------------- + generate_resampled_data - generate the + resample buffer for a given input +-------------------------------------------------*/ + +static stream_sample_t *generate_resampled_data(stream_input *input, UINT32 numsamples) +{ + stream_sample_t *dest = input->resample; + stream_output *output = input->source; + sound_stream *stream = input->owner; + sound_stream *input_stream; + stream_sample_t *source; + stream_sample_t sample; + attoseconds_t basetime; + INT32 basesample; + UINT32 basefrac; + UINT32 step; + int gain; + + /* if we don't have an output to pull data from, generate silence */ + if (output == NULL) + { + memset(dest, 0, numsamples * sizeof(*dest)); + return input->resample; + } + + /* grab data from the output */ + input_stream = output->owner; + gain = (input->gain * output->gain) >> 8; + + /* determine the time at which the current sample begins, accounting for the + latency we calculated between the input and output streams */ + basetime = stream->output_sampindex * stream->attoseconds_per_sample - input->latency_attoseconds; + + /* now convert that time into a sample in the input stream */ + if (basetime >= 0) + basesample = basetime / input_stream->attoseconds_per_sample; + else + basesample = -(-basetime / input_stream->attoseconds_per_sample) - 1; + + /* compute a source pointer to the first sample */ + assert(basesample >= input_stream->output_base_sampindex); + source = output->buffer + (basesample - input_stream->output_base_sampindex); + + /* determine the current fraction of a sample */ + basefrac = (basetime - basesample * input_stream->attoseconds_per_sample) / (ATTOSECONDS_PER_SECOND >> FRAC_BITS); + assert(basefrac >= 0); + assert(basefrac < FRAC_ONE); + + /* compute the stepping fraction */ + step = ((UINT64)input_stream->sample_rate << FRAC_BITS) / stream->sample_rate; + + /* if we have equal sample rates, we just need to copy */ + if (step == FRAC_ONE) + { + while (numsamples--) + { + /* compute the sample */ + sample = *source++; + *dest++ = (sample * gain) >> 8; + } + } + + /* input is undersampled: use linear interpolation */ + else if (step < FRAC_ONE) + { + while (numsamples--) + { + int interp_frac = basefrac >> (FRAC_BITS - 12); + + /* compute the sample */ + sample = (source[0] * (0x1000 - interp_frac) + source[1] * interp_frac) >> 12; + *dest++ = (sample * gain) >> 8; + + /* advance */ + basefrac += step; + source += basefrac >> FRAC_BITS; + basefrac &= FRAC_MASK; + } + } + + /* input is oversampled: sum the energy */ + else + { + /* use 8 bits to allow some extra headroom */ + int smallstep = step >> (FRAC_BITS - 8); + + while (numsamples--) + { + int remainder = smallstep; + int tpos = 0; + int scale; + + /* compute the sample */ + scale = (FRAC_ONE - basefrac) >> (FRAC_BITS - 8); + sample = source[tpos++] * scale; + remainder -= scale; + while (remainder > 0x100) + { + sample += source[tpos++] * 0x100; + remainder -= 0x100; + } + sample += source[tpos] * remainder; + sample /= smallstep; + + *dest++ = (sample * gain) >> 8; + + /* advance */ + basefrac += step; + source += basefrac >> FRAC_BITS; + basefrac &= FRAC_MASK; + } + } + + return input->resample; +} |