Always have audio context and sound manager available.

This commit is contained in:
Pachira 2024-02-10 02:26:38 +00:00
parent 3bc595c3ee
commit fc58537974
9 changed files with 99 additions and 84 deletions

View file

@ -1,6 +1,5 @@
const MamiAudioBuffer = function(ctx, buffer) {
return {
getBuffer: () => buffer,
createSource: () => ctx.createSource(buffer),
};
};

View file

@ -5,53 +5,75 @@
const MamiAudioContext = function() {
const pub = {};
let volume = null;
let volume;
let isMuted = false;
const ctxArgs = { latencyHint: 'playback' };
let ctx = null;
let mainGain = null;
const init = () => {
let ctx, mainGain;
const onReset = [];
const reset = () => {
const audObj = window.AudioContext || window.webkitAudioContext;
try {
ctx = new audObj(ctxArgs);
} catch(ex) {
ctx = new audObj;
}
mainGain = ctx.createGain();
if(volume === null) {
if(volume === undefined) {
volume = mainGain.gain.defaultValue;
isMuted = false;
} else
mainGain.gain.value = isMuted ? 0 : volume;
mainGain.connect(ctx.destination);
for(const handler of onReset)
handler();
};
init();
reset();
pub.getContext = () => ctx;
pub.resetContext = init;
pub.getAudioContext = () => ctx;
pub.getAudioContextGain = () => mainGain;
pub.isReady = () => ctx !== undefined;
pub.reset = reset;
pub.onReset = handler => {
if(!onReset.includes(handler))
onReset.push(handler);
};
pub.getVolume = () => volume;
pub.setVolume = vol => {
volume = vol;
if(!isMuted)
if(!isMuted && mainGain !== undefined)
mainGain.gain.value = volume;
};
pub.isMuted = () => isMuted;
pub.setMuted = mute => {
mainGain.gain.value = (isMuted = mute) ? 0 : volume;
isMuted = mute;
if(mainGain !== undefined)
mainGain.gain.value = isMuted ? 0 : volume;
};
pub.createBuffer = async url => {
if(ctx === undefined)
return new MamiAudioBuffer(pub);
const result = await $x.get(url, { type: 'arraybuffer' });
const buffer = await ctx.decodeAudioData(result.body());
return new MamiAudioBuffer(pub, buffer);
};
pub.createSource = buffer => {
if(ctx === undefined)
return new MamiAudioSourceDummy;
const gain = ctx.createGain();
gain.connect(mainGain);

View file

@ -1,3 +1,19 @@
const MamiAudioSourceDummy = function() {
return {
play: async () => {},
stop: () => {},
getRate: () => 0,
setRate: rate => {},
getDetune: () => 0,
setDetune: rate => {},
getVolume: () => 0,
setVolume: volume => {},
isMuted: () => true,
setMuted: mute => {},
setLoop: (loop, start, end) => {},
};
};
const MamiAudioSource = function(source, gain, buffer) {
const pub = {};
@ -6,8 +22,6 @@ const MamiAudioSource = function(source, gain, buffer) {
let hasDisconnected = false;
pub.getSource = () => source;
const play = () => {
return new Promise(resolve => {
source.addEventListener('ended', () => resolve());

View file

@ -17,41 +17,20 @@ const MamiContext = function(targetBody) {
const viewsCtx = new MamiViewsControl({ body: targetBody });
pub.getViews = () => viewsCtx;
let audioCtx = null;
pub.hasAudio = function() { return audioCtx !== null; };
pub.getAudio = function() { return audioCtx; };
const initAudio = function() {
if(audioCtx !== null)
return;
const audioCtx = new MamiAudioContext;
pub.getAudio = () => audioCtx;
audioCtx = new MamiAudioContext;
};
pub.initAudio = initAudio;
const soundMgr = new MamiSoundManager(audioCtx);
const soundLib = new MamiSoundLibrary;
const soundPck = new MamiSoundPacks;
const sndPckPlay = new MamiSoundPackPlayer(soundMgr, soundLib);
let soundMgr = null;
let sndPckPlay = null;
const soundLib = new MamiSoundLibrary();
const soundPck = new MamiSoundPacks();
pub.initSound = function() {
if(soundMgr !== null)
return;
initAudio();
soundMgr = new MamiSoundManager(audioCtx);
sndPckPlay = new MamiSoundPackPlayer(soundMgr, soundLib);
};
pub.getSound = function() { return soundMgr; };
pub.hasSound = function() { return soundMgr !== null; };
pub.getSoundLibrary = function() { return soundLib; };
pub.getSoundPacks = function() { return soundPck; };
pub.getSoundPackPlayer = function() { return sndPckPlay; };
pub.getSound = () => soundMgr;
pub.getSoundLibrary = () => soundLib;
pub.getSoundPacks = () => soundPck;
pub.getSoundPackPlayer = () => sndPckPlay;
pub.playLibrarySound = async (name, volume, rate) => {
if(soundMgr === null)
return;
const buffer = await soundMgr.load(soundLib.getSoundSources(name));
const source = buffer.createSource();
@ -65,12 +44,12 @@ const MamiContext = function(targetBody) {
};
const txtTriggers = new MamiTextTriggers;
pub.getTextTriggers = function() { return txtTriggers; };
pub.getTextTriggers = () => txtTriggers;
let eeprom;
pub.hasEEPROM = () => eeprom !== undefined;
pub.getEEPROM = () => eeprom;
pub.createEEPROM = function() {
pub.createEEPROM = () => {
if(eeprom !== undefined)
return;

View file

@ -157,8 +157,10 @@ const Umi = { UI: {} };
}
settings.watch('soundEnable', (v, n, i) => {
if(v && !mami.hasSound()) {
mami.initSound();
if(v) {
const audio = mami.getAudio();
if(!audio.isReady())
audio.reset();
settings.touch('soundVolume');
settings.touch('soundPack');
@ -168,8 +170,7 @@ const Umi = { UI: {} };
player.playEvent('server');
}
if(mami.hasAudio())
mami.getAudio().setMuted(!v);
mami.getAudio().setMuted(!v);
});
settings.watch('soundPack', (v, n, i) => {
@ -187,8 +188,7 @@ const Umi = { UI: {} };
});
settings.watch('soundVolume', v => {
if(mami.hasAudio())
mami.getAudio().setVolume(v / 100);
mami.getAudio().setVolume(v / 100);
})
@ -248,10 +248,12 @@ const Umi = { UI: {} };
settings.watch('tmpDisableOldThemeSys', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); });
settings.watch('minecraft', (v, n, i) => {
if(i)
mami.playLibrarySound('minecraft:nether:enter');
else
Umi.Sound.Play('join');
if(v !== 'no') {
if(i)
mami.playLibrarySound('minecraft:nether:enter');
else
Umi.Sound.Play('join');
}
});
settings.watch('enableNotifications', v => {

View file

@ -105,9 +105,6 @@ Umi.Protocol.SockChat.Protocol = function(views, settings) {
await mami.playLibrarySound('touhou:pichuun');
};
const playBannedBgm = async preload => {
if(!mami.hasSound())
return;
const soundMgr = mami.getSound();
const soundLib = mami.getSoundLibrary();

View file

@ -1,6 +1,4 @@
#include utility.js
#include sound/sndlibrary.js
#include sound/sndpacks.js
const MamiSoundManager = function(context) {
const probablySupported = [];
@ -13,8 +11,6 @@ const MamiSoundManager = function(context) {
wav: 'audio/wav',
};
const loaded = new Map;
(() => {
const elem = $e('audio');
for(const name in formats) {
@ -28,9 +24,7 @@ const MamiSoundManager = function(context) {
}
})();
const pub = {};
const extractSupportedUrl = urls => {
const extractUrl = urls => {
if(typeof urls === 'object' && typeof urls.getSources === 'function')
urls = urls.getSources();
@ -48,25 +42,26 @@ const MamiSoundManager = function(context) {
throw 'No supported audio format could be determined.';
};
pub.load = async urls => {
const url = extractSupportedUrl(urls);
const loaded = new Map;
context.onReset(() => loaded.clear());
if(loaded.has(url))
return loaded.get(url);
return {
load: async urls => {
const url = extractUrl(urls);
const buffer = await context.createBuffer(url);
loaded.set(url, buffer);
if(loaded.has(url))
return loaded.get(url);
return buffer;
const buffer = await context.createBuffer(url);
loaded.set(url, buffer);
return buffer;
},
unload: urls => {
loaded.delete(extractUrl(urls));
},
reset: () => {
loaded.clear();
},
};
pub.unload = urls => {
loaded.delete(extractSupportedUrl(urls));
};
pub.reset = () => {
loaded.clear();
};
return pub;
};

View file

@ -5,7 +5,7 @@
Umi.Sound = (() => {
return {
Play: sound => {
if(!sound || sound === 'none' || !mami.hasSound())
if(!sound || sound === 'none')
return;
const settings = mami.getSettings();

View file

@ -396,6 +396,13 @@ Umi.UI.Settings = (function() {
button.disabled = false;
},
},
{
title: 'Reset audio context',
type: 'button',
invoke: async () => {
mami.getAudio().reset();
},
},
],
}
];