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

225 lines
6.0 KiB
Vue

<template>
<h1>AUDiO Tag mit dem volume: {{ safeVolume }}</h1>
<p>audioStatus {{ audioStatus }}</p>
<button @click="muteAudio">muteAudio</button>
<audio
v-if="src"
ref="audioElement"
:src="src"
loop="true"
:volume="safeVolume"
mediagroup="noiseGroup"
sinkId=""
@canplay="()=> $emit('canplay')"
/>
</template>
<script lang="ts">
import type { Logger } from 'pino'
import { defineComponent, ref, watch, computed } from 'vue'
import { ensureAudio, useAudioStore } from '~/stores/audio'
import { useUserStore } from '~/stores/user'
export default defineComponent({
name: 'AudioTag',
props: {
src: {
type: String,
required: true
},
volume: {
type: [Number, String],
default: 0.23,
validator: (value:any) => {
const num = parseFloat(value)
return num >= -3.4 && num <= 3.4
}
},
play: {
type: Boolean,
default: false
}
},
emits: ['canplay'],
setup (props, { emit: $emit }) {
const logger = useNuxtApp().$logger as Logger
const audioElement = ref<HTMLAudioElement | null>(null)
const audioStatus = ref<'stopped' | 'playing' | 'paused'>('stopped')
const safeVolume = computed(() => {
const vol = parseFloat(props.volume.toString())
return Math.max(0, Math.min(1, isNaN(vol) ? 1 : vol))
})
const muteAudio = () => {
if (audioElement.value) {
audioElement.value.muted = !audioElement.value.muted
}
}
// kleine Hilfsfunktion für Volume-Fading
const fadeVolume = (audio: HTMLAudioElement, from: number, to: number, duration: number) => {
const steps = 20
const stepTime = duration / steps
let currentStep = 0
const fadeInterval = setInterval(() => {
currentStep++
const progress = currentStep / steps
const newVolume = from + (to - from) * progress
audio.volume = Math.max(0, Math.min(1, newVolume))
if (currentStep >= steps) {
clearInterval(fadeInterval)
audio.volume = to
}
}, stepTime)
}
const startPlaying = async () => {
const audioStore = useAudioStore()
await ensureAudio()
const sink = useUserStore().audioOutputDevice as MediaDeviceInfo
audioElement.value?.setSinkId(sink.deviceId)
if (audioElement.value && audioStore.isPlaying()) {
try {
const audio = audioElement.value
audio.currentTime = 0
audio.volume = 0
audio.muted = false
await audio.play().catch((error) => {
logger.warn('Error playing audio:', error)
})
fadeVolume(audio, 0, 0.3, 8000) // weiches Fade-In
audioStatus.value = 'playing'
} catch (error) {
logger.warn('Error starting audio:', error)
}
} else {
logger.info('Audio Playback has not started, audioStore is not playing')
}
}
const pausePlaying = () => {
if (audioElement.value && audioStatus.value === 'playing') {
try {
const audio = audioElement.value
const initialVolume = audio.volume
fadeVolume(audio, initialVolume, 0, 300)
setTimeout(() => {
audio.pause()
audioStatus.value = 'paused'
}, 350) // minimal länger als Fade
} catch (error) {
logger.warn('Error pausing audio:', error)
}
}
}
const stopAndResetPlaying = () => {
if (audioElement.value) {
try {
const audio = audioElement.value
const sink = useUserStore().audioOutputDevice as MediaDeviceInfo
audio.setSinkId(sink.deviceId)
audio.pause()
audio.currentTime = 0
audio.volume = safeVolume.value
audio.muted = true
audioStatus.value = 'stopped'
} catch (error) {
logger.warn('Error stopping audio:', error)
}
}
}
const resumePlaying = async () => {
const audioStore = useAudioStore()
await ensureAudio()
if (audioElement.value && audioStore.isPlaying() && audioStatus.value === 'paused') {
try {
const audio = audioElement.value
audio.muted = false
audio.volume = 0
await audio.play().catch((error) => {
logger.warn('Error resuming audio:', error)
})
fadeVolume(audio, 0, safeVolume.value, 500) // kürzeres Fade-In
audioStatus.value = 'playing'
} catch (error) {
logger.warn('Error resuming audio:', error)
}
} else {
logger.info('Audio Resume not possible: wrong audio state')
}
}
const stopPlaying = () => {
if (audioElement.value) {
try {
const audio = audioElement.value
const initialVolume = audio.volume
fadeVolume(audio, initialVolume, 0, 500)
setTimeout(() => {
audio.pause()
audio.currentTime = 0
audio.volume = safeVolume.value
audio.muted = true
audioStatus.value = 'stopped'
}, 550)
} catch (error) {
logger.warn('Error stopping and fading audio:', error)
}
}
}
watch(() => props.src, () => {
stopAndResetPlaying()
if (audioElement.value) {
audioElement.value.load()
}
})
watch(() => props.play, (newPlay) => {
setTimeout(() => {
if (newPlay === false) {
pausePlaying()
} else if (audioStatus.value === 'paused') {
resumePlaying()
} else {
startPlaying()
}
}, 500)
})
watch(() => safeVolume.value, (newVolume) => {
if (audioElement.value && audioStatus.value === 'playing') {
try {
audioElement.value.volume = newVolume
} catch (error) {
logger.warn('Error updating audio volume:', error)
}
}
})
return {
audioElement,
startPlaying,
pausePlaying,
stopPlaying,
resumePlaying,
safeVolume,
muteAudio,
audioStatus
}
}
})
</script>