From 56b786f55e0b7fd17fed2e81729c199a4df89f80 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 02:32:01 -0500 Subject: [PATCH 01/29] asd;klfj --- src/engine/platform/sound/c64_d/LICENSE | 19 ++ src/engine/platform/sound/c64_d/README.md | 11 + src/engine/platform/sound/c64_d/dsid.c | 346 ++++++++++++++++++++++ src/engine/platform/sound/c64_d/dsid.h | 112 +++++++ 4 files changed, 488 insertions(+) create mode 100644 src/engine/platform/sound/c64_d/LICENSE create mode 100644 src/engine/platform/sound/c64_d/README.md create mode 100644 src/engine/platform/sound/c64_d/dsid.c create mode 100644 src/engine/platform/sound/c64_d/dsid.h diff --git a/src/engine/platform/sound/c64_d/LICENSE b/src/engine/platform/sound/c64_d/LICENSE new file mode 100644 index 000000000..2127f8a3b --- /dev/null +++ b/src/engine/platform/sound/c64_d/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2021 DefleMask Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/engine/platform/sound/c64_d/README.md b/src/engine/platform/sound/c64_d/README.md new file mode 100644 index 000000000..63b7fa533 --- /dev/null +++ b/src/engine/platform/sound/c64_d/README.md @@ -0,0 +1,11 @@ +dSID +=== + +This is the SID core used in DefleMask. + +The project started as a very careful port from jsSID, comparing the wave +output from both, ensuring they were exactly the same. + +## License + +MIT License diff --git a/src/engine/platform/sound/c64_d/dsid.c b/src/engine/platform/sound/c64_d/dsid.c new file mode 100644 index 000000000..884d8669c --- /dev/null +++ b/src/engine/platform/sound/c64_d/dsid.c @@ -0,0 +1,346 @@ +#include "dsid.h" +#include // INFINITY +#include +#include // memset, memcpy + +// defle internals +void waveforms_add_sample(uint8_t id, int16_t left, int16_t right); + +struct SID_chip sid; +static char init_wf = 1; + +const int Aexp[256] = { + 1, 30, 30, 30, 30, 30, 30, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + +double cmbWF(int chn, int *wfa, int index, int differ6581, struct SID_globals *g) { + if (differ6581 && sid.g.model == 6581) + index &= 0x7FF; + + return wfa[index]; +} + +void cCmbWF(int *wfa, double bitmul, double bstr, double trh) { + for (int i = 0; i < 4096; i++) { + wfa[i] = 0; + for (int j = 0; j < 12; j++) { + double blvl = 0; + for (int k = 0; k < 12; k++) { + blvl += (bitmul / pow(bstr, abs(k - j))) * (((i >> k) & 1) - 0.5); + } + wfa[i] += (blvl >= trh) ? pow(2, j) : 0; + } + wfa[i] *= 12; + } +} + +void dSID_init(double samplingRate, int model) { + if (model == 6581) { + sid.g.model = 6581; + } else { + sid.g.model = 8580; + } + + // SID area + for (int i = 0xD400; i <= 0xD7FF; i++) + sid.M[i] = 0; + // IO area + for (int i = 0xDE00; i <= 0xDFFF; i++) + sid.M[i] = 0; + + memset(sid.SIDct, 0, sizeof(sid.SIDct)); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + sid.SIDct[i].ch[j].Ast = _HZ; + sid.SIDct[i].ch[j].nLFSR = 0x7FFFF8; + sid.SIDct[i].ch[j].prevwfout = 0; + } + sid.SIDct[i].ch[0].FSW = 1; + sid.SIDct[i].ch[1].FSW = 2; + sid.SIDct[i].ch[2].FSW = 4; + } + + sid.g.ctfr = -2.0 * 3.14 * (12500.0 / 256.0) / samplingRate, + sid.g.ctf_ratio_6581 = -2.0 * 3.14 * (20000.0 / 256.0) / samplingRate; + sid.g.ckr = (double) SID_CLK / samplingRate; + + const double bAprd[16] = {9, 32 * 1, 63 * 1, 95 * 1, 149 * 1, 220 * 1, + 267 * 1, 313 * 1, 392 * 1, 977 * 1, 1954 * 1, 3126 * 1, + 3907 * 1, 11720 * 1, 19532 * 1, 31251 * 1}; + const int bAstp[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + memcpy(&sid.g.Aprd, &bAprd, sizeof(bAprd)); + memcpy(&sid.g.Astp, &bAstp, sizeof(bAstp)); + if (init_wf) { + cCmbWF(sid.g.trsaw, 0.8, 2.4, 0.64); + cCmbWF(sid.g.pusaw, 1.4, 1.9, 0.68); + cCmbWF(sid.g.Pulsetrsaw, 0.8, 2.5, 0.64); + init_wf = 0; + } + + double prd0 = sid.g.ckr > 9 ? sid.g.ckr : 9; + sid.g.Aprd[0] = prd0; + sid.g.Astp[0] = ceil(prd0 / 9); +} + +double dSID_render() { + double flin = 0, output = 0; + double wfout = 0; + for (int chn = 0; chn < SID_CHA; chn++) { + struct SIDVOICE *voic = &((struct SIDMEM *) (sid.M))->v[chn]; + double pgt = (sid.SIDct->ch[chn].Ast & GAT); + uint8_t ctrl = voic->control; + uint8_t wf = ctrl & 0xF0; + uint8_t test = ctrl & TST; + uint8_t SR = voic->susres; + double tmp = 0; + if (pgt != (ctrl & GAT)) { + if (pgt) { + sid.SIDct->ch[chn].Ast &= 0xFF - (GAT | ATK | DECSUS); + } else { + sid.SIDct->ch[chn].Ast = (GAT | ATK | DECSUS); + if ((SR & 0xF) > (sid.SIDct->ch[chn].pSR & 0xF)) + tmp = 1; + } + } + sid.SIDct->ch[chn].pSR = SR; + sid.SIDct->ch[chn].rcnt += sid.g.ckr; + if (sid.SIDct->ch[chn].rcnt >= 0x8000) + sid.SIDct->ch[chn].rcnt -= 0x8000; + + static double step; + double prd; + + if (sid.SIDct->ch[chn].Ast & ATK) { + step = voic->attack; + prd = sid.g.Aprd[(int) step]; + } else if (sid.SIDct->ch[chn].Ast & DECSUS) { + step = voic->decay; + prd = sid.g.Aprd[(int) step]; + } else { + step = SR & 0xF; + prd = sid.g.Aprd[(int) step]; + } + step = sid.g.Astp[(int) step]; + if (sid.SIDct->ch[chn].rcnt >= prd && sid.SIDct->ch[chn].rcnt < prd + sid.g.ckr && + tmp == 0) { + sid.SIDct->ch[chn].rcnt -= prd; + if ((sid.SIDct->ch[chn].Ast & ATK) || + ++sid.SIDct->ch[chn].expcnt == Aexp[(int) sid.SIDct->ch[chn].envcnt]) { + if (!(sid.SIDct->ch[chn].Ast & _HZ)) { + if (sid.SIDct->ch[chn].Ast & ATK) { + sid.SIDct->ch[chn].envcnt += step; + if (sid.SIDct->ch[chn].envcnt >= 0xFF) { + sid.SIDct->ch[chn].envcnt = 0xFF; + sid.SIDct->ch[chn].Ast &= 0xFF - ATK; + } + } else if (!(sid.SIDct->ch[chn].Ast & DECSUS) || + sid.SIDct->ch[chn].envcnt > (SR >> 4) + (SR & 0xF0)) { + sid.SIDct->ch[chn].envcnt -= step; + if (sid.SIDct->ch[chn].envcnt <= 0 && + sid.SIDct->ch[chn].envcnt + step != 0) { + sid.SIDct->ch[chn].envcnt = 0; + sid.SIDct->ch[chn].Ast |= _HZ; + } + } + } + sid.SIDct->ch[chn].expcnt = 0; + } else { + } + } + sid.SIDct->ch[chn].envcnt = (int) sid.SIDct->ch[chn].envcnt & 0xFF; + double aAdd = (voic->freq_low + voic->freq_high * 256) * sid.g.ckr; + if (test || ((ctrl & SYN) && sid.SIDct->sMSBrise)) { + sid.SIDct->ch[chn].pacc = 0; + } else { + sid.SIDct->ch[chn].pacc += aAdd; + if (sid.SIDct->ch[chn].pacc > 0xFFFFFF) + sid.SIDct->ch[chn].pacc -= 0x1000000; + } + double MSB = (int) sid.SIDct->ch[chn].pacc & 0x800000; + sid.SIDct->sMSBrise = (MSB > ((int) sid.SIDct->ch[chn].pracc & 0x800000)) ? 1 : 0; + + if (wf & NOI) { + tmp = sid.SIDct->ch[chn].nLFSR; + if ((((int) sid.SIDct->ch[chn].pacc & 0x100000) != + ((int) sid.SIDct->ch[chn].pracc & 0x100000)) || + aAdd >= 0x100000) { + step = ((int) tmp & 0x400000) ^ (((int) tmp & 0x20000) << 5); + tmp = (((int) tmp << 1) + (step > 0 || test)) & 0x7FFFFF; + sid.SIDct->ch[chn].nLFSR = tmp; + } + wfout = (wf & 0x70) ? 0 + : (((int) tmp & 0x100000) >> 5) + (((int) tmp & 0x40000) >> 4) + + (((int) tmp & 0x4000) >> 1) + (((int) tmp & 0x800) << 1) + + (((int) tmp & 0x200) << 2) + (((int) tmp & 0x20) << 5) + + (((int) tmp & 0x04) << 7) + (((int) tmp & 0x01) << 8); + } else if (wf & PUL) { + double pw = (voic->pw_low + (voic->pw_high) * 256) * 16; + tmp = (int) aAdd >> 9; + if (0 < pw && pw < tmp) + pw = tmp; + tmp = (int) tmp ^ 0xFFFF; + if (pw > tmp) + pw = tmp; + tmp = (int) sid.SIDct->ch[chn].pacc >> 8; + if (wf == PUL) { + int lel = ((int) aAdd >> 16); + if (lel > 0) { + step = 256.0 / (double) lel; + } else { + step = INFINITY; + } + if (test) + wfout = 0xFFFF; + else if (tmp < pw) { + double lim = (0xFFFF - pw) * step; + if (lim > 0xFFFF) + lim = 0xFFFF; + wfout = lim - (pw - tmp) * step; + if (wfout < 0) + wfout = 0; + } else { + double lim = pw * step; + if (lim > 0xFFFF) + lim = 0xFFFF; + wfout = (0xFFFF - tmp) * step - lim; + if (wfout >= 0) + wfout = 0xFFFF; + wfout = (int) wfout & 0xFFFF; + } + } else { + wfout = (tmp >= pw || test) ? 0xFFFF : 0; + if (wf & TRI) { + if (wf & SAW) { + wfout = + (wfout) ? cmbWF(chn, sid.g.Pulsetrsaw, (int) tmp >> 4, 1, &sid.g) : 0; + } else { + tmp = (int) sid.SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid.SIDct->sMSB : 0); + wfout = + (wfout) + ? cmbWF(chn, sid.g.pusaw, + ((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 11, + 0, &sid.g) + : 0; + } + } else if (wf & SAW) + wfout = (wfout) ? cmbWF(chn, sid.g.pusaw, (int) tmp >> 4, 1, &sid.g) : 0; + } + } else if (wf & SAW) { + wfout = (int) sid.SIDct->ch[chn].pacc >> 8; + if (wf & TRI) + wfout = cmbWF(chn, sid.g.trsaw, (int) wfout >> 4, 1, &sid.g); + else { + step = aAdd / 0x1200000; + wfout += wfout * step; + if (wfout > 0xFFFF) + wfout = 0xFFFF - (wfout - 0x10000) / step; + } + } else if (wf & TRI) { + tmp = (int) sid.SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid.SIDct->sMSB : 0); + wfout = ((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 7; + } + if (wf) + sid.SIDct->ch[chn].prevwfout = wfout; + else { + wfout = sid.SIDct->ch[chn].prevwfout; + } + sid.SIDct->ch[chn].pracc = sid.SIDct->ch[chn].pacc; + sid.SIDct->sMSB = MSB; + // double preflin = flin; + if ((sid.mute_mask & (1 << chn))) { + if (sid.M[0x17] & sid.SIDct->ch[chn].FSW) { + double chnout = (wfout - 0x8000) * (sid.SIDct->ch[chn].envcnt / 256); + flin += chnout; + + // defle fake filter for solo waveform ahead + // mostly copypasted from below + double fakeflin = chnout; + double fakeflout = 0; + static double fakeplp[SID_CHA] = {0}; + static double fakepbp[SID_CHA] = {0}; + double ctf = ((double) (sid.M[0x15] & 7)) / 8 + sid.M[0x16] + 0.2; + double reso; + if (sid.g.model == 8580) { + ctf = 1 - exp(ctf * sid.g.ctfr); + reso = pow(2, ((double) (4 - (double) (sid.M[0x17] >> 4)) / 8)); + } else { + if (ctf < 24) + ctf = 0.035; + else + ctf = 1 - 1.263 * exp(ctf * sid.g.ctf_ratio_6581); + reso = (sid.M[0x17] > 0x5F) ? 8.0 / (double) (sid.M[0x17] >> 4) : 1.41; + } + double tmp = fakeflin + fakepbp[chn] * reso + fakeplp[chn]; + if (sid.M[0x18] & HP) + fakeflout -= tmp; + tmp = fakepbp[chn] - tmp * ctf; + fakepbp[chn] = tmp; + if (sid.M[0x18] & BP) + fakeflout -= tmp; + tmp = fakeplp[chn] + tmp * ctf; + fakeplp[chn] = tmp; + if (sid.M[0x18] & LP) + fakeflout += tmp; + + double defle_wf_out = (fakeflout / SID_OUT_SCALE) * (sid.M[0x18] & 0xF) * 65535; + waveforms_add_sample(1 + chn, defle_wf_out, defle_wf_out); + } else if ((chn % SID_CHA) != 2 || !(sid.M[0x18] & OFF3)) { + double chnout = (wfout - 0x8000) * (sid.SIDct->ch[chn].envcnt / 256); + output += chnout; + + double defle_wf_out = (chnout / SID_OUT_SCALE) * (sid.M[0x18] & 0xF) * 65535; + waveforms_add_sample(1 + chn, defle_wf_out, defle_wf_out); + } + } else { + waveforms_add_sample(1 + chn, 0, 0); + } + } + int M1 = 0; + if (M1 & 3) + sid.M[0x1B] = (int) wfout >> 8; + sid.M[0x1C] = sid.SIDct->ch[2].envcnt; + + double ctf = ((double) (sid.M[0x15] & 7)) / 8 + sid.M[0x16] + 0.2; + double reso; + if (sid.g.model == 8580) { + ctf = 1 - exp(ctf * sid.g.ctfr); + reso = pow(2, ((double) (4 - (double) (sid.M[0x17] >> 4)) / 8)); + } else { + if (ctf < 24) + ctf = 0.035; + else + ctf = 1 - 1.263 * exp(ctf * sid.g.ctf_ratio_6581); + reso = (sid.M[0x17] > 0x5F) ? 8.0 / (double) (sid.M[0x17] >> 4) : 1.41; + } + + double tmp = flin + sid.SIDct->pbp * reso + sid.SIDct->plp; + if (sid.M[0x18] & HP) + output -= tmp; + tmp = sid.SIDct->pbp - tmp * ctf; + sid.SIDct->pbp = tmp; + if (sid.M[0x18] & BP) + output -= tmp; + tmp = sid.SIDct->plp + tmp * ctf; + sid.SIDct->plp = tmp; + if (sid.M[0x18] & LP) + output += tmp; + return (output / SID_OUT_SCALE) * (sid.M[0x18] & 0xF); +} + +void dSID_setMuteMask(int mute_mask) { + sid.mute_mask = mute_mask; +} + +float dSID_getVolume(int channel) { + if ((sid.M[0x18] & 0xF) == 0) + return 0; + return sid.SIDct[0].ch[channel].envcnt / 256.0f; +} diff --git a/src/engine/platform/sound/c64_d/dsid.h b/src/engine/platform/sound/c64_d/dsid.h new file mode 100644 index 000000000..31d524f18 --- /dev/null +++ b/src/engine/platform/sound/c64_d/dsid.h @@ -0,0 +1,112 @@ +#pragma once + +#include + +#define SID_CLK 985248 +#define SID_CHA 3 +#define SID_OUT_SCALE (0x10000 * SID_CHA * 16) + +// TODO: prefix SID_ + +// CONTROL +#define GAT 0x01 +#define SYN 0x02 +#define RNG 0x04 +#define TST 0x08 +#define TRI 0x10 +#define SAW 0x20 +#define PUL 0x40 +#define NOI 0x80 + +#define _HZ 0x10 +#define DECSUS 0x40 +#define ATK 0x80 + +// TODO: change +// filter mode (high) +#define LP 0x10 +#define BP 0x20 +#define HP 0x40 +#define OFF3 0x80 + +extern const int Aexp[256]; + +struct SID_ctx_chan { + double rcnt; + double envcnt; + double expcnt; + double pacc; + double pracc; + int FSW; + int nLFSR; + double prevwfout; + uint8_t pSR; + int Ast; +}; + +struct SID_ctx { + int sMSBrise; + int sMSB; + double plp; + double pbp; + struct SID_ctx_chan ch[3]; +}; + +struct SIDVOICE { + uint8_t freq_low; + uint8_t freq_high; + uint8_t pw_low; + uint8_t pw_high : 4; + uint8_t UNUSED : 4; + uint8_t control; + uint8_t decay : 4; + uint8_t attack : 4; + uint8_t susres; + // uint8_t release : 4; + // uint8_t sustain : 4; +}; + +struct SIDMEM { + struct SIDVOICE v[SID_CHA]; + uint8_t UNUSED : 4; + uint8_t cutoff_low : 4; + uint8_t cutoff_high; + uint8_t reso_rt : 4; + uint8_t reso : 4; + uint8_t volume : 4; + uint8_t filter_mode : 4; + uint8_t paddlex; + uint8_t paddley; + uint8_t osc3; + uint8_t env3; +}; + +struct SID_globals { + double ckr; + double ctfr; + double ctf_ratio_6581; + + int trsaw[4096]; + int pusaw[4096]; + int Pulsetrsaw[4096]; + + double Aprd[16]; + int Astp[16]; + int model; +}; + +#define MemLen 65536 + +struct SID_chip { + struct SID_globals g; + struct SID_ctx SIDct[3]; + uint8_t M[MemLen]; + int mute_mask; +}; + +extern struct SID_chip sid; + +double dSID_render(); +void dSID_init(double samplingRate, int model); +float dSID_getVolume(int channel); +void dSID_setMuteMask(int mute_mask); From fba48149a5bbdc0e37ba71cb6ccc22b003ac1254 Mon Sep 17 00:00:00 2001 From: MooingLemur Date: Wed, 5 Jul 2023 15:07:44 -0700 Subject: [PATCH 02/29] VERA, ZSM Export: Add EFxx event as synchronization message, add sync message support in ZSM export --- doc/7-systems/vera.md | 2 ++ papers/zsm-format.md | 13 +++++++-- src/engine/dispatch.h | 2 ++ src/engine/platform/vera.cpp | 5 ++++ src/engine/platform/vera.h | 2 +- src/engine/playback.cpp | 2 ++ src/engine/sysDef.cpp | 1 + src/engine/zsm.cpp | 54 +++++++++++++++++++++++------------- src/engine/zsm.h | 8 ++++++ src/engine/zsmOps.cpp | 2 +- 10 files changed, 67 insertions(+), 24 deletions(-) diff --git a/doc/7-systems/vera.md b/doc/7-systems/vera.md index 8f50e0e74..4497887b8 100644 --- a/doc/7-systems/vera.md +++ b/doc/7-systems/vera.md @@ -13,3 +13,5 @@ currently Furnace does not support the PCM channel's stereo mode, though (except - `2`: triangle - `3`: noise - `22xx`: **set duty cycle.** range is `0` to `3F`. +- `EFxx`: **ZSM synchronization event.** + - Where `xx` is the event payload. This has no effect in how the music is played in Furnace, but the ZSMKit library for the Commander X16 interprets these events inside ZSM files and optionally triggers a callback routine. This can be used, for instance, to cause game code to respond to beats or at certain points in the music. diff --git a/papers/zsm-format.md b/papers/zsm-format.md index 5957e3c69..e0fd2c02d 100644 --- a/papers/zsm-format.md +++ b/papers/zsm-format.md @@ -99,7 +99,7 @@ Any offset values contained in the PCM data header block are relative to the beg ### PCM Sample Data -This is blob of PCM data with no internal formatting. Offsets into this blob are provided via the PCM header. The end of this blob will be the end of the ZSM file. +This is a blob of PCM data with no internal formatting. Offsets into this blob are provided via the PCM header. The end of this blob will be the end of the ZSM file. ## EXTCMD Channel Scifications @@ -149,7 +149,7 @@ Players implementing this channel should implement detection routines during ini An expansion HW write will contain the following data: -Chip ID|Nuber of writes (`N`)| `N` tuples of data +Chip ID|Number of writes (`N`)| `N` tuples of data --|--|-- one byte|one byte|N * tuple_size bytes @@ -162,7 +162,14 @@ There are currently no supported expansion HW IDs assigned. The purpose of this channel is to provide for music synchronization cues that applications may use to perform operations in sync with the music (such as when the Goombas jump in New Super Mario Bros in time with the BOP! BOP! notes in the music). It is intended for the reference player to provide a sync channel callback, passing the data bytes to the callback function, and then to proceed with playback. -The data structure within this channel is not yet defined. It is our intention to work with the community in order to collaborate on a useful structure. +The synchronization format currently defines this one event type: + +Event Type|Description|Message Format +--|--|-- +`0x00`|Generic sync message|`xx` (any value from `0x00`-`0xff`) + +An example of an EXTCMD containing one sync event might look as follows: `0x40 0x82 0x00 0x05` + #### 3: Custom diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c19e7d955..9a7a067d3 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -236,6 +236,8 @@ enum DivDispatchCmds { DIV_CMD_NES_LINEAR_LENGTH, + DIV_CMD_SYNC_MESSAGE, // (value) + DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol DIV_CMD_MAX diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 3e3ac42c2..8c1bf9480 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -37,6 +37,7 @@ extern "C" { #define rWritePCMRate(d) {regPool[65]=(d); pcm_write_rate(pcm,d);if (dumpWrites) addWrite(65,(d));} #define rWritePCMData(d) {regPool[66]=(d); pcm_write_fifo(pcm,d);} #define rWritePCMVol(d) rWritePCMCtrl((regPool[64]&(~0x8f))|((d)&15)) +#define rWriteZSMSync(d) {if (dumpWrites) addWrite(68,(d));} const char* regCheatSheetVERA[]={ "CHxFreq", "00+x*4", @@ -46,6 +47,7 @@ const char* regCheatSheetVERA[]={ "AUDIO_CTRL", "40", "AUDIO_RATE", "41", "AUDIO_DATA", "42", + "ZSM_SYNC", "44", NULL }; @@ -414,6 +416,9 @@ int DivPlatformVERA::dispatch(DivCommand c) { case DIV_CMD_MACRO_ON: chan[c.chan].std.mask(c.value,false); break; + case DIV_CMD_SYNC_MESSAGE: + rWriteZSMSync(c.value); + break; case DIV_ALWAYS_SET_VOLUME: return 0; break; diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 7476a316a..227512a73 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -51,7 +51,7 @@ class DivPlatformVERA: public DivDispatch { Channel chan[17]; DivDispatchOscBuffer* oscBuf[17]; bool isMuted[17]; - unsigned char regPool[67]; + unsigned char regPool[69]; struct VERA_PSG* psg; struct VERA_PCM* pcm; diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index ce94b883b..c70844a10 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -236,6 +236,8 @@ const char* cmdName[]={ "NES_LINEAR_LENGTH", + "SYNC_MESSAGE", + "ALWAYS_SET_VOLUME" }; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index f4d76cebc..6b94a66f0 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1460,6 +1460,7 @@ void DivEngine::registerSystems() { { {0x20, {DIV_CMD_WAVE, "20xx: Set waveform"}}, {0x22, {DIV_CMD_STD_NOISE_MODE, "22xx: Set duty cycle (0 to 3F)"}}, + {0xEF, {DIV_CMD_SYNC_MESSAGE, "EFxx: ZSM sync event"}}, } ); diff --git a/src/engine/zsm.cpp b/src/engine/zsm.cpp index 912dc630d..b0f421114 100644 --- a/src/engine/zsm.cpp +++ b/src/engine/zsm.cpp @@ -118,9 +118,13 @@ void DivZSM::writePSG(unsigned char a, unsigned char v) { // TODO: suppress writes to PSG voice that is not audible (volume=0) // ^ Let's leave these alone, ZSMKit has a feature that can benefit // from silent channels. - if (a>=67) { - logD ("ZSM: ignoring VERA PSG write a=%02x v=%02x",a,v); + if (a>=69) { + logD("ZSM: ignoring VERA PSG write a=%02x v=%02x",a,v); return; + } else if (a==68) { + // Sync event + numWrites++; + return syncCache.push_back(v); } else if (a>=64) { return writePCM(a-64,v); } @@ -259,7 +263,7 @@ SafeWriter* DivZSM::finish() { } void DivZSM::flushWrites() { - logD("ZSM: flushWrites.... numwrites=%d ticks=%d ymwrites=%d pcmMeta=%d pcmCache=%d pcmData=%d",numWrites,ticks,ymwrites.size(),pcmMeta.size(),pcmCache.size(),pcmData.size()); + logD("ZSM: flushWrites.... numwrites=%d ticks=%d ymwrites=%d pcmMeta=%d pcmCache=%d pcmData=%d syncCache=%d",numWrites,ticks,ymwrites.size(),pcmMeta.size(),pcmCache.size(),pcmData.size(),syncCache.size()); if (numWrites==0) return; flushTicks(); // only flush ticks if there are writes pending. for (unsigned char i=0; i<64; i++) { @@ -287,43 +291,43 @@ void DivZSM::flushWrites() { unsigned int pcmInst=0; int pcmOff=0; int pcmLen=0; - int extCmdLen=pcmMeta.size()*2; + int extCmd0Len=pcmMeta.size()*2; if (pcmCache.size()) { // collapse stereo data to mono if both channels are fully identical // which cuts PCM data size in half for center-panned PCM events - if (pcmCtrlDCCache & 0x10) { // stereo bit is on + if (pcmCtrlDCCache&0x10) { // stereo bit is on unsigned int e; - if (pcmCtrlDCCache & 0x20) { // 16-bit + if (pcmCtrlDCCache&0x20) { // 16-bit // for 16-bit PCM data, the size must be a multiple of 4 if (pcmCache.size()%4==0) { // check for identical L+R channels - for (e=0;e>1;e+=2) { + for (e=0; e>1; e+=2) { pcmCache[e]=pcmCache[e<<1]; pcmCache[e+1]=pcmCache[(e<<1)+1]; } pcmCache.resize(pcmCache.size()>>1); - pcmCtrlDCCache &= ~0x10; // clear stereo bit + pcmCtrlDCCache&=(unsigned char)~0x10; // clear stereo bit } } } else { // 8-bit // for 8-bit PCM data, the size must be a multiple of 2 if (pcmCache.size()%2==0) { // check for identical L+R channels - for (e=0;e>1;e++) { + for (e=0; e>1; e++) { pcmCache[e]=pcmCache[e<<1]; } pcmCache.resize(pcmCache.size()>>1); - pcmCtrlDCCache &= ~0x10; // clear stereo bit + pcmCtrlDCCache&=(unsigned char)~0x10; // clear stereo bit } } } @@ -340,7 +344,7 @@ void DivZSM::flushWrites() { pcmData.insert(pcmData.end(),pcmCache.begin(),pcmCache.end()); } pcmCache.clear(); - extCmdLen+=2; + extCmd0Len+=2; // search for a matching PCM instrument definition for (S_pcmInst& inst: pcmInsts) { if (inst.offset==pcmOff && inst.length==pcmLen && inst.geometry==pcmCtrlDCCache) @@ -355,14 +359,14 @@ void DivZSM::flushWrites() { pcmInsts.push_back(inst); } } - if (extCmdLen>63) { // this would be bad, but will almost certainly never happen - logE("ZSM: extCmd exceeded maximum length of 63: %d",extCmdLen); - extCmdLen=0; + if (extCmd0Len>63) { // this would be bad, but will almost certainly never happen + logE("ZSM: extCmd 0 exceeded maximum length of 63: %d",extCmd0Len); + extCmd0Len=0; pcmMeta.clear(); } - if (extCmdLen) { // we have some PCM events to write - w->writeC(0x40); - w->writeC((unsigned char)extCmdLen); // the high two bits are guaranteed to be zero, meaning this is a PCM command + if (extCmd0Len) { // we have some PCM events to write + w->writeC(ZSM_EXT); + w->writeC(ZSM_EXT_PCM|(unsigned char)extCmd0Len); for (DivRegWrite& write: pcmMeta) { w->writeC(write.addr); w->writeC(write.val); @@ -373,6 +377,18 @@ void DivZSM::flushWrites() { w->writeC((unsigned char)pcmInst&0xff); } } + n=0; + while (n<(long)syncCache.size()) { // we have one or more sync events to write + int writes=syncCache.size()-n; + w->writeC(ZSM_EXT); + if (writes>ZSM_SYNC_MAX_WRITES) writes=ZSM_SYNC_MAX_WRITES; + w->writeC(ZSM_EXT_SYNC|(writes<<1)); + for (; writes>0; writes--) { + w->writeC(0x00); // 0x00 = Arbitrary sync message + w->writeC(syncCache[n++]); + } + } + syncCache.clear(); numWrites=0; } diff --git a/src/engine/zsm.h b/src/engine/zsm.h index 17a1fd063..42300cf70 100644 --- a/src/engine/zsm.h +++ b/src/engine/zsm.h @@ -30,9 +30,16 @@ #define ZSM_YM_CMD 0x40 #define ZSM_DELAY_CMD 0x80 #define ZSM_YM_MAX_WRITES 63 +#define ZSM_SYNC_MAX_WRITES 31 #define ZSM_DELAY_MAX 127 #define ZSM_EOF ZSM_DELAY_CMD +#define ZSM_EXT ZSM_YM_CMD +#define ZSM_EXT_PCM 0x00 +#define ZSM_EXT_CHIP 0x40 +#define ZSM_EXT_SYNC 0x80 +#define ZSM_EXT_CUSTOM 0xC0 + enum YM_STATE { ym_PREV, ym_NEW, ym_STATES }; enum PSG_STATE { psg_PREV, psg_NEW, psg_STATES }; @@ -52,6 +59,7 @@ class DivZSM { std::vector pcmData; std::vector pcmCache; std::vector pcmInsts; + std::vector syncCache; int loopOffset; int numWrites; int ticks; diff --git a/src/engine/zsmOps.cpp b/src/engine/zsmOps.cpp index c59efeae2..85ddb5d47 100644 --- a/src/engine/zsmOps.cpp +++ b/src/engine/zsmOps.cpp @@ -151,7 +151,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) { for (DivRegWrite& write: writes) { if (i==YM) zsm.writeYM(write.addr&0xff,write.val); if (i==VERA) { - if (done && write.addr >= 64) continue; // don't process any PCM events on the loop lookahead + if (done && write.addr>=64) continue; // don't process any PCM or sync events on the loop lookahead zsm.writePSG(write.addr&0xff,write.val); } } From 621616ae25d5465be7090ced746933e145586b33 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 17:09:02 -0500 Subject: [PATCH 03/29] C64: dSID core, part 1 --- CMakeLists.txt | 2 + src/engine/dispatchContainer.cpp | 4 +- src/engine/platform/c64.cpp | 58 +++-- src/engine/platform/c64.h | 8 +- src/engine/platform/sound/c64_d/dsid.c | 294 +++++++++++++------------ src/engine/platform/sound/c64_d/dsid.h | 55 ++--- src/gui/debug.cpp | 1 - src/gui/gui.h | 2 +- src/gui/settings.cpp | 11 +- 9 files changed, 235 insertions(+), 200 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5e91fb00..2051eb8d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -455,6 +455,8 @@ src/engine/platform/sound/c64_fp/WaveformCalculator.cpp src/engine/platform/sound/c64_fp/WaveformGenerator.cpp src/engine/platform/sound/c64_fp/resample/SincResampler.cpp +src/engine/platform/sound/c64_d/dsid.c + src/engine/platform/sound/tia/AudioChannel.cpp src/engine/platform/sound/tia/Audio.cpp diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index c30d7f231..3ff58e30e 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -273,12 +273,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do break; case DIV_SYSTEM_C64_6581: dispatch=new DivPlatformC64; - ((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1); + ((DivPlatformC64*)dispatch)->setCore(eng->getConfInt("c64Core",0)); ((DivPlatformC64*)dispatch)->setChipModel(true); break; case DIV_SYSTEM_C64_8580: dispatch=new DivPlatformC64; - ((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1); + ((DivPlatformC64*)dispatch)->setCore(eng->getConfInt("c64Core",0)); ((DivPlatformC64*)dispatch)->setChipModel(false); break; case DIV_SYSTEM_YM2151: diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 6d03ba2f4..469890bca 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -65,19 +65,29 @@ const char** DivPlatformC64::getRegisterSheet() { } void DivPlatformC64::acquire(short** buf, size_t len) { - int dcOff=isFP?0:sid.get_dc(0); + int dcOff=(sidCore)?0:sid.get_dc(0); for (size_t i=0; i=4) { + writeOscBuf=0; + oscBuf[0]->data[oscBuf[0]->needle++]=sid_d->lastOut[0]; + oscBuf[1]->data[oscBuf[1]->needle++]=sid_d->lastOut[1]; + oscBuf[2]->data[oscBuf[2]->needle++]=sid_d->lastOut[2]; + } + } else if (sidCore==1) { sid_fp.clock(4,&buf[0][i]); if (++writeOscBuf>=4) { writeOscBuf=0; @@ -452,7 +462,14 @@ int DivPlatformC64::dispatch(DivCommand c) { void DivPlatformC64::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - if (isFP) { + if (sidCore==2) { + dSID_setMuteMask( + sid_d, + (isMuted[0]?1:0)| + (isMuted[1]?2:0)| + (isMuted[2]?4:0) + ); + } else if (sidCore==1) { sid_fp.mute(ch,mute); } else { sid.set_is_muted(ch,mute); @@ -514,7 +531,7 @@ bool DivPlatformC64::getWantPreNote() { } float DivPlatformC64::getPostAmp() { - return isFP?3.0f:1.0f; + return (sidCore==1)?3.0f:1.0f; } void DivPlatformC64::reset() { @@ -524,7 +541,9 @@ void DivPlatformC64::reset() { chan[i].std.setEngine(parent); } - if (isFP) { + if (sidCore==2) { + dSID_init(sid_d,chipClock,rate,sidIs6581?6581:8580,1); + } else if (sidCore==1) { sid_fp.reset(); sid_fp.clockSilent(16000); } else { @@ -555,22 +574,27 @@ void DivPlatformC64::poke(std::vector& wlist) { void DivPlatformC64::setChipModel(bool is6581) { if (is6581) { - if (isFP) { + if (sidCore==2) { + // do nothing + } else if (sidCore==1) { sid_fp.setChipModel(reSIDfp::MOS6581); } else { sid.set_chip_model(MOS6581); } } else { - if (isFP) { + if (sidCore==2) { + // do nothing + } else if (sidCore==1) { sid_fp.setChipModel(reSIDfp::MOS8580); } else { sid.set_chip_model(MOS8580); } } + sidIs6581=is6581; } -void DivPlatformC64::setFP(bool fp) { - isFP=fp; +void DivPlatformC64::setCore(unsigned char which) { + sidCore=which; } void DivPlatformC64::setFlags(const DivConfig& flags) { @@ -591,9 +615,9 @@ void DivPlatformC64::setFlags(const DivConfig& flags) { for (int i=0; i<3; i++) { oscBuf[i]->rate=rate/16; } - if (isFP) { + if (sidCore>0) { rate/=4; - sid_fp.setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0); + if (sidCore==1) sid_fp.setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0); } keyPriority=flags.getBool("keyPriority",true); testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15); @@ -609,6 +633,13 @@ int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, const DivConfi isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; } + + if (sidCore==2) { + sid_d=new struct SID_chip; + } else { + sid_d=NULL; + } + setFlags(flags); reset(); @@ -620,6 +651,7 @@ void DivPlatformC64::quit() { for (int i=0; i<3; i++) { delete oscBuf[i]; } + if (sid_d!=NULL) delete sid_d; } DivPlatformC64::~DivPlatformC64() { diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index b9b30b6ec..77c236760 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -24,6 +24,7 @@ #include #include "sound/c64/sid.h" #include "sound/c64_fp/SID.h" +#include "sound/c64_d/dsid.h" class DivPlatformC64: public DivDispatch { struct Channel: public SharedChannel { @@ -64,15 +65,16 @@ class DivPlatformC64: public DivDispatch { unsigned char filtControl, filtRes, vol; unsigned char writeOscBuf; + unsigned char sidCore; int filtCut, resetTime; - bool isFP; - bool keyPriority; + bool keyPriority, sidIs6581; unsigned char chanOrder[3]; unsigned char testAD, testSR; SID sid; reSIDfp::SID sid_fp; + struct SID_chip* sid_d; unsigned char regPool[32]; friend void putDispatchChip(void*,int); @@ -105,7 +107,7 @@ class DivPlatformC64: public DivDispatch { const char** getRegisterSheet(); int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); void setChipModel(bool is6581); - void setFP(bool fp); + void setCore(unsigned char which); void quit(); ~DivPlatformC64(); }; diff --git a/src/engine/platform/sound/c64_d/dsid.c b/src/engine/platform/sound/c64_d/dsid.c index 884d8669c..72bf6596a 100644 --- a/src/engine/platform/sound/c64_d/dsid.c +++ b/src/engine/platform/sound/c64_d/dsid.c @@ -3,11 +3,30 @@ #include #include // memset, memcpy -// defle internals -void waveforms_add_sample(uint8_t id, int16_t left, int16_t right); +#define SID_OUT_SCALE (0x10000 * 3 * 16) -struct SID_chip sid; -static char init_wf = 1; +// CONTROL +#define GAT 0x01 +#define SYN 0x02 +#define RNG 0x04 +#define TST 0x08 +#define TRI 0x10 +#define SAW 0x20 +#define PUL 0x40 +#define NOI 0x80 + +#define _HZ 0x10 +#define DECSUS 0x40 +#define ATK 0x80 + +// filter mode (high) +#define LP 0x10 +#define BP 0x20 +#define HP 0x40 +#define OFF3 0x80 + +#define waveforms_add_sample(_id,_s) \ + sid->lastOut[_id]=(_s); const int Aexp[256] = { 1, 30, 30, 30, 30, 30, 30, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, @@ -22,7 +41,7 @@ const int Aexp[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; double cmbWF(int chn, int *wfa, int index, int differ6581, struct SID_globals *g) { - if (differ6581 && sid.g.model == 6581) + if (differ6581 && g->model == 6581) index &= 0x7FF; return wfa[index]; @@ -42,60 +61,53 @@ void cCmbWF(int *wfa, double bitmul, double bstr, double trh) { } } -void dSID_init(double samplingRate, int model) { +void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int model, unsigned char init_wf) { if (model == 6581) { - sid.g.model = 6581; + sid->g.model = 6581; } else { - sid.g.model = 8580; + sid->g.model = 8580; } - // SID area - for (int i = 0xD400; i <= 0xD7FF; i++) - sid.M[i] = 0; - // IO area - for (int i = 0xDE00; i <= 0xDFFF; i++) - sid.M[i] = 0; - - memset(sid.SIDct, 0, sizeof(sid.SIDct)); + memset(sid->M,0,MemLen); + memset(sid->SIDct, 0, sizeof(sid->SIDct)); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - sid.SIDct[i].ch[j].Ast = _HZ; - sid.SIDct[i].ch[j].nLFSR = 0x7FFFF8; - sid.SIDct[i].ch[j].prevwfout = 0; + sid->SIDct[i].ch[j].Ast = _HZ; + sid->SIDct[i].ch[j].nLFSR = 0x7FFFF8; + sid->SIDct[i].ch[j].prevwfout = 0; } - sid.SIDct[i].ch[0].FSW = 1; - sid.SIDct[i].ch[1].FSW = 2; - sid.SIDct[i].ch[2].FSW = 4; + sid->SIDct[i].ch[0].FSW = 1; + sid->SIDct[i].ch[1].FSW = 2; + sid->SIDct[i].ch[2].FSW = 4; } - sid.g.ctfr = -2.0 * 3.14 * (12500.0 / 256.0) / samplingRate, - sid.g.ctf_ratio_6581 = -2.0 * 3.14 * (20000.0 / 256.0) / samplingRate; - sid.g.ckr = (double) SID_CLK / samplingRate; + sid->g.ctfr = -2.0 * 3.14 * (12500.0 / 256.0) / samplingRate, + sid->g.ctf_ratio_6581 = -2.0 * 3.14 * (20000.0 / 256.0) / samplingRate; + sid->g.ckr = clockRate / samplingRate; const double bAprd[16] = {9, 32 * 1, 63 * 1, 95 * 1, 149 * 1, 220 * 1, 267 * 1, 313 * 1, 392 * 1, 977 * 1, 1954 * 1, 3126 * 1, 3907 * 1, 11720 * 1, 19532 * 1, 31251 * 1}; const int bAstp[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - memcpy(&sid.g.Aprd, &bAprd, sizeof(bAprd)); - memcpy(&sid.g.Astp, &bAstp, sizeof(bAstp)); + memcpy(&sid->g.Aprd, &bAprd, sizeof(bAprd)); + memcpy(&sid->g.Astp, &bAstp, sizeof(bAstp)); if (init_wf) { - cCmbWF(sid.g.trsaw, 0.8, 2.4, 0.64); - cCmbWF(sid.g.pusaw, 1.4, 1.9, 0.68); - cCmbWF(sid.g.Pulsetrsaw, 0.8, 2.5, 0.64); - init_wf = 0; + cCmbWF(sid->g.trsaw, 0.8, 2.4, 0.64); + cCmbWF(sid->g.pusaw, 1.4, 1.9, 0.68); + cCmbWF(sid->g.Pulsetrsaw, 0.8, 2.5, 0.64); } - double prd0 = sid.g.ckr > 9 ? sid.g.ckr : 9; - sid.g.Aprd[0] = prd0; - sid.g.Astp[0] = ceil(prd0 / 9); + double prd0 = sid->g.ckr > 9 ? sid->g.ckr : 9; + sid->g.Aprd[0] = prd0; + sid->g.Astp[0] = ceil(prd0 / 9); } -double dSID_render() { +double dSID_render(struct SID_chip* sid) { double flin = 0, output = 0; double wfout = 0; - for (int chn = 0; chn < SID_CHA; chn++) { - struct SIDVOICE *voic = &((struct SIDMEM *) (sid.M))->v[chn]; - double pgt = (sid.SIDct->ch[chn].Ast & GAT); + for (int chn = 0; chn < 3; chn++) { + struct SIDVOICE *voic = &((struct SIDMEM *) (sid->M))->v[chn]; + double pgt = (sid->SIDct->ch[chn].Ast & GAT); uint8_t ctrl = voic->control; uint8_t wf = ctrl & 0xF0; uint8_t test = ctrl & TST; @@ -103,78 +115,78 @@ double dSID_render() { double tmp = 0; if (pgt != (ctrl & GAT)) { if (pgt) { - sid.SIDct->ch[chn].Ast &= 0xFF - (GAT | ATK | DECSUS); + sid->SIDct->ch[chn].Ast &= 0xFF - (GAT | ATK | DECSUS); } else { - sid.SIDct->ch[chn].Ast = (GAT | ATK | DECSUS); - if ((SR & 0xF) > (sid.SIDct->ch[chn].pSR & 0xF)) + sid->SIDct->ch[chn].Ast = (GAT | ATK | DECSUS); + if ((SR & 0xF) > (sid->SIDct->ch[chn].pSR & 0xF)) tmp = 1; } } - sid.SIDct->ch[chn].pSR = SR; - sid.SIDct->ch[chn].rcnt += sid.g.ckr; - if (sid.SIDct->ch[chn].rcnt >= 0x8000) - sid.SIDct->ch[chn].rcnt -= 0x8000; + sid->SIDct->ch[chn].pSR = SR; + sid->SIDct->ch[chn].rcnt += sid->g.ckr; + if (sid->SIDct->ch[chn].rcnt >= 0x8000) + sid->SIDct->ch[chn].rcnt -= 0x8000; static double step; double prd; - if (sid.SIDct->ch[chn].Ast & ATK) { + if (sid->SIDct->ch[chn].Ast & ATK) { step = voic->attack; - prd = sid.g.Aprd[(int) step]; - } else if (sid.SIDct->ch[chn].Ast & DECSUS) { + prd = sid->g.Aprd[(int) step]; + } else if (sid->SIDct->ch[chn].Ast & DECSUS) { step = voic->decay; - prd = sid.g.Aprd[(int) step]; + prd = sid->g.Aprd[(int) step]; } else { step = SR & 0xF; - prd = sid.g.Aprd[(int) step]; + prd = sid->g.Aprd[(int) step]; } - step = sid.g.Astp[(int) step]; - if (sid.SIDct->ch[chn].rcnt >= prd && sid.SIDct->ch[chn].rcnt < prd + sid.g.ckr && + step = sid->g.Astp[(int) step]; + if (sid->SIDct->ch[chn].rcnt >= prd && sid->SIDct->ch[chn].rcnt < prd + sid->g.ckr && tmp == 0) { - sid.SIDct->ch[chn].rcnt -= prd; - if ((sid.SIDct->ch[chn].Ast & ATK) || - ++sid.SIDct->ch[chn].expcnt == Aexp[(int) sid.SIDct->ch[chn].envcnt]) { - if (!(sid.SIDct->ch[chn].Ast & _HZ)) { - if (sid.SIDct->ch[chn].Ast & ATK) { - sid.SIDct->ch[chn].envcnt += step; - if (sid.SIDct->ch[chn].envcnt >= 0xFF) { - sid.SIDct->ch[chn].envcnt = 0xFF; - sid.SIDct->ch[chn].Ast &= 0xFF - ATK; + sid->SIDct->ch[chn].rcnt -= prd; + if ((sid->SIDct->ch[chn].Ast & ATK) || + ++sid->SIDct->ch[chn].expcnt == Aexp[(int) sid->SIDct->ch[chn].envcnt]) { + if (!(sid->SIDct->ch[chn].Ast & _HZ)) { + if (sid->SIDct->ch[chn].Ast & ATK) { + sid->SIDct->ch[chn].envcnt += step; + if (sid->SIDct->ch[chn].envcnt >= 0xFF) { + sid->SIDct->ch[chn].envcnt = 0xFF; + sid->SIDct->ch[chn].Ast &= 0xFF - ATK; } - } else if (!(sid.SIDct->ch[chn].Ast & DECSUS) || - sid.SIDct->ch[chn].envcnt > (SR >> 4) + (SR & 0xF0)) { - sid.SIDct->ch[chn].envcnt -= step; - if (sid.SIDct->ch[chn].envcnt <= 0 && - sid.SIDct->ch[chn].envcnt + step != 0) { - sid.SIDct->ch[chn].envcnt = 0; - sid.SIDct->ch[chn].Ast |= _HZ; + } else if (!(sid->SIDct->ch[chn].Ast & DECSUS) || + sid->SIDct->ch[chn].envcnt > (SR >> 4) + (SR & 0xF0)) { + sid->SIDct->ch[chn].envcnt -= step; + if (sid->SIDct->ch[chn].envcnt <= 0 && + sid->SIDct->ch[chn].envcnt + step != 0) { + sid->SIDct->ch[chn].envcnt = 0; + sid->SIDct->ch[chn].Ast |= _HZ; } } } - sid.SIDct->ch[chn].expcnt = 0; + sid->SIDct->ch[chn].expcnt = 0; } else { } } - sid.SIDct->ch[chn].envcnt = (int) sid.SIDct->ch[chn].envcnt & 0xFF; - double aAdd = (voic->freq_low + voic->freq_high * 256) * sid.g.ckr; - if (test || ((ctrl & SYN) && sid.SIDct->sMSBrise)) { - sid.SIDct->ch[chn].pacc = 0; + sid->SIDct->ch[chn].envcnt = (int) sid->SIDct->ch[chn].envcnt & 0xFF; + double aAdd = (voic->freq_low + voic->freq_high * 256) * sid->g.ckr; + if (test || ((ctrl & SYN) && sid->SIDct->sMSBrise)) { + sid->SIDct->ch[chn].pacc = 0; } else { - sid.SIDct->ch[chn].pacc += aAdd; - if (sid.SIDct->ch[chn].pacc > 0xFFFFFF) - sid.SIDct->ch[chn].pacc -= 0x1000000; + sid->SIDct->ch[chn].pacc += aAdd; + if (sid->SIDct->ch[chn].pacc > 0xFFFFFF) + sid->SIDct->ch[chn].pacc -= 0x1000000; } - double MSB = (int) sid.SIDct->ch[chn].pacc & 0x800000; - sid.SIDct->sMSBrise = (MSB > ((int) sid.SIDct->ch[chn].pracc & 0x800000)) ? 1 : 0; + double MSB = (int) sid->SIDct->ch[chn].pacc & 0x800000; + sid->SIDct->sMSBrise = (MSB > ((int) sid->SIDct->ch[chn].pracc & 0x800000)) ? 1 : 0; if (wf & NOI) { - tmp = sid.SIDct->ch[chn].nLFSR; - if ((((int) sid.SIDct->ch[chn].pacc & 0x100000) != - ((int) sid.SIDct->ch[chn].pracc & 0x100000)) || + tmp = sid->SIDct->ch[chn].nLFSR; + if ((((int) sid->SIDct->ch[chn].pacc & 0x100000) != + ((int) sid->SIDct->ch[chn].pracc & 0x100000)) || aAdd >= 0x100000) { step = ((int) tmp & 0x400000) ^ (((int) tmp & 0x20000) << 5); tmp = (((int) tmp << 1) + (step > 0 || test)) & 0x7FFFFF; - sid.SIDct->ch[chn].nLFSR = tmp; + sid->SIDct->ch[chn].nLFSR = tmp; } wfout = (wf & 0x70) ? 0 : (((int) tmp & 0x100000) >> 5) + (((int) tmp & 0x40000) >> 4) + @@ -189,7 +201,7 @@ double dSID_render() { tmp = (int) tmp ^ 0xFFFF; if (pw > tmp) pw = tmp; - tmp = (int) sid.SIDct->ch[chn].pacc >> 8; + tmp = (int) sid->SIDct->ch[chn].pacc >> 8; if (wf == PUL) { int lel = ((int) aAdd >> 16); if (lel > 0) { @@ -220,23 +232,23 @@ double dSID_render() { if (wf & TRI) { if (wf & SAW) { wfout = - (wfout) ? cmbWF(chn, sid.g.Pulsetrsaw, (int) tmp >> 4, 1, &sid.g) : 0; + (wfout) ? cmbWF(chn, sid->g.Pulsetrsaw, (int) tmp >> 4, 1, &sid->g) : 0; } else { - tmp = (int) sid.SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid.SIDct->sMSB : 0); + tmp = (int) sid->SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid->SIDct->sMSB : 0); wfout = (wfout) - ? cmbWF(chn, sid.g.pusaw, + ? cmbWF(chn, sid->g.pusaw, ((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 11, - 0, &sid.g) + 0, &sid->g) : 0; } } else if (wf & SAW) - wfout = (wfout) ? cmbWF(chn, sid.g.pusaw, (int) tmp >> 4, 1, &sid.g) : 0; + wfout = (wfout) ? cmbWF(chn, sid->g.pusaw, (int) tmp >> 4, 1, &sid->g) : 0; } } else if (wf & SAW) { - wfout = (int) sid.SIDct->ch[chn].pacc >> 8; + wfout = (int) sid->SIDct->ch[chn].pacc >> 8; if (wf & TRI) - wfout = cmbWF(chn, sid.g.trsaw, (int) wfout >> 4, 1, &sid.g); + wfout = cmbWF(chn, sid->g.trsaw, (int) wfout >> 4, 1, &sid->g); else { step = aAdd / 0x1200000; wfout += wfout * step; @@ -244,103 +256,107 @@ double dSID_render() { wfout = 0xFFFF - (wfout - 0x10000) / step; } } else if (wf & TRI) { - tmp = (int) sid.SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid.SIDct->sMSB : 0); + tmp = (int) sid->SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid->SIDct->sMSB : 0); wfout = ((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 7; } if (wf) - sid.SIDct->ch[chn].prevwfout = wfout; + sid->SIDct->ch[chn].prevwfout = wfout; else { - wfout = sid.SIDct->ch[chn].prevwfout; + wfout = sid->SIDct->ch[chn].prevwfout; } - sid.SIDct->ch[chn].pracc = sid.SIDct->ch[chn].pacc; - sid.SIDct->sMSB = MSB; + sid->SIDct->ch[chn].pracc = sid->SIDct->ch[chn].pacc; + sid->SIDct->sMSB = MSB; // double preflin = flin; - if ((sid.mute_mask & (1 << chn))) { - if (sid.M[0x17] & sid.SIDct->ch[chn].FSW) { - double chnout = (wfout - 0x8000) * (sid.SIDct->ch[chn].envcnt / 256); + if ((sid->mute_mask & (1 << chn))) { + if (sid->M[0x17] & sid->SIDct->ch[chn].FSW) { + double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256); flin += chnout; - // defle fake filter for solo waveform ahead + // fake filter for solo waveform ahead // mostly copypasted from below double fakeflin = chnout; double fakeflout = 0; - static double fakeplp[SID_CHA] = {0}; - static double fakepbp[SID_CHA] = {0}; - double ctf = ((double) (sid.M[0x15] & 7)) / 8 + sid.M[0x16] + 0.2; + static double fakeplp[3] = {0}; + static double fakepbp[3] = {0}; + double ctf = ((double) (sid->M[0x15] & 7)) / 8 + sid->M[0x16] + 0.2; double reso; - if (sid.g.model == 8580) { - ctf = 1 - exp(ctf * sid.g.ctfr); - reso = pow(2, ((double) (4 - (double) (sid.M[0x17] >> 4)) / 8)); + if (sid->g.model == 8580) { + ctf = 1 - exp(ctf * sid->g.ctfr); + reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8)); } else { if (ctf < 24) ctf = 0.035; else - ctf = 1 - 1.263 * exp(ctf * sid.g.ctf_ratio_6581); - reso = (sid.M[0x17] > 0x5F) ? 8.0 / (double) (sid.M[0x17] >> 4) : 1.41; + ctf = 1 - 1.263 * exp(ctf * sid->g.ctf_ratio_6581); + reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41; } double tmp = fakeflin + fakepbp[chn] * reso + fakeplp[chn]; - if (sid.M[0x18] & HP) + if (sid->M[0x18] & HP) fakeflout -= tmp; tmp = fakepbp[chn] - tmp * ctf; fakepbp[chn] = tmp; - if (sid.M[0x18] & BP) + if (sid->M[0x18] & BP) fakeflout -= tmp; tmp = fakeplp[chn] + tmp * ctf; fakeplp[chn] = tmp; - if (sid.M[0x18] & LP) + if (sid->M[0x18] & LP) fakeflout += tmp; - double defle_wf_out = (fakeflout / SID_OUT_SCALE) * (sid.M[0x18] & 0xF) * 65535; - waveforms_add_sample(1 + chn, defle_wf_out, defle_wf_out); - } else if ((chn % SID_CHA) != 2 || !(sid.M[0x18] & OFF3)) { - double chnout = (wfout - 0x8000) * (sid.SIDct->ch[chn].envcnt / 256); + double wf_out = (fakeflout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535; + waveforms_add_sample(1 + chn, wf_out); + } else if ((chn % 3) != 2 || !(sid->M[0x18] & OFF3)) { + double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256); output += chnout; - double defle_wf_out = (chnout / SID_OUT_SCALE) * (sid.M[0x18] & 0xF) * 65535; - waveforms_add_sample(1 + chn, defle_wf_out, defle_wf_out); + double wf_out = (chnout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535; + waveforms_add_sample(1 + chn, wf_out); } } else { - waveforms_add_sample(1 + chn, 0, 0); + waveforms_add_sample(1 + chn, 0); } } int M1 = 0; if (M1 & 3) - sid.M[0x1B] = (int) wfout >> 8; - sid.M[0x1C] = sid.SIDct->ch[2].envcnt; + sid->M[0x1B] = (int) wfout >> 8; + sid->M[0x1C] = sid->SIDct->ch[2].envcnt; - double ctf = ((double) (sid.M[0x15] & 7)) / 8 + sid.M[0x16] + 0.2; + double ctf = ((double) (sid->M[0x15] & 7)) / 8 + sid->M[0x16] + 0.2; double reso; - if (sid.g.model == 8580) { - ctf = 1 - exp(ctf * sid.g.ctfr); - reso = pow(2, ((double) (4 - (double) (sid.M[0x17] >> 4)) / 8)); + if (sid->g.model == 8580) { + ctf = 1 - exp(ctf * sid->g.ctfr); + reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8)); } else { if (ctf < 24) ctf = 0.035; else - ctf = 1 - 1.263 * exp(ctf * sid.g.ctf_ratio_6581); - reso = (sid.M[0x17] > 0x5F) ? 8.0 / (double) (sid.M[0x17] >> 4) : 1.41; + ctf = 1 - 1.263 * exp(ctf * sid->g.ctf_ratio_6581); + reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41; } - double tmp = flin + sid.SIDct->pbp * reso + sid.SIDct->plp; - if (sid.M[0x18] & HP) + double tmp = flin + sid->SIDct->pbp * reso + sid->SIDct->plp; + if (sid->M[0x18] & HP) output -= tmp; - tmp = sid.SIDct->pbp - tmp * ctf; - sid.SIDct->pbp = tmp; - if (sid.M[0x18] & BP) + tmp = sid->SIDct->pbp - tmp * ctf; + sid->SIDct->pbp = tmp; + if (sid->M[0x18] & BP) output -= tmp; - tmp = sid.SIDct->plp + tmp * ctf; - sid.SIDct->plp = tmp; - if (sid.M[0x18] & LP) + tmp = sid->SIDct->plp + tmp * ctf; + sid->SIDct->plp = tmp; + if (sid->M[0x18] & LP) output += tmp; - return (output / SID_OUT_SCALE) * (sid.M[0x18] & 0xF); + return (output / SID_OUT_SCALE) * (sid->M[0x18] & 0xF); } -void dSID_setMuteMask(int mute_mask) { - sid.mute_mask = mute_mask; +void dSID_setMuteMask(struct SID_chip* sid, int mute_mask) { + sid->mute_mask = mute_mask; } -float dSID_getVolume(int channel) { - if ((sid.M[0x18] & 0xF) == 0) +float dSID_getVolume(struct SID_chip* sid, int channel) { + if ((sid->M[0x18] & 0xF) == 0) return 0; - return sid.SIDct[0].ch[channel].envcnt / 256.0f; + return sid->SIDct[0].ch[channel].envcnt / 256.0f; +} + +void dSID_write(struct SID_chip* sid, unsigned char addr, unsigned char val) { + sid->M[addr&0x1f]=val; } diff --git a/src/engine/platform/sound/c64_d/dsid.h b/src/engine/platform/sound/c64_d/dsid.h index 31d524f18..59b83eb32 100644 --- a/src/engine/platform/sound/c64_d/dsid.h +++ b/src/engine/platform/sound/c64_d/dsid.h @@ -1,36 +1,12 @@ -#pragma once +#ifndef DSID_H +#define DSID_H + +#ifdef __cplusplus +extern "C" { +#endif #include -#define SID_CLK 985248 -#define SID_CHA 3 -#define SID_OUT_SCALE (0x10000 * SID_CHA * 16) - -// TODO: prefix SID_ - -// CONTROL -#define GAT 0x01 -#define SYN 0x02 -#define RNG 0x04 -#define TST 0x08 -#define TRI 0x10 -#define SAW 0x20 -#define PUL 0x40 -#define NOI 0x80 - -#define _HZ 0x10 -#define DECSUS 0x40 -#define ATK 0x80 - -// TODO: change -// filter mode (high) -#define LP 0x10 -#define BP 0x20 -#define HP 0x40 -#define OFF3 0x80 - -extern const int Aexp[256]; - struct SID_ctx_chan { double rcnt; double envcnt; @@ -67,7 +43,7 @@ struct SIDVOICE { }; struct SIDMEM { - struct SIDVOICE v[SID_CHA]; + struct SIDVOICE v[3]; uint8_t UNUSED : 4; uint8_t cutoff_low : 4; uint8_t cutoff_high; @@ -101,12 +77,19 @@ struct SID_chip { struct SID_globals g; struct SID_ctx SIDct[3]; uint8_t M[MemLen]; + int16_t lastOut[3]; int mute_mask; }; -extern struct SID_chip sid; +double dSID_render(struct SID_chip* sid); +void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int model, unsigned char init_wf); +float dSID_getVolume(struct SID_chip* sid, int channel); +void dSID_setMuteMask(struct SID_chip* sid, int mute_mask); -double dSID_render(); -void dSID_init(double samplingRate, int model); -float dSID_getVolume(int channel); -void dSID_setMuteMask(int mute_mask); +void dSID_write(struct SID_chip* sid, unsigned char addr, unsigned char val); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/gui/debug.cpp b/src/gui/debug.cpp index 49d568b3c..60806bde1 100644 --- a/src/gui/debug.cpp +++ b/src/gui/debug.cpp @@ -356,7 +356,6 @@ void putDispatchChip(void* data, int type) { ImGui::Text("- filtCut: %d",ch->filtCut); ImGui::Text("- resetTime: %d",ch->resetTime); COMMON_CHIP_DEBUG_BOOL; - ImGui::TextColored(ch->isFP?colorOn:colorOff,">> IsFP"); break; } case DIV_SYSTEM_ARCADE: diff --git a/src/gui/gui.h b/src/gui/gui.h index a8befff29..b3bd9539c 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1544,7 +1544,7 @@ class FurnaceGUI { snCore(0), nesCore(0), fdsCore(0), - c64Core(1), + c64Core(0), pokeyCore(1), opnCore(1), pcSpeakerOutMethod(0), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 882bc8075..f67e8e406 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -116,7 +116,8 @@ const char* nesCores[]={ const char* c64Cores[]={ "reSID", - "reSIDfp" + "reSIDfp", + "dSID" }; const char* pokeyCores[]={ @@ -1254,7 +1255,7 @@ void FurnaceGUI::drawSettings() { ImGui::Text("SID core"); ImGui::SameLine(); - ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,2); + ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,3); ImGui::Text("POKEY core"); ImGui::SameLine(); @@ -2645,7 +2646,7 @@ void FurnaceGUI::syncSettings() { settings.snCore=e->getConfInt("snCore",0); settings.nesCore=e->getConfInt("nesCore",0); settings.fdsCore=e->getConfInt("fdsCore",0); - settings.c64Core=e->getConfInt("c64Core",1); + settings.c64Core=e->getConfInt("c64Core",0); settings.pokeyCore=e->getConfInt("pokeyCore",1); settings.opnCore=e->getConfInt("opnCore",1); settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0); @@ -2791,7 +2792,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.snCore,0,1); clampSetting(settings.nesCore,0,1); clampSetting(settings.fdsCore,0,1); - clampSetting(settings.c64Core,0,1); + clampSetting(settings.c64Core,0,2); clampSetting(settings.pokeyCore,0,1); clampSetting(settings.opnCore,0,1); clampSetting(settings.pcSpeakerOutMethod,0,4); @@ -2971,7 +2972,7 @@ void FurnaceGUI::commitSettings() { settings.snCore!=e->getConfInt("snCore",0) || settings.nesCore!=e->getConfInt("nesCore",0) || settings.fdsCore!=e->getConfInt("fdsCore",0) || - settings.c64Core!=e->getConfInt("c64Core",1) || + settings.c64Core!=e->getConfInt("c64Core",0) || settings.pokeyCore!=e->getConfInt("pokeyCore",1) || settings.opnCore!=e->getConfInt("opnCore",1) ); From a8a02b9ebbb3e898b573017cca279fd0812ac7fc Mon Sep 17 00:00:00 2001 From: MooingLemur Date: Wed, 5 Jul 2023 15:29:11 -0700 Subject: [PATCH 04/29] Changed EFxx to EExx at request of tildearrow --- doc/7-systems/vera.md | 2 +- src/engine/dispatch.h | 2 +- src/engine/platform/vera.cpp | 2 +- src/engine/playback.cpp | 3 ++- src/engine/sysDef.cpp | 1 - 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/7-systems/vera.md b/doc/7-systems/vera.md index 4497887b8..e89744a9d 100644 --- a/doc/7-systems/vera.md +++ b/doc/7-systems/vera.md @@ -13,5 +13,5 @@ currently Furnace does not support the PCM channel's stereo mode, though (except - `2`: triangle - `3`: noise - `22xx`: **set duty cycle.** range is `0` to `3F`. -- `EFxx`: **ZSM synchronization event.** +- `EExx`: **ZSM synchronization event.** - Where `xx` is the event payload. This has no effect in how the music is played in Furnace, but the ZSMKit library for the Commander X16 interprets these events inside ZSM files and optionally triggers a callback routine. This can be used, for instance, to cause game code to respond to beats or at certain points in the music. diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index 9a7a067d3..c458cf9c4 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -236,7 +236,7 @@ enum DivDispatchCmds { DIV_CMD_NES_LINEAR_LENGTH, - DIV_CMD_SYNC_MESSAGE, // (value) + DIV_CMD_EXTERNAL, // (value) DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 8c1bf9480..fe34b8bc4 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -416,7 +416,7 @@ int DivPlatformVERA::dispatch(DivCommand c) { case DIV_CMD_MACRO_ON: chan[c.chan].std.mask(c.value,false); break; - case DIV_CMD_SYNC_MESSAGE: + case DIV_CMD_EXTERNAL: rWriteZSMSync(c.value); break; case DIV_ALWAYS_SET_VOLUME: diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index c70844a10..4d9e12ea4 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -236,7 +236,7 @@ const char* cmdName[]={ "NES_LINEAR_LENGTH", - "SYNC_MESSAGE", + "EXTERNAL", "ALWAYS_SET_VOLUME" }; @@ -915,6 +915,7 @@ void DivEngine::processRow(int i, bool afterDelay) { //printf("\x1b[1;36m%d: extern command %d\x1b[m\n",i,effectVal); extValue=effectVal; extValuePresent=true; + dispatchCmd(DivCommand(DIV_CMD_EXTERNAL,effectVal)); break; case 0xef: // global pitch globalPitch+=(signed char)(effectVal-0x80); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 6b94a66f0..f4d76cebc 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1460,7 +1460,6 @@ void DivEngine::registerSystems() { { {0x20, {DIV_CMD_WAVE, "20xx: Set waveform"}}, {0x22, {DIV_CMD_STD_NOISE_MODE, "22xx: Set duty cycle (0 to 3F)"}}, - {0xEF, {DIV_CMD_SYNC_MESSAGE, "EFxx: ZSM sync event"}}, } ); From bcd5bfdb424f0802ee0aca22d446b6d886bfb49e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 17:37:25 -0500 Subject: [PATCH 05/29] C64: dSID core, part 2 --- src/engine/platform/c64.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 469890bca..3b4119864 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -80,7 +80,8 @@ void DivPlatformC64::acquire(short** buf, size_t len) { writes.pop(); } if (sidCore==2) { - buf[0][i]=32767.0*dSID_render(sid_d); + double o=dSID_render(sid_d); + buf[0][i]=32767*CLAMP(o,-1.0,1.0); if (++writeOscBuf>=4) { writeOscBuf=0; oscBuf[0]->data[oscBuf[0]->needle++]=sid_d->lastOut[0]; @@ -465,9 +466,9 @@ void DivPlatformC64::muteChannel(int ch, bool mute) { if (sidCore==2) { dSID_setMuteMask( sid_d, - (isMuted[0]?1:0)| - (isMuted[1]?2:0)| - (isMuted[2]?4:0) + (isMuted[0]?0:1)| + (isMuted[1]?0:2)| + (isMuted[2]?0:4) ); } else if (sidCore==1) { sid_fp.mute(ch,mute); @@ -543,6 +544,12 @@ void DivPlatformC64::reset() { if (sidCore==2) { dSID_init(sid_d,chipClock,rate,sidIs6581?6581:8580,1); + dSID_setMuteMask( + sid_d, + (isMuted[0]?0:1)| + (isMuted[1]?0:2)| + (isMuted[2]?0:4) + ); } else if (sidCore==1) { sid_fp.reset(); sid_fp.clockSilent(16000); From 432f36ffcbe05d71863884f6419b2629c374960d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 19:11:28 -0500 Subject: [PATCH 06/29] Merge branch 'dSID' From 9d5e5446957200adaa89aebc67b95e7680dcf7d1 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 19:36:40 -0500 Subject: [PATCH 07/29] C64: fix memory corruption --- src/engine/platform/sound/c64_d/dsid.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/platform/sound/c64_d/dsid.c b/src/engine/platform/sound/c64_d/dsid.c index 72bf6596a..d0eb3cc65 100644 --- a/src/engine/platform/sound/c64_d/dsid.c +++ b/src/engine/platform/sound/c64_d/dsid.c @@ -303,16 +303,16 @@ double dSID_render(struct SID_chip* sid) { fakeflout += tmp; double wf_out = (fakeflout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535; - waveforms_add_sample(1 + chn, wf_out); + waveforms_add_sample(chn, wf_out); } else if ((chn % 3) != 2 || !(sid->M[0x18] & OFF3)) { double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256); output += chnout; double wf_out = (chnout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535; - waveforms_add_sample(1 + chn, wf_out); + waveforms_add_sample(chn, wf_out); } } else { - waveforms_add_sample(1 + chn, 0); + waveforms_add_sample(chn, 0); } } int M1 = 0; From 7b14e0cb81dad1b48048459d610ac1ad7cf9a5b2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 19:41:31 -0500 Subject: [PATCH 08/29] WARNINGS_ARE_ERRORS shouldn't produce a warning it's what we use at work --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2051eb8d4..8fb2aa919 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -862,7 +862,7 @@ endif() string(REPLACE ";" " " WARNING_FLAGS_STRING "${WARNING_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_STRING}") if (WARNINGS_ARE_ERRORS) - message(WARNING + message(STATUS "Treating all warnings in furnace's C++ code as errors! " "Please report any errors you encounter on the bug tracker." ) From 073bc8d8b5f394385737e0e0924becc9700d15d4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 20:12:08 -0500 Subject: [PATCH 09/29] C64: faster --- src/engine/platform/c64.cpp | 4 +++- src/engine/platform/c64.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 3b4119864..35195a203 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -543,13 +543,14 @@ void DivPlatformC64::reset() { } if (sidCore==2) { - dSID_init(sid_d,chipClock,rate,sidIs6581?6581:8580,1); + dSID_init(sid_d,chipClock,rate,sidIs6581?6581:8580,needInitTables); dSID_setMuteMask( sid_d, (isMuted[0]?0:1)| (isMuted[1]?0:2)| (isMuted[2]?0:4) ); + needInitTables=false; } else if (sidCore==1) { sid_fp.reset(); sid_fp.clockSilent(16000); @@ -635,6 +636,7 @@ int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, const DivConfi parent=p; dumpWrites=false; skipRegisterWrites=false; + needInitTables=true; writeOscBuf=0; for (int i=0; i<3; i++) { isMuted[i]=false; diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 77c236760..2ea987cc8 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -68,7 +68,7 @@ class DivPlatformC64: public DivDispatch { unsigned char sidCore; int filtCut, resetTime; - bool keyPriority, sidIs6581; + bool keyPriority, sidIs6581, needInitTables; unsigned char chanOrder[3]; unsigned char testAD, testSR; From 7375d2e6fbc237fd736609fa6e115d4e519cf82d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 20:31:31 -0500 Subject: [PATCH 10/29] C64: faster startup --- src/engine/platform/c64.cpp | 83 +++++++++++++++++++++---------------- src/engine/platform/c64.h | 4 +- 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 35195a203..ecc90d480 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -65,16 +65,16 @@ const char** DivPlatformC64::getRegisterSheet() { } void DivPlatformC64::acquire(short** buf, size_t len) { - int dcOff=(sidCore)?0:sid.get_dc(0); + int dcOff=(sidCore)?0:sid->get_dc(0); for (size_t i=0; iwrite(w.addr,w.val); } else { - sid.write(w.addr,w.val); + sid->write(w.addr,w.val); } regPool[w.addr&0x1f]=w.val; writes.pop(); @@ -89,21 +89,21 @@ void DivPlatformC64::acquire(short** buf, size_t len) { oscBuf[2]->data[oscBuf[2]->needle++]=sid_d->lastOut[2]; } } else if (sidCore==1) { - sid_fp.clock(4,&buf[0][i]); + sid_fp->clock(4,&buf[0][i]); if (++writeOscBuf>=4) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5; - oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5; - oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>5; + oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp->lastChanOut[0]-dcOff)>>5; + oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp->lastChanOut[1]-dcOff)>>5; + oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp->lastChanOut[2]-dcOff)>>5; } } else { - sid.clock(); - buf[0][i]=sid.output(); + sid->clock(); + buf[0][i]=sid->output(); if (++writeOscBuf>=16) { writeOscBuf=0; - oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5; - oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5; - oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5; + oscBuf[0]->data[oscBuf[0]->needle++]=(sid->last_chan_out[0]-dcOff)>>5; + oscBuf[1]->data[oscBuf[1]->needle++]=(sid->last_chan_out[1]-dcOff)>>5; + oscBuf[2]->data[oscBuf[2]->needle++]=(sid->last_chan_out[2]-dcOff)>>5; } } } @@ -471,9 +471,9 @@ void DivPlatformC64::muteChannel(int ch, bool mute) { (isMuted[2]?0:4) ); } else if (sidCore==1) { - sid_fp.mute(ch,mute); + sid_fp->mute(ch,mute); } else { - sid.set_is_muted(ch,mute); + sid->set_is_muted(ch,mute); } } @@ -552,10 +552,10 @@ void DivPlatformC64::reset() { ); needInitTables=false; } else if (sidCore==1) { - sid_fp.reset(); - sid_fp.clockSilent(16000); + sid_fp->reset(); + sid_fp->clockSilent(16000); } else { - sid.reset(); + sid->reset(); } memset(regPool,0,32); @@ -581,23 +581,6 @@ void DivPlatformC64::poke(std::vector& wlist) { } void DivPlatformC64::setChipModel(bool is6581) { - if (is6581) { - if (sidCore==2) { - // do nothing - } else if (sidCore==1) { - sid_fp.setChipModel(reSIDfp::MOS6581); - } else { - sid.set_chip_model(MOS6581); - } - } else { - if (sidCore==2) { - // do nothing - } else if (sidCore==1) { - sid_fp.setChipModel(reSIDfp::MOS8580); - } else { - sid.set_chip_model(MOS8580); - } - } sidIs6581=is6581; } @@ -625,7 +608,7 @@ void DivPlatformC64::setFlags(const DivConfig& flags) { } if (sidCore>0) { rate/=4; - if (sidCore==1) sid_fp.setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0); + if (sidCore==1) sid_fp->setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0); } keyPriority=flags.getBool("keyPriority",true); testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15); @@ -644,9 +627,35 @@ int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, const DivConfi } if (sidCore==2) { + sid=NULL; + sid_fp=NULL; sid_d=new struct SID_chip; - } else { + } else if (sidCore==1) { + sid=NULL; + sid_fp=new reSIDfp::SID; sid_d=NULL; + } else { + sid=new SID; + sid_fp=NULL; + sid_d=NULL; + } + + if (sidIs6581) { + if (sidCore==2) { + // do nothing + } else if (sidCore==1) { + sid_fp->setChipModel(reSIDfp::MOS6581); + } else { + sid->set_chip_model(MOS6581); + } + } else { + if (sidCore==2) { + // do nothing + } else if (sidCore==1) { + sid_fp->setChipModel(reSIDfp::MOS8580); + } else { + sid->set_chip_model(MOS8580); + } } setFlags(flags); @@ -660,6 +669,8 @@ void DivPlatformC64::quit() { for (int i=0; i<3; i++) { delete oscBuf[i]; } + if (sid!=NULL) delete sid; + if (sid_fp!=NULL) delete sid_fp; if (sid_d!=NULL) delete sid_d; } diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 2ea987cc8..26b39c804 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -72,8 +72,8 @@ class DivPlatformC64: public DivDispatch { unsigned char chanOrder[3]; unsigned char testAD, testSR; - SID sid; - reSIDfp::SID sid_fp; + SID* sid; + reSIDfp::SID* sid_fp; struct SID_chip* sid_d; unsigned char regPool[32]; From 5d015ae0ed3c6f67b8eb35bf4ab5580c47e30eeb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 20:38:31 -0500 Subject: [PATCH 11/29] update credits --- src/gui/about.cpp | 1 + src/main.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 8131d3c30..2aef20b9e 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -173,6 +173,7 @@ const char* aboutLine[]={ "reSID by Dag Lem", "reSIDfp by Dag Lem, Antti Lankila", "and Leandro Nini", + "dSID by DefleMask Team based on jsSID", "Stella by Stella Team", "QSound emulator by superctr and Valley Bell", "VICE VIC-20 sound core by Rami Rasanen and viznut", diff --git a/src/main.cpp b/src/main.cpp index 574c1dd7a..e58a455ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -198,6 +198,7 @@ TAParamResult pVersion(String) { printf("- NSFPlay by Brad Smith and Brezza (unknown open-source license)\n"); printf("- reSID by Dag Lem (GPLv2)\n"); printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n"); + printf("- dSID by DefleMask Team (based on jsSID by Hermit) (MIT)\n"); printf("- Stella by Stella Team (GPLv2)\n"); printf("- vgsound_emu (second version, modified version) by cam900 (zlib license)\n"); printf("- MAME GA20 core by Acho A. Tang, R. Belmont, Valley Bell (BSD 3-clause)\n"); From 59e93892dd153d0112a20c42851a281eea75b069 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 5 Jul 2023 23:55:50 -0500 Subject: [PATCH 12/29] fix MIDI clock/time using too much CPU --- src/engine/playback.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index ce94b883b..3a2ef7510 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1980,14 +1980,10 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi } else { // 3. run MIDI clock int midiTotal=MIN(cycles,runLeftG); - for (int i=0; i Date: Thu, 6 Jul 2023 02:12:34 -0500 Subject: [PATCH 13/29] C64: fix dSID filters --- src/engine/platform/sound/c64_d/dsid.c | 37 +++++++++++++++----------- src/engine/platform/sound/c64_d/dsid.h | 2 ++ 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/engine/platform/sound/c64_d/dsid.c b/src/engine/platform/sound/c64_d/dsid.c index d0eb3cc65..d01dc7ad7 100644 --- a/src/engine/platform/sound/c64_d/dsid.c +++ b/src/engine/platform/sound/c64_d/dsid.c @@ -1,4 +1,5 @@ #include "dsid.h" +#include #include // INFINITY #include #include // memset, memcpy @@ -82,7 +83,7 @@ void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int } sid->g.ctfr = -2.0 * 3.14 * (12500.0 / 256.0) / samplingRate, - sid->g.ctf_ratio_6581 = -2.0 * 3.14 * (20000.0 / 256.0) / samplingRate; + sid->g.ctf_ratio_6581 = -2.0 * 3.14 * (samplingRate / 44100.0) * (20000.0 / 256.0) / samplingRate; sid->g.ckr = clockRate / samplingRate; const double bAprd[16] = {9, 32 * 1, 63 * 1, 95 * 1, 149 * 1, 220 * 1, @@ -92,9 +93,23 @@ void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int memcpy(&sid->g.Aprd, &bAprd, sizeof(bAprd)); memcpy(&sid->g.Astp, &bAstp, sizeof(bAstp)); if (init_wf) { - cCmbWF(sid->g.trsaw, 0.8, 2.4, 0.64); - cCmbWF(sid->g.pusaw, 1.4, 1.9, 0.68); - cCmbWF(sid->g.Pulsetrsaw, 0.8, 2.5, 0.64); + cCmbWF(sid->g.trsaw, 0.8, 2.4, 0.64); + cCmbWF(sid->g.pusaw, 1.4, 1.9, 0.68); + cCmbWF(sid->g.Pulsetrsaw, 0.8, 2.5, 0.64); + + for (int i = 0; i < 2048; i++) { + double ctf = (double) i / 8.0 + 0.2; + if (model == 8580) { + ctf = 1 - exp(ctf * sid->g.ctfr); + } else { + if (ctf < 24) { + ctf = 2.0 * sin(771.78 / samplingRate); + } else { + ctf = (44100.0 / samplingRate) - 1.263 * (44100.0 / samplingRate) * exp(ctf * sid->g.ctf_ratio_6581); + } + } + sid->g.ctf_table[i] = ctf; + } } double prd0 = sid->g.ckr > 9 ? sid->g.ckr : 9; @@ -278,16 +293,11 @@ double dSID_render(struct SID_chip* sid) { double fakeflout = 0; static double fakeplp[3] = {0}; static double fakepbp[3] = {0}; - double ctf = ((double) (sid->M[0x15] & 7)) / 8 + sid->M[0x16] + 0.2; + double ctf = sid->g.ctf_table[((sid->M[0x15]&7)|(sid->M[0x16]<<3))&0x7ff]; double reso; if (sid->g.model == 8580) { - ctf = 1 - exp(ctf * sid->g.ctfr); reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8)); } else { - if (ctf < 24) - ctf = 0.035; - else - ctf = 1 - 1.263 * exp(ctf * sid->g.ctf_ratio_6581); reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41; } double tmp = fakeflin + fakepbp[chn] * reso + fakeplp[chn]; @@ -320,16 +330,11 @@ double dSID_render(struct SID_chip* sid) { sid->M[0x1B] = (int) wfout >> 8; sid->M[0x1C] = sid->SIDct->ch[2].envcnt; - double ctf = ((double) (sid->M[0x15] & 7)) / 8 + sid->M[0x16] + 0.2; + double ctf = sid->g.ctf_table[((sid->M[0x15]&7)|(sid->M[0x16]<<3))&0x7ff]; double reso; if (sid->g.model == 8580) { - ctf = 1 - exp(ctf * sid->g.ctfr); reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8)); } else { - if (ctf < 24) - ctf = 0.035; - else - ctf = 1 - 1.263 * exp(ctf * sid->g.ctf_ratio_6581); reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41; } diff --git a/src/engine/platform/sound/c64_d/dsid.h b/src/engine/platform/sound/c64_d/dsid.h index 59b83eb32..b02d1870e 100644 --- a/src/engine/platform/sound/c64_d/dsid.h +++ b/src/engine/platform/sound/c64_d/dsid.h @@ -62,6 +62,8 @@ struct SID_globals { double ctfr; double ctf_ratio_6581; + double ctf_table[2048]; + int trsaw[4096]; int pusaw[4096]; int Pulsetrsaw[4096]; From 3b151a78a59951d196593b4a4577cf0329fea628 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 6 Jul 2023 02:12:45 -0500 Subject: [PATCH 14/29] tiny playback optimization --- src/engine/dispatchContainer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 3ff58e30e..6a532548b 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -172,6 +172,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size) if (bbIn[i]==NULL) continue; if (bb[i]==NULL) continue; for (size_t j=0; j Date: Thu, 6 Jul 2023 02:20:22 -0500 Subject: [PATCH 15/29] C64: fix overlord after more than one year --- src/engine/platform/c64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index ecc90d480..b41a52d5e 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -377,7 +377,7 @@ int DivPlatformC64::dispatch(DivCommand c) { break; case DIV_CMD_C64_CUTOFF: if (c.value>100) c.value=100; - filtCut=c.value*2047/100; + filtCut=(c.value+2)*2047/102; updateFilter(); break; case DIV_CMD_C64_FINE_CUTOFF: From a267901b187f68aead547e08a9b50f218ba700b7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 6 Jul 2023 04:04:33 -0500 Subject: [PATCH 16/29] GUI: default to non-SDL render backend if availabl --- src/gui/gui.cpp | 4 ++-- src/gui/gui.h | 12 ++++++++---- src/gui/settings.cpp | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 62608960b..a1a5cb842 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -6347,8 +6347,8 @@ bool FurnaceGUI::init() { logD("starting render backend..."); if (!rend->init(sdlWin)) { - if (settings.renderBackend!="SDL" && !settings.renderBackend.empty()) { - settings.renderBackend=""; + if (settings.renderBackend!="SDL") { + settings.renderBackend="SDL"; //e->setConf("renderBackend",""); //e->saveConf(); //lastError=fmt::sprintf("\r\nthe render backend has been set to a safe value. please restart Furnace."); diff --git a/src/gui/gui.h b/src/gui/gui.h index b3bd9539c..267e34a0f 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -75,16 +75,20 @@ enum FurnaceGUIRenderBackend { GUI_BACKEND_DX11 }; -#ifdef HAVE_RENDER_SDL -#define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL -#define GUI_BACKEND_DEFAULT_NAME "SDL" -#else #ifdef HAVE_RENDER_DX11 #define GUI_BACKEND_DEFAULT GUI_BACKEND_DX11 #define GUI_BACKEND_DEFAULT_NAME "DirectX 11" #else +#ifdef HAVE_RENDER_GL #define GUI_BACKEND_DEFAULT GUI_BACKEND_GL #define GUI_BACKEND_DEFAULT_NAME "OpenGL" +#else +#ifdef HAVE_RENDER_SDL +#define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL +#define GUI_BACKEND_DEFAULT_NAME "SDL" +#else +#error how did you manage to do that? +#endif #endif #endif diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index f67e8e406..ab06d74f0 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2772,7 +2772,7 @@ void FurnaceGUI::syncSettings() { settings.orderButtonPos=e->getConfInt("orderButtonPos",2); settings.compress=e->getConfInt("compress",1); settings.newPatternFormat=e->getConfInt("newPatternFormat",1); - settings.renderBackend=e->getConfString("renderBackend","SDL"); + settings.renderBackend=e->getConfString("renderBackend",GUI_BACKEND_DEFAULT_NAME); settings.renderClearPos=e->getConfInt("renderClearPos",0); settings.insertBehavior=e->getConfInt("insertBehavior",1); settings.pullDeleteRow=e->getConfInt("pullDeleteRow",1); From ce2661df6649f5f16f62529ce0be18d9bcb9fbd2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 6 Jul 2023 18:29:29 -0500 Subject: [PATCH 17/29] audio issue debugging --- src/audio/sdlAudio.cpp | 10 ++++++++-- src/engine/engine.h | 4 ++++ src/engine/playback.cpp | 4 ++++ src/gui/debugWindow.cpp | 28 ++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/audio/sdlAudio.cpp b/src/audio/sdlAudio.cpp index 6d0ceff06..5d07921e9 100644 --- a/src/audio/sdlAudio.cpp +++ b/src/audio/sdlAudio.cpp @@ -127,14 +127,20 @@ bool TAAudioSDL::init(TAAudioDesc& request, TAAudioDesc& response) { ac.callback=taSDLProcess; ac.userdata=this; - ai=SDL_OpenAudioDevice(request.deviceName.empty()?NULL:request.deviceName.c_str(),0,&ac,&ar,SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + ai=SDL_OpenAudioDevice(request.deviceName.empty()?NULL:request.deviceName.c_str(),0,&ac,&ar,0); if (ai==0) { logE("could not open audio device: %s",SDL_GetError()); return false; } + const char* backendName=SDL_GetCurrentAudioDriver(); + desc.deviceName=request.deviceName; - desc.name=""; + if (backendName==NULL) { + desc.name=""; + } else { + desc.name=backendName; + } desc.rate=ar.freq; desc.inChans=0; desc.outChans=ar.channels; diff --git a/src/engine/engine.h b/src/engine/engine.h index aceee308b..e4b3e56a3 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -570,6 +570,7 @@ class DivEngine { float oscSize; int oscReadPos, oscWritePos; int tickMult; + int lastNBIns, lastNBOuts, lastNBSize; std::atomic processTime; void runExportThread(); @@ -1252,6 +1253,9 @@ class DivEngine { oscReadPos(0), oscWritePos(0), tickMult(1), + lastNBIns(0), + lastNBOuts(0), + lastNBSize(0), processTime(0), yrw801ROM(NULL), tg100ROM(NULL), diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 798de87b2..7825e1886 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -1694,6 +1694,10 @@ void DivEngine::runMidiTime(int totalCycles) { } void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size) { + lastNBIns=inChans; + lastNBOuts=outChans; + lastNBSize=size; + if (!size) { logW("nextBuf called with size 0!"); return; diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index b9dc6c423..bfeb9067e 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -388,6 +388,34 @@ void FurnaceGUI::drawDebug() { ImGui::Text("System Managed Scale: %d",sysManagedScale); ImGui::TreePop(); } + if (ImGui::TreeNode("Audio Debug")) { + TAAudioDesc& audioWant=e->getAudioDescWant(); + TAAudioDesc& audioGot=e->getAudioDescGot(); + + ImGui::Text("want:"); + ImGui::Text("- name: %s",audioWant.name.c_str()); + ImGui::Text("- device name: %s",audioWant.deviceName.c_str()); + ImGui::Text("- rate: %f",audioWant.rate); + ImGui::Text("- buffer size: %d",audioWant.bufsize); + ImGui::Text("- fragments: %d",audioWant.fragments); + ImGui::Text("- inputs: %d",audioWant.inChans); + ImGui::Text("- outputs: %d",audioWant.outChans); + ImGui::Text("- format: %d",audioWant.outFormat); + + ImGui::Text("got:"); + ImGui::Text("- name: %s",audioGot.name.c_str()); + ImGui::Text("- device name: %s",audioGot.deviceName.c_str()); + ImGui::Text("- rate: %f",audioGot.rate); + ImGui::Text("- buffer size: %d",audioGot.bufsize); + ImGui::Text("- fragments: %d",audioGot.fragments); + ImGui::Text("- inputs: %d",audioGot.inChans); + ImGui::Text("- outputs: %d",audioGot.outChans); + ImGui::Text("- format: %d",audioGot.outFormat); + + ImGui::Text("last call to nextBuf(): in %d, out %d, size %d",e->lastNBIns,e->lastNBOuts,e->lastNBSize); + + ImGui::TreePop(); + } if (ImGui::TreeNode("Visualizer Debug")) { if (ImGui::BeginTable("visX",3,ImGuiTableFlags_Borders)) { ImGui::TableNextRow(ImGuiTableRowFlags_Headers); From 5c97f9981a9b184c952d88df764525d1e771beef Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 6 Jul 2023 21:14:25 -0500 Subject: [PATCH 18/29] add option to change SDL audio driver --- src/engine/engine.cpp | 11 ++++++++++- src/gui/gui.cpp | 13 +++++++++++++ src/gui/gui.h | 5 ++++- src/gui/settings.cpp | 27 +++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 1aa8188e0..c7b5ac9e0 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -4586,6 +4586,15 @@ bool DivEngine::initAudioBackend() { } } +#ifdef HAVE_SDL2 + if (audioEngine==DIV_AUDIO_SDL) { + String audioDriver=getConfString("sdlAudioDriver",""); + if (!audioDriver.empty()) { + SDL_SetHint("SDL_HINT_AUDIODRIVER",audioDriver.c_str()); + } + } +#endif + lowQuality=getConfInt("audioQuality",0); forceMono=getConfInt("forceMono",0); clampSamples=getConfInt("clampSamples",0); @@ -4594,7 +4603,7 @@ bool DivEngine::initAudioBackend() { midiOutClock=getConfInt("midiOutClock",0); midiOutTime=getConfInt("midiOutTime",0); midiOutTimeRate=getConfInt("midiOutTimeRate",0); - midiOutProgramChange = getConfInt("midiOutProgramChange",0); + midiOutProgramChange=getConfInt("midiOutProgramChange",0); midiOutMode=getConfInt("midiOutMode",DIV_MIDI_MODE_NOTE); if (metroVol<0.0f) metroVol=0.0f; if (metroVol>2.0f) metroVol=2.0f; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index a1a5cb842..3e7ed0275 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -6329,6 +6329,19 @@ bool FurnaceGUI::init() { } #endif + int numDriversA=SDL_GetNumAudioDrivers(); + if (numDriversA<0) { + logW("could not list audio drivers! %s",SDL_GetError()); + } else { + for (int i=0; i recentFile; std::vector makeInsTypeList; std::vector availRenderDrivers; + std::vector availAudioDrivers; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, zsmExportLoop, vgmExportPatternHints; bool vgmExportDirectStream, displayInsTypeList; @@ -1534,6 +1535,7 @@ class FurnaceGUI { String macroRelLabel; String emptyLabel; String emptyLabel2; + String sdlAudioDriver; DivConfig initialSys; Settings(): @@ -1686,7 +1688,8 @@ class FurnaceGUI { noteRelLabel("==="), macroRelLabel("REL"), emptyLabel("..."), - emptyLabel2("..") {} + emptyLabel2(".."), + sdlAudioDriver("") {} } settings; struct Tutorial { diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index ab06d74f0..6c70a8914 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -807,6 +807,25 @@ void FurnaceGUI::drawSettings() { } #endif + if (settings.audioEngine==DIV_AUDIO_SDL) { + ImGui::Text("Driver"); + ImGui::SameLine(); + if (ImGui::BeginCombo("##SDLADriver",settings.sdlAudioDriver.empty()?"Automatic":settings.sdlAudioDriver.c_str())) { + if (ImGui::Selectable("Automatic",settings.sdlAudioDriver.empty())) { + settings.sdlAudioDriver=""; + } + for (String& i: availAudioDrivers) { + if (ImGui::Selectable(i.c_str(),i==settings.sdlAudioDriver)) { + settings.sdlAudioDriver=i; + } + } + ImGui::EndCombo(); + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); + } + } + ImGui::Text("Device"); ImGui::SameLine(); String audioDevName=settings.audioDevice.empty()?"":settings.audioDevice; @@ -1324,6 +1343,9 @@ void FurnaceGUI::drawSettings() { #endif ImGui::EndCombo(); } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); + } if (curRenderBackend=="SDL") { if (ImGui::BeginCombo("Render driver",settings.renderDriver.empty()?"Automatic":settings.renderDriver.c_str())) { if (ImGui::Selectable("Automatic",settings.renderDriver.empty())) { @@ -1336,6 +1358,9 @@ void FurnaceGUI::drawSettings() { } ImGui::EndCombo(); } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("you may need to restart Furnace for this setting to take effect."); + } } bool dpiScaleAuto=(settings.dpiScale<0.5f); @@ -2638,6 +2663,7 @@ void FurnaceGUI::syncSettings() { settings.midiOutDevice=e->getConfString("midiOutDevice",""); settings.c163Name=e->getConfString("c163Name",DIV_C163_DEFAULT_NAME); settings.renderDriver=e->getConfString("renderDriver",""); + settings.sdlAudioDriver=e->getConfString("sdlAudioDriver",""); settings.audioQuality=e->getConfInt("audioQuality",0); settings.audioBufSize=e->getConfInt("audioBufSize",1024); settings.audioRate=e->getConfInt("audioRate",44100); @@ -2986,6 +3012,7 @@ void FurnaceGUI::commitSettings() { e->setConf("midiOutDevice",settings.midiOutDevice); e->setConf("c163Name",settings.c163Name); e->setConf("renderDriver",settings.renderDriver); + e->setConf("sdlAudioDriver",settings.sdlAudioDriver); e->setConf("audioQuality",settings.audioQuality); e->setConf("audioBufSize",settings.audioBufSize); e->setConf("audioRate",settings.audioRate); From f841025ce6cfc0b4030f57a0c7110dea296a5832 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 7 Jul 2023 02:21:13 -0500 Subject: [PATCH 19/29] Game Boy: fix wave corruption this time for real thanks jvsTSX --- src/engine/platform/gb.cpp | 2 +- src/gui/about.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 683f97211..c5c66092e 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -322,7 +322,7 @@ void DivPlatformGB::tick(bool sysTick) { rWrite(16+i*5+4,((chan[i].keyOn||chan[i].keyOff)?0x80:0x00)|((chan[i].soundLen<64)<<6)); } else { rWrite(16+i*5+3,(2048-chan[i].freq)&0xff); - rWrite(16+i*5+4,(((2048-chan[i].freq)>>8)&7)|((chan[i].keyOn||chan[i].keyOff)?0x80:0x00)|((chan[i].soundLen<63)<<6)); + rWrite(16+i*5+4,(((2048-chan[i].freq)>>8)&7)|((chan[i].keyOn||(chan[i].keyOff && i!=2))?0x80:0x00)|((chan[i].soundLen<63)<<6)); } if (enoughAlready) { // more compat garbage rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(chan[i].soundLen&63))); diff --git a/src/gui/about.cpp b/src/gui/about.cpp index 2aef20b9e..7afd015c1 100644 --- a/src/gui/about.cpp +++ b/src/gui/about.cpp @@ -134,6 +134,7 @@ const char* aboutLine[]={ "fd", "GENATARi", "host12prog", + "jvsTSX", "Lumigado", "Lunathir", "plane", From aececf352f671b44cf5e723045a4b7f55490bdeb Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 7 Jul 2023 02:25:32 -0500 Subject: [PATCH 20/29] Game Boy: fix 10xx turning wave channel on when it's off --- src/engine/platform/gb.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index c5c66092e..9b565cdc7 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -466,7 +466,9 @@ int DivPlatformGB::dispatch(DivCommand c) { if (c.chan!=2) break; chan[c.chan].wave=c.value; ws.changeWave1(chan[c.chan].wave); - chan[c.chan].keyOn=true; + if (chan[c.chan].active) { + chan[c.chan].keyOn=true; + } break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_PERIODIC(c.value2); From 97fec35b00b98a3a7d2c8bb2160d9ea6cac3a2bc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 7 Jul 2023 03:15:09 -0500 Subject: [PATCH 21/29] GUI: fix hidden channels breaking visualizer --- src/gui/pattern.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index a34a856d8..6e2fc1ed7 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -133,17 +133,20 @@ inline void FurnaceGUI::patternRow(int i, bool isPlaying, float lineHeight, int } ImGui::PopStyleColor(); // for each column + int mustSetXOf=0; for (int j=0; j