123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- // Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/
- #include "Gb_Apu.h"
- #include <string.h>
- /* Copyright (C) 2003-2006 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"
- unsigned const vol_reg = 0xFF24;
- unsigned const status_reg = 0xFF26;
- Gb_Apu::Gb_Apu()
- {
- square1.synth = &square_synth;
- square2.synth = &square_synth;
- wave.synth = &other_synth;
- noise.synth = &other_synth;
-
- oscs [0] = &square1;
- oscs [1] = &square2;
- oscs [2] = &wave;
- oscs [3] = &noise;
-
- for ( int i = 0; i < osc_count; i++ )
- {
- Gb_Osc& osc = *oscs [i];
- osc.regs = ®s [i * 5];
- osc.output = 0;
- osc.outputs [0] = 0;
- osc.outputs [1] = 0;
- osc.outputs [2] = 0;
- osc.outputs [3] = 0;
- }
-
- set_tempo( 1.0 );
- volume( 1.0 );
- reset();
- }
- void Gb_Apu::treble_eq( const blip_eq_t& eq )
- {
- square_synth.treble_eq( eq );
- other_synth.treble_eq( eq );
- }
- void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
- {
- require( (unsigned) index < osc_count );
- require( (center && left && right) || (!center && !left && !right) );
- Gb_Osc& osc = *oscs [index];
- osc.outputs [1] = right;
- osc.outputs [2] = left;
- osc.outputs [3] = center;
- osc.output = osc.outputs [osc.output_select];
- }
- void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
- {
- for ( int i = 0; i < osc_count; i++ )
- osc_output( i, center, left, right );
- }
- void Gb_Apu::update_volume()
- {
- // TODO: doesn't handle differing left/right global volume (support would
- // require modification to all oscillator code)
- int data = regs [vol_reg - start_addr];
- double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit;
- square_synth.volume( vol );
- other_synth.volume( vol );
- }
- static unsigned char const powerup_regs [0x20] = {
- 0x80,0x3F,0x00,0xFF,0xBF, // square 1
- 0xFF,0x3F,0x00,0xFF,0xBF, // square 2
- 0x7F,0xFF,0x9F,0xFF,0xBF, // wave
- 0xFF,0xFF,0x00,0x00,0xBF, // noise
- 0x00, // left/right enables
- 0x77, // master volume
- 0x80, // power
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
- };
- void Gb_Apu::set_tempo( double t )
- {
- frame_period = 4194304 / 256; // 256 Hz
- if ( t != 1.0 )
- frame_period = blip_time_t (frame_period / t);
- }
- void Gb_Apu::reset()
- {
- next_frame_time = 0;
- last_time = 0;
- frame_count = 0;
-
- square1.reset();
- square2.reset();
- wave.reset();
- noise.reset();
- noise.bits = 1;
- wave.wave_pos = 0;
-
- // avoid click at beginning
- regs [vol_reg - start_addr] = 0x77;
- update_volume();
-
- regs [status_reg - start_addr] = 0x01; // force power
- write_register( 0, status_reg, 0x00 );
-
- static unsigned char const initial_wave [] = {
- 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table
- 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA
- };
- memcpy( wave.wave, initial_wave, sizeof wave.wave );
- }
- void Gb_Apu::run_until( blip_time_t end_time )
- {
- require( end_time >= last_time ); // end_time must not be before previous time
- if ( end_time == last_time )
- return;
-
- while ( true )
- {
- blip_time_t time = next_frame_time;
- if ( time > end_time )
- time = end_time;
-
- // run oscillators
- for ( int i = 0; i < osc_count; ++i )
- {
- Gb_Osc& osc = *oscs [i];
- if ( osc.output )
- {
- osc.output->set_modified(); // TODO: misses optimization opportunities?
- int playing = false;
- if ( osc.enabled && osc.volume &&
- (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) )
- playing = -1;
- switch ( i )
- {
- case 0: square1.run( last_time, time, playing ); break;
- case 1: square2.run( last_time, time, playing ); break;
- case 2: wave .run( last_time, time, playing ); break;
- case 3: noise .run( last_time, time, playing ); break;
- }
- }
- }
- last_time = time;
-
- if ( time == end_time )
- break;
-
- next_frame_time += frame_period;
-
- // 256 Hz actions
- square1.clock_length();
- square2.clock_length();
- wave.clock_length();
- noise.clock_length();
-
- frame_count = (frame_count + 1) & 3;
- if ( frame_count == 0 )
- {
- // 64 Hz actions
- square1.clock_envelope();
- square2.clock_envelope();
- noise.clock_envelope();
- }
-
- if ( frame_count & 1 )
- square1.clock_sweep(); // 128 Hz action
- }
- }
- void Gb_Apu::end_frame( blip_time_t end_time )
- {
- if ( end_time > last_time )
- run_until( end_time );
-
- assert( next_frame_time >= end_time );
- next_frame_time -= end_time;
-
- assert( last_time >= end_time );
- last_time -= end_time;
- }
- void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
- {
- require( (unsigned) data < 0x100 );
-
- int reg = addr - start_addr;
- if ( (unsigned) reg >= register_count )
- return;
-
- run_until( time );
-
- int old_reg = regs [reg];
- regs [reg] = data;
-
- if ( addr < vol_reg )
- {
- write_osc( reg / 5, reg, data );
- }
- else if ( addr == vol_reg && data != old_reg ) // global volume
- {
- // return all oscs to 0
- for ( int i = 0; i < osc_count; i++ )
- {
- Gb_Osc& osc = *oscs [i];
- int amp = osc.last_amp;
- osc.last_amp = 0;
- if ( amp && osc.enabled && osc.output )
- other_synth.offset( time, -amp, osc.output );
- }
-
- if ( wave.outputs [3] )
- other_synth.offset( time, 30, wave.outputs [3] );
-
- update_volume();
-
- if ( wave.outputs [3] )
- other_synth.offset( time, -30, wave.outputs [3] );
-
- // oscs will update with new amplitude when next run
- }
- else if ( addr == 0xFF25 || addr == status_reg )
- {
- int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0;
- int flags = regs [0xFF25 - start_addr] & mask;
-
- // left/right assignments
- for ( int i = 0; i < osc_count; i++ )
- {
- Gb_Osc& osc = *oscs [i];
- osc.enabled &= mask;
- int bits = flags >> i;
- Blip_Buffer* old_output = osc.output;
- osc.output_select = (bits >> 3 & 2) | (bits & 1);
- osc.output = osc.outputs [osc.output_select];
- if ( osc.output != old_output )
- {
- int amp = osc.last_amp;
- osc.last_amp = 0;
- if ( amp && old_output )
- other_synth.offset( time, -amp, old_output );
- }
- }
-
- if ( addr == status_reg && data != old_reg )
- {
- if ( !(data & 0x80) )
- {
- for ( unsigned i = 0; i < sizeof powerup_regs; i++ )
- {
- if ( i != status_reg - start_addr )
- write_register( time, i + start_addr, powerup_regs [i] );
- }
- }
- else
- {
- //debug_printf( "APU powered on\n" );
- }
- }
- }
- else if ( addr >= 0xFF30 )
- {
- int index = (addr & 0x0F) * 2;
- wave.wave [index] = data >> 4;
- wave.wave [index + 1] = data & 0x0F;
- }
- }
- int Gb_Apu::read_register( blip_time_t time, unsigned addr )
- {
- run_until( time );
-
- int index = addr - start_addr;
- require( (unsigned) index < register_count );
- int data = regs [index];
-
- if ( addr == status_reg )
- {
- data = (data & 0x80) | 0x70;
- for ( int i = 0; i < osc_count; i++ )
- {
- const Gb_Osc& osc = *oscs [i];
- if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) )
- data |= 1 << i;
- }
- }
-
- return data;
- }
|