raze/libraries/game-music-emu/gme/Effects_Buffer.cpp
Christoph Oelckers 718112a8fe - added external libraries for music format playback and decompression from GZDoom.
Currently none of these is being used, but eventually they will, once more code gets ported over.
So it's better to have them right away and avoid editing the project file too much, only to revert that later.
2019-09-22 08:59:48 +02:00

595 lines
17 KiB
C++

// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
#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( int num_voices, bool center_only )
: Multi_Buffer( 2*num_voices )
, max_voices(num_voices)
, bufs(max_voices * (center_only ? (max_buf_count - 4) : max_buf_count))
, chan_types(max_voices * chan_types_count)
, stereo_remain(0)
, effect_remain(0)
// TODO: Reorder buf_count to be initialized before bufs to factor out channel sizing
, buf_count(max_voices * (center_only ? (max_buf_count - 4) : max_buf_count))
, effects_enabled(false)
, reverb_buf(max_voices, std::vector<blip_sample_t>(reverb_size))
, echo_buf(max_voices, std::vector<blip_sample_t>(echo_size))
, reverb_pos(max_voices)
, echo_pos(max_voices)
{
set_depth( 0 );
}
Effects_Buffer::~Effects_Buffer()
{}
blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec )
{
try
{
for(int i=0; i<max_voices; i++)
{
if ( !echo_buf[i].size() )
{
echo_buf[i].resize( echo_size );
}
if ( !reverb_buf[i].size() )
{
reverb_buf[i].resize( reverb_size );
}
}
}
catch(std::bad_alloc& ba)
{
return "Out of memory";
}
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;
for(int i=0; i<max_voices; i++)
{
if ( echo_buf[i].size() )
memset( &echo_buf[i][0], 0, echo_size * sizeof echo_buf[i][0] );
if ( reverb_buf[i].size() )
memset( &reverb_buf[i][0], 0, reverb_size * sizeof reverb_buf[i][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
// ensure the echo/reverb buffers have already been allocated, so this method can be
// called before set_sample_rate is called
if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf[0].size() )
{
for(int i=0; i<max_voices; i++)
{
memset( &echo_buf[i][0], 0, echo_size * sizeof echo_buf[i][0] );
memset( &reverb_buf[i][0], 0, reverb_size * sizeof reverb_buf[i][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 );
for(int i=0; i<max_voices; i++)
{
chan_types [i*chan_types_count+0].center = &bufs [i*max_buf_count+0];
chan_types [i*chan_types_count+0].left = &bufs [i*max_buf_count+3];
chan_types [i*chan_types_count+0].right = &bufs [i*max_buf_count+4];
chan_types [i*chan_types_count+1].center = &bufs [i*max_buf_count+1];
chan_types [i*chan_types_count+1].left = &bufs [i*max_buf_count+3];
chan_types [i*chan_types_count+1].right = &bufs [i*max_buf_count+4];
chan_types [i*chan_types_count+2].center = &bufs [i*max_buf_count+2];
chan_types [i*chan_types_count+2].left = &bufs [i*max_buf_count+5];
chan_types [i*chan_types_count+2].right = &bufs [i*max_buf_count+6];
}
assert( 2 < chan_types_count );
}
else
{
for(int i=0; i<max_voices; i++)
{
// set up outputs
for ( int j = 0; j < chan_types_count; j++ )
{
channel_t& c = chan_types [i*chan_types_count+j];
c.center = &bufs [i*max_buf_count+0];
c.left = &bufs [i*max_buf_count+1];
c.right = &bufs [i*max_buf_count+2];
}
}
}
if ( buf_count < max_buf_count ) // if center_only
{
for(int i=0; i<max_voices; i++)
{
for ( int j = 0; j < chan_types_count; j++ )
{
channel_t& c = chan_types [i*chan_types_count+j];
c.left = c.center;
c.right = c.center;
}
}
}
}
Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type )
{
int out = chan_types_count-1;
if ( !type )
{
out = i % 5;
if ( out > chan_types_count-1 )
out = chan_types_count-1;
}
else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 )
{
out = type & 1;
}
return chan_types [(i%max_voices)*chan_types_count+out];
}
void Effects_Buffer::end_frame( blip_time_t clock_count )
{
int bufs_used = 0;
int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06);
const int buf_count_per_voice = buf_count/max_voices;
for ( int v = 0; v < max_voices; v++ ) // foreach voice
{
for ( int i = 0; i < buf_count_per_voice; i++) // foreach buffer of that voice
{
bufs_used |= bufs [v*buf_count_per_voice + i].clear_modified() << i;
bufs [v*buf_count_per_voice + i].end_frame( clock_count );
if ( (bufs_used & stereo_mask) && buf_count == max_voices*max_buf_count )
stereo_remain = max(stereo_remain, bufs [v*buf_count_per_voice + i].samples_avail() + bufs [v*buf_count_per_voice + i].output_latency());
if ( effects_enabled || config_.effects_enabled )
effect_remain = max(effect_remain, bufs [v*buf_count_per_voice + i].samples_avail() + bufs [v*buf_count_per_voice + i].output_latency());
}
bufs_used = 0;
}
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 )
{
const int n_channels = max_voices * 2;
const int buf_count_per_voice = buf_count/max_voices;
require( total_samples % n_channels == 0 ); // as many items needed to fill at least one frame
long remain = bufs [0].samples_avail();
total_samples = remain = min( remain, total_samples/n_channels );
while ( remain )
{
int active_bufs = buf_count_per_voice;
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 * n_channels;
remain -= count;
stereo_remain -= count;
if ( stereo_remain < 0 )
stereo_remain = 0;
effect_remain -= count;
if ( effect_remain < 0 )
effect_remain = 0;
// skip the output from any buffers that didn't contribute to the sound output
// during this frame (e.g. if we only render mono then only the very first buf
// is 'active')
for ( int v = 0; v < max_voices; v++ ) // foreach voice
{
for ( int i = 0; i < buf_count_per_voice; i++) // foreach buffer of that voice
{
if ( i < active_bufs )
bufs [v*buf_count_per_voice + i].remove_samples( count );
else // keep time synchronized
bufs [v*buf_count_per_voice + i].remove_silence( count );
}
}
}
return total_samples * n_channels;
}
void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count )
{
for(int i=0; i<max_voices; i++)
{
blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( c, bufs [i*max_buf_count+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 ( (int16_t) cs0 != cs0 )
cs0 = 0x7FFF - (cs0 >> 24);
((uint32_t*) out) [i*2+0] = ((uint16_t) cs0) | (uint16_t(cs0) << 16);
if ( (int16_t) cs1 != cs1 )
cs1 = 0x7FFF - (cs1 >> 24);
((uint32_t*) out) [i*2+1] = ((uint16_t) cs1) | (uint16_t(cs1) << 16);
out += max_voices*4;
}
if ( count & 1 )
{
int s = BLIP_READER_READ( c );
BLIP_READER_NEXT( c, bass );
out [i*2+0] = s;
out [i*2+1] = s;
if ( (int16_t) s != s )
{
s = 0x7FFF - (s >> 24);
out [i*2+0] = s;
out [i*2+1] = s;
}
}
BLIP_READER_END( c, bufs [i*max_buf_count+0] );
}
}
void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long frames )
{
for(int i=0; i<max_voices; i++)
{
blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( c, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( l, bufs [i*max_buf_count+1] );
BLIP_READER_BEGIN( r, bufs [i*max_buf_count+2] );
int count = frames;
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 ( (int16_t) left != left )
left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24);
out [i*2+0] = left;
out [i*2+1] = right;
out += max_voices*2;
}
BLIP_READER_END( r, bufs [i*max_buf_count+2] );
BLIP_READER_END( l, bufs [i*max_buf_count+1] );
BLIP_READER_END( c, bufs [i*max_buf_count+0] );
}
}
void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long frames )
{
for(int i=0; i<max_voices; i++)
{
blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( center, bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] );
blip_sample_t* const reverb_buf = &this->reverb_buf[i][0];
blip_sample_t* const echo_buf = &this->echo_buf[i][0];
int echo_pos = this->echo_pos[i];
int reverb_pos = this->reverb_pos[i];
int count = frames;
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 ( (int16_t) left != left )
left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24);
out [i*2+0] = left;
out [i*2+1] = right;
out += max_voices*2;
}
this->reverb_pos[i] = reverb_pos;
this->echo_pos[i] = echo_pos;
BLIP_READER_END( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_END( sq2, bufs [i*max_buf_count+1] );
BLIP_READER_END( center, bufs [i*max_buf_count+2] );
}
}
void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long frames )
{
for(int i=0; i<max_voices; i++)
{
blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( center, bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( l1, bufs [i*max_buf_count+3] );
BLIP_READER_BEGIN( r1, bufs [i*max_buf_count+4] );
BLIP_READER_BEGIN( l2, bufs [i*max_buf_count+5] );
BLIP_READER_BEGIN( r2, bufs [i*max_buf_count+6] );
BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] );
blip_sample_t* const reverb_buf = &this->reverb_buf[i][0];
blip_sample_t* const echo_buf = &this->echo_buf[i][0];
int echo_pos = this->echo_pos[i];
int reverb_pos = this->reverb_pos[i];
int count = frames;
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 ( (int16_t) left != left )
left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24);
out [i*2+0] = left;
out [i*2+1] = right;
out += max_voices*2;
}
this->reverb_pos[i] = reverb_pos;
this->echo_pos[i] = echo_pos;
BLIP_READER_END( l1, bufs [i*max_buf_count+3] );
BLIP_READER_END( r1, bufs [i*max_buf_count+4] );
BLIP_READER_END( l2, bufs [i*max_buf_count+5] );
BLIP_READER_END( r2, bufs [i*max_buf_count+6] );
BLIP_READER_END( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_END( sq2, bufs [i*max_buf_count+1] );
BLIP_READER_END( center, bufs [i*max_buf_count+2] );
}
}