开始
我想做个播客系统,那就要录制麦克风和系统声音。 起初我认为非常简单,毕竟obs能做到,我基于obs基于的ffmpeg也能做到。
然而现实狠狠地给了我一嘴巴
- 麦克风是很容易获取,但系统音频并不容易。Windows使用dshow可以实现,但Mac就需要新的驱动BlackHole了。跨平台问题需要解决。
- 现成的库都是执行ffmpeg的命令,我总不能写入文件再读文件吧,那样延迟太大了。而且还要在包中再打包一个ffmpeg执行文件,太笨重。
这时我发现了cpal,一个rust的跨平台音频库。它对接了windows的WASAPI、Mac的CoreAudio、Linux的ALSA,甚至移动端都有对接。虽然我用不到那么多平台,但这也太棒了。
实现
首先要获取设备
let host = cpal::default_host();
// 两种方案
// 直接获取默认的输出音频设备
let mut out_device = host
.default_output_device()
.ok_or_else(|| anyhow::anyhow!("no output device available"))?;
// 或者获取所有输出音频设备再遍历名称选择想要的设备
let output_devices = host.output_devices().expect("list output device error");
再获取设备配置
// 这里需要mac安装blackhole,windows不需要其他安装
let config = if out_device.supports_input() {
out_device.default_input_config()
} else {
out_device.default_output_config()
}
// 如果需要对采样率,采样精度做修改就重新生成一份config
let config = cpal::SupportedStreamConfig::new(
1,
cpal::SampleRate(16000),
*config.buffer_size(),
cpal::SampleFormat::I16,
);
开始录制
// 因为我直接设置了采样精度是i16,如果直接使用默认就需要对多个精度match
// record是把pcm存入本地,如果希望使用其他格式,需要自行找库
let sys_stream = match config.sample_format() {
cpal::SampleFormat::I16 => out_device.build_input_stream(
&config.into(),
move |data, _: &_| record_audio::<i16, i16>(data),
err_fn,
None,
)?,
sample_format => {
println!("匹配的采样格式:{}", sample_format);
return Err(
anyhow::Error::msg(format!("Unsupported sample format '{sample_format}'")).into(),
);
}
};
sys_stream.play()?;
好了,成功录制!
仍需解决
- 跨平台问题还是没有完全搞定,mac端仍需客户手动安装BlackHole,CoreAudio应该是提供了Loopback的能力,还需要调研下
- pcm直接存入文件体积膨胀速度太大,需要使用其他格式进行压缩
- 音量阈值能力还未提供,音频短时音量计算方式需要了解,采样时间需要设置