Search code examples
javascripthtmljqueryweb-audio-api

2 Audio Players Independently from each other


I'm trying to adapt someone's code from codepen.io that made this pretty visual audio player playlist. My goal I want to achieve is to have multiple of these on a single page since the original code only supported one. I'm trying to figure out how to get these audio players to behave on their own accord rather than, when I press play on one it will pause the previous or vice versa. At the moment, the problem is that they're all sharing the same audio controls and I'm trying to figure out a way to target each audio player uniquely.

Been trying at this for a couple days so I'm reaching out for help. My initial thought which you'll see in the codepen.io below was to wrap each div of the audio player in its own unique div. Then in JS, call an array of the divs and with forEach iterate the JS function for the audio playlist on each div. I know there is something wrong with my functions or perhaps my approach in general and I would love to have some feedback and direction.

Currently, I have just two audio players to start with so I can even see if my idea is working. Right now their both accessing the same songs/album covers, however I just wanted to figure out how to get them to play differently first before figuring out the rest of the stuff haha. So apologies if you notice that problem too.

2 Audio Players Independently -- Codepen.io

Lastly, I'm implementing this with some AHK scripts & Edge/IE, so I'm forced for the time being to use Legacy JS.

Thank you. Cheers!

Here is the code for the JS in case you don't want to visit codepen.io.

let musicPlayers = [
  {
    audioPlayer: ".audio-1",
    trackInfo: [
      {
        song: "https://raw.githubusercontent.com/himalayasingh/music-player-1/master/music/1.mp3",
        title: "Dawn",
        album: "Skylike - Dawn",
        art: "https://raw.githubusercontent.com/himalayasingh/music-player-1/master/img/_1.jpg",
      },
      {
        song: "https://raw.githubusercontent.com/himalayasingh/music-player-1/master/music/2.mp3",
        title: "Me & You",
        album: "Alex Skrindo - Me & You",
        art: "https://raw.githubusercontent.com/himalayasingh/music-player-1/master/img/_2.jpg",
      },
    ],
  },
  {
    audioPlayer: ".audio-2",
    trackInfo: [
      {
        song: "https://raw.githubusercontent.com/himalayasingh/music-player-1/master/music/3.mp3",
        title: "Electro Boy",
        album: "Kaaze - Electro Boy",
        art: "https://raw.githubusercontent.com/himalayasingh/music-player-1/master/img/_3.jpg",
      },
      {
        song: "https://raw.githubusercontent.com/himalayasingh/music-player-1/master/music/4.mp3",
        title: "Home",
        album: "Martin Garrix - Home",
        art: "https://raw.githubusercontent.com/himalayasingh/music-player-1/master/img/_5.jpg",
      },
    ],
  },
]

