Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
folium-app
GitHub Repository: folium-app/Folium
Path: blob/a-new-beginning/Cherry/Core/audio/Sms_Apu.cpp
2 views
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
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
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
40
inline void Sms_Square::reset()
41
{
42
period = 0;
43
phase = 0;
44
Sms_Osc::reset();
45
}
46
47
void Sms_Square::run( blip_time_t time, blip_time_t end_time )
48
{
49
int amp = volume;
50
if ( period > 128 )
51
amp = amp << 1 & -phase;
52
53
{
54
int delta = amp - last_amp;
55
if ( delta )
56
{
57
last_amp = amp;
58
synth->offset( time, delta, output );
59
}
60
}
61
62
time += delay;
63
delay = 0;
64
if ( period )
65
{
66
if ( time < end_time )
67
{
68
if ( !volume || period <= 128 ) // ignore 16kHz and higher
69
{
70
// keep calculating phase
71
int count = (end_time - time + period - 1) / period;
72
phase = (phase + count) & 1;
73
time += count * period;
74
}
75
else
76
{
77
Blip_Buffer* const output_ = this->output;
78
int delta = amp * 2 - volume * 2;
79
do
80
{
81
delta = -delta;
82
synth->offset_inline( time, delta, output_ );
83
time += period;
84
}
85
while ( time < end_time );
86
87
last_amp = (delta >> 1) + volume;
88
phase = (delta >= 0);
89
}
90
}
91
delay = time - end_time;
92
}
93
}
94
95
// Sms_Noise
96
97
static int const noise_periods [3] = { 0x100, 0x200, 0x400 };
98
99
inline void Sms_Noise::reset()
100
{
101
period = &noise_periods [0];
102
shifter = 0x8000;
103
feedback = 0x9000;
104
Sms_Osc::reset();
105
}
106
107
void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
108
{
109
int amp = (shifter & 1) ? 0 : volume;
110
111
{
112
int delta = amp - last_amp;
113
if ( delta )
114
{
115
last_amp = amp;
116
synth.offset( time, delta, output );
117
}
118
}
119
120
time += delay;
121
if ( !volume )
122
time = end_time;
123
124
if ( time < end_time )
125
{
126
Blip_Buffer* const output_ = this->output;
127
unsigned shifter_ = this->shifter;
128
int delta = (shifter_ & 1) ? -volume : volume;
129
int period_ = *this->period * 2;
130
if ( !period_ )
131
period_ = 16;
132
133
do
134
{
135
int changed = shifter_ + 1;
136
shifter_ = (feedback & -(shifter_ & 1)) ^ (shifter_ >> 1);
137
if ( changed & 2 ) // true if bits 0 and 1 differ
138
{
139
amp = (shifter_ & 1) ? 0 : volume;
140
delta = -delta;
141
synth.offset_inline( time, delta, output_ );
142
last_amp = amp;
143
}
144
time += period_;
145
}
146
while ( time < end_time );
147
148
this->shifter = shifter_;
149
this->last_amp = (shifter_ & 1) ? 0 : volume; //delta >> 1;
150
}
151
delay = time - end_time;
152
}
153
154
// Sms_Apu
155
156
Sms_Apu::Sms_Apu()
157
{
158
for ( int i = 0; i < 3; i++ )
159
{
160
squares [i].synth = &square_synth;
161
oscs [i] = &squares [i];
162
}
163
oscs [3] = &noise;
164
165
volume( 1.0 );
166
reset();
167
}
168
169
Sms_Apu::~Sms_Apu()
170
{
171
}
172
173
void Sms_Apu::volume( double vol )
174
{
175
vol *= 0.85 / (osc_count * 64 * 2);
176
square_synth.volume( vol );
177
noise.synth.volume( vol );
178
}
179
180
void Sms_Apu::treble_eq( const blip_eq_t& eq )
181
{
182
square_synth.treble_eq( eq );
183
noise.synth.treble_eq( eq );
184
}
185
186
void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
187
{
188
require( (unsigned) index < osc_count );
189
require( (center && left && right) || (!center && !left && !right) );
190
Sms_Osc& osc = *oscs [index];
191
osc.outputs [1] = right;
192
osc.outputs [2] = left;
193
osc.outputs [3] = center;
194
osc.output = osc.outputs [osc.output_select];
195
}
196
197
void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
198
{
199
for ( int i = 0; i < osc_count; i++ )
200
osc_output( i, center, left, right );
201
}
202
203
void Sms_Apu::reset( unsigned feedback, int noise_width )
204
{
205
last_time = 0;
206
latch = 0;
207
ggstereo_save = 0xFF;
208
209
if ( !feedback || !noise_width )
210
{
211
feedback = 0x0003;
212
noise_width = 15;
213
}
214
// convert to "Galios configuration"
215
looped_feedback = 1 << (noise_width - 1);
216
noise_feedback = 0;
217
while ( noise_width-- )
218
{
219
noise_feedback = (noise_feedback << 1) | (feedback & 1);
220
feedback >>= 1;
221
}
222
223
squares [0].reset();
224
squares [1].reset();
225
squares [2].reset();
226
noise.reset();
227
}
228
229
void Sms_Apu::run_until( blip_time_t end_time )
230
{
231
require( end_time >= last_time ); // end_time must not be before previous time
232
233
if ( end_time > last_time )
234
{
235
// run oscillators
236
for ( int i = 0; i < osc_count; ++i )
237
{
238
Sms_Osc& osc = *oscs [i];
239
if ( osc.output )
240
{
241
if ( i < 3 )
242
squares [i].run( last_time, end_time );
243
else
244
noise.run( last_time, end_time );
245
}
246
}
247
248
last_time = end_time;
249
}
250
}
251
252
void Sms_Apu::end_frame( blip_time_t end_time )
253
{
254
if ( end_time > last_time )
255
run_until( end_time );
256
257
assert( last_time >= end_time );
258
last_time -= end_time;
259
}
260
261
void Sms_Apu::write_ggstereo( blip_time_t time, int data )
262
{
263
require( (unsigned) data <= 0xFF );
264
265
ggstereo_save = data;
266
267
run_until( time );
268
269
for ( int i = 0; i < osc_count; i++ )
270
{
271
Sms_Osc& osc = *oscs [i];
272
int flags = data >> i;
273
Blip_Buffer* old_output = osc.output;
274
osc.output_select = (flags >> 3 & 2) | (flags & 1);
275
osc.output = osc.outputs [osc.output_select];
276
if ( osc.output != old_output && osc.last_amp )
277
{
278
if ( old_output )
279
{
280
square_synth.offset( time, -osc.last_amp, old_output );
281
}
282
osc.last_amp = 0;
283
}
284
}
285
}
286
287
// volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
288
static unsigned char const volumes [16] = {
289
64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0
290
};
291
292
void Sms_Apu::write_data( blip_time_t time, int data )
293
{
294
require( (unsigned) data <= 0xFF );
295
296
run_until( time );
297
298
if ( data & 0x80 )
299
latch = data;
300
301
int index = (latch >> 5) & 3;
302
if ( latch & 0x10 )
303
{
304
oscs [index]->volume = volumes [data & 15];
305
}
306
else if ( index < 3 )
307
{
308
Sms_Square& sq = squares [index];
309
if ( data & 0x80 )
310
sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF);
311
else
312
sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00);
313
}
314
else
315
{
316
int select = data & 3;
317
if ( select < 3 )
318
noise.period = &noise_periods [select];
319
else
320
noise.period = &squares [2].period;
321
322
noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback;
323
noise.shifter = 0x8000;
324
}
325
}
326
327