Path: blob/a-new-beginning/Cherry/Core/audio/Effects_Buffer.cpp
2 views
// Game_Music_Emu $vers. http://www.slack.net/~ant/12#include "Effects_Buffer.h"34#include <string.h>56/* Copyright (C) 2006-2007 Shay Green. This module is free software; you7can redistribute it and/or modify it under the terms of the GNU Lesser8General Public License as published by the Free Software Foundation; either9version 2.1 of the License, or (at your option) any later version. This10module is distributed in the hope that it will be useful, but WITHOUT ANY11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS12FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more13details. You should have received a copy of the GNU Lesser General Public14License along with this module; if not, write to the Free Software Foundation,15Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */1617#include "blargg_source.h"1819int const fixed_shift = 12;20#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift))21#define FROM_FIXED( f ) ((f) >> fixed_shift)2223int const max_read = 2560; // determines minimum delay2425Effects_Buffer::Effects_Buffer( int max_bufs, long echo_size_ ) : Multi_Buffer( stereo )26{27echo_size = (int)max( max_read * (long) stereo, echo_size_ & ~1 );28clock_rate_ = 0;29bass_freq_ = 90;30bufs = 0;31bufs_size = 0;32bufs_max = max( max_bufs, (int) extra_chans );33no_echo = true;34no_effects = true;3536// defaults37config_.enabled = false;38config_.delay [0] = 120;39config_.delay [1] = 122;40config_.feedback = 0.2f;41config_.treble = 0.4f;4243static float const sep = 0.8f;44config_.side_chans [0].pan = -sep;45config_.side_chans [1].pan = +sep;46config_.side_chans [0].vol = 1.0f;47config_.side_chans [1].vol = 1.0f;4849memset( &s, 0, sizeof s );50clear();51}5253Effects_Buffer::~Effects_Buffer()54{55delete_bufs();56}5758// avoid using new []59blargg_err_t Effects_Buffer::new_bufs( int size )60{61bufs = (buf_t*) malloc( size * sizeof *bufs );62CHECK_ALLOC( bufs );63for ( int i = 0; i < size; i++ )64new (bufs + i) buf_t;65bufs_size = size;66return 0;67}6869void Effects_Buffer::delete_bufs()70{71if ( bufs )72{73for ( int i = bufs_size; --i >= 0; )74bufs [i].~buf_t();75free( bufs );76bufs = 0;77}78bufs_size = 0;79}8081blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec )82{83// extra to allow farther past-the-end pointers84mixer.samples_read = 0;85RETURN_ERR( echo.resize( echo_size + stereo ) );86return Multi_Buffer::set_sample_rate( rate, msec );87}8889void Effects_Buffer::clock_rate( long rate )90{91clock_rate_ = rate;92for ( int i = bufs_size; --i >= 0; )93bufs [i].clock_rate( clock_rate_ );94}9596void Effects_Buffer::bass_freq( int freq )97{98bass_freq_ = freq;99for ( int i = bufs_size; --i >= 0; )100bufs [i].bass_freq( bass_freq_ );101}102103blargg_err_t Effects_Buffer::set_channel_count( int count, int const* types )104{105RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) );106107delete_bufs();108109mixer.samples_read = 0;110111RETURN_ERR( chans.resize( count + extra_chans ) );112113RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) );114115for ( int i = bufs_size; --i >= 0; )116RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) );117118for ( int i = (int)chans.size(); --i >= 0; )119{120chan_t& ch = chans [i];121ch.cfg.vol = 1.0f;122ch.cfg.pan = 0.0f;123ch.cfg.surround = false;124ch.cfg.echo = false;125}126// side channels with echo127chans [2].cfg.echo = true;128chans [3].cfg.echo = true;129130clock_rate( clock_rate_ );131bass_freq( bass_freq_ );132apply_config();133clear();134135return 0;136}137138void Effects_Buffer::clear_echo()139{140if ( echo.size() )141memset( echo.begin(), 0, echo.size() * sizeof echo [0] );142}143144void Effects_Buffer::clear()145{146echo_pos = 0;147s.low_pass [0] = 0;148s.low_pass [1] = 0;149mixer.samples_read = 0;150151for ( int i = bufs_size; --i >= 0; )152bufs [i].clear();153clear_echo();154}155156Effects_Buffer::channel_t Effects_Buffer::channel( int i )157{158i += extra_chans;159require( extra_chans <= i && i < (int) chans.size() );160return chans [i].channel;161}162163164// Configuration165166// 3 wave positions with/without surround, 2 multi (one with same config as wave)167int const simple_bufs = 3 * 2 + 2 - 1;168169Simple_Effects_Buffer::Simple_Effects_Buffer() :170Effects_Buffer( extra_chans + simple_bufs, 18 * 1024L )171{172config_.echo = 0.20f;173config_.stereo = 0.20f;174config_.surround = true;175config_.enabled = false;176}177178void Simple_Effects_Buffer::apply_config()179{180Effects_Buffer::config_t& c = Effects_Buffer::config();181182c.enabled = config_.enabled;183if ( c.enabled )184{185c.delay [0] = 120;186c.delay [1] = 122;187c.feedback = config_.echo * 0.7f;188c.treble = 0.6f - 0.3f * config_.echo;189190float sep = config_.stereo + 0.80f;191if ( sep > 1.0f )192sep = 1.0f;193194c.side_chans [0].pan = -sep;195c.side_chans [1].pan = +sep;196197for ( int i = channel_count(); --i >= 0; )198{199chan_config_t& ch = Effects_Buffer::chan_config( i );200201ch.pan = 0.0f;202ch.surround = config_.surround;203ch.echo = false;204205int const type = (channel_types() ? channel_types() [i] : 0);206if ( !(type & noise_type) )207{208int index = (type & type_index_mask) % 6 - 3;209if ( index < 0 )210{211index += 3;212ch.surround = false;213ch.echo = true;214}215if ( index >= 1 )216{217ch.pan = config_.stereo;218if ( index == 1 )219ch.pan = -ch.pan;220}221}222else if ( type & 1 )223{224ch.surround = false;225}226}227}228229Effects_Buffer::apply_config();230}231232int Effects_Buffer::min_delay() const233{234require( sample_rate() );235return max_read * 1000L / sample_rate();236}237238int Effects_Buffer::max_delay() const239{240require( sample_rate() );241return (echo_size / stereo - max_read) * 1000L / sample_rate();242}243244void Effects_Buffer::apply_config()245{246int i;247248if ( !bufs_size )249return;250251s.treble = TO_FIXED( config_.treble );252253bool echo_dirty = false;254255fixed_t old_feedback = s.feedback;256s.feedback = TO_FIXED( config_.feedback );257if ( !old_feedback && s.feedback )258echo_dirty = true;259260// delays261for ( i = stereo; --i >= 0; )262{263long delay = config_.delay [i] * sample_rate() / 1000 * stereo;264delay = max( delay, long (max_read * stereo) );265delay = min( delay, long (echo_size - max_read * stereo) );266if ( s.delay [i] != delay )267{268s.delay [i] = delay;269echo_dirty = true;270}271}272273// side channels274for ( i = 2; --i >= 0; )275{276chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f;277chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan;278}279280// convert volumes281for ( i = (int)chans.size(); --i >= 0; )282{283chan_t& ch = chans [i];284ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan );285ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan );286if ( ch.cfg.surround )287ch.vol [0] = -ch.vol [0];288}289290assign_buffers();291292// set side channels293for ( i = (int)chans.size(); --i >= 0; )294{295chan_t& ch = chans [i];296ch.channel.left = chans [ch.cfg.echo*2 ].channel.center;297ch.channel.right = chans [ch.cfg.echo*2+1].channel.center;298}299300bool old_echo = !no_echo && !no_effects;301302// determine whether effects and echo are needed at all303no_effects = true;304no_echo = true;305for ( i = (int)chans.size(); --i >= extra_chans; )306{307chan_t& ch = chans [i];308if ( ch.cfg.echo && s.feedback )309no_echo = false;310311if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) )312no_effects = false;313}314if ( !no_echo )315no_effects = false;316317if ( chans [0].vol [0] != TO_FIXED( 1 ) ||318chans [0].vol [1] != TO_FIXED( 0 ) ||319chans [1].vol [0] != TO_FIXED( 0 ) ||320chans [1].vol [1] != TO_FIXED( 1 ) )321no_effects = false;322323if ( !config_.enabled )324no_effects = true;325326if ( no_effects )327{328for ( i = (int)chans.size(); --i >= 0; )329{330chan_t& ch = chans [i];331ch.channel.center = &bufs [2];332ch.channel.left = &bufs [0];333ch.channel.right = &bufs [1];334}335}336337mixer.bufs [0] = &bufs [0];338mixer.bufs [1] = &bufs [1];339mixer.bufs [2] = &bufs [2];340341if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) )342clear_echo();343344channels_changed();345}346347void Effects_Buffer::assign_buffers()348{349// assign channels to buffers350int buf_count = 0;351for ( int i = 0; i < (int) chans.size(); i++ )352{353// put second two side channels at end to give priority to main channels354// in case closest matching is necessary355int x = i;356if ( i > 1 )357x += 2;358if ( x >= (int) chans.size() )359x -= ((int)chans.size() - 2);360chan_t& ch = chans [x];361362int b = 0;363for ( ; b < buf_count; b++ )364{365if ( ch.vol [0] == bufs [b].vol [0] &&366ch.vol [1] == bufs [b].vol [1] &&367(ch.cfg.echo == bufs [b].echo || !s.feedback) )368break;369}370371if ( b >= buf_count )372{373if ( buf_count < bufs_max )374{375bufs [b].vol [0] = ch.vol [0];376bufs [b].vol [1] = ch.vol [1];377bufs [b].echo = ch.cfg.echo;378buf_count++;379}380else381{382// TODO: this is a mess, needs refinement383dprintf( "Effects_Buffer ran out of buffers; using closest match\n" );384b = 0;385fixed_t best_dist = TO_FIXED( 8 );386for ( int h = buf_count; --h >= 0; )387{388#define CALC_LEVELS( vols, sum, diff, surround ) \389fixed_t sum, diff;\390bool surround = false;\391{\392fixed_t vol_0 = vols [0];\393if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\394fixed_t vol_1 = vols [1];\395if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\396sum = vol_0 + vol_1;\397diff = vol_0 - vol_1;\398}399CALC_LEVELS( ch.vol, ch_sum, ch_diff, ch_surround );400CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround );401402fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff );403404if ( ch_surround != buf_surround )405dist += TO_FIXED( 1 ) / 2;406407if ( s.feedback && ch.cfg.echo != bufs [h].echo )408dist += TO_FIXED( 1 ) / 2;409410if ( best_dist > dist )411{412best_dist = dist;413b = h;414}415}416}417}418419//dprintf( "ch %d->buf %d\n", x, b );420ch.channel.center = &bufs [b];421}422}423424425// Mixing426427void Effects_Buffer::end_frame( blip_time_t time )428{429for ( int i = bufs_size; --i >= 0; )430bufs [i].end_frame( time );431}432433long Effects_Buffer::read_samples( blip_sample_t* out, long out_size )434{435out_size = min( out_size, samples_avail() );436437int pair_count = int (out_size >> 1);438require( pair_count * stereo == out_size ); // must read an even number of samples439if ( pair_count )440{441if ( no_effects )442{443mixer.read_pairs( out, pair_count );444}445else446{447int pairs_remain = pair_count;448do449{450// mix at most max_read pairs at a time451int count = max_read;452if ( count > pairs_remain )453count = pairs_remain;454455if ( no_echo )456{457// optimization: clear echo here to keep mix_effects() a leaf function458echo_pos = 0;459memset( echo.begin(), 0, count * stereo * sizeof echo [0] );460}461mix_effects( out, count );462463blargg_long new_echo_pos = echo_pos + count * stereo;464if ( new_echo_pos >= echo_size )465new_echo_pos -= echo_size;466echo_pos = new_echo_pos;467assert( echo_pos < echo_size );468469out += count * stereo;470mixer.samples_read += count;471pairs_remain -= count;472}473while ( pairs_remain );474}475476if ( samples_avail() <= 0 || immediate_removal() )477{478for ( int i = bufs_size; --i >= 0; )479{480buf_t& b = bufs [i];481// TODO: might miss non-silence settling since it checks END of last read482if ( b.non_silent() )483b.remove_samples( mixer.samples_read );484else485b.remove_silence( mixer.samples_read );486}487mixer.samples_read = 0;488}489}490return out_size;491}492493void Effects_Buffer::mix_effects( blip_sample_t* out_, int pair_count )494{495typedef fixed_t stereo_fixed_t [stereo];496497// add channels with echo, do echo, add channels without echo, then convert to 16-bit and output498int echo_phase = 1;499do500{501// mix any modified buffers502{503buf_t* buf = bufs;504int bufs_remain = bufs_size;505do506{507if ( buf->non_silent() && ( buf->echo == !!echo_phase ) )508{509stereo_fixed_t* BLIP_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos];510int const bass = BLIP_READER_BASS( *buf );511BLIP_READER_BEGIN( in, *buf );512BLIP_READER_ADJ_( in, mixer.samples_read );513fixed_t const vol_0 = buf->vol [0];514fixed_t const vol_1 = buf->vol [1];515516int count = unsigned (echo_size - echo_pos) / stereo;517int remain = pair_count;518if ( count > remain )519count = remain;520do521{522remain -= count;523BLIP_READER_ADJ_( in, count );524525out += count;526int offset = -count;527do528{529fixed_t s = BLIP_READER_READ( in );530BLIP_READER_NEXT_IDX_( in, bass, offset );531532out [offset] [0] += s * vol_0;533out [offset] [1] += s * vol_1;534}535while ( ++offset );536537out = (stereo_fixed_t*) echo.begin();538count = remain;539}540while ( remain );541542BLIP_READER_END( in, *buf );543}544buf++;545}546while ( --bufs_remain );547}548549// add echo550if ( echo_phase && !no_echo )551{552fixed_t const feedback = s.feedback;553fixed_t const treble = s.treble;554555int i = 1;556do557{558fixed_t low_pass = s.low_pass [i];559560fixed_t* echo_end = &echo [echo_size + i];561fixed_t const* BLIP_RESTRICT in_pos = &echo [echo_pos + i];562blargg_long out_offset = (int)(echo_pos + i + s.delay [i]);563if ( out_offset >= echo_size )564out_offset -= echo_size;565assert( out_offset < echo_size );566fixed_t* BLIP_RESTRICT out_pos = &echo [out_offset];567568// break into up to three chunks to avoid having to handle wrap-around569// in middle of core loop570int remain = pair_count;571do572{573fixed_t const* pos = in_pos;574if ( pos < out_pos )575pos = out_pos;576int count = blargg_ulong ((char*) echo_end - (char const*) pos) /577unsigned (stereo * sizeof (fixed_t));578if ( count > remain )579count = remain;580remain -= count;581582in_pos += count * stereo;583out_pos += count * stereo;584int offset = -count;585do586{587low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble;588out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback;589}590while ( ++offset );591592if ( in_pos >= echo_end ) in_pos -= echo_size;593if ( out_pos >= echo_end ) out_pos -= echo_size;594}595while ( remain );596597s.low_pass [i] = low_pass;598}599while ( --i >= 0 );600}601}602while ( --echo_phase >= 0 );603604// clamp to 16 bits605{606stereo_fixed_t const* BLIP_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos];607typedef blip_sample_t stereo_blip_sample_t [stereo];608stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_;609int count = unsigned (echo_size - echo_pos) / (unsigned) stereo;610int remain = pair_count;611if ( count > remain )612count = remain;613do614{615remain -= count;616in += count;617out += count;618int offset = -count;619do620{621fixed_t in_0 = FROM_FIXED( in [offset] [0] );622fixed_t in_1 = FROM_FIXED( in [offset] [1] );623624BLIP_CLAMP( in_0, in_0 );625out [offset] [0] = (blip_sample_t) in_0;626627BLIP_CLAMP( in_1, in_1 );628out [offset] [1] = (blip_sample_t) in_1;629}630while ( ++offset );631632in = (stereo_fixed_t*) echo.begin();633count = remain;634}635while ( remain );636}637}638639640