#audio-processing #traits #abstraction #buffer #processor #traits-structs #node

audio-processor-traits

音频处理器类型和音频缓冲区类型的特性。可能会大幅更改。

15个版本 (7个稳定)

4.2.0 2024年1月17日
4.1.0 2023年5月22日
4.0.0 2023年4月19日
3.2.0 2023年3月22日
0.3.1 2021年7月23日

#29 in 音频

Download history 61/week @ 2024-03-11 83/week @ 2024-03-18 92/week @ 2024-03-25 98/week @ 2024-04-01 43/week @ 2024-04-08 43/week @ 2024-04-15 133/week @ 2024-04-22 70/week @ 2024-04-29 36/week @ 2024-05-06 50/week @ 2024-05-13 62/week @ 2024-05-20 38/week @ 2024-05-27 48/week @ 2024-06-03 56/week @ 2024-06-10 49/week @ 2024-06-17 52/week @ 2024-06-24

每月下载量206
用于 18 个crate(16个直接使用)

MIT 许可证

96KB
1.5K SLoC

音频处理器特性

crates.io docs.rs


音频处理器类型和音频缓冲区类型的特性。可能会大幅更改。

简介

这很大程度上是基于探索的,我仍在寻找最佳Rust类型API来表达。

我认为我已经找到了一个处理AudioBuffer和基本FXAudioProcessor的好抽象,但还有更多东西需要挖掘。

动机

为处理音频的结构提供通用特性会很好。

这些可能是音频处理之上的1或多个抽象,使得高级系统可以操作音频处理节点。

在核心,音频处理可能被定义为函数

fn process_audio(_input: &mut [f32]) { /* ... */ }

input将是你输入/输出缓冲区,你可以在其上执行可变操作。然而,这并没有编码几个音频处理问题,如

  • 缓冲区不总是f32,它可能是f64
  • 缓冲区通道数没有表达
  • 多通道数据的缓冲区布局没有表达(例如,交错数据或其他方式)
  • 采样率没有表达
  • 干/湿配置没有表达
  • MIDI及其他问题没有表达

我们希望有一些抽象可以解决这些问题。不考虑外部系统问题(如MIDI、状态和干/湿),一个基本的音频处理器特性可以解决缓冲区/样本转换问题。

AudioBuffer

提供了一个AudioBuffer特性。它提供了一个抽象来获取缓冲区的大小并修改它。

《AudioBuffer》特质可以包裹不同布局或所有权的样本,然而,建议使用《AudioBuffer::frame》、《AudioBuffer::frame_mut》、《AudioBuffer::slice》和《AudioBuffer::slice_mut》来处理样本。

原因在于使用《slice》迭代器比遍历数字范围并调用《AudioBuffer::get》更高效。

遗憾的是,在这个crate中还没有提供一种统一优化的方法来迭代具有不同布局的缓冲区。

我认为可以有一种通道迭代器,它将包裹切片迭代器。通道数量应该较低,这样在读取帧时丢失一些优化不应成问题。

请注意,10-20倍的减速是基于一个非常简单的“增益”工作负载。实际上这可能会成为一个问题。

在我的电脑上,使用《get/set》函数大约需要每512个样本600纳秒的开销。

使用不检查边界的《_unchecked》版本,开销约为300纳秒,而使用《frames/slice》则没有开销。

相比之下,我当前的混音到VST缓冲区转换实现将CPAL转换为VST大约需要1.15微秒,从VST转换回CPAL大约需要1.1微秒。

这意味着在转换每个512个样本块时大约会花费2微秒,而迭代则会导致300-600纳秒的减速。

这让我想到,可能更好的做法是让《AudioProcessor》只公开一个《process_sample(&mut self, sample: f32)》函数,这样它们就与《AudioBuffer》没有任何联系。

AudioProcessor

《AudioProcessor》特质仅有两个方法

pub trait AudioProcessor {
    type SampleType;
    fn prepare(&mut self, _settings: AudioProcessorSettings) {}
    fn process<BufferType: AudioBuffer<SampleType = Self::SampleType>>(
        &mut self,
        data: &mut BufferType,
    );
}

它提供了一个准备回调,其中会提供通道和采样率配置,以及一个处理回调,其中提供了一个通用的《AudioBuffer》。

设计说明

SampleType关联类型

《SampleType》作为关联类型提供给《AudioBuffer》和《AudioProcessor》特质。这使得实现者可以在他们的处理器中使用通用的《SampleType》类型。

例如,这是在这个crate中《SilenceAudioProcessor》的实现,它应该适用于任何《num::Float》类型和任何《AudioBuffer》实现

pub struct SilenceAudioProcessor<SampleType>(PhantomData<SampleType>);

impl<SampleType: num::Float + Send> AudioProcessor for SilenceAudioProcessor<SampleType> {
    type SampleType = SampleType;

    fn process<BufferType: AudioBuffer<SampleType = Self::SampleType>>(
        &mut self,
        output: &mut BufferType,
    ) {
        for sample_index in 0..output.num_samples() {
            for channel_index in 0..output.num_channels() {
                output.set(
                    channel_index,
                    sample_index,
                    <BufferType as AudioBuffer>::SampleType::zero(),
                );
            }
        }
    }
}

待办事项

  • MIDI特质
  • 更丰富的应用程序API
  • 状态管理指南,使用后台引用计数垃圾收集器与不可变的“状态句柄”引用(同时仍然允许处理器的内部状态可变)
  • 为所有特质实现自动实现VST API
  • 为所有特质实现自动实现LV2 API
  • 为所有特质实现自动实现一个基于“独立”《cpal》的应用程序(请参阅此存储库中的《audio-processor-standalone》)
  • 音频图实现
  • GUI支持
  • 测试工具

缓冲区性能

在简单的增益基准测试中,使用AudioBuffer::get API的性能在VecAudioBuffer上比在Vec上差10-20倍。

其他需要测量的内容

  • 在小型窗口大小上测量缓冲区转换时间
    • AudioBuffer抽象的额外开销进行比较
  • 与非平凡处理器(非增益)的大范围开销进行比较

其他思考

  • 哪种布局更高效?
  • 音频处理器应该公开一个逐样本处理的process函数吗?也许这更容易优化

许可证

MIT

依赖项

~0.9–3MB
~63K SLoC