Sound support (using Blargg's Nes_Snd_Emu for now)

This commit is contained in:
Souryo 2014-06-22 22:15:35 -04:00
parent bed43cec62
commit 8c079e5ca1
30 changed files with 1931 additions and 6 deletions

62
Core/APU.cpp Normal file
View file

@ -0,0 +1,62 @@
#pragma once
#include "stdafx.h"
#include "APU.h"
#include "CPU.h"
void output_samples(const blip_sample_t*, size_t count);
APU* APU::Instance = nullptr;
IAudioDevice* APU::AudioDevice = nullptr;
APU::APU()
{
blargg_err_t error = _buf.sample_rate(44100);
if(error) {
//report_error(error);
}
_buf.clock_rate(1789773);
_apu.output(&_buf);
_apu.dmc_reader(&APU::DMCRead);
//_apu.irq_notifier(irq_changed);
}
int APU::DMCRead(void*, cpu_addr_t addr)
{
return APU::Instance->ReadRAM(addr);
}
uint8_t APU::ReadRAM(uint16_t addr)
{
switch(addr) {
case 0x4015:
return _apu.read_status(0);
break;
}
return 0;
}
void APU::WriteRAM(uint16_t addr, uint8_t value)
{
_apu.write_register(0, addr, value);
}
void APU::Exec(uint32_t executedCycles)
{
_apu.end_frame(executedCycles);
_buf.end_frame(executedCycles);
// Read some samples out of Blip_Buffer if there are enough to
// fill our output buffer
const size_t out_size = 4096;
blip_sample_t out_buf[out_size];
if(_buf.samples_avail() >= out_size) {
size_t count = _buf.read_samples(out_buf, out_size);
APU::AudioDevice->PlayBuffer(out_buf, count * sizeof(blip_sample_t));
}
}

35
Core/APU.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "IAudioDevice.h"
#include "Nes_Apu/Nes_Apu.h"
class APU : public IMemoryHandler
{
Nes_Apu _apu;
Blip_Buffer _buf;
private:
static IAudioDevice* AudioDevice;
static int DMCRead(void*, cpu_addr_t addr);
static APU* Instance;
public:
APU();
vector<std::array<uint16_t, 2>> GetRAMAddresses()
{
return{ { { 0x4000, 0x4013 } }, { { 0x4015, 0x4015 } }, {{ 0x4017, 0x4017 } } };
}
static void RegisterAudioDevice(IAudioDevice *audioDevice)
{
APU::AudioDevice = audioDevice;
}
uint8_t ReadRAM(uint16_t addr);
void WriteRAM(uint16_t addr, uint8_t value);
void Exec(uint32_t executedCycles);
};

View file

@ -12,11 +12,13 @@ Console::Console(wstring filename)
_memoryManager.reset(new MemoryManager(_mapper->GetHeader()));
_cpu.reset(new CPU(_memoryManager.get()));
_ppu.reset(new PPU(_memoryManager.get()));
_apu.reset(new APU());
_controlManager.reset(new ControlManager());
_memoryManager->RegisterIODevice(_mapper.get());
_memoryManager->RegisterIODevice(_ppu.get());
_memoryManager->RegisterIODevice(_apu.get());
_memoryManager->RegisterIODevice(_controlManager.get());
Reset();
@ -57,12 +59,15 @@ void Console::Run()
Timer fpsTimer;
uint32_t lastFrameCount = 0;
double elapsedTime = 0;
uint32_t executedCycles = 0;
uint32_t cycleCount = 0;
while(true) {
executedCycles += _cpu->Exec();
uint32_t executedCycles = _cpu->Exec();
_ppu->Exec();
_apu->Exec(executedCycles);
if(CheckFlag(EmulationFlags::LimitFPS) && executedCycles >= 29780) {
cycleCount += executedCycles;
if(CheckFlag(EmulationFlags::LimitFPS) && cycleCount >= 29780) {
double targetTime = 16.638935108153078202995008319468;
elapsedTime = clockTimer.GetElapsedMS();
while(targetTime > elapsedTime) {
@ -71,7 +76,7 @@ void Console::Run()
}
elapsedTime = clockTimer.GetElapsedMS();
}
executedCycles = 0;
cycleCount = 0;
clockTimer.Reset();
}

View file

@ -3,6 +3,7 @@
#include "stdafx.h"
#include "CPU.h"
#include "PPU.h"
#include "APU.h"
#include "BaseMapper.h"
#include "MemoryManager.h"
#include "ControlManager.h"
@ -19,6 +20,7 @@ class Console
unique_ptr<CPU> _cpu;
unique_ptr<PPU> _ppu;
unique_ptr<APU> _apu;
unique_ptr<BaseMapper> _mapper;
unique_ptr<ControlManager> _controlManager;
unique_ptr<MemoryManager> _memoryManager;

View file

@ -87,12 +87,25 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="APU.h" />
<ClInclude Include="BaseMapper.h" />
<ClInclude Include="ControlManager.h" />
<ClInclude Include="IAudioDevice.h" />
<ClInclude Include="IControlDevice.h" />
<ClInclude Include="IMemoryHandler.h" />
<ClInclude Include="Console.h" />
<ClInclude Include="IVideoDevice.h" />
<ClInclude Include="Nes_Apu\apu_snapshot.h" />
<ClInclude Include="Nes_Apu\blargg_common.h" />
<ClInclude Include="Nes_Apu\blargg_source.h" />
<ClInclude Include="Nes_Apu\Blip_Buffer.h" />
<ClInclude Include="Nes_Apu\Blip_Synth.h" />
<ClInclude Include="Nes_Apu\Multi_Buffer.h" />
<ClInclude Include="Nes_Apu\Nes_Apu.h" />
<ClInclude Include="Nes_Apu\Nes_Namco.h" />
<ClInclude Include="Nes_Apu\Nes_Oscs.h" />
<ClInclude Include="Nes_Apu\Nes_Vrc6.h" />
<ClInclude Include="Nes_Apu\Nonlinear_Buffer.h" />
<ClInclude Include="PPU.h" />
<ClInclude Include="CPU.h" />
<ClInclude Include="MemoryManager.h" />
@ -101,6 +114,7 @@
<ClInclude Include="targetver.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="APU.cpp" />
<ClCompile Include="Console.cpp" />
<ClCompile Include="ControlManager.cpp" />
<ClCompile Include="MemoryManager.cpp" />

View file

@ -13,6 +13,9 @@
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Header Files\Nes_Apu">
<UniqueIdentifier>{c6dc2048-98f6-4551-89dc-830f12f1bb2e}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@ -51,6 +54,45 @@
<ClInclude Include="ControlManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="APU.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\apu_snapshot.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\blargg_common.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\blargg_source.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\Blip_Buffer.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\Blip_Synth.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\Multi_Buffer.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\Nes_Apu.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\Nes_Namco.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\Nes_Oscs.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\Nes_Vrc6.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="Nes_Apu\Nonlinear_Buffer.h">
<Filter>Header Files\Nes_Apu</Filter>
</ClInclude>
<ClInclude Include="IAudioDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Timer.h">
@ -74,5 +116,8 @@
<ClCompile Include="ControlManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="APU.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

9
Core/IAudioDevice.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
#include "stdafx.h"
class IAudioDevice
{
public:
virtual void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize) = 0;
};

253
Core/Nes_Apu/Blip_Buffer.h Normal file
View file

@ -0,0 +1,253 @@
// 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

203
Core/Nes_Apu/Blip_Synth.h Normal file
View file

@ -0,0 +1,203 @@
// 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<int quality,int range>
class Blip_Synth {
BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 );
BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 );
enum {
abs_range = (range < 0) ? -range : range,
fine_mode = (range > 512 || range < 0),
width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_),
res = 1 << blip_res_bits_,
impulse_size = width / 2 * (fine_mode + 1),
base_impulses_size = width / 2 * (res / 2 + 1),
fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 :
abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 :
abs_range <= 2048 ? 7 : 8) : 0)
};
blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size];
Blip_Impulse_ impulse;
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<int quality,int range>
class Blip_Wave {
Blip_Synth<quality,range> 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<int quality,int range>
void Blip_Wave<quality,range>::amplitude( int amp ) {
int delta = amp - last_amp;
last_amp = amp;
synth.offset_inline( time_, delta );
}
template<int quality,int range>
inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
int delta, Blip_Buffer* blip_buf ) const
{
typedef blip_pair_t_ pair_t;
unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1;
assert(( "Blip_Synth/Blip_wave: Went past end of buffer",
sample_index < blip_buf->buffer_size_ ));
enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 };
pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index];
enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ };
enum { mask = res * 2 - 1 };
const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size];
pair_t offset = impulse.offset * delta;
if ( !fine_bits )
{
// normal mode
for ( int n = width / 4; n; --n )
{
pair_t t0 = buf [0] - offset;
pair_t t1 = buf [1] - offset;
t0 += imp [0] * delta;
t1 += imp [1] * delta;
imp += 2;
buf [0] = t0;
buf [1] = t1;
buf += 2;
}
}
else
{
// fine mode
enum { sub_range = 1 << fine_bits };
delta += sub_range / 2;
int delta2 = (delta & (sub_range - 1)) - sub_range / 2;
delta >>= fine_bits;
for ( int n = width / 4; n; --n )
{
pair_t t0 = buf [0] - offset;
pair_t t1 = buf [1] - offset;
t0 += imp [0] * delta2;
t0 += imp [1] * delta;
t1 += imp [2] * delta2;
t1 += imp [3] * delta;
imp += 4;
buf [0] = t0;
buf [1] = t1;
buf += 2;
}
}
}
template<int quality,int range>
void Blip_Synth<quality,range>::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const {
offset_resampled( time * buf->factor_ + buf->offset_, delta, buf );
}
#endif

