diff --git a/BlipBuffer/BlipBuffer.vcxproj b/BlipBuffer/BlipBuffer.vcxproj new file mode 100644 index 00000000..7635a70b --- /dev/null +++ b/BlipBuffer/BlipBuffer.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {CF35D78C-F710-41D2-968F-C46ACCFF6F07} + Win32Proj + BlipBuffer + + + + DynamicLibrary + true + v120 + Unicode + + + DynamicLibrary + true + v120 + Unicode + + + DynamicLibrary + false + v120 + true + Unicode + + + DynamicLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\bin\$(PlatformTarget)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\bin\$(PlatformTarget)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\bin\$(PlatformTarget)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\bin\$(PlatformTarget)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;BLIPBUFFER_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;BLIPBUFFER_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;BLIPBUFFER_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;BLIPBUFFER_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/BlipBuffer/BlipBuffer.vcxproj.filters b/BlipBuffer/BlipBuffer.vcxproj.filters new file mode 100644 index 00000000..a50d99b8 --- /dev/null +++ b/BlipBuffer/BlipBuffer.vcxproj.filters @@ -0,0 +1,23 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/BlipBuffer/Blip_Buffer.cpp b/BlipBuffer/Blip_Buffer.cpp new file mode 100644 index 00000000..dbde1501 --- /dev/null +++ b/BlipBuffer/Blip_Buffer.cpp @@ -0,0 +1,406 @@ + +// Blip_Buffer 0.4.0. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include +#include +#include +#include +#include + +/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for +more details. You should have received a copy of the GNU Lesser General +Public License along with this module; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +int const buffer_extra = blip_widest_impulse_ + 2; + +Blip_Buffer::Blip_Buffer() +{ + factor_ = LONG_MAX; + offset_ = 0; + buffer_ = 0; + buffer_size_ = 0; + sample_rate_ = 0; + reader_accum = 0; + bass_shift = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + int i = INT_MIN; + assert( (i >> 1) == INT_MIN / 2 ); + + // casting to smaller signed type truncates bits and extends sign + long l = (SHRT_MAX + 1) * 5; + assert( (short) l == SHRT_MIN ); + #endif +} + +Blip_Buffer::~Blip_Buffer() +{ + free( buffer_ ); +} + +void Blip_Buffer::clear( int entire_buffer ) +{ + offset_ = 0; + reader_accum = 0; + if ( buffer_ ) + { + long count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + buffer_extra) * sizeof (buf_t_) ); + } +} + +Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) +{ + // start with maximum length that resampled time can represent + long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - 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 + buffer_extra) * sizeof *buffer_ ); + if ( !p ) + return "Out of memory"; + buffer_ = (buf_t_*) p; + } + + buffer_size_ = new_size; + + // update things based on the sample rate + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( msec ) + assert( length_ == msec ); // ensure length is same as that passed in + 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 clock_rate ) const +{ + double ratio = (double) sample_rate_ / clock_rate; + long factor = (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_ ); // time outside buffer length +} + +void Blip_Buffer::remove_silence( long count ) +{ + assert( count <= samples_avail() ); // tried to remove more samples than available + offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; +} + +long Blip_Buffer::count_samples( blip_time_t t ) const +{ + unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (long) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( long count ) const +{ + if ( count > 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() + buffer_extra; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +// Blip_Synth_ + +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; +} + +static double const 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] *= (float)(0.54 - 0.46 * 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 impulses_size = this->impulses_size(); + for ( i = 0; i < impulses_size; i++ ) + { + impulses [i] = (short) 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) (((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 ); + } +} + +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 sample_shift = blip_sample_bits - 16; + int const bass_shift = this->bass_shift; + long accum = reader_accum; + buf_t_* in = buffer_; + + if ( !stereo ) + { + for ( long n = count; n--; ) + { + long s = accum >> sample_shift; + accum -= accum >> bass_shift; + accum += *in++; + *out++ = (blip_sample_t) s; + + // clamp sample + if ( (blip_sample_t) s != s ) + out [-1] = (blip_sample_t) (0x7FFF - (s >> 24)); + } + } + else + { + for ( long n = count; n--; ) + { + long s = accum >> sample_shift; + accum -= accum >> bass_shift; + accum += *in++; + *out = (blip_sample_t) s; + out += 2; + + // clamp sample + if ( (blip_sample_t) s != s ) + out [-2] = (blip_sample_t) (0x7FFF - (s >> 24)); + } + } + + reader_accum = accum; + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) +{ + 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-- ) + { + long s = (long) *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + diff --git a/BlipBuffer/Blip_Buffer.h b/BlipBuffer/Blip_Buffer.h new file mode 100644 index 00000000..8972bb7e --- /dev/null +++ b/BlipBuffer/Blip_Buffer.h @@ -0,0 +1,354 @@ + +// Band-limited sound synthesis and buffering + +// Blip_Buffer 0.4.0 + +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + +// Time unit at source clock rate +typedef long blip_time_t; + +// Output samples are 16-bit signed, with a range of -32767 to 32767 +typedef short blip_sample_t; +enum { blip_sample_max = 32767 }; + +class __declspec(dllexport) Blip_Buffer { +public: + typedef const char* blargg_err_t; + + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults + // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there + // isn't enough memory, returns error without affecting current buffer setup. + blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); + + // Set number of source time units per second + void clock_rate( long ); + + // End current time frame of specified duration and make its samples available + // (along with any still-unread samples) for reading with read_samples(). Begins + // a new time frame at the end of the current frame. + void end_frame( blip_time_t time ); + + // Read at most 'max_samples' out of buffer into 'dest', removing them from from + // the buffer. Returns number of samples actually read and removed. If stereo is + // true, increments 'dest' one extra time after writing each sample, to allow + // easy interleving of two channels into a stereo output buffer. + long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); + +// Additional optional features + + // Current output sample rate + long sample_rate() const; + + // Length of buffer, in milliseconds + int length() const; + + // Number of source time units per second + long clock_rate() const; + + // Set frequency high-pass filter frequency, where higher values reduce bass more + void bass_freq( int frequency ); + + // Number of samples delay from synthesis to samples read out + int output_latency() const; + + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is + // false, just clears out any samples waiting rather than the entire buffer. + void clear( int entire_buffer = 1 ); + + // Number of samples available for reading with read_samples() + long samples_avail() const; + + // Remove 'count' samples from those waiting to be read + void remove_samples( long count ); + +// Experimental features + + // Number of raw samples that can be mixed within frame of specified duration. + long count_samples( blip_time_t duration ) const; + + // Mix 'count' samples from 'buf' into buffer. + void mix_samples( blip_sample_t const* buf, long count ); + + // Count number of clocks needed until 'count' samples will be available. + // If buffer can't even hold 'count' samples, returns number of clocks until + // buffer becomes full. + blip_time_t count_clocks( long count ) const; + + // not documented yet + typedef unsigned long blip_resampled_time_t; + void remove_silence( long count ); + 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 long buf_t_; + unsigned long factor_; + blip_resampled_time_t offset_; + buf_t_* buffer_; + long buffer_size_; +private: + long reader_accum; + int bass_shift; + long sample_rate_; + long clock_rate_; + int bass_freq_; + int length_; + 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 + #define BLIP_PHASE_BITS 6 +#endif + + // Internal + typedef unsigned long blip_resampled_time_t; + int const blip_widest_impulse_ = 16; + int const blip_res = 1 << BLIP_PHASE_BITS; + class blip_eq_t; + + class __declspec(dllexport) Blip_Synth_ { + double volume_unit_; + short* const impulses; + int const width; + long kernel_unit; + int impulses_size() const { return blip_res / 2 * width + 1; } + void adjust_impulse(); + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + Blip_Synth_( short* impulses, int width ); + void treble_eq( blip_eq_t const& ); + void volume_unit( double ); + }; + +// Quality level. Start with blip_good_quality. +const int blip_med_quality = 8; +const int blip_good_quality = 12; +const int blip_high_quality = 16; + +// Range specifies the greatest expected change in amplitude. Calculate it +// by finding the difference between the maximum and minimum expected +// amplitudes (max - min). +template +class __declspec(dllexport) Blip_Synth { +public: + // Set overall volume of waveform + void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } + + // Configure low-pass filter (see notes.txt) + void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } + + // Get/set Blip_Buffer used for output + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Update amplitude of waveform at given time. Using this requires a separate + // Blip_Synth for each waveform. + void update( blip_time_t time, int amplitude ); + +// Low-level interface + + // Add an amplitude transition of specified delta, optionally into specified buffer + // rather than the one set with output(). Delta can be positive or negative. + // The actual change in amplitude is delta * (volume / range) + void offset( blip_time_t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Contact author for more. + 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 ); + } + +public: + Blip_Synth() : impl( impulses, quality ) { } +private: + typedef short imp_t; + imp_t impulses [blip_res * (quality / 2) + 1]; + Blip_Synth_ impl; +}; + +// 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 notes.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; + +// Optimized inline sample reader for custom sample formats and mixing of Blip_Buffer samples +class Blip_Reader { +public: + // Begin reading samples from buffer. Returns value to pass to next() (can + // be ignored if default bass_freq is acceptable). + int begin( Blip_Buffer& ); + + // Current sample + long read() const { return accum >> (blip_sample_bits - 16); } + + // Current raw sample in full internal resolution + long read_raw() const { return accum; } + + // Advance to next sample + void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } + + // End reading samples from buffer. The number of samples read must now be removed + // using Blip_Buffer::remove_samples(). + void end( Blip_Buffer& b ) { b.reader_accum = accum; } + +private: + const Blip_Buffer::buf_t_* buf; + long accum; +}; + + +// End of public interface + + +#include + +// 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; + +#define BLIP_FWD( i ) { \ + long t0 = i0 * delta + buf [fwd + i]; \ + 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 ) { \ + long t0 = i0 * delta + buf [rev - r]; \ + 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; } + +template +inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, + int delta, Blip_Buffer* blip_buf ) const +{ + // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the + // need for a longer buffer as set by set_sample_rate(). + assert( (long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); + delta *= impl.delta_factor; + int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); + imp_t const* imp = impulses + blip_res - phase; + long* buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); + long i0 = *imp; + + int const fwd = (blip_widest_impulse_ - quality) / 2; + int const rev = fwd + quality - 2; + + BLIP_FWD( 0 ) + if ( quality > 8 ) BLIP_FWD( 2 ) + if ( quality > 12 ) BLIP_FWD( 4 ) + { + int const mid = quality / 2 - 1; + long t0 = i0 * delta + buf [fwd + mid - 1]; + 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 ) + + long t0 = i0 * delta + buf [rev]; + long t1 = *imp * delta + buf [rev + 1]; + buf [rev] = t0; + buf [rev + 1] = t1; +} + +#undef BLIP_FWD +#undef BLIP_REV + +template +void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const +{ + offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); +} + +template +void Blip_Synth::update( blip_time_t t, int amp ) +{ + int delta = amp - impl.last_amp; + impl.last_amp = amp; + offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); +} + +inline 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; +} + +int const blip_max_length = 0; +int const blip_default_length = 250; + +#endif + diff --git a/Core/APU.cpp b/Core/APU.cpp index 6b037d34..57744e94 100644 --- a/Core/APU.cpp +++ b/Core/APU.cpp @@ -1,7 +1,12 @@ #include "stdafx.h" +#include "../BlipBuffer/Blip_Buffer.h" #include "APU.h" #include "CPU.h" -#include "Nes_Apu\apu_snapshot.h" +#include "SquareChannel.h" +#include "TriangleChannel.h" +#include "NoiseChannel.h" +#include "DeltaModulationChannel.h" +#include "ApuFrameCounter.h" APU* APU::Instance = nullptr; IAudioDevice* APU::AudioDevice = nullptr; @@ -11,15 +16,31 @@ APU::APU(MemoryManager* memoryManager) APU::Instance = this; _memoryManager = memoryManager; - - _buf.sample_rate(APU::SampleRate); - _buf.clock_rate(CPU::ClockRate); - _apu.output(&_buf); - - _apu.dmc_reader(&APU::DMCRead); - //_apu.irq_notifier(&APU::IRQChanged); + _blipBuffer = new Blip_Buffer(); + _blipBuffer->sample_rate(APU::SampleRate); + _blipBuffer->clock_rate(CPU::ClockRate); _outputBuffer = new int16_t[APU::SamplesPerFrame]; + + _squareChannel.push_back(unique_ptr(new SquareChannel(true))); + _squareChannel.push_back(unique_ptr(new SquareChannel(false))); + _triangleChannel.reset(new TriangleChannel()); + _noiseChannel.reset(new NoiseChannel()); + _deltaModulationChannel.reset(new DeltaModulationChannel(_memoryManager)); + _frameCounter.reset(new ApuFrameCounter(&APU::FrameCounterTick)); + + _squareChannel[0]->SetBuffer(_blipBuffer); + _squareChannel[1]->SetBuffer(_blipBuffer); + _triangleChannel->SetBuffer(_blipBuffer); + _noiseChannel->SetBuffer(_blipBuffer); + _deltaModulationChannel->SetBuffer(_blipBuffer); + + _memoryManager->RegisterIODevice(_squareChannel[0].get()); + _memoryManager->RegisterIODevice(_squareChannel[1].get()); + _memoryManager->RegisterIODevice(_frameCounter.get()); + _memoryManager->RegisterIODevice(_triangleChannel.get()); + _memoryManager->RegisterIODevice(_noiseChannel.get()); + _memoryManager->RegisterIODevice(_deltaModulationChannel.get()); } APU::~APU() @@ -29,52 +50,137 @@ APU::~APU() void APU::Reset() { - _apu.reset(); + //_apu.reset(); } -int APU::DMCRead(void*, cpu_addr_t addr) +void APU::FrameCounterTick(FrameType type) { - return APU::Instance->_memoryManager->Read(addr); + //Quarter & half frame clock envelope & linear counter + Instance->_squareChannel[0]->TickEnvelope(); + Instance->_squareChannel[1]->TickEnvelope(); + Instance->_triangleChannel->TickLinearCounter(); + Instance->_noiseChannel->TickEnvelope(); + + if(type == FrameType::HalfFrame) { + //Half frames clock length counter & sweep + Instance->_squareChannel[0]->TickLengthCounter(); + Instance->_squareChannel[1]->TickLengthCounter(); + Instance->_triangleChannel->TickLengthCounter(); + Instance->_noiseChannel->TickLengthCounter(); + + Instance->_squareChannel[0]->TickSweep(); + Instance->_squareChannel[1]->TickSweep(); + } } uint8_t APU::ReadRAM(uint16_t addr) { - switch(addr) { - case 0x4015: - CPU::ClearIRQSource(IRQSource::FrameCounter); - return _apu.read_status(_currentClock + 4); - } + //$4015 read + Run(); - return 0; + uint8_t status = 0; + status |= _squareChannel[0]->GetStatus() ? 0x01 : 0x00; + status |= _squareChannel[1]->GetStatus() ? 0x02 : 0x00; + status |= _triangleChannel->GetStatus() ? 0x04 : 0x00; + status |= _noiseChannel->GetStatus() ? 0x08 : 0x00; + status |= _deltaModulationChannel->GetStatus() ? 0x10 : 0x00; + status |= CPU::HasIRQSource(IRQSource::FrameCounter) ? 0x40 : 0x00; + status |= CPU::HasIRQSource(IRQSource::DMC) ? 0x80 : 0x00; + + //Reading $4015 clears the Frame Counter interrupt flag. + CPU::ClearIRQSource(IRQSource::FrameCounter); + + return status; } void APU::WriteRAM(uint16_t addr, uint8_t value) { - _apu.write_register(_currentClock + 4, addr, value); - if(addr == 0x4017 && (value & 0x40) == 0x40) { - //Disable frame interrupts - CPU::ClearIRQSource(IRQSource::FrameCounter); - } + //$4015 write + Run(); + _squareChannel[0]->SetEnabled((value & 0x01) == 0x01); + _squareChannel[1]->SetEnabled((value & 0x02) == 0x02); + _triangleChannel->SetEnabled((value & 0x04) == 0x04); + _noiseChannel->SetEnabled((value & 0x08) == 0x08); + _deltaModulationChannel->SetEnabled((value & 0x10) == 0x10); + + //Writing to $4015 clears the DMC interrupt flag. + CPU::ClearIRQSource(IRQSource::DMC); } -bool APU::Exec(uint32_t currentCPUCycle) +void APU::GetMemoryRanges(MemoryRanges &ranges) { - _currentClock = currentCPUCycle; + ranges.AddHandler(MemoryType::RAM, MemoryOperation::Read, 0x4015); + ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4015); +} - if(_currentClock >= 29780) { - _apu.end_frame(_currentClock); - _buf.end_frame(_currentClock); +void APU::Run() +{ + //Update framecounter and all channels + //This is called: + //-At the end of a frame + //-Before APU registers are read/written to + //-When a DMC or FrameCounter interrupt needs to be fired + uint32_t targetCycle = CPU::GetCycleCount(); + uint32_t currentCycle = _previousCycle; + uint32_t cyclesToRun = targetCycle - _previousCycle; - _currentClock = 0; + while(currentCycle < targetCycle) { + currentCycle += _frameCounter->Run(cyclesToRun); - if(APU::Instance->_apu.earliest_irq() == Nes_Apu::irq_waiting) { - CPU::SetIRQSource(IRQSource::FrameCounter); - } + _squareChannel[0]->Run(currentCycle); + _squareChannel[1]->Run(currentCycle); + _noiseChannel->Run(currentCycle); + _triangleChannel->Run(currentCycle); + _deltaModulationChannel->Run(currentCycle); + } + + _previousCycle = targetCycle; +} + +void APU::StaticRun() +{ + Instance->Run(); +} + +bool APU::IrqPending(uint32_t currentCycle) +{ + uint32_t cyclesToRun = currentCycle - _previousCycle; + if(_frameCounter->IrqPending(cyclesToRun)) { + return true; + } else if(_deltaModulationChannel->IrqPending(cyclesToRun)) { + return true; + } + return false; +} + +void APU::ExecStatic(uint32_t currentCpuCycle) +{ + Instance->Exec(currentCpuCycle); +} + +bool APU::Exec(uint32_t currentCpuCycle) +{ + if(IrqPending(currentCpuCycle)) { + Run(); + } + + if(currentCpuCycle >= 29780) { + Run(); + + _previousCycle = 0; + + _squareChannel[0]->EndFrame(); + _squareChannel[1]->EndFrame(); + _triangleChannel->EndFrame(); + _noiseChannel->EndFrame(); + _deltaModulationChannel->EndFrame(); + + _blipBuffer->end_frame(currentCpuCycle); // Read some samples out of Blip_Buffer if there are enough to fill our output buffer - uint32_t availableSampleCount = _buf.samples_avail(); + uint32_t availableSampleCount = _blipBuffer->samples_avail(); if(availableSampleCount >= APU::SamplesPerFrame) { - size_t sampleCount = _buf.read_samples(_outputBuffer, APU::SamplesPerFrame); + size_t sampleCount = _blipBuffer->read_samples(_outputBuffer, APU::SamplesPerFrame); if(APU::AudioDevice) { APU::AudioDevice->PlayBuffer(_outputBuffer, (uint32_t)(sampleCount * BitsPerSample / 8)); } @@ -93,9 +199,9 @@ void APU::StopAudio() void APU::StreamState(bool saving) { - apu_snapshot_t snapshot; + /*apu_snapshot_t snapshot; if(saving) { - _apu.save_snapshot(&snapshot); + //_apu.save_snapshot(&snapshot); } Stream(_currentClock); @@ -145,6 +251,6 @@ void APU::StreamState(bool saving) Stream(snapshot.dmc.irq_flag); if(!saving) { - _apu.load_snapshot(snapshot); - } + //_apu.load_snapshot(snapshot); + }*/ } \ No newline at end of file diff --git a/Core/APU.h b/Core/APU.h index 3c0862d7..6dc5b026 100644 --- a/Core/APU.h +++ b/Core/APU.h @@ -1,28 +1,44 @@ #pragma once #include "stdafx.h" -#include "MemoryManager.h" #include "IMemoryHandler.h" #include "IAudioDevice.h" #include "Snapshotable.h" -#include "Nes_Apu/Nes_Apu.h" -class APU : public IMemoryHandler, public Snapshotable +class MemoryManager; +class SquareChannel; +class TriangleChannel; +class NoiseChannel; +class DeltaModulationChannel; +class ApuFrameCounter; +class Blip_Buffer; +enum class FrameType; + +class APU : public Snapshotable, public IMemoryHandler { private: static IAudioDevice* AudioDevice; static APU* Instance; uint32_t _currentClock = 0; + uint32_t _previousCycle = 0; - Nes_Apu _apu; - Blip_Buffer _buf; + vector> _squareChannel; + unique_ptr _triangleChannel; + unique_ptr _noiseChannel; + unique_ptr _deltaModulationChannel; + + unique_ptr _frameCounter; + + Blip_Buffer* _blipBuffer; int16_t* _outputBuffer; MemoryManager* _memoryManager; private: - static int DMCRead(void*, cpu_addr_t addr); - static void IRQChanged(void* data); + bool IrqPending(uint32_t currentCycle); + void Run(); + + static void FrameCounterTick(FrameType type); protected: void StreamState(bool saving); @@ -38,14 +54,6 @@ class APU : public IMemoryHandler, public Snapshotable void Reset(); - void GetMemoryRanges(MemoryRanges &ranges) - { - ranges.AddHandler(MemoryType::RAM, MemoryOperation::Read, 0x4015); - ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4000, 0x4013); - ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4015); - ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4017); - } - static void RegisterAudioDevice(IAudioDevice *audioDevice) { APU::AudioDevice = audioDevice; @@ -53,7 +61,11 @@ class APU : public IMemoryHandler, public Snapshotable uint8_t ReadRAM(uint16_t addr); void WriteRAM(uint16_t addr, uint8_t value); + void GetMemoryRanges(MemoryRanges &ranges); - bool Exec(uint32_t executedCycles); + bool Exec(uint32_t currentCpuCycle); + static void ExecStatic(uint32_t currentCpuCycle); + + static void StaticRun(); static void StopAudio(); }; \ No newline at end of file diff --git a/Core/ApuEnvelope.h b/Core/ApuEnvelope.h new file mode 100644 index 00000000..dee33dac --- /dev/null +++ b/Core/ApuEnvelope.h @@ -0,0 +1,62 @@ +#pragma once +#include "stdafx.h" +#include "ApuLengthCounter.h" + +class ApuEnvelope : public ApuLengthCounter +{ +private: + bool _constantVolume = false; + uint8_t _volume = 0; + + uint8_t _envelope = 0; + uint8_t _envelopeCounter = 0; + + bool _start = false; + int8_t _divider = 0; + uint8_t _counter = 0; + +protected: + void InitializeEnvelope(uint8_t regValue) + { + _constantVolume = (regValue & 0x10) == 0x10; + _volume = regValue & 0x0F; + } + + void ResetEnvelope() + { + _start = true; + } + + uint32_t GetVolume() + { + if(_lengthCounter > 0) { + if(_constantVolume) { + return _volume; + } else { + return _counter; + } + } else { + return 0; + } + } + +public: + void TickEnvelope() + { + if(!_start) { + _divider--; + if(_divider < 0) { + _divider = _volume; + if(_counter > 0) { + _counter--; + } else if(_lengthCounterHalt) { + _counter = 15; + } + } + } else { + _start = false; + _counter = 15; + _divider = _volume; + } + } +}; diff --git a/Core/ApuFrameCounter.h b/Core/ApuFrameCounter.h new file mode 100644 index 00000000..8c740ab1 --- /dev/null +++ b/Core/ApuFrameCounter.h @@ -0,0 +1,115 @@ +#pragma once +#include "stdafx.h" +#include "IMemoryHandler.h" +#include "CPU.h" + +enum class FrameType +{ + None = 0, + QuarterFrame = 1, + HalfFrame = 2, +}; + +class ApuFrameCounter : public IMemoryHandler +{ +private: + int32_t _nextIrqCycle = 29828; + uint32_t _previousCycle = 0; + + const vector> _stepCycles = { { { 7457, 14913, 22371, 29828, 29829, 29830}, + { 7457, 14913, 22371, 29829, 37281, 37282} } }; + const vector> _frameType = { { { FrameType::QuarterFrame, FrameType::HalfFrame, FrameType::QuarterFrame, FrameType::None, FrameType::HalfFrame, FrameType::None }, + { FrameType::QuarterFrame, FrameType::HalfFrame, FrameType::QuarterFrame, FrameType::None, FrameType::HalfFrame, FrameType::None } } }; + + void (*_callback)(FrameType); + + uint32_t _currentStep = 0; + uint32_t _stepMode = 0; //0: 4-step mode, 1: 5-step mode + bool _inhibitIRQ = false; + +public: + ApuFrameCounter(void (*frameCounterTickCallback)(FrameType)) + { + _callback = frameCounterTickCallback; + } + + uint32_t Run(uint32_t &cyclesToRun) + { + uint32_t cyclesRan; + + if(_previousCycle + cyclesToRun >= _stepCycles[_stepMode][_currentStep]) { + if(!_inhibitIRQ && _stepMode == 0 && _currentStep >= 3) { + //Set irq on the last 3 cycles for 4-step mode + CPU::SetIRQSource(IRQSource::FrameCounter); + _nextIrqCycle++; + } + + FrameType type = _frameType[_stepMode][_currentStep]; + if(type != FrameType::None) { + _callback(type); + } + + cyclesRan = _stepCycles[_stepMode][_currentStep] - _previousCycle; + cyclesToRun -= cyclesRan; + + _currentStep++; + if(_currentStep == 6) { + _currentStep = 0; + _previousCycle = 0; + if(_stepMode == 0 && !_inhibitIRQ) { + _nextIrqCycle = 29828; + } + } else { + _previousCycle += cyclesRan; + } + } else { + cyclesRan = cyclesToRun; + cyclesToRun = 0; + _previousCycle += cyclesRan; + } + return cyclesRan; + } + + bool IrqPending(uint32_t cyclesToRun) + { + if(_nextIrqCycle != -1) { + if(_previousCycle + cyclesToRun >= (uint32_t)_nextIrqCycle) { + return true; + } + } + return false; + } + + void GetMemoryRanges(MemoryRanges &ranges) + { + ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4017); + } + + uint8_t ReadRAM(uint16_t addr) + { + return 0; + } + + void WriteRAM(uint16_t addr, uint8_t value) + { + APU::StaticRun(); + _stepMode = ((value & 0x80) == 0x80) ? 1 : 0; + _inhibitIRQ = (value & 0x40) == 0x40; + _nextIrqCycle = -1; + + if(_inhibitIRQ) { + CPU::ClearIRQSource(IRQSource::FrameCounter); + } else if(_stepMode == 0) { + _nextIrqCycle = 29828; + } + + //Reset sequence when $4017 is written to + _previousCycle = 0; + _currentStep = 0; + + if(_stepMode == 1) { + //Writing to $4017 with bit 7 set will immediately generate a clock for both the quarter frame and the half frame units, regardless of what the sequencer is doing. + _callback(FrameType::HalfFrame); + } + } +}; \ No newline at end of file diff --git a/Core/ApuLengthCounter.h b/Core/ApuLengthCounter.h new file mode 100644 index 00000000..92e5bd32 --- /dev/null +++ b/Core/ApuLengthCounter.h @@ -0,0 +1,47 @@ +#pragma once +#include "stdafx.h" +#include "BaseApuChannel.h" + +class ApuLengthCounter : public BaseApuChannel<15> +{ +private: + const vector _lcLookupTable = { { 10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30 } }; + bool _enabled = false; + +protected: + bool _lengthCounterHalt = false; + uint8_t _lengthCounter = 0; + + void InitializeLengthCounter(bool haltFlag) + { + _lengthCounterHalt = haltFlag; + } + + void LoadLengthCounter(uint8_t value) + { + if(_enabled) { + _lengthCounter = _lcLookupTable[value]; + } + } + +public: + bool GetStatus() + { + return _lengthCounter > 0; + } + + void TickLengthCounter() + { + if(_lengthCounter > 0 && !_lengthCounterHalt) { + _lengthCounter--; + } + } + + void SetEnabled(bool enabled) + { + if(!enabled) { + _lengthCounter = 0; + } + _enabled = enabled; + } +}; diff --git a/Core/BaseApuChannel.h b/Core/BaseApuChannel.h new file mode 100644 index 00000000..c44248e3 --- /dev/null +++ b/Core/BaseApuChannel.h @@ -0,0 +1,76 @@ +#pragma once +#include "stdafx.h" +#include "IMemoryHandler.h" +#include "../BlipBuffer/Blip_Buffer.h" + +template +class BaseApuChannel : public IMemoryHandler +{ +private: + unique_ptr> _synth; + uint16_t _lastOutput = 0; + uint32_t _previousCycle = 0; + Blip_Buffer *_buffer; + +protected: + uint16_t _timer = 0; + uint16_t _period = 0; + uint32_t _clockDivider = 2; //All channels except triangle clock overy other cpu clock + +public: + virtual void Clock() = 0; + virtual bool GetStatus() = 0; + + BaseApuChannel() + { + _synth.reset(new Blip_Synth()); + } + + void SetBuffer(Blip_Buffer *buffer) + { + _buffer = buffer; + } + + void SetVolume(double volume) + { + _synth->volume(volume); + } + + virtual void Run(uint32_t targetCycle) + { + while(_previousCycle < targetCycle) { + if(_timer == 0) { + Clock(); + _timer = _period; + _previousCycle += _clockDivider; + } else { + uint32_t cyclesToRun = (targetCycle - _previousCycle) / _clockDivider; + uint16_t skipCount = _timer > cyclesToRun ? cyclesToRun : _timer; + _timer -= skipCount; + _previousCycle += skipCount * _clockDivider; + + if(cyclesToRun == 0) { + break; + } + } + } + } + + uint8_t ReadRAM(uint16_t addr) + { + return 0; + } + + void AddOutput(uint16_t output) + { + if(output != _lastOutput) { + _synth->offset_inline(_previousCycle, output - _lastOutput, _buffer); + } + _lastOutput = output; + } + + void EndFrame() + { + _previousCycle = 0; + } +}; \ No newline at end of file diff --git a/Core/CPU.h b/Core/CPU.h index 175d449b..8d9f8864 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -667,6 +667,7 @@ public: static void SetNMIFlag() { CPU::Instance->_state.NMIFlag = true; } static void ClearNMIFlag() { CPU::Instance->_state.NMIFlag = false; } static void SetIRQSource(IRQSource source) { CPU::Instance->_state.IRQFlag |= (int)source; } + static bool HasIRQSource(IRQSource source) { return (CPU::Instance->_state.IRQFlag & (int)source) != 0; } static void ClearIRQSource(IRQSource source) { CPU::Instance->_state.IRQFlag &= ~(int)source; } static void RunDMATransfer(uint8_t* spriteRAM, uint32_t &spriteRamAddr, uint8_t offsetValue); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index cba5d5c3..567751cf 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -266,7 +266,12 @@ + + + + + @@ -293,6 +298,8 @@ + + @@ -309,23 +316,13 @@ - - - - - - - - - - - + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 700891ee..1fa2998a 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -9,9 +9,6 @@ {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd - - {c6dc2048-98f6-4551-89dc-830f12f1bb2e} - {ca661408-b52a-4378-aef4-80fda1d64cd6} @@ -30,6 +27,12 @@ {ff3c6e48-3987-41d2-8916-b588a457ff30} + + {d9dec4ba-97e7-4a80-85e1-6b53f5ed7218} + + + {b99fc308-b28a-48b7-9ca8-6e8005bbc2bf} + @@ -53,42 +56,6 @@ Header Files - - Header Files - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - - - Header Files\Nes_Apu - Header Files\Interfaces @@ -224,6 +191,33 @@ Header Files + + Header Files\APU + + + Header Files\APU + + + Header Files\APU + + + Header Files\APU + + + Header Files\APU + + + Header Files\APU + + + Header Files\APU + + + Header Files\APU + + + Header Files\APU + @@ -244,9 +238,6 @@ Source Files - - Source Files - Source Files @@ -295,5 +286,8 @@ Source Files + + Source Files\APU + \ No newline at end of file diff --git a/Core/DeltaModulationChannel.h b/Core/DeltaModulationChannel.h new file mode 100644 index 00000000..ab1b1005 --- /dev/null +++ b/Core/DeltaModulationChannel.h @@ -0,0 +1,154 @@ +#pragma once +#include "stdafx.h" +#include "../BlipBuffer/Blip_Buffer.h" +#include "APU.h" +#include "IMemoryHandler.h" +#include "ApuEnvelope.h" + +class DeltaModulationChannel : public BaseApuChannel<127> +{ +private: + const vector _dmcPeriodLookupTable = { { 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 } }; + + MemoryManager *_memoryManager = nullptr; + + uint16_t _sampleAddr = 0; + uint16_t _sampleLength = 0; + uint8_t _outputLevel = 0; + bool _irqEnabled = false; + bool _loopFlag = false; + + uint16_t _currentAddr = 0; + uint16_t _bytesRemaining = 0; + uint8_t _readBuffer = 0; + bool _bufferEmpty = true; + + uint8_t _shiftRegister = 0; + uint8_t _bitsRemaining = 0; + bool _silenceFlag = true; + +public: + DeltaModulationChannel(MemoryManager* memoryManager) + { + _memoryManager = memoryManager; + _clockDivider = 1; + SetVolume(0.42545); + } + + bool IrqPending(uint32_t cyclesToRun) + { + if(_irqEnabled && _bytesRemaining > 0) { + uint32_t cyclesToEmptyBuffer = (_bitsRemaining + (_bytesRemaining-1)* 8) * _period; + if(cyclesToRun >= cyclesToEmptyBuffer) { + return true; + } + } + return false; + } + + bool GetStatus() + { + return _bytesRemaining > 0; + } + + void GetMemoryRanges(MemoryRanges &ranges) + { + ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4010, 0x4013); + } + + void WriteRAM(uint16_t addr, uint8_t value) + { + APU::StaticRun(); + switch(addr & 0x03) { + case 0: //4010 + _irqEnabled = (value & 0x80) == 0x80; + _loopFlag = (value & 0x40) == 0x40; + _period = _dmcPeriodLookupTable[value & 0x0F]; + + if(!_irqEnabled) { + CPU::ClearIRQSource(IRQSource::DMC); + } + break; + + case 1: //4011 + _outputLevel = value & 0x7F; + _shiftRegister = value & 0x7F; + break; + + case 2: //4012 + _sampleAddr = 0xC000 | ((uint32_t)value << 6); + break; + + case 3: //4013 + _sampleLength = (value << 4) | 0x0001; + break; + } + } + + void SetEnabled(bool enabled) + { + if(!enabled) { + _bytesRemaining = 0; + } else if(_bytesRemaining == 0) { + InitSample(); + FillReadBuffer(); + } + } + + void InitSample() + { + _currentAddr = _sampleAddr; + _bytesRemaining = _sampleLength; + } + + void FillReadBuffer() + { + if(_bufferEmpty && _bytesRemaining > 0) { + _readBuffer = _memoryManager->Read(_currentAddr); + _bufferEmpty = false; + + _currentAddr++; + _bytesRemaining--; + + if(_bytesRemaining == 0) { + if(_loopFlag) { + //Looped sample should never set IRQ flag + InitSample(); + } else if(_irqEnabled) { + CPU::SetIRQSource(IRQSource::DMC); + } + } + } + } + + void Clock() + { + if(!_silenceFlag) { + if(_shiftRegister & 0x01) { + if(_outputLevel <= 125) { + _outputLevel += 2; + } + } else { + if(_outputLevel >= 2) { + _outputLevel -= 2; + } + } + _shiftRegister >>= 1; + } + + _bitsRemaining--; + if(_bitsRemaining == 0) { + _bitsRemaining = 8; + if(_bufferEmpty) { + _silenceFlag = true; + } else { + _silenceFlag = false; + _shiftRegister = _readBuffer; + _bufferEmpty = true; + FillReadBuffer(); + } + } + + AddOutput(_outputLevel); + } +}; \ No newline at end of file diff --git a/Core/Nes_Apu/Blip_Buffer.h b/Core/Nes_Apu/Blip_Buffer.h deleted file mode 100644 index 19822682..00000000 --- a/Core/Nes_Apu/Blip_Buffer.h +++ /dev/null @@ -1,253 +0,0 @@ - -// Buffer of sound samples into which band-limited waveforms can be synthesized -// using Blip_Wave or Blip_Synth. - -// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef BLIP_BUFFER_H -#define BLIP_BUFFER_H - -#include "blargg_common.h" - -class Blip_Reader; - -// Source time unit. -typedef long blip_time_t; - -// Type of sample produced. Signed 16-bit format. -typedef BOOST::int16_t blip_sample_t; - -// Make buffer as large as possible (currently about 65000 samples) -const int blip_default_length = 0; - -class Blip_Buffer { -public: - // Construct an empty buffer. - Blip_Buffer(); - ~Blip_Buffer(); - - // Set output sample rate and buffer length in milliseconds (1/1000 sec), - // then clear buffer. If length is not specified, make as large as possible. - // If there is insufficient memory for the buffer, sets the buffer length - // to 0 and returns error string (or propagates exception if compiler supports it). - blargg_err_t sample_rate( long samples_per_sec, int msec_length = blip_default_length ); - // to do: rename to set_sample_rate - - // Length of buffer, in milliseconds - int length() const; - - // Current output sample rate - long sample_rate() const; - - // Number of source time units per second - void clock_rate( long ); - long clock_rate() const; - - // Set frequency at which high-pass filter attenuation passes -3dB - void bass_freq( int frequency ); - - // Remove all available samples and clear buffer to silence. If 'entire_buffer' is - // false, just clear out any samples waiting rather than the entire buffer. - void clear( bool entire_buffer = true ); - - // to do: - // Notify Blip_Buffer that synthesis has been performed until specified time - //void run_until( blip_time_t ); - - // End current time frame of specified duration and make its samples available - // (along with any still-unread samples) for reading with read_samples(). Begin - // a new time frame at the end of the current frame. All transitions must have - // been added before 'time'. - void end_frame( blip_time_t time ); - - // Number of samples available for reading with read_samples() - long samples_avail() const; - - // Read at most 'max_samples' out of buffer into 'dest', removing them from from - // the buffer. Return number of samples actually read and removed. If stereo is - // true, increment 'dest' one extra time after writing each sample, to allow - // easy interleving of two channels into a stereo output buffer. - long read_samples( blip_sample_t* dest, long max_samples, bool stereo = false ); - - // Remove 'count' samples from those waiting to be read - void remove_samples( long count ); - - // Number of samples delay from synthesis to samples read out - int output_latency() const; - - - // Experimental external buffer mixing support - - // Number of raw samples that can be mixed within frame of specified duration - long count_samples( blip_time_t duration ) const; - - // Mix 'count' samples from 'buf' into buffer. - void mix_samples( const blip_sample_t* buf, long count ); - - - // not documented yet - - void remove_silence( long count ); - - typedef unsigned long resampled_time_t; - - resampled_time_t resampled_time( blip_time_t t ) const { - return t * resampled_time_t (factor_) + offset_; - } - - resampled_time_t resampled_duration( int t ) const { - return t * resampled_time_t (factor_); - } - -private: - // noncopyable - Blip_Buffer( const Blip_Buffer& ); - Blip_Buffer& operator = ( const Blip_Buffer& ); - - // Don't use the following members. They are public only for technical reasons. - public: - enum { widest_impulse_ = 24 }; - typedef BOOST::uint16_t buf_t_; - - unsigned long factor_; - resampled_time_t offset_; - buf_t_* buffer_; - unsigned buffer_size_; - private: - long reader_accum; - int bass_shift; - long samples_per_sec; - long clocks_per_sec; - int bass_freq_; - int length_; - - enum { accum_fract = 15 }; // less than 16 to give extra sample range - enum { sample_offset = 0x7F7F }; // repeated byte allows memset to clear buffer - - friend class Blip_Reader; -}; - -// Low-pass equalization parameters (see notes.txt) -class blip_eq_t { -public: - blip_eq_t( double treble = 0 ); - blip_eq_t( double treble, long cutoff, long sample_rate ); -private: - double treble; - long cutoff; - long sample_rate; - friend class Blip_Impulse_; -}; - -// not documented yet (see Multi_Buffer.cpp for an example of use) -class Blip_Reader { - const Blip_Buffer::buf_t_* buf; - long accum; - #ifdef __MWERKS__ - void operator = ( struct foobar ); // helps optimizer - #endif -public: - // avoid anything which might cause optimizer to put object in memory - - int begin( Blip_Buffer& blip_buf ) { - buf = blip_buf.buffer_; - accum = blip_buf.reader_accum; - return blip_buf.bass_shift; - } - - int read() const { - return accum >> Blip_Buffer::accum_fract; - } - - void next( int bass_shift = 9 ) { - accum -= accum >> bass_shift; - accum += ((long) *buf++ - Blip_Buffer::sample_offset) << Blip_Buffer::accum_fract; - } - - void end( Blip_Buffer& blip_buf ) { - blip_buf.reader_accum = accum; - } -}; - - - -// End of public interface - -#ifndef BLIP_BUFFER_ACCURACY - #define BLIP_BUFFER_ACCURACY 16 -#endif - -const int blip_res_bits_ = 5; - -typedef BOOST::uint32_t blip_pair_t_; - -class Blip_Impulse_ { - typedef BOOST::uint16_t imp_t; - - blip_eq_t eq; - double volume_unit_; - imp_t* impulses; - imp_t* impulse; - int width; - int fine_bits; - int res; - bool generate; - - void fine_volume_unit(); - void scale_impulse( int unit, imp_t* ) const; -public: - Blip_Buffer* buf; - BOOST::uint32_t offset; - - void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 ); - void volume_unit( double ); - void treble_eq( const blip_eq_t& ); -}; - -inline blip_eq_t::blip_eq_t( double t ) : - treble( t ), cutoff( 0 ), sample_rate( 44100 ) { -} - -inline blip_eq_t::blip_eq_t( double t, long c, long sr ) : - treble( t ), cutoff( c ), sample_rate( sr ) { -} - -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 samples_per_sec; -} - -inline void Blip_Buffer::end_frame( blip_time_t t ) { - offset_ += t * factor_; - assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer", - samples_avail() <= (long) buffer_size_ )); -} - -inline void Blip_Buffer::remove_silence( long count ) { - assert(( "Blip_Buffer::remove_silence(): Tried to remove more samples than available", - count <= samples_avail() )); - offset_ -= resampled_time_t (count) << BLIP_BUFFER_ACCURACY; -} - -inline int Blip_Buffer::output_latency() const { - return widest_impulse_ / 2; -} - -inline long Blip_Buffer::clock_rate() const { - return clocks_per_sec; -} - -// MSVC6 fix -typedef Blip_Buffer::resampled_time_t blip_resampled_time_t; - -#include "Blip_Synth.h" - -#endif - diff --git a/Core/Nes_Apu/Blip_Synth.h b/Core/Nes_Apu/Blip_Synth.h deleted file mode 100644 index b1665742..00000000 --- a/Core/Nes_Apu/Blip_Synth.h +++ /dev/null @@ -1,203 +0,0 @@ - -// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding -// waveforms to a Blip_Buffer. - -// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef BLIP_SYNTH_H -#define BLIP_SYNTH_H - -#ifndef BLIP_BUFFER_H - #include "Blip_Buffer.h" -#endif - -// Quality level. Higher levels are slower, and worse in a few cases. -// Use blip_good_quality as a starting point. -const int blip_low_quality = 1; -const int blip_med_quality = 2; -const int blip_good_quality = 3; -const int blip_high_quality = 4; - -// Blip_Synth is a transition waveform synthesizer which adds band-limited -// offsets (transitions) into a Blip_Buffer. For a simpler interface, use -// Blip_Wave (below). -// -// Range specifies the greatest expected offset that will occur. For a -// waveform that goes between +amp and -amp, range should be amp * 2 (half -// that if it only goes between +amp and 0). When range is large, a higher -// accuracy scheme is used; to force this even when range is small, pass -// the negative of range (i.e. -range). -template -class Blip_Synth { - BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 ); - BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 ); - enum { - abs_range = (range < 0) ? -range : range, - fine_mode = (range > 512 || range < 0), - width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_), - res = 1 << blip_res_bits_, - impulse_size = width / 2 * (fine_mode + 1), - base_impulses_size = width / 2 * (res / 2 + 1), - fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 : - abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 : - abs_range <= 2048 ? 7 : 8) : 0) - }; - blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size]; - Blip_Impulse_ impulse; -public: - Blip_Synth() { impulse.init( impulses, width, res, fine_bits ); } - - // Configure low-pass filter (see notes.txt). Not optimized for real-time control - void treble_eq( const blip_eq_t& eq ) { impulse.treble_eq( eq ); } - - // Set volume of a transition at amplitude 'range' by setting volume_unit - // to v / range - void volume( double v ) { impulse.volume_unit( v * (1.0 / abs_range) ); } - - // Set base volume unit of transitions, where 1.0 is a full swing between the - // positive and negative extremes. Not optimized for real-time control. - void volume_unit( double unit ) { impulse.volume_unit( unit ); } - - // Default Blip_Buffer used for output when none is specified for a given call - Blip_Buffer* output() const { return impulse.buf; } - void output( Blip_Buffer* b ) { impulse.buf = b; } - - // Add an amplitude offset (transition) with a magnitude of delta * volume_unit - // into the specified buffer (default buffer if none specified) at the - // specified source time. Delta can be positive or negative. To increase - // performance by inlining code at the call site, use offset_inline(). - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - void offset_resampled( blip_resampled_time_t t, int o ) const { - offset_resampled( t, o, impulse.buf ); - } - void offset( blip_time_t t, int delta ) const { - offset( t, delta, impulse.buf ); - } - void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t time, int delta ) const { - offset_inline( time, delta, impulse.buf ); - } -}; - -// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer. -// A wave is built from a series of delays and new amplitudes. This provides a -// simpler interface than Blip_Synth, nothing more. -template -class Blip_Wave { - Blip_Synth synth; - blip_time_t time_; - int last_amp; -public: - // Start wave at time 0 and amplitude 0 - Blip_Wave() : time_( 0 ), last_amp( 0 ) { } - - // 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. - void end_frame( blip_time_t duration ) { - assert(( "Blip_Wave::end_frame(): Wave hadn't yet been run for entire frame", - duration <= time_ )); - time_ -= duration; - } -}; - - -// End of public interface - -template -void Blip_Wave::amplitude( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - synth.offset_inline( time_, delta ); -} - -template -inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - typedef blip_pair_t_ pair_t; - - unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1; - assert(( "Blip_Synth/Blip_wave: Went past end of buffer", - sample_index < blip_buf->buffer_size_ )); - enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 }; - pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index]; - - enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ }; - enum { mask = res * 2 - 1 }; - const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size]; - - pair_t offset = impulse.offset * delta; - - if ( !fine_bits ) - { - // normal mode - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta; - t1 += imp [1] * delta; - imp += 2; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } - else - { - // fine mode - enum { sub_range = 1 << fine_bits }; - delta += sub_range / 2; - int delta2 = (delta & (sub_range - 1)) - sub_range / 2; - delta >>= fine_bits; - - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta2; - t0 += imp [1] * delta; - - t1 += imp [2] * delta2; - t1 += imp [3] * delta; - - imp += 4; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } -} - -template -void Blip_Synth::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); -} - -#endif - diff --git a/Core/Nes_Apu/Multi_Buffer.h b/Core/Nes_Apu/Multi_Buffer.h deleted file mode 100644 index f7dd3237..00000000 --- a/Core/Nes_Apu/Multi_Buffer.h +++ /dev/null @@ -1,157 +0,0 @@ - -// Multi-channel sound buffer interface, and basic mono and stereo buffers - -// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef MULTI_BUFFER_H -#define MULTI_BUFFER_H - -#include "Blip_Buffer.h" - -// Multi_Buffer is an 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() { } - - // Set the number of channels available - virtual blargg_err_t set_channel_count( int ); - - // Get 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 ) = 0; - - // See Blip_Buffer.h - // to do: rename to set_sample_rate - virtual blargg_err_t sample_rate( long rate, int msec = blip_default_length ) = 0; - virtual void clock_rate( long ) = 0; - virtual void bass_freq( int ) = 0; - virtual void clear() = 0; - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo' - // if nothing was added to the left and right buffers of any channel for - // this time frame. - virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0; - - // Number of samples per output frame (1 = mono, 2 = stereo) - int samples_per_frame() const; - - // See Blip_Buffer.h - virtual long read_samples( blip_sample_t*, long ) = 0; - virtual long samples_avail() const = 0; - -private: - // noncopyable - Multi_Buffer( const Multi_Buffer& ); - Multi_Buffer& operator = ( const Multi_Buffer& ); - - long sample_rate_; - int length_; - int const samples_per_frame_; -}; - -// Mono_Buffer uses a single buffer and outputs mono samples. -class Mono_Buffer : public Multi_Buffer { - Blip_Buffer buf; -public: - Mono_Buffer(); - ~Mono_Buffer(); - - // Buffer used for all channels - Blip_Buffer* center(); - - // See Multi_Buffer - blargg_err_t sample_rate( long rate, int msec = blip_default_length ); - Multi_Buffer::sample_rate; - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); -}; - -// Stereo_Buffer uses three buffers (one for center) and outputs stereo sample pairs. -class Stereo_Buffer : public Multi_Buffer { -public: - Stereo_Buffer(); - ~Stereo_Buffer(); - - // Buffers used for all channels - Blip_Buffer* center(); - Blip_Buffer* left(); - Blip_Buffer* right(); - - // See Multi_Buffer - blargg_err_t sample_rate( long, int msec = blip_default_length ); - Multi_Buffer::sample_rate; - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int index ); - void end_frame( blip_time_t, bool added_stereo = true ); - - long samples_avail() const; - long read_samples( blip_sample_t*, long ); - -private: - enum { buf_count = 3 }; - Blip_Buffer bufs [buf_count]; - channel_t chan; - bool stereo_added; - bool was_stereo; - - void mix_stereo( blip_sample_t*, long ); - void mix_mono( blip_sample_t*, long ); -}; - - -// End of public interface - -inline blargg_err_t Multi_Buffer::sample_rate( long rate, int msec ) -{ - sample_rate_ = rate; - length_ = msec; - return blargg_success; -} - -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } - -inline Blip_Buffer* Stereo_Buffer::left() { return &bufs [1]; } - -inline Blip_Buffer* Stereo_Buffer::center() { return &bufs [0]; } - -inline Blip_Buffer* Stereo_Buffer::right() { return &bufs [2]; } - -inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; } - -inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int index ) { return chan; } - -inline long Multi_Buffer::sample_rate() const { return sample_rate_; } - -inline int Multi_Buffer::length() const { return length_; } - -inline Blip_Buffer* Mono_Buffer::center() { return &buf; } - -inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); } - -inline void Mono_Buffer::clear() { buf.clear(); } - -inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); } - -inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } - -inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); } - -#endif - diff --git a/Core/Nes_Apu/Nes_Apu.h b/Core/Nes_Apu/Nes_Apu.h deleted file mode 100644 index 6ce4c986..00000000 --- a/Core/Nes_Apu/Nes_Apu.h +++ /dev/null @@ -1,162 +0,0 @@ - -// NES 2A03 APU sound chip emulator - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NES_APU_H -#define NES_APU_H - -typedef long cpu_time_t; // CPU clock cycle count -typedef unsigned cpu_addr_t; // 16-bit memory address - -#include "Nes_Oscs.h" - -struct apu_snapshot_t; -class Nonlinear_Buffer; - -class Nes_Apu { -public: - Nes_Apu(); - ~Nes_Apu(); - - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); - - // Set memory reader callback used by DMC oscillator to fetch samples. - // When callback is invoked, 'user_data' is passed unchanged as the - // first parameter. - void dmc_reader( int (*callback)( void* user_data, cpu_addr_t ), void* user_data = NULL ); - - // All time values are the number of CPU clock cycles relative to the - // beginning of the current time frame. Before resetting the CPU clock - // count, call end_frame( last_cpu_time ). - - // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) - enum { start_addr = 0x4000 }; - enum { end_addr = 0x4017 }; - void write_register( cpu_time_t, cpu_addr_t, int data ); - - // Read from status register at 0x4015 - enum { status_addr = 0x4015 }; - int read_status( cpu_time_t ); - - // Run all oscillators up to specified time, end current time frame, then - // start a new time frame at time 0. Time frames have no effect on emulation - // and each can be whatever length is convenient. - void end_frame( cpu_time_t ); - -// Additional optional features (can be ignored without any problem) - - // Reset internal frame counter, registers, and all oscillators. - // Use PAL timing if pal_timing is true, otherwise use NTSC timing. - // Set the DMC oscillator's initial DAC value to initial_dmc_dac without - // any audible click. - void reset( bool pal_timing = false, int initial_dmc_dac = 0 ); - - // Save/load snapshot of exact emulation state - void save_snapshot( apu_snapshot_t* out ) const; - void load_snapshot( apu_snapshot_t const& ); - - // Set overall volume (default is 1.0) - void volume( double ); - - // Reset oscillator amplitudes. Must be called when clearing buffer while - // using non-linear sound. - void buffer_cleared(); - - // Set treble equalization (see notes.txt). - void treble_eq( const blip_eq_t& ); - - // Set sound output of specific oscillator to buffer. If buffer is NULL, - // the specified oscillator is muted and emulation accuracy is reduced. - // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, - // 2) Triangle, 3) Noise, 4) DMC. - enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* buffer ); - - // Set IRQ time callback that is invoked when the time of earliest IRQ - // may have changed, or NULL to disable. When callback is invoked, - // 'user_data' is passed unchanged as the first parameter. - void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); - - // Get time that APU-generated IRQ will occur if no further register reads - // or writes occur. If IRQ is already pending, returns irq_waiting. If no - // IRQ will occur, returns no_irq. - enum { no_irq = LONG_MAX / 2 + 1 }; - enum { irq_waiting = 0 }; - cpu_time_t earliest_irq() const; - - // Count number of DMC reads that would occur if 'run_until( t )' were executed. - // If last_read is not NULL, set *last_read to the earliest time that - // 'count_dmc_reads( time )' would result in the same result. - int count_dmc_reads( cpu_time_t t, cpu_time_t* last_read = NULL ) const; - - // Run APU until specified time, so that any DMC memory reads can be - // accounted for (i.e. inserting CPU wait states). - void run_until( cpu_time_t ); - -// End of public interface. -private: - friend class Nes_Nonlinearizer; - void enable_nonlinear( double volume ); -private: - // noncopyable - Nes_Apu( const Nes_Apu& ); - Nes_Apu& operator = ( const Nes_Apu& ); - - Nes_Osc* oscs [osc_count]; - Nes_Square square1; - Nes_Square square2; - Nes_Noise noise; - Nes_Triangle triangle; - Nes_Dmc dmc; - - cpu_time_t last_time; // has been run until this time in current frame - cpu_time_t earliest_irq_; - cpu_time_t next_irq; - int frame_period; - int frame_delay; // cycles until frame counter runs next - int frame; // current frame (0-3) - int osc_enables; - int frame_mode; - bool irq_flag; - void (*irq_notifier_)( void* user_data ); - void* irq_data; - Nes_Square::Synth square_synth; // shared by squares - - void irq_changed(); - void state_restored(); - - friend struct Nes_Dmc; -}; - -inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) -{ - assert(( "Nes_Apu::osc_output(): Index out of range", 0 <= osc && osc < osc_count )); - oscs [osc]->output = buf; -} - -inline cpu_time_t Nes_Apu::earliest_irq() const -{ - return earliest_irq_; -} - -inline void Nes_Apu::dmc_reader( int (*func)( void*, cpu_addr_t ), void* user_data ) -{ - dmc.rom_reader_data = user_data; - dmc.rom_reader = func; -} - -inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) -{ - irq_notifier_ = func; - irq_data = user_data; -} - -inline int Nes_Apu::count_dmc_reads( cpu_time_t time, cpu_time_t* last_read ) const -{ - return dmc.count_reads( time, last_read ); -} - -#endif - diff --git a/Core/Nes_Apu/Nes_Namco.h b/Core/Nes_Apu/Nes_Namco.h deleted file mode 100644 index 87d65858..00000000 --- a/Core/Nes_Apu/Nes_Namco.h +++ /dev/null @@ -1,86 +0,0 @@ - -// Namco 106 sound chip emulator - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NES_NAMCO_H -#define NES_NAMCO_H - -#include "Nes_Apu.h" - -struct namco_snapshot_t; - -class Nes_Namco { -public: - Nes_Namco(); - ~Nes_Namco(); - - // See Nes_Apu.h for reference. - void volume( double ); - void treble_eq( const blip_eq_t& ); - void output( Blip_Buffer* ); - enum { osc_count = 8 }; - void osc_output( int index, Blip_Buffer* ); - void reset(); - void end_frame( cpu_time_t ); - - // Read/write data register is at 0x4800 - enum { data_reg_addr = 0x4800 }; - void write_data( cpu_time_t, int ); - int read_data(); - - // Write-only address register is at 0xF800 - enum { addr_reg_addr = 0xF800 }; - void write_addr( int ); - - // to do: implement save/restore - void save_snapshot( namco_snapshot_t* out ); - void load_snapshot( namco_snapshot_t const& ); - -private: - // noncopyable - Nes_Namco( const Nes_Namco& ); - Nes_Namco& operator = ( const Nes_Namco& ); - - struct Namco_Osc { - long delay; - Blip_Buffer* output; - short last_amp; - short wave_pos; - }; - - Namco_Osc oscs [osc_count]; - - cpu_time_t last_time; - int addr_reg; - - enum { reg_count = 0x80 }; - BOOST::uint8_t reg [reg_count]; - Blip_Synth synth; - - BOOST::uint8_t& access(); - void run_until( cpu_time_t ); -}; - -inline void Nes_Namco::volume( double v ) { synth.volume( 0.10 / osc_count * v ); } - -inline void Nes_Namco::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } - -inline void Nes_Namco::write_addr( int v ) { addr_reg = v; } - -inline int Nes_Namco::read_data() { return access(); } - -inline void Nes_Namco::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Namco::write_data( cpu_time_t time, int data ) -{ - run_until( time ); - access() = data; -} - -#endif - diff --git a/Core/Nes_Apu/Nes_Oscs.h b/Core/Nes_Apu/Nes_Oscs.h deleted file mode 100644 index c1893041..00000000 --- a/Core/Nes_Apu/Nes_Oscs.h +++ /dev/null @@ -1,142 +0,0 @@ - -// Private oscillators used by Nes_Apu - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NES_OSCS_H -#define NES_OSCS_H - -#include "Blip_Buffer.h" - -class Nes_Apu; - -struct Nes_Osc -{ - unsigned char regs [4]; - bool reg_written [4]; - Blip_Buffer* output; - int length_counter;// length counter (0 if unused by oscillator) - int delay; // delay until next (potential) transition - int last_amp; // last amplitude oscillator was outputting - - void clock_length( int halt_mask ); - int period() const { - return (regs [3] & 7) * 0x100 + (regs [2] & 0xff); - } - void reset() { - delay = 0; - last_amp = 0; - } - int update_amp( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - return delta; - } -}; - -struct Nes_Envelope : Nes_Osc -{ - int envelope; - int env_delay; - - void clock_envelope(); - int volume() const; - void reset() { - envelope = 0; - env_delay = 0; - Nes_Osc::reset(); - } -}; - -// Nes_Square -struct Nes_Square : Nes_Envelope -{ - enum { negate_flag = 0x08 }; - enum { shift_mask = 0x07 }; - enum { phase_range = 8 }; - int phase; - int sweep_delay; - - typedef Blip_Synth Synth; - const Synth* synth; // shared between squares - - void clock_sweep( int adjust ); - void run( cpu_time_t, cpu_time_t ); - void reset() { - sweep_delay = 0; - Nes_Envelope::reset(); - } -}; - -// Nes_Triangle -struct Nes_Triangle : Nes_Osc -{ - enum { phase_range = 16 }; - int phase; - int linear_counter; - Blip_Synth synth; - - int calc_amp() const; - void run( cpu_time_t, cpu_time_t ); - void clock_linear_counter(); - void reset() { - linear_counter = 0; - phase = phase_range; - Nes_Osc::reset(); - } -}; - -// Nes_Noise -struct Nes_Noise : Nes_Envelope -{ - int noise; - Blip_Synth synth; - - void run( cpu_time_t, cpu_time_t ); - void reset() { - noise = 1 << 14; - Nes_Envelope::reset(); - } -}; - -// Nes_Dmc -struct Nes_Dmc : Nes_Osc -{ - int address; // address of next byte to read - int period; - //int length_counter; // bytes remaining to play (already defined in Nes_Osc) - int buf; - int bits_remain; - int bits; - bool buf_empty; - bool silence; - - enum { loop_flag = 0x40 }; - - int dac; - - cpu_time_t next_irq; - bool irq_enabled; - bool irq_flag; - bool pal_mode; - bool nonlinear; - - int (*rom_reader)( void*, cpu_addr_t ); // needs to be initialized to rom read function - void* rom_reader_data; - - Nes_Apu* apu; - - Blip_Synth synth; - - void start(); - void write_register( int, int ); - void run( cpu_time_t, cpu_time_t ); - void recalc_irq(); - void fill_buffer(); - void reload_sample(); - void reset(); - int count_reads( cpu_time_t, cpu_time_t* ) const; -}; - -#endif - diff --git a/Core/Nes_Apu/Nes_Vrc6.h b/Core/Nes_Apu/Nes_Vrc6.h deleted file mode 100644 index 038cd587..00000000 --- a/Core/Nes_Apu/Nes_Vrc6.h +++ /dev/null @@ -1,85 +0,0 @@ - -// Konami VRC6 sound chip emulator - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NES_VRC6_H -#define NES_VRC6_H - -#include "Nes_Apu.h" - -struct vrc6_snapshot_t; - -class Nes_Vrc6 { -public: - Nes_Vrc6(); - ~Nes_Vrc6(); - - // See Nes_Apu.h for reference - void reset(); - void volume( double ); - void treble_eq( blip_eq_t const& ); - void output( Blip_Buffer* ); - enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); - void end_frame( cpu_time_t ); - void save_snapshot( vrc6_snapshot_t* ) const; - void load_snapshot( vrc6_snapshot_t const& ); - - // Oscillator 0 write-only registers are at $9000-$9002 - // Oscillator 1 write-only registers are at $A000-$A002 - // Oscillator 2 write-only registers are at $B000-$B002 - enum { reg_count = 3 }; - enum { base_addr = 0x9000 }; - enum { addr_step = 0x1000 }; - void write_osc( cpu_time_t, int osc, int reg, int data ); - -private: - // noncopyable - Nes_Vrc6( const Nes_Vrc6& ); - Nes_Vrc6& operator = ( const Nes_Vrc6& ); - - struct Vrc6_Osc - { - BOOST::uint8_t regs [3]; - Blip_Buffer* output; - int delay; - int last_amp; - int phase; - int amp; // only used by saw - - int period() const - { - return (regs [2] & 0x0f) * 0x100L + regs [1] + 1; - } - }; - - Vrc6_Osc oscs [osc_count]; - cpu_time_t last_time; - - Blip_Synth saw_synth; - Blip_Synth square_synth; - - void run_until( cpu_time_t ); - void run_square( Vrc6_Osc& osc, cpu_time_t ); - void run_saw( cpu_time_t ); -}; - -struct vrc6_snapshot_t -{ - BOOST::uint8_t regs [3] [3]; - BOOST::uint8_t saw_amp; - BOOST::uint16_t delays [3]; - BOOST::uint8_t phases [3]; - BOOST::uint8_t unused; -}; -BOOST_STATIC_ASSERT( sizeof (vrc6_snapshot_t) == 20 ); - -inline void Nes_Vrc6::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -#endif - diff --git a/Core/Nes_Apu/Nonlinear_Buffer.h b/Core/Nes_Apu/Nonlinear_Buffer.h deleted file mode 100644 index 8856b544..00000000 --- a/Core/Nes_Apu/Nonlinear_Buffer.h +++ /dev/null @@ -1,65 +0,0 @@ - -// NES non-linear audio output handling. - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NONLINEAR_BUFFER_H -#define NONLINEAR_BUFFER_H - -#include "Multi_Buffer.h" -class Nes_Apu; - -// Use to make samples non-linear in Blip_Buffer used for triangle, noise, and DMC only -class Nes_Nonlinearizer { -public: - Nes_Nonlinearizer(); - - // Must be called when buffer is cleared - void clear() { accum = 0x8000; } - - // Enable/disable non-linear output - void enable( Nes_Apu&, bool = true ); - - // Make at most 'count' samples in buffer non-linear and return number - // of samples modified. This many samples must then be read out of the buffer. - long make_nonlinear( Blip_Buffer&, long count ); - -private: - enum { shift = 5 }; - enum { half = 0x8000 >> shift }; - enum { entry_mask = half * 2 - 1 }; - BOOST::uint16_t table [half * 2]; - long accum; - bool nonlinear; -}; - -class Nonlinear_Buffer : public Multi_Buffer { -public: - Nonlinear_Buffer(); - ~Nonlinear_Buffer(); - - // Enable/disable non-linear output - void enable_nonlinearity( Nes_Apu&, bool = true ); - - // Blip_Buffer to output other sound chips to - Blip_Buffer* buffer() { return &buf; } - - // See Multi_Buffer.h - blargg_err_t sample_rate( long rate, int msec = blip_default_length ); - Multi_Buffer::sample_rate; - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); - -private: - Blip_Buffer buf; - Blip_Buffer tnd; - Nes_Nonlinearizer nonlinearizer; -}; - -#endif - diff --git a/Core/Nes_Apu/apu_snapshot.h b/Core/Nes_Apu/apu_snapshot.h deleted file mode 100644 index 5b894cc1..00000000 --- a/Core/Nes_Apu/apu_snapshot.h +++ /dev/null @@ -1,75 +0,0 @@ - -// NES APU snapshot support - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef APU_SNAPSHOT_H -#define APU_SNAPSHOT_H - -#include "blargg_common.h" - -struct apu_snapshot_t -{ - typedef BOOST::uint8_t byte; - - typedef byte env_t [3]; - /*struct env_t { - byte delay; - byte env;3 - byte written; - };*/ - - byte w40xx [0x14]; // $4000-$4013 - byte w4015; // enables - byte w4017; // mode - BOOST::uint16_t delay; - byte step; - byte irq_flag; - - struct square_t { - BOOST::uint16_t delay; - env_t env; - byte length; - byte phase; - byte swp_delay; - byte swp_reset; - byte unused [1]; - }; - - square_t square1; - square_t square2; - - struct triangle_t { - BOOST::uint16_t delay; - byte length; - byte phase; - byte linear_counter; - byte linear_mode; - } triangle; - - struct noise_t { - BOOST::uint16_t delay; - env_t env; - byte length; - BOOST::uint16_t shift_reg; - } noise; - - struct dmc_t { - BOOST::uint16_t delay; - BOOST::uint16_t remain; - BOOST::uint16_t addr; - byte buf; - byte bits_remain; - byte bits; - byte buf_empty; - byte silence; - byte irq_flag; - } dmc; - - enum { tag = 'APUR' }; - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (apu_snapshot_t) == 72 ); - -#endif - diff --git a/Core/Nes_Apu/blargg_common.h b/Core/Nes_Apu/blargg_common.h deleted file mode 100644 index 941c192b..00000000 --- a/Core/Nes_Apu/blargg_common.h +++ /dev/null @@ -1,181 +0,0 @@ - -// Sets up common environment for Shay Green's libraries. -// -// Don't modify this file directly; #define HAVE_CONFIG_H and put your -// configuration into "config.h". - -// Copyright (C) 2004-2005 Shay Green. - -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -// Allow prefix configuration file *which can re-include blargg_common.h* -// (probably indirectly). -#ifdef HAVE_CONFIG_H - #undef BLARGG_COMMON_H - #include "config.h" - #define BLARGG_COMMON_H -#endif - -// Source files use #include BLARGG_ENABLE_OPTIMIZER before performance-critical code -#ifndef BLARGG_ENABLE_OPTIMIZER - #define BLARGG_ENABLE_OPTIMIZER "blargg_common.h" -#endif - -// Source files have #include BLARGG_SOURCE_BEGIN at the beginning -#ifndef BLARGG_SOURCE_BEGIN - #define BLARGG_SOURCE_BEGIN "blargg_source.h" -#endif - -// Determine compiler's language support - -#if defined (__MWERKS__) - // Metrowerks CodeWarrior - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -#elif defined (_MSC_VER) - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - // Microsoft Visual C++ - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -#elif defined (__GNUC__) - // GNU C++ - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #define BLARGG_COMPILER_HAS_BOOL 1 - -#elif defined (__MINGW32__) - // Mingw? - #define BLARGG_COMPILER_HAS_BOOL 1 - -#elif __cplusplus < 199711 - // Pre-ISO C++ compiler - #define BLARGG_COMPILER_HAS_BOOL 0 - #define BLARGG_NEW new - #define STATIC_CAST( type ) (type) - -#endif - -// STATIC_CAST(T) (expr) -> static_cast< T > (expr) -#ifndef STATIC_CAST - #define STATIC_CAST( type ) static_cast< type > -#endif - -// Set up boost -#include "boost\config.hpp" -#ifndef BOOST_MINIMAL - #define BOOST boost - #ifndef BLARGG_COMPILER_HAS_NAMESPACE - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #endif - #ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 1 - #endif -#endif - -// Bool support -#ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 1 -#elif !BLARGG_COMPILER_HAS_BOOL - typedef int bool; - const bool true = 1; - const bool false = 0; -#endif - -// Set up namespace support - -#ifndef BLARGG_COMPILER_HAS_NAMESPACE - #define BLARGG_COMPILER_HAS_NAMESPACE 0 -#endif - -#ifndef BLARGG_USE_NAMESPACE - #define BLARGG_USE_NAMESPACE BLARGG_COMPILER_HAS_NAMESPACE -#endif - -#ifndef BOOST - #if BLARGG_USE_NAMESPACE - #define BOOST boost - #else - #define BOOST - #endif -#endif - -#undef BLARGG_BEGIN_NAMESPACE -#undef BLARGG_END_NAMESPACE -#if BLARGG_USE_NAMESPACE - #define BLARGG_BEGIN_NAMESPACE( name ) namespace name { - #define BLARGG_END_NAMESPACE } -#else - #define BLARGG_BEGIN_NAMESPACE( name ) - #define BLARGG_END_NAMESPACE -#endif - -#if BLARGG_USE_NAMESPACE - #define STD std -#else - #define STD -#endif - -// BOOST::uint8_t, BOOST::int16_t, etc. -#include "boost/cstdint.hpp" - -// BOOST_STATIC_ASSERT( expr ) -#include "boost/static_assert.hpp" - -// Common standard headers -#if BLARGG_COMPILER_HAS_NAMESPACE - #include - #include - #include -#else - #include - #include -#endif - -// blargg_err_t (NULL on success, otherwise error string) -typedef const char* blargg_err_t; -const blargg_err_t blargg_success = 0; - -// BLARGG_NEW is used in place of 'new' to create objects. By default, -// nothrow new is used. -#ifndef BLARGG_NEW - #define BLARGG_NEW new (STD::nothrow) -#endif - -// BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN -// Only needed if modules are used which must know byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) - #if defined (__powerc) || defined (macintosh) - #define BLARGG_BIG_ENDIAN 1 - - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_LITTLE_ENDIAN 1 - - #endif -#endif - -// BLARGG_NONPORTABLE (allow use of nonportable optimizations/features) -#ifndef BLARGG_NONPORTABLE - #define BLARGG_NONPORTABLE 0 -#endif -#ifdef BLARGG_MOST_PORTABLE - #error "BLARGG_MOST_PORTABLE has been removed; use BLARGG_NONPORTABLE." -#endif - -// BLARGG_CPU_* -#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86) - #if defined (__powerc) - #define BLARGG_CPU_POWERPC 1 - - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_CPU_X86 1 - - #endif -#endif - -#endif - diff --git a/Core/Nes_Apu/blargg_source.h b/Core/Nes_Apu/blargg_source.h deleted file mode 100644 index f74f311a..00000000 --- a/Core/Nes_Apu/blargg_source.h +++ /dev/null @@ -1,43 +0,0 @@ - -// By default, #included at beginning of library source files - -// Copyright (C) 2005 Shay Green. - -#ifndef BLARGG_SOURCE_H -#define BLARGG_SOURCE_H - -// If debugging is enabled, abort program if expr is false. Meant for checking -// internal state and consistency. A failed assertion indicates a bug in the module. -// void assert( bool expr ); -#include - -// If debugging is enabled and expr is false, abort program. Meant for checking -// caller-supplied parameters and operations that are outside the control of the -// module. A failed requirement indicates a bug outside the module. -// void require( bool expr ); -#undef require -#define require( expr ) assert(( "unmet requirement", expr )) - -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -#undef dprintf -#define dprintf (1) ? ((void) 0) : (void) - -// If enabled, evaluate expr and if false, make debug log entry with source file -// and line. Meant for finding situations that should be examined further, but that -// don't indicate a problem. In all cases, execution continues normally. -#undef check -#define check( expr ) ((void) 0) - -// If expr returns non-NULL error string, return it from current function, otherwise continue. -#define BLARGG_RETURN_ERR( expr ) do { \ - blargg_err_t blargg_return_err_ = (expr); \ - if ( blargg_return_err_ ) return blargg_return_err_; \ - } while ( 0 ) - -// If ptr is NULL, return out of memory error string. -#define BLARGG_CHECK_ALLOC( ptr ) do { if ( !(ptr) ) return "Out of memory"; } while ( 0 ) - -#endif - diff --git a/Core/Nes_Apu/boost/config.hpp b/Core/Nes_Apu/boost/config.hpp deleted file mode 100644 index f271715c..00000000 --- a/Core/Nes_Apu/boost/config.hpp +++ /dev/null @@ -1,13 +0,0 @@ - -// Boost substitute. For full boost library see http://boost.org - -#ifndef BOOST_CONFIG_HPP -#define BOOST_CONFIG_HPP - -#define BOOST_MINIMAL 1 - -#define BLARGG_BEGIN_NAMESPACE( name ) -#define BLARGG_END_NAMESPACE - -#endif - diff --git a/Core/Nes_Apu/boost/cstdint.hpp b/Core/Nes_Apu/boost/cstdint.hpp deleted file mode 100644 index e446dfdd..00000000 --- a/Core/Nes_Apu/boost/cstdint.hpp +++ /dev/null @@ -1,42 +0,0 @@ - -// Boost substitute. For full boost library see http://boost.org - -#ifndef BOOST_CSTDINT_HPP -#define BOOST_CSTDINT_HPP - -#if BLARGG_USE_NAMESPACE - #include -#else - #include -#endif - -BLARGG_BEGIN_NAMESPACE( boost ) - -#if UCHAR_MAX != 0xFF || SCHAR_MAX != 0x7F -# error "No suitable 8-bit type available" -#endif - -typedef unsigned char uint8_t; -typedef signed char int8_t; - -#if USHRT_MAX != 0xFFFF -# error "No suitable 16-bit type available" -#endif - -typedef short int16_t; -typedef unsigned short uint16_t; - -#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 -# error "No suitable 32-bit type available" -#endif - -BLARGG_END_NAMESPACE - -#endif - diff --git a/Core/Nes_Apu/boost/static_assert.hpp b/Core/Nes_Apu/boost/static_assert.hpp deleted file mode 100644 index 66927ccb..00000000 --- a/Core/Nes_Apu/boost/static_assert.hpp +++ /dev/null @@ -1,22 +0,0 @@ - -// Boost substitute. For full boost library see http://boost.org - -#ifndef BOOST_STATIC_ASSERT_HPP -#define BOOST_STATIC_ASSERT_HPP - -#if defined (_MSC_VER) && _MSC_VER <= 1200 - // MSVC6 can't handle the ##line concatenation - #define BOOST_STATIC_ASSERT( expr ) struct { int n [1 / ((expr) ? 1 : 0)]; } - -#else - #define BOOST_STATIC_ASSERT3( expr, line ) \ - typedef int boost_static_assert_##line [1 / ((expr) ? 1 : 0)] - - #define BOOST_STATIC_ASSERT2( expr, line ) BOOST_STATIC_ASSERT3( expr, line ) - - #define BOOST_STATIC_ASSERT( expr ) BOOST_STATIC_ASSERT2( expr, __LINE__ ) - -#endif - -#endif - diff --git a/Core/NoiseChannel.h b/Core/NoiseChannel.h new file mode 100644 index 00000000..7dee885f --- /dev/null +++ b/Core/NoiseChannel.h @@ -0,0 +1,70 @@ +#pragma once +#include "stdafx.h" +#include "../BlipBuffer/Blip_Buffer.h" +#include "APU.h" +#include "IMemoryHandler.h" +#include "ApuEnvelope.h" + +class NoiseChannel : public ApuEnvelope +{ +private: + const vector _noisePeriodLookupTable = { { 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 } }; + + //On power-up, the shift register is loaded with the value 1. + uint16_t _shiftRegister = 1; + bool _modeFlag = false; + + bool IsMuted() + { + //The mixer receives the current envelope volume except when Bit 0 of the shift register is set, or The length counter is zero + return (_shiftRegister & 0x01) == 0x01; + } + +public: + NoiseChannel() + { + SetVolume(0.0741); + } + + void GetMemoryRanges(MemoryRanges &ranges) + { + ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x400C, 0x400F); + } + + void WriteRAM(uint16_t addr, uint8_t value) + { + APU::StaticRun(); + switch(addr & 0x03) { + case 0: //400C + InitializeLengthCounter((value & 0x20) == 0x20); + InitializeEnvelope(value); + break; + + case 2: //400E + _period = _noisePeriodLookupTable[value & 0x0F]; + break; + + case 3: //400F + LoadLengthCounter(value >> 3); + + //The envelope is also restarted. + ResetEnvelope(); + break; + } + } + + void Clock() + { + uint32_t volume = GetVolume(); + //Feedback is calculated as the exclusive-OR of bit 0 and one other bit: bit 6 if Mode flag is set, otherwise bit 1. + uint16_t feedback = (_shiftRegister & 0x01) ^ ((_shiftRegister >> (_modeFlag ? 6 : 1)) & 0x01); + _shiftRegister >>= 1; + _shiftRegister |= (feedback << 14); + + if(IsMuted()) { + AddOutput(0); + } else { + AddOutput(GetVolume()); + } + } +}; \ No newline at end of file diff --git a/Core/SquareChannel.h b/Core/SquareChannel.h new file mode 100644 index 00000000..8b730b7c --- /dev/null +++ b/Core/SquareChannel.h @@ -0,0 +1,154 @@ +#pragma once +#include "stdafx.h" +#include "../BlipBuffer/Blip_Buffer.h" +#include "APU.h" +#include "IMemoryHandler.h" +#include "ApuEnvelope.h" + +class SquareChannel : public ApuEnvelope +{ +private: + const vector> _dutySequences = { { + { 0, 1, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 1, 1, 1, 1, 0, 0, 0 }, + { 1, 0, 0, 1, 1, 1, 1, 1 } + } }; + + bool _isChannel1 = false; + + uint8_t _duty = 0; + uint8_t _dutyPos = 0; + + bool _sweepEnabled = false; + uint8_t _sweepPeriod = 0; + bool _sweepNegate = false; + uint8_t _sweepShift = 0; + bool _reloadSweep = false; + uint8_t _sweepDivider = 0; + uint32_t _sweepTargetPeriod = 0; + + bool IsMuted() + { + //A period of t < 8, either set explicitly or via a sweep period update, silences the corresponding pulse channel. + return _period < 8 || _sweepTargetPeriod > 0x7FF; + } + +public: + SquareChannel(bool isChannel1) + { + SetVolume(0.1128); + _isChannel1 = isChannel1; + } + + void GetMemoryRanges(MemoryRanges &ranges) + { + if(_isChannel1) { + ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4000, 0x4003); + } else { + ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4004, 0x4007); + } + } + + void WriteRAM(uint16_t addr, uint8_t value) + { + APU::StaticRun(); + switch(addr & 0x03) { + case 0: //4000 & 4004 + InitializeLengthCounter((value & 0x20) == 0x20); + InitializeEnvelope(value); + + _duty = (value & 0xC0) >> 6; + break; + + case 1: //4001 & 4005 + InitializeSweep(value); + break; + + case 2: //4002 & 4006 + _period &= ~0x00FF; + _period |= value; + break; + + case 3: //4003 & 4007 + LoadLengthCounter(value >> 3); + + _period &= ~0x0700; + _period |= (value & 0x07) << 8; + + //The sequencer is restarted at the first value of the current sequence. + _timer = _period + 1; + _dutyPos = 0; + + //The envelope is also restarted. + ResetEnvelope(); + break; + } + } + + void InitializeSweep(uint8_t regValue) + { + _sweepEnabled = (regValue & 0x80) == 0x80; + _sweepNegate = (regValue & 0x08) == 0x08; + + //The divider's period is set to P + 1 + _sweepPeriod = ((regValue & 0x70) >> 4) + 1; + _sweepShift = (regValue & 0x07); + + //Side effects: Sets the reload flag + _reloadSweep = true; + } + + void UpdateTargetPeriod(bool setPeriod) + { + uint16_t shiftResult = (_period >> _sweepShift); + if(_sweepNegate) { + _sweepTargetPeriod = _period - shiftResult; + if(_isChannel1) { + // As a result, a negative sweep on pulse channel 1 will subtract the shifted period value minus 1 + _sweepTargetPeriod++; + } + } else { + _sweepTargetPeriod = _period + shiftResult; + } + if(setPeriod && _sweepShift > 0 && _period >= 8 && _sweepTargetPeriod <= 0x7FF) { + _period = _sweepTargetPeriod; + } + } + + void TickSweep() + { + if(_reloadSweep) { + if(_sweepDivider == 0 && _sweepEnabled) { + //If the divider's counter was zero before the reload and the sweep is enabled, the pulse's period is also adjusted + UpdateTargetPeriod(true); + } + _sweepDivider = _sweepPeriod; + _reloadSweep = false; + } else { + if(_sweepDivider > 0) { + _sweepDivider--; + } else if(_sweepEnabled) { + UpdateTargetPeriod(true); + _sweepDivider = _sweepPeriod; + } + } + } + + void Run(uint32_t targetCycle) + { + UpdateTargetPeriod(false); + BaseApuChannel::Run(targetCycle); + } + + void Clock() + { + _dutyPos = (_dutyPos - 1) & 0x07; + + if(IsMuted()) { + AddOutput(0); + } else { + AddOutput(_dutySequences[_duty][_dutyPos] * GetVolume()); + } + } +}; \ No newline at end of file diff --git a/Core/TriangleChannel.h b/Core/TriangleChannel.h new file mode 100644 index 00000000..a25c9855 --- /dev/null +++ b/Core/TriangleChannel.h @@ -0,0 +1,82 @@ +#pragma once +#include "stdafx.h" +#include "../BlipBuffer/Blip_Buffer.h" +#include "APU.h" +#include "IMemoryHandler.h" +#include "ApuEnvelope.h" + +class TriangleChannel : public ApuLengthCounter +{ +private: + const vector _sequence = { { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } }; + + uint8_t _linearCounter = 0; + uint8_t _linearCounterReload = 0; + bool _linearReloadFlag = false; + bool _linearControlFlag = false; + + uint8_t _sequencePosition = 0; + +public: + TriangleChannel() + { + _clockDivider = 1; //Triangle clocks at the same speed as the cpu + SetVolume(0.12765); + } + + void GetMemoryRanges(MemoryRanges &ranges) + { + ranges.AddHandler(MemoryType::RAM, MemoryOperation::Write, 0x4008, 0x400B); + } + + void WriteRAM(uint16_t addr, uint8_t value) + { + APU::StaticRun(); + switch(addr & 0x03) { + case 0: //4008 + _linearControlFlag = (value & 0x80) == 0x80; + _linearCounterReload = value & 0x7F; + + InitializeLengthCounter(_linearControlFlag); + break; + + case 2: //400A + _period &= ~0x00FF; + _period |= value; + break; + + case 3: //400B + LoadLengthCounter(value >> 3); + + _period &= ~0xFF00; + _period |= (value & 0x03) << 8; + + //Side effects Sets the linear counter reload flag + _linearReloadFlag = true; + break; + } + } + + void Clock() + { + //The sequencer is clocked by the timer as long as both the linear counter and the length counter are nonzero. + if(_lengthCounter > 0 && _linearCounter > 0) { + _sequencePosition = (_sequencePosition + 1) & 0x1F; + + AddOutput(_sequence[_sequencePosition]); + } + } + + void TickLinearCounter() + { + if(_linearReloadFlag) { + _linearCounter = _linearCounterReload; + } else if(_linearCounter > 0) { + _linearCounter--; + } + + if(!_linearControlFlag) { + _linearReloadFlag = false; + } + } +}; \ No newline at end of file diff --git a/Dependencies/Nes_Apu.Debug.x64.lib b/Dependencies/Nes_Apu.Debug.x64.lib deleted file mode 100644 index 1aaa7dbb..00000000 Binary files a/Dependencies/Nes_Apu.Debug.x64.lib and /dev/null differ diff --git a/Dependencies/Nes_Apu.Debug.x86.lib b/Dependencies/Nes_Apu.Debug.x86.lib deleted file mode 100644 index 6301f7f7..00000000 Binary files a/Dependencies/Nes_Apu.Debug.x86.lib and /dev/null differ diff --git a/Dependencies/Nes_Apu.Release.x64.lib b/Dependencies/Nes_Apu.Release.x64.lib deleted file mode 100644 index c7a4c7f4..00000000 Binary files a/Dependencies/Nes_Apu.Release.x64.lib and /dev/null differ diff --git a/Dependencies/Nes_Apu.Release.x86.lib b/Dependencies/Nes_Apu.Release.x86.lib deleted file mode 100644 index 4984a22d..00000000 Binary files a/Dependencies/Nes_Apu.Release.x86.lib and /dev/null differ diff --git a/InteropDLL/InteropDLL.vcxproj b/InteropDLL/InteropDLL.vcxproj index 37813e44..a010a5c7 100644 --- a/InteropDLL/InteropDLL.vcxproj +++ b/InteropDLL/InteropDLL.vcxproj @@ -254,6 +254,16 @@ Create + + + {cf35d78c-f710-41d2-968f-c46accff6f07} + false + true + false + true + false + + diff --git a/InteropDLL/stdafx.h b/InteropDLL/stdafx.h index ac798d3c..1f5931ee 100644 --- a/InteropDLL/stdafx.h +++ b/InteropDLL/stdafx.h @@ -45,6 +45,5 @@ #pragma comment(lib, MESEN_LIBRARY_PATH"Utilities.lib") #pragma comment(lib, MESEN_LIBRARY_PATH"Windows.lib") #pragma comment(lib, "../Dependencies/DirectXTK"MESEN_LIBRARY_SUFFIX) -#pragma comment(lib, "../Dependencies/Nes_Apu"MESEN_LIBRARY_SUFFIX) #define DllExport __declspec(dllexport) \ No newline at end of file diff --git a/NES.sln b/NES.sln index b523a3ee..93f7b0f5 100644 --- a/NES.sln +++ b/NES.sln @@ -6,6 +6,7 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "Core\Core.vcxproj", "{78FEF1A1-6DF1-4CBB-A373-AE6FA7CE5CE0}" ProjectSection(ProjectDependencies) = postProject {B5330148-E8C7-46BA-B54E-69BE59EA337D} = {B5330148-E8C7-46BA-B54E-69BE59EA337D} + {CF35D78C-F710-41D2-968F-C46ACCFF6F07} = {CF35D78C-F710-41D2-968F-C46ACCFF6F07} {7761E790-B42C-4179-8550-8365FF9EB23E} = {7761E790-B42C-4179-8550-8365FF9EB23E} EndProjectSection EndProject @@ -29,6 +30,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PGOHelper", "PGOHelper\PGOH {37749BB2-FA78-4EC9-8990-5628FC0BBA19} = {37749BB2-FA78-4EC9-8990-5628FC0BBA19} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlipBuffer", "BlipBuffer\BlipBuffer.vcxproj", "{CF35D78C-F710-41D2-968F-C46ACCFF6F07}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -85,6 +88,13 @@ Global {38D74EE1-5276-4D24-AABC-104B912A27D2}.Release|x64.Build.0 = Release|x64 {38D74EE1-5276-4D24-AABC-104B912A27D2}.Release|x86.ActiveCfg = Release|Win32 {38D74EE1-5276-4D24-AABC-104B912A27D2}.Release|x86.Build.0 = Release|Win32 + {CF35D78C-F710-41D2-968F-C46ACCFF6F07}.Debug|x64.ActiveCfg = Debug|x64 + {CF35D78C-F710-41D2-968F-C46ACCFF6F07}.Debug|x64.Build.0 = Debug|x64 + {CF35D78C-F710-41D2-968F-C46ACCFF6F07}.Debug|x86.ActiveCfg = Debug|Win32 + {CF35D78C-F710-41D2-968F-C46ACCFF6F07}.Debug|x86.Build.0 = Debug|Win32 + {CF35D78C-F710-41D2-968F-C46ACCFF6F07}.Release|x64.ActiveCfg = Release|x64 + {CF35D78C-F710-41D2-968F-C46ACCFF6F07}.Release|x86.ActiveCfg = Release|Win32 + {CF35D78C-F710-41D2-968F-C46ACCFF6F07}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE