React Native中react-native-voice与expo-speech协同异常问题求助
问题:react-native-voice 与 expo-speech 音频冲突
我用 react-native-voice 实现语音转写功能,同时用 expo-speech 做文本转语音,但遇到了音频冲突问题:结束语音录制后调用 expo-speech 播放时完全没有声音,看起来 react-native-voice 在结束录音后把音频通道静音了——明明语音已经开始播放,却必须重新按下麦克风按钮激活语音识别才能听到声音。
我试过的临时方案是等文本转语音结束后再停止录音,但这种方式存在大量边缘情况,没法投入正式使用。所有常规停止录音的方法都解决不了这个问题,库文档里也找不到相关说明,求可行的解决方案。
附当前核心代码:
语音识别相关函数
const startRecognizing = async () => { setButtonColor('#38b000'); // 启动对应语种的语音识别 try { await Voice.start('en-EN'); } catch (e) { console.error(e); } }; const destroyRecognizer = async () => { // 销毁当前语音识别实例 try { await Voice.destroy(); } catch (e) { console.error(e); } }; const stopRecognizing = async () => { setButtonColor('#344E41'); setTimeout(() => { Speech.speak("I did not understand, can you repeat please ?", { language: 'en-EN', onDone: () => { setTimeout( async() => { await destroyRecognizer(); }, 1000); }, }); }, 1000); };
UI 部分代码
return ( <View style={styles.microphoneButtonContainer}> <TouchableOpacity onPressIn={startRecognizing} onPressOut={stopRecognizing}> <Image style={styles.microphoneButton} source={require('../img/microphone-icon.png')} /> </TouchableOpacity> </View> );
可行解决方案
1. 按正确顺序释放音频资源
问题根源是 react-native-voice 占用音频输入设备后,未正确释放或切换音频会话模式,导致 expo-speech 的输出被阻塞。正确流程应该是:先停止识别,等待资源释放后再播放语音,最后销毁识别实例。
修改后的 stopRecognizing 函数:
const stopRecognizing = async () => { setButtonColor('#344E41'); try { // 先停止识别,而非直接销毁实例 await Voice.stop(); // 短暂等待资源释放后启动播放 setTimeout(() => { Speech.speak("I did not understand, can you repeat please ?", { language: 'en-EN', onDone: () => { // 播放完成后再销毁识别实例 destroyRecognizer(); }, }); }, 500); } catch (e) { console.error(e); // 出错时兜底播放语音 Speech.speak("I did not understand, can you repeat please ?", { language: 'en-EN', }); } };
2. 手动切换音频会话模式(iOS 专属)
iOS 上音频会话模式切换需要手动干预,可以借助 expo-av 的 Audio 模块重置音频会话:
首先安装依赖:
npx expo install expo-av
然后修改停止识别逻辑:
import { Audio } from 'expo-av'; const stopRecognizing = async () => { setButtonColor('#344E41'); try { await Voice.stop(); // 重置音频会话为播放模式 await Audio.setAudioModeAsync({ allowsRecordingIOS: false, playsInSilentModeIOS: true, shouldDuckAndroid: true, }); Speech.speak("I did not understand, can you repeat please ?", { language: 'en-EN', onDone: async () => { await destroyRecognizer(); // 如果后续还要录音,记得切回录音模式 await Audio.setAudioModeAsync({ allowsRecordingIOS: true, }); }, }); } catch (e) { console.error(e); Speech.speak("I did not understand, can you repeat please ?", { language: 'en-EN', }); } };
3. 优化麦克风触发逻辑
onPressOut 可能因手势触发时机问题导致资源释放不及时,建议改成用 onPress 配合状态控制录音启停:
import { Audio } from 'expo-av'; // 用状态控制录音状态 const [isRecording, setIsRecording] = useState(false); const handleMicPress = async () => { if (isRecording) { // 停止录音逻辑 setIsRecording(false); setButtonColor('#344E41'); try { await Voice.stop(); await Audio.setAudioModeAsync({ allowsRecordingIOS: false }); Speech.speak("I did not understand, can you repeat please ?", { language: 'en-EN', onDone: async () => { await destroyRecognizer(); }, }); } catch (e) { console.error(e); } } else { // 开始录音逻辑 setIsRecording(true); setButtonColor('#38b000'); try { await Audio.setAudioModeAsync({ allowsRecordingIOS: true }); await Voice.start('en-EN'); } catch (e) { console.error(e); } } }; // UI 部分改成 onPress 触发 return ( <View style={styles.microphoneButtonContainer}> <TouchableOpacity onPress={handleMicPress}> <Image style={styles.microphoneButton} source={require('../img/microphone-icon.png')} /> </TouchableOpacity> </View> );
内容的提问来源于stack exchange,提问作者Benkoula Amir
相关产品推荐
相关产品推荐

