1 // Sms_Snd_Emu 0.1.1. http://www.slack.net/~ant/
5 /* Copyright (C) 2003-2008 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 */
16 #include "blargg_source.h"
18 int const noise_osc
= 3;
20 void Sms_apu_volume( struct Sms_Apu
* this, double vol
)
22 vol
*= 0.85 / sms_osc_count
/ 64;
23 Synth_volume( &this->synth
, vol
);
26 inline int calc_output( struct Sms_Apu
* this, int i
)
28 int flags
= this->ggstereo
>> i
;
29 return (flags
>> 3 & 2) | (flags
& 1);
32 void Sms_apu_set_output( struct Sms_Apu
* this, int i
, struct Blip_Buffer
* center
, struct Blip_Buffer
* left
, struct Blip_Buffer
* right
)
39 // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
40 require( !center
|| (center
&& !left
&& !right
) || (center
&& left
&& right
) );
41 require( (unsigned) i
< sms_osc_count
); // fails if you pass invalid osc index
45 unsigned const divisor
= 16384 * 16 * 2;
46 this->min_tone_period
= ((unsigned) Blip_clock_rate( center
) + divisor
/2) / divisor
;
49 if ( !center
|| !left
|| !right
)
55 struct Osc
* o
= &this->oscs
[i
];
56 o
->outputs
[0] = NULL
;
57 o
->outputs
[1] = right
;
58 o
->outputs
[2] = left
;
59 o
->outputs
[3] = center
;
60 o
->output
= o
->outputs
[calc_output( this, i
)];
63 static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci
, int width
)
66 while ( --width
>= 0 )
68 galois
= (galois
<< 1) | (fibonacci
& 1);
74 void Sms_apu_reset( struct Sms_Apu
* this, unsigned feedback
, int noise_width
)
80 // Calculate noise feedback values
81 if ( !feedback
|| !noise_width
)
86 this->looped_feedback
= 1 << (noise_width
- 1);
87 this->noise_feedback
= fibonacci_to_galois_lfsr( feedback
, noise_width
);
91 for ( i
= sms_osc_count
; --i
>= 0; )
93 struct Osc
* o
= &this->oscs
[i
];
99 o
->volume
= 15; // silent
102 this->oscs
[noise_osc
].phase
= 0x8000;
103 Sms_apu_write_ggstereo( this, 0, 0xFF );
106 void Sms_apu_init( struct Sms_Apu
* this )
108 this->min_tone_period
= 7;
110 Synth_init( &this->synth
);
112 // Clear outputs to NULL FIRST
116 for ( i
= sms_osc_count
; --i
>= 0; )
117 Sms_apu_set_output( this, i
, NULL
, NULL
, NULL
);
119 Sms_apu_volume( this, 1.0 );
120 Sms_apu_reset( this, 0, 0 );
123 static void run_until( struct Sms_Apu
* this, blip_time_t end_time
)
125 require( end_time
>= this->last_time
);
126 if ( end_time
<= this->last_time
)
129 // Synthesize each oscillator
131 for ( idx
= sms_osc_count
; --idx
>= 0; )
133 struct Osc
* osc
= &this->oscs
[idx
];
137 // Determine what will be generated
138 struct Blip_Buffer
* const out
= osc
->output
;
141 // volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
142 static unsigned char const volumes
[16] ICONST_ATTR
= {
143 64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0
146 vol
= volumes
[osc
->volume
];
147 amp
= (osc
->phase
& 1) * vol
;
149 // Square freq above 16 kHz yields constant amplitude at half volume
150 if ( idx
!= noise_osc
&& osc
->period
< this->min_tone_period
)
157 int delta
= amp
- osc
->last_amp
;
161 /* norm_synth.offset( last_time, delta, out ); */
162 Synth_offset( &this->synth
, this->last_time
, delta
, out
);
163 /* out->set_modified(); */
164 Blip_set_modified( out
);
169 blip_time_t time
= this->last_time
+ osc
->delay
;
170 if ( time
< end_time
)
172 // Calculate actual period
173 int period
= osc
->period
;
174 if ( idx
== noise_osc
)
176 period
= 0x20 << (period
& 3);
177 if ( period
== 0x100 )
178 period
= this->oscs
[2].period
* 2;
184 // Maintain phase when silent
185 int phase
= osc
->phase
;
188 int count
= (end_time
- time
+ period
- 1) / period
;
189 time
+= count
* period
;
190 if ( idx
!= noise_osc
) // TODO: maintain noise LFSR phase?
195 int delta
= amp
* 2 - vol
;
197 if ( idx
!= noise_osc
)
203 /* norm_synth.offset( time, delta, out ); */
204 Synth_offset( &this->synth
, time
, delta
, out
);
207 while ( time
< end_time
);
208 phase
= (delta
>= 0);
213 unsigned const feedback
= (osc
->period
& 4 ? this->noise_feedback
: this->looped_feedback
);
216 unsigned changed
= phase
+ 1;
217 phase
= ((phase
& 1) * feedback
) ^ (phase
>> 1);
218 if ( changed
& 2 ) // true if bits 0 and 1 differ
221 /* fast_synth.offset_inline( time, delta, out ); */
222 Synth_offset_inline( &this->synth
, time
, delta
, out
);
226 while ( time
< end_time
);
229 osc
->last_amp
= (phase
& 1) * vol
;
230 Blip_set_modified( out
);
234 osc
->delay
= time
- end_time
;
236 this->last_time
= end_time
;
239 void Sms_apu_write_ggstereo( struct Sms_Apu
* this, blip_time_t time
, int data
)
241 require( (unsigned) data
<= 0xFF );
243 run_until( this, time
);
244 this->ggstereo
= data
;
247 for ( i
= sms_osc_count
; --i
>= 0; )
249 struct Osc
* osc
= &this->oscs
[i
];
251 struct Blip_Buffer
* old
= osc
->output
;
252 osc
->output
= osc
->outputs
[calc_output( this, i
)];
253 if ( osc
->output
!= old
)
255 int delta
= -osc
->last_amp
;
261 Blip_set_modified( old
);
262 Synth_offset( &this->synth
, this->last_time
, delta
, old
);
269 void Sms_apu_write_data( struct Sms_Apu
* this, blip_time_t time
, int data
)
271 require( (unsigned) data
<= 0xFF );
273 run_until( this, time
);
278 // We want the raw values written so our save state format can be
279 // as close to hardware as possible and unspecific to any emulator.
280 int idx
= this->latch
>> 5 & 3;
281 struct Osc
* osc
= &this->oscs
[idx
];
282 if ( this->latch
& 0x10 )
284 osc
->volume
= data
& 0x0F;
288 if ( idx
== noise_osc
)
289 osc
->phase
= 0x8000; // reset noise LFSR
291 // Replace high 6 bits/low 4 bits of register with data
292 int lo
= osc
->period
;
294 if ( idx
== noise_osc
|| (data
& 0x80) )
299 osc
->period
= (hi
& 0x3F0) | (lo
& 0x00F);
303 void Sms_apu_end_frame( struct Sms_Apu
* this, blip_time_t end_time
)
305 if ( end_time
> this->last_time
)
306 run_until( this, end_time
);
308 this->last_time
-= end_time
;
309 assert( this->last_time
>= 0 );