157
Core/Nes_Apu/Multi_Buffer.h Normal file
View file

@ -0,0 +1,157 @@
// 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

Binary file not shown.

162
Core/Nes_Apu/Nes_Apu.h Normal file
View file

@ -0,0 +1,162 @@
// 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

BIN
Core/Nes_Apu/Nes_Apu.lib Normal file

Binary file not shown.

86
Core/Nes_Apu/Nes_Namco.h Normal file
View file

@ -0,0 +1,86 @@
// 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<blip_good_quality,15> 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

142
Core/Nes_Apu/Nes_Oscs.h Normal file
View file

@ -0,0 +1,142 @@
// 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<blip_good_quality,15> 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<blip_good_quality,15> 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<blip_med_quality,15> 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<blip_med_quality,127> 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

85
Core/Nes_Apu/Nes_Vrc6.h Normal file
View file

@ -0,0 +1,85 @@
// 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<blip_med_quality,31> saw_synth;
Blip_Synth<blip_good_quality,15> 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

View file

@ -0,0 +1,65 @@
// 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

View file

@ -0,0 +1,75 @@
// 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

View file

@ -0,0 +1,181 @@
// 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 <cstddef>
#include <cassert>
#include <new>
#else
#include <stddef.h>
#include <assert.h>
#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

