summaryrefslogtreecommitdiffstats
path: root/gearboy/src/audio
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2022-06-02 15:28:40 +0200
committerLouis Burda <quent.burda@gmail.com>2022-06-02 15:28:40 +0200
commit5bc16063c29aa4d3d287ebd163ccdbcbf54c4f9f (patch)
treec131f947a37b3af2d14d41e9eda098bdec2d061c /gearboy/src/audio
parent78a5f810b22f0d8cafa05f638b0cb2e889824859 (diff)
downloadcscg2022-gearboy-master.tar.gz
cscg2022-gearboy-master.zip
Added submodule filesHEADmaster
Diffstat (limited to 'gearboy/src/audio')
-rw-r--r--gearboy/src/audio/Blip_Buffer.cpp463
-rw-r--r--gearboy/src/audio/Blip_Buffer.h556
-rw-r--r--gearboy/src/audio/Blip_Synth.h208
-rw-r--r--gearboy/src/audio/Effects_Buffer.cpp638
-rw-r--r--gearboy/src/audio/Effects_Buffer.h143
-rw-r--r--gearboy/src/audio/Gb_Apu.cpp395
-rw-r--r--gearboy/src/audio/Gb_Apu.h182
-rw-r--r--gearboy/src/audio/Gb_Apu_State.cpp119
-rw-r--r--gearboy/src/audio/Gb_Oscs.cpp665
-rw-r--r--gearboy/src/audio/Gb_Oscs.h191
-rw-r--r--gearboy/src/audio/Multi_Buffer.cpp281
-rw-r--r--gearboy/src/audio/Multi_Buffer.h204
-rw-r--r--gearboy/src/audio/blargg_common.h206
-rw-r--r--gearboy/src/audio/blargg_config.h33
-rw-r--r--gearboy/src/audio/blargg_source.h92
15 files changed, 4376 insertions, 0 deletions
diff --git a/gearboy/src/audio/Blip_Buffer.cpp b/gearboy/src/audio/Blip_Buffer.cpp
new file mode 100644
index 00000000..85070daf
--- /dev/null
+++ b/gearboy/src/audio/Blip_Buffer.cpp
@@ -0,0 +1,463 @@
+// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
+
+#include "Blip_Buffer.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+// TODO: use scoped for variables in treble_eq()
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
+
+Blip_Buffer::Blip_Buffer()
+{
+ factor_ = (blip_ulong)LONG_MAX;
+ buffer_ = 0;
+ buffer_size_ = 0;
+ sample_rate_ = 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
+
+ clear();
+}
+
+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;
+ clear();
+}
+
+void Blip_Buffer::clear( int entire_buffer )
+{
+ offset_ = 0;
+ reader_accum_ = 0;
+ modified_ = 0;
+ if ( buffer_ )
+ {
+ long count = (entire_buffer ? buffer_size_ : samples_avail());
+ memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) );
+ }
+}
+
+Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec )
+{
+ 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 = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
+ if ( msec != blip_max_length )
+ {
+ long s = (new_rate * (msec + 1) + 999) / 1000;
+ if ( s < new_size )
+ new_size = s;
+ else
+ assert( 0 ); // fails if requested buffer length exceeds limit
+ }
+
+ if ( buffer_size_ != new_size )
+ {
+ void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ );
+ if ( !p )
+ return "Out of memory";
+ buffer_ = (buf_t_*) p;
+ }
+
+ buffer_size_ = (int)new_size;
+ assert( buffer_size_ != silent_buf_size ); // size should never happen to match this
+
+ // update things based on the sample rate
+ sample_rate_ = new_rate;
+ length_ = (int)(new_size * 1000 / new_rate - 1);
+ if ( msec )
+ assert( length_ == msec ); // ensure length is same as that passed in
+
+ // update these since they depend on sample rate
+ if ( clock_rate_ )
+ clock_rate( clock_rate_ );
+ bass_freq( bass_freq_ );
+
+ clear();
+
+ return 0; // success
+}
+
+blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const
+{
+ 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;
+ int shift = 31;
+ if ( freq > 0 )
+ {
+ shift = 13;
+ long f = (freq << 16) / sample_rate_;
+ while ( (f >>= 1) && --shift ) { }
+ }
+ bass_shift_ = shift;
+}
+
+void Blip_Buffer::end_frame( blip_time_t t )
+{
+ offset_ += t * factor_;
+ assert( samples_avail() <= (long) buffer_size_ ); // fails if time is past end of buffer
+}
+
+long Blip_Buffer::count_samples( blip_time_t t ) const
+{
+ blip_resampled_time_t last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY;
+ blip_resampled_time_t first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
+ return long (last_sample - first_sample);
+}
+
+blip_time_t Blip_Buffer::count_clocks( long count ) const
+{
+ if ( !factor_ )
+ {
+ assert( 0 ); // sample rate and clock rates must be set first
+ return 0;
+ }
+
+ if ( count > buffer_size_ )
+ count = buffer_size_;
+ 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_Synth_Fast_::volume_unit( double new_unit )
+{
+ delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5);
+}
+
+#if !BLIP_BUFFER_FAST
+
+Blip_Synth_::Blip_Synth_( short* p, int w ) :
+ impulses( p ),
+ width( w )
+{
+ 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++ )
+ {
+ double angle = ((i - count) * 2 + 1) * to_angle;
+ double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle );
+ double cos_nc_angle = cos( maxh * cutoff * angle );
+ double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle );
+ double cos_angle = cos( angle );
+
+ c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle;
+ double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle);
+ double b = 2.0 - cos_angle - cos_angle;
+ double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle;
+
+ out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d
+ }
+}
+
+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_Synth_::adjust_impulse()
+{
+ // 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; )
+ {
+ 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_Synth_::treble_eq( blip_eq_t const& eq )
+{
+ float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2];
+
+ int const half_size = blip_res / 2 * (width - 1);
+ eq.generate( &fimpulse [blip_res], half_size );
+
+ 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 size = this->impulses_size();
+ for ( i = 0; i < size; i++ )
+ {
+ impulses [i] = (short) (int) floor( (next - sum) * rescale + 0.5 );
+ sum += fimpulse [i];
+ next += fimpulse [i + blip_res];
+ }
+ adjust_impulse();
+
+ // volume might require rescaling
+ double vol = volume_unit_;
+ if ( vol )
+ {
+ volume_unit_ = 0.0;
+ volume_unit( vol );
+ }
+}
+
+void Blip_Synth_::volume_unit( double new_unit )
+{
+ if ( new_unit != volume_unit_ )
+ {
+ // 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 )
+ {
+ int shift = 0;
+
+ // if unit is really small, might need to attenuate kernel
+ while ( factor < 2.0 )
+ {
+ 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) (int) (((impulses [i] + offset) >> shift) - offset2);
+ adjust_impulse();
+ }
+ }
+ delta_factor = (int) floor( factor + 0.5 );
+ //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit );
+ }
+}
+#endif
+
+long Blip_Buffer::read_samples( blip_sample_t* out_, long max_samples, int stereo )
+{
+ long count = samples_avail();
+ if ( count > max_samples )
+ count = max_samples;
+
+ if ( count )
+ {
+ int const bass = BLIP_READER_BASS( *this );
+ BLIP_READER_BEGIN( reader, *this );
+ BLIP_READER_ADJ_( reader, count );
+ blip_sample_t* BLIP_RESTRICT out = out_ + count;
+ blip_long offset = (blip_long) -count;
+
+ if ( !stereo )
+ {
+ do
+ {
+ blip_long s = BLIP_READER_READ( reader );
+ BLIP_READER_NEXT_IDX_( reader, bass, offset );
+ BLIP_CLAMP( s, s );
+ out [offset] = (blip_sample_t) s;
+ }
+ while ( ++offset );
+ }
+ else
+ {
+ do
+ {
+ blip_long s = BLIP_READER_READ( reader );
+ BLIP_READER_NEXT_IDX_( reader, bass, offset );
+ BLIP_CLAMP( s, s );
+ out [offset * 2] = (blip_sample_t) s;
+ }
+ while ( ++offset );
+ }
+
+ BLIP_READER_END( reader, *this );
+
+ remove_samples( count );
+ }
+ return count;
+}
+
+void Blip_Buffer::mix_samples( blip_sample_t const* in, long count )
+{
+ 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-- )
+ {
+ blip_long s = (blip_long) *in++ << sample_shift;
+ *out += s - prev;
+ prev = s;
+ ++out;
+ }
+ *out -= prev;
+}
+
+void Blip_Buffer::save_state( blip_buffer_state_t* out )
+{
+ assert( samples_avail() == 0 );
+ out->offset_ = offset_;
+ out->reader_accum_ = reader_accum_;
+ memcpy( out->buf, &buffer_ [offset_ >> BLIP_BUFFER_ACCURACY], sizeof out->buf );
+}
+
+void Blip_Buffer::load_state( blip_buffer_state_t const& in )
+{
+ clear( false );
+
+ offset_ = in.offset_;
+ reader_accum_ = in.reader_accum_;
+ memcpy( buffer_, in.buf, sizeof in.buf );
+}
diff --git a/gearboy/src/audio/Blip_Buffer.h b/gearboy/src/audio/Blip_Buffer.h
new file mode 100644
index 00000000..699bff7d
--- /dev/null
+++ b/gearboy/src/audio/Blip_Buffer.h
@@ -0,0 +1,556 @@
+// Band-limited sound synthesis buffer
+
+// Blip_Buffer 0.4.1
+#ifndef BLIP_BUFFER_H
+#define BLIP_BUFFER_H
+
+ // internal
+ #include <limits.h>
+ #if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF
+ typedef long blip_long;
+ typedef unsigned long blip_ulong;
+ #else
+ typedef int blip_long;
+ typedef unsigned blip_ulong;
+ #endif
+
+// Time unit at source clock rate
+typedef blip_long blip_time_t;
+
+// Output samples are 16-bit signed, with a range of -32768 to 32767
+typedef short blip_sample_t;
+enum { blip_sample_max = 32767 };
+
+struct blip_buffer_state_t;
+
+class Blip_Buffer {
+public:
+ typedef const char* blargg_err_t;
+
+ // Sets output sample rate and buffer length in milliseconds (1/1000 sec, defaults
+ // to 1/4 second) and clears buffer. If there isn't enough memory, leaves buffer
+ // untouched and returns "Out of memory", otherwise returns NULL.
+ blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 );
+
+ // Sets number of source time units per second
+ void clock_rate( long clocks_per_sec );
+
+ // Ends current time frame of specified duration and makes 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 );
+
+ // Reads at most 'max_samples' out of buffer into 'dest', removing them 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 features
+
+ // Removes 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;
+
+ // Removes 'count' samples from those waiting to be read
+ void remove_samples( long count );
+
+ // Sets frequency high-pass filter frequency, where higher values reduce bass more
+ void bass_freq( int frequency );
+
+ // Current output sample rate
+ long sample_rate() const;
+
+ // Length of buffer in milliseconds
+ int length() const;
+
+ // Number of source time units per second
+ long clock_rate() const;
+
+// Experimental features
+
+ // Saves state, including high-pass filter and tails of last deltas.
+ // All samples must have been read from buffer before calling this.
+ void save_state( blip_buffer_state_t* out );
+
+ // Loads state. State must have been saved from Blip_Buffer with same
+ // settings during same run of program. States can NOT be stored on disk.
+ // Clears buffer before loading state.
+ void load_state( blip_buffer_state_t const& in );
+
+ // Number of samples delay from synthesis to samples read out
+ int output_latency() const;
+
+ // Counts number of clocks needed until 'count' samples will be available.
+ // If buffer can't even hold 'count' samples, returns number of clocks until
+ // 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;
+
+ // Mixes in 'count' samples from 'buf_in'
+ void mix_samples( blip_sample_t const* buf_in, long count );
+
+
+ // Signals that sound has been added to buffer. Could be done automatically in
+ // Blip_Synth, but that would affect performance more, as you can arrange that
+ // this is called only once per time frame rather than for every delta.
+ void set_modified() { modified_ = this; }
+
+ // not documented yet
+ blip_ulong unsettled() const;
+ Blip_Buffer* clear_modified() { Blip_Buffer* b = modified_; modified_ = 0; return b; }
+ void remove_silence( long count );
+ typedef blip_ulong blip_resampled_time_t;
+ 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();
+
+ // 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_long 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_;
+ Blip_Buffer* modified_; // non-zero = true (more optimal than using bool, heh)
+ friend class Blip_Reader;
+};
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+// 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:
+ Blip_Buffer* buf;
+ int last_amp;
+ int delta_factor;
+
+ 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;
+
+ 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, better = slower. In general, use 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<int quality,int range>
+class Blip_Synth {
+public:
+ // Sets overall volume of waveform
+ void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); }
+
+ // Configures low-pass filter (see blip_buffer.txt)
+ void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); }
+
+ // Gets/sets Blip_Buffer used for output
+ Blip_Buffer* output() const { return impl.buf; }
+ void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; }
+
+ // Updates 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
+
+ // Adds 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
+class blip_eq_t {
+public:
+ // 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 rolloff_freq;
+ long sample_rate;
+ long cutoff_freq;
+ void generate( float* out, int count ) const;
+ friend class Blip_Synth_;
+};
+
+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:
+ // 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 );
+
+ Silent_Blip_Buffer();
+};
+
+ #if __GNUC__ >= 3 || _MSC_VER >= 1100
+ #define BLIP_RESTRICT __restrict
+ #else
+ #define BLIP_RESTRICT
+ #endif
+
+// Optimized reading from Blip_Buffer, for use in custom sample output
+
+// Begins 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_
+
+// Gets value to pass to BLIP_READER_NEXT()
+#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_)
+
+// 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;
+
+// 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)
+
+// Advances to next sample
+#define BLIP_READER_NEXT( name, bass ) \
+ (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))
+
+// Ends 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)
+
+
+// experimental
+#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)
+
+blip_long const blip_reader_idx_factor = sizeof (Blip_Buffer::buf_t_);
+
+#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> (bass);\
+ name##_reader_accum += name##_reader_buf [(idx)];\
+}
+
+#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> (bass);\
+ name##_reader_accum +=\
+ *(Blip_Buffer::buf_t_ const*) ((char const*) name##_reader_buf + (idx));\
+}
+
+// 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:
+ 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; }
+private:
+ const Blip_Buffer::buf_t_* buf;
+ blip_long accum;
+};
+
+#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+ defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+ #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
+#else
+ #define BLIP_CLAMP_( in ) (blip_sample_t) in != in
+#endif
+
+// Clamp sample to blip_sample_t range
+#define BLIP_CLAMP( sample, out )\
+ { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 24) ^ 0x7FFF; }
+
+struct blip_buffer_state_t
+{
+ blip_resampled_time_t offset_;
+ blip_long reader_accum_;
+ blip_long buf [blip_buffer_extra_];
+};
+
+// End of public interface
+
+#ifndef assert
+ #include <assert.h>
+#endif
+
+template<int quality,int range>
+inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
+ int delta, Blip_Buffer* blip_buf ) const
+{
+ // If this assertion fails, it means that an attempt was made to add a delta
+ // at a negative time or past the end of the buffer.
+ 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
+
+ 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__)
+
+ // this straight forward version gave 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 );\
+ }
+
+ 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 );
+
+ #undef ADD_IMP
+
+ #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
+}
+
+#undef BLIP_FWD
+#undef BLIP_REV
+
+template<int quality,int range>
+#if BLIP_BUFFER_FAST
+ inline
+#endif
+void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
+{
+ offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
+}
+
+template<int quality,int range>
+#if BLIP_BUFFER_FAST
+ inline
+#endif
+void Blip_Synth<quality,range>::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 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 )
+{
+ buf = blip_buf.buffer_;
+ accum = blip_buf.reader_accum_;
+ return blip_buf.bass_shift_;
+}
+
+inline void Blip_Buffer::remove_silence( long count )
+{
+ // fails if you try to remove more samples than available
+ assert( count <= samples_avail() );
+ offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+}
+
+inline blip_ulong Blip_Buffer::unsettled() const
+{
+ return reader_accum_ >> (blip_sample_bits - 16);
+}
+
+int const blip_max_length = 0;
+int const blip_default_length = 250; // 1/4 second
+
+#endif
diff --git a/gearboy/src/audio/Blip_Synth.h b/gearboy/src/audio/Blip_Synth.h
new file mode 100644
index 00000000..c1bdc396
--- /dev/null
+++ b/gearboy/src/audio/Blip_Synth.h
@@ -0,0 +1,208 @@
+
+// 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<int quality,int range>
+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<int quality,int range>
+class Blip_Wave {
+ Blip_Synth<quality,range> 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<int quality,int range>
+void Blip_Wave<quality,range>::amplitude( int amp ) {
+ int delta = amp - last_amp;
+ last_amp = amp;
+ synth.offset_inline( time_, delta );
+}
+
+template<int quality,int range>
+inline void Blip_Synth<quality,range>::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(( (void)"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<int quality,int range>
+void Blip_Synth<quality,range>::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const {
+ offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
+}
+
+#endif
+
diff --git a/gearboy/src/audio/Effects_Buffer.cpp b/gearboy/src/audio/Effects_Buffer.cpp
new file mode 100644
index 00000000..2a6f96b9
--- /dev/null
+++ b/gearboy/src/audio/Effects_Buffer.cpp
@@ -0,0 +1,638 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Effects_Buffer.h"
+
+#include <string.h>
+
+/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const fixed_shift = 12;
+#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift))
+#define FROM_FIXED( f ) ((f) >> fixed_shift)
+
+int const max_read = 2560; // determines minimum delay
+
+Effects_Buffer::Effects_Buffer( int max_bufs, long echo_size_ ) : Multi_Buffer( stereo )
+{
+ echo_size = (int)max( max_read * (long) stereo, echo_size_ & ~1 );
+ clock_rate_ = 0;
+ bass_freq_ = 90;
+ bufs = 0;
+ bufs_size = 0;
+ bufs_max = max( max_bufs, (int) extra_chans );
+ no_echo = true;
+ no_effects = true;
+
+ // defaults
+ config_.enabled = false;
+ config_.delay [0] = 120;
+ config_.delay [1] = 122;
+ config_.feedback = 0.2f;
+ config_.treble = 0.4f;
+
+ static float const sep = 0.8f;
+ config_.side_chans [0].pan = -sep;
+ config_.side_chans [1].pan = +sep;
+ config_.side_chans [0].vol = 1.0f;
+ config_.side_chans [1].vol = 1.0f;
+
+ memset( &s, 0, sizeof s );
+ clear();
+}
+
+Effects_Buffer::~Effects_Buffer()
+{
+ delete_bufs();
+}
+
+// avoid using new []
+blargg_err_t Effects_Buffer::new_bufs( int size )
+{
+ bufs = (buf_t*) malloc( size * sizeof *bufs );
+ CHECK_ALLOC( bufs );
+ for ( int i = 0; i < size; i++ )
+ new (bufs + i) buf_t;
+ bufs_size = size;
+ return 0;
+}
+
+void Effects_Buffer::delete_bufs()
+{
+ if ( bufs )
+ {
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].~buf_t();
+ free( bufs );
+ bufs = 0;
+ }
+ bufs_size = 0;
+}
+
+blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec )
+{
+ // extra to allow farther past-the-end pointers
+ mixer.samples_read = 0;
+ RETURN_ERR( echo.resize( echo_size + stereo ) );
+ return Multi_Buffer::set_sample_rate( rate, msec );
+}
+
+void Effects_Buffer::clock_rate( long rate )
+{
+ clock_rate_ = rate;
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].clock_rate( clock_rate_ );
+}
+
+void Effects_Buffer::bass_freq( int freq )
+{
+ bass_freq_ = freq;
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].bass_freq( bass_freq_ );
+}
+
+blargg_err_t Effects_Buffer::set_channel_count( int count, int const* types )
+{
+ RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) );
+
+ delete_bufs();
+
+ mixer.samples_read = 0;
+
+ RETURN_ERR( chans.resize( count + extra_chans ) );
+
+ RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) );
+
+ for ( int i = bufs_size; --i >= 0; )
+ RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) );
+
+ for ( int i = (int)chans.size(); --i >= 0; )
+ {
+ chan_t& ch = chans [i];
+ ch.cfg.vol = 1.0f;
+ ch.cfg.pan = 0.0f;
+ ch.cfg.surround = false;
+ ch.cfg.echo = false;
+ }
+ // side channels with echo
+ chans [2].cfg.echo = true;
+ chans [3].cfg.echo = true;
+
+ clock_rate( clock_rate_ );
+ bass_freq( bass_freq_ );
+ apply_config();
+ clear();
+
+ return 0;
+}
+
+void Effects_Buffer::clear_echo()
+{
+ if ( echo.size() )
+ memset( echo.begin(), 0, echo.size() * sizeof echo [0] );
+}
+
+void Effects_Buffer::clear()
+{
+ echo_pos = 0;
+ s.low_pass [0] = 0;
+ s.low_pass [1] = 0;
+ mixer.samples_read = 0;
+
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].clear();
+ clear_echo();
+}
+
+Effects_Buffer::channel_t Effects_Buffer::channel( int i )
+{
+ i += extra_chans;
+ require( extra_chans <= i && i < (int) chans.size() );
+ return chans [i].channel;
+}
+
+
+// Configuration
+
+// 3 wave positions with/without surround, 2 multi (one with same config as wave)
+int const simple_bufs = 3 * 2 + 2 - 1;
+
+Simple_Effects_Buffer::Simple_Effects_Buffer() :
+ Effects_Buffer( extra_chans + simple_bufs, 18 * 1024L )
+{
+ config_.echo = 0.20f;
+ config_.stereo = 0.20f;
+ config_.surround = true;
+ config_.enabled = false;
+}
+
+void Simple_Effects_Buffer::apply_config()
+{
+ Effects_Buffer::config_t& c = Effects_Buffer::config();
+
+ c.enabled = config_.enabled;
+ if ( c.enabled )
+ {
+ c.delay [0] = 120;
+ c.delay [1] = 122;
+ c.feedback = config_.echo * 0.7f;
+ c.treble = 0.6f - 0.3f * config_.echo;
+
+ float sep = config_.stereo + 0.80f;
+ if ( sep > 1.0f )
+ sep = 1.0f;
+
+ c.side_chans [0].pan = -sep;
+ c.side_chans [1].pan = +sep;
+
+ for ( int i = channel_count(); --i >= 0; )
+ {
+ chan_config_t& ch = Effects_Buffer::chan_config( i );
+
+ ch.pan = 0.0f;
+ ch.surround = config_.surround;
+ ch.echo = false;
+
+ int const type = (channel_types() ? channel_types() [i] : 0);
+ if ( !(type & noise_type) )
+ {
+ int index = (type & type_index_mask) % 6 - 3;
+ if ( index < 0 )
+ {
+ index += 3;
+ ch.surround = false;
+ ch.echo = true;
+ }
+ if ( index >= 1 )
+ {
+ ch.pan = config_.stereo;
+ if ( index == 1 )
+ ch.pan = -ch.pan;
+ }
+ }
+ else if ( type & 1 )
+ {
+ ch.surround = false;
+ }
+ }
+ }
+
+ Effects_Buffer::apply_config();
+}
+
+int Effects_Buffer::min_delay() const
+{
+ require( sample_rate() );
+ return max_read * 1000L / sample_rate();
+}
+
+int Effects_Buffer::max_delay() const
+{
+ require( sample_rate() );
+ return (echo_size / stereo - max_read) * 1000L / sample_rate();
+}
+
+void Effects_Buffer::apply_config()
+{
+ int i;
+
+ if ( !bufs_size )
+ return;
+
+ s.treble = TO_FIXED( config_.treble );
+
+ bool echo_dirty = false;
+
+ fixed_t old_feedback = s.feedback;
+ s.feedback = TO_FIXED( config_.feedback );
+ if ( !old_feedback && s.feedback )
+ echo_dirty = true;
+
+ // delays
+ for ( i = stereo; --i >= 0; )
+ {
+ long delay = config_.delay [i] * sample_rate() / 1000 * stereo;
+ delay = max( delay, long (max_read * stereo) );
+ delay = min( delay, long (echo_size - max_read * stereo) );
+ if ( s.delay [i] != delay )
+ {
+ s.delay [i] = delay;
+ echo_dirty = true;
+ }
+ }
+
+ // side channels
+ for ( i = 2; --i >= 0; )
+ {
+ chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f;
+ chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan;
+ }
+
+ // convert volumes
+ for ( i = (int)chans.size(); --i >= 0; )
+ {
+ chan_t& ch = chans [i];
+ ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan );
+ ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan );
+ if ( ch.cfg.surround )
+ ch.vol [0] = -ch.vol [0];
+ }
+
+ assign_buffers();
+
+ // set side channels
+ for ( i = (int)chans.size(); --i >= 0; )
+ {
+ chan_t& ch = chans [i];
+ ch.channel.left = chans [ch.cfg.echo*2 ].channel.center;
+ ch.channel.right = chans [ch.cfg.echo*2+1].channel.center;
+ }
+
+ bool old_echo = !no_echo && !no_effects;
+
+ // determine whether effects and echo are needed at all
+ no_effects = true;
+ no_echo = true;
+ for ( i = (int)chans.size(); --i >= extra_chans; )
+ {
+ chan_t& ch = chans [i];
+ if ( ch.cfg.echo && s.feedback )
+ no_echo = false;
+
+ if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) )
+ no_effects = false;
+ }
+ if ( !no_echo )
+ no_effects = false;
+
+ if ( chans [0].vol [0] != TO_FIXED( 1 ) ||
+ chans [0].vol [1] != TO_FIXED( 0 ) ||
+ chans [1].vol [0] != TO_FIXED( 0 ) ||
+ chans [1].vol [1] != TO_FIXED( 1 ) )
+ no_effects = false;
+
+ if ( !config_.enabled )
+ no_effects = true;
+
+ if ( no_effects )
+ {
+ for ( i = (int)chans.size(); --i >= 0; )
+ {
+ chan_t& ch = chans [i];
+ ch.channel.center = &bufs [2];
+ ch.channel.left = &bufs [0];
+ ch.channel.right = &bufs [1];
+ }
+ }
+
+ mixer.bufs [0] = &bufs [0];
+ mixer.bufs [1] = &bufs [1];
+ mixer.bufs [2] = &bufs [2];
+
+ if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) )
+ clear_echo();
+
+ channels_changed();
+}
+
+void Effects_Buffer::assign_buffers()
+{
+ // assign channels to buffers
+ int buf_count = 0;
+ for ( int i = 0; i < (int) chans.size(); i++ )
+ {
+ // put second two side channels at end to give priority to main channels
+ // in case closest matching is necessary
+ int x = i;
+ if ( i > 1 )
+ x += 2;
+ if ( x >= (int) chans.size() )
+ x -= (chans.size() - 2);
+ chan_t& ch = chans [x];
+
+ int b = 0;
+ for ( ; b < buf_count; b++ )
+ {
+ if ( ch.vol [0] == bufs [b].vol [0] &&
+ ch.vol [1] == bufs [b].vol [1] &&
+ (ch.cfg.echo == bufs [b].echo || !s.feedback) )
+ break;
+ }
+
+ if ( b >= buf_count )
+ {
+ if ( buf_count < bufs_max )
+ {
+ bufs [b].vol [0] = ch.vol [0];
+ bufs [b].vol [1] = ch.vol [1];
+ bufs [b].echo = ch.cfg.echo;
+ buf_count++;
+ }
+ else
+ {
+ // TODO: this is a mess, needs refinement
+ dprintf( "Effects_Buffer ran out of buffers; using closest match\n" );
+ b = 0;
+ fixed_t best_dist = TO_FIXED( 8 );
+ for ( int h = buf_count; --h >= 0; )
+ {
+ #define CALC_LEVELS( vols, sum, diff, surround ) \
+ fixed_t sum, diff;\
+ bool surround = false;\
+ {\
+ fixed_t vol_0 = vols [0];\
+ if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\
+ fixed_t vol_1 = vols [1];\
+ if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\
+ sum = vol_0 + vol_1;\
+ diff = vol_0 - vol_1;\
+ }
+ CALC_LEVELS( ch.vol, ch_sum, ch_diff, ch_surround );
+ CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround );
+
+ fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff );
+
+ if ( ch_surround != buf_surround )
+ dist += TO_FIXED( 1 ) / 2;
+
+ if ( s.feedback && ch.cfg.echo != bufs [h].echo )
+ dist += TO_FIXED( 1 ) / 2;
+
+ if ( best_dist > dist )
+ {
+ best_dist = dist;
+ b = h;
+ }
+ }
+ }
+ }
+
+ //dprintf( "ch %d->buf %d\n", x, b );
+ ch.channel.center = &bufs [b];
+ }
+}
+
+
+// Mixing
+
+void Effects_Buffer::end_frame( blip_time_t time )
+{
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].end_frame( time );
+}
+
+long Effects_Buffer::read_samples( blip_sample_t* out, long out_size )
+{
+ out_size = min( out_size, samples_avail() );
+
+ int pair_count = int (out_size >> 1);
+ require( pair_count * stereo == out_size ); // must read an even number of samples
+ if ( pair_count )
+ {
+ if ( no_effects )
+ {
+ mixer.read_pairs( out, pair_count );
+ }
+ else
+ {
+ int pairs_remain = pair_count;
+ do
+ {
+ // mix at most max_read pairs at a time
+ int count = max_read;
+ if ( count > pairs_remain )
+ count = pairs_remain;
+
+ if ( no_echo )
+ {
+ // optimization: clear echo here to keep mix_effects() a leaf function
+ echo_pos = 0;
+ memset( echo.begin(), 0, count * stereo * sizeof echo [0] );
+ }
+ mix_effects( out, count );
+
+ blargg_long new_echo_pos = echo_pos + count * stereo;
+ if ( new_echo_pos >= echo_size )
+ new_echo_pos -= echo_size;
+ echo_pos = new_echo_pos;
+ assert( echo_pos < echo_size );
+
+ out += count * stereo;
+ mixer.samples_read += count;
+ pairs_remain -= count;
+ }
+ while ( pairs_remain );
+ }
+
+ if ( samples_avail() <= 0 || immediate_removal() )
+ {
+ for ( int i = bufs_size; --i >= 0; )
+ {
+ buf_t& b = bufs [i];
+ // TODO: might miss non-silence settling since it checks END of last read
+ if ( b.non_silent() )
+ b.remove_samples( mixer.samples_read );
+ else
+ b.remove_silence( mixer.samples_read );
+ }
+ mixer.samples_read = 0;
+ }
+ }
+ return out_size;
+}
+
+void Effects_Buffer::mix_effects( blip_sample_t* out_, int pair_count )
+{
+ typedef fixed_t stereo_fixed_t [stereo];
+
+ // add channels with echo, do echo, add channels without echo, then convert to 16-bit and output
+ int echo_phase = 1;
+ do
+ {
+ // mix any modified buffers
+ {
+ buf_t* buf = bufs;
+ int bufs_remain = bufs_size;
+ do
+ {
+ if ( buf->non_silent() && ( buf->echo == !!echo_phase ) )
+ {
+ stereo_fixed_t* BLIP_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos];
+ int const bass = BLIP_READER_BASS( *buf );
+ BLIP_READER_BEGIN( in, *buf );
+ BLIP_READER_ADJ_( in, mixer.samples_read );
+ fixed_t const vol_0 = buf->vol [0];
+ fixed_t const vol_1 = buf->vol [1];
+
+ int count = unsigned (echo_size - echo_pos) / stereo;
+ int remain = pair_count;
+ if ( count > remain )
+ count = remain;
+ do
+ {
+ remain -= count;
+ BLIP_READER_ADJ_( in, count );
+
+ out += count;
+ int offset = -count;
+ do
+ {
+ fixed_t s = BLIP_READER_READ( in );
+ BLIP_READER_NEXT_IDX_( in, bass, offset );
+
+ out [offset] [0] += s * vol_0;
+ out [offset] [1] += s * vol_1;
+ }
+ while ( ++offset );
+
+ out = (stereo_fixed_t*) echo.begin();
+ count = remain;
+ }
+ while ( remain );
+
+ BLIP_READER_END( in, *buf );
+ }
+ buf++;
+ }
+ while ( --bufs_remain );
+ }
+
+ // add echo
+ if ( echo_phase && !no_echo )
+ {
+ fixed_t const feedback = s.feedback;
+ fixed_t const treble = s.treble;
+
+ int i = 1;
+ do
+ {
+ fixed_t low_pass = s.low_pass [i];
+
+ fixed_t* echo_end = &echo [echo_size + i];
+ fixed_t const* BLIP_RESTRICT in_pos = &echo [echo_pos + i];
+ blargg_long out_offset = (int)(echo_pos + i + s.delay [i]);
+ if ( out_offset >= echo_size )
+ out_offset -= echo_size;
+ assert( out_offset < echo_size );
+ fixed_t* BLIP_RESTRICT out_pos = &echo [out_offset];
+
+ // break into up to three chunks to avoid having to handle wrap-around
+ // in middle of core loop
+ int remain = pair_count;
+ do
+ {
+ fixed_t const* pos = in_pos;
+ if ( pos < out_pos )
+ pos = out_pos;
+ int count = blargg_ulong ((char*) echo_end - (char const*) pos) /
+ unsigned (stereo * sizeof (fixed_t));
+ if ( count > remain )
+ count = remain;
+ remain -= count;
+
+ in_pos += count * stereo;
+ out_pos += count * stereo;
+ int offset = -count;
+ do
+ {
+ low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble;
+ out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback;
+ }
+ while ( ++offset );
+
+ if ( in_pos >= echo_end ) in_pos -= echo_size;
+ if ( out_pos >= echo_end ) out_pos -= echo_size;
+ }
+ while ( remain );
+
+ s.low_pass [i] = low_pass;
+ }
+ while ( --i >= 0 );
+ }
+ }
+ while ( --echo_phase >= 0 );
+
+ // clamp to 16 bits
+ {
+ stereo_fixed_t const* BLIP_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos];
+ typedef blip_sample_t stereo_blip_sample_t [stereo];
+ stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_;
+ int count = unsigned (echo_size - echo_pos) / (unsigned) stereo;
+ int remain = pair_count;
+ if ( count > remain )
+ count = remain;
+ do
+ {
+ remain -= count;
+ in += count;
+ out += count;
+ int offset = -count;
+ do
+ {
+ fixed_t in_0 = FROM_FIXED( in [offset] [0] );
+ fixed_t in_1 = FROM_FIXED( in [offset] [1] );
+
+ BLIP_CLAMP( in_0, in_0 );
+ out [offset] [0] = (blip_sample_t) in_0;
+
+ BLIP_CLAMP( in_1, in_1 );
+ out [offset] [1] = (blip_sample_t) in_1;
+ }
+ while ( ++offset );
+
+ in = (stereo_fixed_t*) echo.begin();
+ count = remain;
+ }
+ while ( remain );
+ }
+}
diff --git a/gearboy/src/audio/Effects_Buffer.h b/gearboy/src/audio/Effects_Buffer.h
new file mode 100644
index 00000000..790325d9
--- /dev/null
+++ b/gearboy/src/audio/Effects_Buffer.h
@@ -0,0 +1,143 @@
+// Multi-channel effects buffer with echo and individual panning for each channel
+
+// Game_Music_Emu $vers
+#ifndef EFFECTS_BUFFER_H
+#define EFFECTS_BUFFER_H
+
+#include "Multi_Buffer.h"
+
+// See Simple_Effects_Buffer (below) for a simpler interface
+
+class Effects_Buffer : public Multi_Buffer {
+public:
+ // To reduce memory usage, fewer buffers can be used (with a best-fit
+ // approach if there are too few), and maximum echo delay can be reduced
+ Effects_Buffer( int max_bufs = 32, long echo_size = 24 * 1024L );
+
+ struct pan_vol_t
+ {
+ float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal
+ float pan; // -1.0 = left, 0.0 = center, +1.0 = right
+ };
+
+ // Global configuration
+ struct config_t
+ {
+ bool enabled; // false = disable all effects
+
+ // Current sound is echoed at adjustable left/right delay,
+ // with reduced treble and volume (feedback).
+ float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent
+ int delay [2]; // left, right delays (msec)
+ float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony
+ pan_vol_t side_chans [2]; // left and right side channel volume and pan
+ };
+ config_t& config() { return config_; }
+
+ // Limits of delay (msec)
+ int min_delay() const;
+ int max_delay() const;
+
+ // Per-channel configuration. Two or more channels with matching parameters are
+ // optimized to internally use the same buffer.
+ struct chan_config_t : pan_vol_t
+ {
+ // (inherited from pan_vol_t)
+ //float vol; // these only affect center channel
+ //float pan;
+ bool surround; // if true, negates left volume to put sound in back
+ bool echo; // false = channel doesn't have any echo
+ };
+ chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; }
+
+ // Apply any changes made to config() and chan_config()
+ virtual void apply_config();
+
+public:
+ ~Effects_Buffer();
+ blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length );
+ blargg_err_t set_channel_count( int, int const* = 0 );
+ void clock_rate( long );
+ void bass_freq( int );
+ void clear();
+ channel_t channel( int );
+ void end_frame( blip_time_t );
+ long read_samples( blip_sample_t*, long );
+ long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; }
+ enum { stereo = 2 };
+ typedef blargg_long fixed_t;
+protected:
+ enum { extra_chans = stereo * stereo };
+private:
+ config_t config_;
+ long clock_rate_;
+ int bass_freq_;
+
+ blargg_long echo_size;
+
+ struct chan_t
+ {
+ fixed_t vol [stereo];
+ chan_config_t cfg;
+ channel_t channel;
+ };
+ blargg_vector<chan_t> chans;
+
+ struct buf_t : Tracked_Blip_Buffer
+ {
+ fixed_t vol [stereo];
+ bool echo;
+
+ void* operator new ( size_t, void* p ) { return p; }
+ void operator delete ( void* ) { }
+
+ ~buf_t() { }
+ };
+ buf_t* bufs;
+ int bufs_size;
+ int bufs_max; // bufs_size <= bufs_max, to limit memory usage
+ Stereo_Mixer mixer;
+
+ struct {
+ long delay [stereo];
+ fixed_t treble;
+ fixed_t feedback;
+ fixed_t low_pass [stereo];
+ } s;
+
+ blargg_vector<fixed_t> echo;
+ blargg_long echo_pos;
+
+ bool no_effects;
+ bool no_echo;
+
+ void assign_buffers();
+ void clear_echo();
+ void mix_effects( blip_sample_t* out, int pair_count );
+ blargg_err_t new_bufs( int size );
+ void delete_bufs();
+};
+
+// Simpler interface and lower memory usage
+class Simple_Effects_Buffer : public Effects_Buffer {
+public:
+ struct config_t
+ {
+ bool enabled; // false = disable all effects
+ float echo; // 0.0 = none, 1.0 = lots
+ float stereo; // 0.0 = channels in center, 1.0 = channels on left/right
+ bool surround; // true = put some channels in back
+ };
+ config_t& config() { return config_; }
+
+ // Apply any changes made to config()
+ void apply_config();
+
+public:
+ Simple_Effects_Buffer();
+private:
+ config_t config_;
+ void chan_config(); // hide
+};
+
+#endif
diff --git a/gearboy/src/audio/Gb_Apu.cpp b/gearboy/src/audio/Gb_Apu.cpp
new file mode 100644
index 00000000..77c004d7
--- /dev/null
+++ b/gearboy/src/audio/Gb_Apu.cpp
@@ -0,0 +1,395 @@
+// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/
+
+#include "Gb_Apu.h"
+
+/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+unsigned const vol_reg = 0xFF24;
+unsigned const stereo_reg = 0xFF25;
+unsigned const status_reg = 0xFF26;
+unsigned const wave_ram = 0xFF30;
+
+int const power_mask = 0x80;
+
+void Gb_Apu::treble_eq( blip_eq_t const& eq )
+{
+ good_synth.treble_eq( eq );
+ med_synth .treble_eq( eq );
+}
+
+inline int Gb_Apu::calc_output( int osc ) const
+{
+ int bits = regs [stereo_reg - start_addr] >> osc;
+ return (bits >> 3 & 2) | (bits & 1);
+}
+
+void Gb_Apu::set_output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right, int osc )
+{
+ // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+ require( !center || (center && !left && !right) || (center && left && right) );
+ require( (unsigned) osc <= osc_count ); // fails if you pass invalid osc index
+
+ if ( !center || !left || !right )
+ {
+ left = center;
+ right = center;
+ }
+
+ int i = (unsigned) osc % osc_count;
+ do
+ {
+ Gb_Osc& o = *oscs [i];
+ o.outputs [1] = right;
+ o.outputs [2] = left;
+ o.outputs [3] = center;
+ o.output = o.outputs [calc_output( i )];
+ }
+ while ( ++i < osc );
+}
+
+void Gb_Apu::synth_volume( int iv )
+{
+ double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv;
+ good_synth.volume( v );
+ med_synth .volume( v );
+}
+
+void Gb_Apu::apply_volume()
+{
+ // TODO: Doesn't handle differing left and right volumes (panning).
+ // Not worth the complexity.
+ int data = regs [vol_reg - start_addr];
+ int left = data >> 4 & 7;
+ int right = data & 7;
+ //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 );
+ //if ( left != right ) dprintf( "l: %d r: %d\n", left, right );
+ synth_volume( max( left, right ) + 1 );
+}
+
+void Gb_Apu::volume( double v )
+{
+ if ( volume_ != v )
+ {
+ volume_ = v;
+ apply_volume();
+ }
+}
+
+void Gb_Apu::reset_regs()
+{
+ for ( int i = 0; i < 0x20; i++ )
+ regs [i] = 0;
+
+ square1.reset();
+ square2.reset();
+ wave .reset();
+ noise .reset();
+
+ apply_volume();
+}
+
+void Gb_Apu::reset_lengths()
+{
+ square1.length_ctr = 64;
+ square2.length_ctr = 64;
+ wave .length_ctr = 256;
+ noise .length_ctr = 64;
+}
+
+void Gb_Apu::reduce_clicks( bool reduce )
+{
+ reduce_clicks_ = reduce;
+
+ // Click reduction makes DAC off generate same output as volume 0
+ int dac_off_amp = 0;
+ if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks
+ dac_off_amp = -Gb_Osc::dac_bias;
+
+ for ( int i = 0; i < osc_count; i++ )
+ oscs [i]->dac_off_amp = dac_off_amp;
+
+ // AGB always eliminates clicks on wave channel using same method
+ if ( wave.mode == mode_agb )
+ wave.dac_off_amp = -Gb_Osc::dac_bias;
+}
+
+void Gb_Apu::reset( mode_t mode, bool agb_wave )
+{
+ // Hardware mode
+ if ( agb_wave )
+ mode = mode_agb; // using AGB wave features implies AGB hardware
+ wave.agb_mask = agb_wave ? 0xFF : 0;
+ for ( int i = 0; i < osc_count; i++ )
+ oscs [i]->mode = mode;
+ reduce_clicks( reduce_clicks_ );
+
+ // Reset state
+ frame_time = 0;
+ last_time = 0;
+ frame_phase = 0;
+
+ reset_regs();
+ reset_lengths();
+
+ // Load initial wave RAM
+ static byte const initial_wave [2] [16] = {
+ {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA},
+ {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF},
+ };
+ for ( int b = 2; --b >= 0; )
+ {
+ // Init both banks (does nothing if not in AGB mode)
+ // TODO: verify that this works
+ write_register( 0, 0xFF1A, b * 0x40 );
+ for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ )
+ write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] );
+ }
+}
+
+void Gb_Apu::set_tempo( double t )
+{
+ frame_period = 4194304 / 512; // 512 Hz
+ if ( t != 1.0 )
+ frame_period = blip_time_t (frame_period / t);
+}
+
+Gb_Apu::Gb_Apu()
+{
+ wave.wave_ram = &regs [wave_ram - start_addr];
+
+ oscs [0] = &square1;
+ oscs [1] = &square2;
+ oscs [2] = &wave;
+ oscs [3] = &noise;
+
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Gb_Osc& o = *oscs [i];
+ o.regs = &regs [i * 5];
+ o.output = 0;
+ o.outputs [0] = 0;
+ o.outputs [1] = 0;
+ o.outputs [2] = 0;
+ o.outputs [3] = 0;
+ o.good_synth = &good_synth;
+ o.med_synth = &med_synth;
+ }
+
+ reduce_clicks_ = false;
+ set_tempo( 1.0 );
+ volume_ = 1.0;
+ reset();
+}
+
+void Gb_Apu::run_until_( blip_time_t end_time )
+{
+ while ( true )
+ {
+ // run oscillators
+ blip_time_t time = end_time;
+ if ( time > frame_time )
+ time = frame_time;
+
+ square1.run( last_time, time );
+ square2.run( last_time, time );
+ wave .run( last_time, time );
+ noise .run( last_time, time );
+ last_time = time;
+
+ if ( time == end_time )
+ break;
+
+ // run frame sequencer
+ frame_time += frame_period * Gb_Osc::clk_mul;
+ switch ( frame_phase++ )
+ {
+ case 2:
+ case 6:
+ // 128 Hz
+ square1.clock_sweep();
+ /* fall through */
+ case 0:
+ case 4:
+ // 256 Hz
+ square1.clock_length();
+ square2.clock_length();
+ wave .clock_length();
+ noise .clock_length();
+ break;
+
+ case 7:
+ // 64 Hz
+ frame_phase = 0;
+ square1.clock_envelope();
+ square2.clock_envelope();
+ noise .clock_envelope();
+ }
+ }
+}
+
+inline void Gb_Apu::run_until( blip_time_t time )
+{
+ //require( time >= last_time ); // end_time must not be before previous time
+ if ( time > last_time )
+ run_until_( time );
+}
+
+void Gb_Apu::end_frame( blip_time_t end_time )
+{
+ if ( end_time > last_time )
+ run_until( end_time );
+
+ frame_time -= end_time;
+ assert( frame_time >= 0 );
+
+ last_time -= end_time;
+ assert( last_time >= 0 );
+}
+
+void Gb_Apu::silence_osc( Gb_Osc& o )
+{
+ int delta = -o.last_amp;
+ if ( delta )
+ {
+ o.last_amp = 0;
+ if ( o.output )
+ {
+ o.output->set_modified();
+ med_synth.offset( last_time, delta, o.output );
+ }
+ }
+}
+
+void Gb_Apu::apply_stereo()
+{
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Gb_Osc& o = *oscs [i];
+ Blip_Buffer* out = o.outputs [calc_output( i )];
+ if ( o.output != out )
+ {
+ silence_osc( o );
+ o.output = out;
+ }
+ }
+}
+
+void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
+{
+ require( (unsigned) data < 0x100 );
+
+ int reg = addr - start_addr;
+ if ( (unsigned) reg >= register_count )
+ {
+ require( false );
+ return;
+ }
+
+ if ( addr < status_reg && !(regs [status_reg - start_addr] & power_mask) )
+ {
+ // Power is off
+
+ // length counters can only be written in DMG mode
+ if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) )
+ return;
+
+ if ( reg < 10 )
+ data &= 0x3F; // clear square duty
+ }
+
+ run_until( time );
+
+ if ( addr >= wave_ram )
+ {
+ wave.write( addr, data );
+ }
+ else
+ {
+ int old_data = regs [reg];
+ regs [reg] = data;
+
+ if ( addr < vol_reg )
+ {
+ // Oscillator
+ write_osc( reg / 5, reg, old_data, data );
+ }
+ else if ( addr == vol_reg && data != old_data )
+ {
+ // Master volume
+ for ( int i = osc_count; --i >= 0; )
+ silence_osc( *oscs [i] );
+
+ apply_volume();
+ }
+ else if ( addr == stereo_reg )
+ {
+ // Stereo panning
+ apply_stereo();
+ }
+ else if ( addr == status_reg && (data ^ old_data) & power_mask )
+ {
+ // Power control
+ frame_phase = 0;
+ for ( int i = osc_count; --i >= 0; )
+ silence_osc( *oscs [i] );
+
+ reset_regs();
+ if ( wave.mode != mode_dmg )
+ reset_lengths();
+
+ regs [status_reg - start_addr] = data;
+ }
+ }
+}
+
+int Gb_Apu::read_register( blip_time_t time, unsigned addr )
+{
+ run_until( time );
+
+ int reg = addr - start_addr;
+ if ( (unsigned) reg >= register_count )
+ {
+ require( false );
+ return 0;
+ }
+
+ if ( addr >= wave_ram )
+ return wave.read( addr );
+
+ // Value read back has some bits always set
+ static byte const masks [] = {
+ 0x80,0x3F,0x00,0xFF,0xBF,
+ 0xFF,0x3F,0x00,0xFF,0xBF,
+ 0x7F,0xFF,0x9F,0xFF,0xBF,
+ 0xFF,0xFF,0x00,0x00,0xBF,
+ 0x00,0x00,0x70,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
+ };
+ int mask = masks [reg];
+ if ( wave.agb_mask && (reg == 10 || reg == 12) )
+ mask = 0x1F; // extra implemented bits in wave regs on AGB
+ int data = regs [reg] | mask;
+
+ // Status register
+ if ( addr == status_reg )
+ {
+ data &= 0xF0;
+ data |= (int) square1.enabled << 0;
+ data |= (int) square2.enabled << 1;
+ data |= (int) wave .enabled << 2;
+ data |= (int) noise .enabled << 3;
+ }
+
+ return data;
+}
diff --git a/gearboy/src/audio/Gb_Apu.h b/gearboy/src/audio/Gb_Apu.h
new file mode 100644
index 00000000..3f132096
--- /dev/null
+++ b/gearboy/src/audio/Gb_Apu.h
@@ -0,0 +1,182 @@
+// Nintendo Game Boy sound hardware emulator with save state support
+
+// Gb_Snd_Emu 0.2.0
+#ifndef GB_APU_H
+#define GB_APU_H
+
+#include "Gb_Oscs.h"
+
+struct gb_apu_state_t;
+
+class Gb_Apu {
+public:
+// Basics
+
+ // Clock rate that sound hardware runs at.
+ enum { clock_rate = 4194304 * GB_APU_OVERCLOCK };
+
+ // Sets buffer(s) to generate sound into. If left and right are NULL, output is mono.
+ // If all are NULL, no output is generated but other emulation still runs.
+ // If chan is specified, only that channel's output is changed, otherwise all are.
+ enum { osc_count = 4 }; // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise
+ void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL,
+ int chan = osc_count );
+
+ // Resets hardware to initial power on state BEFORE boot ROM runs. Mode selects
+ // sound hardware. Additional AGB wave features are enabled separately.
+ enum mode_t {
+ mode_dmg, // Game Boy monochrome
+ mode_cgb, // Game Boy Color
+ mode_agb // Game Boy Advance
+ };
+ void reset( mode_t mode = mode_cgb, bool agb_wave = false );
+
+ // Reads and writes must be within the start_addr to end_addr range, inclusive.
+ // Addresses outside this range are not mapped to the sound hardware.
+ enum { start_addr = 0xFF10 };
+ enum { end_addr = 0xFF3F };
+ enum { register_count = end_addr - start_addr + 1 };
+
+ // Times are specified as the number of clocks since the beginning of the
+ // current time frame.
+
+ // Emulates CPU write of data to addr at specified time.
+ void write_register( blip_time_t time, unsigned addr, int data );
+
+ // Emulates CPU read from addr at specified time.
+ int read_register( blip_time_t time, unsigned addr );
+
+ // Emulates sound hardware up to specified time, ends current time frame, then
+ // starts a new frame at time 0.
+ void end_frame( blip_time_t frame_length );
+
+// Sound adjustments
+
+ // Sets overall volume, where 1.0 is normal.
+ void volume( double );
+
+ // If true, reduces clicking by disabling DAC biasing. Note that this reduces
+ // emulation accuracy, since the clicks are authentic.
+ void reduce_clicks( bool reduce = true );
+
+ // Sets treble equalization.
+ void treble_eq( blip_eq_t const& );
+
+ // Treble and bass values for various hardware.
+ enum {
+ speaker_treble = -47, // speaker on system
+ speaker_bass = 2000,
+ dmg_treble = 0, // headphones on each system
+ dmg_bass = 30,
+ cgb_treble = 0,
+ cgb_bass = 300, // CGB has much less bass
+ agb_treble = 0,
+ agb_bass = 30
+ };
+
+ // Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the
+ // tempo in a game music player.
+ void set_tempo( double );
+
+// Save states
+
+ // Saves full emulation state to state_out. Data format is portable and
+ // includes some extra space to avoid expansion in case more state needs
+ // to be stored in the future.
+ void save_state( gb_apu_state_t* state_out );
+
+ // Loads state. You should call reset() BEFORE this.
+ blargg_err_t load_state( gb_apu_state_t const& in );
+
+public:
+ Gb_Apu();
+
+ // Use set_output() in place of these
+ BLARGG_DEPRECATED void output ( Blip_Buffer* c ) { set_output( c, c, c ); }
+ BLARGG_DEPRECATED void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); }
+ BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c ) { set_output( c, c, c, i ); }
+ BLARGG_DEPRECATED void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r, i ); }
+
+private:
+ // noncopyable
+ Gb_Apu( const Gb_Apu& );
+ Gb_Apu& operator = ( const Gb_Apu& );
+
+ Gb_Osc* oscs [osc_count];
+ blip_time_t last_time; // time sound emulator has been run to
+ blip_time_t frame_period; // clocks between each frame sequencer step
+ double volume_;
+ bool reduce_clicks_;
+
+ Gb_Sweep_Square square1;
+ Gb_Square square2;
+ Gb_Wave wave;
+ Gb_Noise noise;
+ blip_time_t frame_time; // time of next frame sequencer action
+ int frame_phase; // phase of next frame sequencer step
+ enum { regs_size = register_count + 0x10 };
+ BOOST::uint8_t regs [regs_size];// last values written to registers
+
+ // large objects after everything else
+ Gb_Osc::Good_Synth good_synth;
+ Gb_Osc::Med_Synth med_synth;
+
+ void reset_lengths();
+ void reset_regs();
+ int calc_output( int osc ) const;
+ void apply_stereo();
+ void apply_volume();
+ void synth_volume( int );
+ void run_until_( blip_time_t );
+ void run_until( blip_time_t );
+ void silence_osc( Gb_Osc& );
+ void write_osc( int index, int reg, int old_data, int data );
+ const char* save_load( gb_apu_state_t*, bool save );
+ void save_load2( gb_apu_state_t*, bool save );
+ friend class Gb_Apu_Tester;
+};
+
+// Format of save state. Should be stable across versions of the library,
+// with earlier versions properly opening later save states. Includes some
+// room for expansion so the state size shouldn't increase.
+struct gb_apu_state_t
+{
+#if GB_APU_CUSTOM_STATE
+ // Values stored as plain int so your code can read/write them easily.
+ // Structure can NOT be written to disk, since format is not portable.
+ typedef int val_t;
+#else
+ // Values written in portable little-endian format, allowing structure
+ // to be written directly to disk.
+ typedef unsigned char val_t [4];
+#endif
+
+ enum { format0 = 0x50414247 };
+
+ val_t format; // format of all following data
+ val_t version; // later versions just add fields to end
+
+ unsigned char regs [0x40];
+ val_t frame_time;
+ val_t frame_phase;
+
+ val_t sweep_freq;
+ val_t sweep_delay;
+ val_t sweep_enabled;
+ val_t sweep_neg;
+ val_t noise_divider;
+ val_t wave_buf;
+
+ val_t delay [4];
+ val_t length_ctr [4];
+ val_t phase [4];
+ val_t enabled [4];
+
+ val_t env_delay [3];
+ val_t env_volume [3];
+ val_t env_enabled [3];
+
+ val_t unused [13]; // for future expansion
+};
+
+#endif
diff --git a/gearboy/src/audio/Gb_Apu_State.cpp b/gearboy/src/audio/Gb_Apu_State.cpp
new file mode 100644
index 00000000..6a523460
--- /dev/null
+++ b/gearboy/src/audio/Gb_Apu_State.cpp
@@ -0,0 +1,119 @@
+// Gb_Snd_Emu $vers. http://www.slack.net/~ant/
+
+#include "Gb_Apu.h"
+
+#include <string.h>
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#if GB_APU_CUSTOM_STATE
+ #define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) )
+#else
+ #define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y )))
+
+ static blargg_ulong get_val( byte const* p )
+ {
+ return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0];
+ }
+
+ static void set_val( byte* p, blargg_ulong n )
+ {
+ p [0] = (byte) (n );
+ p [1] = (byte) (n >> 8);
+ p [2] = (byte) (n >> 16);
+ p [3] = (byte) (n >> 24);
+ }
+#endif
+
+inline const char* Gb_Apu::save_load( gb_apu_state_t* io, bool save )
+{
+ #if !GB_APU_CUSTOM_STATE
+ assert( sizeof (gb_apu_state_t) == 256 );
+ #endif
+
+ int format = io->format0;
+ REFLECT( format, format );
+ if ( format != io->format0 )
+ return "Unsupported sound save state format";
+
+ int version = 0;
+ REFLECT( version, version );
+
+ // Registers and wave RAM
+ assert( regs_size == sizeof io->regs );
+ if ( save )
+ memcpy( io->regs, regs, sizeof io->regs );
+ else
+ memcpy( regs, io->regs, sizeof regs );
+
+ // Frame sequencer
+ REFLECT( frame_time, frame_time );
+ REFLECT( frame_phase, frame_phase );
+
+ REFLECT( square1.sweep_freq, sweep_freq );
+ REFLECT( square1.sweep_delay, sweep_delay );
+ REFLECT( square1.sweep_enabled, sweep_enabled );
+ REFLECT( square1.sweep_neg, sweep_neg );
+
+ REFLECT( noise.divider, noise_divider );
+ REFLECT( wave.sample_buf, wave_buf );
+
+ return 0;
+}
+
+// second function to avoid inline limits of some compilers
+inline void Gb_Apu::save_load2( gb_apu_state_t* io, bool save )
+{
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Gb_Osc& osc = *oscs [i];
+ REFLECT( osc.delay, delay [i] );
+ REFLECT( osc.length_ctr, length_ctr [i] );
+ REFLECT( osc.phase, phase [i] );
+ REFLECT( osc.enabled, enabled [i] );
+
+ if ( i != 2 )
+ {
+ int j = min( i, 2 );
+ Gb_Env& env = STATIC_CAST(Gb_Env&,osc);
+ REFLECT( env.env_delay, env_delay [j] );
+ REFLECT( env.volume, env_volume [j] );
+ REFLECT( env.env_enabled, env_enabled [j] );
+ }
+ }
+}
+
+void Gb_Apu::save_state( gb_apu_state_t* out )
+{
+ (void) save_load( out, true );
+ save_load2( out, true );
+
+ #if !GB_APU_CUSTOM_STATE
+ memset( out->unused, 0, sizeof out->unused );
+ #endif
+}
+
+blargg_err_t Gb_Apu::load_state( gb_apu_state_t const& in )
+{
+ RETURN_ERR( save_load( CONST_CAST(gb_apu_state_t*,&in), false ) );
+ save_load2( CONST_CAST(gb_apu_state_t*,&in), false );
+
+ apply_stereo();
+ synth_volume( 0 ); // suppress output for the moment
+ run_until_( last_time ); // get last_amp updated
+ apply_volume(); // now use correct volume
+
+ return 0;
+}
+
diff --git a/gearboy/src/audio/Gb_Oscs.cpp b/gearboy/src/audio/Gb_Oscs.cpp
new file mode 100644
index 00000000..72ec8049
--- /dev/null
+++ b/gearboy/src/audio/Gb_Oscs.cpp
@@ -0,0 +1,665 @@
+// Gb_Snd_Emu 0.2.0. http://www.slack.net/~ant/
+
+#include "Gb_Apu.h"
+
+/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games
+bool const cgb_05 = false; // enables CGB-05 zombie behavior
+
+int const trigger_mask = 0x80;
+int const length_enabled = 0x40;
+
+void Gb_Osc::reset()
+{
+ output = 0;
+ last_amp = 0;
+ delay = 0;
+ phase = 0;
+ enabled = false;
+}
+
+inline void Gb_Osc::update_amp( blip_time_t time, int new_amp )
+{
+ output->set_modified();
+ int delta = new_amp - last_amp;
+ if ( delta )
+ {
+ last_amp = new_amp;
+ med_synth->offset( time, delta, output );
+ }
+}
+
+// Units
+
+void Gb_Osc::clock_length()
+{
+ if ( (regs [4] & length_enabled) && length_ctr )
+ {
+ if ( --length_ctr <= 0 )
+ enabled = false;
+ }
+}
+
+inline int Gb_Env::reload_env_timer()
+{
+ int raw = regs [2] & 7;
+ env_delay = (raw ? raw : 8);
+ return raw;
+}
+
+void Gb_Env::clock_envelope()
+{
+ if ( env_enabled && --env_delay <= 0 && reload_env_timer() )
+ {
+ int v = volume + (regs [2] & 0x08 ? +1 : -1);
+ if ( 0 <= v && v <= 15 )
+ volume = v;
+ else
+ env_enabled = false;
+ }
+}
+
+inline void Gb_Sweep_Square::reload_sweep_timer()
+{
+ sweep_delay = (regs [0] & period_mask) >> 4;
+ if ( !sweep_delay )
+ sweep_delay = 8;
+}
+
+void Gb_Sweep_Square::calc_sweep( bool update )
+{
+ int const shift = regs [0] & shift_mask;
+ int const delta = sweep_freq >> shift;
+ sweep_neg = (regs [0] & 0x08) != 0;
+ int const freq = sweep_freq + (sweep_neg ? -delta : delta);
+
+ if ( freq > 0x7FF )
+ {
+ enabled = false;
+ }
+ else if ( shift && update )
+ {
+ sweep_freq = freq;
+
+ regs [3] = freq & 0xFF;
+ regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07);
+ }
+}
+
+void Gb_Sweep_Square::clock_sweep()
+{
+ if ( --sweep_delay <= 0 )
+ {
+ reload_sweep_timer();
+ if ( sweep_enabled && (regs [0] & period_mask) )
+ {
+ calc_sweep( true );
+ calc_sweep( false );
+ }
+ }
+}
+
+int Gb_Wave::access( unsigned addr ) const
+{
+ if ( enabled && mode != Gb_Apu::mode_agb )
+ {
+ addr = phase & (bank_size - 1);
+ if ( mode == Gb_Apu::mode_dmg )
+ {
+ addr++;
+ if ( delay > clk_mul )
+ return -1; // can only access within narrow time window while playing
+ }
+ addr >>= 1;
+ }
+ return addr & 0x0F;
+}
+
+// write_register
+
+int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data )
+{
+ int data = regs [4];
+
+ if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr )
+ {
+ if ( (data & length_enabled) || cgb_02 )
+ length_ctr--;
+ }
+
+ if ( data & trigger_mask )
+ {
+ enabled = true;
+ if ( !length_ctr )
+ {
+ length_ctr = max_len;
+ if ( (frame_phase & 1) && (data & length_enabled) )
+ length_ctr--;
+ }
+ }
+
+ if ( !length_ctr )
+ enabled = false;
+
+ return data & trigger_mask;
+}
+
+inline void Gb_Env::zombie_volume( int old, int data )
+{
+ int v = volume;
+ if ( mode == Gb_Apu::mode_agb || cgb_05 )
+ {
+ // CGB-05 behavior, very close to AGB behavior as well
+ if ( (old ^ data) & 8 )
+ {
+ if ( !(old & 8) )
+ {
+ v++;
+ if ( old & 7 )
+ v++;
+ }
+
+ v = 16 - v;
+ }
+ else if ( (old & 0x0F) == 8 )
+ {
+ v++;
+ }
+ }
+ else
+ {
+ // CGB-04&02 behavior, very close to MGB behavior as well
+ if ( !(old & 7) && env_enabled )
+ v++;
+ else if ( !(old & 8) )
+ v += 2;
+
+ if ( (old ^ data) & 8 )
+ v = 16 - v;
+ }
+ volume = v & 0x0F;
+}
+
+bool Gb_Env::write_register( int frame_phase, int reg, int old, int data )
+{
+ int const max_len = 64;
+
+ switch ( reg )
+ {
+ case 1:
+ length_ctr = max_len - (data & (max_len - 1));
+ break;
+
+ case 2:
+ if ( !dac_enabled() )
+ enabled = false;
+
+ zombie_volume( old, data );
+
+ if ( (data & 7) && env_delay == 8 )
+ {
+ env_delay = 1;
+ clock_envelope(); // TODO: really happens at next length clock
+ }
+ break;
+
+ case 4:
+ if ( write_trig( frame_phase, max_len, old ) )
+ {
+ volume = regs [2] >> 4;
+ reload_env_timer();
+ env_enabled = true;
+ if ( frame_phase == 7 )
+ env_delay++;
+ if ( !dac_enabled() )
+ enabled = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data )
+{
+ bool result = Gb_Env::write_register( frame_phase, reg, old_data, data );
+ if ( result )
+ delay = (delay & (4 * clk_mul - 1)) + period();
+ return result;
+}
+
+inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data )
+{
+ if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) )
+ {
+ phase = 0x7FFF;
+ delay += 8 * clk_mul;
+ }
+}
+
+inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data )
+{
+ if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) )
+ enabled = false; // sweep negate disabled after used
+
+ if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) )
+ {
+ sweep_freq = frequency();
+ sweep_neg = false;
+ reload_sweep_timer();
+ sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0;
+ if ( regs [0] & shift_mask )
+ calc_sweep( false );
+ }
+}
+
+void Gb_Wave::corrupt_wave()
+{
+ int pos = ((phase + 1) & (bank_size - 1)) >> 1;
+ if ( pos < 4 )
+ wave_ram [0] = wave_ram [pos];
+ else
+ for ( int i = 4; --i >= 0; )
+ wave_ram [i] = wave_ram [(pos & ~3) + i];
+}
+
+inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data )
+{
+ int const max_len = 256;
+
+ switch ( reg )
+ {
+ case 0:
+ if ( !dac_enabled() )
+ enabled = false;
+ break;
+
+ case 1:
+ length_ctr = max_len - data;
+ break;
+
+ case 4:
+ bool was_enabled = enabled;
+ if ( write_trig( frame_phase, max_len, old_data ) )
+ {
+ if ( !dac_enabled() )
+ enabled = false;
+ else if ( mode == Gb_Apu::mode_dmg && was_enabled &&
+ (unsigned) (delay - 2 * clk_mul) < 2 * clk_mul )
+ corrupt_wave();
+
+ phase = 0;
+ delay = period() + 6 * clk_mul;
+ }
+ }
+}
+
+void Gb_Apu::write_osc( int index, int reg, int old_data, int data )
+{
+ reg -= index * 5;
+ switch ( index )
+ {
+ case 0: square1.write_register( frame_phase, reg, old_data, data ); break;
+ case 1: square2.write_register( frame_phase, reg, old_data, data ); break;
+ case 2: wave .write_register( frame_phase, reg, old_data, data ); break;
+ case 3: noise .write_register( frame_phase, reg, old_data, data ); break;
+ }
+}
+
+// Synthesis
+
+void Gb_Square::run( blip_time_t time, blip_time_t end_time )
+{
+ // Calc duty and phase
+ static byte const duty_offsets [4] = { 1, 1, 3, 7 };
+ static byte const duties [4] = { 1, 2, 4, 6 };
+ int const duty_code = regs [1] >> 6;
+ int duty_offset = duty_offsets [duty_code];
+ int duty = duties [duty_code];
+ if ( mode == Gb_Apu::mode_agb )
+ {
+ // AGB uses inverted duty
+ duty_offset -= duty;
+ duty = 8 - duty;
+ }
+ int ph = (this->phase + duty_offset) & 7;
+
+ // Determine what will be generated
+ int vol = 0;
+ Blip_Buffer* const out = this->output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( dac_enabled() )
+ {
+ if ( enabled )
+ vol = this->volume;
+
+ amp = -dac_bias;
+ if ( mode == Gb_Apu::mode_agb )
+ amp = -(vol >> 1);
+
+ // Play inaudible frequencies as constant amplitude
+ if ( frequency() >= 0x7FA && delay < 32 * clk_mul )
+ {
+ amp += (vol * duty) >> 3;
+ vol = 0;
+ }
+
+ if ( ph < duty )
+ {
+ amp += vol;
+ vol = -vol;
+ }
+ }
+ update_amp( time, amp );
+ }
+
+ // Generate wave
+ time += delay;
+ if ( time < end_time )
+ {
+ int const per = this->period();
+ if ( !vol )
+ {
+ // Maintain phase when not playing
+ int count = (end_time - time + per - 1) / per;
+ ph += count; // will be masked below
+ time += (blip_time_t) count * per;
+ }
+ else
+ {
+ // Output amplitude transitions
+ int delta = vol;
+ do
+ {
+ ph = (ph + 1) & 7;
+ if ( ph == 0 || ph == duty )
+ {
+ good_synth->offset_inline( time, delta, out );
+ delta = -delta;
+ }
+ time += per;
+ }
+ while ( time < end_time );
+
+ if ( delta != vol )
+ last_amp -= delta;
+ }
+ this->phase = (ph - duty_offset) & 7;
+ }
+ delay = time - end_time;
+}
+
+// Quickly runs LFSR for a large number of clocks. For use when noise is generating
+// no sound.
+static unsigned run_lfsr( unsigned s, unsigned mask, int count )
+{
+ bool const optimized = true; // set to false to use only unoptimized loop in middle
+
+ // optimization used in several places:
+ // ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n)
+
+ if ( mask == 0x4000 && optimized )
+ {
+ if ( count >= 32767 )
+ count %= 32767;
+
+ // Convert from Fibonacci to Galois configuration,
+ // shifted left 1 bit
+ s ^= (s & 1) * 0x8000;
+
+ // Each iteration is equivalent to clocking LFSR 255 times
+ while ( (count -= 255) > 0 )
+ s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3);
+ count += 255;
+
+ // Each iteration is equivalent to clocking LFSR 15 times
+ // (interesting similarity to single clocking below)
+ while ( (count -= 15) > 0 )
+ s ^= ((s & 2) * (3 << 13)) ^ (s >> 1);
+ count += 15;
+
+ // Remaining singles
+ while ( --count >= 0 )
+ s = ((s & 2) * (3 << 13)) ^ (s >> 1);
+
+ // Convert back to Fibonacci configuration
+ s &= 0x7FFF;
+ }
+ else if ( count < 8 || !optimized )
+ {
+ // won't fully replace upper 8 bits, so have to do the unoptimized way
+ while ( --count >= 0 )
+ s = (s >> 1 | mask) ^ (mask & (0 - ((s - 1) & 2)));
+ }
+ else
+ {
+ if ( count > 127 )
+ {
+ count %= 127;
+ if ( !count )
+ count = 127; // must run at least once
+ }
+
+ // Need to keep one extra bit of history
+ s = s << 1 & 0xFF;
+
+ // Convert from Fibonacci to Galois configuration,
+ // shifted left 2 bits
+ s ^= (s & 2) * 0x80;
+
+ // Each iteration is equivalent to clocking LFSR 7 times
+ // (interesting similarity to single clocking below)
+ while ( (count -= 7) > 0 )
+ s ^= ((s & 4) * (3 << 5)) ^ (s >> 1);
+ count += 7;
+
+ // Remaining singles
+ while ( --count >= 0 )
+ s = ((s & 4) * (3 << 5)) ^ (s >> 1);
+
+ // Convert back to Fibonacci configuration and
+ // repeat last 8 bits above significant 7
+ s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F);
+ }
+
+ return s;
+}
+
+void Gb_Noise::run( blip_time_t time, blip_time_t end_time )
+{
+ // Determine what will be generated
+ int vol = 0;
+ Blip_Buffer* const out = this->output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( dac_enabled() )
+ {
+ if ( enabled )
+ vol = this->volume;
+
+ amp = -dac_bias;
+ if ( mode == Gb_Apu::mode_agb )
+ amp = -(vol >> 1);
+
+ if ( !(phase & 1) )
+ {
+ amp += vol;
+ vol = -vol;
+ }
+ }
+
+ // AGB negates final output
+ if ( mode == Gb_Apu::mode_agb )
+ {
+ vol = -vol;
+ amp = -amp;
+ }
+
+ update_amp( time, amp );
+ }
+
+ // Run timer and calculate time of next LFSR clock
+ static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 };
+ int const period1 = period1s [regs [3] & 7] * clk_mul;
+ {
+ int extra = (end_time - time) - delay;
+ int const per2 = this->period2();
+ time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1;
+
+ int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1);
+ divider = (divider - count) & period2_mask;
+ delay = count * period1 - extra;
+ }
+
+ // Generate wave
+ if ( time < end_time )
+ {
+ unsigned const mask = this->lfsr_mask();
+ unsigned bits = this->phase;
+
+ int per = period2( period1 * 8 );
+ if ( period2_index() >= 0xE )
+ {
+ time = end_time;
+ }
+ else if ( !vol )
+ {
+ // Maintain phase when not playing
+ int count = (end_time - time + per - 1) / per;
+ time += (blip_time_t) count * per;
+ bits = run_lfsr( bits, ~mask, count );
+ }
+ else
+ {
+ // Output amplitude transitions
+ int delta = -vol;
+ do
+ {
+ unsigned changed = bits + 1;
+ bits = bits >> 1 & mask;
+ if ( changed & 2 )
+ {
+ bits |= ~mask;
+ delta = -delta;
+ med_synth->offset_inline( time, delta, out );
+ }
+ time += per;
+ }
+ while ( time < end_time );
+
+ if ( delta == vol )
+ last_amp += delta;
+ }
+ this->phase = bits;
+ }
+}
+
+void Gb_Wave::run( blip_time_t time, blip_time_t end_time )
+{
+ // Calc volume
+ static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 };
+ int const volume_shift = 2;
+ int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB
+ int const volume_mul = volumes [volume_idx];
+
+ // Determine what will be generated
+ int playing = false;
+ Blip_Buffer* const out = this->output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( dac_enabled() )
+ {
+ // Play inaudible frequencies as constant amplitude
+ amp = 8 << 4; // really depends on average of all samples in wave
+
+ // if delay is larger, constant amplitude won't start yet
+ if ( frequency() <= 0x7FB || delay > 15 * clk_mul )
+ {
+ if ( volume_mul )
+ playing = (int) enabled;
+
+ amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing;
+ }
+
+ amp = ((amp * volume_mul) >> (volume_shift + 4)) - dac_bias;
+ }
+ update_amp( time, amp );
+ }
+
+ // Generate wave
+ time += delay;
+ if ( time < end_time )
+ {
+ byte const* wave = this->wave_ram;
+
+ // wave size and bank
+ int const size20_mask = 0x20;
+ int const flags = regs [0] & agb_mask;
+ int const wave_mask = (flags & size20_mask) | 0x1F;
+ int swap_banks = 0;
+ if ( flags & bank40_mask )
+ {
+ swap_banks = flags & size20_mask;
+ wave += bank_size/2 - (swap_banks >> 1);
+ }
+
+ int ph = this->phase ^ swap_banks;
+ ph = (ph + 1) & wave_mask; // pre-advance
+
+ int const per = this->period();
+ if ( !playing )
+ {
+ // Maintain phase when not playing
+ int count = (end_time - time + per - 1) / per;
+ ph += count; // will be masked below
+ time += (blip_time_t) count * per;
+ }
+ else
+ {
+ // Output amplitude transitions
+ int lamp = this->last_amp + dac_bias;
+ do
+ {
+ // Extract nybble
+ int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0;
+ ph = (ph + 1) & wave_mask;
+
+ // Scale by volume
+ int amp = (nybble * volume_mul) >> (volume_shift + 4);
+
+ int delta = amp - lamp;
+ if ( delta )
+ {
+ lamp = amp;
+ med_synth->offset_inline( time, delta, out );
+ }
+ time += per;
+ }
+ while ( time < end_time );
+ this->last_amp = lamp - dac_bias;
+ }
+ ph = (ph - 1) & wave_mask; // undo pre-advance and mask position
+
+ // Keep track of last byte read
+ if ( enabled )
+ sample_buf = wave [ph >> 1];
+
+ this->phase = ph ^ swap_banks; // undo swapped banks
+ }
+ delay = time - end_time;
+}
diff --git a/gearboy/src/audio/Gb_Oscs.h b/gearboy/src/audio/Gb_Oscs.h
new file mode 100644
index 00000000..138802de
--- /dev/null
+++ b/gearboy/src/audio/Gb_Oscs.h
@@ -0,0 +1,191 @@
+// Private oscillators used by Gb_Apu
+
+// Gb_Snd_Emu 0.2.0
+#ifndef GB_OSCS_H
+#define GB_OSCS_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+#ifndef GB_APU_OVERCLOCK
+ #define GB_APU_OVERCLOCK 1
+#endif
+
+#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1)
+ #error "GB_APU_OVERCLOCK must be a power of 2"
+#endif
+
+class Gb_Osc {
+protected:
+
+ // 11-bit frequency in NRx3 and NRx4
+ int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; }
+
+ void update_amp( blip_time_t, int new_amp );
+ int write_trig( int frame_phase, int max_len, int old_data );
+public:
+
+ enum { clk_mul = GB_APU_OVERCLOCK };
+ enum { dac_bias = 7 };
+
+ Blip_Buffer* outputs [4];// NULL, right, left, center
+ Blip_Buffer* output; // where to output sound
+ BOOST::uint8_t* regs; // osc's 5 registers
+ int mode; // mode_dmg, mode_cgb, mode_agb
+ int dac_off_amp;// amplitude when DAC is off
+ int last_amp; // current amplitude in Blip_Buffer
+ typedef Blip_Synth<blip_good_quality,1> Good_Synth;
+ typedef Blip_Synth<blip_med_quality ,1> Med_Synth;
+ Good_Synth const* good_synth;
+ Med_Synth const* med_synth;
+
+ int delay; // clocks until frequency timer expires
+ int length_ctr; // length counter
+ unsigned phase; // waveform phase (or equivalent)
+ bool enabled; // internal enabled flag
+
+ void clock_length();
+ void reset();
+};
+
+class Gb_Env : public Gb_Osc {
+public:
+ Gb_Env() : env_delay(0), env_enabled(false) {}
+ int env_delay;
+ int volume;
+ bool env_enabled;
+
+ void clock_envelope();
+ bool write_register( int frame_phase, int reg, int old_data, int data );
+
+ void reset()
+ {
+ env_delay = 0;
+ volume = 0;
+ Gb_Osc::reset();
+ }
+protected:
+ // Non-zero if DAC is enabled
+ int dac_enabled() const { return regs [2] & 0xF8; }
+private:
+ void zombie_volume( int old, int data );
+ int reload_env_timer();
+};
+
+class Gb_Square : public Gb_Env {
+public:
+ bool write_register( int frame_phase, int reg, int old_data, int data );
+ void run( blip_time_t, blip_time_t );
+
+ void reset()
+ {
+ Gb_Env::reset();
+ delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger)
+ }
+private:
+ // Frequency timer period
+ int period() const { return (2048 - frequency()) * (4 * clk_mul); }
+};
+
+class Gb_Sweep_Square : public Gb_Square {
+public:
+ int sweep_freq;
+ int sweep_delay;
+ bool sweep_enabled;
+ bool sweep_neg;
+
+ void clock_sweep();
+ void write_register( int frame_phase, int reg, int old_data, int data );
+
+ void reset()
+ {
+ sweep_freq = 0;
+ sweep_delay = 0;
+ sweep_enabled = false;
+ sweep_neg = false;
+ Gb_Square::reset();
+ }
+private:
+ enum { period_mask = 0x70 };
+ enum { shift_mask = 0x07 };
+
+ void calc_sweep( bool update );
+ void reload_sweep_timer();
+};
+
+class Gb_Noise : public Gb_Env {
+public:
+
+ int divider; // noise has more complex frequency divider setup
+
+ void run( blip_time_t, blip_time_t );
+ void write_register( int frame_phase, int reg, int old_data, int data );
+
+ void reset()
+ {
+ divider = 0;
+ Gb_Env::reset();
+ delay = 4 * clk_mul; // TODO: remove?
+ }
+private:
+ enum { period2_mask = 0x1FFFF };
+
+ int period2_index() const { return regs [3] >> 4; }
+ int period2( int base = 8 ) const { return base << period2_index(); }
+ unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; }
+};
+
+class Gb_Wave : public Gb_Osc {
+public:
+ int sample_buf; // last wave RAM byte read (hardware has this as well)
+
+ void write_register( int frame_phase, int reg, int old_data, int data );
+ void run( blip_time_t, blip_time_t );
+
+ // Reads/writes wave RAM
+ int read( unsigned addr ) const;
+ void write( unsigned addr, int data );
+
+ void reset()
+ {
+ sample_buf = 0;
+ Gb_Osc::reset();
+ }
+
+private:
+ enum { bank40_mask = 0x40 };
+ enum { bank_size = 32 };
+
+ int agb_mask; // 0xFF if AGB features enabled, 0 otherwise
+ BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU
+
+ friend class Gb_Apu;
+
+ // Frequency timer period
+ int period() const { return (2048 - frequency()) * (2 * clk_mul); }
+
+ // Non-zero if DAC is enabled
+ int dac_enabled() const { return regs [0] & 0x80; }
+
+ void corrupt_wave();
+
+ BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; }
+
+ // Wave index that would be accessed, or -1 if no access would occur
+ int access( unsigned addr ) const;
+};
+
+inline int Gb_Wave::read( unsigned addr ) const
+{
+ int index = access( addr );
+ return (index < 0 ? 0xFF : wave_bank() [index]);
+}
+
+inline void Gb_Wave::write( unsigned addr, int data )
+{
+ int index = access( addr );
+ if ( index >= 0 )
+ wave_bank() [index] = data;;
+}
+
+#endif
diff --git a/gearboy/src/audio/Multi_Buffer.cpp b/gearboy/src/audio/Multi_Buffer.cpp
new file mode 100644
index 00000000..c1ba0b01
--- /dev/null
+++ b/gearboy/src/audio/Multi_Buffer.cpp
@@ -0,0 +1,281 @@
+// Blip_Buffer 0.4.1. http://www.slack.net/~ant/
+
+#include "Multi_Buffer.h"
+
+/* Copyright (C) 2003-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
+{
+ length_ = 0;
+ sample_rate_ = 0;
+ channels_changed_count_ = 1;
+ channel_types_ = 0;
+ channel_count_ = 0;
+ immediate_removal_ = true;
+}
+
+Multi_Buffer::channel_t Multi_Buffer::channel( int /*index*/ )
+{
+ static channel_t const ch = { 0, 0, 0 };
+ return ch;
+}
+
+// Silent_Buffer
+
+Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse
+{
+ // 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::Mono_Buffer() : Multi_Buffer( 1 )
+{
+ chan.center = &buf;
+ chan.left = &buf;
+ chan.right = &buf;
+}
+
+Mono_Buffer::~Mono_Buffer() { }
+
+blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec )
+{
+ RETURN_ERR( buf.set_sample_rate( rate, msec ) );
+ return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
+}
+
+
+// Tracked_Blip_Buffer
+
+Tracked_Blip_Buffer::Tracked_Blip_Buffer()
+{
+ last_non_silence = 0;
+}
+
+void Tracked_Blip_Buffer::clear()
+{
+ last_non_silence = 0;
+ Blip_Buffer::clear();
+}
+
+void Tracked_Blip_Buffer::end_frame( blip_time_t t )
+{
+ Blip_Buffer::end_frame( t );
+ if ( clear_modified() )
+ last_non_silence = (int)samples_avail() + blip_buffer_extra_;
+}
+
+blip_ulong Tracked_Blip_Buffer::non_silent() const
+{
+ return last_non_silence | unsettled();
+}
+
+inline void Tracked_Blip_Buffer::remove_( long n )
+{
+ if ( (last_non_silence -= n) < 0 )
+ last_non_silence = 0;
+}
+
+void Tracked_Blip_Buffer::remove_silence( long n )
+{
+ remove_( n );
+ Blip_Buffer::remove_silence( n );
+}
+
+void Tracked_Blip_Buffer::remove_samples( long n )
+{
+ remove_( n );
+ Blip_Buffer::remove_samples( n );
+}
+
+void Tracked_Blip_Buffer::remove_all_samples()
+{
+ long avail = samples_avail();
+ if ( !non_silent() )
+ remove_silence( avail );
+ else
+ remove_samples( avail );
+}
+
+long Tracked_Blip_Buffer::read_samples( blip_sample_t* out, long count )
+{
+ count = Blip_Buffer::read_samples( out, count );
+ remove_( count );
+ return count;
+}
+
+// Stereo_Buffer
+
+int const stereo = 2;
+
+Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 )
+{
+ chan.center = mixer.bufs [2] = &bufs [2];
+ chan.left = mixer.bufs [0] = &bufs [0];
+ chan.right = mixer.bufs [1] = &bufs [1];
+ mixer.samples_read = 0;
+}
+
+Stereo_Buffer::~Stereo_Buffer() { }
+
+blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec )
+{
+ mixer.samples_read = 0;
+ for ( int i = bufs_size; --i >= 0; )
+ RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
+ return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
+}
+
+void Stereo_Buffer::clock_rate( long rate )
+{
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].clock_rate( rate );
+}
+
+void Stereo_Buffer::bass_freq( int bass )
+{
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].bass_freq( bass );
+}
+
+void Stereo_Buffer::clear()
+{
+ mixer.samples_read = 0;
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].clear();
+}
+
+void Stereo_Buffer::end_frame( blip_time_t time )
+{
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].end_frame( time );
+}
+
+long Stereo_Buffer::read_samples( blip_sample_t* out, long out_size )
+{
+ require( (out_size & 1) == 0 ); // must read an even number of samples
+ out_size = min( out_size, samples_avail() );
+
+ int pair_count = int (out_size >> 1);
+ if ( pair_count )
+ {
+ mixer.read_pairs( out, pair_count );
+
+ if ( samples_avail() <= 0 || immediate_removal() )
+ {
+ for ( int i = bufs_size; --i >= 0; )
+ {
+ buf_t& b = bufs [i];
+ // TODO: might miss non-silence settling since it checks END of last read
+ if ( !b.non_silent() )
+ b.remove_silence( mixer.samples_read );
+ else
+ b.remove_samples( mixer.samples_read );
+ }
+ mixer.samples_read = 0;
+ }
+ }
+ return out_size;
+}
+
+
+// Stereo_Mixer
+
+// mixers use a single index value to improve performance on register-challenged processors
+// offset goes from negative to zero
+
+void Stereo_Mixer::read_pairs( blip_sample_t* out, int count )
+{
+ // TODO: if caller never marks buffers as modified, uses mono
+ // except that buffer isn't cleared, so caller can encounter
+ // subtle problems and not realize the cause.
+ samples_read += count;
+ if ( bufs [0]->non_silent() | bufs [1]->non_silent() )
+ mix_stereo( out, count );
+ else
+ mix_mono( out, count );
+}
+
+void Stereo_Mixer::mix_mono( blip_sample_t* out_, int count )
+{
+ int const bass = BLIP_READER_BASS( *bufs [2] );
+ BLIP_READER_BEGIN( center, *bufs [2] );
+ BLIP_READER_ADJ_( center, samples_read );
+
+ typedef blip_sample_t stereo_blip_sample_t [stereo];
+ stereo_blip_sample_t* BLIP_RESTRICT out = (stereo_blip_sample_t*) out_ + count;
+ int offset = -count;
+ do
+ {
+ blargg_long s = BLIP_READER_READ( center );
+ BLIP_READER_NEXT_IDX_( center, bass, offset );
+ BLIP_CLAMP( s, s );
+
+ out [offset] [0] = (blip_sample_t) s;
+ out [offset] [1] = (blip_sample_t) s;
+ }
+ while ( ++offset );
+
+ BLIP_READER_END( center, *bufs [2] );
+}
+
+void Stereo_Mixer::mix_stereo( blip_sample_t* out_, int count )
+{
+ blip_sample_t* BLIP_RESTRICT out = out_ + count * stereo;
+
+ // do left + center and right + center separately to reduce register load
+ Tracked_Blip_Buffer* const* buf = &bufs [2];
+ while ( true ) // loop runs twice
+ {
+ --buf;
+ --out;
+
+ int const bass = BLIP_READER_BASS( *bufs [2] );
+ BLIP_READER_BEGIN( side, **buf );
+ BLIP_READER_BEGIN( center, *bufs [2] );
+
+ BLIP_READER_ADJ_( side, samples_read );
+ BLIP_READER_ADJ_( center, samples_read );
+
+ int offset = -count;
+ do
+ {
+ blargg_long s = BLIP_READER_READ_RAW( center ) + BLIP_READER_READ_RAW( side );
+ s >>= blip_sample_bits - 16;
+ BLIP_READER_NEXT_IDX_( side, bass, offset );
+ BLIP_READER_NEXT_IDX_( center, bass, offset );
+ BLIP_CLAMP( s, s );
+
+ ++offset; // before write since out is decremented to slightly before end
+ out [offset * stereo] = (blip_sample_t) s;
+ }
+ while ( offset );
+
+ BLIP_READER_END( side, **buf );
+
+ if ( buf != bufs )
+ continue;
+
+ // only end center once
+ BLIP_READER_END( center, *bufs [2] );
+ break;
+ }
+}
diff --git a/gearboy/src/audio/Multi_Buffer.h b/gearboy/src/audio/Multi_Buffer.h
new file mode 100644
index 00000000..50b0a81a
--- /dev/null
+++ b/gearboy/src/audio/Multi_Buffer.h
@@ -0,0 +1,204 @@
+// Multi-channel sound buffer interface, and basic mono and stereo buffers
+
+// 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
+// consisting of left, center, and right buffers.
+class Multi_Buffer {
+public:
+ Multi_Buffer( int samples_per_frame );
+ virtual ~Multi_Buffer() { }
+
+ // Sets the number of channels available and optionally their types
+ // (type information used by Effects_Buffer)
+ enum { type_index_mask = 0xFF };
+ enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
+ virtual blargg_err_t set_channel_count( int, int const* types = 0 );
+ int channel_count() const { return channel_count_; }
+
+ // Gets indexed channel, from 0 to channel count - 1
+ struct channel_t {
+ Blip_Buffer* center;
+ Blip_Buffer* left;
+ Blip_Buffer* right;
+ };
+ virtual channel_t channel( int index ) BLARGG_PURE( ; )
+
+ // See Blip_Buffer.h
+ virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) BLARGG_PURE( ; )
+ virtual void clock_rate( long ) BLARGG_PURE( { } )
+ virtual void bass_freq( int ) BLARGG_PURE( { } )
+ virtual void clear() BLARGG_PURE( { } )
+ long sample_rate() const;
+
+ // Length of buffer, in milliseconds
+ int length() const;
+
+ // See Blip_Buffer.h
+ virtual void end_frame( blip_time_t ) BLARGG_PURE( { } )
+
+ // Number of samples per output frame (1 = mono, 2 = stereo)
+ int samples_per_frame() const;
+
+ // Count of changes to channel configuration. Incremented whenever
+ // a change is made to any of the Blip_Buffers for any channel.
+ unsigned channels_changed_count() { return channels_changed_count_; }
+
+ // See Blip_Buffer.h
+ virtual long read_samples( blip_sample_t*, long ) BLARGG_PURE( { return 0; } )
+ virtual long samples_avail() const BLARGG_PURE( { return 0; } )
+
+public:
+ BLARGG_DISABLE_NOTHROW
+ void disable_immediate_removal() { immediate_removal_ = false; }
+protected:
+ bool immediate_removal() const { return immediate_removal_; }
+ int const* channel_types() const { return channel_types_; }
+ void channels_changed() { channels_changed_count_++; }
+private:
+ // noncopyable
+ Multi_Buffer( const Multi_Buffer& );
+ Multi_Buffer& operator = ( const Multi_Buffer& );
+
+ unsigned channels_changed_count_;
+ long sample_rate_;
+ int length_;
+ int channel_count_;
+ int const samples_per_frame_;
+ int const* channel_types_;
+ bool immediate_removal_;
+};
+
+// Uses a single buffer and outputs mono samples.
+class Mono_Buffer : public Multi_Buffer {
+ Blip_Buffer buf;
+ channel_t chan;
+public:
+ // Buffer used for all channels
+ Blip_Buffer* center() { return &buf; }
+
+public:
+ Mono_Buffer();
+ ~Mono_Buffer();
+ blargg_err_t set_sample_rate( long rate, int msec = blip_default_length );
+ 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 ) { return chan; }
+ void end_frame( blip_time_t t ) { buf.end_frame( t ); }
+};
+
+ class Tracked_Blip_Buffer : public Blip_Buffer {
+ public:
+ // Non-zero if buffer still has non-silent samples in it. Requires that you call
+ // set_modified() appropriately.
+ blip_ulong non_silent() const;
+
+ // remove_samples( samples_avail() )
+ void remove_all_samples();
+
+ public:
+ BLARGG_DISABLE_NOTHROW
+
+ long read_samples( blip_sample_t*, long );
+ void remove_silence( long );
+ void remove_samples( long );
+ Tracked_Blip_Buffer();
+ void clear();
+ void end_frame( blip_time_t );
+ private:
+ blip_long last_non_silence;
+ void remove_( long );
+ };
+
+ class Stereo_Mixer {
+ public:
+ Tracked_Blip_Buffer* bufs [3];
+ blargg_long samples_read;
+
+ Stereo_Mixer() : samples_read( 0 ) { }
+ void read_pairs( blip_sample_t* out, int count );
+ private:
+ void mix_mono ( blip_sample_t* out, int pair_count );
+ void mix_stereo( blip_sample_t* out, int pair_count );
+ };
+
+// Uses three buffers (one for center) and outputs stereo sample pairs.
+class Stereo_Buffer : public Multi_Buffer {
+public:
+
+ // Buffers used for all channels
+ Blip_Buffer* center() { return &bufs [2]; }
+ Blip_Buffer* left() { return &bufs [0]; }
+ Blip_Buffer* right() { return &bufs [1]; }
+
+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 ) { return chan; }
+ void end_frame( blip_time_t );
+
+ long samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; }
+ long read_samples( blip_sample_t*, long );
+
+private:
+ enum { bufs_size = 3 };
+ typedef Tracked_Blip_Buffer buf_t;
+ buf_t bufs [bufs_size];
+ Stereo_Mixer mixer;
+ channel_t chan;
+};
+
+// Silent_Buffer generates no samples, useful where no sound is wanted
+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 ) { }
+ long samples_avail() const { return 0; }
+ long read_samples( blip_sample_t*, long ) { return 0; }
+};
+
+
+inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec )
+{
+ sample_rate_ = rate;
+ length_ = msec;
+ return 0;
+}
+
+inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec )
+{
+ return Multi_Buffer::set_sample_rate( rate, msec );
+}
+
+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 blargg_err_t Multi_Buffer::set_channel_count( int n, int const* types )
+{
+ channel_count_ = n;
+ channel_types_ = types;
+ return 0;
+}
+
+#endif
diff --git a/gearboy/src/audio/blargg_common.h b/gearboy/src/audio/blargg_common.h
new file mode 100644
index 00000000..1203d387
--- /dev/null
+++ b/gearboy/src/audio/blargg_common.h
@@ -0,0 +1,206 @@
+// Sets up common environment for Shay Green's libraries.
+// To change configuration options, modify blargg_config.h, not this file.
+
+// Gb_Snd_Emu 0.2.0
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+
+#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
+
+// BLARGG_RESTRICT: equivalent to restrict, where supported
+#if __GNUC__ >= 3 || _MSC_VER >= 1100
+ #define BLARGG_RESTRICT __restrict
+#else
+ #define BLARGG_RESTRICT
+#endif
+
+// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr)
+// CONST_CAST( T,expr): Used in place of const_cast<T> (expr)
+#ifndef STATIC_CAST
+ #if __GNUC__ >= 4
+ #define STATIC_CAST(T,expr) static_cast<T> (expr)
+ #define CONST_CAST( T,expr) const_cast<T> (expr)
+ #else
+ #define STATIC_CAST(T,expr) ((T) (expr))
+ #define CONST_CAST( T,expr) ((T) (expr))
+ #endif
+#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 T>
+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 )
+ {
+ // TODO: blargg_common.cpp to hold this as an outline function, ugh
+ void* p = realloc( begin_, n * sizeof (T) );
+ if ( p )
+ begin_ = (T*) p;
+ else if ( n > size_ ) // realloc failure only a problem if expanding
+ return "Out of memory";
+ 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
+ #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 <new>
+ #define BLARGG_NEW new (std::nothrow)
+#endif
+
+// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant)
+#define BLARGG_4CHAR( a, b, c, d ) \
+ ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF))
+
+// 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
+
+// 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
+ #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
+
+// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough
+
+#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF
+ typedef long blargg_long;
+#else
+ typedef int blargg_long;
+#endif
+
+#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF
+ typedef unsigned long blargg_ulong;
+#else
+ typedef unsigned blargg_ulong;
+#endif
+
+// BOOST::int8_t etc.
+
+// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc.
+#if defined (HAVE_STDINT_H)
+ #include <stdint.h>
+ #define BOOST
+
+// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
+#elif defined (HAVE_INTTYPES_H)
+ #include <inttypes.h>
+ #define BOOST
+
+#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
+
+// 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
+
+#endif
+#endif
diff --git a/gearboy/src/audio/blargg_config.h b/gearboy/src/audio/blargg_config.h
new file mode 100644
index 00000000..d577a1c5
--- /dev/null
+++ b/gearboy/src/audio/blargg_config.h
@@ -0,0 +1,33 @@
+// $package user configuration file. Don't replace when updating library.
+
+#ifndef BLARGG_CONFIG_H
+#define BLARGG_CONFIG_H
+
+// Uncomment to have Gb_Apu run at 4x normal clock rate (16777216 Hz), useful in
+// a Game Boy Advance emulator.
+//#define GB_APU_OVERCLOCK 4
+
+#define GB_APU_CUSTOM_STATE 1
+
+// Uncomment to enable platform-specific (and possibly non-portable) optimizations.
+//#define BLARGG_NONPORTABLE 1
+
+// Uncomment if automatic byte-order determination doesn't work
+//#define BLARGG_BIG_ENDIAN 1
+
+// Uncomment to use zlib for transparent decompression of gzipped files
+//#define HAVE_ZLIB_H
+
+// Uncomment if you get errors in the bool section of blargg_common.h
+//#define BLARGG_COMPILER_HAS_BOOL 1
+
+// Uncomment to disable out-of-memory exceptions
+//#include <memory>
+//#define BLARGG_NEW new (std::nothrow)
+
+// Use standard config.h if present
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#endif
diff --git a/gearboy/src/audio/blargg_source.h b/gearboy/src/audio/blargg_source.h
new file mode 100644
index 00000000..ddef37d6
--- /dev/null
+++ b/gearboy/src/audio/blargg_source.h
@@ -0,0 +1,92 @@
+/* Included at the beginning of library source files, AFTER all other #include lines.
+Sets up helpful macros and services used in my source code. Since this is only "active"
+in my source code, I don't have to worry about polluting the global namespace with
+unprefixed names. */
+
+// Gb_Snd_Emu 0.2.0
+#ifndef BLARGG_SOURCE_H
+#define BLARGG_SOURCE_H
+
+// The following four macros are for debugging only. Some or all might be defined
+// to do nothing, depending on the circumstances. Described is what happens when
+// a particular macro is defined to do something. When defined to do nothing, the
+// macros do NOT evaluate their argument(s).
+
+// If expr is false, prints file and line number, then aborts program. Meant for
+// checking internal state and consistency. A failed assertion indicates a bug
+// in MY code.
+//
+// void assert( bool expr );
+#include <assert.h>
+
+// If expr is false, prints file and line number, then aborts program. Meant for
+// checking caller-supplied parameters and operations that are outside the control
+// of the module. A failed requirement probably indicates a bug in YOUR code.
+//
+// void require( bool expr );
+#undef require
+#define require( expr ) assert( expr )
+
+// Like printf() except output goes to debugging console/file.
+//
+// void dprintf( const char* format, ... );
+static inline void blargg_dprintf_( const char*, ... ) { }
+#undef dprintf
+#define dprintf (1) ? (void) 0 : blargg_dprintf_
+
+// If expr is false, prints file and line number to debug console/log, then
+// continues execution normally. Meant for flagging potential problems or things
+// that should be looked into, but that aren't serious problems.
+//
+// void check( bool expr );
+#undef check
+#define check( expr ) ((void) 0)
+
+// If expr yields non-NULL error string, returns it from current function,
+// otherwise continues normally.
+#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, returns "Out of memory" error string, otherwise continues normally.
+#undef CHECK_ALLOC
+#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 )
+
+// The usual min/max functions for built-in types.
+//
+// template<typename T> T min( T x, T y ) { return x < y ? x : y; }
+// template<typename T> T max( T x, T y ) { return x > y ? x : y; }
+#define BLARGG_DEF_MIN_MAX( type ) \
+ static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\
+ static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; }
+
+BLARGG_DEF_MIN_MAX( int )
+BLARGG_DEF_MIN_MAX( unsigned )
+BLARGG_DEF_MIN_MAX( long )
+BLARGG_DEF_MIN_MAX( unsigned long )
+BLARGG_DEF_MIN_MAX( float )
+BLARGG_DEF_MIN_MAX( double )
+
+#undef min
+#define min blargg_min
+
+#undef max
+#define max blargg_max
+
+// typedef unsigned char byte;
+typedef unsigned char blargg_byte;
+#undef byte
+#define byte blargg_byte
+
+// deprecated
+#define BLARGG_CHECK_ALLOC CHECK_ALLOC
+#define BLARGG_RETURN_ERR RETURN_ERR
+
+// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check
+#ifdef BLARGG_SOURCE_BEGIN
+ #include BLARGG_SOURCE_BEGIN
+#endif
+
+#endif