I created an html file with a javascprit script that allows you to do fade operations between two audio files. The files are played in two players, Player1 and Player2 respectively. On PC/Mac it works but on iPhone I don't get the gradual increase/decrease of volume. The files are played but they stop suddenly.
document.addEventListener("DOMContentLoaded", function () {
const players = [
document.getElementById("audioPlayer1"),
document.getElementById("audioPlayer2"),
]
const audioContext = new (window.AudioContext || window.webkitAudioContext)()
const gainNodes = []
let isActionInProgress = false
// Configura ogni lettore con un GainNode
players.forEach((player, index) => {
try {
const source = audioContext.createMediaElementSource(player)
const gainNode = audioContext.createGain()
source.connect(gainNode).connect(audioContext.destination)
gainNodes.push(gainNode)
// Sincronizza le barre del volume con i GainNode
players.forEach((player, index) => {
const volumeBar = document.getElementById(`volumeBar${index + 1}`)
const gainNode = gainNodes[index]
if (gainNode && volumeBar) {
setInterval(() => {
const currentVolume = gainNode.gain.value * 100
volumeBar.value = Math.round(currentVolume) // Aggiorna il valore della barra
}, 100) // Aggiorna ogni 100ms
}
})
} catch (error) {
console.error(
"Errore durante la configurazione di AudioContext per:",
player,
error,
)
gainNodes.push(null)
}
})
console.log("Array dei lettori audio:", players)
console.log("Array dei GainNode associati:", gainNodes)
// Funzione per il fade
function fade(gainNode, startValue, endValue, duration, onComplete) {
const currentTime = audioContext.currentTime
gainNode.gain.cancelScheduledValues(currentTime)
gainNode.gain.setValueAtTime(startValue, currentTime)
gainNode.gain.linearRampToValueAtTime(endValue, currentTime + duration)
if (onComplete) {
setTimeout(onComplete, duration * 1000)
}
}
// Funzione per avviare un brano con fade-in e fermare altri con fade-out
function playWithFade(button) {
if (isActionInProgress) {
console.error("Un'azione è già in corso. Attendi il completamento.")
return
}
const audioSrc = button.dataset.src
const initialVolume = parseFloat(button.dataset.initialVolume) || 0
const fadeInTime = parseFloat(button.dataset.fadeinTime) || 0
const holdTime = parseFloat(button.dataset.holdTime) || 0
const fadeOutTime = parseFloat(button.dataset.fadeoutTime) || 0
const fadeOutVolume =
button.dataset.fadeoutVolume !== undefined
? parseFloat(button.dataset.fadeoutVolume)
: initialVolume
const loop = button.dataset.loop === "true"
audioContext
.resume()
.then(() => {
console.log("AudioContext ripreso correttamente.")
})
.catch((error) => {
console.error("Errore durante la ripresa dell'AudioContext:", error)
})
const availablePlayer = players.find((p) => p.paused && p.currentTime === 0)
if (!availablePlayer) {
console.error("Nessun lettore disponibile.")
return
}
const gainNode = gainNodes[players.indexOf(availablePlayer)]
if (!gainNode) {
console.error("GainNode non disponibile per il lettore.")
return
}
const currentTrackDisplay =
availablePlayer.id === "audioPlayer1"
? document.getElementById("currentTrack1")
: document.getElementById("currentTrack2")
// Aggiorna il nome del brano in esecuzione
currentTrackDisplay.textContent = button.innerText || "Brano sconosciuto"
currentTrackDisplay.style.color = "green"
// Ferma altri lettori con fade-out graduale
players.forEach((player, index) => {
if (!player.paused && player !== availablePlayer) {
const otherGainNode = gainNodes[index]
const otherTrackDisplay =
player.id === "audioPlayer1"
? document.getElementById("currentTrack1")
: document.getElementById("currentTrack2")
// Imposta fadeOutTime a 5 secondi
const fadeOutTime = 5
// Esegui fade-out graduale
fade(otherGainNode, otherGainNode.gain.value, 0, fadeOutTime, () => {
player.pause()
player.currentTime = 0
// Usa la funzione dedicata per aggiornare il display
updateTrackDisplay(player, otherTrackDisplay)
})
}
})
// Se è specificato solo il volume iniziale
if (fadeInTime === 0 && holdTime === 0 && fadeOutTime === 0) {
console.log("Avvio della traccia con volume fisso:", initialVolume)
// Imposta il volume iniziale direttamente
gainNode.gain.setValueAtTime(initialVolume, audioContext.currentTime)
availablePlayer.src = audioSrc
availablePlayer.loop = loop
availablePlayer.currentTime = 0
availablePlayer
.play()
.then(() => {
console.log(
"Riproduzione avviata con successo a volume fisso:",
initialVolume,
)
})
.catch((error) => {
console.error("Errore durante la riproduzione:", error)
})
isActionInProgress = false // Nessuna azione complessa in corso
return // Termina qui perché non ci sono fade da gestire
}
// Configura il lettore per il nuovo brano
isActionInProgress = true
availablePlayer.src = audioSrc
availablePlayer.currentTime = 0
availablePlayer.loop = loop
availablePlayer
.play()
.then(() => {
console.log("Riproduzione avviata con successo.")
})
.catch((error) => {
console.error("Errore durante la riproduzione:", error)
isActionInProgress = false
})
// Gestione del fade-in, hold-time e fade-out
fade(gainNode, initialVolume, 1, fadeInTime, () => {
if (holdTime > 0 && fadeOutTime > 0) {
setTimeout(() => {
fade(gainNode, 1, fadeOutVolume, fadeOutTime, () => {
isActionInProgress = false
// Usa la funzione dedicata per aggiornare il display
updateTrackDisplay(availablePlayer, currentTrackDisplay)
})
}, holdTime * 1000)
} else {
// Caso in cui non ci sono holdTime o fadeOutTime definiti
isActionInProgress = false
// Usa la funzione dedicata per aggiornare il display
updateTrackDisplay(availablePlayer, currentTrackDisplay)
}
})
// Evento per aggiornare la scritta quando il brano finisce
availablePlayer.onended = () => {
//if (!loop) {
currentTrackDisplay.textContent = `${currentTrackDisplay.textContent.split(" ")[0]} fermato`
currentTrackDisplay.style.color = "red"
}
//};
}
// Funzione Unica per Stop
function stopAudio(button) {
if (isActionInProgress) {
console.error("Un'azione è già in corso. Attendi il completamento.")
return
}
const fadeOutTime = parseFloat(button.dataset.fadeoutTime) || 0 // Default 0s
const fadeInTime = parseFloat(button.dataset.fadeinTime) || 0 // Default 0s
const holdTime = parseFloat(button.dataset.holdTime) || 0 // Default 0s
players.forEach((player, index) => {
if (!player.paused) {
const gainNode = gainNodes[index]
if (!gainNode) {
console.error("GainNode non disponibile per il lettore.")
return
}
const currentTrackDisplay =
player.id === "audioPlayer1"
? document.getElementById("currentTrack1")
: document.getElementById("currentTrack2")
const currentGain = gainNode.gain.value // Volume corrente
isActionInProgress = true
if (fadeInTime > 0 && holdTime > 0) {
// Stop FIHO (Fade-In, Hold, Fade-Out)
fade(gainNode, currentGain, 1, fadeInTime, () => {
setTimeout(() => {
fade(gainNode, 1, 0, fadeOutTime, () => {
player.pause()
player.currentTime = 0
isActionInProgress = false
currentTrackDisplay.textContent += " fermato"
currentTrackDisplay.style.color = "red"
console.log("Riproduzione interrotta con successo.")
})
}, holdTime * 1000)
})
} else {
// Solo Fade-Out
fade(gainNode, currentGain, 0, fadeOutTime, () => {
player.pause()
player.currentTime = 0
isActionInProgress = false
currentTrackDisplay.textContent += " fermato"
currentTrackDisplay.style.color = "red"
console.log("Riproduzione interrotta con successo.")
})
}
}
})
}
// Assegna eventi ai pulsanti di riproduzione
document.querySelectorAll("button[data-src]").forEach((button) => {
button.addEventListener("click", function () {
playWithFade(this)
})
button.addEventListener("touchstart", function () {
playWithFade(this)
})
})
// Assegna eventi ai pulsanti di stop
document.querySelectorAll(".stopAction").forEach((button) => {
button.addEventListener("click", function () {
stopAudio(this)
})
})
// Controlla che il Player sia fermo e aggiorna la scritta in rosso
function updateTrackDisplay(player, displayElement) {
if (player.paused && player.currentTime === 0) {
displayElement.textContent = `${displayElement.textContent} fermato`
displayElement.style.color = "red"
console.log("Lettore fermo. Scritta aggiornata in rosso.")
} else {
console.log("Lettore ancora attivo o non fermo. Nessun aggiornamento.")
}
}
})
I didn't succeed with Howler or Tone either. I'm sure I'm doing something wrong. Could you suggest something to refer to?
My html/js on: https://jsfiddle.net/4q2vk7do/1/#&togetherjs=gBKsnl85vA
On iPhone I don't get the gradual increase/decrease of volume. The audio files are played but they stop suddenly.