510 lines
14 KiB
C++
510 lines
14 KiB
C++
// Blip_Buffer $vers. http://www.slack.net/~ant/
|
|
|
|
#include "Blip_Buffer.h"
|
|
|
|
#include <math.h>
|
|
|
|
/* Copyright (C) 2003-2008 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
#include "blargg_source.h"
|
|
|
|
//// Blip_Buffer
|
|
|
|
Blip_Buffer::Blip_Buffer()
|
|
{
|
|
factor_ = UINT_MAX/2 + 1;
|
|
buffer_ = NULL;
|
|
buffer_center_ = NULL;
|
|
buffer_size_ = 0;
|
|
sample_rate_ = 0;
|
|
bass_shift_ = 0;
|
|
clock_rate_ = 0;
|
|
bass_freq_ = 16;
|
|
length_ = 0;
|
|
|
|
// assumptions code makes about implementation-defined features
|
|
#ifndef NDEBUG
|
|
// right shift of negative value preserves sign
|
|
int i = -0x7FFFFFFE;
|
|
assert( (i >> 1) == -0x3FFFFFFF );
|
|
|
|
// casting truncates and sign-extends
|
|
i = 0x18000;
|
|
assert( (BOOST::int16_t) i == -0x8000 );
|
|
#endif
|
|
|
|
clear();
|
|
}
|
|
|
|
Blip_Buffer::~Blip_Buffer()
|
|
{
|
|
free( buffer_ );
|
|
}
|
|
|
|
void Blip_Buffer::clear()
|
|
{
|
|
bool const entire_buffer = true;
|
|
|
|
offset_ = 0;
|
|
reader_accum_ = 0;
|
|
modified_ = false;
|
|
|
|
if ( buffer_ )
|
|
{
|
|
int count = (entire_buffer ? buffer_size_ : samples_avail());
|
|
memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (delta_t) );
|
|
}
|
|
}
|
|
|
|
blargg_err_t Blip_Buffer::set_sample_rate( int new_rate, int msec )
|
|
{
|
|
// Limit to maximum size that resampled time can represent
|
|
int max_size = (((blip_resampled_time_t) -1) >> BLIP_BUFFER_ACCURACY) -
|
|
blip_buffer_extra_ - 64; // TODO: -64 isn't needed
|
|
int new_size = (new_rate * (msec + 1) + 999) / 1000;
|
|
if ( new_size > max_size )
|
|
new_size = max_size;
|
|
|
|
// Resize buffer
|
|
if ( buffer_size_ != new_size )
|
|
{
|
|
//dprintf( "%d \n", (new_size + blip_buffer_extra_) * sizeof *buffer_ );
|
|
void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ );
|
|
CHECK_ALLOC( p );
|
|
buffer_ = (delta_t*) p;
|
|
buffer_center_ = buffer_ + BLIP_MAX_QUALITY/2;
|
|
buffer_size_ = new_size;
|
|
}
|
|
|
|
// Update sample_rate and things that depend on it
|
|
sample_rate_ = new_rate;
|
|
length_ = new_size * 1000 / new_rate - 1;
|
|
if ( clock_rate_ )
|
|
clock_rate( clock_rate_ );
|
|
bass_freq( bass_freq_ );
|
|
|
|
clear();
|
|
|
|
return blargg_ok;
|
|
}
|
|
|
|
blip_resampled_time_t Blip_Buffer::clock_rate_factor( int rate ) const
|
|
{
|
|
double ratio = (double) sample_rate_ / rate;
|
|
int factor = (int) floor( ratio * (1 << BLIP_BUFFER_ACCURACY) + 0.5 );
|
|
assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large
|
|
return (blip_resampled_time_t) factor;
|
|
}
|
|
|
|
void Blip_Buffer::bass_freq( int freq )
|
|
{
|
|
bass_freq_ = freq;
|
|
int shift = 31;
|
|
if ( freq > 0 && sample_rate_ )
|
|
{
|
|
shift = 13;
|
|
int f = (freq << 16) / sample_rate_;
|
|
while ( (f >>= 1) != 0 && --shift ) { }
|
|
}
|
|
bass_shift_ = shift;
|
|
}
|
|
|
|
void Blip_Buffer::end_frame( blip_time_t t )
|
|
{
|
|
offset_ += t * factor_;
|
|
assert( samples_avail() <= (int) buffer_size_ ); // fails if time is past end of buffer
|
|
}
|
|
|
|
int Blip_Buffer::count_samples( blip_time_t t ) const
|
|
{
|
|
blip_resampled_time_t last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY;
|
|
blip_resampled_time_t first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
|
|
return (int) (last_sample - first_sample);
|
|
}
|
|
|
|
blip_time_t Blip_Buffer::count_clocks( int count ) const
|
|
{
|
|
if ( count > buffer_size_ )
|
|
count = buffer_size_;
|
|
blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
|
|
return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_);
|
|
}
|
|
|
|
void Blip_Buffer::remove_samples( int count )
|
|
{
|
|
if ( count )
|
|
{
|
|
remove_silence( count );
|
|
|
|
// copy remaining samples to beginning and clear old samples
|
|
int remain = samples_avail() + blip_buffer_extra_;
|
|
memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ );
|
|
memset( buffer_ + remain, 0, count * sizeof *buffer_ );
|
|
}
|
|
}
|
|
|
|
int Blip_Buffer::read_samples( blip_sample_t out_ [], int max_samples, bool stereo )
|
|
{
|
|
int count = samples_avail();
|
|
if ( count > max_samples )
|
|
count = max_samples;
|
|
|
|
if ( count )
|
|
{
|
|
int const bass = highpass_shift();
|
|
delta_t const* reader = read_pos() + count;
|
|
int reader_sum = integrator();
|
|
|
|
blip_sample_t* BLARGG_RESTRICT out = out_ + count;
|
|
if ( stereo )
|
|
out += count;
|
|
int offset = -count;
|
|
|
|
if ( !stereo )
|
|
{
|
|
do
|
|
{
|
|
int s = reader_sum >> delta_bits;
|
|
|
|
reader_sum -= reader_sum >> bass;
|
|
reader_sum += reader [offset];
|
|
|
|
BLIP_CLAMP( s, s );
|
|
out [offset] = (blip_sample_t) s;
|
|
}
|
|
while ( ++offset );
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
int s = reader_sum >> delta_bits;
|
|
|
|
reader_sum -= reader_sum >> bass;
|
|
reader_sum += reader [offset];
|
|
|
|
BLIP_CLAMP( s, s );
|
|
out [offset * 2] = (blip_sample_t) s;
|
|
}
|
|
while ( ++offset );
|
|
}
|
|
|
|
set_integrator( reader_sum );
|
|
|
|
remove_samples( count );
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void Blip_Buffer::mix_samples( blip_sample_t const in [], int count )
|
|
{
|
|
delta_t* out = buffer_center_ + (offset_ >> BLIP_BUFFER_ACCURACY);
|
|
|
|
int const sample_shift = blip_sample_bits - 16;
|
|
int prev = 0;
|
|
while ( --count >= 0 )
|
|
{
|
|
int s = *in++ << sample_shift;
|
|
*out += s - prev;
|
|
prev = s;
|
|
++out;
|
|
}
|
|
*out -= prev;
|
|
}
|
|
|
|
void Blip_Buffer::save_state( blip_buffer_state_t* out )
|
|
{
|
|
assert( samples_avail() == 0 );
|
|
out->offset_ = offset_;
|
|
out->reader_accum_ = reader_accum_;
|
|
memcpy( out->buf, &buffer_ [offset_ >> BLIP_BUFFER_ACCURACY], sizeof out->buf );
|
|
}
|
|
|
|
void Blip_Buffer::load_state( blip_buffer_state_t const& in )
|
|
{
|
|
clear();
|
|
|
|
offset_ = in.offset_;
|
|
reader_accum_ = in.reader_accum_;
|
|
memcpy( buffer_, in.buf, sizeof in.buf );
|
|
}
|
|
|
|
|
|
//// Blip_Synth_
|
|
|
|
Blip_Synth_Fast_::Blip_Synth_Fast_()
|
|
{
|
|
buf = NULL;
|
|
last_amp = 0;
|
|
delta_factor = 0;
|
|
}
|
|
|
|
void Blip_Synth_Fast_::volume_unit( double new_unit )
|
|
{
|
|
delta_factor = int (new_unit * (1 << blip_sample_bits) + 0.5);
|
|
}
|
|
|
|
#if BLIP_BUFFER_FAST
|
|
|
|
void blip_eq_t::generate( float* out, int count ) const { }
|
|
|
|
#else
|
|
|
|
Blip_Synth_::Blip_Synth_( short p [], int w ) :
|
|
phases( p ),
|
|
width( w )
|
|
{
|
|
volume_unit_ = 0.0;
|
|
kernel_unit = 0;
|
|
buf = NULL;
|
|
last_amp = 0;
|
|
delta_factor = 0;
|
|
}
|
|
|
|
#undef PI
|
|
#define PI 3.1415926535897932384626433832795029
|
|
|
|
// Generates right half of sinc kernel (including center point) with cutoff at
|
|
// sample rate / 2 / oversample. Frequency response at cutoff frequency is
|
|
// treble dB (-6=0.5,-12=0.25). Mid controls frequency that rolloff begins at,
|
|
// cut * sample rate / 2.
|
|
static void gen_sinc( float out [], int out_size, double oversample,
|
|
double treble, double mid )
|
|
{
|
|
if ( mid > 0.9999 ) mid = 0.9999;
|
|
if ( treble < -300.0 ) treble = -300.0;
|
|
if ( treble > 5.0 ) treble = 5.0;
|
|
|
|
double const maxh = 4096.0;
|
|
double rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - mid) );
|
|
double const pow_a_n = pow( rolloff, maxh - maxh * mid );
|
|
double const to_angle = PI / maxh / oversample;
|
|
for ( int i = 1; i < out_size; i++ )
|
|
{
|
|
double angle = i * to_angle;
|
|
double c = rolloff * cos( angle * maxh - angle ) -
|
|
cos( angle * maxh );
|
|
double cos_nc_angle = cos( angle * maxh * mid );
|
|
double cos_nc1_angle = cos( angle * maxh * mid - angle );
|
|
double cos_angle = cos( angle );
|
|
|
|
c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
|
|
double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle);
|
|
double b = 2.0 - cos_angle - cos_angle;
|
|
double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
|
|
|
|
out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d
|
|
}
|
|
|
|
// Approximate center by looking at two points to right. Much simpler
|
|
// and more reliable than trying to calculate it properly.
|
|
out [0] = out [1] + 0.5 * (out [1] - out [2]);
|
|
}
|
|
|
|
// Gain is 1-2800 for beta of 0-10, instead of 1.0 as it should be, but
|
|
// this is corrected by normalization in treble_eq().
|
|
static void kaiser_window( float io [], int count, float beta )
|
|
{
|
|
int const accuracy = 10;
|
|
|
|
float const beta2 = beta * beta;
|
|
float const step = (float) 0.5 / count;
|
|
float pos = (float) 0.5;
|
|
for ( float* const end = io + count; io < end; ++io )
|
|
{
|
|
float x = (pos - pos*pos) * beta2;
|
|
float u = x;
|
|
float k = 1;
|
|
float n = 2;
|
|
|
|
// Keep refining until adjustment becomes small
|
|
do
|
|
{
|
|
u *= x / (n * n);
|
|
n += 1;
|
|
k += u;
|
|
}
|
|
while ( k <= u * (1 << accuracy) );
|
|
|
|
pos += step;
|
|
*io *= k;
|
|
}
|
|
}
|
|
|
|
void blip_eq_t::generate( float out [], int count ) const
|
|
{
|
|
// lower cutoff freq for narrow kernels with their wider transition band
|
|
// (8 points->1.49, 16 points->1.15)
|
|
double cutoff_adj = blip_res * 2.25 / count + 0.85;
|
|
if ( cutoff_adj < 1.02 )
|
|
cutoff_adj = 1.02;
|
|
double half_rate = sample_rate * 0.5;
|
|
if ( cutoff_freq )
|
|
cutoff_adj = half_rate / cutoff_freq;
|
|
double cutoff = rolloff_freq * cutoff_adj / half_rate;
|
|
|
|
gen_sinc( out, count, oversample * cutoff_adj, treble, cutoff );
|
|
|
|
kaiser_window( out, count, kaiser );
|
|
}
|
|
|
|
void Blip_Synth_::treble_eq( blip_eq_t const& eq )
|
|
{
|
|
// Generate right half of kernel
|
|
int const half_size = blip_eq_t::calc_count( width );
|
|
float fimpulse [blip_res / 2 * (BLIP_MAX_QUALITY - 1) + 1];
|
|
eq.generate( fimpulse, half_size );
|
|
|
|
int i;
|
|
|
|
// Find rescale factor. Summing from small to large (right to left)
|
|
// reduces error.
|
|
double total = 0.0;
|
|
for ( i = half_size; --i > 0; )
|
|
total += fimpulse [i];
|
|
total = total * 2.0 + fimpulse [0];
|
|
|
|
//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
|
|
double const base_unit = 32768.0; // necessary for blip_unscaled to work
|
|
double rescale = base_unit / total;
|
|
kernel_unit = (int) base_unit;
|
|
|
|
// Integrate, first difference, rescale, convert to int
|
|
double sum = 0;
|
|
double next = 0;
|
|
int const size = impulses_size();
|
|
for ( i = 0; i < size; i++ )
|
|
{
|
|
int j = (half_size - 1) - i;
|
|
|
|
if ( i >= blip_res )
|
|
sum += fimpulse [j + blip_res];
|
|
|
|
// goes slightly past center, so it needs a little mirroring
|
|
next += fimpulse [j < 0 ? -j : j];
|
|
|
|
// calculate unintereleved index
|
|
int x = (~i & (blip_res - 1)) * (width >> 1) + (i >> BLIP_PHASE_BITS);
|
|
assert( (unsigned) x < (unsigned) size );
|
|
|
|
// flooring separately virtually eliminates error
|
|
phases [x] = (short) (int)
|
|
(floor( sum * rescale + 0.5 ) - floor( next * rescale + 0.5 ));
|
|
//phases [x] = (short) (int)
|
|
// floor( sum * rescale - next * rescale + 0.5 );
|
|
}
|
|
|
|
adjust_impulse();
|
|
|
|
// volume might require rescaling
|
|
double vol = volume_unit_;
|
|
if ( vol )
|
|
{
|
|
volume_unit_ = 0.0;
|
|
volume_unit( vol );
|
|
}
|
|
}
|
|
|
|
void Blip_Synth_::adjust_impulse()
|
|
{
|
|
int const size = impulses_size();
|
|
int const half_width = width / 2;
|
|
|
|
// Sum each phase as would be done when synthesizing, and correct
|
|
// any that don't add up to exactly kernel_half.
|
|
for ( int phase = blip_res / 2; --phase >= 0; )
|
|
{
|
|
int const fwd = phase * half_width;
|
|
int const rev = size - half_width - fwd;
|
|
|
|
int error = kernel_unit;
|
|
for ( int i = half_width; --i >= 0; )
|
|
{
|
|
error += phases [fwd + i];
|
|
error += phases [rev + i];
|
|
}
|
|
phases [fwd + half_width - 1] -= (short) error;
|
|
|
|
// Error shouldn't occur now with improved calculation
|
|
//if ( error ) printf( "error: %ld\n", error );
|
|
}
|
|
|
|
#if 0
|
|
for ( int i = 0; i < blip_res; i++, printf( "\n" ) )
|
|
for ( int j = 0; j < width / 2; j++ )
|
|
printf( "%5d,", (int) -phases [j + width/2 * i] );
|
|
#endif
|
|
}
|
|
|
|
void Blip_Synth_::rescale_kernel( int shift )
|
|
{
|
|
// Keep values positive to avoid round-towards-zero of sign-preserving
|
|
// right shift for negative values.
|
|
int const keep_positive = 0x8000 + (1 << (shift - 1));
|
|
|
|
int const half_width = width / 2;
|
|
for ( int phase = blip_res; --phase >= 0; )
|
|
{
|
|
int const fwd = phase * half_width;
|
|
|
|
// Integrate, rescale, then differentiate again.
|
|
// If differences are rescaled directly, more error results.
|
|
int sum = keep_positive;
|
|
for ( int i = 0; i < half_width; i++ )
|
|
{
|
|
int prev = sum;
|
|
sum += phases [fwd + i];
|
|
phases [fwd + i] = (sum >> shift) - (prev >> shift);
|
|
}
|
|
}
|
|
|
|
adjust_impulse();
|
|
}
|
|
|
|
void Blip_Synth_::volume_unit( double new_unit )
|
|
{
|
|
if ( volume_unit_ != new_unit )
|
|
{
|
|
// use default eq if it hasn't been set yet
|
|
if ( !kernel_unit )
|
|
treble_eq( -8.0 );
|
|
|
|
// Factor that kernel must be multiplied by
|
|
volume_unit_ = new_unit;
|
|
double factor = new_unit * (1 << blip_sample_bits) / kernel_unit;
|
|
|
|
if ( factor > 0.0 )
|
|
{
|
|
// If factor is low, reduce amplitude of kernel itself
|
|
int shift = 0;
|
|
while ( factor < 2.0 )
|
|
{
|
|
shift++;
|
|
factor *= 2.0;
|
|
}
|
|
|
|
if ( shift )
|
|
{
|
|
kernel_unit >>= shift;
|
|
assert( kernel_unit > 0 ); // fails if volume unit is too low
|
|
|
|
rescale_kernel( shift );
|
|
}
|
|
}
|
|
|
|
delta_factor = -(int) floor( factor + 0.5 );
|
|
//printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit );
|
|
}
|
|
}
|
|
#endif
|