免责声明:我不考虑任何用于语音和语音识别的算法和API。 本文介绍了音频处理问题以及如何使用Go解决问题。

phono
是用于声音处理的应用程序框架。 它的主要目的–创建不同技术的管道,以您需要的方式处理声音。
- 移动墙壁—安装横梁
- 移动网络专家系列:响应式网页设计(视频和音频内容)
- 布鲁诺·马尔斯(Bruno Mars)-矮胖(带有歌词的音频)
- 播客的力量
- Whizzer A15评论:什么时候Chi-Fi得到如此完善?
管道使用不同的技术做什么,为什么我们需要另一个框架? 我们现在会解决。
声音从何而来?
在2018年,声音成为人类与技术之间互动的标准方式。 大多数IT巨人要么已经具有语音助手,要么现在正在研究它。 集成到主要操作系统和语音消息中的语音控制是任何现代Messenger的默认功能。 大约有上千家初创公司从事自然语言处理,约有200家从事语音识别。
音乐也有同样的故事。 它可以通过任何设备播放,并且任何有计算机的人都可以使用录音。 音乐软件是由全球数百家公司和数千名爱好者开发的。
常见任务
如果您执行过任何声音处理任务,那么您应该熟悉以下条件:
- 应该从文件,设备,网络等接收音频。
- 应处理音频:添加FX,编码,分析等。
- 音频应发送到文件,设备,网络等中。
- 数据在小缓冲区中传输
它变成了一条流水线-数据流经过多个处理阶段。
解决方案
为了清楚起见,让我们来看一个现实生活中的问题:我们需要将语音转换为文本:
- 用设备录制音频
- 消除噪音
- 均衡
- 发送信号到语音识别API
与其他任何问题一样,该问题有几种解决方案。
蛮力
仅限于铁杆开发人员。 通过声音接口驱动程序直接录制声音,编写智能降噪器和多轨均衡器。 这很有趣,但是您可能会忘记几个月的原始任务。
耗时且非常复杂。
正常方式
替代方法是使用现有的音频API。 可以使用ASIO,CoreAudio,PortAudio,ALSA等录制音频。 可以使用多种标准的插件进行处理:AAX,VST2,VST3,AU。
丰富的选择并不意味着可以使用所有内容。 通常,以下限制适用:
- 操作系统。 并非所有API在所有操作系统上都可用。 例如,AU是OS X的本机技术,仅在此可用。
- 编程语言。 大多数音频库都是用C或C ++编写的。 1996年,Steinberg发布了第一个版本的VST SDK,它仍然是最受欢迎的插件格式。 如今,您不再需要用C / C ++编写:Java,Python,C#,Rust等许多VST包装器。 尽管语言仍然是一种局限性,但如今人们甚至可以使用Java处理声音。
- 功能性。 如果问题很简单,则无需编写新的应用程序。 FFmpeg具有大量功能。
在这种情况下,复杂程度取决于您的选择。 最坏的情况是-您必须处理多个库。 如果您真的很不幸,那么您将拥有复杂的抽象和完全不同的接口。
到底是什么?
我们必须在非常复杂和复杂之间进行选择:
- 处理几个低级API来发明我们自己的轮子
- 处理几个API并尝试使其成为朋友
选择哪种方式都无关紧要,任务总是落在管道上。 技术可能有所不同,但本质没有改变。 同样,问题在于,除了解决问题之外,我们还必须建立一条管道。
但是有一个选择。
唱机

创建phono
是为了解决常见的任务-“ 接收,处理和发送 ”声音。 它利用管道作为最自然的抽象。 Go官方博客中有一篇文章。 它描述了管道模式。 流水线模式的核心思想是,数据处理分为几个阶段,这些阶段独立工作并通过通道交换数据。 那就是需要的。
但是,为什么要走?
最初,许多音频软件和库都是用C编写的。Go被称为其继任者。 最重要的是,我们可以采用和使用cgo和现有音频API的各种绑定。
其次,我认为Go是一门好语言。 我不想深入探讨,但是我注意到它的并发可能性。 通道和goroutines使管道的实现变得更加容易。
抽象
pipe.Pipe
结构是phono
的核心,它实现了流水线模式。 就像在博客示例中一样,定义了三个阶段:
-
pipe.Pump
– 接收声音,仅输出通道 -
pipe.Processor
– 处理声音,输入和输出通道 -
pipe.Sink
– 发送声音,仅输入通道
数据在pipe.Pipe
内的缓冲区中pipe.Pipe
。 允许您构建管道的规则:

- 单管
pipe.Pump
- 多个
pipe.Processor
,按顺序放置 - 单个或多个
pipe.Sink
,平行放置 - 所有
pipe.Pipe
组件应具有相同的内容:
- 缓冲区大小
- 采样率
- 通道数
最小配置是带有单个水槽的泵,其余是可选的。
让我们来看几个例子。
简单
问题:播放wav文件。
让我们以“ 接收,处理,发送 ”形式表达一个问题:
- 从WAV文件接收音频
- 将音频发送到端口音频设备

音频被读取并立即播放。

再次,没有处理阶段,但是我们现在有两个管道!

在这里,我们有三个pipe.Pipe
实例,所有实例都与一个混音器相连。 执行从p.Begin(pipe.actionFn) (pipe.State, error)
。 与p.Do(pipe.actionFn) error
,它不会阻止调用,只是返回预期状态。 可以通过p.Wait(pipe.State) error
等待状态。
下一步是什么?
我希望phono
是一个非常方便的应用程序框架。 如果有声音的任务,则无需了解复杂的API,也无需花时间研究标准。 您需要做的就是用合适的元素构建一个管道并启动它。
在过去的六个月中,构建了以下软件包:
-
phono/wav
–读取/写入WAV文件 -
phono/vst2
–未完成VST2 SDK绑定,仅打开插件并调用其方法,并非所有结构都被映射 -
phono/mixer
–混音器,汇总N个信号,没有平衡和音量 -
phono/asset
–采样缓冲区 -
phono/track
–顺序读取缓冲区 -
phono/portaudio
–播放实验音频
除了此列表之外,新想法的积压也在不断增加,其中:
- 时间测量
- 可变的实时流水线
- HTTP泵/接收器
- 参数自动化
- 重采样处理器
- 搅拌机的平衡和体积
- 实时泵
- 多轨同步泵
- 完整的vst2
即将发表的文章的主题:
-
pipe.Pipe
生命周期–由于内部结构复杂,其状态由有限状态机管理 - 如何编写自己的管道阶段
这是我的第一个开源项目,因此很高兴获得任何帮助和建议。
链接
- 唱机
- 并发模式:管道和取消