/* This file runs after the audio system is initiated */ Object.readonly(audio, 'samplerate'); Object.readonly(audio, 'channels'); Object.readonly(audio, 'buffer_frames'); var sources = []; audio.play = function(file,bus = audio.bus.master) { var filename = file; file = Resources.find_sound(file); if (!file) { console.error(`Cannot play sound ${file}: does not exist.`); return; } var src = audio.dsp.source(file); src.plugin(bus); src.guid = prosperon.guid(); src.name = file; src.type = "source"; sources.push(src); return src; } audio.bus = {}; audio.bus.master = dspsound.master(); audio.dsp = {}; audio.dsp = dspsound; audio.bus.master.__proto__.type = "bus"; audio.bus.master.name = "master"; var plugin_node = audio.bus.master.plugin; audio.bus.master.__proto__.plugin = function(to) { this.tos ??= []; this.tos.push(to); to.ins ??= []; to.ins.push(this); plugin_node.call(this, to); } var unplug_node = audio.bus.master.unplug; audio.bus.master.__proto__.unplug = function() { if (this.tos) { for (var node of this.tos) node.ins.remove(this); this.tos = []; } unplug_node.call(this); } audio.dsp.mix().__proto__.imgui = function() { imgui.pushid(this.memid()); this.volume = imgui.slider("Volume", this.volume); this.off = imgui.checkbox("Mute", this.off); imgui.popid(); } audio.cry = function(file, bus = audio.bus.sfx) { file = Resources.find_sound(file); var player = audio.play(file, bus); if (!player) return; player.ended = function() { player.unplug(); player = undefined; } return player.ended; } // This function is called when every audio source is finished var killer = Register.appupdate.register(function() { for (var src of sources) { if (!src.loop && (src.frame < src.lastframe || src.frame === src.frames())) { src.unplug(); src.ended?.(); } src.lastframe = src.frame; } }); var song; // Play 'file' for new song, cross fade for seconds audio.music = function(file, fade = 0.5) { if (!file) { if (song) song.volume = 0; return; } file = Resources.find_sound(file); if (!fade) { song = audio.play(file, audio.bus.music); song.loop = true; return; } if (!song) { song = audio.play(file, audio.bus.music); song.volume = 1; // tween(song,'volume', 1, fade); return; } var temp = audio.play(file, audio.bus.music); if (!temp) return; temp.volume = 1; var temp2 = song; // tween(temp, 'volume', 1, fade); // tween(temp2, 'volume', 0, fade); song = temp; song.loop = true; } audio.bus.music = audio.dsp.mix(); audio.bus.music.plugin(audio.bus.master); audio.bus.music.name = "music"; audio.bus.sfx = audio.dsp.mix(); audio.bus.sfx.plugin(audio.bus.master); audio.bus.sfx.name = "sfx"; audio.dsp.allpass = function(secs, decay) { var composite = {}; var fwd = audio.dsp.fwd_delay(secs,-decay); var fbk = audio.dsp.delay(secs,decay); composite.id = fwd.id; composite.plugin = composite.plugin.bind(fbk); composite.unplug = dsp_node.unplug.bind(fbk); fwd.plugin(fbk); return composite; } audio.dsp.doc = { delay: "Delays the input by secs, multiplied by decay", fwd_delay: "Forward feedback delays the input by secs, multiplied by decay", allpass: "Composite node of a delay and fwd_delay", lpf: "Low pass filter at a given frequency", hpf: "High pass filter at a given frequency", midi: "A source node for a midi file with a given soundfont file", crush: "Bitcrush the input to a given rate and bit depth", limiter: "Limit audio to ceil with pleasent rolloff", noise_gate: "Do not pass audio below the given floor", pitchshift: "Shift sound by octaves", noise: "Plain randon noise", pink: "Pink noise", red: "Red noise" }; audio.dsp.obscure('doc'); Object.mixin(audio.bus.master.__proto__, { get db() { return 20*Math.log10(Math.abs(this.volume)); }, set db(x) { x = Math.clamp(x,-100,0); this.volume = Math.pow(10, x/20); }, get volume() { return this.gain; }, set volume(x) { this.gain = x; }, }); audio.bus.master.__proto__.toJSON = function() { return { volume: this.volume, off: this.off, pan: this.pan, pass: this.pass }; } /*Object.mixin(audio.dsp.source().__proto__, { length() { return this.frames()/audio.samplerate(); }, time() { return this.frame/sound.samplerate(); }, pct() { return this.time()/this.length(); }, }); */ return {audio};