diff options
Diffstat (limited to 'plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp')
-rw-r--r-- | plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp new file mode 100644 index 00000000..18625894 --- /dev/null +++ b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp @@ -0,0 +1,124 @@ +// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/ + +#include "Kss_Scc_Apu.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// Tones above this frequency are treated as disabled tone at half volume. +// Power of two is more efficient (avoids division). +int const inaudible_freq = 16384; + +int const wave_size = 0x20; + +void Scc_Apu::set_output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); +} + +void Scc_Apu::volume( double v ) +{ + synth.volume( 0.43 / osc_count / amp_range * v ); +} + +void Scc_Apu::reset() +{ + last_time = 0; + + for ( int i = osc_count; --i >= 0; ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); + + memset( regs, 0, sizeof regs ); +} + +Scc_Apu::Scc_Apu() +{ + set_output( NULL ); + volume( 1.0 ); + reset(); +} + +void Scc_Apu::run_until( blip_time_t end_time ) +{ + for ( int index = 0; index < osc_count; index++ ) + { + osc_t& osc = oscs [index]; + + Blip_Buffer* const output = osc.output; + if ( !output ) + continue; + + blip_time_t period = (regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 + + regs [0xA0 + index * 2] + 1; + int volume = 0; + if ( regs [0xAF] & (1 << index) ) + { + blip_time_t inaudible_period = (unsigned) (output->clock_rate() + + inaudible_freq * 32) / (unsigned) (inaudible_freq * 16); + if ( period > inaudible_period ) + volume = (regs [0xAA + index] & 0x0F) * (amp_range / 256 / 15); + } + + BOOST::int8_t const* wave = (BOOST::int8_t*) regs + index * wave_size; + /*if ( index == osc_count - 1 ) + wave -= wave_size; // last two oscs share same wave RAM*/ + + { + int delta = wave [osc.phase] * volume - osc.last_amp; + if ( delta ) + { + osc.last_amp += delta; + output->set_modified(); + synth.offset( last_time, delta, output ); + } + } + + blip_time_t time = last_time + osc.delay; + if ( time < end_time ) + { + int phase = osc.phase; + if ( !volume ) + { + // maintain phase + int count = (end_time - time + period - 1) / period; + phase += count; // will be masked below + time += count * period; + } + else + { + int last_wave = wave [phase]; + phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop + do + { + int delta = wave [phase] - last_wave; + phase = (phase + 1) & (wave_size - 1); + if ( delta ) + { + last_wave += delta; + synth.offset_inline( time, delta * volume, output ); + } + time += period; + } + while ( time < end_time ); + + osc.last_amp = last_wave * volume; + output->set_modified(); + phase--; // undo pre-advance + } + osc.phase = phase & (wave_size - 1); + } + osc.delay = time - end_time; + } + last_time = end_time; +} |