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

286 lines
7.8 KiB
Vue

<template>
<div>
<!-- Slot for passing any audio element -->
<slot />
<div class="slider">
<input
id="gain-control"
v-model="volume"
type="range"
min="0"
max="1"
step="0.02"
@wheel.prevent="changeVolumeOnWheel"
>
</div>
<audio
ref="audioElement"
hidden
autoplay
muted
loop
@play="handlePlay"
@pause="handlePause"
@keydown="handleKeyDown"
@loadedmetadata="handleLoaded"
/>
</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'
}
},
emits: ['update:volume', 'update:loaded', 'update:playing', 'update:fadeout'],
setup (props, { emit }) {
const audioStore = useAudioStore()
const audioElement = ref<HTMLAudioElement | null>(null)
const volume = ref(1)
const play = () => {
try {
// set audio element to mute, will be anyway muted after connecting to the web audio graph
// useNuxtApp().$logger.log('Trigger Play of the audioelement tag: CurrentTime ' + currentTime + ' paused' + paused + ' ended' + ended)
const sink = useUserStore().audioOutputDevice as MediaDeviceInfo
audioElement.value?.setSinkId(sink.deviceId)
audioElement.value?.play()
if (audioElement.value) { audioElement.value.muted = true }
} catch (e) {
}
}
const pause = () => {
audioElement.value?.pause()
}
const pauseFadeOut = () => {
// useNuxtApp().$logger.log('pauseFadeOut')
emit('update:fadeout')
setTimeout(() => {
pause()
}, 2000)
}
const isPlaying = () => {
return audioStore.playing
}
onUpdated(() => {
if (props.title === 'Noise') {
// useNuxtApp().$logger.log('cannot set control of noise to false')
} else if ('mediaSession' in navigator) {
const path = window.location.origin + '/images/scenery/' + props.title.toLowerCase() + '.jpg'
navigator.mediaSession.metadata = new MediaMetadata({
title: props.title,
artist: 'mindboost',
album: 'mindboost Originale',
artwork: [
{ src: path, sizes: '192x192', type: 'image/jpeg' }
]
})
}
})
watch(() => audioStore.playing, (newValue) => {
if (newValue) {
// useNuxtApp().$logger.log('PLAY THE AUDIO')
play()
} else {
// useNuxtApp().$logger.log('PAUSE THE AUDIO')
pauseFadeOut()
}
})
watch(() => volume.value, (newValue) => {
if (audioElement.value) {
emit('update:volume', newValue)
}
})
const changeVolumeOnWheel = (event:WheelEvent) => {
// Adjust volume on wheel scroll
const deltaY = event.deltaY
if (deltaY < 0) {
const volumeAdd = (Math.min(1, volume.value + 0.02))
volume.value = volumeAdd
} else {
const volumeCut = (Math.max(0, volume.value - 0.02))
volume.value = volumeCut
}
}
let enableKeyHandler = true
const debounce = <T extends (...args: any[]) => void>(func: T, timeout: number = 1500): () => void => {
let timer: NodeJS.Timeout | null
return () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => { func.apply(this) }, timeout)
}
}
const reEnableKeyHandler = debounce(() => {
enableKeyHandler = true
})
const handleKeyDown = (e: KeyboardEvent) => {
if (!enableKeyHandler) { return }
if (e.code === 'Space') {
e.preventDefault() // Prevent the default action (scrolling)
// useNuxtApp().$logger.log('The current state is ' + audioStore.playing + ' so start or stop audio')
if (audioStore.playing) {
handlePause()
} else {
handlePlay()
}
enableKeyHandler = false // Disable handler
reEnableKeyHandler()
}
}
const handlePlay = () => {
// useNuxtApp().$logger.log('handlePlay called ' + audioStore.isPlaying())
// First handlePlay is triggered by the autoplay, so no need to start play
emit('update:playing', true)
audioStore.setPlaying(true)
// useNuxtApp().$logger.log('handlePlay ended with ' + audioStore.isPlaying())
}
const handlePause = () => {
// useNuxtApp().$logger.log('handlePause: Change play state to false')
emit('update:playing', false)
audioStore.playing = false
}
const handleLoaded = () => {
emit('update:loaded', true)
}
onMounted(() => {
if (audioElement.value) {
audioElement.value.src = props.src
if (props.title !== 'Noise') {
window.addEventListener('keydown', handleKeyDown.bind(this))
if ('mediaSession' in navigator) {
// Play action
navigator.mediaSession.setActionHandler('play', (_e) => {
handlePlay()
// if (!enableMediaHandler) {
/// / useNuxtApp().$logger.log('during event handling - being busy ')
// return
// }
// enableMediaHandler = false // Disable handler
// reEnableMediaHandler()
// // Your play action here
})
// Stop action
navigator.mediaSession.setActionHandler('stop', (_e) => {
// if (!enableMediaHandler) { return }
// // Your stop action here
// if (audioElement.value) {
// audioElement.value.currentTime = 0
// audioElement.value.src = ''
// audioElement.value.removeAttribute('src')
// }
// audioStore.playing = false
})
// Pause action
navigator.mediaSession.setActionHandler('pause', (_e) => {
handlePause()
// if (!enableMediaHandler) {
/// / useNuxtApp().$logger.log('during event handling - being busy ')
// return
// }
// audioStore.playing = false
// enableMediaHandler = false // Disable handler
// reEnableMediaHandler()
})
}
}
}
})
onRenderTriggered(() => {
// useNuxtApp().$logger.log('render AudioElement-----------' + props.title)
})
onUnmounted(() => {
window.removeEventListener('keydown', handleKeyDown.bind(this))
})
return {
audioElement,
play,
pause,
isPlaying,
handlePlay,
handlePause,
handleLoaded,
handleKeyDown,
volume,
changeVolumeOnWheel
}
}
}
</script>
<style scoped>
.slider {
margin-bottom: 1em;
}
/* Style adjustments if needed */
input[type="range"] {
background:transparent !important;
}
/* Styles the track */
input[type="range"]::-webkit-slider-runnable-track {
background: #e9c046; /* yellow track */
height: 10px;
border-radius: 5px;
}
input[type="range"]::-moz-range-track {
background: #e9c046; /* yellow track */
height: 10px;
border-radius: 5px;
}
input[type="range"]::-ms-track {
background: #e9c046; /* yellow track */
border-color: transparent;
color: transparent;
height: 8px;
}
/* Styles the thumb */
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none; /* Required to style in Webkit browsers */
margin-top: -6px; /* Adjusts the position of the thumb relative to the track */
}
input[type="range"]::-moz-range-thumb {
border: none; /* Removes any default border */
}
input[type="range"]::-ms-thumb {
margin-top: 0; /* May need to adjust the position similar to webkit */
border: none; /* Removes any default border */
}
</style>