原生音频

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?)

引擎选项

选项默认值说明
autoStartfalse创建时调用 start()
sampleRate48000引擎采样率
playbackChannels2请求的播放声道数
startOptions默认值自动启动时传递给 start() 的选项

创建新的 Audio 实例以更改 sampleRateplaybackChannels

声音和语音

方法说明
loadSound(data)解码 Uint8ArrayArrayBuffer;返回 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..4pan 限制在 -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()返回 soundsLoadedvoicesActiveframesMixedlockMisseslastPeaklastRms
enableTap(capacityFrames=8192)启用固定大小的原生环形缓冲区,存储最近混合的帧
readTapFrames(frameCount, channels=2)复制最新的采样帧;返回 { frames, framesRead }
disableTap()释放采样缓冲区

采样功能默认禁用。启用后,它会存储最新的帧并覆盖旧数据;读取不会清空缓冲区。OpenTUI 不原生计算 FFT。要进行频谱可视化,请读取采样帧并在 TypeScript 中运行 FFT。

启动选项

start(options?) 接受底层设备选项:periodSizeInFramesperiodSizeInMillisecondsperiodsperformanceProfileshareModenoPreSilencedOutputBuffernoClipnoDisableDenormalsnoFixedSizedCallbackwasapiNoAutoConvertSrcwasapiNoDefaultQualitySrcalsaNoMMapalsaNoAutoFormatalsaNoAutoChannelsalsaNoAutoResample

performanceProfile 使用 0 表示低延迟,1 表示保守模式。shareMode 使用 0 表示共享模式,1 表示独占模式。

生命周期和错误

使用 start()startMixer()stop()isStarted()isMixerStarted()dispose() 进行生命周期管理。Audio 发出 startedmixerStartedstoppeddisposederror 事件。失败的方法返回 falsenull 并发出带有 { action, status } 上下文的 error 事件。