UNPKG

@nafr/echo-ui

Version:

A UI library born for WAA

99 lines (98 loc) 3.52 kB
import * as Tone from 'tone'; import { useEffect, useRef, useState, useCallback } from 'react'; import { logger } from '../lib/log'; const FFT_SIZE = 1024; /** * useSpectrogram is a custom React hook for analyzing and visualizing audio frequencies using Tone.js. * It creates an FFT (Fast Fourier Transform) based analyzer to process audio data and provide spectrogram data points. * * @param {UseSpectrogramProps} props - The configuration properties for the hook. * @param {number} props.fftSize - The size of the FFT. Represents the window size in samples that is used when performing a FFT. * - Default value is 1024. * * @returns {object} An object containing various properties and methods to interact with the spectrogram: * - init: A method to initialize the analyser. * - analyser: An instance of Tone.Analyser used for analyzing audio frequencies. * - data: An array of spectrogram data points. * - getData: A method to fetch the latest spectrogram data from the analyser. * - observe: A method to start observing and updating the spectrogram data. * - cancelObserve: A method to stop observing the spectrogram data. * - error: A boolean indicating whether an error has occurred. * - errorMessage: A string containing the error message if an error has occurred. */ export const useSpectrogram = (props = {}) => { const { fftSize = FFT_SIZE } = props; const analyser = useRef(null); const observerId = useRef(0); const [data, setData] = useState([]); const [error, setError] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const handleError = useCallback((err) => { setError(true); setErrorMessage(err); logger.error(err); }, []); useEffect(() => { return () => { analyser.current?.dispose(); cancelObserve(); }; }, []); useEffect(() => { if (!analyser.current || error) return; try { analyser.current.size = fftSize; } catch (err) { handleError(err); } }, [fftSize, handleError, error]); const init = useCallback(() => { try { analyser.current = new Tone.Analyser('fft', fftSize); } catch (err) { handleError(err); } }, [fftSize, handleError]); const getData = useCallback(() => { if (!analyser.current || error) return; try { const spectrogramData = analyser.current.getValue(); if (spectrogramData instanceof Float32Array) { const formattedData = Array.from(spectrogramData).map((amplitude, frequency) => ({ frequency, amplitude, })); setData(formattedData); } } catch (err) { handleError(err); } }, [error, handleError]); const observe = useCallback(() => { if (!analyser.current || error) return; getData(); observerId.current = requestAnimationFrame(observe); }, [getData, error]); const cancelObserve = useCallback(() => { if (observerId.current && !error) { setData([]); cancelAnimationFrame(observerId.current); } }, [error]); return { analyser, data, init, getData, observe, cancelObserve, error, errorMessage, }; };