原生音频
OpenTUI 通过 @opentui/core 提供基于原生 miniaudio 的 Audio 引擎。它可以解码音频字节或文件,通过命名组播放声音,选择播放设备,混音到 PCM 缓冲区,并暴露最近的混合帧用于可视化。
基本用法
import { Audio } from "@opentui/core"
const audio = Audio.create({ autoStart: false })
audio.on("error", (error, context) => {
console.error(`${context.action}: ${error.message}`)
})
const sound = await audio.loadSoundFile("click.wav")
if (sound != null && audio.start()) {
audio.play(sound, { volume: 0.8, pan: 0, loop: false })
}
audio.dispose()
当你更喜欢使用函数包装器而不是 Audio.create(options?) 时,可以使用 setupAudio(options?)。
引擎选项
| 选项 | 默认值 | 说明 |
|---|---|---|
autoStart | false | 创建时调用 start() |
sampleRate | 48000 | 引擎采样率 |
playbackChannels | 2 | 请求的播放声道数 |
startOptions | 默认值 | 自动启动时传递给 start() 的选项 |
创建新的 Audio 实例以更改 sampleRate 或 playbackChannels。
声音和语音
| 方法 | 说明 |
|---|---|
loadSound(data) | 解码 Uint8Array 或 ArrayBuffer;返回 AudioSound |
loadSoundFile(path) | 读取并解码文件;返回 Promise<AudioSound | null> |
unloadSound(sound) | 释放已加载的声音并停止使用它的语音 |
play(sound, options?) | 启动语音;返回 AudioVoice | null |
stopVoice(voice) | 停止一个活跃语音 |
group(name) | 创建或复用命名组;组 0 是默认组 |
setVoiceGroup(voice, group) | 将语音移动到另一个组 |
setGroupVolume(group, volume) | 设置组音量 |
setMasterVolume(volume) | 设置主音量 |
play() 接受 { volume, pan, loop, groupId }。原生播放将 volume 限制在 0..4,pan 限制在 -1..1。引擎有 32 个语音槽位。
启动音频
start() 启动原生播放,当没有可用输出设备时返回 false。使用 isStarted() 检查原生播放状态。
对于无头混音、测试、基准测试或手动 mixFrames() 输出,使用 startMixer()。它会启动混音器而不打开播放设备。使用 isMixerStarted() 检查任一模式。
Bun 独立可执行文件
当使用 with { type: "file" } 导入时,Bun 会在 bun build --compile 可执行文件中嵌入文件。将嵌入的文件作为字节读取并传递给 loadSound(),这样音频将从内存解码而不是从文件路径。
import { file } from "bun"
import { Audio } from "@opentui/core"
import clickPath from "./click.wav" with { type: "file" }
const audio = Audio.create({ autoStart: false })
const clickBytes = await file(clickPath).bytes()
const click = audio.loadSound(clickBytes)
if (click != null && audio.start()) {
audio.play(click)
}
使用 bun build --compile ./app.ts --outfile app 正常编译应用。
Node.js 独立可执行文件
即将推出。
设备
在启动引擎之前选择设备:
const audio = Audio.create({ autoStart: false })
const devices = audio.listPlaybackDevices() ?? []
const device = devices.find((item) => item.isDefault) ?? devices[0]
if (device) {
audio.selectPlaybackDevice(device.index)
}
if (!audio.start()) {
console.error("No playback device available")
}
listPlaybackDevices() 返回 { index, name, isDefault }[] | null。使用 clearPlaybackDeviceSelection() 恢复到默认设备选择。
混音和分析
| 方法 | 说明 |
|---|---|
mixFrames(frameCount, channels=2) | 混音到 Float32Array;单声道降混,超过立体声的声道保持静音 |
getStats() | 返回 soundsLoaded、voicesActive、framesMixed、lockMisses、lastPeak、lastRms |
enableTap(capacityFrames=8192) | 启用固定大小的原生环形缓冲区,存储最近混合的帧 |
readTapFrames(frameCount, channels=2) | 复制最新的采样帧;返回 { frames, framesRead } |
disableTap() | 释放采样缓冲区 |
采样功能默认禁用。启用后,它会存储最新的帧并覆盖旧数据;读取不会清空缓冲区。OpenTUI 不原生计算 FFT。要进行频谱可视化,请读取采样帧并在 TypeScript 中运行 FFT。
启动选项
start(options?) 接受底层设备选项:periodSizeInFrames、periodSizeInMilliseconds、periods、performanceProfile、shareMode、noPreSilencedOutputBuffer、noClip、noDisableDenormals、noFixedSizedCallback、wasapiNoAutoConvertSrc、wasapiNoDefaultQualitySrc、alsaNoMMap、alsaNoAutoFormat、alsaNoAutoChannels 和 alsaNoAutoResample。
performanceProfile 使用 0 表示低延迟,1 表示保守模式。shareMode 使用 0 表示共享模式,1 表示独占模式。
生命周期和错误
使用 start()、startMixer()、stop()、isStarted()、isMixerStarted() 和 dispose() 进行生命周期管理。Audio 发出 started、mixerStarted、stopped、disposed 和 error 事件。失败的方法返回 false 或 null 并发出带有 { action, status } 上下文的 error 事件。