Skip to content

Instantly share code, notes, and snippets.

@laytan
Created October 23, 2024 17:12
Show Gist options
  • Save laytan/7e076c697e2a5a083f8b9cc331989414 to your computer and use it in GitHub Desktop.
Save laytan/7e076c697e2a5a083f8b9cc331989414 to your computer and use it in GitHub Desktop.
Very simple sound abstraction in Odin over WebAudio and MiniAudio
<!DOCTYPE html>
<html lang="en" style="height: 100%;">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Yo</title>
</head>
<body id="body" style="height: 100%; padding: 0; margin: 0; overflow: hidden;">
<canvas id="wgpu-canvas"></canvas>
<script type="text/javascript" src="odin.js"></script>
<script type="text/javascript" src="sound.js"></script>
<script type="text/javascript">
const mem = new WebAssembly.Memory({ initial: 2000, maximum: 65536, shared: false });
const memInterface = new odin.WasmMemoryInterface();
memInterface.setMemory(mem);
const soundInterface = new odin.SoundInterface(memInterface);
odin.runWasm("module.wasm", null, {
sound: soundInterface.getInterface(),
}, memInterface, /*intSize=8*/);
</script>
</body>
</html>
(function() {
const BONK = 0;
const SCORE = 1;
class SoundInterface {
/**
* @param {WasmMemoryInterface} mem
*/
constructor(mem) {
this.mem = mem;
this.audioCtx = new AudioContext();
this.sounds = {};
}
async load(idx, filepath) {
try {
const response = await fetch(filepath);
const buffer = await this.audioCtx.decodeAudioData(await response.arrayBuffer());
this.sounds[idx] = buffer;
} catch (err) {
console.error("sound: load: " + filepath, err);
}
}
getInterface() {
return {
init: async () => {
this.load(BONK, "impactPlank_medium_000.ogg");
this.load(SCORE, "laserLarge_002.ogg");
},
play: (sound) => {
const source = this.audioCtx.createBufferSource();
source.buffer = this.sounds[sound];
source.connect(this.audioCtx.destination);
source.start();
},
};
}
}
window.odin = window.odin || {};
window.odin.SoundInterface = SoundInterface;
})();
package sound
Sound :: enum {
Bonk,
Score,
}
init :: proc() {
_init()
}
play :: proc(sound: Sound) {
_play(sound)
}
//+private
package sound
foreign import soundjs "sound"
foreign soundjs {
@(link_name="init")
_init :: proc "contextless" () ---
@(link_name="play")
_play :: proc "contextless" (sound: Sound) ---
}
//+build !js
//+private
package sound
import "core:fmt"
import "core:log"
import ma "vendor:miniaudio"
Engine :: struct {
daddy: ma.engine,
sounds: [Sound]ma.sound,
}
engine: Engine
_init :: proc() {
if result := ma.engine_init(nil, &engine.daddy); result != .SUCCESS {
fmt.panicf("sound: init: engine: %v", result)
}
flags := u32(ma.sound_flags.ASYNC | ma.sound_flags.DECODE)
if result := ma.sound_init_from_file(
&engine.daddy,
"impactPlank_medium_000.ogg",
flags, nil, nil,
&engine.sounds[.Bonk],
); result != .SUCCESS {
fmt.panicf("sound: init: bonk: %v", result)
}
if result := ma.sound_init_from_file(
&engine.daddy,
"laserLarge_002.ogg",
flags, nil, nil,
&engine.sounds[.Score],
); result != .SUCCESS {
fmt.panicf("sound: init: score: %v", result)
}
}
_play :: proc(sound: Sound) {
s := &engine.sounds[sound]
// if ma.sound_is_playing(s) && !ma.sound_at_end(s) {
// log.warnf("sound: play: %v: already playing", sound)
// return
// }
//
// ma.sound_set_start
if result := ma.sound_start(s); result != .SUCCESS {
log.errorf("sound: play: %v: %v", sound, result)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment