这是一个c++编写,node—gyp编译的JavaScript插件,可以实现录制指定进程输出的音频数据并在js中重建可播放的音频数据
这个插件通过WASAPI获取指定进程输出的音频 示例使用一个electron的渲染进程来在不阻塞进程的情况下将录制的音频转换成媒体流,并用一个自制的频谱图显示波形 目前只支持在页面中还原成单声道 插件的原理是在c++中通过WASAPI获取指定进程的Audio Capture Client,录制原始pcm音频数据再构造对应的wav头信息 拼接头信息和音频数据后得到了可以被正常解码的类似wav文件的数据,在传给js后可通过AudioContext的decodeAudioData解码成可被直接播放的浮点数数据 数据之后通过audioworklet节点播放和显示频谱图
直接传递可播放的pcm数据的实现在考虑中,可能会带来一些性能提升
要使用自行编译的插件,需要以下工具:
- node-gyp
- Visual Studio构建工具
- Windows SDK
- Windows Implementation Library (WIL)
由于binding.gyp配置文件中的头文件目录使用了绝对路径,自行编译需要更改至正确路径。
此仓库编译的node文件位于./build/Release/test_addon.node
此仓库中使用打包完成的node模块
模块打包仓库位于 https://github.com/Need-an-AwP/win-process-audio-capture
使用electron-builder打包。dist目录中为打包完成的便携版exe文件,可以直接运行查看效果。
由于Electron自动获取的是Chrome的进程,所以Chrome未运行及未播放音频时会检索不到。
capture_async现可实现响应式的方法,期望间隔时间后得到模块返回的wav数据
由于我没有找到更改m_AudioClient的缓冲区大小的有效方法,所以使用了这个外部缓冲区+异步调用的方式实现这个功能
从audioworket节点中的.port.onmessage方法获取处理节点中的缓冲长度
if (event.data.type === 'bufferLength') {
document.getElementById('bufferLenth').innerHTML = 'buffer length in audioworklet: ' + event.data.data
}
当bufferLength为0时意味着该处理节点中的缓冲已被消耗完毕,如果等待补充缓冲的时间过长就会产生音频中断 当中断时间非常短时人耳可能感知不到
在前端页面中添加了一个canvas绘制bufferlength折线图
以下是关键代码片段,请务必查看完整的使用案例
const result = test_addon.getActivateStatus()
console.log(result)
if (result.interfaceActivateResult === 0) {
const captureControl = test_addon.capture_async(500, (err, result) => {
if (err) {
console.error("Capture error:", err);
return;
}
//console.log(result)
if (result !== null) {
ctx_main.decodeAudioData(result.wavData.buffer)
.then((audioBuffer) => {
const wavChannelData = audioBuffer.getChannelData(0)
handleAddonData.port.postMessage(wavChannelData)
})
}
})
document.getElementById('stopCapture').addEventListener('click', () => {
captureControl.stop()
})
}
在electron部分用了一个简单的纯js页面来测试
使用 npm i
安装依赖
使用 npm start
运行
如果chrome正在播放音频,electron 会自动获取其进程名并捕获其音频
前台显示波形canvas会造成一般cpu占用
连接其他音频输出设备并在electron窗口中选择即可听到单独的捕获音频 新音频输出设备连接时需要重新启动electron应用,因为音频设备枚举只在应用启动时进行一次
2024-10-04.18-16-03.mp4
这里录制时使用的桌面音频为默认输出,桌面音频2为蓝牙耳机输出
在众多参考资料中,对本项目最有帮助的是: