麦克风播放的hook
大约 1 分钟
麦克风播放的hook
import { activeLiveProject } from '@/renderer/store/modules/activeLiveProject';
import { ApplicationStore } from '@/renderer/store/sharedHelpers';
import store from '@/renderer/store/store';
import { useSnackbar } from 'material-ui-snackbar-provider';
import { useCallback, useEffect, useRef } from 'react';
import { useSelector } from 'react-redux';
// 这个hook搞人声混入的逻辑
type RootState = ReturnType<typeof store.getState>;
const useInputDevice = (context: AudioContext) => {
const voiceInputDeviceId = useSelector((store: ApplicationStore) => store.voiceInputDeviceId);
const livingConfigStore = useSelector<RootState, RootState['livingConfig']>((rootStore) => rootStore.livingConfig);
const deviceSource = useRef<MediaStreamAudioSourceNode | null>(null);
const lGain = useRef(context.createGain()); // 左声道
const rGain = useRef(context.createGain()); // 右声道
const processor = useRef<ScriptProcessorNode>(context.createScriptProcessor(0, 1, 1));
const merger = useRef<ChannelMergerNode>(context.createChannelMerger(2));
const splitter = useRef<ChannelMergerNode>(context.createChannelSplitter(2));
const audioStream = useRef<MediaStream>();
const snackbar = useSnackbar();
// 开播才监听麦克风
useEffect(() => {
if (livingConfigStore.isLiving && activeLiveProject.config.openInputDevice) {
startConnect();
} else {
inputDeviceDisconnect();
}
}, [voiceInputDeviceId, livingConfigStore.isLiving, activeLiveProject.config.openInputDevice]);
// 获取媒体资源
const getMediaStream = async () => {
return await navigator.mediaDevices.getUserMedia({
video: false,
audio: { deviceId: voiceInputDeviceId || 'default' },
});
};
const onAudioProcess = useCallback((e) => {
//获取输入和输出的数据缓冲区
let input = e.inputBuffer.getChannelData(0);
let output = e.outputBuffer.getChannelData(0);
for (let i = 0; i < input.length; i++) {
output[i] = input[i];
}
}, []);
// 开始连接
const startConnect = async () => {
inputDeviceDisconnect();
await new Promise((resolve) => setTimeout(resolve, 1000));
audioStream.current = await getMediaStream();
audioStream.current.getAudioTracks()[0].addEventListener('ended', () => {
snackbar.showMessage('输出设备中断,请刷新设备列表');
inputDeviceDisconnect();
});
deviceSource.current = context.createMediaStreamSource(audioStream.current);
// 设置播放声音
setGainWithRange(1);
deviceSource.current.connect(processor.current);
deviceSource.current.connect(splitter.current);
splitter.current.connect(lGain.current, 0);
splitter.current.connect(rGain.current, 1);
lGain.current.connect(merger.current, 0, 0);
rGain.current.connect(merger.current, 0, 1);
merger.current.connect(processor.current);
processor.current.connect(context.destination);
processor.current.addEventListener('audioprocess', onAudioProcess);
};
// 设置音量
const setGainWithRange = (gain: number) => {
lGain.current.gain.value = gain;
rGain.current.gain.value = gain;
};
// 关闭麦克风
const inputDeviceDisconnect = () => {
if (deviceSource.current) {
if (audioStream.current) {
audioStream.current.getTracks().forEach((track) => track.stop());
audioStream.current = null;
}
deviceSource.current.disconnect();
processor.current.disconnect();
merger.current.disconnect();
splitter.current.disconnect();
lGain.current.disconnect();
rGain.current.disconnect();
}
};
return { setGainWithRange, inputDeviceDisconnect };
};
export default useInputDevice;