cog/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp

219 lines
4.2 KiB
C++

#include "Nes_Vrc7_Apu.h"
extern "C" {
#include "../vgmplay/chips/emu2413.h"
}
#include <string.h>
#include "blargg_source.h"
static unsigned char vrc7_inst[(16 + 3) * 8] =
{
#include "../vgmplay/chips/vrc7tone.h"
};
int const period = 36; // NES CPU clocks per FM clock
Nes_Vrc7_Apu::Nes_Vrc7_Apu()
{
opll = 0;
}
blargg_err_t Nes_Vrc7_Apu::init()
{
CHECK_ALLOC( opll = OPLL_new( 3579545, 3579545 / 72 ) );
OPLL_SetChipMode((OPLL *) opll, 1);
OPLL_setPatch((OPLL *) opll, vrc7_inst);
set_output( 0 );
volume( 1.0 );
reset();
return 0;
}
Nes_Vrc7_Apu::~Nes_Vrc7_Apu()
{
if ( opll )
OPLL_delete( (OPLL *) opll );
}
void Nes_Vrc7_Apu::set_output( Blip_Buffer* buf )
{
for ( int i = 0; i < osc_count; ++i )
oscs [i].output = buf;
output_changed();
}
void Nes_Vrc7_Apu::output_changed()
{
mono.output = oscs [0].output;
for ( int i = osc_count; --i; )
{
if ( mono.output != oscs [i].output )
{
mono.output = 0;
break;
}
}
if ( mono.output )
{
for ( int i = osc_count; --i; )
{
mono.last_amp += oscs [i].last_amp;
oscs [i].last_amp = 0;
}
}
}
void Nes_Vrc7_Apu::reset()
{
addr = 0;
next_time = 0;
mono.last_amp = 0;
for ( int i = osc_count; --i >= 0; )
{
Vrc7_Osc& osc = oscs [i];
osc.last_amp = 0;
for ( int j = 0; j < 3; ++j )
osc.regs [j] = 0;
}
OPLL_reset( (OPLL *) opll );
}
void Nes_Vrc7_Apu::write_reg( int data )
{
addr = data;
}
void Nes_Vrc7_Apu::write_data( blip_time_t time, int data )
{
int type = (addr >> 4) - 1;
int chan = addr & 15;
if ( (unsigned) type < 3 && chan < osc_count )
oscs [chan].regs [type] = data;
if ( addr < 0x08 )
inst [addr] = data;
if ( time > next_time )
run_until( time );
OPLL_writeIO( (OPLL *) opll, 0, addr );
OPLL_writeIO( (OPLL *) opll, 1, data );
}
void Nes_Vrc7_Apu::end_frame( blip_time_t time )
{
if ( time > next_time )
run_until( time );
next_time -= time;
assert( next_time >= 0 );
for ( int i = osc_count; --i >= 0; )
{
Blip_Buffer* output = oscs [i].output;
if ( output )
output->set_modified();
}
}
void Nes_Vrc7_Apu::save_snapshot( vrc7_snapshot_t* out ) const
{
out->latch = addr;
out->delay = next_time;
for ( int i = osc_count; --i >= 0; )
{
for ( int j = 0; j < 3; ++j )
out->regs [i] [j] = oscs [i].regs [j];
}
memcpy( out->inst, inst, 8 );
}
void Nes_Vrc7_Apu::load_snapshot( vrc7_snapshot_t const& in )
{
assert( offsetof (vrc7_snapshot_t,delay) == 28 - 1 );
reset();
next_time = in.delay;
write_reg( in.latch );
int i;
for ( i = 0; i < osc_count; ++i )
{
for ( int j = 0; j < 3; ++j )
oscs [i].regs [j] = in.regs [i] [j];
}
memcpy( inst, in.inst, 8 );
for ( i = 0; i < 8; ++i )
{
OPLL_writeIO( (OPLL *) opll, 0, i );
OPLL_writeIO( (OPLL *) opll, 1, in.inst [i] );
}
for ( i = 0; i < 3; ++i )
{
for ( int j = 0; j < 6; ++j )
{
OPLL_writeIO( (OPLL *) opll, 0, 0x10 + i * 0x10 + j );
OPLL_writeIO( (OPLL *) opll, 1, oscs [j].regs [i] );
}
}
}
void Nes_Vrc7_Apu::run_until( blip_time_t end_time )
{
require( end_time > next_time );
blip_time_t time = next_time;
void* opll = this->opll; // cache
Blip_Buffer* const mono_output = mono.output;
e_int32 buffer [2];
e_int32* buffers[2] = {&buffer[0], &buffer[1]};
if ( mono_output )
{
// optimal case
do
{
OPLL_calc_stereo( (OPLL *) opll, buffers, 1, -1 );
int amp = buffer [0] + buffer [1];
int delta = amp - mono.last_amp;
if ( delta )
{
mono.last_amp = amp;
synth.offset_inline( time, delta, mono_output );
}
time += period;
}
while ( time < end_time );
}
else
{
mono.last_amp = 0;
do
{
OPLL_advance( (OPLL *) opll );
for ( int i = 0; i < osc_count; ++i )
{
Vrc7_Osc& osc = oscs [i];
if ( osc.output )
{
OPLL_calc_stereo( (OPLL *) opll, buffers, 1, i );
int amp = buffer [0] + buffer [1];
int delta = amp - osc.last_amp;
if ( delta )
{
osc.last_amp = amp;
synth.offset( time, delta, osc.output );
}
}
}
time += period;
}
while ( time < end_time );
}
next_time = time;
}