Files
battle-caster-v1/src/components/Audio/Soundboard.vue
2024-11-24 17:34:41 +00:00

166 lines
4.1 KiB
Vue

<template>
<div class="soundboard">
<div class="soundboard__controls">
<DeviceVolume />
</div>
<div class="soundboard__sounds" style="--color-theme: var(--color-lime)">
<template v-for="node in nodes" :key="node">
<Playlist
v-if="node.type === 'playlist'"
:node="node"
@delete="deleteNode(node)"
/>
<Sfx
v-else-if="node.type === 'sfx'"
:node="node"
@delete="deleteNode(node)"
/>
<TabSource
v-else-if="node.type === 'tabSource'"
:node="node"
@delete="deleteNode(node)"
/>
</template>
</div>
<div class="a-card add-card" style="--box-color: var(--color-zinc)">
<div class="a-card__label-wrapper">
<div class="a-card__label">Add</div>
</div>
<div @click="addNode('playlist')" class="soundboard-add-card__btn">
Playlist
</div>
<div @click="addNode('sfx')" class="soundboard-add-card__btn">
Sfx
</div>
<div @click="addNode('tab')" class="soundboard-add-card__btn">
Tab Source
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {inject, reactive, ref, watch} from 'vue';
import Playlist from './Playlist.vue';
import {PlaylistGroupNode} from '../../model/Audio/PlaylistGroupNode';
import {AudioContextSymbol, MountedFsSymbol, RemoteDeviceManagerSymbol} from '../../model/injectionSymbols';
import {SfxGroupNode} from '../../model/Audio/SfxGroupNode';
import Sfx from './Sfx.vue';
import Modal from './modal.vue';
import {TabSourceNode} from '../../model/Audio/TabSourceNode';
import TabSource from './TabSource.vue';
import DeviceVolume from './DeviceVolume.vue';
import {AudioNodeManager} from '../../model/Audio/AudioNodesManager';
import {debounce} from '../../utils/debounce';
import IconPlus from '../Icons/IconPlus.vue';
import {SoundNode} from '../../model/Audio/SoundNode';
const fs = inject(MountedFsSymbol)!;
const audioContext = inject(AudioContextSymbol) as AudioContext;
const deviceManager = inject(RemoteDeviceManagerSymbol)!;
const audioNodesManager = new AudioNodeManager();
const nodes = reactive(audioNodesManager.restore({fs, audioContext}));
function addNode(name: string) {
if (name === 'playlist') {
nodes.push(reactive(new PlaylistGroupNode({
currentTrack: 0,
currentTrackTime: 0,
volume: 100,
isMuted: false,
sounds: [],
}, {
fs,
audioContext,
})))
} else if (name === 'sfx') {
nodes.push(reactive(new SfxGroupNode({
isMuted: false,
volume: 100,
sounds: [],
}, {
fs,
audioContext,
})))
} else if (name === 'tab') {
nodes.push(reactive(new TabSourceNode({
isMuted: false,
volume: 100,
sounds: [],
}, {
fs,
audioContext,
})))
}
}
const deleteNode = (node: SoundNode) => {
node.stop?.();
node.disconnect();
// @ts-ignore
nodes.splice(nodes.indexOf(node), 1);
}
watch(() => nodes, debounce((newNodes) => {
console.log('NEW_NODES', newNodes);
audioNodesManager.store(newNodes);
}, 500), {
deep: true,
})
const dest = audioContext.createGain();
// for (const node of nodes) {
// // node.connect(audioContext.destination);
// node.connect(dest);
// }
watch(() => deviceManager.audioDevices.length, () => {
console.log('DEVICE_MANAGER', deviceManager)
for(const device of deviceManager.audioDevices) {
dest.connect(device.audioSink);
console.log('DEVICES', device, device.audioSink);
}
}, {immediate: true})
</script>
<style>
.soundboard {
}
.soundboard__controls {
display: flex;
flex-direction: row;
padding: 0 1rem 2rem 1rem;
gap: 1rem;
}
.soundboard__sounds {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 3rem 1rem;
padding: 1rem;
}
.soundboard-add-card {
}
.soundboard-add-card__btn {
text-align: center;
border: 1px solid;
margin: 0.25rem;
background: var(--box-color-900);
border: 1px solid rgba(from var(--box-color) r g b / 0.2);
cursor: pointer;
}
.soundboard-add-card__btn:hover {
background: var(--box-color-800);
}
</style>