From f40bfc9267b13b54e6379dfe7539447662879d24 Mon Sep 17 00:00:00 2001 From: Sean Bartell Date: Sat, 25 Jun 2011 21:32:25 -0400 Subject: Add codecs to librbcodec. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id7f4717d51ed02d67cb9f9cb3c0ada4a81843f97 Reviewed-on: http://gerrit.rockbox.org/137 Reviewed-by: Nils Wallménius Tested-by: Nils Wallménius --- lib/rbcodec/codecs/libgme/kss_scc_apu.c | 166 ++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 lib/rbcodec/codecs/libgme/kss_scc_apu.c (limited to 'lib/rbcodec/codecs/libgme/kss_scc_apu.c') diff --git a/lib/rbcodec/codecs/libgme/kss_scc_apu.c b/lib/rbcodec/codecs/libgme/kss_scc_apu.c new file mode 100644 index 0000000000..1bec9b7f0e --- /dev/null +++ b/lib/rbcodec/codecs/libgme/kss_scc_apu.c @@ -0,0 +1,166 @@ +// 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). +extern int const inaudible_freq; + +int const wave_size = 0x20; + +static void set_output( struct Scc_Apu* this, struct Blip_Buffer* buf ) +{ + int i; + for ( i = 0; i < scc_osc_count; ++i ) + Scc_set_output( this, i, buf ); +} + +void Scc_volume( struct Scc_Apu* this, int v ) +{ + Synth_volume( &this->synth, (v/2 - (v*7)/100) / scc_osc_count / scc_amp_range ); +} + +void Scc_reset( struct Scc_Apu* this ) +{ + this->last_time = 0; + + int i; + for ( i = scc_osc_count; --i >= 0; ) + memset( &this->oscs [i], 0, offsetof (struct scc_osc_t,output) ); + + memset( this->regs, 0, sizeof this->regs ); +} + +void Scc_init( struct Scc_Apu* this ) +{ + Synth_init( &this->synth); + + set_output( this, NULL ); + Scc_volume( this, (int)FP_ONE_VOLUME ); + Scc_reset( this ); +} + +static void run_until( struct Scc_Apu* this, blip_time_t end_time ) +{ + int index; + for ( index = 0; index < scc_osc_count; index++ ) + { + struct scc_osc_t* osc = &this->oscs [index]; + + struct Blip_Buffer* const output = osc->output; + if ( !output ) + continue; + + blip_time_t period = (this->regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 + + this->regs [0xA0 + index * 2] + 1; + int volume = 0; + if ( this->regs [0xAF] & (1 << index) ) + { + blip_time_t inaudible_period = (unsigned) (Blip_clock_rate( output ) + + inaudible_freq * 32) / (unsigned) (inaudible_freq * 16); + if ( period > inaudible_period ) + volume = (this->regs [0xAA + index] & 0x0F) * (scc_amp_range / 256 / 15); + } + + int8_t const* wave = (int8_t*) this->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; + Blip_set_modified( output ); + Synth_offset( &this->synth, this->last_time, delta, output ); + } + } + + blip_time_t time = this->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( &this->synth, time, delta * volume, output ); + } + time += period; + } + while ( time < end_time ); + + osc->last_amp = last_wave * volume; + Blip_set_modified( output ); + phase--; // undo pre-advance + } + osc->phase = phase & (wave_size - 1); + } + osc->delay = time - end_time; + } + this->last_time = end_time; +} + +void Scc_write( struct Scc_Apu* this, blip_time_t time, int addr, int data ) +{ + //assert( (unsigned) addr < reg_count ); + assert( ( addr >= 0x9800 && addr <= 0x988F ) || ( addr >= 0xB800 && addr <= 0xB8AF ) ); + run_until( this, time ); + + addr -= 0x9800; + if ( ( unsigned ) addr < 0x90 ) + { + if ( ( unsigned ) addr < 0x60 ) + this->regs [addr] = data; + else if ( ( unsigned ) addr < 0x80 ) + { + this->regs [addr] = this->regs[addr + 0x20] = data; + } + else if ( ( unsigned ) addr < 0x90 ) + { + this->regs [addr + 0x20] = data; + } + } + else + { + addr -= 0xB800 - 0x9800; + if ( ( unsigned ) addr < 0xB0 ) + this->regs [addr] = data; + } +} + +void Scc_end_frame( struct Scc_Apu* this, blip_time_t end_time ) +{ + if ( end_time > this->last_time ) + run_until( this, end_time ); + + this->last_time -= end_time; + assert( this->last_time >= 0 ); +} -- cgit v1.2.3