React Native(Expo)移动端应用如何显示音频波形?
React Native 音频波形可视化播放器实现方案
可用第三方库适配方案
- react-native-audio-waveform:若原库因版本兼容问题无法运行,可尝试社区维护的fork版本,不少分支已适配React Native 0.70+,修复了原生模块编译错误。
- react-native-wave-visual:原库失效时,可手动修改其原生代码:Android端更新JNI调用逻辑与
ReactPackage实现,iOS端替换废弃的AudioToolkit API,适配当前RN版本的桥接规范。 - Expo生态组合方案:用
expo-av处理音频播放,通过其API获取音频PCM数据,再结合react-native-canvas或动态生成的View组件绘制波形。这种方式灵活性强,适配性优于老旧第三方库。
原生实现方案(自定义桥接)
Android端
- 用
ExoPlayer或MediaPlayer处理音频播放,通过AudioRecord采集实时PCM数据; - 将PCM数据转换为振幅值,通过React Native桥接将数据传递至JS层;
- JS层根据振幅数据,用
FlatList或动态View组件绘制波形条。
简化的Android原生代码片段:
// PCM数据采集与振幅计算 AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize); short[] buffer = new short[bufferSize]; audioRecord.startRecording(); while (isPlaying) { int readCount = audioRecord.read(buffer, 0, bufferSize); int amplitude = calculateAverageAmplitude(buffer, readCount); // 向JS发送波形数据 sendEvent(getReactApplicationContext(), "audioWaveData", amplitude); }
iOS端
- 用
AVAudioPlayer播放音频,通过AVAudioEngine的inputNode获取PCM数据; - 从
AVAudioPCMBuffer中提取振幅信息,通过桥接传递至JS层; - JS层动态渲染波形组件。
简化的iOS原生代码片段:
// 音频数据监听与振幅提取 let audioEngine = AVAudioEngine() let inputNode = audioEngine.inputNode let format = inputNode.inputFormat(forBus: 0) inputNode.installTap(onBus: 0, bufferSize: 1024, format: format) { buffer, _ in let channelData = buffer.floatChannelData![0] let amplitude = self.calculateAmplitude(from: channelData, length: Int(buffer.frameLength)) // 向JS发送波形数据 self.sendEvent(withName: "audioWaveData", body: amplitude) } try? audioEngine.start()
JS层波形绘制示例
import React, { useState, useEffect } from 'react'; import { View, StyleSheet, NativeEventEmitter, NativeModules } from 'react-native'; const AudioWaveform = () => { const [amplitudes, setAmplitudes] = useState([]); const { AudioWaveModule } = NativeModules; const eventEmitter = new NativeEventEmitter(AudioWaveModule); useEffect(() => { const subscription = eventEmitter.addListener('audioWaveData', (amplitude) => { // 保留最近20个振幅值,避免组件过多 setAmplitudes(prev => [...prev.slice(-20), amplitude]); }); return () => subscription.remove(); }, []); return ( <View style={styles.waveContainer}> {amplitudes.map((amp, idx) => ( <View key={idx} style={[ styles.waveBar, { height: Math.max(8, amp * 1.5), backgroundColor: '#2563eb' } ]} /> ))} </View> ); }; const styles = StyleSheet.create({ waveContainer: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', height: 120, gap: 3, paddingHorizontal: 10, }, waveBar: { width: 5, borderRadius: 3, }, }); export default AudioWaveform;
内容的提问来源于stack exchange,提问作者MargaritaTarnovska
相关产品推荐
相关产品推荐

