diff --git a/runtimes/native/src/apu.c b/runtimes/native/src/apu.c index aa34bcb8..28ea5f1e 100644 --- a/runtimes/native/src/apu.c +++ b/runtimes/native/src/apu.c @@ -5,7 +5,10 @@ #define SAMPLE_RATE 44100 #define MAX_VOLUME 0x1333 // ~15% of INT16_MAX +// The triangle channel sounds a bit quieter than the others, so give it higher amplitude #define MAX_VOLUME_TRIANGLE 0x2000 // ~25% of INT16_MAX +// Also the triangle channel prevent popping on hard stops by adding a 1 ms release +#define RELEASE_TIME_TRIANGLE (SAMPLE_RATE / 1000) typedef struct { /** Starting frequency. */ @@ -97,7 +100,7 @@ static float getCurrentFrequency (const Channel* channel) { } static int16_t getCurrentVolume (const Channel* channel) { - if (time >= channel->sustainTime && channel->releaseTime != channel->sustainTime) { + if (time >= channel->sustainTime && (channel->releaseTime - channel->sustainTime) > RELEASE_TIME_TRIANGLE) { // Release return ramp(channel->sustainVolume, 0, channel->sustainTime, channel->releaseTime); } else if (time >= channel->decayTime) { @@ -192,9 +195,8 @@ void w4_apuTone (int frequency, int duration, int volume, int flags) { } } else if (channelIdx == 2) { - // For the triangle channel, prevent popping on hard stops by adding a 1 ms release if (release == 0) { - channel->releaseTime += SAMPLE_RATE/1000; + channel->releaseTime += RELEASE_TIME_TRIANGLE; } } } diff --git a/runtimes/web/src/apu-worklet.ts b/runtimes/web/src/apu-worklet.ts index 803a9797..9011f1b7 100644 --- a/runtimes/web/src/apu-worklet.ts +++ b/runtimes/web/src/apu-worklet.ts @@ -3,7 +3,10 @@ // Audio worklet file: do not export anything directly. const SAMPLE_RATE = 44100; const MAX_VOLUME = 0.15; +// The triangle channel sounds a bit quieter than the others, so give it higher amplitude const MAX_VOLUME_TRIANGLE = 0.25; +// Also the triangle channel prevent popping on hard stops by adding a 1 ms release +const RELEASE_TIME_TRIANGLE = Math.floor(SAMPLE_RATE / 1000); class Channel { /** Starting frequency. */ @@ -114,7 +117,7 @@ class APUProcessor extends AudioWorkletProcessor { getCurrentVolume (channel: Channel) { const time = this.time; - if (time >= channel.sustainTime && channel.releaseTime != channel.sustainTime) { + if (time >= channel.sustainTime && (channel.releaseTime - channel.sustainTime) > RELEASE_TIME_TRIANGLE) { // Release return this.ramp(channel.sustainVolume, 0, channel.sustainTime, channel.releaseTime); } else if (time >= channel.decayTime) { @@ -188,9 +191,8 @@ class APUProcessor extends AudioWorkletProcessor { } } else if (channelIdx == 2) { - // For the triangle channel, prevent popping on hard stops by adding a 1 ms release if (release == 0) { - channel.releaseTime += (SAMPLE_RATE/1000) >>> 0; + channel.releaseTime += RELEASE_TIME_TRIANGLE; } } } diff --git a/runtimes/web/src/runtime.ts b/runtimes/web/src/runtime.ts index 83efee7a..1f4c940b 100644 --- a/runtimes/web/src/runtime.ts +++ b/runtimes/web/src/runtime.ts @@ -334,8 +334,8 @@ export class Runtime { let update_function = this.wasm!.exports["update"]; if (typeof update_function === "function") { this.bluescreenOnError(update_function); - this.apu.tick(); } + this.apu.tick(); } blueScreen (text: string) {