Wavesurfer JS, how to play only one instance at the time?

25 Views Asked by At

This is a component that renders a single Wavesurfer wave:

import React, { useEffect, useRef, useState } from 'react';
import WaveSurfer from 'wavesurfer.js';
import { Box, Grid, Typography, useTheme } from '@mui/material';
import Image from 'next/image';
import { PiPauseFill, PiPlayFill } from 'react-icons/pi';
import { DesignTokens } from '@/theme/getDesignTokens';
import CustomSecondaryButton from '@/components/customButton/CustomSecondaryButton';
import { Song } from '../[artist_name]';

interface MusicPlayerProps {
  song: Song;
}
const MusicPlayer: React.FC<MusicPlayerProps> = ({ song }) => {
  const theme = useTheme<DesignTokens>();
  const containerRef = useRef<HTMLDivElement>(null);
  const waveSurferRef = useRef<any>();
  const [currentTime, setCurrentTime] = useState<string>('0:00');
  const [duration, setDuration] = useState('0:00');
  const [isPlaying, setIsPlaying] = useState(false);

  const formatTime = function (time: number) {
    return [
      Math.floor((time % 3600) / 60), // minutes
      ('00' + Math.floor(time % 60)).slice(-2), // seconds
    ].join(':');
  };

  useEffect(() => {
    const waveSurfer = WaveSurfer.create({
      container: containerRef.current as HTMLDivElement,
      normalize: true,
      height: 46,
      waveColor: theme.palette.audio.barBackground,
      progressColor: '#5b4aff',
      cursorColor: '#ddd5e9',
      cursorWidth: 2,
      barWidth: 4,
      barGap: 5,
      barRadius: 30,
      minPxPerSec: 1,
      fillParent: true,
      interact: true,
      dragToSeek: true,
      audioRate: 1,
      autoScroll: true,
      autoCenter: true,
      sampleRate: 8000,
      url: song.audioFileUrl,
    });

    waveSurfer.on('ready', () => {
      waveSurferRef.current = waveSurfer;
    });

    waveSurfer.on('play', () => {
      setIsPlaying(true);
    });

    waveSurfer.on('pause', () => {
      setIsPlaying(false);
    });

    // Show current time
    waveSurfer.on('audioprocess', function () {
      setCurrentTime(formatTime(waveSurfer.getCurrentTime()));
      console.log(formatTime(waveSurfer.getCurrentTime()));
    });

    waveSurfer.on('ready', function () {
      setDuration(formatTime(waveSurfer.getDuration()));
    });

    console.log('WAVESURFER', containerRef.current);

    return () => {
      waveSurfer.destroy();
    };
  }, [song]);



  return (
    <Grid container p={4} alignItems="center">
      <Grid
        sx={{
          pb: {
            xs: 2,
            sm: 0,
          },
        }}
        display={'flex'}
        minWidth={'300px'}
        flexDirection={'row'}
      >
        <Image
          style={{ borderRadius: '4px' }}
          src={song.coverUrl}
          priority
          width={'64px'}
          height={'64px'}
          alt="album_pic"
        />
        <Grid
          sx={{
            pl: {
              xs: 2,
              sm: 4,
            },
          }}
          justifyContent={'space-between'}
          display={'flex'}
          flexDirection={'column'}
          py={1}
        >
          <Typography sx={{ color: theme.palette.core.textPrimary }}>{song.title}</Typography>
          <Typography sx={{ color: theme.palette.core.textSecondary }}>{song.year}</Typography>
        </Grid>
      </Grid>
      <Box
        pr={4}
        pt={1}
        sx={{ cursor: 'pointer' }}
        onClick={() => waveSurferRef.current.playPause()}
      >
        {isPlaying ? <PiPauseFill size={24} /> : <PiPlayFill size={24} />}
      </Box>
      <Box pr={2}>
        <Typography>{currentTime}</Typography>
      </Box>
      <Grid item xs>
        <Box
          sx={{
            position: 'relative',
            height: '50px',
          }}
        >
          <div
            ref={containerRef}
            style={{
              position: 'absolute',
              top: 2,
              bottom: 0,
              left: 0,
              right: 0,
            }}
          />
        </Box>
      </Grid>
      <Box
        pl={1}
        sx={{
          pr: {
            xs: 3,
            sm: 6,
          },
        }}
      >
        <Typography>{duration}</Typography>
      </Box>
      <Box
        sx={{
          pl: {
            xs: 0,
            md: 3,
          },
        }}
      >
        <CustomSecondaryButton onClick={handleLicenseClick} text="License" />
      </Box>
    </Grid>
  );
};

export default MusicPlayer;

In the parent component, I render the MusicPlayer in this way:

  {songs.map((song, index) => (
        <Grid key={index}>
          <MusicPlayer song={song} />
        </Grid>
      ))}

I am seriously struggling to find a way to stop a playing song if another song on the list is played, basically, I just want ONE song playing at the time, if the user plays a song which is not in a "play" state, I want to stop the currently played song. At the moment, each song is independent, therefore if I play 3 songs, the audio of these 3 songs will overlap.

Does anyone have a suggestion on how to do this? I have been trying several options but none of them work.

Thank you :D

0

There are 0 best solutions below