【问题标题】:Correct handling of React Hooks for microphone audio正确处理麦克风音频的 React Hooks
【发布时间】:2019-07-31 20:43:26
【问题描述】:

我正在尝试编写一个 React Hook 来将流式音频处理到使用 Meyda 分析的 AudioContext。

https://meyda.js.org/

我已经设法让流工作并且能够提取我想要的数据。但是,我无法取消初始化音频。

如果有人能给我一些正确设置这个钩子的指导,我将不胜感激。

当我使用这些挂钩离开页面时,我目前收到以下错误:

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。

我曾尝试在我的钩子末尾添加一个清理功能,但我的尝试经常以音频立即中断或任何数量的其他奇怪错误而告终。

带有 Meyda 分析器的麦克风音频挂钩

export const useMeydaAnalyser = () => {

    const [running, setRunning] = useState(false);
    const [features, setFeatures] = useState(null);
    const featuresRef = useRef(features);
    const audioContext = useRef(new AudioContext());

    const getMedia = async() => {
        try {
            return await navigator
                .mediaDevices
                .getUserMedia({audio: true, video: false});
        } catch(err) {
            console.log('Error:', err);
        }
    };

    useEffect(
        () => {
            const audio = audioContext.current;
            let unmounted = false;
            if(!running) {
                getMedia().then(stream => {
                    if (unmounted) return;
                    setRunning(true);
                    const source = audio.createMediaStreamSource(stream);
                    const analyser = Meyda.createMeydaAnalyzer({
                        audioContext: audio,
                        source: source,
                        bufferSize: 1024,
                        featureExtractors: [
                            'amplitudeSpectrum',
                            'mfcc',
                            'rms',
                        ],
                        callback: nextFeatures => {
                            if(!isEqual(featuresRef.current, nextFeatures)) {
                                setFeatures(nextFeatures);
                            }
                        },
                    });
                    analyser.start();
                });
            }
            return () => {
                unmounted = true;
            }
        },
        [running, audioContext],
    );

    useEffect(
        () => {
            featuresRef.current = features;
        },
        [features],
    );

    return features;
};

音频视图

import React, {useEffect} from 'react';
import { useMeydaAnalyser } from '../hooks/use-meyda-audio';

const AudioViewDemo = () => {
    const audioContext = new AudioContext();
    const features = useMeydaAnalyser(audioContext);

    useEffect(() => {
        // Todo: Handle Audio features
        console.log(features);
        // setAudioData(features);
    }, [features]);

    return (
        <div>
            RMS: {features && features.rms}
        </div>
    );
};

export default AudioViewDemo;

【问题讨论】:

标签: reactjs react-hooks web-audio-api


【解决方案1】:

错误应该是没有关闭AudioContext引起的。您需要在清理函数中关闭AudioContext

注意,在使用AudioContext之前,先判断状态是否为off,因为getMedia是异步的,所以如果组件在加载后很快就卸载了,AudioContext在使用的时候就关闭了。

const getMedia = async () => {
  try {
    return await navigator.mediaDevices.getUserMedia({
      audio: true,
      video: false,
    })
  } catch (err) {
    console.log('Error:', err)
  }
}

const useMeydaAnalyser = () => {
  const [analyser, setAnalyser] = useState(null)
  const [running, setRunning] = useState(false)
  const [features, setFeatures] = useState(null)

  useEffect(() => {
    const audioContext = new AudioContext()

    let newAnalyser
    getMedia().then(stream => {
      if (audioContext.state === 'closed') {
        return
      }
      const source = audioContext.createMediaStreamSource(stream)
      newAnalyser = Meyda.createMeydaAnalyzer({
        audioContext: audioContext,
        source: source,
        bufferSize: 1024,
        featureExtractors: ['amplitudeSpectrum', 'mfcc', 'rms'],
        callback: features => {
          console.log(features)
          setFeatures(features)
        },
      })
      setAnalyser(newAnalyser)
    })
    return () => {
      if (newAnalyser) {
        newAnalyser.stop()
      }
      if (audioContext) {
        audioContext.close()
      }
    }
  }, [])

  useEffect(() => {
    if (analyser) {
      if (running) {
        analyser.start()
      } else {
        analyser.stop()
      }
    }
  }, [running, analyser])

  return [running, setRunning, features]
}

【讨论】:

  • 非常感谢,我现在就试试。
  • 非常感谢您。我想原则上类似的模式将适用于视频流。如果我能解决,我会的。
  • 我想我会将与视频元素相关的位移到一个新问题上。这似乎很好地解决了音频处理部分。
  • 我不太清楚您在视频流上遇到了什么问题。你能详细说明吗?此外,我还有一个关于同一 CodeSandbox 上的视频流的演示。你可以检查一下。由于我这里没有安装摄像头,所以我使用屏幕预览的视频预览。
  • 我会的,我还没来得及拆分它。完成后,我会将其标记为正确并在此处添加指向新问题的链接
猜你喜欢
  • 2010-11-26
  • 2018-12-12
  • 2017-10-26
  • 1970-01-01
  • 2019-12-02
  • 2018-06-18
  • 1970-01-01
  • 2018-02-14
  • 1970-01-01
相关资源
最近更新 更多