Mercurial > audlegacy
diff Plugins/Input/console/Effects_Buffer.cpp @ 90:252843aac42f trunk
[svn] Import the initial sources for console music support.
author | nenolod |
---|---|
date | Tue, 01 Nov 2005 19:57:26 -0800 |
parents | |
children | 84aabc053b6e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/Input/console/Effects_Buffer.cpp Tue Nov 01 19:57:26 2005 -0800 @@ -0,0 +1,466 @@ + +// Game_Music_Emu 0.2.4. http://www.slack.net/~ant/libs/ + +#include "Effects_Buffer.h" + +#include <string.h> + +/* Copyright (C) 2003-2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include BLARGG_SOURCE_BEGIN + +typedef 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.0; + pan_2 = 0.0; + reverb_delay = 88; + reverb_level = 0.10; + echo_delay = 61; + echo_level = 0.12; + delay_variance = 18; + effects_enabled = false; +} + +Effects_Buffer::Effects_Buffer() +{ + echo_buf = NULL; + echo_pos = 0; + + reverb_buf = NULL; + reverb_pos = 0; + + stereo_remain = 0; + effect_remain = 0; + effects_enabled = false; + config( config_t() ); +} + +Effects_Buffer::~Effects_Buffer() +{ + delete [] echo_buf; + delete [] reverb_buf; +} + +blargg_err_t Effects_Buffer::sample_rate( long rate, int msec ) +{ + if ( !echo_buf ) + { + echo_buf = new blip_sample_t [echo_size]; + if ( !echo_buf ) + return "Out of memory"; + } + + if ( !reverb_buf ) + { + reverb_buf = new blip_sample_t [reverb_size]; + if ( !reverb_buf ) + return "Out of memory"; + } + + for ( int i = 0; i < buf_count; i++ ) + BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) ); + + length_ = msec; + sample_rate_ = rate; + + config( config_ ); + + return blargg_success; +} + +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; + memset( echo_buf, 0, echo_size * sizeof (blip_sample_t) ); + memset( reverb_buf, 0, reverb_size * sizeof (blip_sample_t) ); + 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 ) +{ + // clear echo and reverb buffers + if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf ) { + memset( echo_buf, 0, echo_size * sizeof (blip_sample_t) ); + memset( reverb_buf, 0, reverb_size * sizeof (blip_sample_t) ); + } + + 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 ); + + const int delay_offset = config_.delay_variance * sample_rate_ / (1000 * 2); + + const int reverb_sample_delay = config_.reverb_delay * sample_rate_ / 1000; + 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 ); + + const int echo_sample_delay = config_.echo_delay * sample_rate_ / 1000; + 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 ); + + // set up outputs + for ( unsigned i = 0; i < chan_count; i++ ) { + channel_t& o = channels [i]; + if ( i < 2 ) { + o.center = &bufs [i]; + o.left = &bufs [3]; + o.right = &bufs [4]; + } + else { + o.center = &bufs [2]; + o.left = &bufs [5]; + o.right = &bufs [6]; + } + } + + } + else { + // set up outputs + for ( unsigned i = 0; i < chan_count; i++ ) { + channel_t& o = channels [i]; + o.center = &bufs [0]; + o.left = &bufs [1]; + o.right = &bufs [2]; + } + } +} + +void Effects_Buffer::end_frame( blip_time_t clock_count, bool stereo ) +{ + for ( int i = 0; i < buf_count; i++ ) + bufs [i].end_frame( clock_count ); + + if ( stereo ) + 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; +} + +#include BLARGG_ENABLE_OPTIMIZER + +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 > (unsigned) total_samples / 2 ) + remain = (unsigned) total_samples / 2; + total_samples = remain; + while ( remain ) + { + int active_bufs = buf_count; + long count = remain; + + 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, long count ) +{ + Blip_Reader c; + int shift = c.begin( bufs [0] ); + + // unrolled loop + for ( long n = count >> 1; n--; ) + { + long cs0 = c.read(); + c.next( shift ); + + long cs1 = c.read(); + c.next( shift ); + + 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 = c.read(); + c.next( shift ); + out [0] = s; + out [1] = s; + if ( (BOOST::int16_t) s != s ) { + s = 0x7FFF - (s >> 24); + out [0] = s; + out [1] = s; + } + } + + c.end( bufs [0] ); +} + +void Effects_Buffer::mix_stereo( blip_sample_t* out, long count ) +{ + Blip_Reader l; l.begin( bufs [1] ); + Blip_Reader r; r.begin( bufs [2] ); + Blip_Reader c; + int shift = c.begin( bufs [0] ); + + while ( count-- ) { + int cs = c.read(); + c.next( shift ); + int left = cs + l.read(); + int right = cs + r.read(); + l.next( shift ); + r.next( shift ); + + 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); + } + + c.end( bufs [0] ); + r.end( bufs [2] ); + l.end( bufs [1] ); +} + +void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out, long count ) +{ + Blip_Reader sq1; sq1.begin( bufs [0] ); + Blip_Reader sq2; sq2.begin( bufs [1] ); + Blip_Reader center; + int shift = center.begin( bufs [2] ); + + int echo_pos = this->echo_pos; + int reverb_pos = this->reverb_pos; + + while ( count-- ) + { + int sum1_s = sq1.read(); + int sum2_s = sq2.read(); + + sq1.next( shift ); + sq2.next( shift ); + + 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] = FMUL( new_reverb_l, reverb_level ); + reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level ); + reverb_pos = (reverb_pos + 2) & reverb_mask; + + int sum3_s = center.read(); + center.next( shift ); + + 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; + + sq1.end( bufs [0] ); + sq2.end( bufs [1] ); + center.end( bufs [2] ); +} + +void Effects_Buffer::mix_enhanced( blip_sample_t* out, long count ) +{ + Blip_Reader l1; l1.begin( bufs [3] ); + Blip_Reader r1; r1.begin( bufs [4] ); + Blip_Reader l2; l2.begin( bufs [5] ); + Blip_Reader r2; r2.begin( bufs [6] ); + Blip_Reader sq1; sq1.begin( bufs [0] ); + Blip_Reader sq2; sq2.begin( bufs [1] ); + Blip_Reader center; + int shift = center.begin( bufs [2] ); + + int echo_pos = this->echo_pos; + int reverb_pos = this->reverb_pos; + + while ( count-- ) + { + int sum1_s = sq1.read(); + int sum2_s = sq2.read(); + + sq1.next( shift ); + sq2.next( shift ); + + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + + FMUL( sum2_s, chans.pan_2_levels [0] ) + l1.read() + + 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] ) + r1.read() + + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; + + l1.next( shift ); + r1.next( shift ); + + fixed_t reverb_level = chans.reverb_level; + reverb_buf [reverb_pos] = FMUL( new_reverb_l, reverb_level ); + reverb_buf [reverb_pos + 1] = FMUL( new_reverb_r, reverb_level ); + reverb_pos = (reverb_pos + 2) & reverb_mask; + + int sum3_s = center.read(); + center.next( shift ); + + int left = new_reverb_l + sum3_s + l2.read() + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); + int right = new_reverb_r + sum3_s + r2.read() + FMUL( chans.echo_level, + echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); + + l2.next( shift ); + r2.next( shift ); + + 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; + + sq1.end( bufs [0] ); + sq2.end( bufs [1] ); + center.end( bufs [2] ); + l1.end( bufs [3] ); + r1.end( bufs [4] ); + l2.end( bufs [5] ); + r2.end( bufs [6] ); +} +