From 0f73094113602dc5b63bc752a5f16b55bc8876f0 Mon Sep 17 00:00:00 2001 From: Jeremy Penner Date: Sat, 30 Nov 2024 22:14:20 -0500 Subject: [PATCH] Tweak note-change detection Values have been tuned to ensure samples aren't retriggered during Mario's jump, but are generally retriggered in every other situation where the frequency is changed. Retriggering more often makes the music in games like Pictionary and Rad Gravity sound _way_ better, as well as the sound effects in games with slower music. * do not repeat samples for sustained notes; this never sounds great. * reset after _any_ frequency change if we have been sitting on the same note for longer than a couple of frames. * fix the "reset if the note has jumped by ~2 semitones" logic, which was *wildly* wrong. --- jsnes/src/sample.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/jsnes/src/sample.js b/jsnes/src/sample.js index a1b0886..7b8802c 100644 --- a/jsnes/src/sample.js +++ b/jsnes/src/sample.js @@ -38,14 +38,22 @@ Sample.prototype = { this.index += this.increment / clocksPerPeriod; }, reset: function() { this.index = 0; }, + freqFromTimer: function(timer) { + // timer -> frequency + // clocksPerPeriodTriangle = 8, clocksPerPeriodPulse = 4 + // fTriangle = fCPU/(32*(t + 1)) + // fpulse = fCPU/(16*(t+1) + return 1789773 / (4 * this.clocksPerPeriod * (timer + 1)); + }, sample: function(timer, maxTimer, volume) { const timerIncreasing = maxTimer - timer; if (maxTimer !== this.prevMaxTimer) { if (this.prevMaxTimer) { - var ratio = this.prevMaxTimer < maxTimer ? maxTimer / this.prevMaxTimer : this.prevMaxTimer / maxTimer; - var semitoneInterval = 1.059463; - if (ratio >= semitoneInterval * 2 || this.clocksSinceMaxTimerChange > 210000 || this.index >= this.samples.length) { - this.index = 0; + var ratio = this.freqFromTimer(this.prevMaxTimer) / this.freqFromTimer(maxTimer); + if (ratio < 1) { ratio = 1/ratio }; + var significantNoteChange = 1.1; + if ((this.clocksSinceMaxTimerChange > 130000) || ratio >= significantNoteChange) { + this.reset(); } } this.prevMaxTimer = maxTimer;