From 3f10fbb88e0d64374468aa31d72f82fbcc1aeb05 Mon Sep 17 00:00:00 2001 From: Karmo Rosental Date: Fri, 14 Jul 2017 23:51:58 +0300 Subject: [PATCH] Updated Freeboy files from Game Music Emu 0.6.1. (#3618) Uses upstream files to fix #326 --- plugins/papu/Basic_Gb_Apu.cpp | 14 +- plugins/papu/Basic_Gb_Apu.h | 20 +- plugins/papu/CMakeLists.txt | 2 +- plugins/papu/gb_apu/Blip_Buffer.cpp | 669 ++++++++++++++------------- plugins/papu/gb_apu/Blip_Buffer.h | 579 ++++++++++++++++------- plugins/papu/gb_apu/Blip_Synth.h | 208 --------- plugins/papu/gb_apu/Gb_Apu.cpp | 255 +++++----- plugins/papu/gb_apu/Gb_Apu.h | 66 +-- plugins/papu/gb_apu/Gb_Oscs.cpp | 511 ++++++++------------ plugins/papu/gb_apu/Gb_Oscs.h | 101 ++-- plugins/papu/gb_apu/Multi_Buffer.cpp | 207 +++++---- plugins/papu/gb_apu/Multi_Buffer.h | 94 ++-- plugins/papu/gb_apu/blargg_common.h | 298 ++++++------ plugins/papu/gb_apu/blargg_config.h | 43 ++ plugins/papu/gb_apu/blargg_source.h | 68 ++- plugins/papu/papu_instrument.cpp | 4 +- 16 files changed, 1606 insertions(+), 1533 deletions(-) delete mode 100644 plugins/papu/gb_apu/Blip_Synth.h create mode 100644 plugins/papu/gb_apu/blargg_config.h diff --git a/plugins/papu/Basic_Gb_Apu.cpp b/plugins/papu/Basic_Gb_Apu.cpp index 7307eb8a21d..53c2fab1785 100644 --- a/plugins/papu/Basic_Gb_Apu.cpp +++ b/plugins/papu/Basic_Gb_Apu.cpp @@ -14,15 +14,11 @@ 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 */ -gb_time_t const frame_length = 70224; +blip_time_t const frame_length = 70224; Basic_Gb_Apu::Basic_Gb_Apu() { time = 0; - - // Adjust frequency equalization to make it sound like a tiny speaker - apu.treble_eq( -20.0 ); // lower values muffle it more - buf.bass_freq( 461 ); // higher values simulate smaller speaker } Basic_Gb_Apu::~Basic_Gb_Apu() @@ -36,12 +32,12 @@ blargg_err_t Basic_Gb_Apu::set_sample_rate( long rate ) return buf.set_sample_rate( rate ); } -void Basic_Gb_Apu::write_register( gb_addr_t addr, int data ) +void Basic_Gb_Apu::write_register( blip_time_t addr, int data ) { apu.write_register( clock(), addr, data ); } -int Basic_Gb_Apu::read_register( gb_addr_t addr ) +int Basic_Gb_Apu::read_register( blip_time_t addr ) { return apu.read_register( clock(), addr ); } @@ -49,8 +45,8 @@ int Basic_Gb_Apu::read_register( gb_addr_t addr ) void Basic_Gb_Apu::end_frame() { time = 0; - bool stereo = apu.end_frame( frame_length ); - buf.end_frame( frame_length, stereo ); + apu.end_frame( frame_length ); + buf.end_frame( frame_length ); } long Basic_Gb_Apu::samples_avail() const diff --git a/plugins/papu/Basic_Gb_Apu.h b/plugins/papu/Basic_Gb_Apu.h index 24b9dc7749d..b39efe9cfb9 100644 --- a/plugins/papu/Basic_Gb_Apu.h +++ b/plugins/papu/Basic_Gb_Apu.h @@ -15,23 +15,23 @@ class Basic_Gb_Apu { public: Basic_Gb_Apu(); ~Basic_Gb_Apu(); - + // Set output sample rate blargg_err_t set_sample_rate( long rate ); - + // Pass reads and writes in the range 0xff10-0xff3f - void write_register( gb_addr_t, int data ); - int read_register( gb_addr_t ); - + void write_register( blip_time_t, int data ); + int read_register( blip_time_t ); + // End a 1/60 sound frame and add samples to buffer void end_frame(); - + // Samples are generated in stereo, left first. Sample counts are always // a multiple of 2. - + // Number of samples in buffer long samples_avail() const; - + // Read at most 'count' samples out of buffer and return number actually read typedef blip_sample_t sample_t; long read_samples( sample_t* out, long count ); @@ -41,12 +41,12 @@ class Basic_Gb_Apu { void treble_eq( const blip_eq_t& eq ); void bass_freq( int bf ); //<--- - + private: Gb_Apu apu; Stereo_Buffer buf; blip_time_t time; - + // faked CPU timing blip_time_t clock() { return time += 4; } }; diff --git a/plugins/papu/CMakeLists.txt b/plugins/papu/CMakeLists.txt index 8662a1cb3b4..b45fcf61472 100644 --- a/plugins/papu/CMakeLists.txt +++ b/plugins/papu/CMakeLists.txt @@ -3,4 +3,4 @@ INCLUDE(BuildPlugin) # Disable C++11 REMOVE_DEFINITIONS(-std=c++0x) -BUILD_PLUGIN(papu papu_instrument.cpp papu_instrument.h Basic_Gb_Apu.cpp Basic_Gb_Apu.h gb_apu/Gb_Oscs.cpp gb_apu/Gb_Apu.h gb_apu/Blip_Buffer.cpp gb_apu/Gb_Apu.cpp gb_apu/Gb_Oscs.h gb_apu/blargg_common.h gb_apu/Blip_Buffer.h gb_apu/Multi_Buffer.cpp gb_apu/blargg_source.h gb_apu/Blip_Synth.h gb_apu/Multi_Buffer.h MOCFILES papu_instrument.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(papu papu_instrument.cpp papu_instrument.h Basic_Gb_Apu.cpp Basic_Gb_Apu.h gb_apu/Gb_Oscs.cpp gb_apu/Gb_Apu.h gb_apu/Blip_Buffer.cpp gb_apu/Gb_Apu.cpp gb_apu/Gb_Oscs.h gb_apu/blargg_common.h gb_apu/Blip_Buffer.h gb_apu/Multi_Buffer.cpp gb_apu/blargg_source.h gb_apu/Multi_Buffer.h MOCFILES papu_instrument.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/papu/gb_apu/Blip_Buffer.cpp b/plugins/papu/gb_apu/Blip_Buffer.cpp index e291d73abe2..a46b0e350d4 100644 --- a/plugins/papu/gb_apu/Blip_Buffer.cpp +++ b/plugins/papu/gb_apu/Blip_Buffer.cpp @@ -1,429 +1,460 @@ - -// Blip_Buffer 0.3.4. http://www.slack.net/~ant/libs/ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ #include "Blip_Buffer.h" +#include +#include #include -#include +#include #include -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +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 */ + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif -#include BLARGG_SOURCE_BEGIN +int const silent_buf_size = 1; // size used for Silent_Blip_Buffer Blip_Buffer::Blip_Buffer() { - samples_per_sec = 44100; - buffer_ = NULL; - - // try to cause assertion failure if buffer is used before these are set - clocks_per_sec = 0; - factor_ = ~0ul; - offset_ = 0; - buffer_size_ = 0; - length_ = 0; - - bass_freq_ = 16; + factor_ = (blip_ulong)-1 / 2; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + buf_t_ i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting to short truncates to 16 bits and sign-extends + i = 0x18000; + assert( (short) i == -0x8000 ); + #endif +} + +Blip_Buffer::~Blip_Buffer() +{ + if ( buffer_size_ != silent_buf_size ) + free( buffer_ ); +} + +Silent_Blip_Buffer::Silent_Blip_Buffer() +{ + factor_ = 0; + buffer_ = buf; + buffer_size_ = silent_buf_size; + memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow } -void Blip_Buffer::clear( bool entire_buffer ) +void Blip_Buffer::clear( int entire_buffer ) { - long count = (entire_buffer ? buffer_size_ : samples_avail()); - offset_ = 0; - reader_accum = 0; + offset_ = 0; + reader_accum_ = 0; + modified_ = 0; if ( buffer_ ) - memset( buffer_, sample_offset_ & 0xFF, (count + widest_impulse_) * sizeof (buf_t_) ); + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); + } } -blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) { - unsigned new_size = (0xFFFFFFFF >> BLIP_BUFFER_ACCURACY) + 1 - widest_impulse_ - 64; - if ( msec != blip_default_length ) + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return "Internal (tried to resize Silent_Blip_Buffer)"; + } + + // start with maximum length that resampled time can represent + long new_size = (UINT_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; + if ( msec != blip_max_length ) { - size_t s = (new_rate * (msec + 1) + 999) / 1000; + long s = (new_rate * (msec + 1) + 999) / 1000; if ( s < new_size ) new_size = s; else - require( false ); // requested buffer length exceeds limit + assert( 0 ); // fails if requested buffer length exceeds limit } - + if ( buffer_size_ != new_size ) { - delete [] buffer_; - buffer_ = NULL; // allow for exception in allocation below - buffer_size_ = 0; - offset_ = 0; - - int const count_clocks_extra = 2; - buffer_ = BLARGG_NEW buf_t_ [new_size + widest_impulse_ + count_clocks_extra]; - BLARGG_CHECK_ALLOC( buffer_ ); + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (buf_t_*) p; } - + buffer_size_ = new_size; + assert( buffer_size_ != silent_buf_size ); + + // update things based on the sample rate + sample_rate_ = new_rate; length_ = new_size * 1000 / new_rate - 1; if ( msec ) assert( length_ == msec ); // ensure length is same as that passed in - - samples_per_sec = new_rate; - if ( clocks_per_sec ) - clock_rate( clocks_per_sec ); // recalculate factor - - bass_freq( bass_freq_ ); // recalculate shift - + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + clear(); - - return blargg_success; -} -blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const -{ - blip_resampled_time_t factor = (unsigned long) floor( - (double) samples_per_sec / clock_rate * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); - require( factor > 0 ); // clock_rate/sample_rate ratio is too large - return factor; + return 0; // success } -Blip_Buffer::~Blip_Buffer() +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const { - delete [] buffer_; + double ratio = (double) sample_rate_ / rate; + blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; } void Blip_Buffer::bass_freq( int freq ) { bass_freq_ = freq; - if ( freq == 0 ) + int shift = 31; + if ( freq > 0 ) { - bass_shift = 31; // 32 or greater invokes undefined behavior elsewhere - return; + shift = 13; + long f = (freq << 16) / sample_rate_; + while ( (f >>= 1) && --shift ) { } } - bass_shift = 1 + (int) floor( 1.442695041 * log( 0.124 * samples_per_sec / freq ) ); - if ( bass_shift < 0 ) - bass_shift = 0; - if ( bass_shift > 24 ) - bass_shift = 24; + bass_shift_ = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; } long Blip_Buffer::count_samples( blip_time_t t ) const { - return (resampled_time( t ) >> BLIP_BUFFER_ACCURACY) - (offset_ >> BLIP_BUFFER_ACCURACY); + unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long) (last_sample - first_sample); } blip_time_t Blip_Buffer::count_clocks( long count ) const { - if ( count > (long) buffer_size_ ) + if ( !factor_ ) + { + assert( 0 ); // sample rate and clock rates must be set first + return 0; + } + + if ( count > buffer_size_ ) count = buffer_size_; - - return ((count << BLIP_BUFFER_ACCURACY) - offset_ + (factor_ - 1)) / factor_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( long count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + long remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; } -void Blip_Impulse_::init( blip_pair_t_* imps, int w, int r, int fb ) +void Blip_Synth_Fast_::volume_unit( double new_unit ) { - fine_bits = fb; - width = w; - impulses = (imp_t*) imps; - generate = true; - volume_unit_ = -1.0; - res = r; - buf = NULL; - - impulse = &impulses [width * res * 2 * (fine_bits ? 2 : 1)]; - offset = 0; + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); } -const int impulse_bits = 15; -const long impulse_amp = 1L << impulse_bits; -const long impulse_offset = impulse_amp / 2; +#if !BLIP_BUFFER_FAST -void Blip_Impulse_::scale_impulse( int unit, imp_t* imp_in ) const +Blip_Synth_::Blip_Synth_( short* p, int w ) : + impulses( p ), + width( w ) { - long offset = ((long) unit << impulse_bits) - impulse_offset * unit + - (1 << (impulse_bits - 1)); - imp_t* imp = imp_in; - imp_t* fimp = impulse; - for ( int n = res / 2 + 1; n--; ) + volume_unit_ = 0.0; + kernel_unit = 0; + buf = 0; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) +{ + if ( cutoff >= 0.999 ) + cutoff = 0.999; + + if ( treble < -300.0 ) + treble = -300.0; + if ( treble > 5.0 ) + treble = 5.0; + + double const maxh = 4096.0; + double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); + double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); + double const to_angle = PI / 2 / maxh / oversample; + for ( int i = 0; i < count; i++ ) { - int error = unit; - for ( int nn = width; nn--; ) + double angle = ((i - count) * 2 + 1) * to_angle; + double angle_maxh = angle * maxh; + double angle_maxh_mid = angle_maxh * cutoff; + + double y = maxh; + + // 0 to Fs/2*cutoff, flat + if ( angle_maxh_mid ) // unstable at t=0 + y *= sin( angle_maxh_mid ) / angle_maxh_mid; + + // Fs/2*cutoff to Fs/2, logarithmic rolloff + double cosa = cos( angle ); + double den = 1 + rolloff * (rolloff - cosa - cosa); + + // Becomes unstable when rolloff is near 1.0 and t is near 0, + // which is the only time den becomes small + if ( den > 1e-13 ) { - long a = ((long) *fimp++ * unit + offset) >> impulse_bits; - error -= a - unit; - *imp++ = (imp_t) a; + double num = + (cos( angle_maxh - angle ) * rolloff - cos( angle_maxh )) * pow_a_n - + cos( angle_maxh_mid - angle ) * rolloff + cos( angle_maxh_mid ); + + y = y * cutoff + num / den; } - - // add error to middle - imp [-width / 2 - 1] += (imp_t) error; - } - - if ( res > 2 ) - { - // second half is mirror-image - const imp_t* rev = imp - width - 1; - for ( int nn = (res / 2 - 1) * width - 1; nn--; ) - *imp++ = *--rev; - *imp++ = (imp_t) unit; + + out [i] = (float) y; } - - // copy to odd offset - *imp++ = (imp_t) unit; - memcpy( imp, imp_in, (res * width - 1) * sizeof *imp ); - - /* - for ( int i = 0; i < res; i++ ) - { - for ( int j = 0; j < width; j++ ) - printf( "%6d,", imp_in [i * width + j] - 0x8000 ); - printf( "\n" ); - }*/ } -const int max_res = 1 << blip_res_bits_; +void blip_eq_t::generate( float* out, int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double oversample = blip_res * 2.25 / count + 0.85; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + oversample = half_rate / cutoff_freq; + double cutoff = rolloff_freq * oversample / half_rate; + + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); + + // apply (half of) hamming window + double to_fraction = PI / (count - 1); + for ( int i = count; i--; ) + out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); +} -void Blip_Impulse_::fine_volume_unit() +void Blip_Synth_::adjust_impulse() { - // to do: find way of merging in-place without temporary buffer - - imp_t temp [max_res * 2 * Blip_Buffer::widest_impulse_]; - scale_impulse( (offset & 0xffff) << fine_bits, temp ); - imp_t* imp2 = impulses + res * 2 * width; - scale_impulse( offset & 0xffff, imp2 ); - - // merge impulses - imp_t* imp = impulses; - imp_t* src2 = temp; - for ( int n = res / 2 * 2 * width; n--; ) + // sum pairs for each phase and add error correction to end of first half + int const size = impulses_size(); + for ( int p = blip_res; p-- >= blip_res / 2; ) { - *imp++ = *imp2++; - *imp++ = *imp2++; - *imp++ = *src2++; - *imp++ = *src2++; + int p2 = blip_res - 2 - p; + long error = kernel_unit; + for ( int i = 1; i < size; i += blip_res ) + { + error -= impulses [i + p ]; + error -= impulses [i + p2]; + } + if ( p == p2 ) + error /= 2; // phase = 0.5 impulse uses same half for both sides + impulses [size - blip_res + p] += (short) error; + //printf( "error: %ld\n", error ); } + + //for ( int i = blip_res; i--; printf( "\n" ) ) + // for ( int j = 0; j < width / 2; j++ ) + // printf( "%5ld,", impulses [j * blip_res + i + 1] ); } -void Blip_Impulse_::volume_unit( double new_unit ) +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) { - if ( new_unit == volume_unit_ ) - return; - - if ( generate ) - treble_eq( blip_eq_t( -8.87, 8800, 44100 ) ); - - volume_unit_ = new_unit; - - offset = 0x10001 * (unsigned long) floor( volume_unit_ * 0x10000 + 0.5 ); - - if ( fine_bits ) - fine_volume_unit(); - else - scale_impulse( offset & 0xffff, impulses ); -} + float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; -static const double pi = 3.1415926535897932384626433832795029L; + int const half_size = blip_res / 2 * (width - 1); + eq.generate( &fimpulse [blip_res], half_size ); -void Blip_Impulse_::treble_eq( const blip_eq_t& new_eq ) -{ - if ( !generate && new_eq.treble == eq.treble && new_eq.cutoff == eq.cutoff && - new_eq.sample_rate == eq.sample_rate ) - return; // already calculated with same parameters - - generate = false; - eq = new_eq; - - double treble = pow( 10.0, 1.0 / 20 * eq.treble ); // dB (-6dB = 0.50) - if ( treble < 0.000005 ) - treble = 0.000005; - - const double treble_freq = 22050.0; // treble level at 22 kHz harmonic - const double sample_rate = eq.sample_rate; - const double pt = treble_freq * 2 / sample_rate; - double cutoff = eq.cutoff * 2 / sample_rate; - if ( cutoff >= pt * 0.95 || cutoff >= 0.95 ) + int i; + + // need mirror slightly past center for calculation + for ( i = blip_res; i--; ) + fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; + + // starts at 0 + for ( i = 0; i < blip_res; i++ ) + fimpulse [i] = 0.0f; + + // find rescale factor + double total = 0.0; + for ( i = 0; i < half_size; i++ ) + total += fimpulse [blip_res + i]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / 2 / total; + kernel_unit = (long) base_unit; + + // integrate, first difference, rescale, convert to int + double sum = 0.0; + double next = 0.0; + int const impulses_size = this->impulses_size(); + for ( i = 0; i < impulses_size; i++ ) { - cutoff = 0.5; - treble = 1.0; + impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); + sum += fimpulse [i]; + next += fimpulse [i + blip_res]; } - - // DSF Synthesis (See T. Stilson & J. Smith (1996), - // Alias-free digital synthesis of classic analog waveforms) - - // reduce adjacent impulse interference by using small part of wide impulse - const double n_harm = 4096; - const double rolloff = pow( treble, 1.0 / (n_harm * pt - n_harm * cutoff) ); - const double rescale = 1.0 / pow( rolloff, n_harm * cutoff ); - - const double pow_a_n = rescale * pow( rolloff, n_harm ); - const double pow_a_nc = rescale * pow( rolloff, n_harm * cutoff ); - - double total = 0.0; - const double to_angle = pi / 2 / n_harm / max_res; - - float buf [max_res * (Blip_Buffer::widest_impulse_ - 2) / 2]; - const int size = max_res * (width - 2) / 2; - for ( int i = size; i--; ) + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) { - double angle = (i * 2 + 1) * to_angle; - - // equivalent - //double y = dsf( angle, n_harm * cutoff, 1.0 ); - //y -= rescale * dsf( angle, n_harm * cutoff, rolloff ); - //y += rescale * dsf( angle, n_harm, rolloff ); - - const double cos_angle = cos( angle ); - const double cos_nc_angle = cos( n_harm * cutoff * angle ); - const double cos_nc1_angle = cos( (n_harm * cutoff - 1.0) * angle ); - - double b = 2.0 - 2.0 * cos_angle; - double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; - - double d = 1.0 + rolloff * (rolloff - 2.0 * cos_angle); - double c = pow_a_n * rolloff * cos( (n_harm - 1.0) * angle ) - - pow_a_n * cos( n_harm * angle ) - - pow_a_nc * rolloff * cos_nc1_angle + - pow_a_nc * cos_nc_angle; - - // optimization of a / b + c / d - double y = (a * d + c * b) / (b * d); - - // fixed window which affects wider impulses more - if ( width > 12 ) - { - double window = cos( n_harm / 1.25 / Blip_Buffer::widest_impulse_ * angle ); - y *= window * window; - } - - total += (float) y; - buf [i] = (float) y; + volume_unit_ = 0.0; + volume_unit( vol ); } - - // integrate runs of length 'max_res' - double factor = impulse_amp * 0.5 / total; // 0.5 accounts for other mirrored half - imp_t* imp = impulse; - const int step = max_res / res; - int offset = res > 1 ? max_res : max_res / 2; - for ( int n = res / 2 + 1; n--; offset -= step ) +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( new_unit != volume_unit_ ) { - for ( int w = -width / 2; w < width / 2; w++ ) + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + volume_unit_ = new_unit; + double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) { - double sum = 0; - for ( int i = max_res; i--; ) + int shift = 0; + + // if unit is really small, might need to attenuate kernel + while ( factor < 2.0 ) { - int index = w * max_res + offset + i; - if ( index < 0 ) - index = -index - 1; - if ( index < size ) - sum += buf [index]; + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + // keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values + long offset = 0x8000 + (1 << (shift - 1)); + long offset2 = 0x8000 >> shift; + for ( int i = impulses_size(); i--; ) + impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); + adjust_impulse(); } - *imp++ = (imp_t) floor( sum * factor + (impulse_offset + 0.5) ); } - } - - // rescale - double unit = volume_unit_; - if ( unit >= 0 ) - { - volume_unit_ = -1; - volume_unit( unit ); + delta_factor = (int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); } } +#endif -void Blip_Buffer::remove_samples( long count ) +long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo ) { - require( buffer_ ); // sample rate must have been set - - if ( !count ) // optimization - return; - - remove_silence( count ); - - // Allows synthesis slightly past time passed to end_frame(), as long as it's - // not more than an output sample. - // to do: kind of hacky, could add run_until() which keeps track of extra synthesis - int const copy_extra = 1; - - // copy remaining samples to beginning and clear old samples - long remain = samples_avail() + widest_impulse_ + copy_extra; - if ( count >= remain ) - memmove( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); - else - memcpy( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); - memset( buffer_ + remain, sample_offset_ & 0xFF, count * sizeof (buf_t_) ); -} - -#include BLARGG_ENABLE_OPTIMIZER - -long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, bool stereo ) -{ - require( buffer_ ); // sample rate must have been set - long count = samples_avail(); if ( count > max_samples ) count = max_samples; - - if ( !count ) - return 0; // optimization - - int sample_offset_ = this->sample_offset_; - int bass_shift = this->bass_shift; - buf_t_* buf = buffer_; - long accum = reader_accum; - - if ( !stereo ) + + if ( count ) { - for ( long n = count; n--; ) + int const bass = BLIP_READER_BASS( *this ); + BLIP_READER_BEGIN( reader, *this ); + + if ( !stereo ) { - long s = accum >> accum_fract; - accum -= accum >> bass_shift; - accum += (long (*buf++) - sample_offset_) << accum_fract; - *out++ = (blip_sample_t) s; - - // clamp sample - if ( (BOOST::int16_t) s != s ) - out [-1] = blip_sample_t (0x7FFF - (s >> 24)); + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out++ = (blip_sample_t) s; + BLIP_READER_NEXT( reader, bass ); + } } - } - else - { - for ( long n = count; n--; ) + else { - long s = accum >> accum_fract; - accum -= accum >> bass_shift; - accum += (long (*buf++) - sample_offset_) << accum_fract; - *out = (blip_sample_t) s; - out += 2; - - // clamp sample - if ( (BOOST::int16_t) s != s ) - out [-2] = blip_sample_t (0x7FFF - (s >> 24)); + for ( blip_long n = count; n; --n ) + { + blip_long s = BLIP_READER_READ( reader ); + if ( (blip_sample_t) s != s ) + s = 0x7FFF - (s >> 24); + *out = (blip_sample_t) s; + out += 2; + BLIP_READER_NEXT( reader, bass ); + } } + BLIP_READER_END( reader, *this ); + + remove_samples( count ); } - - reader_accum = accum; - - remove_samples( count ); - return count; } -void Blip_Buffer::mix_samples( const blip_sample_t* in, long count ) +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) { - buf_t_* buf = &buffer_ [(offset_ >> BLIP_BUFFER_ACCURACY) + (widest_impulse_ / 2 - 1)]; - + if ( buffer_size_ == silent_buf_size ) + { + assert( 0 ); + return; + } + + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; + + int const sample_shift = blip_sample_bits - 16; int prev = 0; while ( count-- ) { - int s = *in++; - *buf += s - prev; + blip_long s = (blip_long) *in++ << sample_shift; + *out += s - prev; prev = s; - ++buf; + ++out; } - *buf -= *--in; + *out -= prev; } diff --git a/plugins/papu/gb_apu/Blip_Buffer.h b/plugins/papu/gb_apu/Blip_Buffer.h index ef5209b4b09..4cc526d2f1c 100644 --- a/plugins/papu/gb_apu/Blip_Buffer.h +++ b/plugins/papu/gb_apu/Blip_Buffer.h @@ -1,259 +1,488 @@ +// Band-limited sound synthesis buffer -// Buffer of sound samples into which band-limited waveforms can be synthesized -// using Blip_Wave or Blip_Synth. - -// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - +// Blip_Buffer 0.4.1 #ifndef BLIP_BUFFER_H #define BLIP_BUFFER_H -#include "blargg_common.h" - -class Blip_Reader; - -// Source time unit. -typedef long blip_time_t; - -// Type of sample produced. Signed 16-bit format. -typedef BOOST::int16_t blip_sample_t; + // internal + #include + #if INT_MAX < 0x7FFFFFFF + #error "int must be at least 32 bits" + #endif + + typedef int blip_long; + typedef unsigned blip_ulong; -// Make buffer as large as possible (currently about 65000 samples) -const int blip_default_length = 0; +// Time unit at source clock rate +typedef blip_long blip_time_t; -typedef unsigned long blip_resampled_time_t; // not documented +// Output samples are 16-bit signed, with a range of -32768 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; class Blip_Buffer { public: - // Construct an empty buffer. - Blip_Buffer(); - ~Blip_Buffer(); + typedef const char* blargg_err_t; - // Set output sample rate and buffer length in milliseconds (1/1000 sec), - // then clear buffer. If length is not specified, make as large as possible. - // If there is insufficient memory for the buffer, sets the buffer length - // to 0 and returns error string (or propagates exception if compiler supports it). - blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = blip_default_length ); + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); - // Length of buffer, in milliseconds - int length() const; + // Set number of source time units per second + void clock_rate( long ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional optional features + // Current output sample rate long sample_rate() const; + // Length of buffer, in milliseconds + int length() const; + // Number of source time units per second - void clock_rate( long ); long clock_rate() const; - // Set frequency at which high-pass filter attenuation passes -3dB + // Set frequency high-pass filter frequency, where higher values reduce bass more void bass_freq( int frequency ); - // Remove all available samples and clear buffer to silence. If 'entire_buffer' is - // false, just clear out any samples waiting rather than the entire buffer. - void clear( bool entire_buffer = true ); + // Number of samples delay from synthesis to samples read out + int output_latency() const; - // End current time frame of specified duration and make its samples available - // (along with any still-unread samples) for reading with read_samples(). Begin - // a new time frame at the end of the current frame. All transitions must have - // been added before 'time'. - void end_frame( blip_time_t time ); + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); // Number of samples available for reading with read_samples() long samples_avail() const; - // Read at most 'max_samples' out of buffer into 'dest', removing them from from - // the buffer. Return number of samples actually read and removed. If stereo is - // true, increment 'dest' one extra time after writing each sample, to allow - // easy interleving of two channels into a stereo output buffer. - long read_samples( blip_sample_t* dest, long max_samples, bool stereo = false ); - // Remove 'count' samples from those waiting to be read void remove_samples( long count ); - // Number of samples delay from synthesis to samples read out - int output_latency() const; - -// Beta features - - // Number of raw samples that can be mixed within frame of specified duration - long count_samples( blip_time_t duration ) const; - - // Mix 'count' samples from 'buf' into buffer. - void mix_samples( const blip_sample_t* buf, long count ); +// Experimental features // Count number of clocks needed until 'count' samples will be available. // If buffer can't even hold 'count' samples, returns number of clocks until - // buffer is full. + // buffer becomes full. blip_time_t count_clocks( long count ) const; + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; - // not documented yet + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* buf, long count ); + // not documented yet + void set_modified() { modified_ = 1; } + int clear_modified() { int b = modified_; modified_ = 0; return b; } + typedef blip_ulong blip_resampled_time_t; void remove_silence( long count ); - - blip_resampled_time_t resampled_time( blip_time_t t ) const - { - return t * blip_resampled_time_t (factor_) + offset_; - } - + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } blip_resampled_time_t clock_rate_factor( long clock_rate ) const; +public: + Blip_Buffer(); + ~Blip_Buffer(); - blip_resampled_time_t resampled_duration( int t ) const - { - return t * blip_resampled_time_t (factor_); - } - + // Deprecated + typedef blip_resampled_time_t resampled_time_t; + blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } + blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } private: // noncopyable Blip_Buffer( const Blip_Buffer& ); Blip_Buffer& operator = ( const Blip_Buffer& ); +public: + typedef blip_time_t buf_t_; + blip_ulong factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + blip_long buffer_size_; + blip_long reader_accum_; + int bass_shift_; +private: + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + int modified_; + friend class Blip_Reader; +}; + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif - // Don't use the following members. They are public only for technical reasons. +// Number of bits in resample ratio fraction. Higher values give a more accurate ratio +// but reduce maximum buffer size. +#ifndef BLIP_BUFFER_ACCURACY + #define BLIP_BUFFER_ACCURACY 16 +#endif + +// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in +// noticeable broadband noise when synthesizing high frequency square waves. +// Affects size of Blip_Synth objects since they store the waveform directly. +#ifndef BLIP_PHASE_BITS + #if BLIP_BUFFER_FAST + #define BLIP_PHASE_BITS 8 + #else + #define BLIP_PHASE_BITS 6 + #endif +#endif + + // Internal + typedef blip_ulong blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_buffer_extra_ = blip_widest_impulse_ + 2; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class Blip_Synth_Fast_ { public: - enum { sample_offset_ = 0x7F7F }; // repeated byte allows memset to clear buffer - enum { widest_impulse_ = 24 }; - typedef BOOST::uint16_t buf_t_; - - unsigned long factor_; - blip_resampled_time_t offset_; - buf_t_* buffer_; - unsigned buffer_size_; - private: - long reader_accum; - int bass_shift; - long samples_per_sec; - long clocks_per_sec; - int bass_freq_; - int length_; + Blip_Buffer* buf; + int last_amp; + int delta_factor; - enum { accum_fract = 15 }; // less than 16 to give extra sample range + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + + class Blip_Synth_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; - friend class Blip_Reader; + void volume_unit( double ); + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + private: + double volume_unit_; + short* const impulses; + int const width; + blip_long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see blip_buffer.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more info. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); + } + void offset_inline( blip_time_t t, int delta ) const { + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); + } + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else + Blip_Synth_ impl; + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; +public: + Blip_Synth() : impl( impulses, quality ) { } +#endif }; -// Low-pass equalization parameters (see notes.txt) +// Low-pass equalization parameters class blip_eq_t { public: - blip_eq_t( double treble = 0 ); - blip_eq_t( double treble, long cutoff, long sample_rate ); + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); + private: double treble; - long cutoff; + long rolloff_freq; long sample_rate; - friend class Blip_Impulse_; + long cutoff_freq; + void generate( float* out, int count ) const; + friend class Blip_Synth_; }; -// not documented yet (see Multi_Buffer.cpp for an example of use) -class Blip_Reader { - const Blip_Buffer::buf_t_* buf; - long accum; - #ifdef __MWERKS__ - void operator = ( struct foobar ); // helps optimizer - #endif +int const blip_sample_bits = 30; + +// Dummy Blip_Buffer to direct sound output to, for easy muting without +// having to stop sound code. +class Silent_Blip_Buffer : public Blip_Buffer { + buf_t_ buf [blip_buffer_extra_ + 1]; public: - // avoid anything which might cause optimizer to put object in memory - - int begin( Blip_Buffer& blip_buf ) { - buf = blip_buf.buffer_; - accum = blip_buf.reader_accum; - return blip_buf.bass_shift; - } - - int read() const { - return accum >> Blip_Buffer::accum_fract; - } - - void next( int bass_shift = 9 ) { - accum -= accum >> bass_shift; - accum += ((long) *buf++ - Blip_Buffer::sample_offset_) << Blip_Buffer::accum_fract; - } + // The following cannot be used (an assertion will fail if attempted): + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); + blip_time_t count_clocks( long count ) const; + void mix_samples( blip_sample_t const* buf, long count ); - void end( Blip_Buffer& blip_buf ) { - blip_buf.reader_accum = accum; - } + Silent_Blip_Buffer(); }; + #if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLIP_RESTRICT __restrict + #else + #define BLIP_RESTRICT + #endif +// Optimized reading from Blip_Buffer, for use in custom sample output -// End of public interface - -#ifndef BLIP_BUFFER_ACCURACY - #define BLIP_BUFFER_ACCURACY 16 -#endif +// Begin reading from buffer. Name should be unique to the current block. +#define BLIP_READER_BEGIN( name, blip_buffer ) \ + const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ + blip_long name##_reader_accum = (blip_buffer).reader_accum_ -const int blip_res_bits_ = 5; +// Get value to pass to BLIP_READER_NEXT() +#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) -typedef BOOST::uint32_t blip_pair_t_; +// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal +// code at the cost of having no bass control +int const blip_reader_default_bass = 9; -class Blip_Impulse_ { - typedef BOOST::uint16_t imp_t; - - blip_eq_t eq; - double volume_unit_; - imp_t* impulses; - imp_t* impulse; - int width; - int fine_bits; - int res; - bool generate; - - void fine_volume_unit(); - void scale_impulse( int unit, imp_t* ) const; +// Current sample +#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) + +// Current raw sample in full internal resolution +#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) + +// Advance to next sample +#define BLIP_READER_NEXT( name, bass ) \ + (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) + +// End reading samples from buffer. The number of samples read must now be removed +// using Blip_Buffer::remove_samples(). +#define BLIP_READER_END( name, blip_buffer ) \ + (void) ((blip_buffer).reader_accum_ = name##_reader_accum) + + +// Compatibility with older version +const long blip_unscaled = 65535; +const int blip_low_quality = blip_med_quality; +const int blip_best_quality = blip_high_quality; + +// Deprecated; use BLIP_READER macros as follows: +// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); +// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); +// r.read() -> BLIP_READER_READ( r ) +// r.read_raw() -> BLIP_READER_READ_RAW( r ) +// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) +// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) +// r.end( buf ) -> BLIP_READER_END( r, buf ) +class Blip_Reader { public: - Blip_Buffer* buf; - BOOST::uint32_t offset; + int begin( Blip_Buffer& ); + blip_long read() const { return accum >> (blip_sample_bits - 16); } + blip_long read_raw() const { return accum; } + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } - void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 ); - void volume_unit( double ); - void treble_eq( const blip_eq_t& ); +private: + const Blip_Buffer::buf_t_* buf; + blip_long accum; }; -inline blip_eq_t::blip_eq_t( double t ) : - treble( t ), cutoff( 0 ), sample_rate( 44100 ) { -} - -inline blip_eq_t::blip_eq_t( double t, long c, long sr ) : - treble( t ), cutoff( c ), sample_rate( sr ) { -} +// End of public interface -inline int Blip_Buffer::length() const { - return length_; -} +#include -inline long Blip_Buffer::samples_avail() const { - return long (offset_ >> BLIP_BUFFER_ACCURACY); -} +template +inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + +#if BLIP_BUFFER_FAST + blip_long left = buf [0] + delta; + + // Kind of crappy, but doing shift after multiply results in overflow. + // Alternate way of delaying multiply by delta_factor results in worse + // sub-sample resolution. + blip_long right = (delta >> BLIP_PHASE_BITS) * phase; + left -= right; + right += buf [1]; + + buf [0] = left; + buf [1] = right; +#else -inline long Blip_Buffer::sample_rate() const { - return samples_per_sec; -} + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + int const mid = quality / 2 - 1; + + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; + + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + + // straight forward implementation resulted in better code on GCC for x86 + + #define ADD_IMP( out, in ) \ + buf [out] += (blip_long) imp [blip_res * (in)] * delta + + #define BLIP_FWD( i ) {\ + ADD_IMP( fwd + i, i );\ + ADD_IMP( fwd + 1 + i, i + 1 );\ + } + #define BLIP_REV( r ) {\ + ADD_IMP( rev - r, r + 1 );\ + ADD_IMP( rev + 1 - r, r );\ + } -inline void Blip_Buffer::end_frame( blip_time_t t ) { - offset_ += t * factor_; -/* assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer", - samples_avail() <= (long) buffer_size_ ));*/ + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + ADD_IMP( fwd + mid - 1, mid - 1 ); + ADD_IMP( fwd + mid , mid ); + imp = impulses + phase; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + ADD_IMP( rev , 1 ); + ADD_IMP( rev + 1, 0 ); + + #else + + // for RISC processors, help compiler by reading ahead of writes + + #define BLIP_FWD( i ) {\ + blip_long t0 = i0 * delta + buf [fwd + i];\ + blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ + i0 = imp [blip_res * (i + 2)];\ + buf [fwd + i] = t0;\ + buf [fwd + 1 + i] = t1;\ + } + #define BLIP_REV( r ) {\ + blip_long t0 = i0 * delta + buf [rev - r];\ + blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ + i0 = imp [blip_res * (r - 1)];\ + buf [rev - r] = t0;\ + buf [rev + 1 - r] = t1;\ + } + + blip_long i0 = *imp; + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + blip_long t0 = i0 * delta + buf [fwd + mid - 1]; + blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; + imp = impulses + phase; + i0 = imp [blip_res * mid]; + buf [fwd + mid - 1] = t0; + buf [fwd + mid ] = t1; + } + if ( quality > 12 ) BLIP_REV( 6 ) + if ( quality > 8 ) BLIP_REV( 4 ) + BLIP_REV( 2 ) + + blip_long t0 = i0 * delta + buf [rev ]; + blip_long t1 = *imp * delta + buf [rev + 1]; + buf [rev ] = t0; + buf [rev + 1] = t1; + #endif + +#endif } -inline void Blip_Buffer::remove_silence( long count ) { -/* assert(( "Blip_Buffer::remove_silence(): Tried to remove more samples than available", - count <= samples_avail() ));*/ - offset_ -= blip_resampled_time_t (count) << BLIP_BUFFER_ACCURACY; -} +#undef BLIP_FWD +#undef BLIP_REV -inline int Blip_Buffer::output_latency() const { - return widest_impulse_ / 2; +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); } -inline long Blip_Buffer::clock_rate() const { - return clocks_per_sec; +template +#if BLIP_BUFFER_FAST + inline +#endif +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); } -inline void Blip_Buffer::clock_rate( long cps ) +inline blip_eq_t::blip_eq_t( double t ) : + treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } +inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : + treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } + +inline int Blip_Buffer::length() const { return length_; } +inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +inline long Blip_Buffer::sample_rate() const { return sample_rate_; } +inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } +inline long Blip_Buffer::clock_rate() const { return clock_rate_; } +inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } + +inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) { - clocks_per_sec = cps; - factor_ = clock_rate_factor( cps ); + buf = blip_buf.buffer_; + accum = blip_buf.reader_accum_; + return blip_buf.bass_shift_; } -#include "Blip_Synth.h" +int const blip_max_length = 0; +int const blip_default_length = 250; #endif - diff --git a/plugins/papu/gb_apu/Blip_Synth.h b/plugins/papu/gb_apu/Blip_Synth.h deleted file mode 100644 index 170e2245904..00000000000 --- a/plugins/papu/gb_apu/Blip_Synth.h +++ /dev/null @@ -1,208 +0,0 @@ - -// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding -// waveforms to a Blip_Buffer. - -// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef BLIP_SYNTH_H -#define BLIP_SYNTH_H - -#ifndef BLIP_BUFFER_H - #include "Blip_Buffer.h" -#endif - -// Quality level. Higher levels are slower, and worse in a few cases. -// Use blip_good_quality as a starting point. -const int blip_low_quality = 1; -const int blip_med_quality = 2; -const int blip_good_quality = 3; -const int blip_high_quality = 4; - -// Blip_Synth is a transition waveform synthesizer which adds band-limited -// offsets (transitions) into a Blip_Buffer. For a simpler interface, use -// Blip_Wave (below). -// -// Range specifies the greatest expected offset that will occur. For a -// waveform that goes between +amp and -amp, range should be amp * 2 (half -// that if it only goes between +amp and 0). When range is large, a higher -// accuracy scheme is used; to force this even when range is small, pass -// the negative of range (i.e. -range). -template -class Blip_Synth { - BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 ); - BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 ); - enum { - abs_range = (range < 0) ? -range : range, - fine_mode = (range > 512 || range < 0), - width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_), - res = 1 << blip_res_bits_, - impulse_size = width / 2 * (fine_mode + 1), - base_impulses_size = width / 2 * (res / 2 + 1), - fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 : - abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 : - abs_range <= 2048 ? 7 : 8) : 0) - }; - blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size]; - Blip_Impulse_ impulse; - void init() { impulse.init( impulses, width, res, fine_bits ); } -public: - Blip_Synth() { init(); } - Blip_Synth( double volume ) { init(); this->volume( volume ); } - - // Configure low-pass filter (see notes.txt). Not optimized for real-time control - void treble_eq( const blip_eq_t& eq ) { impulse.treble_eq( eq ); } - - // Set volume of a transition at amplitude 'range' by setting volume_unit - // to v / range - void volume( double v ) { impulse.volume_unit( v * (1.0 / abs_range) ); } - - // Set base volume unit of transitions, where 1.0 is a full swing between the - // positive and negative extremes. Not optimized for real-time control. - void volume_unit( double unit ) { impulse.volume_unit( unit ); } - - // Default Blip_Buffer used for output when none is specified for a given call - Blip_Buffer* output() const { return impulse.buf; } - void output( Blip_Buffer* b ) { impulse.buf = b; } - - // Add an amplitude offset (transition) with a magnitude of delta * volume_unit - // into the specified buffer (default buffer if none specified) at the - // specified source time. Delta can be positive or negative. To increase - // performance by inlining code at the call site, use offset_inline(). - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - void offset_resampled( blip_resampled_time_t t, int o ) const { - offset_resampled( t, o, impulse.buf ); - } - void offset( blip_time_t t, int delta ) const { - offset( t, delta, impulse.buf ); - } - void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t time, int delta ) const { - offset_inline( time, delta, impulse.buf ); - } -}; - -// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer. -// A wave is built from a series of delays and new amplitudes. This provides a -// simpler interface than Blip_Synth, nothing more. -template -class Blip_Wave { - Blip_Synth synth; - blip_time_t time_; - int last_amp; - void init() { time_ = 0; last_amp = 0; } -public: - // Start wave at time 0 and amplitude 0 - Blip_Wave() { init(); } - Blip_Wave( double volume ) { init(); this->volume( volume ); } - - // See Blip_Synth for description - void volume( double v ) { synth.volume( v ); } - void volume_unit( double v ) { synth.volume_unit( v ); } - void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); } - Blip_Buffer* output() const { return synth.output(); } - void output( Blip_Buffer* b ) { synth.output( b ); if ( !b ) time_ = last_amp = 0; } - - // Current time in frame - blip_time_t time() const { return time_; } - void time( blip_time_t t ) { time_ = t; } - - // Current amplitude of wave - int amplitude() const { return last_amp; } - void amplitude( int ); - - // Move forward by 't' time units - void delay( blip_time_t t ) { time_ += t; } - - // End time frame of specified duration. Localize time to new frame. - // If wave hadn't been run to end of frame, start it at beginning of new frame. - void end_frame( blip_time_t duration ) - { - time_ -= duration; - if ( time_ < 0 ) - time_ = 0; - } -}; - -// End of public interface - -template -void Blip_Wave::amplitude( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - synth.offset_inline( time_, delta ); -} - -template -inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - typedef blip_pair_t_ pair_t; - - unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1; -/* assert(( "Blip_Synth/Blip_wave: Went past end of buffer", - sample_index < blip_buf->buffer_size_ ));*/ - enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 }; - pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index]; - - enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ }; - enum { mask = res * 2 - 1 }; - const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size]; - - pair_t offset = impulse.offset * delta; - - if ( !fine_bits ) - { - // normal mode - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta; - t1 += imp [1] * delta; - imp += 2; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } - else - { - // fine mode - enum { sub_range = 1 << fine_bits }; - delta += sub_range / 2; - int delta2 = (delta & (sub_range - 1)) - sub_range / 2; - delta >>= fine_bits; - - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta2; - t0 += imp [1] * delta; - - t1 += imp [2] * delta2; - t1 += imp [3] * delta; - - imp += 4; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } -} - -template -void Blip_Synth::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); -} - -#endif - diff --git a/plugins/papu/gb_apu/Gb_Apu.cpp b/plugins/papu/gb_apu/Gb_Apu.cpp index 6039b043b02..866594ddf62 100644 --- a/plugins/papu/gb_apu/Gb_Apu.cpp +++ b/plugins/papu/gb_apu/Gb_Apu.cpp @@ -1,28 +1,29 @@ - -// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/ +// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ #include "Gb_Apu.h" #include -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +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" -#include BLARGG_SOURCE_BEGIN +unsigned const vol_reg = 0xFF24; +unsigned const status_reg = 0xFF26; Gb_Apu::Gb_Apu() { square1.synth = &square_synth; square2.synth = &square_synth; - square1.has_sweep = true; wave.synth = &other_synth; noise.synth = &other_synth; @@ -31,25 +32,37 @@ Gb_Apu::Gb_Apu() oscs [2] = &wave; oscs [3] = &noise; + for ( int i = 0; i < osc_count; i++ ) + { + Gb_Osc& osc = *oscs [i]; + osc.regs = ®s [i * 5]; + osc.output = 0; + osc.outputs [0] = 0; + osc.outputs [1] = 0; + osc.outputs [2] = 0; + osc.outputs [3] = 0; + } + + set_tempo( 1.0 ); volume( 1.0 ); reset(); } -Gb_Apu::~Gb_Apu() -{ -} - void Gb_Apu::treble_eq( const blip_eq_t& eq ) { square_synth.treble_eq( eq ); other_synth.treble_eq( eq ); } -void Gb_Apu::volume( double vol ) +void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) { - vol *= 0.60 / osc_count; - square_synth.volume( vol ); - other_synth.volume( vol ); + require( (unsigned) index < osc_count ); + require( (center && left && right) || (!center && !left && !right) ); + Gb_Osc& osc = *oscs [index]; + osc.outputs [1] = right; + osc.outputs [2] = left; + osc.outputs [3] = center; + osc.output = osc.outputs [osc.output_select]; } void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) @@ -58,44 +71,62 @@ void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right osc_output( i, center, left, right ); } +void Gb_Apu::update_volume() +{ + // TODO: doesn't handle differing left/right global volume (support would + // require modification to all oscillator code) + int data = regs [vol_reg - start_addr]; + double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit; + square_synth.volume( vol ); + other_synth.volume( vol ); +} + +static unsigned char const powerup_regs [0x20] = { + 0x80,0x3F,0x00,0xFF,0xBF, // square 1 + 0xFF,0x3F,0x00,0xFF,0xBF, // square 2 + 0x7F,0xFF,0x9F,0xFF,0xBF, // wave + 0xFF,0xFF,0x00,0x00,0xBF, // noise + 0x00, // left/right enables + 0x77, // master volume + 0x80, // power + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 256; // 256 Hz + if ( t != 1.0 ) + frame_period = blip_time_t (frame_period / t); +} + void Gb_Apu::reset() { next_frame_time = 0; - last_time = 0; - frame_count = 0; - stereo_found = false; + last_time = 0; + frame_count = 0; square1.reset(); square2.reset(); wave.reset(); noise.reset(); + noise.bits = 1; + wave.wave_pos = 0; - memset( regs, 0, sizeof regs ); -} - -void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); + // avoid click at beginning + regs [vol_reg - start_addr] = 0x77; + update_volume(); - Gb_Osc& osc = *oscs [index]; - if ( center && !left && !right ) - { - // mono - left = center; - right = center; - } - else - { - // must be silenced or stereo - require( (!left && !right) || (left && right) ); - } - osc.outputs [1] = right; - osc.outputs [2] = left; - osc.outputs [3] = center; - osc.output = osc.outputs [osc.output_select]; + regs [status_reg - start_addr] = 0x01; // force power + write_register( 0, status_reg, 0x00 ); + + static unsigned char const initial_wave [] = { + 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table + 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA + }; + memcpy( wave.wave, initial_wave, sizeof wave.wave ); } -void Gb_Apu::run_until( gb_time_t end_time ) +void Gb_Apu::run_until( blip_time_t end_time ) { require( end_time >= last_time ); // end_time must not be before previous time if ( end_time == last_time ) @@ -103,17 +134,28 @@ void Gb_Apu::run_until( gb_time_t end_time ) while ( true ) { - gb_time_t time = next_frame_time; + blip_time_t time = next_frame_time; if ( time > end_time ) time = end_time; // run oscillators - for ( int i = 0; i < osc_count; ++i ) { + for ( int i = 0; i < osc_count; ++i ) + { Gb_Osc& osc = *oscs [i]; - if ( osc.output ) { - if ( osc.output != osc.outputs [3] ) - stereo_found = true; - osc.run( last_time, time ); + if ( osc.output ) + { + osc.output->set_modified(); // TODO: misses optimization opportunities? + int playing = false; + if ( osc.enabled && osc.volume && + (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) ) + playing = -1; + switch ( i ) + { + case 0: square1.run( last_time, time, playing ); break; + case 1: square2.run( last_time, time, playing ); break; + case 2: wave .run( last_time, time, playing ); break; + case 3: noise .run( last_time, time, playing ); break; + } } } last_time = time; @@ -121,7 +163,7 @@ void Gb_Apu::run_until( gb_time_t end_time ) if ( time == end_time ) break; - next_frame_time += 4194304 / 256; // 256 Hz + next_frame_time += frame_period; // 256 Hz actions square1.clock_length(); @@ -130,7 +172,8 @@ void Gb_Apu::run_until( gb_time_t end_time ) noise.clock_length(); frame_count = (frame_count + 1) & 3; - if ( frame_count == 0 ) { + if ( frame_count == 0 ) + { // 64 Hz actions square1.clock_envelope(); square2.clock_envelope(); @@ -142,7 +185,7 @@ void Gb_Apu::run_until( gb_time_t end_time ) } } -bool Gb_Apu::end_frame( gb_time_t end_time ) +void Gb_Apu::end_frame( blip_time_t end_time ) { if ( end_time > last_time ) run_until( end_time ); @@ -152,13 +195,9 @@ bool Gb_Apu::end_frame( gb_time_t end_time ) assert( last_time >= end_time ); last_time -= end_time; - - bool result = stereo_found; - stereo_found = false; - return result; } -void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) +void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) { require( (unsigned) data < 0x100 ); @@ -168,48 +207,39 @@ void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) run_until( time ); + int old_reg = regs [reg]; regs [reg] = data; - if ( addr < 0xff24 ) + if ( addr < vol_reg ) { - // oscillator - int index = reg / 5; - oscs [index]->write_register( reg - index * 5, data ); + write_osc( reg / 5, reg, data ); } - // added - else if ( addr == 0xff24 ) + else if ( addr == vol_reg && data != old_reg ) // global volume { - int global_volume = data & 7; - int old_volume = square1.global_volume; - if ( old_volume != global_volume ) + // return all oscs to 0 + for ( int i = 0; i < osc_count; i++ ) { - int any_enabled = false; - for ( int i = 0; i < osc_count; i++ ) - { - Gb_Osc& osc = *oscs [i]; - if ( osc.enabled ) - { - if ( osc.last_amp ) - { - int new_amp = osc.last_amp * global_volume / osc.global_volume; - if ( osc.output ) - square_synth.offset( time, new_amp - osc.last_amp, osc.output ); - osc.last_amp = new_amp; - } - any_enabled |= osc.volume; - } - osc.global_volume = global_volume; - } - - if ( !any_enabled && square1.outputs [3] ) - square_synth.offset( time, (global_volume - old_volume) * 15 * 2, square1.outputs [3] ); + Gb_Osc& osc = *oscs [i]; + int amp = osc.last_amp; + osc.last_amp = 0; + if ( amp && osc.enabled && osc.output ) + other_synth.offset( time, -amp, osc.output ); } + + if ( wave.outputs [3] ) + other_synth.offset( time, 30, wave.outputs [3] ); + + update_volume(); + + if ( wave.outputs [3] ) + other_synth.offset( time, -30, wave.outputs [3] ); + + // oscs will update with new amplitude when next run } - - else if ( addr == 0xff25 || addr == 0xff26 ) + else if ( addr == 0xFF25 || addr == status_reg ) { - int mask = (regs [0xff26 - start_addr] & 0x80) ? ~0 : 0; - int flags = regs [0xff25 - start_addr] & mask; + int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; + int flags = regs [0xFF25 - start_addr] & mask; // left/right assignments for ( int i = 0; i < osc_count; i++ ) @@ -220,42 +250,57 @@ void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) Blip_Buffer* old_output = osc.output; osc.output_select = (bits >> 3 & 2) | (bits & 1); osc.output = osc.outputs [osc.output_select]; - if ( osc.output != old_output && osc.last_amp ) + if ( osc.output != old_output ) { - if ( old_output ) - square_synth.offset( time, -osc.last_amp, old_output ); + int amp = osc.last_amp; osc.last_amp = 0; + if ( amp && old_output ) + other_synth.offset( time, -amp, old_output ); + } + } + + if ( addr == status_reg && data != old_reg ) + { + if ( !(data & 0x80) ) + { + for ( unsigned i = 0; i < sizeof powerup_regs; i++ ) + { + if ( i != status_reg - start_addr ) + write_register( time, i + start_addr, powerup_regs [i] ); + } + } + else + { + //debug_printf( "APU powered on\n" ); } } } - else if ( addr >= 0xff30 ) + else if ( addr >= 0xFF30 ) { - int index = (addr & 0x0f) * 2; + int index = (addr & 0x0F) * 2; wave.wave [index] = data >> 4; - wave.wave [index + 1] = data & 0x0f; + wave.wave [index + 1] = data & 0x0F; } } -int Gb_Apu::read_register( gb_time_t time, gb_addr_t addr ) +int Gb_Apu::read_register( blip_time_t time, unsigned addr ) { - // function now takes actual address, i.e. 0xFFXX - require( start_addr <= addr && addr < end_addr ); - run_until( time ); - int data = regs [addr - start_addr]; + int index = addr - start_addr; + require( (unsigned) index < register_count ); + int data = regs [index]; - if ( addr == 0xff26 ) + if ( addr == status_reg ) { - data &= 0xf0; + data = (data & 0x80) | 0x70; for ( int i = 0; i < osc_count; i++ ) { const Gb_Osc& osc = *oscs [i]; - if ( osc.enabled && (osc.length || !osc.length_enabled) ) + if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) ) data |= 1 << i; } } return data; } - diff --git a/plugins/papu/gb_apu/Gb_Apu.h b/plugins/papu/gb_apu/Gb_Apu.h index e2f940b94e0..e74ebc55b89 100644 --- a/plugins/papu/gb_apu/Gb_Apu.h +++ b/plugins/papu/gb_apu/Gb_Apu.h @@ -1,20 +1,13 @@ - // Nintendo Game Boy PAPU sound chip emulator -// Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - +// Gb_Snd_Emu 0.1.5 #ifndef GB_APU_H #define GB_APU_H -typedef long gb_time_t; // clock cycle count -typedef unsigned gb_addr_t; // 16-bit address - #include "Gb_Oscs.h" class Gb_Apu { public: - Gb_Apu(); - ~Gb_Apu(); // Set overall volume of all oscillators, where 1.0 is full volume void volume( double ); @@ -22,63 +15,76 @@ class Gb_Apu { // Set treble equalization void treble_eq( const blip_eq_t& ); - // Reset oscillators and internal state - void reset(); + // Outputs can be assigned to a single buffer for mono output, or to three + // buffers for stereo output (using Stereo_Buffer to do the mixing). // Assign all oscillator outputs to specified buffer(s). If buffer - // is NULL, silence all oscillators. + // is NULL, silences all oscillators. void output( Blip_Buffer* mono ); void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, - // which refer to Square 1, Square 2, Wave, and Noise. - // If buffer is NULL, silence oscillator. + // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL, + // silences oscillator. enum { osc_count = 4 }; void osc_output( int index, Blip_Buffer* mono ); void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + // Reset oscillators and internal state + void reset(); + // Reads and writes at addr must satisfy start_addr <= addr <= end_addr - enum { start_addr = 0xff10 }; - enum { end_addr = 0xff3f }; + enum { start_addr = 0xFF10 }; + enum { end_addr = 0xFF3F }; enum { register_count = end_addr - start_addr + 1 }; // Write 'data' to address at specified time - void write_register( gb_time_t, gb_addr_t, int data ); + void write_register( blip_time_t, unsigned addr, int data ); // Read from address at specified time - int read_register( gb_time_t, gb_addr_t ); + int read_register( blip_time_t, unsigned addr ); // Run all oscillators up to specified time, end current time frame, then - // start a new frame at time 0. Return true if any oscillators added - // sound to one of the left/right buffers, false if they only added - // to the center buffer. - bool end_frame( gb_time_t ); + // start a new frame at time 0. + void end_frame( blip_time_t ); + void set_tempo( double ); + +public: + Gb_Apu(); private: // noncopyable Gb_Apu( const Gb_Apu& ); Gb_Apu& operator = ( const Gb_Apu& ); Gb_Osc* oscs [osc_count]; - gb_time_t next_frame_time; - gb_time_t last_time; + blip_time_t next_frame_time; + blip_time_t last_time; + blip_time_t frame_period; + double volume_unit; int frame_count; - bool stereo_found; Gb_Square square1; Gb_Square square2; Gb_Wave wave; Gb_Noise noise; BOOST::uint8_t regs [register_count]; - Gb_Square::Synth square_synth; // shared between squares - Gb_Wave::Synth other_synth; // shared between wave and noise + Gb_Square::Synth square_synth; // used by squares + Gb_Wave::Synth other_synth; // used by wave and noise - void run_until( gb_time_t ); + void update_volume(); + void run_until( blip_time_t ); + void write_osc( int index, int reg, int data ); }; -inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, NULL, NULL ); } +inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } -inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, NULL, NULL ); } +inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } -#endif +inline void Gb_Apu::volume( double vol ) +{ + volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol; + update_volume(); +} +#endif diff --git a/plugins/papu/gb_apu/Gb_Oscs.cpp b/plugins/papu/gb_apu/Gb_Oscs.cpp index bf8d72fcccd..735653fa9c3 100644 --- a/plugins/papu/gb_apu/Gb_Oscs.cpp +++ b/plugins/papu/gb_apu/Gb_Oscs.cpp @@ -1,144 +1,100 @@ - -// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/libs/ +// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ #include "Gb_Apu.h" #include -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN +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 */ -const int trigger = 0x80; +#include "blargg_source.h" // Gb_Osc -Gb_Osc::Gb_Osc() -{ - output = NULL; - outputs [0] = NULL; - outputs [1] = NULL; - outputs [2] = NULL; - outputs [3] = NULL; -} - void Gb_Osc::reset() { delay = 0; last_amp = 0; - period = 2048; - volume = 0; - global_volume = 7; // added - frequency = 0; length = 0; - enabled = false; - length_enabled = false; output_select = 3; output = outputs [output_select]; } void Gb_Osc::clock_length() { - if ( length_enabled && length ) - --length; -} - -void Gb_Osc::write_register( int reg, int value ) -{ - if ( reg == 4 ) - length_enabled = value & 0x40; + if ( (regs [4] & len_enabled_mask) && length ) + length--; } // Gb_Env -void Gb_Env::reset() -{ - env_period = 0; - env_dir = 0; - env_delay = 0; - new_volume = 0; - Gb_Osc::reset(); -} - -Gb_Env::Gb_Env() -{ -} - void Gb_Env::clock_envelope() { if ( env_delay && !--env_delay ) { - env_delay = env_period; - if ( env_dir ) - { - if ( volume < 15 ) - ++volume; - } - else if ( volume > 0 ) - { - --volume; - } + env_delay = regs [2] & 7; + int v = volume - 1 + (regs [2] >> 2 & 2); + if ( (unsigned) v < 15 ) + volume = v; } } -void Gb_Env::write_register( int reg, int value ) +bool Gb_Env::write_register( int reg, int data ) { - if ( reg == 2 ) { - env_period = value & 7; - env_dir = value & 8; - volume = new_volume = value >> 4; - } - else if ( reg == 4 && (value & trigger) ) { - env_delay = env_period; - volume = new_volume; - enabled = true; + switch ( reg ) + { + case 1: + length = 64 - (regs [1] & 0x3F); + break; + + case 2: + if ( !(data >> 4) ) + enabled = false; + break; + + case 4: + if ( data & trigger ) + { + env_delay = regs [2] & 7; + volume = regs [2] >> 4; + enabled = true; + if ( length == 0 ) + length = 64; + return true; + } } - Gb_Osc::write_register( reg, value ); + return false; } // Gb_Square void Gb_Square::reset() { - phase = 1; - duty = 1; - - sweep_period = 0; - sweep_delay = 0; - sweep_shift = 0; - sweep_dir = 0; + phase = 0; sweep_freq = 0; - - new_length = 0; - + sweep_delay = 0; Gb_Env::reset(); } -Gb_Square::Gb_Square() -{ - has_sweep = false; -} - void Gb_Square::clock_sweep() { + int sweep_period = (regs [0] & period_mask) >> 4; if ( sweep_period && sweep_delay && !--sweep_delay ) { sweep_delay = sweep_period; - frequency = sweep_freq; - - period = (2048 - frequency) * 4; + regs [3] = sweep_freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07); - int offset = sweep_freq >> sweep_shift; - if ( sweep_dir ) + int offset = sweep_freq >> (regs [0] & shift_mask); + if ( regs [0] & 0x08 ) offset = -offset; sweep_freq += offset; @@ -148,304 +104,233 @@ void Gb_Square::clock_sweep() } else if ( sweep_freq >= 2048 ) { - sweep_delay = 0; - sweep_freq = 2048; // stop sound output + sweep_delay = 0; // don't modify channel frequency any further + sweep_freq = 2048; // silence sound immediately } } } -void Gb_Square::write_register( int reg, int value ) +void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) { - static unsigned char const duty_table [4] = { 1, 2, 4, 6 }; + if ( sweep_freq == 2048 ) + playing = false; - switch ( reg ) + static unsigned char const table [4] = { 1, 2, 4, 6 }; + int const duty = table [regs [1] >> 6]; + int amp = volume & playing; + if ( phase >= duty ) + amp = -amp; + + int frequency = this->frequency(); + if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041 { - case 0: - sweep_period = (value >> 4) & 7; // changed - sweep_shift = value & 7; - sweep_dir = value & 0x08; - break; + // really high frequency results in DC at half volume + amp = volume >> 1; + playing = false; + } - case 1: - new_length = length = 64 - (value & 0x3f); - duty = duty_table [value >> 6]; - break; + { + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } + } - case 3: - frequency = (frequency & ~0xFF) + value; - length = new_length; - break; + time += delay; + if ( !playing ) + time = end_time; - case 4: - frequency = (value & 7) * 0x100 + (frequency & 0xFF); - length = new_length; - if ( value & trigger ) + if ( time < end_time ) + { + int const period = (2048 - frequency) * 4; + Blip_Buffer* const output = this->output; + int phase = this->phase; + int delta = amp * 2; + do { - sweep_freq = frequency; - if ( has_sweep && sweep_period && sweep_shift ) + phase = (phase + 1) & 7; + if ( phase == 0 || phase == duty ) { - sweep_delay = 1; - clock_sweep(); + delta = -delta; + synth->offset_inline( time, delta, output ); } + time += period; } - break; + while ( time < end_time ); + + this->phase = phase; + last_amp = delta >> 1; } - - period = (2048 - frequency) * 4; - - Gb_Env::write_register( reg, value ); + delay = time - end_time; } -void Gb_Square::run( gb_time_t time, gb_time_t end_time ) +// Gb_Noise + +void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) { - // to do: when frequency goes above 20000 Hz output should actually be 1/2 volume - // rather than 0 + int amp = volume & playing; + int tap = 13 - (regs [3] & 8); + if ( bits >> tap & 2 ) + amp = -amp; - if ( !enabled || (!length && length_enabled) || !volume || sweep_freq == 2048 || - !frequency || period < 27 ) { - if ( last_amp ) + int delta = amp - last_amp; + if ( delta ) { - synth->offset( time, -last_amp, output ); - last_amp = 0; + last_amp = amp; + synth->offset( time, delta, output ); } - delay = 0; } - else + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) { - int amp = (phase < duty) ? volume : -volume; - amp *= global_volume; - if ( amp != last_amp ) - { - synth->offset( time, amp - last_amp, output ); - last_amp = amp; - } + static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 }; + int period = table [regs [3] & 7] << (regs [3] >> 4); + + // keep parallel resampled time to eliminate time conversion in the loop + Blip_Buffer* const output = this->output; + const blip_resampled_time_t resampled_period = + output->resampled_duration( period ); + blip_resampled_time_t resampled_time = output->resampled_time( time ); + unsigned bits = this->bits; + int delta = amp * 2; - time += delay; - if ( time < end_time ) + do { - Blip_Buffer* const output = this->output; - const int duty = this->duty; - int phase = this->phase; - amp *= 2; - do + unsigned changed = (bits >> tap) + 1; + time += period; + bits <<= 1; + if ( changed & 2 ) { - phase = (phase + 1) & 7; - if ( phase == 0 || phase == duty ) - { - amp = -amp; - synth->offset_inline( time, amp, output ); - } - time += period; + delta = -delta; + bits |= 1; + synth->offset_resampled( resampled_time, delta, output ); } - while ( time < end_time ); - - this->phase = phase; - last_amp = amp >> 1; + resampled_time += resampled_period; } - delay = time - end_time; + while ( time < end_time ); + + this->bits = bits; + last_amp = delta >> 1; } + delay = time - end_time; } - // Gb_Wave -void Gb_Wave::reset() -{ - volume_shift = 0; - wave_pos = 0; - new_length = 0; - memset( wave, 0, sizeof wave ); - Gb_Osc::reset(); -} - -Gb_Wave::Gb_Wave() { -} - -void Gb_Wave::write_register( int reg, int value ) +inline void Gb_Wave::write_register( int reg, int data ) { switch ( reg ) { case 0: - new_enabled = value & 0x80; - enabled &= new_enabled; + if ( !(data & 0x80) ) + enabled = false; break; case 1: - new_length = length = 256 - value; + length = 256 - regs [1]; break; case 2: - volume = ((value >> 5) & 3); - volume_shift = (volume - 1) & 7; // silence = 7 - break; - - case 3: - frequency = (frequency & ~0xFF) + value; + volume = data >> 5 & 3; break; case 4: - frequency = (value & 7) * 0x100 + (frequency & 0xFF); - if ( new_enabled && (value & trigger) ) + if ( data & trigger & regs [0] ) { wave_pos = 0; - length = new_length; enabled = true; + if ( length == 0 ) + length = 256; } - break; } - - period = (2048 - frequency) * 2; - - Gb_Osc::write_register( reg, value ); } -void Gb_Wave::run( gb_time_t time, gb_time_t end_time ) +void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) { - // to do: when frequency goes above 20000 Hz output should actually be 1/2 volume - // rather than 0 - if ( !enabled || (!length && length_enabled) || !volume || !frequency || period < 7 ) - { - if ( last_amp ) { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - delay = 0; - } - else + int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7 + int frequency; { - int const vol_factor = global_volume * 2; + int amp = (wave [wave_pos] >> volume_shift & playing) * 2; - // wave data or shift may have changed - int diff = (wave [wave_pos] >> volume_shift) * vol_factor - last_amp; - if ( diff ) + frequency = this->frequency(); + if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 { - last_amp += diff; - synth->offset( time, diff, output ); + amp = 30 >> volume_shift & playing; + playing = false; } - time += delay; - if ( time < end_time ) + int delta = amp - last_amp; + if ( delta ) { - int const volume_shift = this->volume_shift; - int wave_pos = this->wave_pos; - - do + last_amp = amp; + synth->offset( time, delta, output ); + } + } + + time += delay; + if ( !playing ) + time = end_time; + + if ( time < end_time ) + { + Blip_Buffer* const output = this->output; + int const period = (2048 - frequency) * 2; + int wave_pos = (this->wave_pos + 1) & (wave_size - 1); + + do + { + int amp = (wave [wave_pos] >> volume_shift) * 2; + wave_pos = (wave_pos + 1) & (wave_size - 1); + int delta = amp - last_amp; + if ( delta ) { - wave_pos = unsigned (wave_pos + 1) % wave_size; - int amp = (wave [wave_pos] >> volume_shift) * vol_factor; - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset_inline( time, delta, output ); - } - time += period; + last_amp = amp; + synth->offset_inline( time, delta, output ); } - while ( time < end_time ); - - this->wave_pos = wave_pos; + time += period; } - delay = time - end_time; - } -} - - -// Gb_Noise - -void Gb_Noise::reset() -{ - bits = 1; - tap = 14; - Gb_Env::reset(); -} - -Gb_Noise::Gb_Noise() { -} - -void Gb_Noise::write_register( int reg, int value ) -{ - if ( reg == 1 ) { - new_length = length = 64 - (value & 0x3f); - } - else if ( reg == 2 ) { - // based on VBA code, noise is the only exception to the envelope code - // while the volume level here is applied when the channel is enabled, - // current volume is only affected by writes to this register if volume - // is zero and direction is up... (definitely needs verification) - int temp = volume; - Gb_Env::write_register( reg, value ); - if ( ( value & 0xF8 ) != 0 ) volume = temp; - return; - } - else if ( reg == 3 ) { - tap = 14 - (value & 8); - // noise formula and frequency tested against Metroid 2 and Zelda LA - int divisor = (value & 7) * 16; - if ( !divisor ) - divisor = 8; - period = divisor << (value >> 4); - } - else if ( reg == 4 && value & trigger ) { - bits = ~0u; - length = new_length; + while ( time < end_time ); + + this->wave_pos = (wave_pos - 1) & (wave_size - 1); } - - Gb_Env::write_register( reg, value ); + delay = time - end_time; } -#include BLARGG_ENABLE_OPTIMIZER +// Gb_Apu::write_osc -void Gb_Noise::run( gb_time_t time, gb_time_t end_time ) +void Gb_Apu::write_osc( int index, int reg, int data ) { - if ( !enabled || (!length && length_enabled) || !volume ) { - if ( last_amp ) { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - delay = 0; - } - else + reg -= index * 5; + Gb_Square* sq = &square2; + switch ( index ) { - int amp = bits & 1 ? -volume : volume; - amp *= global_volume; - if ( amp != last_amp ) { - synth->offset( time, amp - last_amp, output ); - last_amp = amp; - } - - time += delay; - if ( time < end_time ) + case 0: + sq = &square1; + case 1: + if ( sq->write_register( reg, data ) && index == 0 ) { - Blip_Buffer* const output = this->output; - // keep parallel resampled time to eliminate multiplication in the loop - const blip_resampled_time_t resampled_period = - output->resampled_duration( period ); - blip_resampled_time_t resampled_time = output->resampled_time( time ); - const unsigned mask = ~(1u << tap); - unsigned bits = this->bits; - amp *= 2; - - do { - unsigned feedback = bits; - bits >>= 1; - feedback = 1 & (feedback ^ bits); - time += period; - bits = (feedback << tap) | (bits & mask); - // feedback just happens to be true only when the level needs to change - // (the previous and current bits are different) - if ( feedback ) { - amp = -amp; - synth->offset_resampled( resampled_time, amp, output ); - } - resampled_time += resampled_period; + square1.sweep_freq = square1.frequency(); + if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) ) + { + square1.sweep_delay = 1; // cause sweep to recalculate now + square1.clock_sweep(); } - while ( time < end_time ); - - this->bits = bits; - last_amp = amp >> 1; } - delay = time - end_time; + break; + + case 2: + wave.write_register( reg, data ); + break; + + case 3: + if ( noise.write_register( reg, data ) ) + noise.bits = 0x7FFF; } } - diff --git a/plugins/papu/gb_apu/Gb_Oscs.h b/plugins/papu/gb_apu/Gb_Oscs.h index 0c0092a8d5f..d7f88ea1450 100644 --- a/plugins/papu/gb_apu/Gb_Oscs.h +++ b/plugins/papu/gb_apu/Gb_Oscs.h @@ -1,100 +1,83 @@ - // Private oscillators used by Gb_Apu -// Gb_Snd_Emu 0.1.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - +// Gb_Snd_Emu 0.1.5 #ifndef GB_OSCS_H #define GB_OSCS_H +#include "blargg_common.h" #include "Blip_Buffer.h" -enum { gb_apu_max_vol = 7 }; - -struct Gb_Osc { +struct Gb_Osc +{ + enum { trigger = 0x80 }; + enum { len_enabled_mask = 0x40 }; + Blip_Buffer* outputs [4]; // NULL, right, left, center Blip_Buffer* output; int output_select; + BOOST::uint8_t* regs; // osc's 5 registers int delay; int last_amp; - int period; int volume; - int global_volume; - int frequency; int length; - int new_length; - bool enabled; - bool length_enabled; + int enabled; - Gb_Osc(); - - void clock_length(); void reset(); - virtual void run( gb_time_t begin, gb_time_t end ) = 0; - virtual void write_register( int reg, int value ); + void clock_length(); + int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } }; -struct Gb_Env : Gb_Osc { - int env_period; - int env_dir; +struct Gb_Env : Gb_Osc +{ int env_delay; - int new_volume; - Gb_Env(); void reset(); void clock_envelope(); - void write_register( int, int ); + bool write_register( int, int ); }; -struct Gb_Square : Gb_Env { - int phase; - int duty; +struct Gb_Square : Gb_Env +{ + enum { period_mask = 0x70 }; + enum { shift_mask = 0x07 }; - int sweep_period; + typedef Blip_Synth Synth; + Synth const* synth; int sweep_delay; - int sweep_shift; - int sweep_dir; int sweep_freq; - bool has_sweep; - - typedef Blip_Synth Synth; - const Synth* synth; + int phase; - Gb_Square(); void reset(); - void run( gb_time_t, gb_time_t ); - void write_register( int, int ); void clock_sweep(); + void run( blip_time_t, blip_time_t, int playing ); }; -struct Gb_Wave : Gb_Osc { - int volume_shift; - unsigned wave_pos; - enum { wave_size = 32 }; - bool new_enabled; - BOOST::uint8_t wave [wave_size]; - - typedef Blip_Synth Synth; - const Synth* synth; +struct Gb_Noise : Gb_Env +{ + typedef Blip_Synth Synth; + Synth const* synth; + unsigned bits; - Gb_Wave(); - void reset(); - void run( gb_time_t, gb_time_t ); - void write_register( int, int ); + void run( blip_time_t, blip_time_t, int playing ); }; -struct Gb_Noise : Gb_Env { - unsigned bits; - int tap; - - typedef Blip_Synth Synth; - const Synth* synth; +struct Gb_Wave : Gb_Osc +{ + typedef Blip_Synth Synth; + Synth const* synth; + int wave_pos; + enum { wave_size = 32 }; + BOOST::uint8_t wave [wave_size]; - Gb_Noise(); - void reset(); - void run( gb_time_t, gb_time_t ); void write_register( int, int ); + void run( blip_time_t, blip_time_t, int playing ); }; -#endif +inline void Gb_Env::reset() +{ + env_delay = 0; + Gb_Osc::reset(); +} +#endif diff --git a/plugins/papu/gb_apu/Multi_Buffer.cpp b/plugins/papu/gb_apu/Multi_Buffer.cpp index e4383cde847..57f93b31740 100644 --- a/plugins/papu/gb_apu/Multi_Buffer.cpp +++ b/plugins/papu/gb_apu/Multi_Buffer.cpp @@ -1,20 +1,23 @@ - -// Blip_Buffer 0.3.4. http://www.slack.net/~ant/libs/ +// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ #include "Multi_Buffer.h" -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +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" -#include BLARGG_SOURCE_BEGIN +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) { @@ -23,48 +26,33 @@ Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) channels_changed_count_ = 1; } -blargg_err_t Multi_Buffer::set_channel_count( int ) -{ - return blargg_success; -} - -Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) -{ -} - -Mono_Buffer::~Mono_Buffer() -{ -} - -blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) -{ - BLARGG_RETURN_ERR( buf.set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); -} +blargg_err_t Multi_Buffer::set_channel_count( int ) { return 0; } // Silent_Buffer Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse { - chan.left = NULL; - chan.center = NULL; - chan.right = NULL; + // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? + chan.left = 0; + chan.center = 0; + chan.right = 0; } // Mono_Buffer -Mono_Buffer::channel_t Mono_Buffer::channel( int index ) +Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) { - channel_t ch; - ch.center = &buf; - ch.left = &buf; - ch.right = &buf; - return ch; + chan.center = &buf; + chan.left = &buf; + chan.right = &buf; } -void Mono_Buffer::end_frame( blip_time_t t, bool ) +Mono_Buffer::~Mono_Buffer() { } + +blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) { - buf.end_frame( t ); + RETURN_ERR( buf.set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); } // Stereo_Buffer @@ -76,14 +64,12 @@ Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) chan.right = &bufs [2]; } -Stereo_Buffer::~Stereo_Buffer() -{ -} +Stereo_Buffer::~Stereo_Buffer() { } blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) { for ( int i = 0; i < buf_count; i++ ) - BLARGG_RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); } @@ -101,18 +87,20 @@ void Stereo_Buffer::bass_freq( int bass ) void Stereo_Buffer::clear() { - stereo_added = false; - was_stereo = false; + stereo_added = 0; + was_stereo = false; for ( int i = 0; i < buf_count; i++ ) bufs [i].clear(); } -void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo ) +void Stereo_Buffer::end_frame( blip_time_t clock_count ) { + stereo_added = 0; for ( unsigned i = 0; i < buf_count; i++ ) + { + stereo_added |= bufs [i].clear_modified() << i; bufs [i].end_frame( clock_count ); - - stereo_added |= stereo; + } } long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) @@ -125,91 +113,120 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) count = avail; if ( count ) { - if ( stereo_added || was_stereo ) + int bufs_used = stereo_added | was_stereo; + //debug_printf( "%X\n", bufs_used ); + if ( bufs_used <= 1 ) + { + mix_mono( out, count ); + bufs [0].remove_samples( count ); + bufs [1].remove_silence( count ); + bufs [2].remove_silence( count ); + } + else if ( bufs_used & 1 ) { mix_stereo( out, count ); - bufs [0].remove_samples( count ); bufs [1].remove_samples( count ); bufs [2].remove_samples( count ); } else { - mix_mono( out, count ); - - bufs [0].remove_samples( count ); - - bufs [1].remove_silence( count ); - bufs [2].remove_silence( count ); + mix_stereo_no_center( out, count ); + bufs [0].remove_silence( count ); + bufs [1].remove_samples( count ); + bufs [2].remove_samples( count ); } // to do: this might miss opportunities for optimization - if ( !bufs [0].samples_avail() ) { - was_stereo = stereo_added; - stereo_added = false; + if ( !bufs [0].samples_avail() ) + { + was_stereo = stereo_added; + stereo_added = 0; } } return count * 2; } -#include BLARGG_ENABLE_OPTIMIZER - -void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count ) +void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) { - Blip_Reader left; - Blip_Reader right; - Blip_Reader center; + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [1] ); + BLIP_READER_BEGIN( left, bufs [1] ); + BLIP_READER_BEGIN( right, bufs [2] ); + BLIP_READER_BEGIN( center, bufs [0] ); - left.begin( bufs [1] ); - right.begin( bufs [2] ); - int bass = center.begin( bufs [0] ); - - while ( count-- ) + for ( ; count; --count ) { - int c = center.read(); - long l = c + left.read(); - long r = c + right.read(); - center.next( bass ); + int c = BLIP_READER_READ( center ); + blargg_long l = c + BLIP_READER_READ( left ); + blargg_long r = c + BLIP_READER_READ( right ); + if ( (BOOST::int16_t) l != l ) + l = 0x7FFF - (l >> 24); + + BLIP_READER_NEXT( center, bass ); + if ( (BOOST::int16_t) r != r ) + r = 0x7FFF - (r >> 24); + + BLIP_READER_NEXT( left, bass ); + BLIP_READER_NEXT( right, bass ); + out [0] = l; out [1] = r; out += 2; - + } + + BLIP_READER_END( center, bufs [0] ); + BLIP_READER_END( right, bufs [2] ); + BLIP_READER_END( left, bufs [1] ); +} + +void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count ) +{ + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [1] ); + BLIP_READER_BEGIN( left, bufs [1] ); + BLIP_READER_BEGIN( right, bufs [2] ); + + for ( ; count; --count ) + { + blargg_long l = BLIP_READER_READ( left ); if ( (BOOST::int16_t) l != l ) - out [-2] = 0x7FFF - (l >> 24); - - left.next( bass ); - right.next( bass ); + l = 0x7FFF - (l >> 24); + blargg_long r = BLIP_READER_READ( right ); if ( (BOOST::int16_t) r != r ) - out [-1] = 0x7FFF - (r >> 24); + r = 0x7FFF - (r >> 24); + + BLIP_READER_NEXT( left, bass ); + BLIP_READER_NEXT( right, bass ); + + out [0] = l; + out [1] = r; + out += 2; } - center.end( bufs [0] ); - right.end( bufs [2] ); - left.end( bufs [1] ); + BLIP_READER_END( right, bufs [2] ); + BLIP_READER_END( left, bufs [1] ); } -void Stereo_Buffer::mix_mono( blip_sample_t* out, long count ) +void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) { - Blip_Reader in; - int bass = in.begin( bufs [0] ); + blip_sample_t* BLIP_RESTRICT out = out_; + int const bass = BLIP_READER_BASS( bufs [0] ); + BLIP_READER_BEGIN( center, bufs [0] ); - while ( count-- ) + for ( ; count; --count ) { - long s = in.read(); - in.next( bass ); + blargg_long s = BLIP_READER_READ( center ); + if ( (BOOST::int16_t) s != s ) + s = 0x7FFF - (s >> 24); + + BLIP_READER_NEXT( center, bass ); out [0] = s; out [1] = s; out += 2; - - if ( (BOOST::int16_t) s != s ) { - s = 0x7FFF - (s >> 24); - out [-2] = s; - out [-1] = s; - } } - in.end( bufs [0] ); + BLIP_READER_END( center, bufs [0] ); } - diff --git a/plugins/papu/gb_apu/Multi_Buffer.h b/plugins/papu/gb_apu/Multi_Buffer.h index 5e3c478e0e1..82c8b3ab5af 100644 --- a/plugins/papu/gb_apu/Multi_Buffer.h +++ b/plugins/papu/gb_apu/Multi_Buffer.h @@ -1,11 +1,10 @@ - // Multi-channel sound buffer interface, and basic mono and stereo buffers -// Blip_Buffer 0.3.4. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - +// Blip_Buffer 0.4.1 #ifndef MULTI_BUFFER_H #define MULTI_BUFFER_H +#include "blargg_common.h" #include "Blip_Buffer.h" // Interface to one or more Blip_Buffers mapped to one or more channels @@ -24,7 +23,9 @@ class Multi_Buffer { Blip_Buffer* left; Blip_Buffer* right; }; - virtual channel_t channel( int index ) = 0; + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual channel_t channel( int index, int type ) = 0; // See Blip_Buffer.h virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; @@ -36,10 +37,8 @@ class Multi_Buffer { // Length of buffer, in milliseconds int length() const; - // See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo' - // if nothing was added to the left and right buffers of any channel for - // this time frame. - virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0; + // See Blip_Buffer.h + virtual void end_frame( blip_time_t ) = 0; // Number of samples per output frame (1 = mono, 2 = stereo) int samples_per_frame() const; @@ -52,6 +51,8 @@ class Multi_Buffer { virtual long read_samples( blip_sample_t*, long ) = 0; virtual long samples_avail() const = 0; +public: + BLARGG_DISABLE_NOTHROW protected: void channels_changed() { channels_changed_count_++; } private: @@ -68,55 +69,56 @@ class Multi_Buffer { // Uses a single buffer and outputs mono samples. class Mono_Buffer : public Multi_Buffer { Blip_Buffer buf; + channel_t chan; public: - Mono_Buffer(); - ~Mono_Buffer(); - // Buffer used for all channels Blip_Buffer* center() { return &buf; } - // See Multi_Buffer +public: + Mono_Buffer(); + ~Mono_Buffer(); blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); + void clock_rate( long rate ) { buf.clock_rate( rate ); } + void bass_freq( int freq ) { buf.bass_freq( freq ); } + void clear() { buf.clear(); } + long samples_avail() const { return buf.samples_avail(); } + long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t t ) { buf.end_frame( t ); } }; // Uses three buffers (one for center) and outputs stereo sample pairs. class Stereo_Buffer : public Multi_Buffer { public: - Stereo_Buffer(); - ~Stereo_Buffer(); // Buffers used for all channels Blip_Buffer* center() { return &bufs [0]; } Blip_Buffer* left() { return &bufs [1]; } Blip_Buffer* right() { return &bufs [2]; } - // See Multi_Buffer +public: + Stereo_Buffer(); + ~Stereo_Buffer(); blargg_err_t set_sample_rate( long, int msec = blip_default_length ); void clock_rate( long ); void bass_freq( int ); void clear(); - channel_t channel( int index ); - void end_frame( blip_time_t, bool added_stereo = true ); + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t ); - long samples_avail() const; + long samples_avail() const { return bufs [0].samples_avail() * 2; } long read_samples( blip_sample_t*, long ); private: enum { buf_count = 3 }; Blip_Buffer bufs [buf_count]; channel_t chan; - bool stereo_added; - bool was_stereo; + int stereo_added; + int was_stereo; - void mix_stereo( blip_sample_t*, long ); - void mix_mono( blip_sample_t*, long ); + void mix_stereo_no_center( blip_sample_t*, blargg_long ); + void mix_stereo( blip_sample_t*, blargg_long ); + void mix_mono( blip_sample_t*, blargg_long ); }; // Silent_Buffer generates no samples, useful where no sound is wanted @@ -124,51 +126,33 @@ class Silent_Buffer : public Multi_Buffer { channel_t chan; public: Silent_Buffer(); - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); void clock_rate( long ) { } void bass_freq( int ) { } void clear() { } - channel_t channel( int ) { return chan; } - void end_frame( blip_time_t, bool unused = true ) { } + channel_t channel( int, int ) { return chan; } + void end_frame( blip_time_t ) { } long samples_avail() const { return 0; } long read_samples( blip_sample_t*, long ) { return 0; } }; -// End of public interface - -inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) -{ - return Multi_Buffer::set_sample_rate( rate, msec ); -} - inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) { sample_rate_ = rate; length_ = msec; - return blargg_success; + return 0; } -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } - -inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; } +inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) +{ + return Multi_Buffer::set_sample_rate( rate, msec ); +} -inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int index ) { return chan; } +inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } inline long Multi_Buffer::sample_rate() const { return sample_rate_; } inline int Multi_Buffer::length() const { return length_; } -inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); } - -inline void Mono_Buffer::clear() { buf.clear(); } - -inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); } - -inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } - -inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); } - #endif - diff --git a/plugins/papu/gb_apu/blargg_common.h b/plugins/papu/gb_apu/blargg_common.h index 519bba7c499..ed218a8da3a 100644 --- a/plugins/papu/gb_apu/blargg_common.h +++ b/plugins/papu/gb_apu/blargg_common.h @@ -1,178 +1,196 @@ - // Sets up common environment for Shay Green's libraries. -// -// Don't modify this file directly; #define HAVE_CONFIG_H and put your -// configuration into "config.h". - -// Copyright (C) 2004-2005 Shay Green. +// To change configuration options, modify blargg_config.h, not this file. #ifndef BLARGG_COMMON_H #define BLARGG_COMMON_H -// Allow prefix configuration file *which can re-include blargg_common.h* -// (probably indirectly). -#ifdef HAVE_CONFIG_H - #undef BLARGG_COMMON_H - #include "config.h" - #define BLARGG_COMMON_H -#endif +#include +#include +#include +#include -// Source files use #include BLARGG_ENABLE_OPTIMIZER before performance-critical code -#ifndef BLARGG_ENABLE_OPTIMIZER - #define BLARGG_ENABLE_OPTIMIZER "blargg_common.h" -#endif +#undef BLARGG_COMMON_H +// allow blargg_config.h to #include blargg_common.h +#include "blargg_config.h" +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H -// Source files have #include BLARGG_SOURCE_BEGIN at the beginning -#ifndef BLARGG_SOURCE_BEGIN - #define BLARGG_SOURCE_BEGIN "blargg_source.h" +// BLARGG_RESTRICT: equivalent to restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT #endif -// Determine compiler's language support - -#if defined (__MWERKS__) - // Metrowerks CodeWarrior - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -#elif defined (_MSC_VER) - // Microsoft Visual C++ - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 +// STATIC_CAST(T,expr): Used in place of static_cast (expr) +#ifndef STATIC_CAST + #define STATIC_CAST(T,expr) ((T) (expr)) +#endif + +// blargg_err_t (0 on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif + +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) +template +class blargg_vector { + T* begin_; + size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { free( begin_ ); } + size_t size() const { return size_; } + T* begin() const { return begin_; } + T* end() const { return begin_ + size_; } + blargg_err_t resize( size_t n ) + { + void* p = realloc( begin_, n * sizeof (T) ); + if ( !p && n ) + return "Out of memory"; + begin_ = (T*) p; + size_ = n; + return 0; + } + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } + T& operator [] ( size_t n ) const + { + assert( n <= size_ ); // <= to allow past-the-end value + return begin_ [n]; + } +}; + +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if operator new can return NULL + #if __cplusplus >= 199711 || __GNUC__ >= 3 + #define BLARGG_THROWS( spec ) throw spec + #else + #define BLARGG_THROWS( spec ) #endif - -#elif defined (__GNUC__) - // GNU C++ - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #define BLARGG_COMPILER_HAS_BOOL 1 - -#elif defined (__MINGW32__) - // Mingw? - #define BLARGG_COMPILER_HAS_BOOL 1 - -#elif __cplusplus < 199711 - // Pre-ISO C++ compiler - #define BLARGG_COMPILER_HAS_BOOL 0 - #define STATIC_CAST( type ) (type) - + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ + void operator delete ( void* p ) { free( p ); } + #define BLARGG_NEW new +#else + #include + #define BLARGG_NEW new (std::nothrow) #endif -// STATIC_CAST(T) (expr) -> static_cast< T > (expr) -#ifndef STATIC_CAST - #define STATIC_CAST( type ) static_cast< type > -#endif +// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) -// Set up boost -#include "boost/config.hpp" -#ifndef BOOST_MINIMAL - #define BOOST boost - #ifndef BLARGG_COMPILER_HAS_NAMESPACE - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #endif - #ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 1 +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) #endif #endif -// Bool support +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. #ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 1 -#elif !BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + // If you get errors here, modify your blargg_config.h file typedef int bool; const bool true = 1; const bool false = 0; #endif -// Set up namespace support - -#ifndef BLARGG_COMPILER_HAS_NAMESPACE - #define BLARGG_COMPILER_HAS_NAMESPACE 0 -#endif - -#ifndef BLARGG_USE_NAMESPACE - #define BLARGG_USE_NAMESPACE BLARGG_COMPILER_HAS_NAMESPACE -#endif - -#ifndef BOOST - #if BLARGG_USE_NAMESPACE - #define BOOST boost - #else - #define BOOST - #endif -#endif +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough -#undef BLARGG_BEGIN_NAMESPACE -#undef BLARGG_END_NAMESPACE -#if BLARGG_USE_NAMESPACE - #define BLARGG_BEGIN_NAMESPACE( name ) namespace name { - #define BLARGG_END_NAMESPACE } +#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blargg_long; #else - #define BLARGG_BEGIN_NAMESPACE( name ) - #define BLARGG_END_NAMESPACE + typedef int blargg_long; #endif -#if BLARGG_USE_NAMESPACE - #define STD std +#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF + typedef unsigned long blargg_ulong; #else - #define STD + typedef unsigned blargg_ulong; #endif -// BOOST::uint8_t, BOOST::int16_t, etc. -#include "boost/cstdint.hpp" - -// BOOST_STATIC_ASSERT( expr ) -#include "boost/static_assert.hpp" +// BOOST::int8_t etc. -// Common standard headers -#if BLARGG_COMPILER_HAS_NAMESPACE - #include - #include -#else - #include - #include -#endif - -// blargg_err_t (NULL on success, otherwise error string) -typedef const char* blargg_err_t; -const blargg_err_t blargg_success = 0; +// HAVE_STDINT_H: If defined, use for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST -// BLARGG_NEW is used in place of 'new' to create objects. By default, -// plain new is used. -#ifndef BLARGG_NEW - #define BLARGG_NEW new -#endif +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #define BOOST -// BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN -// Only needed if modules are used which must know byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) - #if defined (__powerc) || defined (macintosh) - #define BLARGG_BIG_ENDIAN 1 - - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_LITTLE_ENDIAN 1 - - #endif +#else + struct BOOST + { + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F + typedef signed char int8_t; + typedef unsigned char uint8_t; + #else + // No suitable 8-bit type available + typedef struct see_blargg_common_h int8_t; + typedef struct see_blargg_common_h uint8_t; + #endif + + #if USHRT_MAX == 0xFFFF + typedef short int16_t; + typedef unsigned short uint16_t; + #else + // No suitable 16-bit type available + typedef struct see_blargg_common_h int16_t; + typedef struct see_blargg_common_h uint16_t; + #endif + + #if ULONG_MAX == 0xFFFFFFFF + typedef long int32_t; + typedef unsigned long uint32_t; + #elif UINT_MAX == 0xFFFFFFFF + typedef int int32_t; + typedef unsigned int uint32_t; + #else + // No suitable 32-bit type available + typedef struct see_blargg_common_h int32_t; + typedef struct see_blargg_common_h uint32_t; + #endif + }; +#endif + +#if __GNUC__ >= 3 + #define BLARGG_DEPRECATED __attribute__ ((deprecated)) +#else + #define BLARGG_DEPRECATED #endif -// BLARGG_NONPORTABLE (allow use of nonportable optimizations/features) -#ifndef BLARGG_NONPORTABLE - #define BLARGG_NONPORTABLE 0 -#endif -#ifdef BLARGG_MOST_PORTABLE - #error "BLARGG_MOST_PORTABLE has been removed; use BLARGG_NONPORTABLE." +// Use in place of "= 0;" for a pure virtual, since these cause calls to std C++ lib. +// During development, BLARGG_PURE( x ) expands to = 0; +// virtual int func() BLARGG_PURE( { return 0; } ) +#ifndef BLARGG_PURE + #define BLARGG_PURE( def ) def #endif -// BLARGG_CPU_* -#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86) - #if defined (__powerc) - #define BLARGG_CPU_POWERPC 1 - - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_CPU_X86 1 - - #endif #endif - #endif - diff --git a/plugins/papu/gb_apu/blargg_config.h b/plugins/papu/gb_apu/blargg_config.h new file mode 100644 index 00000000000..377dd2d8c46 --- /dev/null +++ b/plugins/papu/gb_apu/blargg_config.h @@ -0,0 +1,43 @@ +// Library configuration. Modify this file as necessary. + +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment to use zlib for transparent decompression of gzipped files +//#define HAVE_ZLIB_H + +// Uncomment and edit list to support only the listed game music types, +// so that the others don't get linked in at all. +/* +#define GME_TYPE_LIST \ + gme_ay_type,\ + gme_gbs_type,\ + gme_gym_type,\ + gme_hes_type,\ + gme_kss_type,\ + gme_nsf_type,\ + gme_nsfe_type,\ + gme_sap_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type +*/ + +// Uncomment to enable platform-specific optimizations +//#define BLARGG_NONPORTABLE 1 + +// Uncomment to use faster, lower quality sound synthesis +//#define BLIP_BUFFER_FAST 1 + +// Uncomment if automatic byte-order determination doesn't work +//#define BLARGG_BIG_ENDIAN 1 + +// Uncomment if you get errors in the bool section of blargg_common.h +//#define BLARGG_COMPILER_HAS_BOOL 1 + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/plugins/papu/gb_apu/blargg_source.h b/plugins/papu/gb_apu/blargg_source.h index fa77dc0f7df..b011777ad8f 100644 --- a/plugins/papu/gb_apu/blargg_source.h +++ b/plugins/papu/gb_apu/blargg_source.h @@ -1,7 +1,7 @@ - -// By default, #included at beginning of library source files - -// Copyright (C) 2005 Shay Green. +/* Included at the beginning of library source files, after all other #include lines. +Sets up helpful macros and services used in my source code. They don't need +module an annoying module prefix on their names since they are defined after +all other #include lines. */ #ifndef BLARGG_SOURCE_H #define BLARGG_SOURCE_H @@ -16,13 +16,14 @@ // module. A failed requirement indicates a bug outside the module. // void require( bool expr ); #undef require -#define require( expr ) assert((/* "unmet requirement",*/ expr )) +#define require( expr ) assert( expr ) // Like printf() except output goes to debug log file. Might be defined to do // nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -#undef dprintf -#define dprintf (1) ? ((void) 0) : (void) +// void debug_printf( const char* format, ... ); +static inline void blargg_dprintf_( const char*, ... ) { } +#undef debug_printf +#define debug_printf (1) ? (void) 0 : blargg_dprintf_ // If enabled, evaluate expr and if false, make debug log entry with source file // and line. Meant for finding situations that should be examined further, but that @@ -30,22 +31,40 @@ #undef check #define check( expr ) ((void) 0) -// If expr returns non-NULL error string, return it from current function, otherwise continue. -#define BLARGG_RETURN_ERR( expr ) do { \ +// If expr yields error string, return it from current function, otherwise continue. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ blargg_err_t blargg_return_err_ = (expr); \ if ( blargg_return_err_ ) return blargg_return_err_; \ } while ( 0 ) -// If ptr is NULL, return out of memory error string. -#define BLARGG_CHECK_ALLOC( ptr ) do { if ( !(ptr) ) return "Out of memory"; } while ( 0 ) +// If ptr is 0, return out of memory error string. +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) // Avoid any macros which evaluate their arguments multiple times #undef min #undef max +#define DEF_MIN_MAX( type ) \ + static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\ + static inline type max( type x, type y ) { if ( y < x ) return x; return y; } + +DEF_MIN_MAX( int ) +DEF_MIN_MAX( unsigned ) +DEF_MIN_MAX( long ) +DEF_MIN_MAX( unsigned long ) +DEF_MIN_MAX( float ) +DEF_MIN_MAX( double ) + +#undef DEF_MIN_MAX + +/* // using const references generates crappy code, and I am currenly only using these // for built-in types, so they take arguments by value +// TODO: remove +inline int min( int x, int y ) template inline T min( T x, T y ) { @@ -61,6 +80,31 @@ inline T max( T x, T y ) return y; return x; } +*/ +// TODO: good idea? bad idea? +#undef byte +#define byte byte_ +typedef unsigned char byte; + +// Setup compiler defines useful for exporting required public API symbols in gme.cpp +#ifndef BLARGG_EXPORT + #if defined (_WIN32) && defined(BLARGG_BUILD_DLL) + #define BLARGG_EXPORT __declspec(dllexport) + #elif defined (LIBGME_VISIBILITY) + #define BLARGG_EXPORT __attribute__((visibility ("default"))) + #else + #define BLARGG_EXPORT + #endif #endif +// deprecated +#define BLARGG_CHECK_ALLOC CHECK_ALLOC +#define BLARGG_RETURN_ERR RETURN_ERR + +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of debug_printf and check +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/plugins/papu/papu_instrument.cpp b/plugins/papu/papu_instrument.cpp index ed64995f3e6..3d4dfcf5d8d 100644 --- a/plugins/papu/papu_instrument.cpp +++ b/plugins/papu/papu_instrument.cpp @@ -70,7 +70,7 @@ papuInstrument::papuInstrument( InstrumentTrack * _instrument_track ) : tr( "Sweep RtShift amount" ) ), m_ch1WavePatternDutyModel( 2.0f, 0.0f, 3.0f, 1.0f, this, tr( "Wave Pattern Duty" ) ), - m_ch1VolumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this, + m_ch1VolumeModel( 15.0f, 0.0f, 15.0f, 1.0f, this, tr( "Channel 1 volume" ) ), m_ch1VolSweepDirModel( false, this, tr( "Volume sweep direction" ) ), @@ -396,7 +396,7 @@ void papuInstrument::playNote( NotePlayHandle * _n, } datalen = framesleft>avail?avail:framesleft; datalen = datalen>buf_size?buf_size:datalen; - + long count = papu->read_samples( buf, datalen*2)/2; for( fpp_t frame = 0; frame < count; ++frame )