cscg22-gearboy

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

Effects_Buffer.cpp (15240B)


      1// Game_Music_Emu $vers. http://www.slack.net/~ant/
      2
      3#include "Effects_Buffer.h"
      4
      5#include <string.h>
      6
      7/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
      8can redistribute it and/or modify it under the terms of the GNU Lesser
      9General Public License as published by the Free Software Foundation; either
     10version 2.1 of the License, or (at your option) any later version. This
     11module is distributed in the hope that it will be useful, but WITHOUT ANY
     12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
     13FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
     14details. You should have received a copy of the GNU Lesser General Public
     15License along with this module; if not, write to the Free Software Foundation,
     16Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
     17
     18#include "blargg_source.h"
     19
     20int const fixed_shift = 12;
     21#define TO_FIXED( f )   fixed_t ((f) * ((fixed_t) 1 << fixed_shift))
     22#define FROM_FIXED( f ) ((f) >> fixed_shift)
     23
     24int const max_read = 2560; // determines minimum delay
     25
     26Effects_Buffer::Effects_Buffer( int max_bufs, long echo_size_ ) : Multi_Buffer( stereo )
     27{
     28	echo_size   = (int)max( max_read * (long) stereo, echo_size_ & ~1 );
     29	clock_rate_ = 0;
     30	bass_freq_  = 90;
     31	bufs        = 0;
     32	bufs_size   = 0;
     33	bufs_max    = max( max_bufs, (int) extra_chans );
     34	no_echo     = true;
     35	no_effects  = true;
     36
     37	// defaults
     38	config_.enabled   = false;
     39	config_.delay [0] = 120;
     40	config_.delay [1] = 122;
     41	config_.feedback  = 0.2f;
     42	config_.treble    = 0.4f;
     43
     44	static float const sep = 0.8f;
     45	config_.side_chans [0].pan = -sep;
     46	config_.side_chans [1].pan = +sep;
     47	config_.side_chans [0].vol = 1.0f;
     48	config_.side_chans [1].vol = 1.0f;
     49
     50	memset( &s, 0, sizeof s );
     51	clear();
     52}
     53
     54Effects_Buffer::~Effects_Buffer()
     55{
     56	delete_bufs();
     57}
     58
     59// avoid using new []
     60blargg_err_t Effects_Buffer::new_bufs( int size )
     61{
     62	bufs = (buf_t*) malloc( size * sizeof *bufs );
     63	CHECK_ALLOC( bufs );
     64	for ( int i = 0; i < size; i++ )
     65		new (bufs + i) buf_t;
     66	bufs_size = size;
     67	return 0;
     68}
     69
     70void Effects_Buffer::delete_bufs()
     71{
     72	if ( bufs )
     73	{
     74		for ( int i = bufs_size; --i >= 0; )
     75			bufs [i].~buf_t();
     76		free( bufs );
     77		bufs = 0;
     78	}
     79	bufs_size = 0;
     80}
     81
     82blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec )
     83{
     84	// extra to allow farther past-the-end pointers
     85	mixer.samples_read = 0;
     86	RETURN_ERR( echo.resize( echo_size + stereo ) );
     87	return Multi_Buffer::set_sample_rate( rate, msec );
     88}
     89
     90void Effects_Buffer::clock_rate( long rate )
     91{
     92	clock_rate_ = rate;
     93	for ( int i = bufs_size; --i >= 0; )
     94		bufs [i].clock_rate( clock_rate_ );
     95}
     96
     97void Effects_Buffer::bass_freq( int freq )
     98{
     99	bass_freq_ = freq;
    100	for ( int i = bufs_size; --i >= 0; )
    101		bufs [i].bass_freq( bass_freq_ );
    102}
    103
    104blargg_err_t Effects_Buffer::set_channel_count( int count, int const* types )
    105{
    106	RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) );
    107
    108	delete_bufs();
    109
    110	mixer.samples_read = 0;
    111
    112	RETURN_ERR( chans.resize( count + extra_chans ) );
    113
    114	RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) );
    115
    116	for ( int i = bufs_size; --i >= 0; )
    117		RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) );
    118
    119	for ( int i = (int)chans.size(); --i >= 0; )
    120	{
    121		chan_t& ch = chans [i];
    122		ch.cfg.vol      = 1.0f;
    123		ch.cfg.pan      = 0.0f;
    124		ch.cfg.surround = false;
    125		ch.cfg.echo     = false;
    126	}
    127	// side channels with echo
    128	chans [2].cfg.echo = true;
    129	chans [3].cfg.echo = true;
    130
    131	clock_rate( clock_rate_ );
    132	bass_freq( bass_freq_ );
    133	apply_config();
    134	clear();
    135
    136	return 0;
    137}
    138
    139void Effects_Buffer::clear_echo()
    140{
    141	if ( echo.size() )
    142		memset( echo.begin(), 0, echo.size() * sizeof echo [0] );
    143}
    144
    145void Effects_Buffer::clear()
    146{
    147	echo_pos       = 0;
    148	s.low_pass [0] = 0;
    149	s.low_pass [1] = 0;
    150	mixer.samples_read = 0;
    151
    152	for ( int i = bufs_size; --i >= 0; )
    153		bufs [i].clear();
    154	clear_echo();
    155}
    156
    157Effects_Buffer::channel_t Effects_Buffer::channel( int i )
    158{
    159	i += extra_chans;
    160	require( extra_chans <= i && i < (int) chans.size() );
    161	return chans [i].channel;
    162}
    163
    164
    165// Configuration
    166
    167// 3 wave positions with/without surround, 2 multi (one with same config as wave)
    168int const simple_bufs = 3 * 2 + 2 - 1;
    169
    170Simple_Effects_Buffer::Simple_Effects_Buffer() :
    171	Effects_Buffer( extra_chans + simple_bufs, 18 * 1024L )
    172{
    173	config_.echo     = 0.20f;
    174	config_.stereo   = 0.20f;
    175	config_.surround = true;
    176	config_.enabled  = false;
    177}
    178
    179void Simple_Effects_Buffer::apply_config()
    180{
    181	Effects_Buffer::config_t& c = Effects_Buffer::config();
    182
    183	c.enabled = config_.enabled;
    184	if ( c.enabled )
    185	{
    186		c.delay [0] = 120;
    187		c.delay [1] = 122;
    188		c.feedback  = config_.echo * 0.7f;
    189		c.treble    = 0.6f - 0.3f * config_.echo;
    190
    191		float sep = config_.stereo + 0.80f;
    192		if ( sep > 1.0f )
    193			sep = 1.0f;
    194
    195		c.side_chans [0].pan = -sep;
    196		c.side_chans [1].pan = +sep;
    197
    198		for ( int i = channel_count(); --i >= 0; )
    199		{
    200			chan_config_t& ch = Effects_Buffer::chan_config( i );
    201
    202			ch.pan      = 0.0f;
    203			ch.surround = config_.surround;
    204			ch.echo     = false;
    205
    206			int const type = (channel_types() ? channel_types() [i] : 0);
    207			if ( !(type & noise_type) )
    208			{
    209				int index = (type & type_index_mask) % 6 - 3;
    210				if ( index < 0 )
    211				{
    212					index += 3;
    213					ch.surround = false;
    214					ch.echo     = true;
    215				}
    216				if ( index >= 1 )
    217				{
    218					ch.pan = config_.stereo;
    219					if ( index == 1 )
    220						ch.pan = -ch.pan;
    221				}
    222			}
    223			else if ( type & 1 )
    224			{
    225				ch.surround = false;
    226			}
    227		}
    228	}
    229
    230	Effects_Buffer::apply_config();
    231}
    232
    233int Effects_Buffer::min_delay() const
    234{
    235	require( sample_rate() );
    236	return max_read * 1000L / sample_rate();
    237}
    238
    239int Effects_Buffer::max_delay() const
    240{
    241	require( sample_rate() );
    242	return (echo_size / stereo - max_read) * 1000L / sample_rate();
    243}
    244
    245void Effects_Buffer::apply_config()
    246{
    247	int i;
    248
    249	if ( !bufs_size )
    250		return;
    251
    252	s.treble = TO_FIXED( config_.treble );
    253
    254	bool echo_dirty = false;
    255
    256	fixed_t old_feedback = s.feedback;
    257	s.feedback = TO_FIXED( config_.feedback );
    258	if ( !old_feedback && s.feedback )
    259		echo_dirty = true;
    260
    261	// delays
    262	for ( i = stereo; --i >= 0; )
    263	{
    264		long delay = config_.delay [i] * sample_rate() / 1000 * stereo;
    265		delay = max( delay, long (max_read * stereo) );
    266		delay = min( delay, long (echo_size - max_read * stereo) );
    267		if ( s.delay [i] != delay )
    268		{
    269			s.delay [i] = delay;
    270			echo_dirty = true;
    271		}
    272	}
    273
    274	// side channels
    275	for ( i = 2; --i >= 0; )
    276	{
    277		chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f;
    278		chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan;
    279	}
    280
    281	// convert volumes
    282	for ( i = (int)chans.size(); --i >= 0; )
    283	{
    284		chan_t& ch = chans [i];
    285		ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan );
    286		ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan );
    287		if ( ch.cfg.surround )
    288			ch.vol [0] = -ch.vol [0];
    289	}
    290
    291	assign_buffers();
    292
    293	// set side channels
    294	for ( i = (int)chans.size(); --i >= 0; )
    295	{
    296		chan_t& ch = chans [i];
    297		ch.channel.left  = chans [ch.cfg.echo*2  ].channel.center;
    298		ch.channel.right = chans [ch.cfg.echo*2+1].channel.center;
    299	}
    300
    301	bool old_echo = !no_echo && !no_effects;
    302
    303	// determine whether effects and echo are needed at all
    304	no_effects = true;
    305	no_echo    = true;
    306	for ( i = (int)chans.size(); --i >= extra_chans; )
    307	{
    308		chan_t& ch = chans [i];
    309		if ( ch.cfg.echo && s.feedback )
    310			no_echo = false;
    311
    312		if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) )
    313			no_effects = false;
    314	}
    315	if ( !no_echo )
    316		no_effects = false;
    317
    318	if (    chans [0].vol [0] != TO_FIXED( 1 ) ||
    319			chans [0].vol [1] != TO_FIXED( 0 ) ||
    320			chans [1].vol [0] != TO_FIXED( 0 ) ||
    321			chans [1].vol [1] != TO_FIXED( 1 ) )
    322		no_effects = false;
    323
    324	if ( !config_.enabled )
    325		no_effects = true;
    326
    327	if ( no_effects )
    328	{
    329		for ( i = (int)chans.size(); --i >= 0; )
    330		{
    331			chan_t& ch = chans [i];
    332			ch.channel.center = &bufs [2];
    333			ch.channel.left   = &bufs [0];
    334			ch.channel.right  = &bufs [1];
    335		}
    336	}
    337
    338	mixer.bufs [0] = &bufs [0];
    339	mixer.bufs [1] = &bufs [1];
    340	mixer.bufs [2] = &bufs [2];
    341
    342	if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) )
    343		clear_echo();
    344
    345	channels_changed();
    346}
    347
    348void Effects_Buffer::assign_buffers()
    349{
    350	// assign channels to buffers
    351	int buf_count = 0;
    352	for ( int i = 0; i < (int) chans.size(); i++ )
    353	{
    354		// put second two side channels at end to give priority to main channels
    355		// in case closest matching is necessary
    356		int x = i;
    357		if ( i > 1 )
    358			x += 2;
    359		if ( x >= (int) chans.size() )
    360			x -= (chans.size() - 2);
    361		chan_t& ch = chans [x];
    362
    363		int b = 0;
    364		for ( ; b < buf_count; b++ )
    365		{
    366			if (    ch.vol [0] == bufs [b].vol [0] &&
    367					ch.vol [1] == bufs [b].vol [1] &&
    368					(ch.cfg.echo == bufs [b].echo || !s.feedback) )
    369				break;
    370		}
    371
    372		if ( b >= buf_count )
    373		{
    374			if ( buf_count < bufs_max )
    375			{
    376				bufs [b].vol [0] = ch.vol [0];
    377				bufs [b].vol [1] = ch.vol [1];
    378				bufs [b].echo    = ch.cfg.echo;
    379				buf_count++;
    380			}
    381			else
    382			{
    383				// TODO: this is a mess, needs refinement
    384				dprintf( "Effects_Buffer ran out of buffers; using closest match\n" );
    385				b = 0;
    386				fixed_t best_dist = TO_FIXED( 8 );
    387				for ( int h = buf_count; --h >= 0; )
    388				{
    389					#define CALC_LEVELS( vols, sum, diff, surround ) \
    390					fixed_t sum, diff;\
    391					bool surround = false;\
    392					{\
    393						fixed_t vol_0 = vols [0];\
    394						if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\
    395						fixed_t vol_1 = vols [1];\
    396						if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\
    397						sum  = vol_0 + vol_1;\
    398						diff = vol_0 - vol_1;\
    399					}
    400					CALC_LEVELS( ch.vol,       ch_sum,  ch_diff,  ch_surround );
    401					CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround );
    402
    403					fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff );
    404
    405					if ( ch_surround != buf_surround )
    406						dist += TO_FIXED( 1 ) / 2;
    407
    408					if ( s.feedback && ch.cfg.echo != bufs [h].echo )
    409						dist += TO_FIXED( 1 ) / 2;
    410
    411					if ( best_dist > dist )
    412					{
    413						best_dist = dist;
    414						b = h;
    415					}
    416				}
    417			}
    418		}
    419
    420		//dprintf( "ch %d->buf %d\n", x, b );
    421		ch.channel.center = &bufs [b];
    422	}
    423}
    424
    425
    426// Mixing
    427
    428void Effects_Buffer::end_frame( blip_time_t time )
    429{
    430	for ( int i = bufs_size; --i >= 0; )
    431		bufs [i].end_frame( time );
    432}
    433
    434long Effects_Buffer::read_samples( blip_sample_t* out, long out_size )
    435{
    436	out_size = min( out_size, samples_avail() );
    437
    438	int pair_count = int (out_size >> 1);
    439	require( pair_count * stereo == out_size ); // must read an even number of samples
    440	if ( pair_count )
    441	{
    442		if ( no_effects )
    443		{
    444			mixer.read_pairs( out, pair_count );
    445		}
    446		else
    447		{
    448			int pairs_remain = pair_count;
    449			do
    450			{
    451				// mix at most max_read pairs at a time
    452				int count = max_read;
    453				if ( count > pairs_remain )
    454					count = pairs_remain;
    455
    456				if ( no_echo )
    457				{
    458					// optimization: clear echo here to keep mix_effects() a leaf function
    459					echo_pos = 0;
    460					memset( echo.begin(), 0, count * stereo * sizeof echo [0] );
    461				}
    462				mix_effects( out, count );
    463
    464				blargg_long new_echo_pos = echo_pos + count * stereo;
    465				if ( new_echo_pos >= echo_size )
    466					new_echo_pos -= echo_size;
    467				echo_pos = new_echo_pos;
    468				assert( echo_pos < echo_size );
    469
    470				out += count * stereo;
    471				mixer.samples_read += count;
    472				pairs_remain -= count;
    473			}
    474			while ( pairs_remain );
    475		}
    476
    477		if ( samples_avail() <= 0 || immediate_removal() )
    478		{
    479			for ( int i = bufs_size; --i >= 0; )
    480			{
    481				buf_t& b = bufs [i];
    482				// TODO: might miss non-silence settling since it checks END of last read
    483				if ( b.non_silent() )
    484					b.remove_samples( mixer.samples_read );
    485				else
    486					b.remove_silence( mixer.samples_read );
    487			}
    488			mixer.samples_read = 0;
    489		}
    490	}
    491	return out_size;
    492}
    493
    494void Effects_Buffer::mix_effects( blip_sample_t* out_, int pair_count )
    495{
    496	typedef fixed_t stereo_fixed_t [stereo];
    497
    498	// add channels with echo, do echo, add channels without echo, then convert to 16-bit and output
    499	int echo_phase = 1;
    500	do
    501	{
    502		// mix any modified buffers
    503		{
    504			buf_t* buf = bufs;
    505			int bufs_remain = bufs_size;
    506			do
    507			{
    508				if ( buf->non_silent() && ( buf->echo == !!echo_phase ) )
    509				{
    510					stereo_fixed_t* BLIP_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos];
    511					int const bass = BLIP_READER_BASS( *buf );
    512					BLIP_READER_BEGIN( in, *buf );
    513					BLIP_READER_ADJ_( in, mixer.samples_read );
    514					fixed_t const vol_0 = buf->vol [0];
    515					fixed_t const vol_1 = buf->vol [1];
    516
    517					int count = unsigned (echo_size - echo_pos) / stereo;
    518					int remain = pair_count;
    519					if ( count > remain )
    520						count = remain;
    521					do
    522					{
    523						remain -= count;
    524						BLIP_READER_ADJ_( in, count );
    525
    526						out += count;
    527						int offset = -count;
    528						do
    529						{
    530							fixed_t s = BLIP_READER_READ( in );
    531							BLIP_READER_NEXT_IDX_( in, bass, offset );
    532
    533							out [offset] [0] += s * vol_0;
    534							out [offset] [1] += s * vol_1;
    535						}
    536						while ( ++offset );
    537
    538						out = (stereo_fixed_t*) echo.begin();
    539						count = remain;
    540					}
    541					while ( remain );
    542
    543					BLIP_READER_END( in, *buf );
    544				}
    545				buf++;
    546			}
    547			while ( --bufs_remain );
    548		}
    549
    550		// add echo
    551		if ( echo_phase && !no_echo )
    552		{
    553			fixed_t const feedback = s.feedback;
    554			fixed_t const treble   = s.treble;
    555
    556			int i = 1;
    557			do
    558			{
    559				fixed_t low_pass = s.low_pass [i];
    560
    561				fixed_t* echo_end = &echo [echo_size + i];
    562				fixed_t const* BLIP_RESTRICT in_pos = &echo [echo_pos + i];
    563				blargg_long out_offset = (int)(echo_pos + i + s.delay [i]);
    564				if ( out_offset >= echo_size )
    565					out_offset -= echo_size;
    566				assert( out_offset < echo_size );
    567				fixed_t* BLIP_RESTRICT out_pos = &echo [out_offset];
    568
    569				// break into up to three chunks to avoid having to handle wrap-around
    570				// in middle of core loop
    571				int remain = pair_count;
    572				do
    573				{
    574					fixed_t const* pos = in_pos;
    575					if ( pos < out_pos )
    576						pos = out_pos;
    577					int count = blargg_ulong ((char*) echo_end - (char const*) pos) /
    578							unsigned (stereo * sizeof (fixed_t));
    579					if ( count > remain )
    580						count = remain;
    581					remain -= count;
    582
    583					in_pos  += count * stereo;
    584					out_pos += count * stereo;
    585					int offset = -count;
    586					do
    587					{
    588						low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble;
    589						out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback;
    590					}
    591					while ( ++offset );
    592
    593					if (  in_pos >= echo_end )  in_pos -= echo_size;
    594					if ( out_pos >= echo_end ) out_pos -= echo_size;
    595				}
    596				while ( remain );
    597
    598				s.low_pass [i] = low_pass;
    599			}
    600			while ( --i >= 0 );
    601		}
    602	}
    603	while ( --echo_phase >= 0 );
    604
    605	// clamp to 16 bits
    606	{
    607		stereo_fixed_t const* BLIP_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos];
    608		typedef blip_sample_t stereo_blip_sample_t [stereo];
    609		stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_;
    610		int count = unsigned (echo_size - echo_pos) / (unsigned) stereo;
    611		int remain = pair_count;
    612		if ( count > remain )
    613			count = remain;
    614		do
    615		{
    616			remain -= count;
    617			in  += count;
    618			out += count;
    619			int offset = -count;
    620			do
    621			{
    622				fixed_t in_0 = FROM_FIXED( in [offset] [0] );
    623				fixed_t in_1 = FROM_FIXED( in [offset] [1] );
    624
    625				BLIP_CLAMP( in_0, in_0 );
    626				out [offset] [0] = (blip_sample_t) in_0;
    627
    628				BLIP_CLAMP( in_1, in_1 );
    629				out [offset] [1] = (blip_sample_t) in_1;
    630			}
    631			while ( ++offset );
    632
    633			in = (stereo_fixed_t*) echo.begin();
    634			count = remain;
    635		}
    636		while ( remain );
    637	}
    638}