PageRenderTime 47ms CodeModel.GetById 21ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 0ms

/gme/Gb_Oscs.cpp

http://game-music-emu.googlecode.com/
C++ | 336 lines | 269 code | 48 blank | 19 comment | 53 complexity | 9804f7921fc5d50da3b7f6f00ebcd9dc 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
 20// Gb_Osc
 21
 22void Gb_Osc::reset()
 23{
 24	delay = 0;
 25	last_amp = 0;
 26	length = 0;
 27	output_select = 3;
 28	output = outputs [output_select];
 29}
 30
 31void Gb_Osc::clock_length()
 32{
 33	if ( (regs [4] & len_enabled_mask) && length )
 34		length--;
 35}
 36
 37// Gb_Env
 38
 39void Gb_Env::clock_envelope()
 40{
 41	if ( env_delay && !--env_delay )
 42	{
 43		env_delay = regs [2] & 7;
 44		int v = volume - 1 + (regs [2] >> 2 & 2);
 45		if ( (unsigned) v < 15 )
 46			volume = v;
 47	}
 48}
 49
 50bool Gb_Env::write_register( int reg, int data )
 51{
 52	switch ( reg )
 53	{
 54	case 1:
 55		length = 64 - (regs [1] & 0x3F);
 56		break;
 57	
 58	case 2:
 59		if ( !(data >> 4) )
 60			enabled = false;
 61		break;
 62	
 63	case 4:
 64		if ( data & trigger )
 65		{
 66			env_delay = regs [2] & 7;
 67			volume = regs [2] >> 4;
 68			enabled = true;
 69			if ( length == 0 )
 70				length = 64;
 71			return true;
 72		}
 73	}
 74	return false;
 75}
 76
 77// Gb_Square
 78
 79void Gb_Square::reset()
 80{
 81	phase = 0;
 82	sweep_freq = 0;
 83	sweep_delay = 0;
 84	Gb_Env::reset();
 85}
 86
 87void Gb_Square::clock_sweep()
 88{
 89	int sweep_period = (regs [0] & period_mask) >> 4;
 90	if ( sweep_period && sweep_delay && !--sweep_delay )
 91	{
 92		sweep_delay = sweep_period;
 93		regs [3] = sweep_freq & 0xFF;
 94		regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07);
 95		
 96		int offset = sweep_freq >> (regs [0] & shift_mask);
 97		if ( regs [0] & 0x08 )
 98			offset = -offset;
 99		sweep_freq += offset;
100		
101		if ( sweep_freq < 0 )
102		{
103			sweep_freq = 0;
104		}
105		else if ( sweep_freq >= 2048 )
106		{
107			sweep_delay = 0; // don't modify channel frequency any further
108			sweep_freq = 2048; // silence sound immediately
109		}
110	}
111}
112
113void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing )
114{
115	if ( sweep_freq == 2048 )
116		playing = false;
117	
118	static unsigned char const table [4] = { 1, 2, 4, 6 };
119	int const duty = table [regs [1] >> 6];
120	int amp = volume & playing;
121	if ( phase >= duty )
122		amp = -amp;
123	
124	int frequency = this->frequency();
125	if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041
126	{
127		// really high frequency results in DC at half volume
128		amp = volume >> 1;
129		playing = false;
130	}
131	
132	{
133		int delta = amp - last_amp;
134		if ( delta )
135		{
136			last_amp = amp;
137			synth->offset( time, delta, output );
138		}
139	}
140	
141	time += delay;
142	if ( !playing )
143		time = end_time;
144	
145	if ( time < end_time )
146	{
147		int const period = (2048 - frequency) * 4;
148		Blip_Buffer* const output = this->output;
149		int phase = this->phase;
150		int delta = amp * 2;
151		do
152		{
153			phase = (phase + 1) & 7;
154			if ( phase == 0 || phase == duty )
155			{
156				delta = -delta;
157				synth->offset_inline( time, delta, output );
158			}
159			time += period;
160		}
161		while ( time < end_time );
162		
163		this->phase = phase;
164		last_amp = delta >> 1;
165	}
166	delay = time - end_time;
167}
168
169// Gb_Noise
170
171void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing )
172{
173	int amp = volume & playing;
174	int tap = 13 - (regs [3] & 8);
175	if ( bits >> tap & 2 )
176		amp = -amp;
177	
178	{
179		int delta = amp - last_amp;
180		if ( delta )
181		{
182			last_amp = amp;
183			synth->offset( time, delta, output );
184		}
185	}
186	
187	time += delay;
188	if ( !playing )
189		time = end_time;
190	
191	if ( time < end_time )
192	{
193		static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 };
194		int period = table [regs [3] & 7] << (regs [3] >> 4);
195		
196		// keep parallel resampled time to eliminate time conversion in the loop
197		Blip_Buffer* const output = this->output;
198		const blip_resampled_time_t resampled_period =
199				output->resampled_duration( period );
200		blip_resampled_time_t resampled_time = output->resampled_time( time );
201		unsigned bits = this->bits;
202		int delta = amp * 2;
203		
204		do
205		{
206			unsigned changed = (bits >> tap) + 1;
207			time += period;
208			bits <<= 1;
209			if ( changed & 2 )
210			{
211				delta = -delta;
212				bits |= 1;
213				synth->offset_resampled( resampled_time, delta, output );
214			}
215			resampled_time += resampled_period;
216		}
217		while ( time < end_time );
218		
219		this->bits = bits;
220		last_amp = delta >> 1;
221	}
222	delay = time - end_time;
223}
224
225// Gb_Wave
226
227inline void Gb_Wave::write_register( int reg, int data )
228{
229	switch ( reg )
230	{
231	case 0:
232		if ( !(data & 0x80) )
233			enabled = false;
234		break;
235	
236	case 1:
237		length = 256 - regs [1];
238		break;
239	
240	case 2:
241		volume = data >> 5 & 3;
242		break;
243	
244	case 4:
245		if ( data & trigger & regs [0] )
246		{
247			wave_pos = 0;
248			enabled = true;
249			if ( length == 0 )
250				length = 256;
251		}
252	}
253}
254
255void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing )
256{
257	int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7
258	int frequency;
259	{
260		int amp = (wave [wave_pos] >> volume_shift & playing) * 2;
261		
262		frequency = this->frequency();
263		if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045
264		{
265			amp = 30 >> volume_shift & playing;
266			playing = false;
267		}
268		
269		int delta = amp - last_amp;
270		if ( delta )
271		{
272			last_amp = amp;
273			synth->offset( time, delta, output );
274		}
275	}
276	
277	time += delay;
278	if ( !playing )
279		time = end_time;
280	
281	if ( time < end_time )
282	{
283		Blip_Buffer* const output = this->output;
284		int const period = (2048 - frequency) * 2;
285	 	int wave_pos = (this->wave_pos + 1) & (wave_size - 1);
286	 	
287		do
288		{
289			int amp = (wave [wave_pos] >> volume_shift) * 2;
290			wave_pos = (wave_pos + 1) & (wave_size - 1);
291			int delta = amp - last_amp;
292			if ( delta )
293			{
294				last_amp = amp;
295				synth->offset_inline( time, delta, output );
296			}
297			time += period;
298		}
299		while ( time < end_time );
300		
301		this->wave_pos = (wave_pos - 1) & (wave_size - 1);
302	}
303	delay = time - end_time;
304}
305
306// Gb_Apu::write_osc
307
308void Gb_Apu::write_osc( int index, int reg, int data )
309{
310	reg -= index * 5;
311	Gb_Square* sq = &square2;
312	switch ( index )
313	{
314	case 0:
315		sq = &square1;
316	case 1:
317		if ( sq->write_register( reg, data ) && index == 0 )
318		{
319			square1.sweep_freq = square1.frequency();
320			if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) )
321			{
322				square1.sweep_delay = 1; // cause sweep to recalculate now
323				square1.clock_sweep();
324			}
325		}
326		break;
327	
328	case 2:
329		wave.write_register( reg, data );
330		break;
331	
332	case 3:
333		if ( noise.write_register( reg, data ) )
334			noise.bits = 0x7FFF;
335	}
336}