yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dsp.cc
Go to the documentation of this file.
1#include "app/emu/audio/dsp.h"
2
3#include <cmath>
4#include <cstring>
5
6namespace yaze {
7namespace emu {
8
9static const int rateValues[32] = {0, 2048, 1536, 1280, 1024, 768, 640, 512,
10 384, 320, 256, 192, 160, 128, 96, 80,
11 64, 48, 40, 32, 24, 20, 16, 12,
12 10, 8, 6, 5, 4, 3, 2, 1};
13
14static const int rateOffsets[32] = {0, 0, 1040, 536, 0, 1040, 536, 0, 1040,
15 536, 0, 1040, 536, 0, 1040, 536, 0, 1040,
16 536, 0, 1040, 536, 0, 1040, 536, 0, 1040,
17 536, 0, 1040, 536, 0};
18
19static const int gaussValues[512] = {
20 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
21 0x000, 0x000, 0x000, 0x000, 0x000, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001,
22 0x001, 0x001, 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002, 0x002,
23 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004,
24 0x005, 0x005, 0x005, 0x005, 0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007,
25 0x008, 0x008, 0x008, 0x009, 0x009, 0x009, 0x00a, 0x00a, 0x00a, 0x00b, 0x00b,
26 0x00b, 0x00c, 0x00c, 0x00d, 0x00d, 0x00e, 0x00e, 0x00f, 0x00f, 0x00f, 0x010,
27 0x010, 0x011, 0x011, 0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016,
28 0x017, 0x017, 0x018, 0x018, 0x019, 0x01a, 0x01b, 0x01b, 0x01c, 0x01d, 0x01d,
29 0x01e, 0x01f, 0x020, 0x020, 0x021, 0x022, 0x023, 0x024, 0x024, 0x025, 0x026,
30 0x027, 0x028, 0x029, 0x02a, 0x02b, 0x02c, 0x02d, 0x02e, 0x02f, 0x030, 0x031,
31 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x03a, 0x03b, 0x03c, 0x03d,
32 0x03e, 0x040, 0x041, 0x042, 0x043, 0x045, 0x046, 0x047, 0x049, 0x04a, 0x04c,
33 0x04d, 0x04e, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059, 0x05a, 0x05c,
34 0x05e, 0x05f, 0x061, 0x063, 0x064, 0x066, 0x068, 0x06a, 0x06b, 0x06d, 0x06f,
35 0x071, 0x073, 0x075, 0x076, 0x078, 0x07a, 0x07c, 0x07e, 0x080, 0x082, 0x084,
36 0x086, 0x089, 0x08b, 0x08d, 0x08f, 0x091, 0x093, 0x096, 0x098, 0x09a, 0x09c,
37 0x09f, 0x0a1, 0x0a3, 0x0a6, 0x0a8, 0x0ab, 0x0ad, 0x0af, 0x0b2, 0x0b4, 0x0b7,
38 0x0ba, 0x0bc, 0x0bf, 0x0c1, 0x0c4, 0x0c7, 0x0c9, 0x0cc, 0x0cf, 0x0d2, 0x0d4,
39 0x0d7, 0x0da, 0x0dd, 0x0e0, 0x0e3, 0x0e6, 0x0e9, 0x0ec, 0x0ef, 0x0f2, 0x0f5,
40 0x0f8, 0x0fb, 0x0fe, 0x101, 0x104, 0x107, 0x10b, 0x10e, 0x111, 0x114, 0x118,
41 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, 0x130, 0x133, 0x137, 0x13a, 0x13e,
42 0x141, 0x145, 0x148, 0x14c, 0x150, 0x153, 0x157, 0x15b, 0x15f, 0x162, 0x166,
43 0x16a, 0x16e, 0x172, 0x176, 0x17a, 0x17d, 0x181, 0x185, 0x189, 0x18d, 0x191,
44 0x195, 0x19a, 0x19e, 0x1a2, 0x1a6, 0x1aa, 0x1ae, 0x1b2, 0x1b7, 0x1bb, 0x1bf,
45 0x1c3, 0x1c8, 0x1cc, 0x1d0, 0x1d5, 0x1d9, 0x1dd, 0x1e2, 0x1e6, 0x1eb, 0x1ef,
46 0x1f3, 0x1f8, 0x1fc, 0x201, 0x205, 0x20a, 0x20f, 0x213, 0x218, 0x21c, 0x221,
47 0x226, 0x22a, 0x22f, 0x233, 0x238, 0x23d, 0x241, 0x246, 0x24b, 0x250, 0x254,
48 0x259, 0x25e, 0x263, 0x267, 0x26c, 0x271, 0x276, 0x27b, 0x280, 0x284, 0x289,
49 0x28e, 0x293, 0x298, 0x29d, 0x2a2, 0x2a6, 0x2ab, 0x2b0, 0x2b5, 0x2ba, 0x2bf,
50 0x2c4, 0x2c9, 0x2ce, 0x2d3, 0x2d8, 0x2dc, 0x2e1, 0x2e6, 0x2eb, 0x2f0, 0x2f5,
51 0x2fa, 0x2ff, 0x304, 0x309, 0x30e, 0x313, 0x318, 0x31d, 0x322, 0x326, 0x32b,
52 0x330, 0x335, 0x33a, 0x33f, 0x344, 0x349, 0x34e, 0x353, 0x357, 0x35c, 0x361,
53 0x366, 0x36b, 0x370, 0x374, 0x379, 0x37e, 0x383, 0x388, 0x38c, 0x391, 0x396,
54 0x39b, 0x39f, 0x3a4, 0x3a9, 0x3ad, 0x3b2, 0x3b7, 0x3bb, 0x3c0, 0x3c5, 0x3c9,
55 0x3ce, 0x3d2, 0x3d7, 0x3dc, 0x3e0, 0x3e5, 0x3e9, 0x3ed, 0x3f2, 0x3f6, 0x3fb,
56 0x3ff, 0x403, 0x408, 0x40c, 0x410, 0x415, 0x419, 0x41d, 0x421, 0x425, 0x42a,
57 0x42e, 0x432, 0x436, 0x43a, 0x43e, 0x442, 0x446, 0x44a, 0x44e, 0x452, 0x455,
58 0x459, 0x45d, 0x461, 0x465, 0x468, 0x46c, 0x470, 0x473, 0x477, 0x47a, 0x47e,
59 0x481, 0x485, 0x488, 0x48c, 0x48f, 0x492, 0x496, 0x499, 0x49c, 0x49f, 0x4a2,
60 0x4a6, 0x4a9, 0x4ac, 0x4af, 0x4b2, 0x4b5, 0x4b7, 0x4ba, 0x4bd, 0x4c0, 0x4c3,
61 0x4c5, 0x4c8, 0x4cb, 0x4cd, 0x4d0, 0x4d2, 0x4d5, 0x4d7, 0x4d9, 0x4dc, 0x4de,
62 0x4e0, 0x4e3, 0x4e5, 0x4e7, 0x4e9, 0x4eb, 0x4ed, 0x4ef, 0x4f1, 0x4f3, 0x4f5,
63 0x4f6, 0x4f8, 0x4fa, 0x4fb, 0x4fd, 0x4ff, 0x500, 0x502, 0x503, 0x504, 0x506,
64 0x507, 0x508, 0x50a, 0x50b, 0x50c, 0x50d, 0x50e, 0x50f, 0x510, 0x511, 0x511,
65 0x512, 0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, 0x517, 0x518,
66 0x518, 0x518, 0x518, 0x518, 0x519, 0x519};
67
68void Dsp::Reset() {
69 memset(ram, 0, sizeof(ram));
70 ram[0x7c] = 0xff; // set ENDx
71 for (int i = 0; i < 8; i++) {
72 channel[i].pitch = 0;
73 channel[i].pitchCounter = 0;
74 channel[i].pitchModulation = false;
75 memset(channel[i].decodeBuffer, 0, sizeof(channel[i].decodeBuffer));
76 channel[i].bufferOffset = 0;
77 channel[i].srcn = 0;
78 channel[i].decodeOffset = 0;
79 channel[i].blockOffset = 0;
80 channel[i].brrHeader = 0;
81 channel[i].useNoise = false;
82 channel[i].startDelay = 0;
83 memset(channel[i].adsrRates, 0, sizeof(channel[i].adsrRates));
84 channel[i].adsrState = 0;
85 channel[i].sustainLevel = 0;
87 channel[i].useGain = false;
88 channel[i].gainMode = 0;
89 channel[i].directGain = false;
90 channel[i].gainValue = 0;
91 channel[i].preclampGain = 0;
92 channel[i].gain = 0;
93 channel[i].keyOn = false;
94 channel[i].keyOff = false;
95 channel[i].sampleOut = 0;
96 channel[i].volumeL = 0;
97 channel[i].volumeR = 0;
98 channel[i].echoEnable = false;
99 }
100 counter = 0;
101 dirPage = 0;
102 evenCycle = true;
103 mute = true;
104 reset = true;
105 masterVolumeL = 0;
106 masterVolumeR = 0;
107 sampleOutL = 0;
108 sampleOutR = 0;
109 echoOutL = 0;
110 echoOutR = 0;
111 noiseSample = 0x4000;
112 noiseRate = 0;
113 echoWrites = false;
114 echoVolumeL = 0;
115 echoVolumeR = 0;
116 feedbackVolume = 0;
117 echoBufferAdr = 0;
118 echoDelay = 0;
119 echoLength = 0;
120 echoBufferIndex = 0;
121 firBufferIndex = 0;
122 memset(firValues, 0, sizeof(firValues));
123 memset(firBufferL, 0, sizeof(firBufferL));
124 memset(firBufferR, 0, sizeof(firBufferR));
125 memset(sampleBuffer, 0, sizeof(sampleBuffer));
126 sampleOffset = 0;
128}
129
133
135 sampleOutL = 0;
136 sampleOutR = 0;
137 echoOutL = 0;
138 echoOutR = 0;
139 for (int i = 0; i < 8; i++) {
140 CycleChannel(i);
141 }
142 HandleEcho(); // also applies master volume
143 counter = counter == 0 ? 30720 : counter - 1;
144 HandleNoise();
146 // handle mute flag
147 if (mute) {
148 sampleOutL = 0;
149 sampleOutR = 0;
150 }
151 // put final sample in the ring buffer and advance pointer
152 sampleBuffer[(sampleOffset & 0x3ff) * 2] = sampleOutL;
153 sampleBuffer[(sampleOffset & 0x3ff) * 2 + 1] = sampleOutR;
154 sampleOffset = (sampleOffset + 1) & 0x3ff;
155}
156
157static int clamp16(int val) {
158 return val < -0x8000 ? -0x8000 : (val > 0x7fff ? 0x7fff : val);
159}
160
161static int clip16(int val) { return (int16_t)(val & 0xffff); }
162
163bool Dsp::CheckCounter(int rate) {
164 if (rate == 0) return false;
165 return ((counter + rateOffsets[rate]) % rateValues[rate]) == 0;
166}
167
169 // increment fir buffer index
171 firBufferIndex &= 0x7;
172 // get value out of ram
173 uint16_t adr = echoBufferAdr + echoBufferIndex;
174 int16_t ramSample = aram_[adr] | (aram_[(adr + 1) & 0xffff] << 8);
175 firBufferL[firBufferIndex] = ramSample >> 1;
176 ramSample = aram_[(adr + 2) & 0xffff] | (aram_[(adr + 3) & 0xffff] << 8);
177 firBufferR[firBufferIndex] = ramSample >> 1;
178 // calculate FIR-sum
179 int sumL = 0, sumR = 0;
180 for (int i = 0; i < 8; i++) {
181 sumL += (firBufferL[(firBufferIndex + i + 1) & 0x7] * firValues[i]) >> 6;
182 sumR += (firBufferR[(firBufferIndex + i + 1) & 0x7] * firValues[i]) >> 6;
183 if (i == 6) {
184 // clip to 16-bit before last addition
185 sumL = clip16(sumL);
186 sumR = clip16(sumR);
187 }
188 }
189 sumL = clamp16(sumL) & ~1;
190 sumR = clamp16(sumR) & ~1;
191 // apply master volume and modify output with sum
192 sampleOutL = clamp16(((sampleOutL * masterVolumeL) >> 7) +
193 ((sumL * echoVolumeL) >> 7));
194 sampleOutR = clamp16(((sampleOutR * masterVolumeR) >> 7) +
195 ((sumR * echoVolumeR) >> 7));
196 // get echo value
197 int echoL = clamp16(echoOutL + clip16((sumL * feedbackVolume) >> 7)) & ~1;
198 int echoR = clamp16(echoOutR + clip16((sumR * feedbackVolume) >> 7)) & ~1;
199 // write it to ram
200 if (echoWrites) {
201 aram_[adr] = echoL & 0xff;
202 aram_[(adr + 1) & 0xffff] = echoL >> 8;
203 aram_[(adr + 2) & 0xffff] = echoR & 0xff;
204 aram_[(adr + 3) & 0xffff] = echoR >> 8;
205 }
206 // handle indexes
207 if (echoBufferIndex == 0) {
208 echoLength = echoDelay * 4;
209 }
210 echoBufferIndex += 4;
212 echoBufferIndex = 0;
213 }
214}
215
216void Dsp::CycleChannel(int ch) {
217 // handle pitch counter
218 int pitch = channel[ch].pitch;
219 if (ch > 0 && channel[ch].pitchModulation) {
220 pitch += ((channel[ch - 1].sampleOut >> 5) * pitch) >> 10;
221 }
222 // get current brr header and get sample address
224 uint16_t samplePointer = dirPage + 4 * channel[ch].srcn;
225 if (channel[ch].startDelay == 0) samplePointer += 2;
226 uint16_t sampleAdr =
227 aram_[samplePointer] | (aram_[(samplePointer + 1) & 0xffff] << 8);
228 // handle starting of sample
229 if (channel[ch].startDelay > 0) {
230 if (channel[ch].startDelay == 5) {
231 // first keyed on
232 channel[ch].decodeOffset = sampleAdr;
233 channel[ch].blockOffset = 1;
234 channel[ch].bufferOffset = 0;
235 channel[ch].brrHeader = 0;
236 ram[0x7c] &= ~(1 << ch); // clear ENDx
237 }
238 channel[ch].gain = 0;
239 channel[ch].startDelay--;
240 channel[ch].pitchCounter = 0;
241 if (channel[ch].startDelay > 0 && channel[ch].startDelay < 4) {
242 channel[ch].pitchCounter = 0x4000;
243 }
244 pitch = 0;
245 }
246 // get sample
247 int sample = 0;
248 if (channel[ch].useNoise) {
249 sample = clip16(noiseSample * 2);
250 } else {
251 sample = GetSample(ch);
252 }
253 sample = ((sample * channel[ch].gain) >> 11) & ~1;
254 // handle reset and release
255 if (reset || (channel[ch].brrHeader & 0x03) == 1) {
256 channel[ch].adsrState = 3; // go to release
257 channel[ch].gain = 0;
258 }
259 // handle keyon/keyoff
260 if (evenCycle) {
261 if (channel[ch].keyOff) {
262 channel[ch].adsrState = 3; // go to release
263 }
264 if (channel[ch].keyOn) {
265 channel[ch].startDelay = 5;
266 channel[ch].adsrState = 0; // go to attack
267 channel[ch].keyOn = false;
268 }
269 }
270 // handle envelope
271 if (channel[ch].startDelay == 0) {
272 HandleGain(ch);
273 }
274 // decode new brr samples if needed and update offsets
275 if (channel[ch].pitchCounter >= 0x4000) {
276 DecodeBrr(ch);
277 if (channel[ch].blockOffset >= 7) {
278 if (channel[ch].brrHeader & 0x1) {
279 channel[ch].decodeOffset = sampleAdr;
280 ram[0x7c] |= 1 << ch; // set ENDx
281 } else {
282 channel[ch].decodeOffset += 9;
283 }
284 channel[ch].blockOffset = 1;
285 } else {
286 channel[ch].blockOffset += 2;
287 }
288 }
289 // update pitch counter
290 channel[ch].pitchCounter &= 0x3fff;
291 channel[ch].pitchCounter += pitch;
292 if (channel[ch].pitchCounter > 0x7fff) channel[ch].pitchCounter = 0x7fff;
293 // set outputs
294 ram[(ch << 4) | 8] = channel[ch].gain >> 4;
295 ram[(ch << 4) | 9] = sample >> 8;
296 channel[ch].sampleOut = sample;
297 sampleOutL = clamp16(sampleOutL + ((sample * channel[ch].volumeL) >> 7));
298 sampleOutR = clamp16(sampleOutR + ((sample * channel[ch].volumeR) >> 7));
299 if (channel[ch].echoEnable) {
300 echoOutL = clamp16(echoOutL + ((sample * channel[ch].volumeL) >> 7));
301 echoOutR = clamp16(echoOutR + ((sample * channel[ch].volumeR) >> 7));
302 }
303}
304
305void Dsp::HandleGain(int ch) {
306 int newGain = channel[ch].gain;
307 int rate = 0;
308 // handle gain mode
309 if (channel[ch].adsrState == 3) { // release
310 rate = 31;
311 newGain -= 8;
312 } else {
313 if (!channel[ch].useGain) {
314 rate = channel[ch].adsrRates[channel[ch].adsrState];
315 switch (channel[ch].adsrState) {
316 case 0:
317 newGain += rate == 31 ? 1024 : 32;
318 break; // attack
319 case 1:
320 newGain -= ((newGain - 1) >> 8) + 1;
321 break; // decay
322 case 2:
323 newGain -= ((newGain - 1) >> 8) + 1;
324 break; // sustain
325 }
326 } else {
327 if (!channel[ch].directGain) {
328 rate = channel[ch].adsrRates[3];
329 switch (channel[ch].gainMode) {
330 case 0:
331 newGain -= 32;
332 break; // linear decrease
333 case 1:
334 newGain -= ((newGain - 1) >> 8) + 1;
335 break; // exponential decrease
336 case 2:
337 newGain += 32;
338 break; // linear increase
339 case 3:
340 newGain += (channel[ch].preclampGain < 0x600) ? 32 : 8;
341 break; // bent increase
342 }
343 } else { // direct gain
344 rate = 31;
345 newGain = channel[ch].gainValue;
346 }
347 }
348 }
349 // use sustain level according to mode
350 int sustainLevel = channel[ch].useGain ? channel[ch].gainSustainLevel
351 : channel[ch].sustainLevel;
352 if (channel[ch].adsrState == 1 && (newGain >> 8) == sustainLevel) {
353 channel[ch].adsrState = 2; // go to sustain
354 }
355 // store pre-clamped gain (for bent increase)
356 channel[ch].preclampGain = newGain & 0xffff;
357 // clamp gain
358 if (newGain < 0 || newGain > 0x7ff) {
359 newGain = newGain < 0 ? 0 : 0x7ff;
360 if (channel[ch].adsrState == 0) {
361 channel[ch].adsrState = 1; // go to decay
362 }
363 }
364 // store new value
365 if (CheckCounter(rate)) channel[ch].gain = newGain;
366}
367
368int16_t Dsp::GetSample(int ch) {
369 int pos = (channel[ch].pitchCounter >> 12) + channel[ch].bufferOffset;
370 int offset = (channel[ch].pitchCounter >> 4) & 0xff;
371 int16_t news = channel[ch].decodeBuffer[(pos + 3) % 12];
372 int16_t olds = channel[ch].decodeBuffer[(pos + 2) % 12];
373 int16_t olders = channel[ch].decodeBuffer[(pos + 1) % 12];
374 int16_t oldests = channel[ch].decodeBuffer[pos % 12];
375 int out = (gaussValues[0xff - offset] * oldests) >> 11;
376 out += (gaussValues[0x1ff - offset] * olders) >> 11;
377 out += (gaussValues[0x100 + offset] * olds) >> 11;
378 out = clip16(out) + ((gaussValues[offset] * news) >> 11);
379 return clamp16(out) & ~1;
380}
381
382void Dsp::DecodeBrr(int ch) {
383 int shift = channel[ch].brrHeader >> 4;
384 int filter = (channel[ch].brrHeader & 0xc) >> 2;
385 int bOff = channel[ch].bufferOffset;
386 int old = channel[ch].decodeBuffer[bOff == 0 ? 11 : bOff - 1] >> 1;
387 int older = channel[ch].decodeBuffer[bOff == 0 ? 10 : bOff - 2] >> 1;
388 uint8_t curByte = 0;
389 for (int i = 0; i < 4; i++) {
390 int s = 0;
391 if (i & 1) {
392 s = curByte & 0xf;
393 } else {
394 curByte = aram_[(channel[ch].decodeOffset + channel[ch].blockOffset +
395 (i >> 1)) &
396 0xffff];
397 s = curByte >> 4;
398 }
399 if (s > 7) s -= 16;
400 if (shift <= 0xc) {
401 s = (s << shift) >> 1;
402 } else {
403 s = (s >> 3) << 12;
404 }
405 switch (filter) {
406 case 1:
407 s += old + (-old >> 4);
408 break;
409 case 2:
410 s += 2 * old + ((3 * -old) >> 5) - older + (older >> 4);
411 break;
412 case 3:
413 s += 2 * old + ((13 * -old) >> 6) - older + ((3 * older) >> 4);
414 break;
415 }
416 channel[ch].decodeBuffer[bOff + i] = clamp16(s) * 2; // cuts off bit 15
417 older = old;
418 old = channel[ch].decodeBuffer[bOff + i] >> 1;
419 }
420 channel[ch].bufferOffset += 4;
421 if (channel[ch].bufferOffset >= 12) channel[ch].bufferOffset = 0;
422}
423
425 if (CheckCounter(noiseRate)) {
426 int bit = (noiseSample & 1) ^ ((noiseSample >> 1) & 1);
427 noiseSample = ((noiseSample >> 1) & 0x3fff) | (bit << 14);
428 }
429}
430
431uint8_t Dsp::Read(uint8_t adr) { return ram[adr]; }
432
433void Dsp::Write(uint8_t adr, uint8_t val) {
434 int ch = adr >> 4;
435 switch (adr) {
436 case 0x00:
437 case 0x10:
438 case 0x20:
439 case 0x30:
440 case 0x40:
441 case 0x50:
442 case 0x60:
443 case 0x70: {
444 channel[ch].volumeL = val;
445 break;
446 }
447 case 0x01:
448 case 0x11:
449 case 0x21:
450 case 0x31:
451 case 0x41:
452 case 0x51:
453 case 0x61:
454 case 0x71: {
455 channel[ch].volumeR = val;
456 break;
457 }
458 case 0x02:
459 case 0x12:
460 case 0x22:
461 case 0x32:
462 case 0x42:
463 case 0x52:
464 case 0x62:
465 case 0x72: {
466 channel[ch].pitch = (channel[ch].pitch & 0x3f00) | val;
467 break;
468 }
469 case 0x03:
470 case 0x13:
471 case 0x23:
472 case 0x33:
473 case 0x43:
474 case 0x53:
475 case 0x63:
476 case 0x73: {
477 channel[ch].pitch = ((channel[ch].pitch & 0x00ff) | (val << 8)) & 0x3fff;
478 break;
479 }
480 case 0x04:
481 case 0x14:
482 case 0x24:
483 case 0x34:
484 case 0x44:
485 case 0x54:
486 case 0x64:
487 case 0x74: {
488 channel[ch].srcn = val;
489 break;
490 }
491 case 0x05:
492 case 0x15:
493 case 0x25:
494 case 0x35:
495 case 0x45:
496 case 0x55:
497 case 0x65:
498 case 0x75: {
499 channel[ch].adsrRates[0] = (val & 0xf) * 2 + 1;
500 channel[ch].adsrRates[1] = ((val & 0x70) >> 4) * 2 + 16;
501 channel[ch].useGain = (val & 0x80) == 0;
502 break;
503 }
504 case 0x06:
505 case 0x16:
506 case 0x26:
507 case 0x36:
508 case 0x46:
509 case 0x56:
510 case 0x66:
511 case 0x76: {
512 channel[ch].adsrRates[2] = val & 0x1f;
513 channel[ch].sustainLevel = (val & 0xe0) >> 5;
514 break;
515 }
516 case 0x07:
517 case 0x17:
518 case 0x27:
519 case 0x37:
520 case 0x47:
521 case 0x57:
522 case 0x67:
523 case 0x77: {
524 channel[ch].directGain = (val & 0x80) == 0;
525 channel[ch].gainMode = (val & 0x60) >> 5;
526 channel[ch].adsrRates[3] = val & 0x1f;
527 channel[ch].gainValue = (val & 0x7f) * 16;
528 channel[ch].gainSustainLevel = (val & 0xe0) >> 5;
529 break;
530 }
531 case 0x0c: {
532 masterVolumeL = val;
533 break;
534 }
535 case 0x1c: {
536 masterVolumeR = val;
537 break;
538 }
539 case 0x2c: {
540 echoVolumeL = val;
541 break;
542 }
543 case 0x3c: {
544 echoVolumeR = val;
545 break;
546 }
547 case 0x4c: {
548 for (int i = 0; i < 8; i++) {
549 channel[i].keyOn = val & (1 << i);
550 }
551 break;
552 }
553 case 0x5c: {
554 for (int i = 0; i < 8; i++) {
555 channel[i].keyOff = val & (1 << i);
556 }
557 break;
558 }
559 case 0x6c: {
560 reset = val & 0x80;
561 mute = val & 0x40;
562 echoWrites = (val & 0x20) == 0;
563 noiseRate = val & 0x1f;
564 break;
565 }
566 case 0x7c: {
567 val = 0; // any write clears ENDx
568 break;
569 }
570 case 0x0d: {
571 feedbackVolume = val;
572 break;
573 }
574 case 0x2d: {
575 for (int i = 0; i < 8; i++) {
576 channel[i].pitchModulation = val & (1 << i);
577 }
578 break;
579 }
580 case 0x3d: {
581 for (int i = 0; i < 8; i++) {
582 channel[i].useNoise = val & (1 << i);
583 }
584 break;
585 }
586 case 0x4d: {
587 for (int i = 0; i < 8; i++) {
588 channel[i].echoEnable = val & (1 << i);
589 }
590 break;
591 }
592 case 0x5d: {
593 dirPage = val << 8;
594 break;
595 }
596 case 0x6d: {
597 echoBufferAdr = val << 8;
598 break;
599 }
600 case 0x7d: {
601 echoDelay =
602 (val & 0xf) * 512; // 2048-byte steps, stereo sample is 4 bytes
603 break;
604 }
605 case 0x0f:
606 case 0x1f:
607 case 0x2f:
608 case 0x3f:
609 case 0x4f:
610 case 0x5f:
611 case 0x6f:
612 case 0x7f: {
613 firValues[ch] = val;
614 break;
615 }
616 }
617 ram[adr] = val;
618}
619
620// Helper for 4-point cubic interpolation (Catmull-Rom)
621// Provides higher quality resampling compared to linear interpolation.
622inline int16_t InterpolateCubic(int16_t p0, int16_t p1, int16_t p2, int16_t p3,
623 double t) {
624 double t2 = t * t;
625 double t3 = t2 * t;
626
627 double c0 = p1;
628 double c1 = 0.5 * (p2 - p0);
629 double c2 = (p0 - 2.5 * p1 + 2.0 * p2 - 0.5 * p3);
630 double c3 = 0.5 * (-p0 + 3.0 * p1 - 3.0 * p2 + p3);
631
632 double result = c0 + c1 * t + c2 * t2 + c3 * t3;
633
634 // Clamp to 16-bit range
635 return result > 32767.0
636 ? 32767
637 : (result < -32768.0 ? -32768 : static_cast<int16_t>(result));
638}
639
640// Helper for cosine interpolation
641inline int16_t InterpolateCosine(int16_t s0, int16_t s1, double mu) {
642 const double mu2 = (1.0 - cos(mu * 3.14159265358979323846)) / 2.0;
643 return static_cast<int16_t>(s0 * (1.0 - mu2) + s1 * mu2);
644}
645
646// Helper for linear interpolation
647inline int16_t InterpolateLinear(int16_t s0, int16_t s1, double frac) {
648 return static_cast<int16_t>(s0 + frac * (s1 - s0));
649}
650
651// Helper for Hermite interpolation (used by bsnes/Snes9x)
652// Provides smoother interpolation than linear with minimal overhead
653inline int16_t InterpolateHermite(int16_t p0, int16_t p1, int16_t p2, int16_t p3, double t) {
654 const double c0 = p1;
655 const double c1 = (p2 - p0) * 0.5;
656 const double c2 = p0 - 2.5 * p1 + 2.0 * p2 - 0.5 * p3;
657 const double c3 = (p3 - p0) * 0.5 + 1.5 * (p1 - p2);
658
659 const double result = c0 + c1 * t + c2 * t * t + c3 * t * t * t;
660
661 // Clamp to 16-bit range
662 return result > 32767.0 ? 32767
663 : (result < -32768.0 ? -32768
664 : static_cast<int16_t>(result));
665}
666
667void Dsp::GetSamples(int16_t* sample_data, int samples_per_frame,
668 bool pal_timing) {
669 // Resample from native samples-per-frame (NTSC: ~534, PAL: ~641)
670 const double native_per_frame = pal_timing ? 641.0 : 534.0;
671 const double step = native_per_frame / static_cast<double>(samples_per_frame);
672
673 // Start reading one native frame behind the frame boundary
674 double location = static_cast<double>((lastFrameBoundary + 0x400) & 0x3ff);
675 location -= native_per_frame;
676
677 // Ensure location is within valid range
678 while (location < 0) location += 0x400;
679
680 for (int i = 0; i < samples_per_frame; i++) {
681 const int idx = static_cast<int>(location) & 0x3ff;
682 const double frac = location - static_cast<int>(location);
683
684 switch (interpolation_type) {
686 const int next_idx = (idx + 1) & 0x3ff;
687
688 // Linear interpolation for left channel
689 const int16_t s0_l = sampleBuffer[(idx * 2) + 0];
690 const int16_t s1_l = sampleBuffer[(next_idx * 2) + 0];
691 sample_data[(i * 2) + 0] = static_cast<int16_t>(
692 s0_l + frac * (s1_l - s0_l));
693
694 // Linear interpolation for right channel
695 const int16_t s0_r = sampleBuffer[(idx * 2) + 1];
696 const int16_t s1_r = sampleBuffer[(next_idx * 2) + 1];
697 sample_data[(i * 2) + 1] = static_cast<int16_t>(
698 s0_r + frac * (s1_r - s0_r));
699 break;
700 }
702 const int idx0 = (idx - 1 + 0x400) & 0x3ff;
703 const int idx1 = idx & 0x3ff;
704 const int idx2 = (idx + 1) & 0x3ff;
705 const int idx3 = (idx + 2) & 0x3ff;
706 // Left channel
707 const int16_t p0_l = sampleBuffer[(idx0 * 2) + 0];
708 const int16_t p1_l = sampleBuffer[(idx1 * 2) + 0];
709 const int16_t p2_l = sampleBuffer[(idx2 * 2) + 0];
710 const int16_t p3_l = sampleBuffer[(idx3 * 2) + 0];
711 sample_data[(i * 2) + 0] = InterpolateHermite(p0_l, p1_l, p2_l, p3_l, frac);
712 // Right channel
713 const int16_t p0_r = sampleBuffer[(idx0 * 2) + 1];
714 const int16_t p1_r = sampleBuffer[(idx1 * 2) + 1];
715 const int16_t p2_r = sampleBuffer[(idx2 * 2) + 1];
716 const int16_t p3_r = sampleBuffer[(idx3 * 2) + 1];
717 sample_data[(i * 2) + 1] = InterpolateHermite(p0_r, p1_r, p2_r, p3_r, frac);
718 break;
719 }
721 const int next_idx = (idx + 1) & 0x3ff;
722 const int16_t s0_l = sampleBuffer[(idx * 2) + 0];
723 const int16_t s1_l = sampleBuffer[(next_idx * 2) + 0];
724 sample_data[(i * 2) + 0] = InterpolateCosine(s0_l, s1_l, frac);
725 const int16_t s0_r = sampleBuffer[(idx * 2) + 1];
726 const int16_t s1_r = sampleBuffer[(next_idx * 2) + 1];
727 sample_data[(i * 2) + 1] = InterpolateCosine(s0_r, s1_r, frac);
728 break;
729 }
731 const int idx0 = (idx - 1 + 0x400) & 0x3ff;
732 const int idx1 = idx & 0x3ff;
733 const int idx2 = (idx + 1) & 0x3ff;
734 const int idx3 = (idx + 2) & 0x3ff;
735 // Left channel
736 const int16_t p0_l = sampleBuffer[(idx0 * 2) + 0];
737 const int16_t p1_l = sampleBuffer[(idx1 * 2) + 0];
738 const int16_t p2_l = sampleBuffer[(idx2 * 2) + 0];
739 const int16_t p3_l = sampleBuffer[(idx3 * 2) + 0];
740 sample_data[(i * 2) + 0] =
741 InterpolateCubic(p0_l, p1_l, p2_l, p3_l, frac);
742 // Right channel
743 const int16_t p0_r = sampleBuffer[(idx0 * 2) + 1];
744 const int16_t p1_r = sampleBuffer[(idx1 * 2) + 1];
745 const int16_t p2_r = sampleBuffer[(idx2 * 2) + 1];
746 const int16_t p3_r = sampleBuffer[(idx3 * 2) + 1];
747 sample_data[(i * 2) + 1] =
748 InterpolateCubic(p0_r, p1_r, p2_r, p3_r, frac);
749 break;
750 }
751 }
752 location += step;
753 }
754}
755
756} // namespace emu
757} // namespace yaze
uint16_t echoBufferIndex
Definition dsp.h:151
uint32_t lastFrameBoundary
Definition dsp.h:156
void CycleChannel(int ch)
Definition dsp.cc:216
void HandleGain(int ch)
Definition dsp.cc:305
void GetSamples(int16_t *sample_data, int samples_per_frame, bool pal_timing)
Definition dsp.cc:667
int16_t sampleOutR
Definition dsp.h:137
void DecodeBrr(int ch)
Definition dsp.cc:382
int16_t echoOutR
Definition dsp.h:139
int8_t feedbackVolume
Definition dsp.h:147
int16_t sampleBuffer[0x400 *2]
Definition dsp.h:118
uint8_t Read(uint8_t adr)
Definition dsp.cc:431
std::vector< uint8_t > & aram_
Definition dsp.h:121
uint8_t noiseRate
Definition dsp.h:142
bool CheckCounter(int rate)
Definition dsp.cc:163
uint16_t counter
Definition dsp.h:128
uint16_t sampleOffset
Definition dsp.h:119
uint16_t echoBufferAdr
Definition dsp.h:148
void Cycle()
Definition dsp.cc:134
int8_t masterVolumeL
Definition dsp.h:133
uint16_t echoDelay
Definition dsp.h:149
void HandleEcho()
Definition dsp.cc:168
int8_t firValues[8]
Definition dsp.h:153
int16_t sampleOutL
Definition dsp.h:136
void Reset()
Definition dsp.cc:68
void Write(uint8_t adr, uint8_t val)
Definition dsp.cc:433
int8_t masterVolumeR
Definition dsp.h:134
bool evenCycle
Definition dsp.h:130
InterpolationType interpolation_type
Definition dsp.h:114
uint8_t firBufferIndex
Definition dsp.h:152
void NewFrame()
Definition dsp.cc:130
int16_t firBufferR[8]
Definition dsp.h:155
int16_t noiseSample
Definition dsp.h:141
uint16_t dirPage
Definition dsp.h:129
int16_t echoOutL
Definition dsp.h:138
int16_t firBufferL[8]
Definition dsp.h:154
uint16_t echoLength
Definition dsp.h:150
int8_t echoVolumeR
Definition dsp.h:146
bool echoWrites
Definition dsp.h:144
int8_t echoVolumeL
Definition dsp.h:145
bool mute
Definition dsp.h:131
uint8_t ram[0x80]
Definition dsp.h:124
void HandleNoise()
Definition dsp.cc:424
int16_t GetSample(int ch)
Definition dsp.cc:368
DspChannel channel[8]
Definition dsp.h:126
bool reset
Definition dsp.h:132
int16_t InterpolateCubic(int16_t p0, int16_t p1, int16_t p2, int16_t p3, double t)
Definition dsp.cc:622
int16_t InterpolateLinear(int16_t s0, int16_t s1, double frac)
Definition dsp.cc:647
int16_t InterpolateHermite(int16_t p0, int16_t p1, int16_t p2, int16_t p3, double t)
Definition dsp.cc:653
int16_t InterpolateCosine(int16_t s0, int16_t s1, double mu)
Definition dsp.cc:641
Main namespace for the application.
uint16_t pitch
Definition dsp.h:19
uint8_t startDelay
Definition dsp.h:30
uint8_t adsrState
Definition dsp.h:33
uint16_t pitchCounter
Definition dsp.h:20
uint16_t gain
Definition dsp.h:41
int16_t sampleOut
Definition dsp.h:46
uint16_t decodeOffset
Definition dsp.h:26
uint8_t bufferOffset
Definition dsp.h:24
uint8_t sustainLevel
Definition dsp.h:34
int16_t decodeBuffer[12]
Definition dsp.h:23
uint8_t blockOffset
Definition dsp.h:27
uint8_t brrHeader
Definition dsp.h:28
bool pitchModulation
Definition dsp.h:21
uint16_t gainValue
Definition dsp.h:39
uint8_t adsrRates[4]
Definition dsp.h:32
uint8_t gainMode
Definition dsp.h:37
uint16_t preclampGain
Definition dsp.h:40
uint8_t gainSustainLevel
Definition dsp.h:35