mirror of
https://git.code.sf.net/p/fuse-emulator/fuse
synced 2026-01-27 01:41:34 +03:00
618 lines
15 KiB
C
618 lines
15 KiB
C
/* blipbuffer.c
|
|
|
|
Copyright (C) 2003-2006 Shay Green. This module is free software; you
|
|
can redistribute it and/or modify it under the terms of the GNU Lesser
|
|
General Public License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version. This
|
|
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
|
more details. You should have received a copy of the GNU Lesser General
|
|
Public License along with this module; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
Original C++ source:
|
|
Blip_Buffer 0.4.0. http://www.slack.net/~ant/
|
|
|
|
partially reimplemented in C by Gergely Szasz for FUSE
|
|
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
|
|
#include "blipbuffer.h"
|
|
|
|
|
|
static void _blip_synth_init( Blip_Synth_ * synth_, short *impulses );
|
|
|
|
inline void
|
|
blip_buffer_set_clock_rate( Blip_Buffer * buff, long cps )
|
|
{
|
|
buff->factor_ = blip_buffer_clock_rate_factor( buff, buff->clock_rate_ =
|
|
cps );
|
|
}
|
|
|
|
inline long
|
|
blip_buffer_samples_avail( Blip_Buffer * buff )
|
|
{
|
|
return ( long )( buff->offset_ >> BLIP_BUFFER_ACCURACY );
|
|
}
|
|
|
|
void
|
|
blip_synth_set_output( Blip_Synth * synth, Blip_Buffer * b )
|
|
{
|
|
synth->impl.buf = b;
|
|
synth->impl.last_amp = 0;
|
|
}
|
|
|
|
void
|
|
blip_synth_set_volume( Blip_Synth * synth, double v )
|
|
{
|
|
_blip_synth_volume_unit( &synth->impl,
|
|
v * ( 1.0 /
|
|
( BLIP_SYNTH_RANGE <
|
|
0 ? -( BLIP_SYNTH_RANGE ) :
|
|
BLIP_SYNTH_RANGE ) ) );
|
|
}
|
|
|
|
#define BLIP_FWD( i ) \
|
|
t0 = i0 * delta + buf[fwd + i]; \
|
|
t1 = imp[BLIP_RES * (i + 1)] * delta + buf[fwd + 1 + i]; \
|
|
i0 = imp[BLIP_RES * (i + 2)]; \
|
|
buf[fwd + i] = t0; \
|
|
buf[fwd + 1 + i] = t1
|
|
|
|
#define BLIP_REV( r ) \
|
|
t0 = i0 * delta + buf[rev - r]; \
|
|
t1 = imp[BLIP_RES * r] * delta + buf[rev + 1 - r]; \
|
|
i0 = imp[BLIP_RES * (r - 1)]; \
|
|
buf[rev - r] = t0; \
|
|
buf[rev + 1 - r] = t1
|
|
|
|
inline void
|
|
blip_synth_offset_resampled( Blip_Synth * synth, blip_resampled_time_t time,
|
|
int delta, Blip_Buffer * blip_buf )
|
|
{
|
|
int phase, fwd, rev, mid;
|
|
|
|
imp_t *imp;
|
|
|
|
long *buf, i0, t0, t1;
|
|
|
|
delta *= synth->impl.delta_factor;
|
|
phase =
|
|
( int )( time >> ( BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS ) &
|
|
( BLIP_RES - 1 ) );
|
|
imp = synth->impulses + BLIP_RES - phase;
|
|
buf = blip_buf->buffer_ + ( time >> BLIP_BUFFER_ACCURACY );
|
|
i0 = *imp;
|
|
|
|
fwd = ( BLIP_WIDEST_IMPULSE_ - BLIP_SYNTH_QUALITY ) / 2;
|
|
rev = fwd + BLIP_SYNTH_QUALITY - 2;
|
|
|
|
BLIP_FWD( 0 );
|
|
if( BLIP_SYNTH_QUALITY > 8 ) {
|
|
BLIP_FWD( 2 );
|
|
}
|
|
if( BLIP_SYNTH_QUALITY > 12 ) {
|
|
BLIP_FWD( 4 );
|
|
}
|
|
|
|
mid = BLIP_SYNTH_QUALITY / 2 - 1;
|
|
t0 = i0 * delta + buf[fwd + mid - 1];
|
|
t1 = imp[BLIP_RES * mid] * delta + buf[fwd + mid];
|
|
imp = synth->impulses + phase;
|
|
i0 = imp[BLIP_RES * mid];
|
|
buf[fwd + mid - 1] = t0;
|
|
buf[fwd + mid] = t1;
|
|
|
|
if( BLIP_SYNTH_QUALITY > 12 ) {
|
|
BLIP_REV( 6 );
|
|
}
|
|
if( BLIP_SYNTH_QUALITY > 8 ) {
|
|
BLIP_REV( 4 );
|
|
}
|
|
BLIP_REV( 2 );
|
|
|
|
t0 = i0 * delta + buf[rev];
|
|
t1 = *imp * delta + buf[rev + 1];
|
|
buf[rev] = t0;
|
|
buf[rev + 1] = t1;
|
|
}
|
|
|
|
#undef BLIP_FWD
|
|
#undef BLIP_REV
|
|
|
|
void
|
|
blip_synth_update( Blip_Synth * synth, blip_time_t t, int amp )
|
|
{
|
|
int delta = amp - synth->impl.last_amp;
|
|
|
|
synth->impl.last_amp = amp;
|
|
blip_synth_offset_resampled( synth,
|
|
t * synth->impl.buf->factor_ +
|
|
synth->impl.buf->offset_, delta,
|
|
synth->impl.buf );
|
|
}
|
|
|
|
int
|
|
_blip_synth_impulses_size( Blip_Synth_ * synth_ )
|
|
{
|
|
return BLIP_RES / 2 * BLIP_SYNTH_WIDTH + 1;
|
|
}
|
|
|
|
void
|
|
blip_synth_set_treble_eq( Blip_Synth * synth, double treble )
|
|
{
|
|
blip_eq_t eq = { 0.0, 0, 44100, 0 };
|
|
|
|
eq.treble = treble;
|
|
|
|
_blip_synth_treble_eq( &synth->impl, &eq );
|
|
}
|
|
|
|
#define BUFFER_EXTRA ( BLIP_WIDEST_IMPULSE_ + 2 )
|
|
|
|
static void
|
|
blip_buffer_init( Blip_Buffer * buff )
|
|
{
|
|
buff->factor_ = LONG_MAX;
|
|
buff->offset_ = 0;
|
|
buff->buffer_ = NULL;
|
|
buff->buffer_size_ = 0;
|
|
buff->sample_rate_ = 0;
|
|
buff->reader_accum = 0;
|
|
buff->bass_shift = 0;
|
|
buff->clock_rate_ = 0;
|
|
buff->bass_freq_ = 16;
|
|
buff->length_ = 0;
|
|
}
|
|
|
|
static void
|
|
blip_buffer_end( Blip_Buffer * buff )
|
|
{
|
|
if( buff->buffer_ )
|
|
free( buff->buffer_ );
|
|
buff->buffer_ = NULL;
|
|
}
|
|
|
|
Blip_Buffer *
|
|
new_Blip_Buffer( void )
|
|
{
|
|
Blip_Buffer *ret;
|
|
|
|
ret = malloc( sizeof( Blip_Buffer ) );
|
|
if( ret ) {
|
|
blip_buffer_init( ret );
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
delete_Blip_Buffer( Blip_Buffer ** buff )
|
|
{
|
|
if( !*buff )
|
|
return;
|
|
|
|
blip_buffer_end( *buff );
|
|
free( *buff );
|
|
*buff = NULL;
|
|
}
|
|
|
|
static void
|
|
blip_synth_init( Blip_Synth * synth )
|
|
{
|
|
synth->impulses =
|
|
malloc( ( BLIP_RES * ( BLIP_SYNTH_QUALITY / 2 ) +
|
|
1 ) * sizeof( imp_t ) * 4 );
|
|
if( synth->impulses ) {
|
|
_blip_synth_init( &synth->impl, ( short * )synth->impulses ); /* sorry, somewhere imp_t, somewhere short ???? */
|
|
}
|
|
}
|
|
|
|
static void
|
|
blip_synth_end( Blip_Synth * synth )
|
|
{
|
|
if( synth->impulses ) {
|
|
free( synth->impulses );
|
|
synth->impulses = NULL;
|
|
}
|
|
}
|
|
|
|
Blip_Synth *
|
|
new_Blip_Synth( void )
|
|
{
|
|
Blip_Synth *ret;
|
|
|
|
ret = malloc( sizeof( Blip_Synth ) );
|
|
if( ret ) {
|
|
blip_synth_init( ret );
|
|
if( !ret->impulses ) {
|
|
free( ret );
|
|
return NULL;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
delete_Blip_Synth( Blip_Synth ** synth )
|
|
{
|
|
if( !*synth )
|
|
return;
|
|
|
|
blip_synth_end( *synth );
|
|
free( *synth );
|
|
*synth = NULL;
|
|
}
|
|
|
|
void
|
|
blip_buffer_clear( Blip_Buffer * buff, int entire_buffer )
|
|
{
|
|
buff->offset_ = 0;
|
|
buff->reader_accum = 0;
|
|
if( buff->buffer_ ) {
|
|
long count =
|
|
( entire_buffer ? buff->
|
|
buffer_size_ : blip_buffer_samples_avail( buff ) );
|
|
memset( buff->buffer_, 0, ( count + BUFFER_EXTRA ) * sizeof( buf_t_ ) );
|
|
}
|
|
}
|
|
|
|
blargg_err_t
|
|
blip_buffer_set_sample_rate( Blip_Buffer * buff, long new_rate, int msec )
|
|
{
|
|
/* start with maximum length that resampled time can represent */
|
|
long new_size = ( ULONG_MAX >> BLIP_BUFFER_ACCURACY ) - BUFFER_EXTRA - 64;
|
|
|
|
if( msec != BLIP_MAX_LENGTH ) {
|
|
long s = ( new_rate * ( msec + 1 ) + 999 ) / 1000;
|
|
|
|
if( s < new_size )
|
|
new_size = s;
|
|
}
|
|
|
|
if( buff->buffer_size_ != new_size ) {
|
|
void *p =
|
|
realloc( buff->buffer_,
|
|
( new_size + BUFFER_EXTRA ) * sizeof( buf_t_ ) );
|
|
if( !p )
|
|
return "Out of memory";
|
|
buff->buffer_ = ( buf_t_ * ) p;
|
|
}
|
|
|
|
buff->buffer_size_ = new_size;
|
|
|
|
/* update things based on the sample rate */
|
|
buff->sample_rate_ = new_rate;
|
|
buff->length_ = new_size * 1000 / new_rate - 1;
|
|
if( buff->clock_rate_ )
|
|
blip_buffer_set_clock_rate( buff, buff->clock_rate_ );
|
|
blip_buffer_set_bass_freq( buff, buff->bass_freq_ );
|
|
|
|
blip_buffer_clear( buff, BLIP_BUFFER_DEF_ENTIRE_BUFF );
|
|
|
|
return 0; /* success */
|
|
}
|
|
|
|
blip_resampled_time_t
|
|
blip_buffer_clock_rate_factor( Blip_Buffer * buff, long clock_rate )
|
|
{
|
|
double ratio = ( double )buff->sample_rate_ / clock_rate;
|
|
|
|
long factor = ( long )floor( ratio * ( 1L << BLIP_BUFFER_ACCURACY ) + 0.5 );
|
|
|
|
return ( blip_resampled_time_t ) factor;
|
|
}
|
|
|
|
void
|
|
blip_buffer_set_bass_freq( Blip_Buffer * buff, int freq )
|
|
{
|
|
int shift = 31;
|
|
|
|
long f;
|
|
|
|
buff->bass_freq_ = freq;
|
|
if( freq > 0 ) {
|
|
shift = 13;
|
|
f = ( freq << 16 ) / buff->sample_rate_;
|
|
while( ( f >>= 1 ) && --shift ) {};
|
|
}
|
|
|
|
buff->bass_shift = shift;
|
|
}
|
|
|
|
void
|
|
blip_buffer_end_frame( Blip_Buffer * buff, blip_time_t t )
|
|
{
|
|
buff->offset_ += t * buff->factor_;
|
|
}
|
|
|
|
inline void
|
|
blip_buffer_remove_silence( Blip_Buffer * buff, long count )
|
|
{
|
|
buff->offset_ -= ( blip_resampled_time_t ) count << BLIP_BUFFER_ACCURACY;
|
|
}
|
|
|
|
inline void
|
|
blip_buffer_remove_samples( Blip_Buffer * buff, long count )
|
|
{
|
|
long remain;
|
|
|
|
if( count ) {
|
|
blip_buffer_remove_silence( buff, count );
|
|
|
|
/* copy remaining samples to beginning and clear old samples */
|
|
remain = blip_buffer_samples_avail( buff ) + BUFFER_EXTRA;
|
|
memmove( buff->buffer_, buff->buffer_ + count,
|
|
remain * sizeof( buf_t_ ) );
|
|
memset( buff->buffer_ + remain, 0, count * sizeof( buf_t_ ) );
|
|
}
|
|
}
|
|
|
|
/* Blip_Synth_ */
|
|
|
|
void
|
|
_blip_synth_init( Blip_Synth_ * synth_, short *p )
|
|
{
|
|
synth_->impulses = p;
|
|
synth_->volume_unit_ = 0.0;
|
|
synth_->kernel_unit = 0;
|
|
synth_->buf = NULL;
|
|
synth_->last_amp = 0;
|
|
synth_->delta_factor = 0;
|
|
}
|
|
|
|
#define PI 3.1415926535897932384626433832795029
|
|
|
|
static void
|
|
gen_sinc( float *out, int count, double oversample, double treble,
|
|
double cutoff )
|
|
{
|
|
int i;
|
|
|
|
double maxh, rolloff, pow_a_n, to_angle;
|
|
|
|
if( cutoff > 0.999 )
|
|
cutoff = 0.999;
|
|
if( treble < -300.0 )
|
|
treble = -300.0;
|
|
if( treble > 5.0 )
|
|
treble = 5.0;
|
|
|
|
maxh = 4096.0;
|
|
rolloff = pow( 10.0, 1.0 / ( maxh * 20.0 ) * treble / ( 1.0 - cutoff ) );
|
|
pow_a_n = pow( rolloff, maxh - maxh * cutoff );
|
|
to_angle = PI / 2 / maxh / oversample;
|
|
for( i = 0; i < count; i++ ) {
|
|
double angle, c, cos_nc_angle, cos_nc1_angle, cos_angle, d, b, a;
|
|
|
|
angle = ( ( i - count ) * 2 + 1 ) * to_angle;
|
|
c = rolloff * cos( ( maxh - 1.0 ) * angle ) - cos( maxh * angle );
|
|
cos_nc_angle = cos( maxh * cutoff * angle );
|
|
cos_nc1_angle = cos( ( maxh * cutoff - 1.0 ) * angle );
|
|
cos_angle = cos( angle );
|
|
|
|
c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
|
|
d = 1.0 + rolloff * ( rolloff - cos_angle - cos_angle );
|
|
b = 2.0 - cos_angle - cos_angle;
|
|
a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
|
|
|
|
out[i] = ( float )( ( a * d + c * b ) / ( b * d ) ); /* a / b + c / d */
|
|
}
|
|
}
|
|
|
|
static void
|
|
blip_eq_generate( blip_eq_t * eq, float *out, int count )
|
|
{
|
|
/* lower cutoff freq for narrow kernels with their wider transition band
|
|
(8 points->1.49, 16 points->1.15) */
|
|
int i;
|
|
|
|
double to_fraction, cutoff;
|
|
|
|
double oversample = BLIP_RES * 2.25 / count + 0.85;
|
|
|
|
double half_rate = eq->sample_rate * 0.5;
|
|
|
|
if( eq->cutoff_freq )
|
|
oversample = half_rate / eq->cutoff_freq;
|
|
|
|
cutoff = eq->rolloff_freq * oversample / half_rate;
|
|
|
|
gen_sinc( out, count, BLIP_RES * oversample, eq->treble, cutoff );
|
|
|
|
/* apply (half of) hamming window */
|
|
to_fraction = PI / ( count - 1 );
|
|
for( i = count; i--; )
|
|
out[i] *= 0.54 - 0.46 * cos( i * to_fraction );
|
|
|
|
}
|
|
|
|
void
|
|
_blip_synth_adjust_impulse( Blip_Synth_ * synth_ )
|
|
{
|
|
/* sum pairs for each phase and add error correction to end of first half */
|
|
int size = _blip_synth_impulses_size( synth_ );
|
|
|
|
int i, p, p2, error;
|
|
|
|
for( p = BLIP_RES; p-- >= BLIP_RES / 2; ) {
|
|
error = synth_->kernel_unit;
|
|
p2 = BLIP_RES - 2 - p;
|
|
for( i = 1; i < size; i += BLIP_RES ) {
|
|
error -= synth_->impulses[i + p];
|
|
error -= synth_->impulses[i + p2];
|
|
}
|
|
|
|
if( p == p2 )
|
|
error /= 2; /* phase = 0.5 impulse uses same half for both sides */
|
|
|
|
synth_->impulses[size - BLIP_RES + p] += error;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
_blip_synth_treble_eq( Blip_Synth_ * synth_, blip_eq_t * eq )
|
|
{
|
|
double total, base_unit, rescale, sum, next, vol;
|
|
|
|
int impulses_size, i;
|
|
|
|
float fimpulse[BLIP_RES / 2 * ( BLIP_WIDEST_IMPULSE_ - 1 ) + BLIP_RES * 2];
|
|
|
|
int half_size = BLIP_RES / 2 * ( BLIP_SYNTH_WIDTH - 1 );
|
|
|
|
blip_eq_generate( eq, &fimpulse[BLIP_RES], half_size );
|
|
|
|
/* need mirror slightly past center for calculation */
|
|
for( i = BLIP_RES; i--; )
|
|
fimpulse[BLIP_RES + half_size + i] =
|
|
fimpulse[BLIP_RES + half_size - 1 - i];
|
|
|
|
/* starts at 0 */
|
|
for( i = 0; i < BLIP_RES; i++ )
|
|
fimpulse[i] = 0.0f;
|
|
|
|
/* find rescale factor */
|
|
total = 0.0;
|
|
for( i = 0; i < half_size; i++ )
|
|
total += fimpulse[BLIP_RES + i];
|
|
|
|
/* double const base_unit = 44800.0 - 128 * 18; allows treble up to +0 dB
|
|
double const base_unit = 37888.0; allows treble to +5 dB */
|
|
base_unit = 32768.0; /* necessary for blip_unscaled to work */
|
|
rescale = base_unit / 2 / total;
|
|
synth_->kernel_unit = ( long )base_unit;
|
|
|
|
/* integrate, first difference, rescale, convert to int */
|
|
sum = 0.0;
|
|
next = 0.0;
|
|
impulses_size = _blip_synth_impulses_size( synth_ );
|
|
|
|
for( i = 0; i < impulses_size; i++ ) {
|
|
synth_->impulses[i] = ( short )floor( ( next - sum ) * rescale + 0.5 );
|
|
sum += fimpulse[i];
|
|
next += fimpulse[i + BLIP_RES];
|
|
}
|
|
|
|
_blip_synth_adjust_impulse( synth_ );
|
|
|
|
/* volume might require rescaling */
|
|
vol = synth_->volume_unit_;
|
|
if( vol ) {
|
|
synth_->volume_unit_ = 0.0;
|
|
_blip_synth_volume_unit( synth_, vol );
|
|
}
|
|
}
|
|
|
|
void
|
|
_blip_synth_volume_unit( Blip_Synth_ * synth_, double new_unit )
|
|
{
|
|
if( new_unit != synth_->volume_unit_ ) {
|
|
double factor;
|
|
|
|
/* use default eq if it hasn't been set yet */
|
|
if( !synth_->kernel_unit ) {
|
|
blip_eq_t eq = { -8.0, 0, 44100, 0 };
|
|
|
|
_blip_synth_treble_eq( synth_, &eq );
|
|
}
|
|
|
|
synth_->volume_unit_ = new_unit;
|
|
factor = new_unit * ( 1L << BLIP_SAMPLE_BITS ) / synth_->kernel_unit;
|
|
|
|
if( factor > 0.0 ) {
|
|
int shift = 0;
|
|
|
|
/* if unit is really small, might need to attenuate kernel */
|
|
while( factor < 2.0 ) {
|
|
shift++;
|
|
factor *= 2.0;
|
|
}
|
|
|
|
if( shift ) {
|
|
/* keep values positive to avoid round-towards-zero of sign-preserving
|
|
* right shift for negative values */
|
|
long offset = 0x8000 + ( 1 << ( shift - 1 ) );
|
|
|
|
long offset2 = 0x8000 >> shift;
|
|
|
|
int i;
|
|
|
|
synth_->kernel_unit >>= shift;
|
|
|
|
for( i = _blip_synth_impulses_size( synth_ ); i--; )
|
|
synth_->impulses[i] =
|
|
( short )( ( ( synth_->impulses[i] + offset ) >> shift ) -
|
|
offset2 );
|
|
|
|
_blip_synth_adjust_impulse( synth_ );
|
|
}
|
|
}
|
|
synth_->delta_factor = ( int )floor( factor + 0.5 );
|
|
}
|
|
}
|
|
|
|
long
|
|
blip_buffer_read_samples( Blip_Buffer * buff, blip_sample_t * out,
|
|
long max_samples, int stereo )
|
|
{
|
|
long count = blip_buffer_samples_avail( buff );
|
|
|
|
if( count > max_samples )
|
|
count = max_samples;
|
|
|
|
if( count ) {
|
|
int sample_shift = BLIP_SAMPLE_BITS - 16;
|
|
|
|
int my_bass_shift = buff->bass_shift;
|
|
|
|
long accum = buff->reader_accum;
|
|
|
|
buf_t_ *in = buff->buffer_;
|
|
|
|
if( !stereo ) {
|
|
int n;
|
|
|
|
for( n = count; n--; ) {
|
|
long s = accum >> sample_shift;
|
|
|
|
accum -= accum >> my_bass_shift;
|
|
accum += *in++;
|
|
*out++ = ( blip_sample_t ) s;
|
|
|
|
/* clamp sample */
|
|
if( ( blip_sample_t ) s != s )
|
|
out[-1] = ( blip_sample_t ) ( 0x7FFF - ( s >> 24 ) );
|
|
}
|
|
} else {
|
|
int n;
|
|
|
|
for( n = count; n--; ) {
|
|
long s = accum >> sample_shift;
|
|
|
|
accum -= accum >> my_bass_shift;
|
|
accum += *in++;
|
|
*out = ( blip_sample_t ) s;
|
|
out += 2;
|
|
|
|
/* clamp sample */
|
|
if( ( blip_sample_t ) s != s )
|
|
out[-2] = ( blip_sample_t ) ( 0x7FFF - ( s >> 24 ) );
|
|
}
|
|
}
|
|
|
|
buff->reader_accum = accum;
|
|
blip_buffer_remove_samples( buff, count );
|
|
}
|
|
|
|
return count;
|
|
}
|