View file

@ -0,0 +1,43 @@
// 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 <assert.h>
// 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

View file

@ -0,0 +1,13 @@
// 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

View file

@ -0,0 +1,42 @@
// Boost substitute. For full boost library see http://boost.org
#ifndef BOOST_CSTDINT_HPP
#define BOOST_CSTDINT_HPP
#if BLARGG_USE_NAMESPACE
#include <climits>
#else
#include <limits.h>
#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

View file

@ -0,0 +1,22 @@
// 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

View file

@ -117,6 +117,7 @@
<ClInclude Include="InputManager.h" />
<ClInclude Include="Renderer.h" />
<ClInclude Include="Resource.h" />
<ClInclude Include="SoundManager.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="MainWindow.h" />
@ -126,6 +127,7 @@
<ClCompile Include="InputManager.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="Renderer.cpp" />
<ClCompile Include="SoundManager.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>

View file

@ -87,6 +87,9 @@
<ClInclude Include="GamePad.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SoundManager.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -107,6 +110,9 @@
<ClCompile Include="GamePad.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SoundManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="GUI.rc">

View file

@ -21,6 +21,10 @@ namespace NES
return false;
}
if(_soundManager.Initialize(_hWnd)) {
return false;
}
return true;
}
@ -113,7 +117,7 @@ namespace NES
return E_FAIL;
// Create window
RECT rc = { 0, 0, 260, 270 };
RECT rc = { 0, 0, 800, 700 };
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
_hWnd = CreateWindow(L"NESEmu", L"NESEmu",
WS_OVERLAPPEDWINDOW,

View file

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "Renderer.h"
#include "SoundManager.h"
#include "../Core/Console.h"
namespace NES {
@ -11,6 +12,7 @@ namespace NES {
HWND _hWnd;
int _nCmdShow;
Renderer _renderer;
SoundManager _soundManager;
unique_ptr<Console> _console;
unique_ptr<thread> _emuThread;

168
GUI/SoundManager.cpp Normal file
View file

@ -0,0 +1,168 @@
#include "stdafx.h"
#include "SoundManager.h"
SoundManager::SoundManager()
{
_directSound = 0;
_primaryBuffer = 0;
_secondaryBuffer = 0;
}
SoundManager::~SoundManager()
{
Release();
}
bool SoundManager::Initialize(HWND hwnd)
{
APU::RegisterAudioDevice(this);
bool result;
result = InitializeDirectSound(hwnd);
if(!result) {
return false;
}
return true;
}
bool SoundManager::InitializeDirectSound(HWND hwnd)
{
HRESULT result;
DSBUFFERDESC bufferDesc;
WAVEFORMATEX waveFormat;
// Initialize the direct sound interface pointer for the default sound device.
result = DirectSoundCreate8(NULL, &_directSound, NULL);
if(FAILED(result)) {
return false;
}
// Set the cooperative level to priority so the format of the primary sound buffer can be modified.
result = _directSound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
if(FAILED(result)) {
return false;
}
// Setup the primary buffer description.
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
bufferDesc.dwBufferBytes = 0;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = NULL;
bufferDesc.guid3DAlgorithm = GUID_NULL;
// Get control of the primary sound buffer on the default sound device.
result = _directSound->CreateSoundBuffer(&bufferDesc, &_primaryBuffer, NULL);
if(FAILED(result)) {
return false;
}
// Setup the format of the primary sound bufffer.
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nSamplesPerSec = 44100;
waveFormat.wBitsPerSample = 16;
waveFormat.nChannels = 1;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
waveFormat.cbSize = 0;
// Set the primary buffer to be the wave format specified.
result = _primaryBuffer->SetFormat(&waveFormat);
if(FAILED(result)) {
return false;
}
// Set the buffer description of the secondary sound buffer that the wave file will be loaded onto.
bufferDesc.dwSize = sizeof(DSBUFFERDESC);
bufferDesc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_LOCSOFTWARE | DSBCAPS_CTRLVOLUME;
bufferDesc.dwBufferBytes = 0xFFFFF;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = &waveFormat;
bufferDesc.guid3DAlgorithm = GUID_NULL;
// Create a temporary sound buffer with the specific buffer settings.
IDirectSoundBuffer* tempBuffer;
result = _directSound->CreateSoundBuffer(&bufferDesc, &tempBuffer, NULL);
if(FAILED(result)) {
return false;
}
// Test the buffer format against the direct sound 8 interface and create the secondary buffer.
result = tempBuffer->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)&_secondaryBuffer);
if(FAILED(result)) {
return false;
}
// Set volume of the buffer to 100%.
result = _secondaryBuffer->SetVolume(DSBVOLUME_MAX);
if(FAILED(result)) {
return false;
}
// Release the temporary buffer.
tempBuffer->Release();
return true;
}
void SoundManager::Release()
{
if(_secondaryBuffer) {
_secondaryBuffer->Release();
_secondaryBuffer = nullptr;
}
if(_primaryBuffer) {
_primaryBuffer->Release();
_primaryBuffer = nullptr;
}
if(_directSound) {
_directSound->Release();
_directSound = nullptr;
}
}
void SoundManager::ClearSecondaryBuffer()
{
unsigned char* bufferPtr;
DWORD bufferSize;
_secondaryBuffer->Lock(0, 0, (void**)&bufferPtr, (DWORD*)&bufferSize, nullptr, 0, DSBLOCK_ENTIREBUFFER);
memset(bufferPtr, 0, bufferSize);
_secondaryBuffer->Unlock((void*)bufferPtr, bufferSize, nullptr, 0);
}
void SoundManager::CopyToSecondaryBuffer(uint8_t *data, uint32_t size)
{
unsigned char* bufferPtrA;
unsigned char* bufferPtrB;
DWORD bufferASize;
DWORD bufferBSize;
_secondaryBuffer->Lock(0, size, (void**)&bufferPtrA, (DWORD*)&bufferASize, (void**)&bufferPtrB, (DWORD*)&bufferBSize, DSBLOCK_FROMWRITECURSOR);
memcpy(bufferPtrA, data, min(bufferASize, size));
if(bufferPtrB) {
memcpy(bufferPtrB, data + bufferASize, bufferBSize);
}
_secondaryBuffer->Unlock((void*)bufferPtrA, bufferASize, (void*)bufferPtrB, bufferBSize);
}
void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t soundBufferSize)
{
DWORD status;
_secondaryBuffer->GetStatus(&status);
if(!(status & DSBSTATUS_PLAYING)) {
ClearSecondaryBuffer();
CopyToSecondaryBuffer((uint8_t*)soundBuffer, soundBufferSize);
_secondaryBuffer->SetCurrentPosition(0);
_secondaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
} else {
CopyToSecondaryBuffer((uint8_t*)soundBuffer, soundBufferSize);
}
}

