207 lines
3.8 KiB
C++
207 lines
3.8 KiB
C++
|
#include "Nes_Vrc7_Apu.h"
|
||
|
|
||
|
#include "ym2413.h"
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "blargg_source.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 = ym2413_init( 3579545, 3579545 / 72, 1 ) );
|
||
|
|
||
|
set_output( 0 );
|
||
|
volume( 1.0 );
|
||
|
reset();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
Nes_Vrc7_Apu::~Nes_Vrc7_Apu()
|
||
|
{
|
||
|
if ( opll )
|
||
|
ym2413_shutdown( 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;
|
||
|
}
|
||
|
|
||
|
ym2413_reset_chip( 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 ( time > next_time )
|
||
|
run_until( time );
|
||
|
ym2413_write( opll, 0, addr );
|
||
|
ym2413_write( 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, ym2413_get_inst0( opll ), 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];
|
||
|
}
|
||
|
|
||
|
for ( i = 0; i < 8; ++i )
|
||
|
{
|
||
|
ym2413_write( opll, 0, i );
|
||
|
ym2413_write( opll, 1, in.inst [i] );
|
||
|
}
|
||
|
|
||
|
for ( i = 0; i < 3; ++i )
|
||
|
{
|
||
|
for ( int j = 0; j < 6; ++j )
|
||
|
{
|
||
|
ym2413_write( opll, 0, 0x10 + i * 0x10 + j );
|
||
|
ym2413_write( 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;
|
||
|
if ( mono_output )
|
||
|
{
|
||
|
// optimal case
|
||
|
do
|
||
|
{
|
||
|
ym2413_advance_lfo( opll );
|
||
|
int amp = 0;
|
||
|
for ( int i = 0; i < osc_count; i++ )
|
||
|
amp += ym2413_calcch( opll, i );
|
||
|
ym2413_advance( opll );
|
||
|
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
|
||
|
{
|
||
|
ym2413_advance_lfo( opll );
|
||
|
for ( int i = 0; i < osc_count; ++i )
|
||
|
{
|
||
|
Vrc7_Osc& osc = oscs [i];
|
||
|
if ( osc.output )
|
||
|
{
|
||
|
int amp = ym2413_calcch( opll, i );
|
||
|
int delta = amp - osc.last_amp;
|
||
|
if ( delta )
|
||
|
{
|
||
|
osc.last_amp = amp;
|
||
|
synth.offset( time, delta, osc.output );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ym2413_advance( opll );
|
||
|
time += period;
|
||
|
}
|
||
|
while ( time < end_time );
|
||
|
}
|
||
|
next_time = time;
|
||
|
}
|