0

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.

1
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a minimal reproducible example. Commented Feb 14 at 11:58

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.