diff options
Diffstat (limited to 'plugins/gme/game-music-emu-svn/gme/Effects_Buffer.cpp')
-rw-r--r-- | plugins/gme/game-music-emu-svn/gme/Effects_Buffer.cpp | 529 |
1 files changed, 529 insertions, 0 deletions
diff --git a/plugins/gme/game-music-emu-svn/gme/Effects_Buffer.cpp b/plugins/gme/game-music-emu-svn/gme/Effects_Buffer.cpp new file mode 100644 index 00000000..181b11e9 --- /dev/null +++ b/plugins/gme/game-music-emu-svn/gme/Effects_Buffer.cpp @@ -0,0 +1,529 @@ +// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/ + +#include "Effects_Buffer.h" + +#include <string.h> + +/* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +typedef blargg_long fixed_t; + +#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) +#define FMUL( x, y ) (((x) * (y)) >> 15) + +const unsigned echo_size = 4096; +const unsigned echo_mask = echo_size - 1; +BOOST_STATIC_ASSERT( (echo_size & echo_mask) == 0 ); // must be power of 2 + +const unsigned reverb_size = 8192 * 2; +const unsigned reverb_mask = reverb_size - 1; +BOOST_STATIC_ASSERT( (reverb_size & reverb_mask) == 0 ); // must be power of 2 + +Effects_Buffer::config_t::config_t() +{ + pan_1 = -0.15f; + pan_2 = 0.15f; + reverb_delay = 88.0f; + reverb_level = 0.12f; + echo_delay = 61.0f; + echo_level = 0.10f; + delay_variance = 18.0f; + effects_enabled = false; +} + +void Effects_Buffer::set_depth( double d ) +{ + float f = (float) d; + config_t c; + c.pan_1 = -0.6f * f; + c.pan_2 = 0.6f * f; + c.reverb_delay = 880 * 0.1f; + c.echo_delay = 610 * 0.1f; + if ( f > 0.5 ) + f = 0.5; // TODO: more linear reduction of extreme reverb/echo + c.reverb_level = 0.5f * f; + c.echo_level = 0.30f * f; + c.delay_variance = 180 * 0.1f; + c.effects_enabled = (d > 0.0f); + config( c ); +} + +Effects_Buffer::Effects_Buffer( bool center_only ) : Multi_Buffer( 2 ) +{ + buf_count = center_only ? max_buf_count - 4 : max_buf_count; + + echo_pos = 0; + reverb_pos = 0; + + stereo_remain = 0; + effect_remain = 0; + effects_enabled = false; + set_depth( 0 ); +} + +Effects_Buffer::~Effects_Buffer() { } + +blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) +{ + if ( !echo_buf.size() ) + RETURN_ERR( echo_buf.resize( echo_size ) ); + + if ( !reverb_buf.size() ) + RETURN_ERR( reverb_buf.resize( reverb_size ) ); + + for ( int i = 0; i < buf_count; i++ ) + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + + config( config_ ); + clear(); + + return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); +} + +void Effects_Buffer::clock_rate( long rate ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clock_rate( rate ); +} + +void Effects_Buffer::bass_freq( int freq ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].bass_freq( freq ); +} + +void Effects_Buffer::clear() +{ + stereo_remain = 0; + effect_remain = 0; + if ( echo_buf.size() ) + memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); + + if ( reverb_buf.size() ) + memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); + + for ( int i = 0; i < buf_count; i++ ) + bufs [i].clear(); +} + +inline int pin_range( int n, int max, int min = 0 ) +{ + if ( n < min ) + return min; + if ( n > max ) + return max; + return n; +} + +void Effects_Buffer::config( const config_t& cfg ) +{ + channels_changed(); + + // clear echo and reverb buffers + if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf.size() ) + { + memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); + memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); + } + + config_ = cfg; + + if ( config_.effects_enabled ) + { + // convert to internal format + + chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 ); + chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0]; + + chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 ); + chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0]; + + chans.reverb_level = TO_FIXED( config_.reverb_level ); + chans.echo_level = TO_FIXED( config_.echo_level ); + + int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate()); + + int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate()); + chans.reverb_delay_l = pin_range( reverb_size - + (reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 ); + chans.reverb_delay_r = pin_range( reverb_size + 1 - + (reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 ); + + int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate()); + chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset), + echo_size - 1 ); + chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset), + echo_size - 1 ); + + chan_types [0].center = &bufs [0]; + chan_types [0].left = &bufs [3]; + chan_types [0].right = &bufs [4]; + + chan_types [1].center = &bufs [1]; + chan_types [1].left = &bufs [3]; + chan_types [1].right = &bufs [4]; + + chan_types [2].center = &bufs [2]; + chan_types [2].left = &bufs [5]; + chan_types [2].right = &bufs [6]; + assert( 2 < chan_types_count ); + } + else + { + // set up outputs + for ( unsigned i = 0; i < chan_types_count; i++ ) + { + channel_t& c = chan_types [i]; + c.center = &bufs [0]; + c.left = &bufs [1]; + c.right = &bufs [2]; + } + } + + if ( buf_count < max_buf_count ) + { + for ( int i = 0; i < chan_types_count; i++ ) + { + channel_t& c = chan_types [i]; + c.left = c.center; + c.right = c.center; + } + } +} + +Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type ) +{ + int out = 2; + if ( !type ) + { + out = i % 5; + if ( out > 2 ) + out = 2; + } + else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 ) + { + out = type & 1; + } + return chan_types [out]; +} + +void Effects_Buffer::end_frame( blip_time_t clock_count ) +{ + int bufs_used = 0; + for ( int i = 0; i < buf_count; i++ ) + { + bufs_used |= bufs [i].clear_modified() << i; + bufs [i].end_frame( clock_count ); + } + + int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06); + if ( (bufs_used & stereo_mask) && buf_count == max_buf_count ) + stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency(); + + if ( effects_enabled || config_.effects_enabled ) + effect_remain = bufs [0].samples_avail() + bufs [0].output_latency(); + + effects_enabled = config_.effects_enabled; +} + +long Effects_Buffer::samples_avail() const +{ + return bufs [0].samples_avail() * 2; +} + +long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) +{ + require( total_samples % 2 == 0 ); // count must be even + + long remain = bufs [0].samples_avail(); + if ( remain > (total_samples >> 1) ) + remain = (total_samples >> 1); + total_samples = remain; + while ( remain ) + { + int active_bufs = buf_count; + long count = remain; + + // optimizing mixing to skip any channels which had nothing added + if ( effect_remain ) + { + if ( count > effect_remain ) + count = effect_remain; + + if ( stereo_remain ) + { + mix_enhanced( out, count ); + } + else + { + mix_mono_enhanced( out, count ); + active_bufs = 3; + } + } + else if ( stereo_remain ) + { + mix_stereo( out, count ); + active_bufs = 3; + } + else + { + mix_mono( out, count ); + active_bufs = 1; + } + + out += count * 2; + remain -= count; + + stereo_remain -= count; + if ( stereo_remain < 0 ) + stereo_remain = 0; + + effect_remain -= count; + if ( effect_remain < 0 ) + effect_remain = 0; + + for ( int i = 0; i < buf_count; i++ ) + { + if ( i < active_bufs ) + bufs [i].remove_samples( count ); + else + bufs [i].remove_silence( count ); // keep time synchronized + } + } + + return total_samples * 2; +} + +void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [0] ); + BLIP_READER_BEGIN( c, bufs [0] ); + + // unrolled loop + for ( blargg_long n = count >> 1; n; --n ) + { + blargg_long cs0 = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + + blargg_long cs1 = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + + if ( (BOOST::int16_t) cs0 != cs0 ) + cs0 = 0x7FFF - (cs0 >> 24); + ((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16); + + if ( (BOOST::int16_t) cs1 != cs1 ) + cs1 = 0x7FFF - (cs1 >> 24); + ((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16); + out += 4; + } + + if ( count & 1 ) + { + int s = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + out [0] = s; + out [1] = s; + if ( (BOOST::int16_t) s != s ) + { + s = 0x7FFF - (s >> 24); + out [0] = s; + out [1] = s; + } + } + + BLIP_READER_END( c, bufs [0] ); +} + +void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [0] ); + BLIP_READER_BEGIN( c, bufs [0] ); + BLIP_READER_BEGIN( l, bufs [1] ); + BLIP_READER_BEGIN( r, bufs [2] ); + + while ( count-- ) + { + int cs = BLIP_READER_READ( c ); + BLIP_READER_NEXT( c, bass ); + int left = cs + BLIP_READER_READ( l ); + int right = cs + BLIP_READER_READ( r ); + BLIP_READER_NEXT( l, bass ); + BLIP_READER_NEXT( r, bass ); + + if ( (BOOST::int16_t) left != left ) + left = 0x7FFF - (left >> 24); + + out [0] = left; + out [1] = right; + + out += 2; + + if ( (BOOST::int16_t) right != right ) + out [-1] = 0x7FFF - (right >> 24); + } + + BLIP_READER_END( r, bufs [2] ); + BLIP_READER_END( l, bufs [1] ); + BLIP_READER_END( c, bufs [0] ); +} + +void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [2] ); + BLIP_READER_BEGIN( center, bufs [2] ); + BLIP_READER_BEGIN( sq1, bufs [0] ); + BLIP_READER_BEGIN( sq2, bufs [1] ); + + blip_sample_t* const reverb_buf = this->reverb_buf.begin(); + blip_sample_t* const echo_buf = this->echo_buf.begin(); + int echo_pos = this->echo_pos; + int reverb_pos = this->reverb_pos; + + while ( count-- ) + { + int sum1_s = BLIP_READER_READ( sq1 ); + int sum2_s = BLIP_READER_READ( sq2 ); + + BLIP_READER_NEXT( sq1, bass ); + BLIP_READER_NEXT( sq2, bass ); + + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + + FMUL( sum2_s, chans.pan_2_levels [0] ) + + reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; + + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + + FMUL( sum2_s, chans.pan_2_levels [1] ) + + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; + + fixed_t reverb_level = chans.reverb_level; + reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); + reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); + reverb_pos = (reverb_pos + 2) & reverb_mask; + + int sum3_s = BLIP_READER_READ( center ); + BLIP_READER_NEXT( center, bass ); + + int left = new_reverb_l + sum3_s + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); + int right = new_reverb_r + sum3_s + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); + + echo_buf [echo_pos] = sum3_s; + echo_pos = (echo_pos + 1) & echo_mask; + + if ( (BOOST::int16_t) left != left ) + left = 0x7FFF - (left >> 24); + + out [0] = left; + out [1] = right; + + out += 2; + + if ( (BOOST::int16_t) right != right ) + out [-1] = 0x7FFF - (right >> 24); + } + this->reverb_pos = reverb_pos; + this->echo_pos = echo_pos; + + BLIP_READER_END( sq1, bufs [0] ); + BLIP_READER_END( sq2, bufs [1] ); + BLIP_READER_END( center, bufs [2] ); +} + +void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [2] ); + BLIP_READER_BEGIN( center, bufs [2] ); + BLIP_READER_BEGIN( l1, bufs [3] ); + BLIP_READER_BEGIN( r1, bufs [4] ); + BLIP_READER_BEGIN( l2, bufs [5] ); + BLIP_READER_BEGIN( r2, bufs [6] ); + BLIP_READER_BEGIN( sq1, bufs [0] ); + BLIP_READER_BEGIN( sq2, bufs [1] ); + + blip_sample_t* const reverb_buf = this->reverb_buf.begin(); + blip_sample_t* const echo_buf = this->echo_buf.begin(); + int echo_pos = this->echo_pos; + int reverb_pos = this->reverb_pos; + + while ( count-- ) + { + int sum1_s = BLIP_READER_READ( sq1 ); + int sum2_s = BLIP_READER_READ( sq2 ); + + BLIP_READER_NEXT( sq1, bass ); + BLIP_READER_NEXT( sq2, bass ); + + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + + FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) + + reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; + + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + + FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) + + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; + + BLIP_READER_NEXT( l1, bass ); + BLIP_READER_NEXT( r1, bass ); + + fixed_t reverb_level = chans.reverb_level; + reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); + reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); + reverb_pos = (reverb_pos + 2) & reverb_mask; + + int sum3_s = BLIP_READER_READ( center ); + BLIP_READER_NEXT( center, bass ); + + int left = new_reverb_l + sum3_s + BLIP_READER_READ( l2 ) + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); + int right = new_reverb_r + sum3_s + BLIP_READER_READ( r2 ) + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); + + BLIP_READER_NEXT( l2, bass ); + BLIP_READER_NEXT( r2, bass ); + + echo_buf [echo_pos] = sum3_s; + echo_pos = (echo_pos + 1) & echo_mask; + + if ( (BOOST::int16_t) left != left ) + left = 0x7FFF - (left >> 24); + + out [0] = left; + out [1] = right; + + out += 2; + + if ( (BOOST::int16_t) right != right ) + out [-1] = 0x7FFF - (right >> 24); + } + this->reverb_pos = reverb_pos; + this->echo_pos = echo_pos; + + BLIP_READER_END( l1, bufs [3] ); + BLIP_READER_END( r1, bufs [4] ); + BLIP_READER_END( l2, bufs [5] ); + BLIP_READER_END( r2, bufs [6] ); + BLIP_READER_END( sq1, bufs [0] ); + BLIP_READER_END( sq2, bufs [1] ); + BLIP_READER_END( center, bufs [2] ); +} + |