Skip to content
Snippets Groups Projects
Verified Commit 80146838 authored by João Magalhães's avatar João Magalhães :rocket:
Browse files

feat: great simplification of the audio code

Makes use of the new emukit audio code.
parent 83d0655e
No related branches found
No related tags found
1 merge request!19Initial tentative audio support 🔉
Pipeline #2300 passed
import { import {
AudioSpecs,
BenchmarkResult, BenchmarkResult,
Compilation, Compilation,
Compiler, Compiler,
...@@ -80,13 +81,6 @@ const KEYS_NAME: Record<string, number> = { ...@@ -80,13 +81,6 @@ const KEYS_NAME: Record<string, number> = {
const ROM_PATH = require("../../../res/roms/pocket.gb"); 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 * Top level class that controls the emulator behaviour
* and "joins" all the elements together to bring input/output * and "joins" all the elements together to bring input/output
...@@ -122,13 +116,6 @@ export class GameboyEmulator extends EmulatorBase implements Emulator { ...@@ -122,13 +116,6 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
private romSize = 0; private romSize = 0;
private cartridge: Cartridge | null = null; 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 * Associative map for extra settings to be used in
* opaque local storage operations, associated setting * opaque local storage operations, associated setting
...@@ -294,6 +281,11 @@ export class GameboyEmulator extends EmulatorBase implements Emulator { ...@@ -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 // increments the number of frames rendered in the current
// section, this value is going to be used to calculate FPS // section, this value is going to be used to calculate FPS
this.frameCount += 1; this.frameCount += 1;
...@@ -322,61 +314,6 @@ export class GameboyEmulator extends EmulatorBase implements Emulator { ...@@ -322,61 +314,6 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
); );
ticks = Math.max(ticks, 1); 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 // updates the next update time according to the number of ticks
// that have elapsed since the last operation, this way this value // that have elapsed since the last operation, this way this value
// can better be used to control the game loop // can better be used to control the game loop
...@@ -573,6 +510,26 @@ export class GameboyEmulator extends EmulatorBase implements Emulator { ...@@ -573,6 +510,26 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
return this.gameBoy?.frame_buffer_eager() ?? new Uint8Array(); 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 { get romInfo(): RomInfo {
return { return {
name: this.romName ?? undefined, name: this.romName ?? undefined,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment