286 lines
7.8 KiB
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>
|