summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp')
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp206
1 files changed, 206 insertions, 0 deletions
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp
new file mode 100644
index 00000000..033061d6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp
@@ -0,0 +1,206 @@
+#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;
+}