Added sound test screen.
This commit is contained in:
parent
f82785a9cb
commit
66aea64da1
7 changed files with 272 additions and 23 deletions
|
@ -4,6 +4,8 @@
|
|||
@include controls/msgbox.css;
|
||||
@include controls/views.css;
|
||||
|
||||
@include sound/sndtest.css;
|
||||
|
||||
@include baka.css;
|
||||
@include ping.css;
|
||||
@include chat.css;
|
||||
|
|
79
src/mami.css/sound/sndtest.css
Normal file
79
src/mami.css/sound/sndtest.css
Normal file
|
@ -0,0 +1,79 @@
|
|||
.sndtest {
|
||||
color: #000;
|
||||
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
color: #fff;
|
||||
background: #000;
|
||||
}
|
||||
.sndtest button {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.sndtest-view {
|
||||
margin: 2px;
|
||||
display: flex;
|
||||
}
|
||||
.sndtest-view button {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
background: #f99;
|
||||
color: #fff;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
border-bottom: #f33;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.sndtest-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.sndtest-library {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 2px;
|
||||
margin: 10px;
|
||||
}
|
||||
.sndtest-library button {
|
||||
border: 1px solid #222;
|
||||
border-radius: 0;
|
||||
background: #111;
|
||||
color: #fff;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.sndtest-playing {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
gap: 2px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.sndtest-player {
|
||||
display: flex;
|
||||
background: #111;
|
||||
border: 1px solid #222;
|
||||
padding: 4px;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.sndtest-player button {
|
||||
border: 1px solid #333;
|
||||
border-radius: 0;
|
||||
background: #222;
|
||||
color: #fff;
|
||||
padding: 4px;
|
||||
}
|
||||
.sndtest-player-details {
|
||||
min-width: 500px;
|
||||
}
|
||||
.sndtest-player-title {
|
||||
font-size: 1.4em;
|
||||
line-height: 1.5em;
|
||||
}
|
|
@ -11,6 +11,7 @@ const MamiSettingsScoped = function(settings, prefix) {
|
|||
|
||||
return {
|
||||
define: name => settings.define(prefix + name),
|
||||
defined: name => settings.defined(prefix + name),
|
||||
info: name => settings.info(prefix + name),
|
||||
names: () => {
|
||||
const filtered = [];
|
||||
|
|
|
@ -209,6 +209,7 @@ const MamiSettings = function(storageOrPrefix, eventTarget) {
|
|||
|
||||
const pub = {
|
||||
define: name => new settingBlueprint(name),
|
||||
defined: name => settings.has(name),
|
||||
info: name => getSetting(name),
|
||||
names: () => Array.from(settings.keys()),
|
||||
has: name => {
|
||||
|
|
|
@ -8,33 +8,27 @@ const MamiSoundContext = function() {
|
|||
const manager = new MamiSoundManager(audioCtx);
|
||||
const library = new MamiSoundLibrary(manager);
|
||||
const packs = new MamiSoundPacks;
|
||||
|
||||
let pack = new MamiSoundPack;
|
||||
|
||||
const pub = {};
|
||||
return {
|
||||
get audio() { return audioCtx; },
|
||||
get manager() { return manager; },
|
||||
get library() { return library; },
|
||||
get packs() { return packs; },
|
||||
|
||||
Object.defineProperties(pub, {
|
||||
audio: { value: audioCtx, enumerable: true },
|
||||
manager: { value: manager, enumerable: true },
|
||||
library: { value: library, enumerable: true },
|
||||
packs: { value: packs, enumerable: true },
|
||||
|
||||
pack: {
|
||||
get: () => pack,
|
||||
set: value => {
|
||||
if(typeof value !== 'object' || typeof value.getEventSound !== 'function')
|
||||
throw 'value is not a valid soundpack';
|
||||
pack = value;
|
||||
},
|
||||
enumerable: true,
|
||||
get pack() { return pack; },
|
||||
set pack(value) {
|
||||
if(typeof value !== 'object' || typeof value.getEventSound !== 'function')
|
||||
throw 'value is not a valid soundpack';
|
||||
pack = value;
|
||||
},
|
||||
|
||||
ready: { get: audioCtx.isReady, enumerable: true },
|
||||
volume: { get: audioCtx.getVolume, set: audioCtx.setVolume, enumerable: true },
|
||||
muted: { get: audioCtx.isMuted, set: audioCtx.setMuted, enumerable: true },
|
||||
});
|
||||
get ready() { return audioCtx.isReady; },
|
||||
get volume() { return audioCtx.getVolume(); },
|
||||
set volume(value) { audioCtx.setVolume(value); },
|
||||
get muted() { return audioCtx.isMuted; },
|
||||
set muted(value) { audioCtx.setMuted(value); },
|
||||
|
||||
pub.reset = audioCtx.reset;
|
||||
|
||||
return Object.freeze(pub);
|
||||
reset: audioCtx.reset,
|
||||
};
|
||||
};
|
||||
|
|
157
src/mami.js/sound/sndtest.jsx
Normal file
157
src/mami.js/sound/sndtest.jsx
Normal file
|
@ -0,0 +1,157 @@
|
|||
#include awaitable.js
|
||||
|
||||
const MamiSoundTest = function(settings, audio, manager, library) {
|
||||
if(!settings.defined('soundRate'))
|
||||
settings.define('soundRate').default(1.0).min(0.001).max(2.0).virtual().create();
|
||||
if(!settings.defined('soundDetune'))
|
||||
settings.define('soundDetune').default(0).min(-1200).max(1200).virtual().create();
|
||||
if(!settings.defined('soundLoopStart'))
|
||||
settings.define('soundLoopStart').default(0).min(0).virtual().create();
|
||||
if(!settings.defined('soundLoopEnd'))
|
||||
settings.define('soundLoopEnd').default(0).min(0).virtual().create();
|
||||
if(!settings.defined('soundReverse'))
|
||||
settings.define('soundReverse').default(false).virtual().create();
|
||||
|
||||
const volumeSetting = settings.info('soundVolume');
|
||||
const rateSetting = settings.info('soundRate');
|
||||
const detuneSetting = settings.info('soundDetune');
|
||||
const loopStartSetting = settings.info('soundLoopStart');
|
||||
const loopEndSetting = settings.info('soundLoopEnd');
|
||||
const sources = [];
|
||||
|
||||
let libraryButtons;
|
||||
let nowPlaying;
|
||||
let searchBox, volumeSlider, rateSlider, detuneSlider;
|
||||
let loopStartBox, loopEndBox, reverseBox;
|
||||
const container = <div class="sndtest">
|
||||
<div class="sndtest-view">
|
||||
<button onclick={() => { mami.views.pop(); }}>Exit</button>
|
||||
</div>
|
||||
<div class="sndtest-controls">
|
||||
<label class="sndtest-control">
|
||||
<div class="sndtest-control-name">Search</div>
|
||||
{searchBox = <input type="text" placeholder="type to filter"/>}
|
||||
</label>
|
||||
<label class="sndtest-control">
|
||||
<div class="sndtest-control-name">Volume</div>
|
||||
{volumeSlider = <input type="range" min={volumeSetting.min} max={volumeSetting.max} onchange={() => { settings.set('soundVolume', volumeSlider.value); }} ondblclick={() => { settings.delete('soundVolume'); }}/>}
|
||||
</label>
|
||||
<label class="sndtest-control">
|
||||
<div class="sndtest-control-name">Rate</div>
|
||||
{rateSlider = <input type="range" min={rateSetting.min * 1000} max={rateSetting.max * 1000} onchange={() => { settings.set('soundRate', rateSlider.value / 1000); }} ondblclick={() => { settings.delete('soundRate'); }}/>}
|
||||
</label>
|
||||
<label class="sndtest-control">
|
||||
<div class="sndtest-control-name">Detune</div>
|
||||
{detuneSlider = <input type="range" min={detuneSetting.min} max={detuneSetting.max} onchange={() => { settings.set('soundDetune', detuneSlider.value); }} ondblclick={() => { settings.delete('soundDetune'); }}/>}
|
||||
</label>
|
||||
<label class="sndtest-control">
|
||||
<div class="sndtest-control-name">Loop start</div>
|
||||
{loopStartBox = <input type="number" step="0.00001" min={loopStartSetting.min} onchange={() => { settings.set('soundLoopStart', loopStartBox.value); }} ondblclick={() => { settings.delete('soundLoopStart'); }}/>}
|
||||
</label>
|
||||
<label class="sndtest-control">
|
||||
<div class="sndtest-control-name">Loop end</div>
|
||||
{loopEndBox = <input type="number" step="0.00001" min={loopEndSetting.min} onchange={() => { settings.set('soundLoopEnd', loopEndBox.value); }} ondblclick={() => { settings.delete('soundLoopEnd'); }}/>}
|
||||
</label>
|
||||
<label class="sndtest-control">
|
||||
<div class="sndtest-control-name">Reverse</div>
|
||||
{reverseBox = <input type="checkbox" onchange={() => { settings.set('soundReverse', reverseBox.checked); }}/>}
|
||||
</label>
|
||||
</div>
|
||||
{libraryButtons = <div class="sndtest-library"/>}
|
||||
{nowPlaying = <div class="sndtest-playing"/>}
|
||||
</div>;
|
||||
|
||||
settings.watch('soundVolume', ev => {
|
||||
volumeSlider.value = ev.detail.value;
|
||||
});
|
||||
settings.watch('soundRate', ev => {
|
||||
rateSlider.value = ev.detail.value * 1000;
|
||||
|
||||
for(const source of sources)
|
||||
source.setRate(ev.detail.value);
|
||||
});
|
||||
settings.watch('soundDetune', ev => {
|
||||
detuneSlider.value = ev.detail.value;
|
||||
|
||||
for(const source of sources)
|
||||
source.setDetune(ev.detail.value);
|
||||
});
|
||||
settings.watch('soundLoopStart', ev => {
|
||||
loopStartBox.value = ev.detail.value;
|
||||
});
|
||||
settings.watch('soundLoopEnd', ev => {
|
||||
loopEndBox.value = ev.detail.value;
|
||||
});
|
||||
settings.watch('soundReverse', ev => {
|
||||
reverseBox.checked = ev.detail.value;
|
||||
});
|
||||
|
||||
const startPlay = async info => {
|
||||
let controls, state, name;
|
||||
const player = <div class="sndtest-player">
|
||||
<div class="sndtest-player-details">
|
||||
<div class="sndtest-player-title">{info.getTitle()}</div>
|
||||
{name = <div class="sndtest-player-name">{info.getName()}</div>}
|
||||
</div>
|
||||
{controls = <div class="sndtest-player-controls"/>}
|
||||
{state = <div class="sndtest-player-state"/>}
|
||||
</div>;
|
||||
|
||||
nowPlaying.appendChild(player);
|
||||
|
||||
let buffer, source;
|
||||
try {
|
||||
state.textContent = 'Loading...';
|
||||
buffer = await manager.loadBuffer(info.getSources());
|
||||
name.textContent += ` (${buffer.duration})`;
|
||||
|
||||
source = audio.createSource(buffer, settings.get('soundReverse'));
|
||||
sources.push(source);
|
||||
|
||||
state.textContent = 'Configuring...';
|
||||
const rate = settings.get('soundRate');
|
||||
source.setRate(rate);
|
||||
|
||||
const detune = settings.get('soundDetune');
|
||||
source.setDetune(detune);
|
||||
|
||||
const loopStart = settings.get('soundLoopStart');
|
||||
const loopEnd = settings.get('soundLoopEnd');
|
||||
source.setLoop(loopEnd > 0, loopStart, loopEnd);
|
||||
|
||||
controls.appendChild(<button onclick={() => source.stop()}>Stop</button>);
|
||||
|
||||
state.textContent = 'Playing...';
|
||||
await source.play();
|
||||
|
||||
state.textContent = 'Finished.';
|
||||
} catch(ex) {
|
||||
console.error(ex);
|
||||
state.textContent = `Error: ${ex}`;
|
||||
} finally {
|
||||
$ari(sources, source);
|
||||
await MamiSleep(2000);
|
||||
nowPlaying.removeChild(player);
|
||||
}
|
||||
};
|
||||
|
||||
const names = library.names();
|
||||
for(const name of names) {
|
||||
const info = library.info(name);
|
||||
libraryButtons.appendChild(<button onclick={() => { startPlay(info); }} data-search={`${info.getTitle().toLowerCase()} ${info.getName().toLowerCase()}`}>{info.getTitle()} ({info.getName()})</button>);
|
||||
}
|
||||
|
||||
searchBox.addEventListener('change', () => {
|
||||
const str = searchBox.value.trim().toLowerCase();
|
||||
for(const button of libraryButtons.children)
|
||||
button.classList.toggle('hidden', !button.dataset.search.includes(str));
|
||||
});
|
||||
|
||||
return {
|
||||
getElement: () => container,
|
||||
onViewPop: async () => {
|
||||
for(const source of sources)
|
||||
source.stop();
|
||||
},
|
||||
};
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
#include emotes.js
|
||||
#include utility.js
|
||||
#include settings/backup.js
|
||||
#include sound/sndtest.jsx
|
||||
#include ui/baka.jsx
|
||||
#include ui/emotes.js
|
||||
#include ui/menus.js
|
||||
|
@ -453,6 +454,20 @@ Umi.UI.Settings = (function() {
|
|||
button.disabled = false;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Sound Test',
|
||||
type: 'button',
|
||||
invoke: async button => {
|
||||
button.disabled = true;
|
||||
mami.views.push(new MamiSoundTest(
|
||||
mami.settings,
|
||||
mami.sound.audio,
|
||||
mami.sound.manager,
|
||||
mami.sound.library,
|
||||
));
|
||||
button.disabled = false;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Reset audio context',
|
||||
type: 'button',
|
||||
|
|
Loading…
Reference in a new issue