Compare commits
9 commits
af2919f363
...
a862eca354
Author | SHA1 | Date | |
---|---|---|---|
a862eca354 | |||
11fe3ebcd1 | |||
af09fc7716 | |||
a50a0d67ae | |||
fc58537974 | |||
3bc595c3ee | |||
fd41509c74 | |||
149cdac8ed | |||
719e03e607 |
19 changed files with 451 additions and 821 deletions
9
build.js
9
build.js
|
@ -5,6 +5,7 @@ const path = require('path');
|
|||
const util = require('util');
|
||||
const postcss = require('postcss');
|
||||
const htmlminify = require('html-minifier-terser').minify;
|
||||
const childProcess = require('child_process');
|
||||
const utils = require('./src/utils.js');
|
||||
const assproc = require('./src/assproc.js');
|
||||
|
||||
|
@ -102,6 +103,14 @@ const htmlMinifyOptions = {
|
|||
FUTAMI_URL: config.common_url,
|
||||
MAMI_URL: config.modern_url,
|
||||
AMI_URL: config.compat_url,
|
||||
GIT_HASH: await (() => {
|
||||
return new Promise((resolve, reject) => {
|
||||
childProcess.exec('git log --pretty="%H" -n1 HEAD', (err, stdout) => {
|
||||
if(err) reject(err);
|
||||
else resolve(stdout.trim());
|
||||
});
|
||||
});
|
||||
})(),
|
||||
};
|
||||
|
||||
console.log('Ensuring assets directory exists...');
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
<div class="noscript-icon"><i class="fas fa-exclamation-triangle fa-3x"></i></div>
|
||||
<div class="noscript-header">Enable Javascript!</div>
|
||||
<div class="noscript-body">
|
||||
<p>{title} is a web based chat and requires Javascript to work.</p>
|
||||
<p>If you use any other privacy tools that prevent loading content from other domains, you may also need to white list <code>static.flash.moe</code> as many resources are loaded from there.</p>
|
||||
<p>{title} is a web based chat application and requires Javascript to work.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include easings.js
|
||||
|
||||
const MamiAnimate = function(info) {
|
||||
// This probably need a bit of a rewrite at some point to allow starting for arbitrary time points
|
||||
const MamiAnimate = info => {
|
||||
if(typeof info !== 'object')
|
||||
throw 'info must be an object';
|
||||
|
||||
|
@ -54,7 +55,7 @@ const MamiAnimate = function(info) {
|
|||
tCompletion = 0,
|
||||
started = !delayed;
|
||||
|
||||
const update = function(tCurrent) {
|
||||
const update = tCurrent => {
|
||||
if(tStart === undefined) {
|
||||
tStart = tCurrent;
|
||||
if(onStart !== undefined)
|
||||
|
@ -116,12 +117,8 @@ const MamiAnimate = function(info) {
|
|||
}
|
||||
|
||||
return {
|
||||
getCompletion: function() {
|
||||
return tCompletion;
|
||||
},
|
||||
getRawCompletion: function() {
|
||||
return tRawCompletion;
|
||||
},
|
||||
getCompletion: () => tCompletion,
|
||||
getRawCompletion: () => tRawCompletion,
|
||||
start: () => {
|
||||
if(!started) {
|
||||
started = true;
|
||||
|
@ -131,7 +128,7 @@ const MamiAnimate = function(info) {
|
|||
if(promise !== undefined)
|
||||
return promise;
|
||||
},
|
||||
cancel: function() {
|
||||
cancel: () => {
|
||||
cancel = true;
|
||||
},
|
||||
};
|
||||
|
|
23
src/mami.js/audio/autoplay.js
Normal file
23
src/mami.js/audio/autoplay.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include srle.js
|
||||
#include utility.js
|
||||
|
||||
const MamiDetectAutoPlaySource = '/+NIxA!!FhpbmcA#PA$wA@fgAV$#q$#/$#A#OUxBTUUzLjEwMAIeA!)UCCQC8CIA@gA@H49wpKWgA!%&/+MYxA$NIA$ExBTUUzLjEwMFV^%/+MYxDsA@NIA$FV&&/+MYxHYA@NIA$FV&&';
|
||||
|
||||
const MamiDetectAutoPlay = async () => {
|
||||
try {
|
||||
const audio = $e('audio', { src: 'data:audio/mpeg;base64,' + MamiSRLE.decode(MamiDetectAutoPlaySource) });
|
||||
|
||||
try {
|
||||
await audio.play();
|
||||
} catch(ex) {
|
||||
if('name' in ex && ex.name !== 'NotAllowedError' && ex.name !== 'AbortError') {
|
||||
console.error(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch(ex) {}
|
||||
|
||||
return true;
|
||||
};
|
|
@ -1,10 +0,0 @@
|
|||
const MamiAudioBuffer = function(ctx, buffer) {
|
||||
return {
|
||||
getBuffer: function() {
|
||||
return buffer;
|
||||
},
|
||||
createSource: function() {
|
||||
return ctx.createSource(buffer);
|
||||
},
|
||||
};
|
||||
};
|
|
@ -1,106 +1,83 @@
|
|||
#include srle.js
|
||||
#include utility.js
|
||||
#include audio/buffer.js
|
||||
#include audio/source.js
|
||||
|
||||
const MamiDetectAutoPlaySource = '/+NIxA!!FhpbmcA#PA$wA@fgAV$#q$#/$#A#OUxBTUUzLjEwMAIeA!)UCCQC8CIA@gA@H49wpKWgA!%&/+MYxA$NIA$ExBTUUzLjEwMFV^%/+MYxDsA@NIA$FV&&/+MYxHYA@NIA$FV&&';
|
||||
|
||||
const MamiDetectAutoPlay = async () => {
|
||||
try {
|
||||
const audio = $e('audio', { src: 'data:audio/mpeg;base64,' + MamiSRLE.decode(MamiDetectAutoPlaySource) });
|
||||
|
||||
try {
|
||||
await audio.play();
|
||||
} catch(ex) {
|
||||
if('name' in ex && ex.name !== 'NotAllowedError' && ex.name !== 'AbortError') {
|
||||
console.error(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch(ex) {}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const MamiAudioContext = function() {
|
||||
const pub = {};
|
||||
|
||||
let volume = null,
|
||||
isMuted = false;
|
||||
let volume;
|
||||
let isMuted = false;
|
||||
|
||||
const ctxArgs = { latencyHint: 'playback' };
|
||||
let ctx = null,
|
||||
mainGain = null;
|
||||
const init = function() {
|
||||
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();
|
||||
pub.getContext = function() { return ctx; };
|
||||
pub.resetContext = init;
|
||||
reset();
|
||||
|
||||
pub.useGain = function(callback) {
|
||||
callback.call(pub, mainGain.gain);
|
||||
};
|
||||
return {
|
||||
getAudioContext: () => ctx,
|
||||
getAudioContextGain: () => mainGain,
|
||||
|
||||
pub.getVolume = function() {
|
||||
return volume;
|
||||
};
|
||||
pub.setVolume = function(vol) {
|
||||
volume = vol;
|
||||
if(!isMuted)
|
||||
mainGain.gain.value = volume;
|
||||
};
|
||||
pub.isMuted = function() {
|
||||
return isMuted;
|
||||
};
|
||||
pub.setMuted = function(mute) {
|
||||
mainGain.gain.value = (isMuted = mute) ? 0 : volume;
|
||||
};
|
||||
isReady: () => ctx !== undefined,
|
||||
reset: reset,
|
||||
onReset: handler => {
|
||||
if(!onReset.includes(handler))
|
||||
onReset.push(handler);
|
||||
},
|
||||
|
||||
pub.createBuffer = function(url, callback) {
|
||||
$x.get(url, { type: 'arraybuffer' })
|
||||
.then(resp => {
|
||||
try {
|
||||
ctx.decodeAudioData(
|
||||
resp.body(),
|
||||
buffer => callback.call(pub, true, new MamiAudioBuffer(pub, buffer)),
|
||||
error => callback.call(pub, false, error)
|
||||
);
|
||||
} catch(ex) {
|
||||
callback.call(pub, false, ex);
|
||||
}
|
||||
})
|
||||
.catch(err => callback.call(pub, false, err));
|
||||
getVolume: () => volume,
|
||||
setVolume: vol => {
|
||||
volume = vol;
|
||||
if(!isMuted && mainGain !== undefined)
|
||||
mainGain.gain.value = volume;
|
||||
},
|
||||
isMuted: () => isMuted,
|
||||
setMuted: mute => {
|
||||
isMuted = mute;
|
||||
if(mainGain !== undefined)
|
||||
mainGain.gain.value = isMuted ? 0 : volume;
|
||||
},
|
||||
|
||||
createBuffer: async url => {
|
||||
if(ctx === undefined)
|
||||
return undefined;
|
||||
|
||||
const result = await $x.get(url, { type: 'arraybuffer' });
|
||||
return await ctx.decodeAudioData(result.body());
|
||||
},
|
||||
|
||||
createSource: buffer => {
|
||||
if(ctx === undefined || buffer === undefined)
|
||||
return new MamiAudioSourceDummy;
|
||||
|
||||
const gain = ctx.createGain();
|
||||
gain.connect(mainGain);
|
||||
|
||||
const source = ctx.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.connect(gain);
|
||||
|
||||
return new MamiAudioSource(source, gain);
|
||||
},
|
||||
};
|
||||
|
||||
const createSource = function(buffer) {
|
||||
const gain = ctx.createGain();
|
||||
gain.connect(mainGain);
|
||||
|
||||
const source = ctx.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.connect(gain);
|
||||
|
||||
return new MamiAudioSource(source, gain);
|
||||
};
|
||||
pub.createSource = createSource;
|
||||
|
||||
return pub;
|
||||
};
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
const MamiAudioSource = function(source, gain, buffer) {
|
||||
const pub = {};
|
||||
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) {
|
||||
let volume = 1,
|
||||
isMuted = false;
|
||||
|
||||
let hasDisconnected = false;
|
||||
|
||||
pub.getSource = function() { return source; };
|
||||
|
||||
const play = function() {
|
||||
source.start();
|
||||
};
|
||||
pub.play = play;
|
||||
|
||||
const disconnect = function() {
|
||||
const disconnect = () => {
|
||||
if(hasDisconnected)
|
||||
return;
|
||||
hasDisconnected = true;
|
||||
|
@ -22,85 +29,58 @@ const MamiAudioSource = function(source, gain, buffer) {
|
|||
source.disconnect();
|
||||
};
|
||||
|
||||
const stop = function() {
|
||||
source.stop();
|
||||
disconnect();
|
||||
};
|
||||
pub.stop = stop;
|
||||
source.addEventListener('ended', () => disconnect());
|
||||
|
||||
pub.useGain = function(callback) {
|
||||
callback.call(pub, gain.gain);
|
||||
};
|
||||
pub.usePlaybackRate = function(callback) {
|
||||
callback.call(pub, source.playbackRate);
|
||||
};
|
||||
pub.useDetune = function(callback) {
|
||||
callback.call(pub, source.detune);
|
||||
};
|
||||
return {
|
||||
play: () => {
|
||||
return new Promise(resolve => {
|
||||
source.addEventListener('ended', () => resolve());
|
||||
source.start();
|
||||
});
|
||||
},
|
||||
stop: () => {
|
||||
source.stop();
|
||||
disconnect();
|
||||
},
|
||||
|
||||
pub.getRate = function(rate) {
|
||||
return source.playbackRate.value;
|
||||
};
|
||||
pub.setRate = function(rate) {
|
||||
source.playbackRate.value = Math.min(
|
||||
source.playbackRate.maxValue,
|
||||
Math.max(
|
||||
source.playbackRate.minValue,
|
||||
rate
|
||||
)
|
||||
);
|
||||
};
|
||||
pub.getDetune = function(rate) {
|
||||
return source.detune.value;
|
||||
};
|
||||
pub.setDetune = function(rate) {
|
||||
source.detune.value = Math.min(
|
||||
source.detune.maxValue,
|
||||
Math.max(
|
||||
source.detune.minValue,
|
||||
rate
|
||||
)
|
||||
);
|
||||
};
|
||||
getRate: () => source.playbackRate.value,
|
||||
setRate: rate => {
|
||||
source.playbackRate.value = Math.min(
|
||||
source.playbackRate.maxValue,
|
||||
Math.max(
|
||||
source.playbackRate.minValue,
|
||||
rate
|
||||
)
|
||||
);
|
||||
},
|
||||
getDetune: () => source.detune.value,
|
||||
setDetune: rate => {
|
||||
source.detune.value = Math.min(
|
||||
source.detune.maxValue,
|
||||
Math.max(
|
||||
source.detune.minValue,
|
||||
rate
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
pub.getVolume = function() {
|
||||
return volume;
|
||||
};
|
||||
pub.setVolume = function(vol) {
|
||||
volume = vol;
|
||||
if(!isMuted)
|
||||
gain.gain.value = volume;
|
||||
};
|
||||
pub.isMuted = function() {
|
||||
return isMuted;
|
||||
};
|
||||
pub.setMuted = function(mute) {
|
||||
gain.gain.value = (isMuted = mute) ? 0 : volume;
|
||||
};
|
||||
getVolume: () => volume,
|
||||
setVolume: vol => {
|
||||
volume = vol;
|
||||
if(!isMuted)
|
||||
gain.gain.value = volume;
|
||||
},
|
||||
isMuted: () => isMuted,
|
||||
setMuted: mute => {
|
||||
gain.gain.value = (isMuted = mute) ? 0 : volume;
|
||||
},
|
||||
|
||||
pub.setLoop = function(loop) {
|
||||
if(typeof loop !== 'boolean')
|
||||
loop = true;
|
||||
source.loop = loop;
|
||||
setLoop: (loop, start, end) => {
|
||||
if(typeof start === 'number')
|
||||
source.loopStart = start;
|
||||
if(typeof end === 'number')
|
||||
source.loopEnd = end;
|
||||
source.loop = !!loop;
|
||||
},
|
||||
};
|
||||
pub.setLoopStart = function(start) {
|
||||
if(typeof start !== 'number')
|
||||
start = 0;
|
||||
source.loopStart = start;
|
||||
};
|
||||
pub.setLoopEnd = function(end) {
|
||||
if(typeof end !== 'number')
|
||||
end = 0;
|
||||
source.loopEnd = end;
|
||||
};
|
||||
|
||||
pub.whenEnded = function(handler) {
|
||||
source.addEventListener('ended', handler.bind(pub));
|
||||
};
|
||||
|
||||
source.addEventListener('ended', function() {
|
||||
disconnect();
|
||||
});
|
||||
|
||||
return pub;
|
||||
};
|
||||
|
|
|
@ -17,141 +17,26 @@ 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(soundMgr);
|
||||
const soundPck = new MamiSoundPacks;
|
||||
const sndPckPlay = new MamiSoundPackPlayer(soundLib);
|
||||
|
||||
let soundMgr = null, sndPckPlay = null;
|
||||
const soundLib = new MamiSoundLibrary(),
|
||||
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.playUrlSound = function(soundSources, complete, volume, rate) {
|
||||
if(soundMgr === null)
|
||||
return;
|
||||
|
||||
const hasCallback = typeof complete === 'function';
|
||||
|
||||
try {
|
||||
const soundUrl = soundMgr.findSupportedUrl(soundSources);
|
||||
if(soundUrl === null)
|
||||
return;
|
||||
|
||||
const soundName = 'MamiCtx:' + soundUrl;
|
||||
|
||||
if(soundMgr.isLoaded(soundName)) {
|
||||
const source = soundMgr.get(soundName);
|
||||
if(hasCallback)
|
||||
source.whenEnded(function() {
|
||||
complete();
|
||||
});
|
||||
if(typeof volume === 'number')
|
||||
source.setVolume(volume);
|
||||
if(typeof rate === 'number')
|
||||
source.setRate(rate);
|
||||
source.play();
|
||||
} else {
|
||||
soundMgr.load(soundName, soundUrl, function(success, buffer) {
|
||||
if(success) {
|
||||
const source = buffer.createSource();
|
||||
if(hasCallback)
|
||||
source.whenEnded(function() {
|
||||
complete();
|
||||
});
|
||||
if(typeof volume === 'number')
|
||||
source.setVolume(volume);
|
||||
if(typeof rate === 'number')
|
||||
source.setRate(rate);
|
||||
source.play();
|
||||
} else {
|
||||
console.error(buffer);
|
||||
if(hasCallback)
|
||||
complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch(ex) {
|
||||
console.error(ex);
|
||||
if(hasCallback)
|
||||
complete();
|
||||
}
|
||||
};
|
||||
|
||||
pub.playLibrarySound = function(soundName, complete, volume, rate) {
|
||||
if(soundMgr === null)
|
||||
return;
|
||||
|
||||
const hasCallback = typeof complete === 'function';
|
||||
|
||||
try {
|
||||
const soundInfo = soundLib.getSound(soundName);
|
||||
|
||||
if(soundMgr.isLoaded(soundName)) {
|
||||
const source = soundMgr.get(soundName);
|
||||
if(hasCallback)
|
||||
source.whenEnded(function() {
|
||||
complete();
|
||||
});
|
||||
if(typeof volume === 'number')
|
||||
source.setVolume(volume);
|
||||
if(typeof rate === 'number')
|
||||
source.setRate(rate);
|
||||
source.play();
|
||||
} else {
|
||||
soundMgr.load(soundName, soundInfo.getSources(), function(success, buffer) {
|
||||
if(success) {
|
||||
const source = buffer.createSource();
|
||||
if(hasCallback)
|
||||
source.whenEnded(function() {
|
||||
complete();
|
||||
});
|
||||
if(typeof volume === 'number')
|
||||
source.setVolume(volume);
|
||||
if(typeof rate === 'number')
|
||||
source.setRate(rate);
|
||||
source.play();
|
||||
} else {
|
||||
console.error(buffer);
|
||||
if(hasCallback)
|
||||
complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch(ex) {
|
||||
console.error(ex);
|
||||
if(hasCallback)
|
||||
complete();
|
||||
}
|
||||
};
|
||||
pub.getSound = () => soundMgr;
|
||||
pub.getSoundLibrary = () => soundLib;
|
||||
pub.getSoundPacks = () => soundPck;
|
||||
pub.getSoundPackPlayer = () => sndPckPlay;
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ const Umi = { UI: {} };
|
|||
#include server.js
|
||||
#include utility.js
|
||||
#include weeb.js
|
||||
#include audio/autoplay.js
|
||||
#include audio/context.js
|
||||
#include eeprom/eeprom.js
|
||||
#include settings/backup.js
|
||||
|
@ -143,9 +144,9 @@ const Umi = { UI: {} };
|
|||
try {
|
||||
const sounds = await futami.getJson('sounds2');
|
||||
if(Array.isArray(sounds.library))
|
||||
sndLib.loadSounds(sounds.library, true);
|
||||
sndLib.register(sounds.library, true);
|
||||
if(Array.isArray(sounds.packs))
|
||||
sndPacks.loadPacks(sounds.packs, true);
|
||||
sndPacks.register(sounds.packs, true);
|
||||
} catch(ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
|
@ -156,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');
|
||||
|
@ -167,27 +170,23 @@ const Umi = { UI: {} };
|
|||
player.playEvent('server');
|
||||
}
|
||||
|
||||
if(mami.hasAudio())
|
||||
mami.getAudio().setMuted(!v);
|
||||
mami.getAudio().setMuted(!v);
|
||||
});
|
||||
|
||||
settings.watch('soundPack', (v, n, i) => {
|
||||
const packs = mami.getSoundPacks();
|
||||
if(!packs.hasPack(v)) {
|
||||
if(!packs.has(v)) {
|
||||
settings.delete(n);
|
||||
return;
|
||||
}
|
||||
|
||||
const player = mami.getSoundPackPlayer();
|
||||
if(player !== null) {
|
||||
player.loadPack(packs.getPack(v));
|
||||
if(!i) player.playEvent('server');
|
||||
}
|
||||
player.loadPack(packs.get(v));
|
||||
if(!i) player.playEvent('server');
|
||||
});
|
||||
|
||||
settings.watch('soundVolume', v => {
|
||||
if(mami.hasAudio())
|
||||
mami.getAudio().setVolume(v / 100);
|
||||
mami.getAudio().setVolume(v / 100);
|
||||
})
|
||||
|
||||
|
||||
|
@ -246,16 +245,13 @@ const Umi = { UI: {} };
|
|||
settings.watch('preventOverflow', v => document.body.classList.toggle('prevent-overflow', v));
|
||||
settings.watch('tmpDisableOldThemeSys', (v, n, i) => { if(!i) Umi.UI.View.AccentReload(); });
|
||||
|
||||
const mcPortalSnd = 'minecraft:nether:enter';
|
||||
if(settings.get('minecraft') !== 'no' && ctx.hasSound() && sndLib.hasSound(mcPortalSnd))
|
||||
ctx.getSound().load(mcPortalSnd, sndLib.getSound(mcPortalSnd).getSources(), function(success, buffer) {
|
||||
if(success)
|
||||
buffer.createSource().play();
|
||||
});
|
||||
|
||||
settings.watch('minecraft', (v, n, i) => {
|
||||
if(!i)
|
||||
Umi.Sound.Play('join');
|
||||
if(v !== 'no') {
|
||||
if(i)
|
||||
sndLib.play('minecraft:nether:enter');
|
||||
else
|
||||
Umi.Sound.Play('join');
|
||||
}
|
||||
});
|
||||
|
||||
settings.watch('enableNotifications', v => {
|
||||
|
@ -406,7 +402,7 @@ const Umi = { UI: {} };
|
|||
$r(explode);
|
||||
}, 1700);
|
||||
|
||||
ctx.playLibrarySound('misc:explode');
|
||||
sndLib.play('misc:explode');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -101,47 +101,21 @@ Umi.Protocol.SockChat.Protocol = function(views, settings) {
|
|||
return loading;
|
||||
};
|
||||
|
||||
const playBannedSfx = function() {
|
||||
if(!mami.hasSound())
|
||||
return;
|
||||
|
||||
const urls = {
|
||||
mp3: '//static.flash.moe/sounds/touhou-death.mp3',
|
||||
ogg: '//static.flash.moe/sounds/touhou-death.ogg'
|
||||
};
|
||||
|
||||
mami.getSound().load('banSFX', urls, function(success, buffer) {
|
||||
if(!success) {
|
||||
console.log('Failed to load kick/ban SFX: ' + buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.createSource().play();
|
||||
});
|
||||
const playBannedSfx = async () => {
|
||||
await mami.getSoundLibrary().play('touhou:pichuun');
|
||||
};
|
||||
const playBannedBgm = function(preload) {
|
||||
if(!mami.hasSound())
|
||||
const playBannedBgm = async preload => {
|
||||
const name = 'touhou:th10score';
|
||||
const soundLib = mami.getSoundLibrary();
|
||||
|
||||
if(preload) {
|
||||
await soundLib.loadBuffer(name);
|
||||
return;
|
||||
}
|
||||
|
||||
const urls = {
|
||||
opus: '//static.flash.moe/sounds/players-score.opus',
|
||||
caf: '//static.flash.moe/sounds/players-score.caf'
|
||||
};
|
||||
|
||||
mami.getSound().load('banBGM', urls, function(success, buffer) {
|
||||
if(!success) {
|
||||
console.log('Failed to load kick/ban SFX: ' + buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!preload) {
|
||||
const source = buffer.createSource();
|
||||
source.setLoopStart(10.512);
|
||||
source.setLoopEnd(38.074);
|
||||
source.setLoop();
|
||||
source.play();
|
||||
}
|
||||
});
|
||||
const source = await soundLib.loadSource(name);
|
||||
source.setLoop(true, 10.512, 38.074);
|
||||
await source.play();
|
||||
};
|
||||
|
||||
const onOpen = function(ev) {
|
||||
|
@ -593,9 +567,8 @@ Umi.Protocol.SockChat.Protocol = function(views, settings) {
|
|||
const trigger = mami.getTextTriggers().getTrigger(text);
|
||||
if(trigger.isSoundType()) {
|
||||
sound = '';
|
||||
mami.playLibrarySound(
|
||||
mami.getSoundLibrary().play(
|
||||
trigger.getRandomSoundName(),
|
||||
null,
|
||||
trigger.getVolume(),
|
||||
trigger.getRate()
|
||||
);
|
||||
|
@ -877,15 +850,14 @@ Umi.Protocol.SockChat.Protocol = function(views, settings) {
|
|||
MamiAnimate({
|
||||
duration: 550,
|
||||
easing: 'outExpo',
|
||||
start: function() {
|
||||
start: () => {
|
||||
playBannedBgm(true);
|
||||
playBannedSfx();
|
||||
},
|
||||
update: function(t) {
|
||||
const scale = 'scale(' + (1 - .5 * t).toString() + ', ' + (1 - 1 * t).toString() + ')';
|
||||
currentView.style.transform = scale;
|
||||
update: t => {
|
||||
currentView.style.transform = `scale(${(1 - .5 * t)}, ${(1 - 1 * t)})`;
|
||||
},
|
||||
end: function() {
|
||||
end: () => {
|
||||
getLoadingOverlay(icon, header, message).then(() => {
|
||||
playBannedBgm();
|
||||
|
||||
|
|
|
@ -1,72 +1,33 @@
|
|||
#include rng.js
|
||||
|
||||
const OsuKeys = (function() {
|
||||
const urlBase = '//static.flash.moe/sounds/';
|
||||
const OsuKeys = (() => {
|
||||
const rng = new MamiRNG;
|
||||
let sndRng = false;
|
||||
|
||||
const playSound = function(name) {
|
||||
if(!mami.hasSound())
|
||||
const keyHandler = ev => {
|
||||
if(ev.key === 'Control' || ev.key === 'Alt' || ev.key === 'Shift')
|
||||
return;
|
||||
|
||||
const urls = {
|
||||
ogg: urlBase + name + '.ogg',
|
||||
mp3: urlBase + name + '.mp3',
|
||||
};
|
||||
let soundName;
|
||||
if(ev.key === 'Enter' || ev.key === 'NumpadEnter')
|
||||
soundName = 'confirm';
|
||||
else if(ev.key === 'Backspace' || ev.key === 'Delete')
|
||||
soundName = 'delete';
|
||||
else if(ev.key === 'NumLock' || ev.key === 'CapsLock' || ev.key === 'ScrollLock')
|
||||
soundName = 'caps';
|
||||
else if(ev.key === 'ArrowUp' || ev.key === 'ArrowDown' || ev.key === 'ArrowLeft' || ev.key === 'ArrowRight')
|
||||
soundName = 'movement';
|
||||
else
|
||||
soundName = `press${rng.next(1, 5)}`;
|
||||
|
||||
mami.getSound().load('OsuKeys:' + name, urls, function(success, buffer) {
|
||||
if(success) {
|
||||
const source = buffer.createSource();
|
||||
if(sndRng)
|
||||
source.setRate(1.8 - (rng.sample() * 1.5));
|
||||
source.play();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const playPress = function() { playSound('key-press-' + rng.next(1, 5).toString()); };
|
||||
const playCaps = function() { playSound('key-caps'); };
|
||||
const playConfirm = function() { playSound('key-confirm'); };
|
||||
const playDelete = function() { playSound('key-delete'); };
|
||||
const playMove = function() { playSound('key-movement'); };
|
||||
|
||||
const keyHandler = function(ev) {
|
||||
switch(ev.key) {
|
||||
case 'Enter':
|
||||
case 'NumpadEnter':
|
||||
playConfirm();
|
||||
break;
|
||||
case 'Backspace':
|
||||
case 'Delete':
|
||||
playDelete();
|
||||
break;
|
||||
case 'NumLock':
|
||||
case 'CapsLock':
|
||||
case 'ScrollLock':
|
||||
playCaps();
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
case 'ArrowDown':
|
||||
case 'ArrowLeft':
|
||||
case 'ArrowRight':
|
||||
playMove();
|
||||
break;
|
||||
case 'Control':
|
||||
case 'Alt':
|
||||
case 'Shift':
|
||||
break;
|
||||
default:
|
||||
playPress();
|
||||
break;
|
||||
}
|
||||
mami.getSoundLibrary().play(
|
||||
`osu:key:${soundName}`,
|
||||
undefined,
|
||||
sndRng ? (1.8 - (rng.sample() * 1.5)) : undefined
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
playConfirm: playConfirm,
|
||||
playDelete: playDelete,
|
||||
playCaps: playCaps,
|
||||
playMove: playMove,
|
||||
playPress: playPress,
|
||||
setEnable: function(value) {
|
||||
if(value)
|
||||
window.addEventListener('keydown', keyHandler);
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
#include rng.js
|
||||
|
||||
const Seinfeld = (function() {
|
||||
const urlBase = '//static.flash.moe/sounds/seinfeld/', sounds = 22, rng = new MamiRNG;
|
||||
const Seinfeld = (() => {
|
||||
const sounds = 22;
|
||||
const rng = new MamiRNG;
|
||||
|
||||
return {
|
||||
getRandom: function() {
|
||||
const name = (rng.next(sounds) + 1).toString(),
|
||||
url = urlBase + name;
|
||||
|
||||
return {
|
||||
opus: url + '.opus',
|
||||
caf: url + '.caf',
|
||||
};
|
||||
},
|
||||
getRandom: () => `seinfeld:riff${rng.next(1, sounds + 1)}`,
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -5,42 +5,39 @@ const MamiSoundInfo = function(name, isReadOnly, title, sources) {
|
|||
sources = sources || {};
|
||||
|
||||
return {
|
||||
getName: function() {
|
||||
return name;
|
||||
},
|
||||
isReadOnly: function() {
|
||||
return isReadOnly;
|
||||
},
|
||||
getTitle: function() {
|
||||
return title;
|
||||
},
|
||||
getSources: function() {
|
||||
return sources;
|
||||
},
|
||||
getName: () => name,
|
||||
isReadOnly: () => isReadOnly,
|
||||
getTitle: () => title,
|
||||
getSources: () => sources,
|
||||
};
|
||||
};
|
||||
|
||||
const MamiSoundLibrary = function() {
|
||||
const MamiSoundLibrary = function(soundMgr) {
|
||||
if(typeof soundMgr !== 'object' && typeof soundMgr.loadSource !== 'function')
|
||||
throw 'soundMgr must be an instance of MamiSoundManager';
|
||||
|
||||
const sounds = new Map;
|
||||
|
||||
const addSound = function(soundInfo) {
|
||||
if(sounds.has(soundInfo.getName()))
|
||||
throw 'a sound with that name has already been registered';
|
||||
sounds.set(soundInfo.getName(), soundInfo);
|
||||
};
|
||||
const soundFormats = ['opus', 'caf', 'ogg', 'mp3', 'wav'];
|
||||
const registerSound = (soundInfo, readOnly) => {
|
||||
if(Array.isArray(soundInfo)) {
|
||||
for(const info of soundInfo)
|
||||
registerSound(info, readOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
const loadSoundFormats = ['opus', 'caf', 'ogg', 'mp3', 'wav'];
|
||||
const loadSound = function(soundInfo, readOnly) {
|
||||
if(typeof soundInfo !== 'object')
|
||||
throw 'soundInfo must be an object';
|
||||
if(typeof soundInfo.name !== 'string')
|
||||
throw 'soundInfo must contain a name field';
|
||||
if(typeof soundInfo.sources !== 'object')
|
||||
throw 'soundInfo must contain a sources field';
|
||||
if(sounds.has(soundInfo.name))
|
||||
throw 'a sound with that name has already been registered';
|
||||
|
||||
let sources = {},
|
||||
sCount = 0;
|
||||
for(const fmt of loadSoundFormats) {
|
||||
for(const fmt of soundFormats) {
|
||||
if(typeof soundInfo.sources[fmt] !== 'string')
|
||||
continue;
|
||||
sources[fmt] = soundInfo.sources[fmt];
|
||||
|
@ -50,34 +47,46 @@ const MamiSoundLibrary = function() {
|
|||
if(sCount < 1)
|
||||
throw 'soundInfo does not contain any valid sources';
|
||||
|
||||
addSound(new MamiSoundInfo(soundInfo.name, readOnly, soundInfo.title, sources));
|
||||
soundInfo = new MamiSoundInfo(soundInfo.name, readOnly, soundInfo.title, sources);
|
||||
sounds.set(soundInfo.getName(), soundInfo);
|
||||
|
||||
return soundInfo;
|
||||
};
|
||||
|
||||
const getSound = name => {
|
||||
if(!sounds.has(name))
|
||||
throw 'No sound with this name has been registered.';
|
||||
return sounds.get(name);
|
||||
};
|
||||
|
||||
const getSoundSources = name => getSound(name).getSources();
|
||||
const loadSoundSource = async name => await soundMgr.loadSource(getSoundSources(name));
|
||||
|
||||
return {
|
||||
addSound: addSound,
|
||||
loadSound: loadSound,
|
||||
loadSounds: function(soundInfos, readOnly) {
|
||||
for(const soundInfo of soundInfos)
|
||||
loadSound(soundInfo, readOnly);
|
||||
},
|
||||
removeSound: function(name) {
|
||||
register: registerSound,
|
||||
unregister: name => {
|
||||
sounds.delete(name);
|
||||
},
|
||||
clearSounds: function() {
|
||||
clear: () => {
|
||||
sounds.clear();
|
||||
},
|
||||
forEachSound: function(body) {
|
||||
if(typeof body !== 'function')
|
||||
return;
|
||||
sounds.forEach(body);
|
||||
info: name => getSound(name),
|
||||
names: () => Array.from(sounds.keys()),
|
||||
has: name => sounds.has(name),
|
||||
get: getSound,
|
||||
getSources: getSoundSources,
|
||||
loadBuffer: async name => await soundMgr.loadBuffer(getSoundSources(name)),
|
||||
loadSource: loadSoundSource,
|
||||
play: async (name, volume, rate) => {
|
||||
const source = await loadSoundSource(name);
|
||||
|
||||
if(typeof volume === 'number')
|
||||
source.setVolume(volume);
|
||||
|
||||
if(typeof rate === 'number')
|
||||
source.setRate(rate);
|
||||
|
||||
await source.play();
|
||||
},
|
||||
hasSound: function(name) {
|
||||
return sounds.has(name);
|
||||
},
|
||||
getSound: function(name) {
|
||||
if(!sounds.has(name))
|
||||
throw 'No sound with this name has been registered.';
|
||||
return sounds.get(name);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,162 +1,70 @@
|
|||
#include utility.js
|
||||
#include sound/sndlibrary.js
|
||||
#include sound/sndpacks.js
|
||||
|
||||
const MamiSoundManager = function(context) {
|
||||
const supported = [], fallback = [],
|
||||
formats = {
|
||||
opus: 'audio/ogg;codecs=opus',
|
||||
ogg: 'audio/ogg;codecs=vorbis',
|
||||
mp3: 'audio/mpeg;codecs=mp3',
|
||||
caf: 'audio/x-caf;codecs=opus',
|
||||
wav: 'audio/wav',
|
||||
};
|
||||
const probablySupported = [];
|
||||
const maybeSupported = [];
|
||||
const formats = {
|
||||
opus: 'audio/ogg;codecs=opus',
|
||||
ogg: 'audio/ogg;codecs=vorbis',
|
||||
mp3: 'audio/mpeg;codecs=mp3',
|
||||
caf: 'audio/x-caf;codecs=opus',
|
||||
wav: 'audio/wav',
|
||||
};
|
||||
|
||||
const loaded = new Map;
|
||||
|
||||
(function() {
|
||||
(() => {
|
||||
const elem = $e('audio');
|
||||
for(const name in formats) {
|
||||
const format = formats[name], support = elem.canPlayType(format);
|
||||
const format = formats[name];
|
||||
const support = elem.canPlayType(format);
|
||||
|
||||
if(support === 'probably')
|
||||
supported.push(name);
|
||||
probablySupported.push(name);
|
||||
else if(support === 'maybe')
|
||||
fallback.push(name);
|
||||
maybeSupported.push(name);
|
||||
}
|
||||
})();
|
||||
|
||||
const pub = {};
|
||||
const extractUrl = urls => {
|
||||
if(typeof urls === 'object' && typeof urls.getSources === 'function')
|
||||
urls = urls.getSources();
|
||||
|
||||
const findSupportedUrl = function(urls) {
|
||||
if(typeof urls === 'string')
|
||||
return urls;
|
||||
|
||||
// lol we're going backwards again
|
||||
if(Array.isArray(urls)) {
|
||||
const tmp = urls;
|
||||
urls = {};
|
||||
for(const type of probablySupported)
|
||||
if(type in urls && typeof urls[type] === 'string')
|
||||
return urls[type];
|
||||
|
||||
for(const item of tmp) {
|
||||
let type = null;
|
||||
|
||||
if(item.type)
|
||||
type = item.type;
|
||||
else {
|
||||
switch(item.format) {
|
||||
case 'audio/mpeg':
|
||||
case 'audio/mp3':
|
||||
type = 'mp3';
|
||||
break;
|
||||
case 'audio/ogg': // could also be opus, oops!
|
||||
type = 'ogg';
|
||||
break;
|
||||
case 'audio/opus': // isn't real lol
|
||||
type = 'opus';
|
||||
break;
|
||||
case 'audio/x-caf':
|
||||
type = 'caf';
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(const type of maybeSupported)
|
||||
if(type in urls && typeof urls[type] === 'string')
|
||||
return urls[type];
|
||||
|
||||
if(type === null || type === undefined)
|
||||
continue;
|
||||
|
||||
urls[type] = item.url;
|
||||
}
|
||||
}
|
||||
|
||||
// check "probably" formats
|
||||
let url = null;
|
||||
for(const type of supported) {
|
||||
if(type in urls) {
|
||||
url = urls[type];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// check "maybe" formats
|
||||
if(url === null)
|
||||
for(const type of fallback) {
|
||||
if(type in urls) {
|
||||
url = urls[type];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
throw 'No supported audio format could be determined.';
|
||||
};
|
||||
|
||||
pub.findSupportedUrl = findSupportedUrl;
|
||||
const loaded = new Map;
|
||||
context.onReset(() => loaded.clear());
|
||||
|
||||
pub.loadAnonymous = function(urls, callback) {
|
||||
const url = findSupportedUrl(urls);
|
||||
if(url === null) {
|
||||
callback.call(pub, false, 'No supported audio format could be determined.');
|
||||
return;
|
||||
}
|
||||
const loadBuffer = async urls => {
|
||||
const url = extractUrl(urls);
|
||||
|
||||
context.createBuffer(url, function(success, buffer) {
|
||||
callback.call(pub, success, buffer);
|
||||
});
|
||||
if(loaded.has(url))
|
||||
return loaded.get(url);
|
||||
|
||||
const buffer = await context.createBuffer(url);
|
||||
loaded.set(url, buffer);
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
pub.load = function(name, urls, callback) {
|
||||
const hasCallback = typeof callback === 'function';
|
||||
|
||||
if(loaded.has(name)) {
|
||||
if(hasCallback)
|
||||
callback.call(pub, true, loaded.get(name));
|
||||
return;
|
||||
}
|
||||
|
||||
const url = findSupportedUrl(urls);
|
||||
if(url === null) {
|
||||
const msg = 'No supported audio format could be determined.';
|
||||
if(hasCallback)
|
||||
callback.call(pub, false, msg);
|
||||
else
|
||||
throw msg;
|
||||
return;
|
||||
}
|
||||
|
||||
context.createBuffer(url, function(success, buffer) {
|
||||
if(!success) {
|
||||
if(hasCallback)
|
||||
callback.call(pub, success, buffer);
|
||||
else // not actually sure if this will do anything but maybe it'll show in the console
|
||||
throw buffer;
|
||||
return;
|
||||
}
|
||||
|
||||
loaded.set(name, buffer);
|
||||
|
||||
if(hasCallback)
|
||||
callback.call(pub, success, buffer);
|
||||
});
|
||||
return {
|
||||
loadBuffer: loadBuffer,
|
||||
loadSource: async urls => context.createSource(await loadBuffer(urls)),
|
||||
unload: urls => {
|
||||
loaded.delete(extractUrl(urls));
|
||||
},
|
||||
reset: () => {
|
||||
loaded.clear();
|
||||
},
|
||||
};
|
||||
|
||||
pub.unload = function(name) {
|
||||
loaded.delete(name);
|
||||
};
|
||||
pub.reset = function() {
|
||||
loaded.clear();
|
||||
};
|
||||
|
||||
pub.play = function(name) {
|
||||
if(loaded.has(name))
|
||||
loaded.get(name).createSource().play();
|
||||
};
|
||||
|
||||
pub.isLoaded = function(name) {
|
||||
return loaded.has(name);
|
||||
};
|
||||
|
||||
pub.get = function(name) {
|
||||
if(!loaded.has(name))
|
||||
return null;
|
||||
return loaded.get(name).createSource();
|
||||
};
|
||||
|
||||
return pub;
|
||||
};
|
||||
|
|
|
@ -7,146 +7,98 @@ const MamiSoundPack = function(name, isReadOnly, title, events) {
|
|||
events = events || new Map;
|
||||
|
||||
return {
|
||||
getName: function() {
|
||||
return name;
|
||||
},
|
||||
isReadOnly: function() {
|
||||
return isReadOnly;
|
||||
},
|
||||
getTitle: function() {
|
||||
return title;
|
||||
},
|
||||
setTitle: function(newTitle) {
|
||||
getName: () => name,
|
||||
isReadOnly: () => isReadOnly,
|
||||
getTitle: () => title,
|
||||
setTitle: newTitle => {
|
||||
if(isReadOnly)
|
||||
throw 'Cannot edit read only sound pack.';
|
||||
title = (newTitle || '').toString();
|
||||
},
|
||||
getEventNames: function() {
|
||||
return Array.from(events.keys());
|
||||
},
|
||||
hasEventSound: function(eventName) {
|
||||
return events.has(eventName);
|
||||
},
|
||||
getEventSound: function(eventName) {
|
||||
getEventNames: () => Array.from(events.keys()),
|
||||
hasEventSound: eventName => events.has(eventName),
|
||||
getEventSound: eventName => {
|
||||
if(!events.has(eventName))
|
||||
throw 'event not registered';
|
||||
return events.get(eventName);
|
||||
},
|
||||
setEventSound: function(eventName, soundName) {
|
||||
setEventSound: (eventName, soundName) => {
|
||||
events.set(
|
||||
(eventName || '').toString(),
|
||||
(soundName || '').toString()
|
||||
);
|
||||
},
|
||||
removeEventSound: function(eventName) {
|
||||
removeEventSound: eventName => {
|
||||
events.delete(eventName);
|
||||
},
|
||||
clearEventSounds: function() {
|
||||
clearEventSounds: () => {
|
||||
events.clear();
|
||||
},
|
||||
clone: function(newName, readOnly) {
|
||||
clone: (newName, readOnly) => {
|
||||
return new MamiSoundPack(newName, readOnly, 'Clone of ' + title, new Map(events));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const MamiSoundPackPlayer = function(soundMgr, sndLibrary) {
|
||||
const pub = {};
|
||||
let pack = null;
|
||||
const MamiSoundPackPlayer = function(sndLibrary) {
|
||||
let pack;
|
||||
|
||||
pub.loadPack = function(packInfo) {
|
||||
if(typeof pack !== 'object' && typeof pack.getEventSound !== 'function')
|
||||
throw 'pack is not a valid soundpack';
|
||||
pack = packInfo;
|
||||
};
|
||||
return {
|
||||
loadPack: packInfo => {
|
||||
if(typeof packInfo !== 'object' || typeof packInfo.getEventSound !== 'function')
|
||||
throw 'pack is not a valid soundpack';
|
||||
pack = packInfo;
|
||||
},
|
||||
unloadPack: () => {
|
||||
pack = undefined;
|
||||
buffers.clear();
|
||||
},
|
||||
hasPack: () => pack !== undefined,
|
||||
|
||||
pub.unloadPack = function() {
|
||||
pack = null;
|
||||
buffers.clear();
|
||||
};
|
||||
|
||||
pub.hasPack = function() {
|
||||
return pack !== null;
|
||||
};
|
||||
|
||||
const playSource = function(source, hasCallback, callback) {
|
||||
if(hasCallback)
|
||||
source.whenEnded(function() {
|
||||
callback(true);
|
||||
});
|
||||
source.play();
|
||||
};
|
||||
|
||||
const handlePlayError = function(ex, hasCallback, callback) {
|
||||
console.error(ex);
|
||||
if(hasCallback)
|
||||
callback(false, ex);
|
||||
};
|
||||
|
||||
pub.hasEvent = function(eventName) {
|
||||
return pack !== null && pack.hasEventSound(eventName);
|
||||
};
|
||||
|
||||
pub.playEvent = function(eventName, callback) {
|
||||
if(pack === null)
|
||||
return;
|
||||
|
||||
if(Array.isArray(eventName)) {
|
||||
const names = eventName;
|
||||
eventName = null;
|
||||
|
||||
for(const name of names) {
|
||||
if(pack.hasEventSound(name)) {
|
||||
eventName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(eventName === null)
|
||||
hasEvent: name => pack !== undefined && pack.hasEventSound(event),
|
||||
playEvent: async event => {
|
||||
if(pack === undefined)
|
||||
return;
|
||||
} else if(!pack.hasEventSound(eventName))
|
||||
return;
|
||||
|
||||
const hasCallback = typeof callback === 'function';
|
||||
if(Array.isArray(event)) {
|
||||
const names = event;
|
||||
event = undefined;
|
||||
|
||||
try {
|
||||
const soundInfo = sndLibrary.getSound(pack.getEventSound(eventName)),
|
||||
soundName = soundInfo.getName();
|
||||
for(const name of names) {
|
||||
if(pack.hasEventSound(name)) {
|
||||
event = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(soundMgr.isLoaded(soundName)) {
|
||||
playSource(soundMgr.get(soundName), hasCallback, callback);
|
||||
} else {
|
||||
soundMgr.load(soundName, soundInfo.getSources(), function(success, buffer) {
|
||||
if(success)
|
||||
playSource(buffer.createSource(), hasCallback, callback);
|
||||
else
|
||||
handlePlayError(buffer, hasCallback, callback);
|
||||
});
|
||||
}
|
||||
} catch(ex) {
|
||||
handlePlayError(ex, hasCallback, callback);
|
||||
}
|
||||
if(event === undefined)
|
||||
return;
|
||||
} else if(!pack.hasEventSound(event))
|
||||
return;
|
||||
|
||||
await sndLibrary.play(pack.getEventSound(event));
|
||||
},
|
||||
};
|
||||
|
||||
return pub;
|
||||
};
|
||||
|
||||
const MamiSoundPacks = function() {
|
||||
const packs = new Map;
|
||||
|
||||
const addPack = function(packInfo) {
|
||||
if(packs.has(packInfo.getName()))
|
||||
throw 'a pack with that name has already been registered';
|
||||
packs.set(packInfo.getName(), packInfo);
|
||||
};
|
||||
const registerPack = (packInfo, readOnly) => {
|
||||
if(Array.isArray(packInfo)) {
|
||||
for(const info of packInfo)
|
||||
registerPack(info, readOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
const loadPack = function(packInfo, readOnly) {
|
||||
if(typeof packInfo !== 'object')
|
||||
throw 'packInfo must be an object';
|
||||
if(typeof packInfo.name !== 'string')
|
||||
throw 'packInfo must contain a name field';
|
||||
if(typeof packInfo.events !== 'object')
|
||||
throw 'packInfo must contain a events field';
|
||||
if(packs.has(packInfo.name))
|
||||
throw 'a pack with that name has already been registered';
|
||||
|
||||
const events = new Map;
|
||||
for(const eventName in packInfo.events) {
|
||||
|
@ -158,39 +110,29 @@ const MamiSoundPacks = function() {
|
|||
if(events.size < 1)
|
||||
throw 'packInfo does not contain any valid events';
|
||||
|
||||
addPack(new MamiSoundPack(
|
||||
packInfo.name,
|
||||
readOnly,
|
||||
packInfo.title,
|
||||
events
|
||||
));
|
||||
packInfo = new MamiSoundPack(packInfo.name, readOnly, packInfo.title, events);
|
||||
packs.set(packInfo.getName(), packInfo);
|
||||
|
||||
return packInfo;
|
||||
};
|
||||
|
||||
const getPack = name => {
|
||||
if(!packs.has(name))
|
||||
throw 'No pack with this name has been registered.';
|
||||
return packs.get(name);
|
||||
};
|
||||
|
||||
return {
|
||||
addPack: addPack,
|
||||
loadPack: loadPack,
|
||||
loadPacks: function(packInfos, readOnly) {
|
||||
for(const packInfo of packInfos)
|
||||
loadPack(packInfo, readOnly);
|
||||
},
|
||||
removePack: function(name) {
|
||||
register: registerPack,
|
||||
unregister: name => {
|
||||
packs.delete(name);
|
||||
},
|
||||
clearPacks: function() {
|
||||
clear: () => {
|
||||
packs.clear();
|
||||
},
|
||||
forEachPack: function(body) {
|
||||
if(typeof body !== 'function')
|
||||
return;
|
||||
packs.forEach(body);
|
||||
},
|
||||
hasPack: function(name) {
|
||||
return packs.has(name);
|
||||
},
|
||||
getPack: function(name) {
|
||||
if(!packs.has(name))
|
||||
throw 'No pack with this name has been registered.';
|
||||
return packs.get(name);
|
||||
},
|
||||
info: name => getPack(name),
|
||||
names: () => Array.from(packs.keys()),
|
||||
has: name => packs.has(name),
|
||||
get: getPack,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
#include audio/context.js
|
||||
#include sound/sndpacks.js
|
||||
#include sound/seinfeld.js
|
||||
|
||||
Umi.Sound = (function() {
|
||||
Umi.Sound = (() => {
|
||||
return {
|
||||
Play: function(sound) {
|
||||
if(!sound || sound === 'none' || !mami.hasSound())
|
||||
Play: sound => {
|
||||
if(!sound || sound === 'none')
|
||||
return;
|
||||
|
||||
const settings = mami.getSettings();
|
||||
const sndLib = mami.getSoundLibrary();
|
||||
const sndPackPlay = mami.getSoundPackPlayer();
|
||||
|
||||
switch(sound) {
|
||||
|
@ -17,17 +16,17 @@ Umi.Sound = (function() {
|
|||
return;
|
||||
|
||||
if(settings.get('seinfeld')) {
|
||||
mami.playUrlSound(Seinfeld.getRandom());
|
||||
sndLib.play(Seinfeld.getRandom());
|
||||
break;
|
||||
}
|
||||
|
||||
switch(settings.get('minecraft')) {
|
||||
case 'yes':
|
||||
mami.playLibrarySound('minecraft:door:open');
|
||||
sndLib.play('minecraft:door:open');
|
||||
break;
|
||||
|
||||
case 'old':
|
||||
mami.playLibrarySound('minecraft:door:open-old');
|
||||
sndLib.play('minecraft:door:open-old');
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -42,11 +41,11 @@ Umi.Sound = (function() {
|
|||
|
||||
switch(settings.get('minecraft')) {
|
||||
case 'yes':
|
||||
mami.playLibrarySound('minecraft:door:close');
|
||||
sndLib.play('minecraft:door:close');
|
||||
break;
|
||||
|
||||
case 'old':
|
||||
mami.playLibrarySound('minecraft:door:close-old');
|
||||
sndLib.play('minecraft:door:close-old');
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -81,7 +80,7 @@ Umi.Sound = (function() {
|
|||
return;
|
||||
|
||||
if(settings.get('windowsLiveMessenger')) {
|
||||
mami.playLibrarySound('msn:incoming');
|
||||
sndLib.play('msn:incoming');
|
||||
} else {
|
||||
sndPackPlay.playEvent('incoming');
|
||||
}
|
||||
|
|
|
@ -55,11 +55,11 @@ const MamiDomainTransition = function(onImport, onDismiss) {
|
|||
</div>
|
||||
</div>;
|
||||
|
||||
const soundLib = mami.getSoundLibrary();
|
||||
const soundRNG = new MamiRNG();
|
||||
const soundNames = [];
|
||||
mami.getSoundLibrary().forEachSound((info, name) => soundNames.push(name));
|
||||
const soundNames = soundLib.names();
|
||||
|
||||
const playRandomSound = () => mami.playLibrarySound(soundNames[soundRNG.next(soundNames.length)]);
|
||||
const playRandomSound = () => soundLib.play(soundNames[soundRNG.next(soundNames.length)]);
|
||||
|
||||
for(let i = 0; i < 10; ++i)
|
||||
eggsTarget.appendChild(<i class="fas fa-egg fa-3x" onclick={() => playRandomSound()} />);
|
||||
|
|
|
@ -138,10 +138,12 @@ Umi.UI.Settings = (function() {
|
|||
title: 'Sound pack',
|
||||
type: 'select',
|
||||
options: () => {
|
||||
const registry = mami.getSoundPacks();
|
||||
const packs = {};
|
||||
mami.getSoundPacks().forEachPack(function(pack) {
|
||||
packs[pack.getName()] = pack.getTitle();
|
||||
});
|
||||
|
||||
for(const name of registry.names())
|
||||
packs[name] = registry.info(name).getTitle();
|
||||
|
||||
return packs;
|
||||
},
|
||||
},
|
||||
|
@ -396,14 +398,21 @@ Umi.UI.Settings = (function() {
|
|||
button.disabled = false;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Reset audio context',
|
||||
type: 'button',
|
||||
invoke: async () => {
|
||||
mami.getAudio().reset();
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
];
|
||||
|
||||
const createCopyright = function() {
|
||||
return <div class="mami-copyright">
|
||||
<a href="//patchii.net/flashii/mami" target="_blank">Mami</a> © <a href="//flash.moe" target="_blank">flash.moe</a><br/>
|
||||
<a href="//railgun.sh/sockchat" target="_blank">Sock Chat documentation</a>
|
||||
<a href="//patchii.net/flashii/mami" target="_blank">Mami</a> # <a href={`//patchii.net/flashii/mami/commit/${GIT_HASH}`} target="_blank">{GIT_HASH.substring(0, 7)}</a> © <a href="//flash.moe" target="_blank">flash.moe</a><br/>
|
||||
<a href="//railgun.sh/sockchat" target="_blank">Sock Chat documentation</a><br/>
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
@ -475,46 +484,33 @@ Umi.UI.Settings = (function() {
|
|||
const catBody = <div class={['setting__category', `setting__category--${category.name}`]} style={{ overflow: 'hidden' }}/>;
|
||||
const catContainer = <div>{catHeader}{catBody}</div>;
|
||||
|
||||
let anime, closed = false;
|
||||
|
||||
catHeader.onclick = () => {
|
||||
if(catContainer.dataset.mamiAnimRun === 'yes')
|
||||
return;
|
||||
catContainer.dataset.mamiAnimRun = 'yes';
|
||||
if(anime !== undefined) {
|
||||
anime.cancel();
|
||||
anime = undefined;
|
||||
}
|
||||
|
||||
let start, update, end, height;
|
||||
|
||||
if(catContainer.dataset.mamiIsClosed === 'yes') {
|
||||
catContainer.dataset.mamiIsClosed = 'no';
|
||||
|
||||
if(closed) {
|
||||
start = () => {
|
||||
const curHeight = catBody.style.height;
|
||||
catBody.style.height = null;
|
||||
height = catBody.clientHeight;
|
||||
catBody.style.height = curHeight;
|
||||
};
|
||||
update = t => {
|
||||
catBody.style.height = `${height * t}px`;
|
||||
};
|
||||
end = () => {
|
||||
catBody.style.height = null;
|
||||
catContainer.dataset.mamiAnimRun = 'no';
|
||||
};
|
||||
update = t => catBody.style.height = `${height * t}px`;
|
||||
end = () => catBody.style.height = null;
|
||||
} else {
|
||||
catContainer.dataset.mamiIsClosed = 'yes';
|
||||
|
||||
start = () => {
|
||||
height = catBody.clientHeight;
|
||||
};
|
||||
update = t => {
|
||||
catBody.style.height = `${height - (height * t)}px`;
|
||||
};
|
||||
end = () => {
|
||||
catBody.style.height = '0';
|
||||
catContainer.dataset.mamiAnimRun = 'no';
|
||||
};
|
||||
start = () => height = catBody.clientHeight;
|
||||
update = t => catBody.style.height = `${height - (height * t)}px`;
|
||||
end = () => catBody.style.height = '0';
|
||||
}
|
||||
|
||||
// todo: actually make cancellable
|
||||
MamiAnimate({
|
||||
closed = !closed;
|
||||
anime = MamiAnimate({
|
||||
duration: 500,
|
||||
easing: 'outExpo',
|
||||
start: start,
|
||||
|
|
|
@ -19,27 +19,21 @@ const MamiYouAreAnIdiot = function() {
|
|||
|
||||
const pub = {
|
||||
getElement: () => html,
|
||||
onViewPush: () => {
|
||||
return new Promise(resolve => {
|
||||
const soundMgr = mami.getSound();
|
||||
if(soundMgr === undefined || soundMgr === null) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
onViewPush: async () => {
|
||||
const soundMgr = mami.getSound();
|
||||
if(soundMgr === undefined || soundMgr === null)
|
||||
return;
|
||||
|
||||
const soundSrcs = mami.getSoundLibrary().getSound('misc:youare').getSources();
|
||||
soundMgr.load('youarebgm', soundSrcs, (success, buffer) => {
|
||||
if(success) {
|
||||
soundSrc = buffer.createSource();
|
||||
soundSrc.setMuted(true);
|
||||
soundSrc.setLoopStart(0.21);
|
||||
soundSrc.setLoopEnd(5);
|
||||
soundSrc.setLoop();
|
||||
soundSrc.play();
|
||||
resolve();
|
||||
} else resolve();
|
||||
});
|
||||
});
|
||||
try {
|
||||
const sources = mami.getSoundLibrary().getSources('misc:youare');
|
||||
|
||||
soundSrc = soundMgr.loadSource(sources);
|
||||
soundSrc.setMuted(true);
|
||||
soundSrc.setLoop(true, 0.21, 5);
|
||||
soundSrc.play();
|
||||
} catch(ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
},
|
||||
onViewForeground: async () => {
|
||||
if(soundSrc !== undefined)
|
||||
|
|
Loading…
Reference in a new issue