1 // Sms_Snd_Emu 0.1.4. http://www.slack.net/~ant/
2 
3 #include "Sms_Apu.h"
4 
5 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
6 can redistribute it and/or modify it under the terms of the GNU Lesser
7 General Public License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version. This
9 module is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 details. You should have received a copy of the GNU Lesser General Public
13 License along with this module; if not, write to the Free Software Foundation,
14 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include "blargg_source.h"
17 
18 // Sms_Osc
19 
Sms_Osc()20 Sms_Osc::Sms_Osc()
21 {
22 	output = 0;
23 	outputs [0] = 0; // always stays NULL
24 	outputs [1] = 0;
25 	outputs [2] = 0;
26 	outputs [3] = 0;
27 }
28 
reset()29 void Sms_Osc::reset()
30 {
31 	delay = 0;
32 	last_amp = 0;
33 	volume = 0;
34 	output_select = 3;
35 	output = outputs [3];
36 }
37 
38 // Sms_Square
39 
reset()40 inline void Sms_Square::reset()
41 {
42 	period = 0;
43 	phase = 0;
44 	Sms_Osc::reset();
45 }
46 
run(blip_time_t time,blip_time_t end_time)47 void Sms_Square::run( blip_time_t time, blip_time_t end_time )
48 {
49 	if ( !volume || period <= 128 )
50 	{
51 		// ignore 16kHz and higher
52 		if ( last_amp )
53 		{
54 			synth->offset( time, -last_amp, output );
55 			last_amp = 0;
56 		}
57 		time += delay;
58 		if ( !period )
59 		{
60 			time = end_time;
61 		}
62 		else if ( time < end_time )
63 		{
64 			// keep calculating phase
65 			int count = (end_time - time + period - 1) / period;
66 			phase = (phase + count) & 1;
67 			time += count * period;
68 		}
69 	}
70 	else
71 	{
72 		int amp = phase ? volume : -volume;
73 		{
74 			int delta = amp - last_amp;
75 			if ( delta )
76 			{
77 				last_amp = amp;
78 				synth->offset( time, delta, output );
79 			}
80 		}
81 
82 		time += delay;
83 		if ( time < end_time )
84 		{
85 			Blip_Buffer* const output = this->output;
86 			int delta = amp * 2;
87 			do
88 			{
89 				delta = -delta;
90 				synth->offset_inline( time, delta, output );
91 				time += period;
92 				phase ^= 1;
93 			}
94 			while ( time < end_time );
95 			this->last_amp = phase ? volume : -volume;
96 		}
97 	}
98 	delay = time - end_time;
99 }
100 
101 // Sms_Noise
102 
103 static int const noise_periods [3] = { 0x100, 0x200, 0x400 };
104 
reset()105 inline void Sms_Noise::reset()
106 {
107 	period = &noise_periods [0];
108 	shifter = 0x8000;
109 	feedback = 0x9000;
110 	Sms_Osc::reset();
111 }
112 
run(blip_time_t time,blip_time_t end_time)113 void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
114 {
115 	int amp = volume;
116 	if ( shifter & 1 )
117 		amp = -amp;
118 
119 	{
120 		int delta = amp - last_amp;
121 		if ( delta )
122 		{
123 			last_amp = amp;
124 			synth.offset( time, delta, output );
125 		}
126 	}
127 
128 	time += delay;
129 	if ( !volume )
130 		time = end_time;
131 
132 	if ( time < end_time )
133 	{
134 		Blip_Buffer* const output = this->output;
135 		unsigned shifter = this->shifter;
136 		int delta = amp * 2;
137 		int period = *this->period * 2;
138 		if ( !period )
139 			period = 16;
140 
141 		do
142 		{
143 			int changed = shifter + 1;
144 			shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1);
145 			if ( changed & 2 ) // true if bits 0 and 1 differ
146 			{
147 				delta = -delta;
148 				synth.offset_inline( time, delta, output );
149 			}
150 			time += period;
151 		}
152 		while ( time < end_time );
153 
154 		this->shifter = shifter;
155 		this->last_amp = delta >> 1;
156 	}
157 	delay = time - end_time;
158 }
159 
160 // Sms_Apu
161 
Sms_Apu()162 Sms_Apu::Sms_Apu()
163 {
164 	for ( int i = 0; i < 3; i++ )
165 	{
166 		squares [i].synth = &square_synth;
167 		oscs [i] = &squares [i];
168 	}
169 	oscs [3] = &noise;
170 
171 	volume( 1.0 );
172 	reset();
173 }
174 
~Sms_Apu()175 Sms_Apu::~Sms_Apu()
176 {
177 }
178 
volume(double vol)179 void Sms_Apu::volume( double vol )
180 {
181 	vol *= 0.85 / (osc_count * 64 * 2);
182 	square_synth.volume( vol );
183 	noise.synth.volume( vol );
184 }
185 
treble_eq(const blip_eq_t & eq)186 void Sms_Apu::treble_eq( const blip_eq_t& eq )
187 {
188 	square_synth.treble_eq( eq );
189 	noise.synth.treble_eq( eq );
190 }
191 
osc_output(int index,Blip_Buffer * center,Blip_Buffer * left,Blip_Buffer * right)192 void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
193 {
194 	require( (unsigned) index < osc_count );
195 	require( (center && left && right) || (!center && !left && !right) );
196 	Sms_Osc& osc = *oscs [index];
197 	osc.outputs [1] = right;
198 	osc.outputs [2] = left;
199 	osc.outputs [3] = center;
200 	osc.output = osc.outputs [osc.output_select];
201 }
202 
output(Blip_Buffer * center,Blip_Buffer * left,Blip_Buffer * right)203 void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
204 {
205 	for ( int i = 0; i < osc_count; i++ )
206 		osc_output( i, center, left, right );
207 }
208 
reset(unsigned feedback,int noise_width)209 void Sms_Apu::reset( unsigned feedback, int noise_width )
210 {
211 	last_time = 0;
212 	latch = 0;
213 
214 	if ( !feedback || !noise_width )
215 	{
216 		feedback = 0x0009;
217 		noise_width = 16;
218 	}
219 	// convert to "Galios configuration"
220 	looped_feedback = 1 << (noise_width - 1);
221 	noise_feedback  = 0;
222 	while ( noise_width-- )
223 	{
224 		noise_feedback = (noise_feedback << 1) | (feedback & 1);
225 		feedback >>= 1;
226 	}
227 
228 	squares [0].reset();
229 	squares [1].reset();
230 	squares [2].reset();
231 	noise.reset();
232 }
233 
run_until(blip_time_t end_time)234 void Sms_Apu::run_until( blip_time_t end_time )
235 {
236 	require( end_time >= last_time ); // end_time must not be before previous time
237 
238 	if ( end_time > last_time )
239 	{
240 		// run oscillators
241 		for ( int i = 0; i < osc_count; ++i )
242 		{
243 			Sms_Osc& osc = *oscs [i];
244 			if ( osc.output )
245 			{
246 				osc.output->set_modified();
247 				if ( i < 3 )
248 					squares [i].run( last_time, end_time );
249 				else
250 					noise.run( last_time, end_time );
251 			}
252 		}
253 
254 		last_time = end_time;
255 	}
256 }
257 
end_frame(blip_time_t end_time)258 void Sms_Apu::end_frame( blip_time_t end_time )
259 {
260 	if ( end_time > last_time )
261 		run_until( end_time );
262 
263 	assert( last_time >= end_time );
264 	last_time -= end_time;
265 }
266 
write_ggstereo(blip_time_t time,int data)267 void Sms_Apu::write_ggstereo( blip_time_t time, int data )
268 {
269 	require( (unsigned) data <= 0xFF );
270 
271 	run_until( time );
272 
273 	for ( int i = 0; i < osc_count; i++ )
274 	{
275 		Sms_Osc& osc = *oscs [i];
276 		int flags = data >> i;
277 		Blip_Buffer* old_output = osc.output;
278 		osc.output_select = (flags >> 3 & 2) | (flags & 1);
279 		osc.output = osc.outputs [osc.output_select];
280 		if ( osc.output != old_output && osc.last_amp )
281 		{
282 			if ( old_output )
283 			{
284 				old_output->set_modified();
285 				square_synth.offset( time, -osc.last_amp, old_output );
286 			}
287 			osc.last_amp = 0;
288 		}
289 	}
290 }
291 
292 // volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
293 static unsigned char const volumes [16] = {
294 	64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0
295 };
296 
write_data(blip_time_t time,int data)297 void Sms_Apu::write_data( blip_time_t time, int data )
298 {
299 	require( (unsigned) data <= 0xFF );
300 
301 	run_until( time );
302 
303 	if ( data & 0x80 )
304 		latch = data;
305 
306 	int index = (latch >> 5) & 3;
307 	if ( latch & 0x10 )
308 	{
309 		oscs [index]->volume = volumes [data & 15];
310 	}
311 	else if ( index < 3 )
312 	{
313 		Sms_Square& sq = squares [index];
314 		if ( data & 0x80 )
315 			sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF);
316 		else
317 			sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00);
318 	}
319 	else
320 	{
321 		int select = data & 3;
322 		if ( select < 3 )
323 			noise.period = &noise_periods [select];
324 		else
325 			noise.period = &squares [2].period;
326 
327 		noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback;
328 		noise.shifter = 0x8000;
329 	}
330 }
331