mindboost-rnbo-test-project/components/experiments/AudioElement.vue

314 lines
7.2 KiB
Vue

<template>
<div class="slider-wrapper">
<!-- Slot for passing any audio element -->
<slot />
<div class="slider">
<input
id="gain-control"
v-model.number="volumeValue"
type="range"
min="0"
max="1"
step="0.001"
data-toggle="tooltip"
data-placement="top"
:title="tooltipTitle"
@wheel.prevent="changeVolumeOnWheel"
>
<span
class="slider-progress-bar"
:style="{ width: `${volumeValue * 100}%` }"
/>
</div>
<audio
:id="title"
ref="audioElement"
hidden
loop
@loadedmetadata="handleUpdatedMetadata"
@canplay="ready=true"
/>
</div>
</template>
<script lang="ts">
import { ref, watch, onMounted } from 'vue'
import { useAudioStore } from '~/stores/audio'
import { useUserStore } from '~/stores/user'
export default {
name: 'AudioElement',
props: {
src: {
type: String,
required: true
},
title: {
type: String,
default: 'Unknown'
},
tooltipTitle: {
type: String,
default: 'Change the volume by click, scroll or touch.',
required: false
},
volume: {
type: Number,
default: 1
}
},
emits: ['update:volume', 'update:loaded', 'update:playing', 'update:canplay'],
setup (props, { emit }) {
const audioStore = useAudioStore()
const audioElement = ref<HTMLAudioElement | null>(null)
const volumeValue = ref(props.volume)
const ready = ref(false)
const mute = () => {
if ((audioElement.value instanceof HTMLAudioElement)) {
const ae = audioElement.value
ae.muted = true
}
}
const play = async () => {
try {
const { $audioPrepared } = useNuxtApp()
await $audioPrepared
const sink = useUserStore().audioOutputDevice as MediaDeviceInfo
audioElement.value?.setSinkId(sink.deviceId)
if (audioElement.value instanceof HTMLAudioElement) {
// set audio element to mute, will be anyway muted after connecting to the web audio graph
audioElement.value.play()
}
} catch (error) {
useNuxtApp().$logger.error('Oouh sorry! Error while setting up audio, please reload.', error)
}
}
const pause = () => {
audioElement.value?.pause()
}
const togglePlayPause = async () => {
if (audioElement.value) {
if (audioStore.playing) {
await audioElement.value.play()
} else {
audioElement.value.pause()
}
}
}
watch(() => props.volume, (newValue) => {
volumeValue.value = newValue
})
watch(() => audioStore.playing, async (newValue) => {
if (newValue) {
updateMediaSession()
await togglePlayPause()
} else {
await togglePlayPause()
}
})
watch(volumeValue, (newValue) => {
if (audioElement.value) {
audioElement.value.volume = newValue
emit('update:volume', newValue) // Gib die Änderungen an die Elternkomponente weiter
}
})
const changeVolumeOnWheel = (event:WheelEvent) => {
// Adjust volume on wheel scroll
const deltaY = event.deltaY
if (deltaY < 0) {
const volumeAdd = (Math.min(1, volumeValue.value + 0.02))
volumeValue.value = volumeAdd
} else {
const volumeCut = (Math.max(0, volumeValue.value - 0.02))
volumeValue.value = volumeCut
}
}
const updateMediaSession = () => {
try {
const path = window.location.origin + '/images/scenery/' + props.title.toLowerCase() + '.jpg'
// useNuxtApp().$logger.log(navigator.mediaSession.metadata)
if (audioElement.value) {
audioElement.value.controls = true
navigator.mediaSession.metadata = new MediaMetadata({
title: props.title,
artist: 'mindboost',
album: 'mindboost Originale',
artwork: [
{ src: path, sizes: '192x192', type: 'image/jpeg' }
]
})
}
} catch (e) {
// useNuxtApp().$logger.error(e)
}
}
const handleUpdatedMetadata = () => {
// use the navigator for the soundscape.
if (props.title !== 'Noise') {
updateMediaSession()
}
}
onMounted(() => {
if (audioElement.value) {
audioElement.value.src = props.src
audioElement.value.volume = volumeValue.value
}
})
onUnmounted(() => {
audioElement.value = null
volumeValue.value = 0
})
return {
audioElement,
ready,
play,
pause,
handleUpdatedMetadata,
volumeValue,
togglePlayPause,
changeVolumeOnWheel,
updateMediaSession
}
}
}
</script>
<style>
.slider-wrapper{
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 1.5em;
}
.slider {
position: relative;
width: 100%;
margin: 1em auto;
}
.slider-icon {
cursor: pointer;
}
@media only screen and (max-width: 576px) {
.slider{
max-width: 80%;
}
}
.slider-progress-bar {
position: absolute;
height: 10px;
background-color: #e9c046;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-radius: 5px;
left: 0;
z-index: 0;
}
/* Allgemeine Einstellungen für den Slider */
input[type="range"] {
-webkit-appearance: none; /* Entfernt das Standard-Styling in Webkit-Browsern */
appearance: none; /* Entfernt Standard-Styling in anderen Browsern */
width: 100%;
height: 10px;
background: #fff;
border-radius: 5px;
outline: none;
cursor: pointer;
position: absolute;
z-index:1;
left: 0;
}
/* Styling für den Track in Webkit-Browsern */
input[type="range"]::-webkit-slider-runnable-track {
height: 10px;
background: transparent;
border-radius: 5px;
position: relative;
z-index: 2;
}
/* Styling für den Thumb in Webkit-Browsern */
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 20px;
width: 20px;
background: #fff;
border-radius: 50%;
margin-top: -5px;
position: relative;
z-index: 10;
border: 2px solid #e9c046;
}
/* Styling für den Track in Firefox */
input[type="range"]::-moz-range-track {
height: 10px;
background: transparent;
border-radius: 5px;
position: relative;
z-index: 1;
}
/* Styling für den Thumb in Firefox */
input[type="range"]::-moz-range-thumb {
height: 20px;
width: 20px;
background-color: #e9c046;
border: none;
border-radius: 50%;
position: relative;
z-index: 1;
border: 2px solid #e9c046;
margin-top: -5px;
}
input[type="range"]::-moz-range-progress {
height: 10px;
background-color: #e9c046;
border-radius: 5px;
}
/* Styling für den Track in Internet Explorer/Edge */
input[type="range"]::-ms-track {
height: 10px;
background: transparent;
border-color: transparent;
color: transparent;
border-radius: 5px;
}
/* Styling für den Thumb in Internet Explorer/Edge */
input[type="range"]::-ms-thumb {
height: 20px;
width: 20px;
background: #f5f5f5;
border: none;
border-radius: 50%;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);
position: relative;
z-index: 2;
border: 2px solid #e9c046;
}
/* Entfernt Ticks in IE */
input[type="range"]::-ms-ticks-after,
input[type="range"]::-ms-ticks-before {
display: none;
}
</style>