From 80146838706ccf48614b3deab2b1b55d94faa3f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Wed, 1 Mar 2023 17:46:05 +0000
Subject: [PATCH] feat: great simplification of the audio code Makes use of the
 new emukit audio code.

---
 frontends/web/ts/gb.ts | 95 ++++++++++++------------------------------
 1 file changed, 26 insertions(+), 69 deletions(-)

diff --git a/frontends/web/ts/gb.ts b/frontends/web/ts/gb.ts
index da83b95c..da6cdb78 100644
--- a/frontends/web/ts/gb.ts
+++ b/frontends/web/ts/gb.ts
@@ -1,4 +1,5 @@
 import {
+    AudioSpecs,
     BenchmarkResult,
     Compilation,
     Compiler,
@@ -80,13 +81,6 @@ const KEYS_NAME: Record<string, number> = {
 
 const ROM_PATH = require("../../../res/roms/pocket.gb");
 
-// @TODO: check if this is the right place for this struct
-type AudioChunk = {
-    source: AudioBufferSourceNode;
-    playTime: number;
-    duration: number;
-};
-
 /**
  * Top level class that controls the emulator behaviour
  * and "joins" all the elements together to bring input/output
@@ -122,13 +116,6 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
     private romSize = 0;
     private cartridge: Cartridge | null = null;
 
-    // @TODO: try to think where does this belong
-    private audioContext = new AudioContext({
-        sampleRate: 44100
-    });
-    private audioChunks: AudioChunk[] = [];
-    private nextPlayTime = 0.0;
-
     /**
      * Associative map for extra settings to be used in
      * opaque local storage operations, associated setting
@@ -294,6 +281,11 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
             }
         }
 
+        // triggers the audio event, meaning that the audio should be
+        // processed for the current emulator, effectively emptying
+        // the audio buffer that is pending processing
+        this.trigger("audio");
+
         // increments the number of frames rendered in the current
         // section, this value is going to be used to calculate FPS
         this.frameCount += 1;
@@ -322,61 +314,6 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
         );
         ticks = Math.max(ticks, 1);
 
-        // --- START OF THE AUDIO CODE
-
-        const channels = 2;
-        const internalBuffer = this.gameBoy.audio_buffer_eager(true);
-        const audioBuffer = this.audioContext.createBuffer(
-            channels,
-            internalBuffer.length,
-            44100
-        );
-
-        for (let channel = 0; channel < channels; channel++) {
-            const channelBuffer = audioBuffer.getChannelData(channel);
-            for (let index = 0; index < internalBuffer.length; index++) {
-                channelBuffer[index] = internalBuffer[index] / 100.0;
-            }
-        }
-
-        // @todo check this code so see if it makes sense
-
-        // makes sure that we're not too far away from the audio
-        // and if that's the case drops some of the audio to regain
-        // some sync, this is required because of time hogging
-        const audioCurrentTime = this.audioContext.currentTime;
-        if (
-            this.nextPlayTime > audioCurrentTime + 0.25 ||
-            this.nextPlayTime < audioCurrentTime
-        ) {
-            // @TODO: this is tricky as it cancels most of the code
-            this.audioChunks.forEach((chunk) => {
-                chunk.source.disconnect(this.audioContext.destination);
-                chunk.source.stop();
-            });
-            this.audioChunks = [];
-            this.nextPlayTime = audioCurrentTime + 0.1;
-        }
-
-        const source = this.audioContext.createBufferSource();
-        source.buffer = audioBuffer;
-        source.connect(this.audioContext.destination);
-
-        this.nextPlayTime = this.nextPlayTime || audioCurrentTime;
-
-        const chunk: AudioChunk = {
-            source: source,
-            playTime: this.nextPlayTime,
-            duration: audioBuffer.length / 44100.0
-        };
-
-        source.start(chunk.playTime);
-        this.nextPlayTime += chunk.duration;
-
-        this.audioChunks.push(chunk);
-
-        // ---- END OF THE AUDIO CODE
-
         // updates the next update time according to the number of ticks
         // that have elapsed since the last operation, this way this value
         // can better be used to control the game loop
@@ -573,6 +510,26 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
         return this.gameBoy?.frame_buffer_eager() ?? new Uint8Array();
     }
 
+    get audioSpecs(): AudioSpecs {
+        return {
+            samplingRate: 44100,
+            channels: 2
+        };
+    }
+
+    get audioBuffer(): Float32Array[] {
+        const audioBuffer = [];
+        const internalBuffer = this.gameBoy?.audio_buffer_eager(true) ?? [];
+        for (let channel = 0; channel < 2; channel++) {
+            const stream = new Float32Array(internalBuffer.length);
+            for (let index = 0; index < internalBuffer.length; index++) {
+                stream[index] = internalBuffer[index] / 100.0;
+            }
+            audioBuffer.push(stream);
+        }
+        return audioBuffer;
+    }
+
     get romInfo(): RomInfo {
         return {
             name: this.romName ?? undefined,
-- 
GitLab