27
GUI/SoundManager.h Normal file
View file

@ -0,0 +1,27 @@
#pragma once
#include "stdafx.h"
#include "../Core/APU.h"
class SoundManager : public IAudioDevice
{
public:
SoundManager();
~SoundManager();
bool Initialize(HWND hWnd);
void Release();
void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize);
private:
bool InitializeDirectSound(HWND);
void ShutdownDirectSound();
void ClearSecondaryBuffer();
void CopyToSecondaryBuffer(uint8_t *data, uint32_t size);
private:
vector<uint8_t> _buffer;
IDirectSound8* _directSound;
IDirectSoundBuffer* _primaryBuffer;
IDirectSoundBuffer8* _secondaryBuffer;
};

View file

@ -18,16 +18,31 @@
#pragma comment( lib, "DirectXTK.lib" )
#endif
#ifdef _DEBUG
#pragma comment( lib, "../Core/Nes_Apu/Nes_Apu.debug.lib" )
#else
#pragma comment( lib, "../Core/Nes_Apu/Nes_Apu.lib" )
#endif
#pragma comment(lib, "dsound.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "winmm.lib")
// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <mmsystem.h>
#include <stdio.h>
#include <d3d11_1.h>
#include <d3dcompiler.h>
#include <directxmath.h>
#include <directxcolors.h>
#include <dsound.h>
#include <io.h>
#include <Fcntl.h>
#include <thread>