Always have audio context and sound manager available.
This commit is contained in:
parent
3bc595c3ee
commit
fc58537974
9 changed files with 99 additions and 84 deletions
|
@ -1,6 +1,5 @@
|
||||||
const MamiAudioBuffer = function(ctx, buffer) {
|
const MamiAudioBuffer = function(ctx, buffer) {
|
||||||
return {
|
return {
|
||||||
getBuffer: () => buffer,
|
|
||||||
createSource: () => ctx.createSource(buffer),
|
createSource: () => ctx.createSource(buffer),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,53 +5,75 @@
|
||||||
const MamiAudioContext = function() {
|
const MamiAudioContext = function() {
|
||||||
const pub = {};
|
const pub = {};
|
||||||
|
|
||||||
let volume = null;
|
let volume;
|
||||||
let isMuted = false;
|
let isMuted = false;
|
||||||
|
|
||||||
const ctxArgs = { latencyHint: 'playback' };
|
const ctxArgs = { latencyHint: 'playback' };
|
||||||
let ctx = null;
|
let ctx, mainGain;
|
||||||
let mainGain = null;
|
|
||||||
const init = () => {
|
const onReset = [];
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
const audObj = window.AudioContext || window.webkitAudioContext;
|
const audObj = window.AudioContext || window.webkitAudioContext;
|
||||||
try {
|
try {
|
||||||
ctx = new audObj(ctxArgs);
|
ctx = new audObj(ctxArgs);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
ctx = new audObj;
|
ctx = new audObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
mainGain = ctx.createGain();
|
mainGain = ctx.createGain();
|
||||||
|
|
||||||
if(volume === null) {
|
if(volume === undefined) {
|
||||||
volume = mainGain.gain.defaultValue;
|
volume = mainGain.gain.defaultValue;
|
||||||
isMuted = false;
|
isMuted = false;
|
||||||
} else
|
} else
|
||||||
mainGain.gain.value = isMuted ? 0 : volume;
|
mainGain.gain.value = isMuted ? 0 : volume;
|
||||||
|
|
||||||
mainGain.connect(ctx.destination);
|
mainGain.connect(ctx.destination);
|
||||||
|
|
||||||
|
for(const handler of onReset)
|
||||||
|
handler();
|
||||||
};
|
};
|
||||||
|
|
||||||
init();
|
reset();
|
||||||
|
|
||||||
pub.getContext = () => ctx;
|
pub.getAudioContext = () => ctx;
|
||||||
pub.resetContext = init;
|
pub.getAudioContextGain = () => mainGain;
|
||||||
|
|
||||||
|
pub.isReady = () => ctx !== undefined;
|
||||||
|
pub.reset = reset;
|
||||||
|
|
||||||
|
pub.onReset = handler => {
|
||||||
|
if(!onReset.includes(handler))
|
||||||
|
onReset.push(handler);
|
||||||
|
};
|
||||||
|
|
||||||
pub.getVolume = () => volume;
|
pub.getVolume = () => volume;
|
||||||
pub.setVolume = vol => {
|
pub.setVolume = vol => {
|
||||||
volume = vol;
|
volume = vol;
|
||||||
if(!isMuted)
|
if(!isMuted && mainGain !== undefined)
|
||||||
mainGain.gain.value = volume;
|
mainGain.gain.value = volume;
|
||||||
};
|
};
|
||||||
pub.isMuted = () => isMuted;
|
pub.isMuted = () => isMuted;
|
||||||
pub.setMuted = mute => {
|
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 => {
|
pub.createBuffer = async url => {
|
||||||
|
if(ctx === undefined)
|
||||||
|
return new MamiAudioBuffer(pub);
|
||||||
|
|
||||||
const result = await $x.get(url, { type: 'arraybuffer' });
|
const result = await $x.get(url, { type: 'arraybuffer' });
|
||||||
const buffer = await ctx.decodeAudioData(result.body());
|
const buffer = await ctx.decodeAudioData(result.body());
|
||||||
return new MamiAudioBuffer(pub, buffer);
|
return new MamiAudioBuffer(pub, buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
pub.createSource = buffer => {
|
pub.createSource = buffer => {
|
||||||
|
if(ctx === undefined)
|
||||||
|
return new MamiAudioSourceDummy;
|
||||||
|
|
||||||
const gain = ctx.createGain();
|
const gain = ctx.createGain();
|
||||||
gain.connect(mainGain);
|
gain.connect(mainGain);
|
||||||
|
|
||||||
|
|
|
@ -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 MamiAudioSource = function(source, gain, buffer) {
|
||||||
const pub = {};
|
const pub = {};
|
||||||
|
|
||||||
|
@ -6,8 +22,6 @@ const MamiAudioSource = function(source, gain, buffer) {
|
||||||
|
|
||||||
let hasDisconnected = false;
|
let hasDisconnected = false;
|
||||||
|
|
||||||
pub.getSource = () => source;
|
|
||||||
|
|
||||||
const play = () => {
|
const play = () => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
source.addEventListener('ended', () => resolve());
|
source.addEventListener('ended', () => resolve());
|
||||||
|
|
|
@ -17,41 +17,20 @@ const MamiContext = function(targetBody) {
|
||||||
const viewsCtx = new MamiViewsControl({ body: targetBody });
|
const viewsCtx = new MamiViewsControl({ body: targetBody });
|
||||||
pub.getViews = () => viewsCtx;
|
pub.getViews = () => viewsCtx;
|
||||||
|
|
||||||
let audioCtx = null;
|
const audioCtx = new MamiAudioContext;
|
||||||
pub.hasAudio = function() { return audioCtx !== null; };
|
pub.getAudio = () => audioCtx;
|
||||||
pub.getAudio = function() { return audioCtx; };
|
|
||||||
const initAudio = function() {
|
|
||||||
if(audioCtx !== null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
audioCtx = new MamiAudioContext;
|
const soundMgr = new MamiSoundManager(audioCtx);
|
||||||
};
|
const soundLib = new MamiSoundLibrary;
|
||||||
pub.initAudio = initAudio;
|
const soundPck = new MamiSoundPacks;
|
||||||
|
const sndPckPlay = new MamiSoundPackPlayer(soundMgr, soundLib);
|
||||||
|
|
||||||
let soundMgr = null;
|
pub.getSound = () => soundMgr;
|
||||||
let sndPckPlay = null;
|
pub.getSoundLibrary = () => soundLib;
|
||||||
const soundLib = new MamiSoundLibrary();
|
pub.getSoundPacks = () => soundPck;
|
||||||
const soundPck = new MamiSoundPacks();
|
pub.getSoundPackPlayer = () => sndPckPlay;
|
||||||
|
|
||||||
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.playLibrarySound = async (name, volume, rate) => {
|
pub.playLibrarySound = async (name, volume, rate) => {
|
||||||
if(soundMgr === null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const buffer = await soundMgr.load(soundLib.getSoundSources(name));
|
const buffer = await soundMgr.load(soundLib.getSoundSources(name));
|
||||||
const source = buffer.createSource();
|
const source = buffer.createSource();
|
||||||
|
|
||||||
|
@ -65,12 +44,12 @@ const MamiContext = function(targetBody) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const txtTriggers = new MamiTextTriggers;
|
const txtTriggers = new MamiTextTriggers;
|
||||||
pub.getTextTriggers = function() { return txtTriggers; };
|
pub.getTextTriggers = () => txtTriggers;
|
||||||
|
|
||||||
let eeprom;
|
let eeprom;
|
||||||
pub.hasEEPROM = () => eeprom !== undefined;
|
pub.hasEEPROM = () => eeprom !== undefined;
|
||||||
pub.getEEPROM = () => eeprom;
|
pub.getEEPROM = () => eeprom;
|
||||||
pub.createEEPROM = function() {
|
pub.createEEPROM = () => {
|
||||||
if(eeprom !== undefined)
|
if(eeprom !== undefined)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -157,8 +157,10 @@ const Umi = { UI: {} };
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.watch('soundEnable', (v, n, i) => {
|
settings.watch('soundEnable', (v, n, i) => {
|
||||||
if(v && !mami.hasSound()) {
|
if(v) {
|
||||||
mami.initSound();
|
const audio = mami.getAudio();
|
||||||
|
if(!audio.isReady())
|
||||||
|
audio.reset();
|
||||||
|
|
||||||
settings.touch('soundVolume');
|
settings.touch('soundVolume');
|
||||||
settings.touch('soundPack');
|
settings.touch('soundPack');
|
||||||
|
@ -168,8 +170,7 @@ const Umi = { UI: {} };
|
||||||
player.playEvent('server');
|
player.playEvent('server');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mami.hasAudio())
|
mami.getAudio().setMuted(!v);
|
||||||
mami.getAudio().setMuted(!v);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
settings.watch('soundPack', (v, n, i) => {
|
settings.watch('soundPack', (v, n, i) => {
|
||||||
|
@ -187,8 +188,7 @@ const Umi = { UI: {} };
|
||||||
});
|
});
|
||||||
|
|
||||||
settings.watch('soundVolume', v => {
|
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('tmpDisableOldThemeSys', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); });
|
||||||
|
|
||||||
settings.watch('minecraft', (v, n, i) => {
|
settings.watch('minecraft', (v, n, i) => {
|
||||||
if(i)
|
if(v !== 'no') {
|
||||||
mami.playLibrarySound('minecraft:nether:enter');
|
if(i)
|
||||||
else
|
mami.playLibrarySound('minecraft:nether:enter');
|
||||||
Umi.Sound.Play('join');
|
else
|
||||||
|
Umi.Sound.Play('join');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
settings.watch('enableNotifications', v => {
|
settings.watch('enableNotifications', v => {
|
||||||
|
|
|
@ -105,9 +105,6 @@ Umi.Protocol.SockChat.Protocol = function(views, settings) {
|
||||||
await mami.playLibrarySound('touhou:pichuun');
|
await mami.playLibrarySound('touhou:pichuun');
|
||||||
};
|
};
|
||||||
const playBannedBgm = async preload => {
|
const playBannedBgm = async preload => {
|
||||||
if(!mami.hasSound())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const soundMgr = mami.getSound();
|
const soundMgr = mami.getSound();
|
||||||
const soundLib = mami.getSoundLibrary();
|
const soundLib = mami.getSoundLibrary();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#include utility.js
|
#include utility.js
|
||||||
#include sound/sndlibrary.js
|
|
||||||
#include sound/sndpacks.js
|
|
||||||
|
|
||||||
const MamiSoundManager = function(context) {
|
const MamiSoundManager = function(context) {
|
||||||
const probablySupported = [];
|
const probablySupported = [];
|
||||||
|
@ -13,8 +11,6 @@ const MamiSoundManager = function(context) {
|
||||||
wav: 'audio/wav',
|
wav: 'audio/wav',
|
||||||
};
|
};
|
||||||
|
|
||||||
const loaded = new Map;
|
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
const elem = $e('audio');
|
const elem = $e('audio');
|
||||||
for(const name in formats) {
|
for(const name in formats) {
|
||||||
|
@ -28,9 +24,7 @@ const MamiSoundManager = function(context) {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const pub = {};
|
const extractUrl = urls => {
|
||||||
|
|
||||||
const extractSupportedUrl = urls => {
|
|
||||||
if(typeof urls === 'object' && typeof urls.getSources === 'function')
|
if(typeof urls === 'object' && typeof urls.getSources === 'function')
|
||||||
urls = urls.getSources();
|
urls = urls.getSources();
|
||||||
|
|
||||||
|
@ -48,25 +42,26 @@ const MamiSoundManager = function(context) {
|
||||||
throw 'No supported audio format could be determined.';
|
throw 'No supported audio format could be determined.';
|
||||||
};
|
};
|
||||||
|
|
||||||
pub.load = async urls => {
|
const loaded = new Map;
|
||||||
const url = extractSupportedUrl(urls);
|
context.onReset(() => loaded.clear());
|
||||||
|
|
||||||
if(loaded.has(url))
|
return {
|
||||||
return loaded.get(url);
|
load: async urls => {
|
||||||
|
const url = extractUrl(urls);
|
||||||
|
|
||||||
const buffer = await context.createBuffer(url);
|
if(loaded.has(url))
|
||||||
loaded.set(url, buffer);
|
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;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
Umi.Sound = (() => {
|
Umi.Sound = (() => {
|
||||||
return {
|
return {
|
||||||
Play: sound => {
|
Play: sound => {
|
||||||
if(!sound || sound === 'none' || !mami.hasSound())
|
if(!sound || sound === 'none')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const settings = mami.getSettings();
|
const settings = mami.getSettings();
|
||||||
|
|
|
@ -396,6 +396,13 @@ Umi.UI.Settings = (function() {
|
||||||
button.disabled = false;
|
button.disabled = false;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Reset audio context',
|
||||||
|
type: 'button',
|
||||||
|
invoke: async () => {
|
||||||
|
mami.getAudio().reset();
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
Loading…
Reference in a new issue