UNPKG

@nafr/echo-ui

Version:

A UI library born for WAA

93 lines (92 loc) 3.3 kB
import * as Tone from 'tone'; import { useCallback, useEffect, useRef, useState } from 'react'; import { logger } from '../lib/log'; const FFT_SIZE = 1024; /** * `useOscilloscope` is a custom React hook that integrates with Tone.js to create an oscilloscope. * It is designed to capture and display real-time waveform data from an audio source. * * @param {UseOscilloscopeProps} props - The configuration properties for the oscilloscope. * @param {number} [props.fftSize=1024] - The FFT size, must be a power of 2. Default is 1024. * @param {Function} props.onReady - Callback executed when the oscilloscope is ready. * @param {Function} props.onError - Callback executed in case of an error. * * @returns {object} An object containing various properties and methods for the oscilloscope: * - init: Method to initialize the oscilloscope. * - analyser: The Tone.Analyser instance used by the oscilloscope. * - data: The current waveform data captured by the oscilloscope. * - getData: Method to retrieve the current waveform data. * - observe: Method to start the data observation. * - cancelObserve: Method to stop the data observation. * - error: Boolean indicating if an error has occurred. * - errorMessage: The error message in case of an error. * * This hook can be useful in audio analysis applications, allowing for real-time visualization of waveform data. */ export const useOscilloscope = (props = {}) => { const { fftSize = FFT_SIZE, onReady, onError } = props; const observerId = useRef(0); const analyser = useRef(null); const [data, setData] = useState([]); const [error, setError] = useState(false); const [errorMessage, setErrorMessage] = useState(''); useEffect(() => { return () => { analyser.current?.dispose(); cancelObserve(); }; }, []); const handleError = useCallback((err) => { setError(true); setErrorMessage(err); logger.error(err); onError && onError(); }, []); const init = useCallback(() => { try { analyser.current = new Tone.Analyser('waveform', fftSize); onReady && onReady(); } catch (err) { handleError(err); } }, [fftSize]); const getData = () => { if (!analyser.current || error) return; const spectrumData = analyser.current.getValue(); if (spectrumData instanceof Float32Array) { const formattedData = Array.from(spectrumData).map((amplitude, index) => { return { index, amplitude }; }); setData(formattedData); } }; const observer = useCallback(() => { if (!analyser.current || error) return; try { observerId.current = requestAnimationFrame(observer); getData(); } catch (err) { handleError(err); } }, []); const cancelObserve = useCallback(() => { if (!observerId.current) return; setData([]); cancelAnimationFrame(observerId.current); }, []); return { init, analyser, data, getData, observer, cancelObserve, error, errorMessage, }; };