✨ new maps
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
>
|
||||
<div class="a-card__label-wrapper">
|
||||
<div class="a-card__label">{{ label }}</div>
|
||||
<div class="a-card__delete" @click="emits('delete')">×</div>
|
||||
</div>
|
||||
<div class="a-card__volume">
|
||||
<span style="height: 1.25rem" @click="node.toggleMute()">
|
||||
@@ -60,6 +61,7 @@ import DeviceVolume from './DeviceVolume.vue';
|
||||
import Stop from '../Icons/Stop.vue';
|
||||
|
||||
const emits = defineEmits<{
|
||||
'delete': [],
|
||||
'next': [],
|
||||
'prev': [],
|
||||
'togglePlay': [],
|
||||
@@ -119,6 +121,7 @@ function onDrop(event: DragEvent) {
|
||||
.a-card {
|
||||
--box-color: var(--audio-box-color-playlist, #ffb308);
|
||||
--box-color-900: color-mix(in lab, var(--box-color), #000 80%);
|
||||
--box-color-800: color-mix(in lab, var(--box-color), #000 60%);
|
||||
--box-color-100: color-mix(in lab, var(--box-color), #fff 80%);
|
||||
--box-color-300: color-mix(in lab, var(--box-color), #fff 40%);
|
||||
width: 160px;
|
||||
@@ -179,6 +182,7 @@ function onDrop(event: DragEvent) {
|
||||
}
|
||||
|
||||
.a-card__label-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: -1rem;
|
||||
@@ -224,6 +228,24 @@ function onDrop(event: DragEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
.a-card__delete {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -1rem;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
color: var(--box-color-100);
|
||||
background: var(--box-color-900);
|
||||
border-radius: 0.25rem;
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(from var(--box-color) r g b / 0.2);
|
||||
opacity: 0%;
|
||||
}
|
||||
|
||||
.a-card:hover .a-card__delete {
|
||||
opacity: 100%;
|
||||
}
|
||||
|
||||
@keyframes ripples {
|
||||
to {
|
||||
box-shadow:
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
:supports="[ 'next', 'prev', 'togglePlay' ]"
|
||||
@next="node.next()"
|
||||
@prev="node.prev()"
|
||||
label="Playlist test"
|
||||
@delete="$emit('delete')"
|
||||
label="Playlist"
|
||||
style="--box-color: var(--color-amber)"
|
||||
>
|
||||
<template #preview>
|
||||
@@ -12,20 +13,32 @@
|
||||
</template>
|
||||
|
||||
<template #popover>
|
||||
<div>
|
||||
<!--<div>
|
||||
{{ node.currentTrack?.name ?? 'No track selected' }}
|
||||
<div>
|
||||
<input type="range" min="0" max="100" style="width: 100%" />00:00
|
||||
<input type="range" min="0" max="100" style="width: 100%" />
|
||||
</div>
|
||||
</div>-->
|
||||
<div>
|
||||
<div
|
||||
class="a-playlist__song"
|
||||
:class="{
|
||||
'a-playlist__song--playing': song.isPlaying
|
||||
}"
|
||||
v-for="(song, idx) in node.sounds()"
|
||||
:key="song.options.vFile.name"
|
||||
@click="playTrack(idx)"
|
||||
>
|
||||
<span class="a-playlist__song-play-indicator">
|
||||
<Play style="height: 1rem" />
|
||||
</span>
|
||||
<span class="a-playlist__song-name">
|
||||
{{ song.options.vFile.name }}
|
||||
</span>
|
||||
<span @click.prevent="removeSong(song)">
|
||||
×
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="a-playlist__song"
|
||||
v-for="(song, idx) in node.sounds()"
|
||||
:key="song.options.vFile.name"
|
||||
@click="playTrack(idx)"
|
||||
>
|
||||
<span>{{ song.options.vFile.name }}</span>
|
||||
<span>00:00</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,17 +47,27 @@
|
||||
<script setup lang="ts">
|
||||
import AudioCard from './AudioCard.vue';
|
||||
import {PlaylistGroupNode} from '../../model/Audio/PlaylistGroupNode';
|
||||
import Play from '../Icons/Play.vue';
|
||||
import {SoundNode} from '../../model/Audio/SoundNode';
|
||||
|
||||
const props = defineProps<{
|
||||
node: PlaylistGroupNode
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'delete': [],
|
||||
}>();
|
||||
|
||||
// playlist.connect(audioContext.destination);
|
||||
|
||||
function playTrack(idx: number) {
|
||||
props.node.changeTrack(idx);
|
||||
}
|
||||
|
||||
function removeSong(song: SoundNode) {
|
||||
props.node.removeSoundNode(song);
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
|
||||
@@ -92,4 +115,36 @@ function playTrack(idx: number) {
|
||||
border: 2px solid var(--box-color-300);
|
||||
}
|
||||
}
|
||||
|
||||
.a-playlist__song {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid var(--box-color-800);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.a-playlist__song-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.a-playlist__song--playing {
|
||||
background-color: var(--box-color-800);
|
||||
}
|
||||
|
||||
.a-playlist__song:first-child {
|
||||
border-top: 1px solid var(--box-color-800);
|
||||
}
|
||||
|
||||
.a-playlist__song-play-indicator {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.a-playlist__song:hover .a-playlist__song-play-indicator,
|
||||
.a-playlist__song--playing .a-playlist__song-play-indicator {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -6,6 +6,7 @@
|
||||
:supports="['stop']"
|
||||
label="SFX"
|
||||
:node="node"
|
||||
@delete="emit('delete')"
|
||||
>
|
||||
<template #preview>
|
||||
<div
|
||||
@@ -38,6 +39,11 @@ const props = defineProps<{
|
||||
node: SfxGroupNode,
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'delete': [],
|
||||
}>();
|
||||
|
||||
|
||||
const grouppedSounds = computed(() => {
|
||||
return groupBy(props.node.sounds(), (sound) => {
|
||||
return sound.name.replace(/(\d+)$/, '').trim();
|
||||
|
||||
@@ -8,17 +8,34 @@
|
||||
<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">
|
||||
@@ -34,45 +51,56 @@ 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 nodesDefinition = JSON.parse(localStorage.getItem('soundboard') ?? '[]');
|
||||
const audioNodesManager = new AudioNodeManager();
|
||||
|
||||
// const playlist = reactive(new PlaylistGroupNode({
|
||||
// currentTrack: 0,
|
||||
// currentTrackTime: 0,
|
||||
// volume: 100,
|
||||
// isMuted: false,
|
||||
// sounds: [],
|
||||
// }, {
|
||||
// fs,
|
||||
// audioContext,
|
||||
// }))
|
||||
|
||||
// const sfx = reactive(new SfxGroupNode({
|
||||
// isMuted: false,
|
||||
// volume: 100,
|
||||
// sounds: [],
|
||||
// }, {
|
||||
// fs,
|
||||
// audioContext,
|
||||
// }))
|
||||
|
||||
// const tabSource = reactive(new TabSourceNode({
|
||||
// isMuted: false,
|
||||
// volume: 100,
|
||||
// sounds: [],
|
||||
// }, {
|
||||
// fs,
|
||||
// audioContext,
|
||||
// }))
|
||||
|
||||
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);
|
||||
@@ -116,5 +144,22 @@ watch(() => deviceManager.audioDevices.length, () => {
|
||||
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>
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
:supports="['play', 'stop']"
|
||||
label="Tab Audio"
|
||||
:node="node"
|
||||
@delete="emit('delete')"
|
||||
>
|
||||
</AudioCard>
|
||||
</template>
|
||||
@@ -15,6 +16,10 @@
|
||||
import {TabSourceNode} from '../../model/Audio/TabSourceNode';
|
||||
import AudioCard from './AudioCard.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
'delete': [],
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
node: TabSourceNode,
|
||||
}>();
|
||||
|
||||
Reference in New Issue
Block a user