Sound support (using Blargg's Nes_Snd_Emu for now)
This commit is contained in:
parent
bed43cec62
commit
8c079e5ca1
30 changed files with 1931 additions and 6 deletions
62
Core/APU.cpp
Normal file
62
Core/APU.cpp
Normal 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
35
Core/APU.h
Normal 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);
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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
9
Core/IAudioDevice.h
Normal 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
253
Core/Nes_Apu/Blip_Buffer.h
Normal 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
203
Core/Nes_Apu/Blip_Synth.h
Normal 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
157
Core/Nes_Apu/Multi_Buffer.h
Normal 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
|
||||
|
BIN
Core/Nes_Apu/Nes_Apu.debug.lib
Normal file
BIN
Core/Nes_Apu/Nes_Apu.debug.lib
Normal file
Binary file not shown.
162
Core/Nes_Apu/Nes_Apu.h
Normal file
162
Core/Nes_Apu/Nes_Apu.h
Normal 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
BIN
Core/Nes_Apu/Nes_Apu.lib
Normal file
Binary file not shown.
86
Core/Nes_Apu/Nes_Namco.h
Normal file
86
Core/Nes_Apu/Nes_Namco.h
Normal 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
142
Core/Nes_Apu/Nes_Oscs.h
Normal 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
85
Core/Nes_Apu/Nes_Vrc6.h
Normal 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
|
||||
|
65
Core/Nes_Apu/Nonlinear_Buffer.h
Normal file
65
Core/Nes_Apu/Nonlinear_Buffer.h
Normal 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
|
||||
|
75
Core/Nes_Apu/apu_snapshot.h
Normal file
75
Core/Nes_Apu/apu_snapshot.h
Normal 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
|
||||
|
181
Core/Nes_Apu/blargg_common.h
Normal file
181
Core/Nes_Apu/blargg_common.h
Normal 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
|
||||
|
43
Core/Nes_Apu/blargg_source.h
Normal file
43
Core/Nes_Apu/blargg_source.h
Normal 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
|
||||
|
13
Core/Nes_Apu/boost/config.hpp
Normal file
13
Core/Nes_Apu/boost/config.hpp
Normal 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
|
||||
|
42
Core/Nes_Apu/boost/cstdint.hpp
Normal file
42
Core/Nes_Apu/boost/cstdint.hpp
Normal 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
|
||||
|
22
Core/Nes_Apu/boost/static_assert.hpp
Normal file
22
Core/Nes_Apu/boost/static_assert.hpp
Normal 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
|
||||
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
168
GUI/SoundManager.cpp
Normal 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
27
GUI/SoundManager.h
Normal 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;
|
||||
};
|
15
GUI/stdafx.h
15
GUI/stdafx.h
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue