@ audiowave/反应
用于实时音频可视化的轻量级 React 组件
启发和关键差异
这个项目的灵感来自 反应-视听器 但采取不同、更有重点的办法:
是什么让我们不同:
- 只可视化 - 没有内置播放或记录功能
- 无控制按钮 - 纯可视化组件,无UI控制
- 轻巧 - 有重点特性集的最小捆绑大小
- 简单整合 - 投放现有音频应用程序而不发生冲突
- 弹性 - 使用你提供的任何音频源
适合这些情景:
- 现有音频应用程序 - 在已经处理音频的应用程序中添加可视化
- 记录软件 - 可视化麦克风输入而不发生音频冲突
- 音乐播放器 - 在音频/视频播放中添加波形
- 自定义音频工作流程 - 与任何音频来源或处理管道相结合
- 轻量级项目 - 最小捆绑大小,只需要可视化
安装
npm install @audiowave/react
快速启动
用麦克风输入的基本用途 :
import { AudioWave, useMediaAudio } from '@audiowave/react';
import { useRef, useState, useMemo, useCallback } from 'react';
export default function App() {
const [mediaStream, setMediaStream] = useState<MediaStream | null>(null);
const [isRecording, setIsRecording] = useState(false);
// AudioWave handles visualization, you control the audio source
// Note: For dynamic audio sources, memoize the options object (see Important Usage Notes)
const handleError = useCallback((error: Error) => {
console.error('Audio error:', error);
}, []);
const audioOptions = useMemo(() => ({
source: mediaStream,
onError: handleError,
}), [mediaStream, handleError]);
const { source, error } = useMediaAudio(audioOptions);
const audioWaveRef = useRef<AudioWaveController>(null);
const startRecording = async () => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
setMediaStream(stream);
setIsRecording(true);
};
const stopRecording = () => {
mediaStream?.getTracks().forEach(track => track.stop());
setMediaStream(null);
setIsRecording(false);
};
return (
<div>
{error && <div>Error: {error.message}</div>}
<AudioWave ref={audioWaveRef} source={source} height={100} />
{/* You provide the controls - AudioWave just visualizes */}
<button onClick={isRecording ? stopRecording : startRecording}>
{isRecording ? 'Stop Recording' : 'Start Recording'}
</button>
{/* Optional: Control visualization display */}
<button onClick={() => audioWaveRef.current?.pause()}>Pause Waveform</button>
<button onClick={() => audioWaveRef.current?.resume()}>Resume Waveform</button>
<button onClick={() => audioWaveRef.current?.clear()}>Clear Waveform</button>
</div>
);
}
工作与任何音频源 :
// With audio file
const audioElement = useRef<HTMLAudioElement>(null);
const { source } = useAudioSource({ source: audioElement.current });
// With video file
const videoElement = useRef<HTMLVideoElement>(null);
const { source } = useAudioSource({ source: videoElement.current });
// With Web Audio API nodes
const { source } = useAudioSource({ source: audioNode });
// With custom audio data (Electron, Node.js, etc.)
const { source } = useCustomAudio();
API 参考
音频窗口组件
主要可视化组件 - 纯显示,无音频控制.
<AudioWave
// === AUDIO SOURCE ===
source={source} // AudioSource from useAudioSource
// === DIMENSIONS ===
width="100%" // Component width (string | number)
height={200} // Component height in pixels
// === VISUAL STYLING ===
backgroundColor="transparent" // Background color
barColor="#ffffff" // Primary bar color
secondaryBarColor="#5e5e5e" // Secondary/inactive bar color
barWidth={2} // Width of each frequency bar
gap={1} // Gap between bars in pixels
rounded={0} // Border radius for rounded bars
// === BORDER STYLING ===
showBorder={false} // Show border around visualization
borderColor="#333333" // Border color
borderWidth={1} // Border width in pixels
borderRadius={0} // Border radius for rounded corners
// === AMPLITUDE CALCULATION ===
amplitudeMode="peak" // Amplitude calculation: 'peak' | 'rms' | 'adaptive'
// === ANIMATION & RENDERING ===
speed={3} // Animation speed (1-6, higher = slower)
animateCurrentPick={true} // Enable smooth bar transitions
fullscreen={false} // Fill entire parent container
onlyActive={false} // Show visualization only when active
gain={1.0} // Audio gain multiplier (0.1-10.0, default: 1.0)
// === STATE CONTROL ===
isPaused={false} // Pause visualization (freeze display)
// === ADVANCED CUSTOMIZATION ===
customRenderer={(context) => {
// Custom rendering function for advanced visualizations
// context: { canvas, audioData, width, height, ... }
}}
// === PLACEHOLDER CONTENT ===
placeholder={<div>No audio source</div>} // Custom placeholder content
showPlaceholderBackground={true} // Show background in placeholder state
// === CSS CLASSES ===
className="my-waveform" // CSS class for main container
canvasClassName="my-canvas" // CSS class for canvas element
// === CALLBACKS ===
onStateChange={(state) => console.log('State:', state)} // State change callback
onRenderStart={() => console.log('Rendering started')} // Render start callback
onRenderStop={() => console.log('Rendering stopped')} // Render stop callback
onError={(error) => console.error('Error:', error)} // Error callback
/>
基本主张
音频来源 :
source- 音频来源于useMediaAudio或者说useCustomAudio钩子(视觉需要)
尺寸 :
width- 组件宽度,支持CSS单元和数字(默认:"100%"(中文(简体) )height- 组件高度,以像素计(默认值:200(中文(简体) )
视觉造型 :
backgroundColor- 背景颜色( 默认 ):"transparent"(中文(简体) )barColor- 活动音频栏的主要颜色( 默认 ):"#ffffff"(中文(简体) )secondaryBarColor- 非活动/后行的颜色(默认值 :"#5e5e5e"(中文(简体) )barWidth- 每个频率栏以像素表示的宽度( 默认 ):2(中文(简体) )gap- 像素条之间的间隔( 默认值 :1(中文(简体) )rounded- 四舍五入条的边框半径(默认值:0(中文(简体) )
高级道具
边框样式 :
showBorder- 显示可视化区域的边框( 默认 ):false(中文(简体) )borderColor- 边框颜色( 默认 ):"#333333"(中文(简体) )borderWidth- 以像素为单位的边框宽度( 默认值 :1(中文(简体) )borderRadius- 圆角的边框半径( 默认 ):0(中文(简体) )
动画和渲染 :
speed- 动画速度从 1 - 6 起,较高数字更慢(默认 :3(中文(简体) )animateCurrentPick- 启用平滑条过渡(默认:true(中文(简体) )fullscreen- 装入整个母容器(默认值 :false(中文(简体) )onlyActive- 仅在音频活动时显示可视化( 默认 ):false(中文(简体) )
国家管制:
isPaused- 在不影响音频的情况下暂停可视化显示(默认:false(中文(简体) )
高级自定义 :
customRenderer- 高级可视化的自定义渲染功能placeholder- 自定义反应组件,在没有音频源时显示showPlaceholderBackground- 是否在占位符状态下显示背景
CSS 类 :
className- 主容器的CSS级canvasClassName- 帆布元素的CSS类
事件召回 :
onStateChange- 当视觉状态发生变化时调用onRenderStart- 开始渲染时打电话onRenderStop- 停止渲染时打电话onError- 调用 翻译错误
使用MediaAudio Hook( 建议)
将媒体来源转换为可视化数据。 这是大多数使用案例的主要钩子.
const { source, error } = useMediaAudio({
source: mediaStream // MediaStream | HTMLAudioElement | HTMLVideoElement | AudioNode
});
重要内容: 当使用懒惰装入的音频源时(开始于
null),您必须记住选项对象以防止闪烁。 见 重要使用说明 详细情况.
支持的来源 :
MediaStream- 麦克风、录音软件HTMLAudioElement- 音频文件HTMLVideoElement- 视频文件AudioNode- 网络音频 API 节点
回返情况:
source- AudioWave 组件的音频来源实例error- 任何处理错误(无效)
使用Audio Source Hook( 语言)
说明:
useAudioSource是用于useMediaAudio为后向相容性维护。 新项目应使用useMediaAudio。 。 。 .
专用钩子
为了更好的类型安全和方便,您可以使用专门的钩:
// For microphone or recording software
const { source, error } = useMediaStreamSource(mediaStream);
// For audio/video files
const { source, error } = useMediaElementSource(audioElement);
// For Web Audio API nodes
const { source, error } = useAudioNodeSource(audioNode);
// For custom audio data (Electron, Node.js, etc.)
const { source } = useCustomAudio({ provider: myProvider });
使用 CustomAudio Hook 软件
对于需要提供自定义音频数据(Electron Apps、Node.js音频处理、自定义音频管道)的高级使用案例,请使用 useCustomAudio 与自定义提供者连接 :
import { useCustomAudio } from '@audiowave/react';
import { useMemo } from 'react';
function CustomAudioApp() {
// Create audio data provider
const audioProvider = useMemo(() => ({
onAudioData: (callback: (data: Uint8Array) => void) => {
// Your audio data subscription logic
const unsubscribe = yourAudioSource.subscribe(callback);
return unsubscribe; // Return cleanup function
},
onAudioError: (callback: (error: string) => void) => {
// Optional error handling
const unsubscribe = yourAudioSource.onError(callback);
return unsubscribe;
}
}), []);
const { source, error } = useCustomAudio({
provider: audioProvider,
deviceId: 'default'
});
return (
<div>
{error && <div>Error: {error}</div>}
<AudioWave source={source} height={120} />
</div>
);
}
参数:
provider- 音频数据实施(需要)deviceId- 设备标识符(可选,默认:"默认")
回返情况:
source- AudioWave 组件的音频来源实例status- 当前状态 : “ idle ” ; “ 活动 ” ; “ 暂停 ”error- 如果有错误消息isActive- 布尔表示音频是否活动clearError- 清除错误状态的函数
音频数据格式 :
Uint8Array数值在幅度 [0, 255]- 128 代表静默( 中值)
- 128以上值表示正振幅
- 128以下值表示负振幅
- 阵列长度应该符合您想要的可视化分辨率
电子融合指南
用于电子应用, 使用 useCustomAudio 与本地音频处理的自定义提供者:
最佳做法:主要进程音频处理
最佳性能的主要进程中处理音频 :
主要进程main.js:
import { AudioProcessor } from '@audiowave/core';
import { ipcMain } from 'electron';
class ElectronAudioCapture {
private audioProcessor: AudioProcessor;
constructor() {
this.audioProcessor = new AudioProcessor({
bufferSize: 1024,
skipInitialFrames: 2,
inputBitsPerSample: 32, // naudiodon uses 32-bit
inputChannels: 2, // stereo input
});
this.setupAudioCapture();
}
private setupAudioCapture() {
// Setup your audio capture (naudiodon, etc.)
audioCapture.on('data', (buffer: Buffer) => {
// Process audio in main process
const result = this.audioProcessor.process(buffer);
if (result) {
// Send processed data to renderer
mainWindow.webContents.send('audio-data', result.timeDomainData);
}
});
}
}
// IPC handlers
ipcMain.handle('start-audio-capture', async () => {
// Start audio capture logic
});
ipcMain.handle('stop-audio-capture', async () => {
// Stop audio capture logic
});
渲染器进程( R)renderer.tsx:
import { useCustomAudio } from '@audiowave/react';
import { useMemo } from 'react';
function ElectronAudioApp() {
// Create provider for Electron IPC communication
const electronProvider = useMemo(() => ({
onAudioData: (callback: (data: Uint8Array) => void) => {
const handleAudioData = (_event: any, audioData: Uint8Array) => {
callback(audioData);
};
window.electronAPI.onAudioData(handleAudioData);
return () => {
window.electronAPI.removeAudioDataListener?.(handleAudioData);
};
},
onAudioError: (callback: (error: string) => void) => {
const handleError = (_event: any, error: string) => {
callback(error);
};
window.electronAPI.onAudioError?.(handleError);
return () => {
window.electronAPI.removeAudioErrorListener?.(handleError);
};
}
}), []);
const { source, error } = useCustomAudio({
provider: electronProvider,
deviceId: 'default'
});
const startCapture = () => {
window.electronAPI.startAudioCapture();
};
const stopCapture = () => {
window.electronAPI.stopAudioCapture();
};
return (
<div>
{error && <div>Error: {error}</div>}
<AudioWave source={source} height={120} />
<button onClick={startCapture}>Start</button>
<button onClick={stopCapture}>Stop</button>
</div>
);
}
预装脚本( P)preload.js:
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('electronAPI', {
startAudioCapture: () => ipcRenderer.invoke('start-audio-capture'),
stopAudioCapture: () => ipcRenderer.invoke('stop-audio-capture'),
onAudioData: (callback: (event: any, data: Uint8Array) => void) => {
ipcRenderer.on('audio-data', callback);
},
onAudioError: (callback: (event: any, error: string) => void) => {
ipcRenderer.on('audio-error', callback);
},
removeAudioDataListener: (callback: Function) => {
ipcRenderer.removeListener('audio-data', callback);
},
removeAudioErrorListener: (callback: Function) => {
ipcRenderer.removeListener('audio-error', callback);
}
});
这种办法的好处:
- 最小数据传输的优化性能
- 维持UI响应能力
- 多个窗口的单一处理实例
可视化控制
AudioWave为可视化显示提供了简单的控制(而不是音频本身):
使用参考文献( 模拟)
const audioWaveRef = useRef<AudioWaveController>(null);
// Basic visualization controls
audioWaveRef.current?.pause(); // Freeze display
audioWaveRef.current?.resume(); // Resume display
audioWaveRef.current?.clear(); // Clear waveform data
// State inspection methods
const isPaused = audioWaveRef.current?.isPaused(); // Check if paused
const state = audioWaveRef.current?.getState(); // Get current state: 'idle' | 'visualizing' | 'paused'
const audioData = audioWaveRef.current?.getAudioData(); // Get current audio data (Uint8Array)
音频窗口控制器方法
控制方法:
pause()- 暂停可视化(冻结波形显示)resume()- 从暂停状态恢复可视化clear()- 清除所有波形数据并重置显示
国家方法:
isPaused()- 返回布尔表示是否暂停可视化getState()- 返回当前状态:'idle'|'visualizing'|'paused'getAudioData()- 返回当前音频数据为 Uint8Array
使用道具( 声明)
const [isPaused, setIsPaused] = useState(false);
<AudioWave
source={source}
isPaused={isPaused} // Control via props
/>
重要内容: 这些控件只影响可视化显示,而不是你的音频源.
高度计算模式
AudioWave支持三种不同的振幅计算方法,以满足不同的可视化需要:
峰值模式( 默认)
<AudioWave source={source} amplitudeMode="peak" />
- 最好还是:通用可视化,音乐,动态内容
- 行为: 使用频率数据的峰值振幅值
- 特征:高响应度,清晰显示所有音频峰值
- 向后兼容: 默认模式, 维护已有行为
RMS 模式( 实际声音)
<AudioWave source={source} amplitudeMode="rms" />
- 最好还是:语音分析,广播音频,感知出响比对
- 行为: 根平方计算代表人类如何看待高音
- 特征: 更平滑的可视化, 更好地代表所感知的音量
- 安静的环境: 通过平稳的噪音地面过渡而增强
- 范围 1-9: 有指数缩放的静态信号
- 范围 10+: 有对数缩放的可听信号
- 自然基线(1)为环境噪声下限
适应模式( 动态缩放)
<AudioWave source={source} amplitudeMode="adaptive" />
- 最好还是: 音频级别不同,自动增益调整,内容混合
- 行为: 根据最近的音频级别动态调整缩放
- 特征: 自动补偿静音或高音源
- 使用大小写: 当音频级别差异很大或无法预测时
模式切换示例
import { useState } from 'react';
import { AudioWave, useAudioSource } from '@audiowave/react';
function AmplitudeModeDemo() {
const [mediaStream, setMediaStream] = useState<MediaStream | null>(null);
const [amplitudeMode, setAmplitudeMode] = useState<'peak' | 'rms' | 'adaptive'>('peak');
const { source } = useAudioSource({ source: mediaStream });
return (
<div>
<AudioWave
source={source}
amplitudeMode={amplitudeMode}
height={120}
barWidth={2}
gap={1}
/>
<div>
<label>Amplitude Mode:</label>
<select
value={amplitudeMode}
onChange={(e) => setAmplitudeMode(e.target.value as any)}
>
<option value="peak">Peak (Default)</option>
<option value="rms">RMS (Perceptual)</option>
<option value="adaptive">Adaptive (Auto-scaling)</option>
</select>
</div>
</div>
);
}
最佳做法
处理错误
const { source, error } = useAudioSource({ source: mediaStream });
if (error) {
return <div>Visualization error: {error.message}</div>;
}
重要使用说明
懒惰装入音频源( 适当功能需要)
当您的音频源启动时 null 后设定(常见于用户启动的情景),您 必须 记住 useAudioSource 选项。 没有这个,可视化将不断闪烁并重启,使其无法使用:
- 这会导致闪烁:
function MyAudioApp() {
const [mediaStream, setMediaStream] = useState<MediaStream | null>(null);
// This creates a new options object on every render!
const { source } = useMediaAudio({
source: mediaStream,
onError: (error) => console.error('Audio error:', error)
});
// ... rest of component
}
QQ CORRECT - 纪念选项对象 :
function MyAudioApp() {
const [mediaStream, setMediaStream] = useState<MediaStream | null>(null);
// Step 1: Memoize the onError callback
const handleAudioError = useCallback((error: Error) => {
console.error('Audio error:', error);
}, []);
// Step 2: Memoize the entire options object
const audioSourceOptions = useMemo(() => ({
source: mediaStream,
onError: handleAudioError,
}), [mediaStream, handleAudioError]);
// Step 3: Use the memoized options
const { source } = useMediaAudio(audioSourceOptions);
const startRecording = async () => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
setMediaStream(stream); // This triggers re-render with new source
};
return (
<div>
<button onClick={startRecording}>Start Recording</button>
<AudioWave source={source} />
</div>
);
}
为什么需要这样做?
- 每个参数都会创建一个新选项对象
{ source: mediaStream, onError: ... } useMediaAudio视此为"变化"并重新初始化音频处理- 这导致可视化反复闪烁并重启,使其无法使用
- 这不是性能问题 这是正常运行的功能要求
快速引用 - 复制此模式 :
// Always use this pattern for lazy-loaded audio sources
const handleError = useCallback((error: Error) => {
console.error('Audio error:', error);
}, []);
const options = useMemo(() => ({
source: yourAudioSource, // MediaStream | HTMLAudioElement | etc.
onError: handleError,
}), [yourAudioSource, handleError]);
const { source } = useMediaAudio(options);
性能优化
纪念组件道具
// Memoize AudioWave props for better performance
const audioWaveProps = useMemo(() => ({
source,
height: 200,
barWidth: 2,
gap: 1,
backgroundColor: 'transparent',
barColor: '#ffffff'
}), [source]);
return <AudioWave {...audioWaveProps} />;
响应设计
<AudioWave
source={source}
width="100%"
height={window.innerWidth < 768 ? 80 : 120}
style={{ maxWidth: '100%' }}
/>
类型脚本支持
包含完整类型的全部类型脚本支持 :
import type {
AudioWaveProps,
AudioWaveController,
AudioSource,
UseAudioSourceOptions
} from '@audiowave/react';
浏览器支持
- Chrome 66+ (2018年3月) (英语)
- Firefox 60+ (2018年5月) (英语)
- Safari 14+ (2020年9月) (英语)
- 79岁以上(2020年1月)
需要Web音频API和MediaStream API支持.
许可证
麻省理工学院 温度