cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

Blip_Synth.h (7047B)


      1
      2// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding
      3// waveforms to a Blip_Buffer.
      4
      5// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license.
      6
      7#ifndef BLIP_SYNTH_H
      8#define BLIP_SYNTH_H
      9
     10#ifndef BLIP_BUFFER_H
     11	#include "Blip_Buffer.h"
     12#endif
     13
     14// Quality level. Higher levels are slower, and worse in a few cases.
     15// Use blip_good_quality as a starting point.
     16const int blip_low_quality = 1;
     17const int blip_med_quality = 2;
     18const int blip_good_quality = 3;
     19const int blip_high_quality = 4;
     20
     21// Blip_Synth is a transition waveform synthesizer which adds band-limited
     22// offsets (transitions) into a Blip_Buffer. For a simpler interface, use
     23// Blip_Wave (below).
     24//
     25// Range specifies the greatest expected offset that will occur. For a
     26// waveform that goes between +amp and -amp, range should be amp * 2 (half
     27// that if it only goes between +amp and 0). When range is large, a higher
     28// accuracy scheme is used; to force this even when range is small, pass
     29// the negative of range (i.e. -range).
     30template<int quality,int range>
     31class Blip_Synth {
     32	BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 );
     33	BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 );
     34	enum {
     35		abs_range = (range < 0) ? -range : range,
     36		fine_mode = (range > 512 || range < 0),
     37		width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_),
     38		res = 1 << blip_res_bits_,
     39		impulse_size = width / 2 * (fine_mode + 1),
     40		base_impulses_size = width / 2 * (res / 2 + 1),
     41		fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 :
     42			abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 :
     43			abs_range <= 2048 ? 7 : 8) : 0)
     44	};
     45	blip_pair_t_  impulses [impulse_size * res * 2 + base_impulses_size];
     46	Blip_Impulse_ impulse;
     47	void init() { impulse.init( impulses, width, res, fine_bits ); }
     48public:
     49	Blip_Synth()                            { init(); }
     50	Blip_Synth( double volume )             { init(); this->volume( volume ); }
     51	
     52	// Configure low-pass filter (see notes.txt). Not optimized for real-time control
     53	void treble_eq( const blip_eq_t& eq )   { impulse.treble_eq( eq ); }
     54	
     55	// Set volume of a transition at amplitude 'range' by setting volume_unit
     56	// to v / range
     57	void volume( double v )                 { impulse.volume_unit( v * (1.0 / abs_range) ); }
     58	
     59	// Set base volume unit of transitions, where 1.0 is a full swing between the
     60	// positive and negative extremes. Not optimized for real-time control.
     61	void volume_unit( double unit )         { impulse.volume_unit( unit ); }
     62	
     63	// Default Blip_Buffer used for output when none is specified for a given call
     64	Blip_Buffer* output() const             { return impulse.buf; }
     65	void output( Blip_Buffer* b )           { impulse.buf = b; }
     66	
     67	// Add an amplitude offset (transition) with a magnitude of delta * volume_unit
     68	// into the specified buffer (default buffer if none specified) at the
     69	// specified source time. Delta can be positive or negative. To increase
     70	// performance by inlining code at the call site, use offset_inline().
     71	void offset( blip_time_t, int delta, Blip_Buffer* ) const;
     72	
     73	void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
     74	void offset_resampled( blip_resampled_time_t t, int o ) const {
     75		offset_resampled( t, o, impulse.buf );
     76	}
     77	void offset( blip_time_t t, int delta ) const {
     78		offset( t, delta, impulse.buf );
     79	}
     80	void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const {
     81		offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
     82	}
     83	void offset_inline( blip_time_t time, int delta ) const {
     84		offset_inline( time, delta, impulse.buf );
     85	}
     86};
     87
     88// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer.
     89// A wave is built from a series of delays and new amplitudes. This provides a
     90// simpler interface than Blip_Synth, nothing more.
     91template<int quality,int range>
     92class Blip_Wave {
     93	Blip_Synth<quality,range> synth;
     94	blip_time_t time_;
     95	int last_amp;
     96	void init() { time_ = 0; last_amp = 0; }
     97public:
     98	// Start wave at time 0 and amplitude 0
     99	Blip_Wave()                         { init(); }
    100	Blip_Wave( double volume )          { init(); this->volume( volume ); }
    101	
    102	// See Blip_Synth for description
    103	void volume( double v )             { synth.volume( v ); }
    104	void volume_unit( double v )        { synth.volume_unit( v ); }
    105	void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); }
    106	Blip_Buffer* output() const         { return synth.output(); }
    107	void output( Blip_Buffer* b )       { synth.output( b ); if ( !b ) time_ = last_amp = 0; }
    108	
    109	// Current time in frame
    110	blip_time_t time() const            { return time_; }
    111	void time( blip_time_t t )          { time_ = t; }
    112	
    113	// Current amplitude of wave
    114	int amplitude() const               { return last_amp; }
    115	void amplitude( int );
    116	
    117	// Move forward by 't' time units
    118	void delay( blip_time_t t )         { time_ += t; }
    119	
    120	// End time frame of specified duration. Localize time to new frame.
    121	// If wave hadn't been run to end of frame, start it at beginning of new frame.
    122	void end_frame( blip_time_t duration )
    123	{
    124		time_ -= duration;
    125		if ( time_ < 0 )
    126			time_ = 0;
    127	}
    128};
    129
    130// End of public interface
    131	
    132template<int quality,int range>
    133void Blip_Wave<quality,range>::amplitude( int amp ) {
    134	int delta = amp - last_amp;
    135	last_amp = amp;
    136	synth.offset_inline( time_, delta );
    137}
    138
    139template<int quality,int range>
    140inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
    141		int delta, Blip_Buffer* blip_buf ) const
    142{
    143	typedef blip_pair_t_ pair_t;
    144	
    145	unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1;
    146	assert(( (void)"Blip_Synth/Blip_wave: Went past end of buffer",
    147			sample_index < blip_buf->buffer_size_ ));
    148	enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 };
    149	pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index];
    150	
    151	enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ };
    152	enum { mask = res * 2 - 1 };
    153	const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size];
    154	
    155	pair_t offset = impulse.offset * delta;
    156	
    157	if ( !fine_bits )
    158	{
    159		// normal mode
    160		for ( int n = width / 4; n; --n )
    161		{
    162			pair_t t0 = buf [0] - offset;
    163			pair_t t1 = buf [1] - offset;
    164			
    165			t0 += imp [0] * delta;
    166			t1 += imp [1] * delta;
    167			imp += 2;
    168			
    169			buf [0] = t0;
    170			buf [1] = t1;
    171			buf += 2;
    172		}
    173	}
    174	else
    175	{
    176		// fine mode
    177		enum { sub_range = 1 << fine_bits };
    178		delta += sub_range / 2;
    179		int delta2 = (delta & (sub_range - 1)) - sub_range / 2;
    180		delta >>= fine_bits;
    181		
    182		for ( int n = width / 4; n; --n )
    183		{
    184			pair_t t0 = buf [0] - offset;
    185			pair_t t1 = buf [1] - offset;
    186			
    187			t0 += imp [0] * delta2;
    188			t0 += imp [1] * delta;
    189			
    190			t1 += imp [2] * delta2;
    191			t1 += imp [3] * delta;
    192			
    193			imp += 4;
    194			
    195			buf [0] = t0;
    196			buf [1] = t1;
    197			buf += 2;
    198		}
    199	}
    200}
    201
    202template<int quality,int range>
    203void Blip_Synth<quality,range>::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const {
    204	offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
    205}
    206
    207#endif
    208