mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-18 02:01:39 +00:00
718112a8fe
Currently none of these is being used, but eventually they will, once more code gets ported over. So it's better to have them right away and avoid editing the project file too much, only to revert that later.
490 lines
16 KiB
C++
490 lines
16 KiB
C++
// Band-limited sound synthesis buffer
|
|
|
|
// Blip_Buffer 0.4.1
|
|
#ifndef BLIP_BUFFER_H
|
|
#define BLIP_BUFFER_H
|
|
|
|
// internal
|
|
#include <limits.h>
|
|
#if INT_MAX < 0x7FFFFFFF
|
|
#error "int must be at least 32 bits"
|
|
#endif
|
|
|
|
typedef int blip_long;
|
|
typedef unsigned blip_ulong;
|
|
|
|
// Time unit at source clock rate
|
|
typedef blip_long blip_time_t;
|
|
|
|
// Output samples are 16-bit signed, with a range of -32768 to 32767
|
|
typedef short blip_sample_t;
|
|
enum { blip_sample_max = 32767 };
|
|
|
|
class Blip_Buffer {
|
|
public:
|
|
typedef const char* blargg_err_t;
|
|
|
|
// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
|
|
// to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there
|
|
// isn't enough memory, returns error without affecting current buffer setup.
|
|
blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 );
|
|
|
|
// Set number of source time units per second
|
|
void clock_rate( long );
|
|
|
|
// End current time frame of specified duration and make its samples available
|
|
// (along with any still-unread samples) for reading with read_samples(). Begins
|
|
// a new time frame at the end of the current frame.
|
|
void end_frame( blip_time_t time );
|
|
|
|
// Read at most 'max_samples' out of buffer into 'dest', removing them from from
|
|
// the buffer. Returns number of samples actually read and removed. If stereo is
|
|
// true, increments 'dest' one extra time after writing each sample, to allow
|
|
// easy interleving of two channels into a stereo output buffer.
|
|
long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 );
|
|
|
|
// Additional optional features
|
|
|
|
// Current output sample rate
|
|
long sample_rate() const;
|
|
|
|
// Length of buffer, in milliseconds
|
|
int length() const;
|
|
|
|
// Number of source time units per second
|
|
long clock_rate() const;
|
|
|
|
// Set frequency high-pass filter frequency, where higher values reduce bass more
|
|
void bass_freq( int frequency );
|
|
|
|
// Number of samples delay from synthesis to samples read out
|
|
int output_latency() const;
|
|
|
|
// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
|
|
// false, just clears out any samples waiting rather than the entire buffer.
|
|
void clear( int entire_buffer = 1 );
|
|
|
|
// Number of samples available for reading with read_samples()
|
|
long samples_avail() const;
|
|
|
|
// Remove 'count' samples from those waiting to be read
|
|
void remove_samples( long count );
|
|
|
|
// Experimental features
|
|
|
|
// Count number of clocks needed until 'count' samples will be available.
|
|
// If buffer can't even hold 'count' samples, returns number of clocks until
|
|
// buffer becomes full.
|
|
blip_time_t count_clocks( long count ) const;
|
|
|
|
// Number of raw samples that can be mixed within frame of specified duration.
|
|
long count_samples( blip_time_t duration ) const;
|
|
|
|
// Mix 'count' samples from 'buf' into buffer.
|
|
void mix_samples( blip_sample_t const* buf, long count );
|
|
|
|
// not documented yet
|
|
void set_modified() { modified_ = 1; }
|
|
int clear_modified() { int b = modified_; modified_ = 0; return b; }
|
|
typedef blip_ulong blip_resampled_time_t;
|
|
void remove_silence( long count );
|
|
blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; }
|
|
blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; }
|
|
blip_resampled_time_t clock_rate_factor( long clock_rate ) const;
|
|
public:
|
|
Blip_Buffer();
|
|
~Blip_Buffer();
|
|
|
|
Blip_Buffer(Blip_Buffer &&) = default;
|
|
|
|
// Deprecated
|
|
typedef blip_resampled_time_t resampled_time_t;
|
|
blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); }
|
|
blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); }
|
|
private:
|
|
// noncopyable
|
|
Blip_Buffer( const Blip_Buffer& );
|
|
Blip_Buffer& operator = ( const Blip_Buffer& );
|
|
public:
|
|
typedef blip_time_t buf_t_;
|
|
blip_ulong factor_;
|
|
blip_resampled_time_t offset_;
|
|
buf_t_* buffer_;
|
|
blip_long buffer_size_;
|
|
blip_long reader_accum_;
|
|
int bass_shift_;
|
|
private:
|
|
long sample_rate_;
|
|
long clock_rate_;
|
|
int bass_freq_;
|
|
int length_;
|
|
int modified_;
|
|
friend class Blip_Reader;
|
|
};
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
// Number of bits in resample ratio fraction. Higher values give a more accurate ratio
|
|
// but reduce maximum buffer size.
|
|
#ifndef BLIP_BUFFER_ACCURACY
|
|
#define BLIP_BUFFER_ACCURACY 16
|
|
#endif
|
|
|
|
// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in
|
|
// noticeable broadband noise when synthesizing high frequency square waves.
|
|
// Affects size of Blip_Synth objects since they store the waveform directly.
|
|
#ifndef BLIP_PHASE_BITS
|
|
#if BLIP_BUFFER_FAST
|
|
#define BLIP_PHASE_BITS 8
|
|
#else
|
|
#define BLIP_PHASE_BITS 6
|
|
#endif
|
|
#endif
|
|
|
|
// Internal
|
|
typedef blip_ulong blip_resampled_time_t;
|
|
int const blip_widest_impulse_ = 16;
|
|
int const blip_buffer_extra_ = blip_widest_impulse_ + 2;
|
|
int const blip_res = 1 << BLIP_PHASE_BITS;
|
|
class blip_eq_t;
|
|
|
|
class Blip_Synth_Fast_ {
|
|
public:
|
|
Blip_Buffer* buf;
|
|
int last_amp;
|
|
int delta_factor;
|
|
|
|
void volume_unit( double );
|
|
Blip_Synth_Fast_();
|
|
void treble_eq( blip_eq_t const& ) { }
|
|
};
|
|
|
|
class Blip_Synth_ {
|
|
public:
|
|
Blip_Buffer* buf;
|
|
int last_amp;
|
|
int delta_factor;
|
|
|
|
void volume_unit( double );
|
|
Blip_Synth_( short* impulses, int width );
|
|
void treble_eq( blip_eq_t const& );
|
|
private:
|
|
double volume_unit_;
|
|
short* const impulses;
|
|
int const width;
|
|
blip_long kernel_unit;
|
|
int impulses_size() const { return blip_res / 2 * width + 1; }
|
|
void adjust_impulse();
|
|
};
|
|
|
|
// Quality level. Start with blip_good_quality.
|
|
const int blip_med_quality = 8;
|
|
const int blip_good_quality = 12;
|
|
const int blip_high_quality = 16;
|
|
|
|
// Range specifies the greatest expected change in amplitude. Calculate it
|
|
// by finding the difference between the maximum and minimum expected
|
|
// amplitudes (max - min).
|
|
template<int quality,int range>
|
|
class Blip_Synth {
|
|
public:
|
|
// Set overall volume of waveform
|
|
void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); }
|
|
|
|
// Configure low-pass filter (see blip_buffer.txt)
|
|
void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); }
|
|
|
|
// Get/set Blip_Buffer used for output
|
|
Blip_Buffer* output() const { return impl.buf; }
|
|
void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; }
|
|
|
|
// Update amplitude of waveform at given time. Using this requires a separate
|
|
// Blip_Synth for each waveform.
|
|
void update( blip_time_t time, int amplitude );
|
|
|
|
// Low-level interface
|
|
|
|
// Add an amplitude transition of specified delta, optionally into specified buffer
|
|
// rather than the one set with output(). Delta can be positive or negative.
|
|
// The actual change in amplitude is delta * (volume / range)
|
|
void offset( blip_time_t, int delta, Blip_Buffer* ) const;
|
|
void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); }
|
|
|
|
// Works directly in terms of fractional output samples. Contact author for more info.
|
|
void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
|
|
|
|
// Same as offset(), except code is inlined for higher performance
|
|
void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const {
|
|
offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
|
|
}
|
|
void offset_inline( blip_time_t t, int delta ) const {
|
|
offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf );
|
|
}
|
|
|
|
private:
|
|
#if BLIP_BUFFER_FAST
|
|
Blip_Synth_Fast_ impl;
|
|
#else
|
|
Blip_Synth_ impl;
|
|
typedef short imp_t;
|
|
imp_t impulses [blip_res * (quality / 2) + 1];
|
|
public:
|
|
Blip_Synth() : impl( impulses, quality ) { }
|
|
#endif
|
|
};
|
|
|
|
// Low-pass equalization parameters
|
|
class blip_eq_t {
|
|
public:
|
|
// Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce
|
|
// treble, small positive values (0 to 5.0) increase treble.
|
|
blip_eq_t( double treble_db = 0 );
|
|
|
|
// See blip_buffer.txt
|
|
blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 );
|
|
|
|
private:
|
|
double treble;
|
|
long rolloff_freq;
|
|
long sample_rate;
|
|
long cutoff_freq;
|
|
void generate( float* out, int count ) const;
|
|
friend class Blip_Synth_;
|
|
};
|
|
|
|
int const blip_sample_bits = 30;
|
|
|
|
// Dummy Blip_Buffer to direct sound output to, for easy muting without
|
|
// having to stop sound code.
|
|
class Silent_Blip_Buffer : public Blip_Buffer {
|
|
buf_t_ buf [blip_buffer_extra_ + 1];
|
|
public:
|
|
// The following cannot be used (an assertion will fail if attempted):
|
|
blargg_err_t set_sample_rate( long samples_per_sec, int msec_length );
|
|
blip_time_t count_clocks( long count ) const;
|
|
void mix_samples( blip_sample_t const* buf, long count );
|
|
|
|
Silent_Blip_Buffer();
|
|
};
|
|
|
|
#if defined (__GNUC__) || _MSC_VER >= 1100
|
|
#define BLIP_RESTRICT __restrict
|
|
#else
|
|
#define BLIP_RESTRICT
|
|
#endif
|
|
|
|
// Optimized reading from Blip_Buffer, for use in custom sample output
|
|
|
|
// Begin reading from buffer. Name should be unique to the current block.
|
|
#define BLIP_READER_BEGIN( name, blip_buffer ) \
|
|
const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\
|
|
blip_long name##_reader_accum = (blip_buffer).reader_accum_
|
|
|
|
// Get value to pass to BLIP_READER_NEXT()
|
|
#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_)
|
|
|
|
// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
|
|
// code at the cost of having no bass control
|
|
int const blip_reader_default_bass = 9;
|
|
|
|
// Current sample
|
|
#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16))
|
|
|
|
// Current raw sample in full internal resolution
|
|
#define BLIP_READER_READ_RAW( name ) (name##_reader_accum)
|
|
|
|
// Advance to next sample
|
|
#define BLIP_READER_NEXT( name, bass ) \
|
|
(void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))
|
|
|
|
// End reading samples from buffer. The number of samples read must now be removed
|
|
// using Blip_Buffer::remove_samples().
|
|
#define BLIP_READER_END( name, blip_buffer ) \
|
|
(void) ((blip_buffer).reader_accum_ = name##_reader_accum)
|
|
|
|
|
|
// Compatibility with older version
|
|
const long blip_unscaled = 65535;
|
|
const int blip_low_quality = blip_med_quality;
|
|
const int blip_best_quality = blip_high_quality;
|
|
|
|
// Deprecated; use BLIP_READER macros as follows:
|
|
// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf );
|
|
// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf );
|
|
// r.read() -> BLIP_READER_READ( r )
|
|
// r.read_raw() -> BLIP_READER_READ_RAW( r )
|
|
// r.next( bass ) -> BLIP_READER_NEXT( r, bass )
|
|
// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass )
|
|
// r.end( buf ) -> BLIP_READER_END( r, buf )
|
|
class Blip_Reader {
|
|
public:
|
|
int begin( Blip_Buffer& );
|
|
blip_long read() const { return accum >> (blip_sample_bits - 16); }
|
|
blip_long read_raw() const { return accum; }
|
|
void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); }
|
|
void end( Blip_Buffer& b ) { b.reader_accum_ = accum; }
|
|
|
|
private:
|
|
const Blip_Buffer::buf_t_* buf;
|
|
blip_long accum;
|
|
};
|
|
|
|
// End of public interface
|
|
|
|
#include <assert.h>
|
|
|
|
template<int quality,int range>
|
|
inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
|
|
int delta, Blip_Buffer* blip_buf ) const
|
|
{
|
|
// Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the
|
|
// need for a longer buffer as set by set_sample_rate().
|
|
assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ );
|
|
delta *= impl.delta_factor;
|
|
blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY);
|
|
int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1));
|
|
|
|
#if BLIP_BUFFER_FAST
|
|
blip_long left = buf [0] + delta;
|
|
|
|
// Kind of crappy, but doing shift after multiply results in overflow.
|
|
// Alternate way of delaying multiply by delta_factor results in worse
|
|
// sub-sample resolution.
|
|
blip_long right = (delta >> BLIP_PHASE_BITS) * phase;
|
|
left -= right;
|
|
right += buf [1];
|
|
|
|
buf [0] = left;
|
|
buf [1] = right;
|
|
#else
|
|
|
|
int const fwd = (blip_widest_impulse_ - quality) / 2;
|
|
int const rev = fwd + quality - 2;
|
|
int const mid = quality / 2 - 1;
|
|
|
|
imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase;
|
|
|
|
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
|
|
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
|
|
|
|
// straight forward implementation resulted in better code on GCC for x86
|
|
|
|
#define ADD_IMP( out, in ) \
|
|
buf [out] += (blip_long) imp [blip_res * (in)] * delta
|
|
|
|
#define BLIP_FWD( i ) {\
|
|
ADD_IMP( fwd + i, i );\
|
|
ADD_IMP( fwd + 1 + i, i + 1 );\
|
|
}
|
|
#define BLIP_REV( r ) {\
|
|
ADD_IMP( rev - r, r + 1 );\
|
|
ADD_IMP( rev + 1 - r, r );\
|
|
}
|
|
|
|
BLIP_FWD( 0 )
|
|
if ( quality > 8 ) BLIP_FWD( 2 )
|
|
if ( quality > 12 ) BLIP_FWD( 4 )
|
|
{
|
|
ADD_IMP( fwd + mid - 1, mid - 1 );
|
|
ADD_IMP( fwd + mid , mid );
|
|
imp = impulses + phase;
|
|
}
|
|
if ( quality > 12 ) BLIP_REV( 6 )
|
|
if ( quality > 8 ) BLIP_REV( 4 )
|
|
BLIP_REV( 2 )
|
|
|
|
ADD_IMP( rev , 1 );
|
|
ADD_IMP( rev + 1, 0 );
|
|
|
|
#else
|
|
|
|
// for RISC processors, help compiler by reading ahead of writes
|
|
|
|
#define BLIP_FWD( i ) {\
|
|
blip_long t0 = i0 * delta + buf [fwd + i];\
|
|
blip_long 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 ) {\
|
|
blip_long t0 = i0 * delta + buf [rev - r];\
|
|
blip_long 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;\
|
|
}
|
|
|
|
blip_long i0 = *imp;
|
|
BLIP_FWD( 0 )
|
|
if ( quality > 8 ) BLIP_FWD( 2 )
|
|
if ( quality > 12 ) BLIP_FWD( 4 )
|
|
{
|
|
blip_long t0 = i0 * delta + buf [fwd + mid - 1];
|
|
blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ];
|
|
imp = impulses + phase;
|
|
i0 = imp [blip_res * mid];
|
|
buf [fwd + mid - 1] = t0;
|
|
buf [fwd + mid ] = t1;
|
|
}
|
|
if ( quality > 12 ) BLIP_REV( 6 )
|
|
if ( quality > 8 ) BLIP_REV( 4 )
|
|
BLIP_REV( 2 )
|
|
|
|
blip_long t0 = i0 * delta + buf [rev ];
|
|
blip_long t1 = *imp * delta + buf [rev + 1];
|
|
buf [rev ] = t0;
|
|
buf [rev + 1] = t1;
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
#undef BLIP_FWD
|
|
#undef BLIP_REV
|
|
|
|
template<int quality,int range>
|
|
#if BLIP_BUFFER_FAST
|
|
inline
|
|
#endif
|
|
void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
|
|
{
|
|
offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
|
|
}
|
|
|
|
template<int quality,int range>
|
|
#if BLIP_BUFFER_FAST
|
|
inline
|
|
#endif
|
|
void Blip_Synth<quality,range>::update( blip_time_t t, int amp )
|
|
{
|
|
int delta = amp - impl.last_amp;
|
|
impl.last_amp = amp;
|
|
offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf );
|
|
}
|
|
|
|
inline blip_eq_t::blip_eq_t( double t ) :
|
|
treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { }
|
|
inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) :
|
|
treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { }
|
|
|
|
inline int Blip_Buffer::length() const { return length_; }
|
|
inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); }
|
|
inline long Blip_Buffer::sample_rate() const { return sample_rate_; }
|
|
inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; }
|
|
inline long Blip_Buffer::clock_rate() const { return clock_rate_; }
|
|
inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); }
|
|
|
|
inline int Blip_Reader::begin( Blip_Buffer& blip_buf )
|
|
{
|
|
buf = blip_buf.buffer_;
|
|
accum = blip_buf.reader_accum_;
|
|
return blip_buf.bass_shift_;
|
|
}
|
|
|
|
int const blip_max_length = 0;
|
|
int const blip_default_length = 250;
|
|
|
|
#endif
|