PageRenderTime 35ms CodeModel.GetById 12ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 1ms

/gme/Gb_Apu.cpp

http://game-music-emu.googlecode.com/
C++ | 306 lines | 239 code | 46 blank | 21 comment | 64 complexity | 7b1b480f751c8d8bf6e8b54c5ea264bf MD5 | raw file
  1// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/
  2
  3#include "Gb_Apu.h"
  4
  5#include <string.h>
  6
  7/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
  8can redistribute it and/or modify it under the terms of the GNU Lesser
  9General Public License as published by the Free Software Foundation; either
 10version 2.1 of the License, or (at your option) any later version. This
 11module is distributed in the hope that it will be useful, but WITHOUT ANY
 12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 13FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 14details. You should have received a copy of the GNU Lesser General Public
 15License along with this module; if not, write to the Free Software Foundation,
 16Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
 17
 18#include "blargg_source.h"
 19
 20unsigned const vol_reg    = 0xFF24;
 21unsigned const status_reg = 0xFF26;
 22
 23Gb_Apu::Gb_Apu()
 24{
 25	square1.synth = &square_synth;
 26	square2.synth = &square_synth;
 27	wave.synth  = &other_synth;
 28	noise.synth = &other_synth;
 29	
 30	oscs [0] = &square1;
 31	oscs [1] = &square2;
 32	oscs [2] = &wave;
 33	oscs [3] = &noise;
 34	
 35	for ( int i = 0; i < osc_count; i++ )
 36	{
 37		Gb_Osc& osc = *oscs [i];
 38		osc.regs = &regs [i * 5];
 39		osc.output = 0;
 40		osc.outputs [0] = 0;
 41		osc.outputs [1] = 0;
 42		osc.outputs [2] = 0;
 43		osc.outputs [3] = 0;
 44	}
 45	
 46	set_tempo( 1.0 );
 47	volume( 1.0 );
 48	reset();
 49}
 50
 51void Gb_Apu::treble_eq( const blip_eq_t& eq )
 52{
 53	square_synth.treble_eq( eq );
 54	other_synth.treble_eq( eq );
 55}
 56
 57void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
 58{
 59	require( (unsigned) index < osc_count );
 60	require( (center && left && right) || (!center && !left && !right) );
 61	Gb_Osc& osc = *oscs [index];
 62	osc.outputs [1] = right;
 63	osc.outputs [2] = left;
 64	osc.outputs [3] = center;
 65	osc.output = osc.outputs [osc.output_select];
 66}
 67
 68void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
 69{
 70	for ( int i = 0; i < osc_count; i++ )
 71		osc_output( i, center, left, right );
 72}
 73
 74void Gb_Apu::update_volume()
 75{
 76	// TODO: doesn't handle differing left/right global volume (support would
 77	// require modification to all oscillator code)
 78	int data = regs [vol_reg - start_addr];
 79	double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit;
 80	square_synth.volume( vol );
 81	other_synth.volume( vol );
 82}
 83
 84static unsigned char const powerup_regs [0x20] = {
 85	0x80,0x3F,0x00,0xFF,0xBF, // square 1
 86	0xFF,0x3F,0x00,0xFF,0xBF, // square 2
 87	0x7F,0xFF,0x9F,0xFF,0xBF, // wave
 88	0xFF,0xFF,0x00,0x00,0xBF, // noise
 89	0x00, // left/right enables
 90	0x77, // master volume
 91	0x80, // power
 92	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
 93};
 94
 95void Gb_Apu::set_tempo( double t )
 96{
 97	frame_period = 4194304 / 256; // 256 Hz
 98	if ( t != 1.0 )
 99		frame_period = blip_time_t (frame_period / t);
100}
101
102void Gb_Apu::reset()
103{
104	next_frame_time = 0;
105	last_time       = 0;
106	frame_count     = 0;
107	
108	square1.reset();
109	square2.reset();
110	wave.reset();
111	noise.reset();
112	noise.bits = 1;
113	wave.wave_pos = 0;
114	
115	// avoid click at beginning
116	regs [vol_reg - start_addr] = 0x77;
117	update_volume();
118	
119	regs [status_reg - start_addr] = 0x01; // force power
120	write_register( 0, status_reg, 0x00 );
121	
122	static unsigned char const initial_wave [] = {
123		0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table
124		0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA
125	};
126	memcpy( wave.wave, initial_wave, sizeof wave.wave );
127}
128
129void Gb_Apu::run_until( blip_time_t end_time )
130{
131	require( end_time >= last_time ); // end_time must not be before previous time
132	if ( end_time == last_time )
133		return;
134	
135	while ( true )
136	{
137		blip_time_t time = next_frame_time;
138		if ( time > end_time )
139			time = end_time;
140		
141		// run oscillators
142		for ( int i = 0; i < osc_count; ++i )
143		{
144			Gb_Osc& osc = *oscs [i];
145			if ( osc.output )
146			{
147				osc.output->set_modified(); // TODO: misses optimization opportunities?
148				int playing = false;
149				if ( osc.enabled && osc.volume &&
150						(!(osc.regs [4] & osc.len_enabled_mask) || osc.length) )
151					playing = -1;
152				switch ( i )
153				{
154				case 0: square1.run( last_time, time, playing ); break;
155				case 1: square2.run( last_time, time, playing ); break;
156				case 2: wave   .run( last_time, time, playing ); break;
157				case 3: noise  .run( last_time, time, playing ); break;
158				}
159			}
160		}
161		last_time = time;
162		
163		if ( time == end_time )
164			break;
165		
166		next_frame_time += frame_period;
167		
168		// 256 Hz actions
169		square1.clock_length();
170		square2.clock_length();
171		wave.clock_length();
172		noise.clock_length();
173		
174		frame_count = (frame_count + 1) & 3;
175		if ( frame_count == 0 )
176		{
177			// 64 Hz actions
178			square1.clock_envelope();
179			square2.clock_envelope();
180			noise.clock_envelope();
181		}
182		
183		if ( frame_count & 1 )
184			square1.clock_sweep(); // 128 Hz action
185	}
186}
187
188void Gb_Apu::end_frame( blip_time_t end_time )
189{
190	if ( end_time > last_time )
191		run_until( end_time );
192	
193	assert( next_frame_time >= end_time );
194	next_frame_time -= end_time;
195	
196	assert( last_time >= end_time );
197	last_time -= end_time;
198}
199
200void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
201{
202	require( (unsigned) data < 0x100 );
203	
204	int reg = addr - start_addr;
205	if ( (unsigned) reg >= register_count )
206		return;
207	
208	run_until( time );
209	
210	int old_reg = regs [reg];
211	regs [reg] = data;
212	
213	if ( addr < vol_reg )
214	{
215		write_osc( reg / 5, reg, data );
216	}
217	else if ( addr == vol_reg && data != old_reg ) // global volume
218	{
219		// return all oscs to 0
220		for ( int i = 0; i < osc_count; i++ )
221		{
222			Gb_Osc& osc = *oscs [i];
223			int amp = osc.last_amp;
224			osc.last_amp = 0;
225			if ( amp && osc.enabled && osc.output )
226				other_synth.offset( time, -amp, osc.output );
227		}
228		
229		if ( wave.outputs [3] )
230			other_synth.offset( time, 30, wave.outputs [3] );
231		
232		update_volume();
233		
234		if ( wave.outputs [3] )
235			other_synth.offset( time, -30, wave.outputs [3] );
236		
237		// oscs will update with new amplitude when next run
238	}
239	else if ( addr == 0xFF25 || addr == status_reg )
240	{
241		int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0;
242		int flags = regs [0xFF25 - start_addr] & mask;
243		
244		// left/right assignments
245		for ( int i = 0; i < osc_count; i++ )
246		{
247			Gb_Osc& osc = *oscs [i];
248			osc.enabled &= mask;
249			int bits = flags >> i;
250			Blip_Buffer* old_output = osc.output;
251			osc.output_select = (bits >> 3 & 2) | (bits & 1);
252			osc.output = osc.outputs [osc.output_select];
253			if ( osc.output != old_output )
254			{
255				int amp = osc.last_amp;
256				osc.last_amp = 0;
257				if ( amp && old_output )
258					other_synth.offset( time, -amp, old_output );
259			}
260		}
261		
262		if ( addr == status_reg && data != old_reg )
263		{
264			if ( !(data & 0x80) )
265			{
266				for ( unsigned i = 0; i < sizeof powerup_regs; i++ )
267				{
268					if ( i != status_reg - start_addr )
269						write_register( time, i + start_addr, powerup_regs [i] );
270				}
271			}
272			else
273			{
274				//debug_printf( "APU powered on\n" );
275			}
276		}
277	}
278	else if ( addr >= 0xFF30 )
279	{
280		int index = (addr & 0x0F) * 2;
281		wave.wave [index] = data >> 4;
282		wave.wave [index + 1] = data & 0x0F;
283	}
284}
285
286int Gb_Apu::read_register( blip_time_t time, unsigned addr )
287{
288	run_until( time );
289	
290	int index = addr - start_addr;
291	require( (unsigned) index < register_count );
292	int data = regs [index];
293	
294	if ( addr == status_reg )
295	{
296		data = (data & 0x80) | 0x70;
297		for ( int i = 0; i < osc_count; i++ )
298		{
299			const Gb_Osc& osc = *oscs [i];
300			if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) )
301				data |= 1 << i;
302		}
303	}
304	
305	return data;
306}