Displaying audio waveforms in React

Viewed 47

So i´m building this webpage which allow users to upload a song, and the displayind that sound as a card on the home-page. Sort of like Soundcloud...

Im just getting to learn React, after coming from html, css and JS. So please understand im new to this all.

I´ve been researched the topic alot, and no one has seemed to work for me.

Ive been trying howler.js, and wavesurfer.js, without any luck of displaying waveforms.

have anyone else tried doing this before? someone who could maybe help out?

import { ErrorResponse } from '@remix-run/router';
import React from 'react'
import wavesurfer from 'wavesurfer.js'
import "./css/audio.css"
import { useRef } from 'react';

export const AudioVisualizer = (props) => {
// the homepage has a function to map through all the objects     in the 
// database, and in return i get every object. I then get the link from each
// object and pass this link into this function as an ARgument. 
let link = props; 
const audioRef = useRef();
console.log("here is props:  " + link);

try {

var audioTrack = wavesurfer.create({
    container: audioRef,
    wavecolor: "#eee",
    progressColor: "red",
    barWidth: 2,
});

audioTrack.load(link);
} catch (ErrorResponse)  {
    console.error("Something happened..");
    return ErrorResponse;
};

return (
    <div className='audio' ref={audioRef}>

    </div>
)

}

From there I have the actual Home.js page where I want to display the returned from the function above.

the home.js file looks like this:

import React, { useEffect, useState } from 'react';
import '../components/css/home/home.css';
import {collection, getDocs, onSnapshot} from 'firebase/firestore';
import {db} from '../firebase'
import { useNavigate } from 'react-router-dom';
import {ClipLoader} from 'react-spinners';
import {AudioVisualizer} from "../components/audioVisualizer"


const Home = () => {
const [songs, setSongs] = useState([]);
const [loading, setLoading] = useState(false);
const navigate = useNavigate();


useEffect(() => {
    setLoading(true);
    const retrieveSongs = onSnapshot(
        collection(db, "songs"), 
        (snapshot) => {
            let arrayList = [];
            snapshot.docs.forEach((doc) => {
                arrayList.push({ id: doc.id, ...doc.data() });
            });
            setSongs(arrayList);
            setLoading(false);

        }, 
        (error) => {
            console.log(error);
        }
    );

    return () => {
        retrieveSongs();
    };
    
}, []);

return (
    <div className='home_wrapper'>
        <>
        
            {loading ? 
                <ClipLoader color="#36d7b7" />
            :

                <div className='homepage_container'>

                    {   songs.map((data) => {
                        return (
                            <article key={data.id} className='card'>
                                <div className='card_content'>
                                    <img className='card_image' src={data.image} />

                                    <div className='song_info'>
                                    <h2>{data.title}</h2>
                                    <h4>{data.artist}</h4>
                                    </div>

                                    <div className='audioplayer'>
                                    {AudioVisualizer(data.audio)}
                                    {/* <ReactAudioPlayer src={data.audio} autoPlay controls/> */}
                                    {/* <Waveform className="audio_file" audio={data.audio}/> */}
                                    </div>

                                </div>

                                <div className='card_content_extra'>
                                    <button onClick={() => navigate('/update/${data.id}')}>Edit</button>
                                    <button >Listen</button>
                                </div>

                                {/* <div id="waveform"></div>
                                    <button class="btn btn-primary" onclick="wavesurfer.playPause()">
                                    <i class="glyphicon glyphicon-play"></i>Play/Pause
                                    </button> */}
                            </article>
                        )
                    })}
                </div>
            }
        </>
    </div>
)

}

export default Home

UPDATE::

So as i described in my comment. When i am mapping through the songs object from my database, the waveform wont display. When i pass a direct link to the component it works. but when im passing my object "audio", and getting the value, , it will not show the waveform. When i try to console.log(data.audio) // it returns undefined.

see for yourself: As you can see from the console.log, it acts weird..

1 Answers

The reference to the DOM element is accessed by the .current property Not the reference object created by React.

You could use the useEffect hook, to load the data.

Then create the AudioVisualizer Component in the JSX react way and pass the link to the wavesurfer.

Also the wavesurfer dom object need to have some size.

Have a look at this mini example:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { useRef, useEffect } from 'react';
import wavesurfer from 'wavesurfer.js'

const AudioVisualizer = (props) => {

  const audioRef = useRef();

  useEffect(()=>{
    if (audioRef.current){
      let audioTrack = wavesurfer.create({
          container: audioRef.current,
      });
      audioTrack.load(props.link);
    }
  })

  return <div style={{minWidth: "200px"}} className='audio' ref={audioRef}></div> 
}

function App(props) {
  return (
    <div className='App'>
      <AudioVisualizer link={"https://actions.google.com/sounds/v1/science_fiction/creature_distortion_white_noise.ogg"}></AudioVisualizer>
    </div>
  );
}

ReactDOM.createRoot( 
  document.querySelector('#root')
).render(<App />)
Related