6个版本

0.2.0 2020年5月16日
0.1.4 2020年5月14日
0.1.3 2020年4月8日

#1101 in Rust模式

44 每月下载量

MIT/Apache

38KB
560

管道风格的流处理

Current Crates.io Version

这个Rust包包含了一个用于组合式处理管道的抽象层,灵感来源于Rust的 Iterator 和 Haskell的 pipes

包的核心是 Pipe trait,它基本上可以归结为

trait Pipe {
    type InputItem;
    type OutputItem;

    fn next(&mut self, input: Self::InputItem) -> Self::OutputItem;
}

它类似于 Iterator,因为它产生一个项目流,但它通过同时消耗一个项目流来实现。就像 Iterator 一样,它也有许多装饰方法,用于对管道进行常见的修改,并且它们也可以连接起来形成更大的管道。

示例

以下是如何设计一个方波发生器的示例

管道被分成几个部分。首先,它从一个开区间迭代器开始,并将其包装在一个管道中。你可以这样做,因为迭代器可以被视为一个消耗一个 () 并产生一个 Option<T> 的任意 T 的管道。然后,它使用一个惰性构建的管道片段来解包值。

现在,我们必须将索引流转换成某种波形。这是通过两个自定义管道: ProgressSquareWave 来实现的。 Progress 管道接受一个索引流,并将它们包装在给定的波长内。它还将包装的索引除以波长的长度,这基本上创建了一个从 0.01.0 的锯齿波。

这个信号可以用来渲染任何波形,而与波长无关,这是 SquareWave 管道所做的。

use iterpipes::*;

/// A pipe that turns an index into a periodic progress value between 0.0 and 1.0.
struct Progress {
    period_length: usize,
}

impl Pipe for Progress {
    type InputItem = usize;
    type OutputItem = f32;

    fn next(&mut self, index: usize) -> f32 {
        (index % self.period_length) as f32 / self.period_length as f32
    }
}

/// A pipe that turns a progress value into a square wave.
struct SquareWave;

impl Pipe for SquareWave {
    type InputItem = f32;
    type OutputItem = f32;

    fn next(&mut self, progress: f32) -> f32 {
        if progress < 0.5 {
            -1.0
        } else {
            1.0
        }
    }
}

// Putting it all together
let mut pipe = PipeIter::new(0..).compose()
    >> Lazy::new(|i: Option<usize>| i.unwrap())
    >> Progress {period_length: 4}.compose()
    >> SquareWave;

// Asserting that it works!
for frame in &[-1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0] {
    assert_eq!(*frame, pipe.next(()));
}

问答

为什么我应该使用 Pipe

《Pipe》的替代方案是将所有内容写成一个大的函数。然而,这样做使得测试算法的各个部分以及在不同任务中重复使用它变得困难。然而,使用定义良好且粒度化的管道可以提升代码的可重用性和可测试性。

在上面的例子中,例如,你可以用正弦波发生器或采样映射器替换方波发生器,而不必担心其他内容。同时,也很容易使用相同的代码播放更长的样本,并在播放时减速。

拥有许多小方法的另一个优点是,你可以为它们编写单独的单元测试。当你正在处理实时音频处理且无法在程序运行时调试时,这尤其有用。

手动编写会更慢吗?

显然,使用许多小函数构建一个大的函数会引入一些开销。然而,这种开销很小,大约1-2%,并且也可以通过启用链接时优化完全消除。启用后,llvm链接器将最终二进制文件(程序、共享对象或静态库)作为一个整体来评估,并跨越函数和crate边界进行优化和内联。只需将以下行添加到您的Cargo.toml中即可。

[profile.release]
lto = true

[profile.bench]
lto = true

本项目还包含一个小型基准测试,该测试使用正弦波和攻击-衰减包络计算节拍器的信号。根据您的机器,这需要大约15分钟。

关闭lto时,基于Pipe的实现比手动实现慢约1-2%,但开启lto后,它们的性能完全相同。

我可以将其用于我的商业产品吗?

是的,您可以使用MIT或Apache 2.0许可证来许可代码,这意味着您几乎可以做什么!

无运行时依赖