console.log("number of audio players:", musicPlayers.length)
musicPlayers.forEach(function (musicPlayer, index) {
  let tracksObj = { song: [], title: [], album: [], art: [] }
  musicPlayer.trackInfo.forEach(function (track) {
    tracksObj.song.push(track.song)
    tracksObj.title.push(track.title)
    tracksObj.album.push(track.album)
    tracksObj.art.push(track.art)
  })
  console.log(tracksObj.art)
  // console.log(index, musicPlayer.trackInfo[0].song)
  $(function () {
    let playerTrack = $(
        musicPlayer.audioPlayer + " > #app-cover > #player > #player-track"
      ),
      bgArtwork = $(musicPlayer.audioPlayer + " > #app-cover > #bg-artwork"),
      bgArtworkUrl,
      albumName = playerTrack.children("#album-name"),
      trackName = playerTrack.children("#track-name"),
      trackTime = playerTrack.children("#track-time"),
      tProgress = trackTime.children("#current-time"),
      tTime = trackTime.children("#track-length"),
      sArea = playerTrack.children("#s-area"),
      insTime = sArea.children("#ins-time"),
      sHover = sArea.children("#s-hover"),
      seekBar = sArea.children("#seek-bar"),
      playerContent = $(
        musicPlayer.audioPlayer + " > #app-cover > #player > #player-content"
      ),
      albumArt = playerContent.children("#album-art"),
      playerControls = playerContent.children("#player-controls"),
      playPauseButton = playerControls
        .children(".control")
        .children("#play-pause-button"),
      i = playPauseButton.find("i"),
      seekT,
      seekLoc,
      seekBarPos,
      cM,
      ctMinutes,
      ctSeconds,
      curMinutes,
      curSeconds,
      durMinutes,
      durSeconds,
      playProgress,
      bTime,
      nTime = 0,
      buffInterval = null,
      tFlag = false,
      albums = tracksObj.album,
      trackNames = tracksObj.title,
      albumArtworks = tracksObj.art,
      trackUrl = tracksObj.song,
      playPreviousTrackButton = playerControls
        .children(".control")
        .children("#play-previous"),
      playNextTrackButton = playerControls
        .children(".control")
        .children("#play-next"),
      currIndex = -1

    function playPause() {
      setTimeout(function () {
        if (audio.paused) {
          playerTrack.addClass("active")
          albumArt.addClass("active")
          checkBuffering()
          i.attr("class", "fas fa-pause")
          audio.play()
          console.log(currIndex)
        } else {
          playerTrack.removeClass("active")
          albumArt.removeClass("active")
          clearInterval(buffInterval)
          albumArt.removeClass("buffering")
          i.attr("class", "fas fa-play")
          audio.pause()
        }
      }, 300)
    }

    function showHover(event) {
      seekBarPos = sArea.offset()
      seekT = event.clientX - seekBarPos.left
      seekLoc = audio.duration * (seekT / sArea.outerWidth())

      sHover.width(seekT)

      cM = seekLoc / 60

      ctMinutes = Math.floor(cM)
      ctSeconds = Math.floor(seekLoc - ctMinutes * 60)

      if (ctMinutes < 0 || ctSeconds < 0) return

      if (ctMinutes < 0 || ctSeconds < 0) return

      if (ctMinutes < 10) ctMinutes = "0" + ctMinutes
      if (ctSeconds < 10) ctSeconds = "0" + ctSeconds

      if (isNaN(ctMinutes) || isNaN(ctSeconds)) insTime.text("--:--")
      else insTime.text(ctMinutes + ":" + ctSeconds)

      insTime.css({ left: seekT, "margin-left": "-21px" }).fadeIn(0)
    }

    function hideHover() {
      sHover.width(0)
      insTime
        .text("00:00")
        .css({ left: "0px", "margin-left": "0px" })
        .fadeOut(0)
    }

    function playFromClickedPos() {
      audio.currentTime = seekLoc
      seekBar.width(seekT)
      hideHover()
    }

    function updateCurrTime() {
      nTime = new Date()
      nTime = nTime.getTime()

      if (!tFlag) {
        tFlag = true
        trackTime.addClass("active")
      }

      curMinutes = Math.floor(audio.currentTime / 60)
      curSeconds = Math.floor(audio.currentTime - curMinutes * 60)

      durMinutes = Math.floor(audio.duration / 60)
      durSeconds = Math.floor(audio.duration - durMinutes * 60)

      playProgress = (audio.currentTime / audio.duration) * 100

      if (curMinutes < 10) curMinutes = "0" + curMinutes
      if (curSeconds < 10) curSeconds = "0" + curSeconds

      if (durMinutes < 10) durMinutes = "0" + durMinutes
      if (durSeconds < 10) durSeconds = "0" + durSeconds

      if (isNaN(curMinutes) || isNaN(curSeconds)) tProgress.text("00:00")
      else tProgress.text(curMinutes + ":" + curSeconds)

      if (isNaN(durMinutes) || isNaN(durSeconds)) tTime.text("00:00")
      else tTime.text(durMinutes + ":" + durSeconds)

      if (
        isNaN(curMinutes) ||
        isNaN(curSeconds) ||
        isNaN(durMinutes) ||
        isNaN(durSeconds)
      )
        trackTime.removeClass("active")
      else trackTime.addClass("active")

      seekBar.width(playProgress + "%")

      if (playProgress == 100) {
        i.attr("class", "fa fa-play")
        seekBar.width(0)
        tProgress.text("00:00")
        albumArt.removeClass("buffering").removeClass("active")
        clearInterval(buffInterval)
      }
    }

    function checkBuffering() {
      clearInterval(buffInterval)
      buffInterval = setInterval(function () {
        if (nTime == 0 || bTime - nTime > 1000) albumArt.addClass("buffering")
        else albumArt.removeClass("buffering")

        bTime = new Date()
        bTime = bTime.getTime()
      }, 100)
    }

    function selectTrack(flag) {
      // console.log("flag = ", flag)
      if (flag == 0 || flag == 1) ++currIndex
      else --currIndex

      if (currIndex > -1 && currIndex < albumArtworks.length) {
        if (flag == 0) i.attr("class", "fa fa-play")
        else {
          albumArt.removeClass("buffering")
          i.attr("class", "fa fa-pause")
        }

        seekBar.width(0)
        trackTime.removeClass("active")
        tProgress.text("00:00")
        tTime.text("00:00")

        currAlbum = albums[currIndex]
        currTrackName = trackNames[currIndex]
        currArtwork = albumArtworks[currIndex]

        audio.src = trackUrl[currIndex]

        nTime = 0
        bTime = new Date()
        bTime = bTime.getTime()

        if (flag != 0) {
          audio.play()
          playerTrack.addClass("active")
          albumArt.addClass("active")

          clearInterval(buffInterval)
          checkBuffering()
        }

        albumName.text(currAlbum)
        trackName.text(currTrackName)
        albumArt.find("img.active").removeClass("active")
        albumArt.find("#current-album-art").addClass("active")
        albumArt.find("img.active").attr("src", currArtwork)

        bgArtworkUrl = albumArt.find("img.active").attr("src")

        bgArtwork.css({ "background-image": "url(" + bgArtworkUrl + ")" })
      } else {
        if (flag == 0 || flag == 1) --currIndex
        else ++currIndex
      }
    }

    function initPlayer() {
      // audio = new Audio()
      audio = document.createElement("audio")

      selectTrack(0)

      audio.loop = false

      playPauseButton.on("click", playPause)

      sArea.mousemove(function (event) {
        showHover(event)
      })

      sArea.mouseout(hideHover)

      sArea.on("click", playFromClickedPos)

      $(audio).on("timeupdate", updateCurrTime)

      playPreviousTrackButton.on("click", function () {
        selectTrack(-1)
        console.log(currIndex)
      })
      playNextTrackButton.on("click", function () {
        selectTrack(1)
      })
    }

    initPlayer()
  })
})


Solution

  • Alright, I've figured out why the players won't play independently, and it seems to be related to some type of JS scoping issue, and how the audio variable was being assigned in the init function. I'm still not exactly sure why, but it seems as every audio player created was referencing the same exact audio instance.

    If you change two things, then it should work for you.

    First, at the very beginning of the immediately invoked function expression, declare an audio variable, set to nothing.

     $(function () {
        let audio, playerTrack = $(
            musicPlayer.audioPlayer + " > #app-cover > #player > #player-track"
          ),
        ...rest of code
    

    Next, go into the init function, and change the code from this:

    // audio = new Audio()
    audio = document.createElement("audio")
    

    to this:

    audio = new Audio()
    

    This is bugging me, because in theory it looks like a new audio instance should have been assigned to each, but apparently not. Anyways...this should get you headed in the right direction.