31个版本 (17个重大更新)

0.18.2 2024年7月6日
0.17.1 2024年4月16日
0.17.0 2024年3月30日
0.16.0 2023年12月29日
0.5.1 2022年3月29日

#21 in 音频

Download history 138/week @ 2024-05-03 145/week @ 2024-05-10 121/week @ 2024-05-17 104/week @ 2024-05-24 116/week @ 2024-05-31 142/week @ 2024-06-07 136/week @ 2024-06-14 415/week @ 2024-06-21 108/week @ 2024-06-28 208/week @ 2024-07-05 102/week @ 2024-07-12 149/week @ 2024-07-19 236/week @ 2024-07-26 124/week @ 2024-08-02 121/week @ 2024-08-09 94/week @ 2024-08-16

每月597次下载
用于 10 个crate(6个直接)

MIT/Apache

1MB
18K SLoC

FunDSP

Actions Status crates.io License

Rust音频处理和合成库

FunDSP 是一个音频DSP(数字信号处理)库,用于音频处理和合成。

FunDSP具有强大的内联图形符号,用于描述音频处理网络。该符号利用可组合、零成本的抽象,将音频网络的架构表达为 Rust 类型。

FunDSP的另一项创新功能是其信号流系统,可以确定任何 线性网络 的分析 频率响应

FunDSP附带一个组合环境,包含一系列音频组件、数学和实用函数以及过程生成工具。

用途

  • 游戏和应用中的音频处理和合成
  • 教育
  • 音乐制作
  • 声音黑客和音频高尔夫
  • DSP算法的原型设计

Rust Audio Discord

要讨论 FunDSP 和其他主题,请加入我们的 Rust Audio Discord

bevy_fundsp 将 FunDSP 集成到 Bevy 游戏引擎中。

midi_fundsp 允许使用 FunDSP 进行合成,轻松创建现场合成软件。

安装

fundsp 添加到您的 Cargo.toml 作为依赖项。

[dependencies]
fundsp = "0.18.1"

默认启用 files 功能。它添加了对通过 Symphonia crate 加载音频文件到 Wave 对象的支持。

no_std 支持

FunDSP支持no_std环境。要启用no_std,请禁用默认启用的功能std。对于需要分配内存的组件,仍然需要alloccrate。

no_std中不可用音频文件读取和写入。

[dependencies]
fundsp = { version = "0.18.1", default-features = false }

图表示法

FunDSP可组合图表示法使用图运算符以代数形式表达音频网络。它与功能环境一起开发,以最大限度地减少完成常见音频任务所需的类型字符数量。

许多常见算法可以以自然的形式表达,便于理解。例如,一个FM振荡器可以简单地写成(对于某些fm):

sine_hz(f) * f * m + f >> sine()

上述表达式定义了一个音频图,它使用内置在Rust中的强大泛型抽象编译成堆栈分配的内联形式。连接错误会在编译期间检测到,从而节省开发时间。

音频DSP成为一等公民

无需宏,FunDSP图表示法将音频DSP紧密集成到Rust编程语言中,作为一等公民。本机Rust运算符优先级与表示法和谐共存,最大限度地减少了所需括号的数量。

FunDSP图表达式在泛型方面提供更多的经济性,因为通道算术被编码在类型级别。单声道网络可以通过用立体声发生器和滤波器替换其单声道发生器和滤波器来表示为立体声网络,图表示法保持不变。

基础知识

组件系统

有两种并行组件系统:静态的AudioNode和动态的AudioUnit


特质 分发 分配策略 连接性
AudioNode 静态,内联 堆栈 输入和输出算术在编译时固定
AudioUnit 动态,对象安全 输入和输出算术在构造后固定

所有AudioNodeAudioUnit组件都使用32位浮点样本(f32)。

任何系统中的组件的主要属性是它是一个具有特定数量输入和输出连接的图处理节点,称为其算术。音频和控制信号通过输入和输出连接流动。

两个系统都同步地以无限流的形式操作信号。可以使用reset方法在任何时候将流重置到开始。

大多数AudioNode可以堆栈分配。某些节点可能使用堆进行音频缓冲区等。

allocate方法预先分配所有需要的内存。应该在将某些内容发送到实时上下文之前最后调用它。在NetSequencer前端中会自动执行此操作。

AudioUnit系统的目的是在动态情况下提供更多灵活性:关于输入和输出算术和内容的决定可以延迟到运行时。

转换

使用包装类型An,它实现了AudioUnit,将AudioNode转换为AudioUnit系统。预述中的指令返回已包装的节点。

使用包装器unit,可以将AudioUnit转换为AudioNode。在这种情况下,必须提供输入和输出算术作为类型级别的常量U0U1、...,例如:

use fundsp::hacker32::*;
// The number of inputs is zero and the number of outputs is one.
let type_erased: An<Unit<U0, U1>> = unit::<U0, U1>(Box::new(white() >> lowpass_hz(5000.0, 1.0) >> highpass_hz(1000.0, 1.0)));

处理中

AudioNodeAudioUnit系统中处理样本都非常简单。`tick`方法用于处理单个样本帧,而`process`方法则处理整个块。

如果最大速度很重要,那么使用块处理是个好主意,因为它分摊了函数调用、处理设置和动态网络开销,并支持显式的SIMD

可以使用`get_mono`和`filter_mono`方法检索单声道样本。`get_mono`方法从一个没有输入和一个或两个输出的生成器返回下一个样本,而`filter_mono`方法从只有一个输入和一个输出的节点过滤下一个样本。

let out_sample = node.get_mono();
let out_sample = node.filter_mono(sample);

可以使用`get_stereo`和`filter_stereo`方法检索立体声样本。`get_stereo`方法从一个没有输入和一个或两个输出的生成器返回下一个立体声样本对,而`filter_stereo`方法从有两个输入和两个输出的节点过滤下一个样本。

let (out_left_sample, out_right_sample) = node.get_stereo();
let (out_left_sample, out_right_sample) = node.filter_stereo(left_sample, right_sample);

块处理

buffer模块包含用于块处理的缓冲区。缓冲区包含32位浮点样本。有两种类型的所有者缓冲区:静态的BufferArray和动态的BufferVec

缓冲区总是64个样本长(MAX_BUFFER_SIZE),具有任意数量的通道,并且使用来自wide crate 的f32x8类型显式地SIMD加速。样本在扁平数组中以非交错方式排列。

BufferArray是由数组支持的音频缓冲区。通道数是一个必须在编译时已知的泛型参数。使用这种缓冲区类型,可以在不分配堆内存的情况下进行块处理。

BufferVec是由动态向量支持的音频缓冲区。缓冲区是堆分配的。通道数可以在运行时决定。

use fundsp::hacker::*;
// Create a stereo buffer on the stack.
let mut buffer = BufferArray::<U2>::new();
// Declare stereo noise.
let mut node = noise() | noise();
// Process 50 samples into the buffer. There are no inputs, so we can borrow an empty buffer.
node.process(50, &BufferRef::empty(), &mut buffer.buffer_mut());
// Create another stereo buffer, this one on the heap.
let mut filtered = BufferVec::new(2);
// Declare stereo filter.
let mut filter = lowpole_hz(3000.0) | lowpole_hz(3000.0);
// Filter the 50 noise samples.
filter.process(50, &buffer.buffer_ref(), &mut filtered.buffer_mut());

要自动在幕后调用process,请使用BlockRateAdapter适配器组件。然而,它仅与生成器一起工作,这些是没有任何输入的组件。

要访问缓冲区中的f32值,请使用具有f32后缀的方法,例如at_f32channel_f32

样本率独立性

在图中流动的信号中,一些包含音频,而另一些则是不同类型的控制。

对于控制信号和参数,我们更喜欢使用自然单位,如Hz和秒。保持参数与样本率无关很有用,这样我们就可以像我们喜欢的那样调整它。

除了样本率调整之外,自然单位还支持在易于配置和修改的嵌套部分中实现选择性过采样(使用`oversample`组件)。

一些低级组件按设计忽略样本率,例如单个样本延迟`tick`。

默认样本率为44.1 kHz。在两个系统中,都可以通过`A.set_sample_rate(sample_rate)`为组件`A`及其可能有的任何子组件设置样本率。

音频处理环境

FunDSP 预言定义了音频处理的便捷组合环境。

有三种与名称兼容的预言版本。

默认环境(`fundsp::prelude`)提供了一个通用接口。

64位黑客环境(fundsp::hacker)用于音频黑客,使用64位内部状态来最大化音频质量。

32位黑客环境(fundsp::hacker32)使用32位内部状态。其目的是提供最大的处理速度。

fundsp 接口的应用可以按需混合和匹配序言。环境的目的是

  • 最小化表示习语所需的字符数。
  • 保持语法简洁,以便黑客环境的子集可以简单地解析为高级DSL,以快速原型设计。
  • 使语法对没有编程经验的人也是可用的。

确定性伪随机相位

FunDSP 使用确定性伪随机相位系统来处理音频生成器。生成器相位从网络结构和节点位置生成。

因此,两个相同的网络分别听起来相同,但组合时则不同。这意味着 noise() | noise() 是一个立体声噪声源,例如。

伪随机相位是一种尝试解耦不同音频通道的方法。它还用于选择包络的样本点,有助于产生“更温暖”的声音。

运算符

提供自定义运算符以在行内组合音频组件。按照优先级顺序,从高到低


表达式 意义 输入 输出 备注
-A 否定 A a a 否定任意数量的输出,甚至零。
!A 通过 A a 与输入相同 传递额外的输入。
A*B AB 相乘 a + b a = b 也称为放大,或者当两者都是音频信号时的环形调制。在 AB 中的输出数量必须匹配。
A * constant A 与常数相乘 a a Broadcasts constant. Same applies to constant * A.
A+B AB 相加 a + b a = b 也称为混合。在 AB 中的输出数量必须匹配。
A + constant A 添加 a a Broadcasts constant. Same applies to constant + A.
A-B AB 中减去差值 a + b a = b AB 中的输出数量必须匹配。
A - constant A 中减去 a a Broadcasts constant. Same applies to constant - A.
A>>B A 管道到 B a b 也称为链式。在 A 中的输出数量必须匹配在 B 中的输入数量。
A&B AB 网络化 a = b a = b 计算 AB 的和。 AB 必须具有相同的连接性。
A^B 将分支输入并联到 AB a = b a + b AB 的输入数量必须匹配。
A|B AB 并联堆叠 a + b a + b 连接 AB 的输入和输出。

在表中,constant 表示一个 f32 值。

所有运算符都是结合的,除了左结合的 -

一些运算符的替代方法是预定义中的函数。其中一些有多个组合版本;这些只适用于相同类型的节点,并且节点数量在编译时静态设置。

所有函数中节点都是内联分配的,以及任何用于块处理的内部缓冲区。

运算符形式 函数形式 多重组合形式
!A 通过(A) -
A*B 乘积(A,B) -
A+B 总和(A,B) 总和i,总和f
A>>B 管道(A,B) 管道i,管道f
A&B 总线(A,B) 总线i,总线f
A^B 分支(A,B) 分支i,分支f
A|B 堆栈(A,B) 堆叠i,堆叠f

运算符图

上图中的每个青色圆点可以包含任意数量的通道,包括零。

AudioNode 系统中,通道数在编译时静态确定,而在 AudioUnit 系统中(使用 Net),通道数可以在运行时确定。

广播

算术运算符按通道应用于输出。

两个组件之间的算术运算永远不会广播通道:通道算符必须始终匹配。

然而,与 f32 值的直接算术运算会广播到任意数量的通道。

否定运算符也会广播:-A 等价于 (0.0 - A)

例如,A * constant(2.0)A >> mul(2.0) 是等价的,并期望 A 有一个输出。另一方面,A * 2.0 可以与任何 A 一起工作,即使没有输出。

通过

通过 (!) 运算符是用于链式连接具有类似连接性的滤波器的语法糖。

它调整输出算符以匹配输入算符,并将任何缺失的输出传递到下一个节点。缺失的输出是滤波器的参数。

例如,虽然 lowpass() 是一个二阶低通滤波器,!lowpass() >> lowpass() 是一个具有相同连接性的更陡峭的四阶低通滤波器。

通过操作符也可以作为一个函数使用:thru(A)!A等价。

生成器、过滤器与接收器

组件可以广泛分为生成器、过滤器和接收器。 生成器 只有输出,而 过滤器 既有输入又有输出。

接收器是没有输出的组件。对接收器进行直接算术运算相当于无操作。在预定义中,sink()返回一个单通道接收器。

图组合子

在操作符中,特别值得注意的是四个自定义组合子:管道 ( >> ),总线 ( & ),分支 ( ^ ) 和 堆栈 ( | )。

管道是一个序列操作符,组件按照 处理 顺序出现。分支、堆栈和算术操作符是并行操作符,组件按照 通道 顺序出现。

总线是一个交换操作符,组件可以以任何顺序出现。其他操作符通常不是交换的。

所有这四个都是完全结合的,并且每个都有自己的连接规则。

管道

管道 ( >> ) 操作符构建类似于函数组合的传统处理链。 A 首先运行,然后是 B。在 A >> B 中,A 的每个输出都通过管道连接到 B 的匹配输入,因此 A 的输出元数必须与 B 的输入元数相匹配。

整个组合具有 A 的输入元数和 B 的输出元数。管道是一个基本操作。它以串联的方式连接单元。

可以将接收器连接到生成器。这与堆叠相似。处理工作正常,接收器在生成器运行之前处理其输入。

管道操作也可以作为一个函数使用:pipe(A, B)等于 A >> B

分支

在算术操作符是归约性质的情况下,分支 ( ^ ) 操作符将信号分割成并行分支。

A ^ B 中,两个组件接收相同的输入,但它们的输出是互斥的。因为组件接收相同的输入,所以 AB 中的输入数量必须匹配。在 A ^ B 中,A 的输出先出现,然后是 B 的输出。

分支对于构建如过滤器之类的组件的 非常有用。

分支操作也可以作为一个函数使用:branch(A, B)等于 A ^ B

总线

总线 ( & ) 操作符可以看作是一个具有固定输入和输出通道的行内 音频总线。它从具有相同连接性的组件中构建信号总线。

A & B 中,相同的输入被发送到 AB,它们的输出被混合在一起。总线上的组件可以按任何顺序出现。

总线特别有用,因为它不会改变连接性:我们可以随时将任何一组匹配的组件连接起来,而不会影响到表达式的其他部分。

两者 A + BA & B 都是混合运算符。两者之间的区别在于 A + B 是减少的:AB 有它们自己的、不重叠的输入,这些输入在输出处组合。在 A & B 中,两个组件都来自相同的输入,输入的数量必须匹配。

总线的一个应用是与 pass 操作码结合控制效果混合,该操作码通过不变地传递信号。例如,要给单声道信号添加20%的合唱,可以输入 pass() & 0.2 * chorus(0, 0.0, 0.01, 0.3)

或者,要给立体声信号添加20%的混响,可以输入 multipass() & 0.2 * reverb_stereo(20.0, 2.0, 1.0)。在这里,类型推断对我们有利,节省了编写 multipass 的可变参数,并且常数 0.2 被广播到两个通道。

总线操作还可以作为一个函数:bus(A, B) 等于 A & B

堆栈

堆栈(|)运算符构建组合组件。它可以应用于任何两个组件。

作为一个图运算符,堆栈对应于不交并。在 A | B 中,AB 的输入和输出是不交的,并且它们是独立、并行处理的。

在堆栈中,组件按通道顺序书写。在 A | B | C 中,首先是 A 的通道,然后是 B 的通道,最后是 C 的通道。

堆栈通常用于构建过滤器参数。例如,在 (pass() | constant((440.0, 1.0))) >> lowpass() 中,输入信号通过堆栈和两个其他信号,频率和Q,连接到它,以形成低通滤波器的输入。

堆栈操作也可以作为一个函数:stack(A, B) 等于 A | B

表达式是图

表达式 A >> (B ^ C ^ D) 定义了一个信号处理图。它具有任何输入 A,并从 BCD 并行输出所有内容。

整个结构被打包、单态化和内联,构成节点被消耗。如果您想重用组件,请将其定义为函数或闭包,或克隆它们。请参阅示例预读。

在编译期间检查连接性。不匹配的连接性将导致编译错误,错误信息将关于不匹配的 typenum 类型。连接 tick 中组件的数组 Frame<f32, Size> 来自 generic-arraynumeric-array 库。

计算结构

图组合子消耗它们的参数。这防止了循环,并使结果计算图具有整体树形结构。

隐式循环防止意味着构建的结构在数据流意义上总是计算高效的。所有计算数据的重用都在组合子和组件的本地进行。

在 FunDSP 图表示法中,有两种主要方式来结构信号的重用:分支和总线。两者都作为基本运算符公开,指导计算的有效结构。因此,数据流问题在图表示法本身中得到阐述。

网络

图表示法对于复杂的图可能会变得繁琐。此外,有时输入和输出的数量直到运行时才知道。

Net 组件为连接 AudioUnit 节点提供了一个显式的图接口。网络的阿基米德数作为构造函数参数指定。

例如,使用 Net 动态构建 dc(220.0) >> sine()

use fundsp::hacker::*;
// Instantiate network with 0 inputs and 1 output.
let mut net = Net::new(0, 1);
// Add nodes, obtaining their IDs.
let dc_id = net.push(Box::new(dc(220.0)));
let sine_id = net.push(Box::new(sine()));
// Connect nodes.
net.pipe(dc_id, sine_id);
net.pipe_output(sine_id);

Net的开销是调用Box<dyn AudioUnit>对象的开销。除此之外,动态版本与静态版本大致一样高效。

图语法也可用于组合Net实例。连接性检查将延迟到运行时。

网络还可以与预定义组件内联组合。组件首先被转换为Net

当需要动态处理时,可以使用Net::wrap开始,它将任何单元转换为网络。

use fundsp::hacker::*;
// Wrap saw wave in a network.
let mut net = Net::wrap(Box::new(saw()));
// Now we can add filters conditionally.
let add_filter = true;
if add_filter {
    net = net >> lowpass_hz(1000.0, 1.0);
}

对于实时情况,Net可以分成前端和后端。前端处理网络的变化,而实时安全的后端渲染音频。

use fundsp::hacker::*;
let mut net = Net::new(0, 1);
let noise_id = net.chain(Box::new(pink()));
// Create the backend.
let mut backend = net.backend();
// The backend is now ready to be sent into an audio thread.
// We can make changes to the frontend and then commit them to the backend.
net.replace(noise_id, Box::new(brown()));
net.commit();
// We can also use the graph syntax to make changes, as long as connectivity
// is maintained at commit time.
net = net >> peak_hz(1000.0, 1.0);
net.commit();

使用动态网络会带来一些开销,因此使用块处理是一个特别好的主意,它可以有效地分摊这些开销。

序列器

Sequencer组件可以动态混合生成节点。它可以以采样精度启动和停止渲染节点。

序列器没有输入,但有用户指定的输出数量。

use fundsp::hacker::*;
// Create stereo sequencer.
// The first argument should be set true if we want to replay events after `reset`.
let mut sequencer = Sequencer::new(false, 2);

可以添加新事件,设置其开始和结束时间以及淡入和淡出包络。序列器返回一个ID,可用于稍后编辑事件。

// Add a new event with start time 1.0 seconds and end time 2.0 seconds.
// Fade-in time is 0.1 seconds, while the fade-out time is 0.2 seconds.
// This returns an `EventId`.
let id1 = sequencer.push(1.0, 2.0, Fade::Smooth, 0.1, 0.2, Box::new(noise() | noise()));
// Add a new event that starts immediately and plays indefinitely.
let id2 = sequencer.push_relative(0.0, f64::INFINITY, Fade::Smooth, 0.1, 0.2, Box::new(pink() | pink()));

用作动态混音器时,序列器可以分成前端和后端。前端用于添加和编辑事件,而实时安全的后端渲染音频。

// Get a backend for this sequencer. This sequencer is then the frontend.
let mut backend = sequencer.backend();
// Now we can insert the backend into, for example, a `Net`.
// Later we can use the frontend to create events and make edits to them; the end time and fade-out time
// can be changed. Here we start fading out the event immediately with an envelope duration of 0.1 seconds.
sequencer.edit_relative(id2, 0.0, 0.1);

输入模式和范围

一些在音频网络中发现的信号。

模式 首选单位/范围 备注
频率 Hz
相位 0...1 波形表振荡器使用这个范围。
时间 s
音频数据 -1...1 内部处理可能使用任何方便的范围。然而,只有特殊的输出格式才能存储此范围之外的音频数据。
立体声声像 -1...1 (从左到右) 出于人体工程学考虑,请考虑将任何声像输入夹在这范围内。
控制量 0...1 如果参数没有自然的解释。

与波形一起工作

FunDSP包括一个多通道波形抽象,称为Wave。例如,渲染10秒的粉红噪声

use fundsp::hacker::*;
let wave1 = Wave::render(44100.0, 10.0, &mut (pink()));

然后使用移动带通滤波器过滤它,并将样本归一化到-1..1

let mut wave2 = wave1.filter(10.0, &mut ((pass() | lfo(|t| (xerp11(110.0, 880.0, spline_noise(0, t * 5.0)), 1.0))) >> bandpass()));
wave2.normalize();

启用std功能时,可以将波形保存为16位或32位WAV。16位格式是整数格式,而32位格式是浮点格式。

例如,将wave2保存到test.wav

wave2.save_wav16("test.wav").expect("Could not save wave.");

处理各种格式的音频文件由Symphonia包处理。Symphonia集成通过默认启用的files功能启用。必须启用std功能。

例如,加载test.wav

let wave3 = Wave::load("test.wav").expect("Could not load wave.");

可以使用wavechwavech_at操作码播放回波形的单个通道。

信号流分析

FunDSP具有一个全面的信号流系统,它分析音频网络中的因果延迟和频率响应。

系统可以通过组合传递函数和折叠常数,从理论上计算出任何线性网络频率响应。线性网络由线性滤波器、延迟以及以下操作构成:

  • 混合。
  • 链式连接。线性滤波器和延迟的链式连接保持线性。
  • 恒定缩放。信号可以被常数因子缩放。

信号延迟在输入到输出的整个过程中都会被详细分析,这有助于自动从效果链中去除预延迟。

例如,FIR滤波器可以通过单样本延迟(tick操作码)和算术直接组合。信号流分析将立即揭示,2点平均滤波器在奈奎斯特频率处增益为零,而3点平均滤波器则不是。

use fundsp::hacker::*;
assert!((pass() & tick()).response(0, 22050.0).unwrap().norm() < 1.0e-9);
assert!((pass() & tick() & tick() >> tick()).response(0, 22050.0).unwrap().norm() > 0.1);

然而,通过适当的缩放,3点FIR也可以消失。

use fundsp::hacker::*;
assert!((0.5 * pass() & tick() & 0.5 * tick() >> tick()).response(0, 22050.0).unwrap().norm() < 1.0e-9);

滤波器

线性滤波器列表

所有线性滤波器都有经过验证的频率响应。


操作码 类型 参数 系列 备注
全通 全通(二阶) 频率,Q Simper SVF
全极点 全通(一阶) 延迟 一阶 在直流下可调节的样本延迟。
带通 带通(二阶) 频率,Q Simper SVF
钟形 峰值(二阶) 频率,Q,增益 Simper SVF 可调节幅度增益。
二阶 二阶二阶 - 二阶 具有固定参数的任意二阶。
低通 低通(二阶) 频率 二阶 巴特沃斯低通具有最大平坦的通带和单调的频率响应。
直流阻塞 直流阻塞器(一阶) 频率 一阶 将信号中心为零,抵消任何常数偏移(直流)。
fir FIR - FIR
跟随 低通(三阶) 响应时间 嵌套一阶 具有可调节边缘响应时间的平滑滤波器。
高通 高通(二阶) 频率,Q Simper SVF
高通(一阶) 高通(一阶) 频率 一阶
高通 shelves 高通 shelves(二阶) 频率,Q,增益 Simper SVF 可调节幅度增益。
低通 低通(二阶) 频率,Q Simper SVF
低通(一阶) 低通(一阶) 频率 一阶
低通 shelves 低通 shelves(二阶) 频率,Q,增益 Simper SVF 可调节幅度增益。
morph morphing(二阶) 频率,Q,morph Simper SVF Morphs between lowpass, peaking and highpass modes.
峰谷 峰谷(二阶) 频率,Q Simper SVF
峰值 峰值(二阶) 频率,Q Simper SVF
粉色带通 低通(3 dB/八度) - 混合 FIR / 1st order 将白噪声转换为粉色噪声。
谐振器 带通(二阶) 频率,带宽 二阶 当带宽变化时,增益保持不变。

参数平滑滤波器

follow滤波器是特殊的。它支持上升(攻击)和下降(释放)段的不同速率。例如,afollow(0.1, 0.2)具有0.1秒的攻击和0.2秒的释放。

它还会立即跳转到输入流中的第一个值,并从那里开始平滑。这意味着输出值始终在输入范围内。

非线性滤波器列表

与线性滤波器不同,非线性滤波器可能对输入信号级别敏感。由于非线性,我们不尝试为这些滤波器计算频率响应。


操作码 类型 参数 系列 备注
bandrez 带通(二阶) 频率,Q 嵌套一阶 对输入级别敏感。
lowrez 低通(二阶) 频率,Q 嵌套一阶 -..-
moog 低通滤波器(四阶) 频率,Q Moog梯形滤波器 -..-

频域重合分析

使用resynth操作码,滤波和其他效果也可以在频域中完成。

重合分析器将输入窗口以四个重叠的窗口转换到频域。然后,处理函数将这些输入转换为频域输出。输出窗口进行逆变换并重叠添加。

重合分析器可以精确地重建输入,因此可以将效果从输入和处理版本之间平滑地变化。

例如,要实现一个高度选择性的FFT带通滤波器

use fundsp::hacker32::*;

// The window length, which must be a power of two and at least four,
// determines the frequency resolution. Latency is equal to the window length.
let window_length = 1024;

// Passband in Hz.
let pass_min = 1000.0;
let pass_max = 2000.0;

// The number of input and output channels is user configurable.
// Here both are 1 (`U1`).
let synth = resynth::<U1, U1, _>(window_length, |fft|
    for i in 0..fft.bins() {
        if fft.frequency(i) >= pass_min && fft.frequency(i) <= pass_max {
            fft.set(0, i, fft.at(0, i));
        }
    });

处理函数可以从提供的FFT窗口对象中获取窗口时间,以支持时间变化的效果。

有关该技术的更多信息,请参阅音频信号的四阶分析和重建

关于多线程和实时控制的更多信息

除了NetSequencer前端外,还有两种方法可以将实时控制引入图表达式:共享原子变量和设置监听器。

原子变量

我们可以使用共享原子变量与外部上下文通信数据。共享变量用初始值声明

use fundsp::hacker::*;
let amp = shared(1.0);

共享变量可以被克隆并发送到另一个线程。要将共享变量输出实例化到音频图中,请使用varvar_fn操作码。例如,要控制振幅

let amp_controlled = noise() * var(&amp);

稍后我们可以从任何地方设置振幅

amp.set_value(0.5);

一个有用的模式是将共享变量通过一个follow滤波器管道传递以平滑参数变化,这里以0.1秒的响应时间

let amp_controlled = noise() * (var(&amp) >> follow(0.1));

timer操作码在共享变量中维护流时间。定时器节点没有输入或输出,可以通过堆叠与任何节点连接。

设置

设置是所有没有专用输入的节点参数。一个节点可以响应一种或多种设置类型,这些设置通过set方法应用。

use fundsp::hacker::*;
let mut node = afollow(0.1, 1.0);
node.set(Setting::attack_release(0.2, 2.0));

要从其他线程调整设置,可以将节点包装在一个设置监听器中,使用listen(node)操作码。它返回一个(sender, node)对。

发送者可以被克隆并从其他线程调用。通过sender可用的设置格式取决于图类型。

可以通过寻址访问结构内的节点

Setting调用 地址含义
left() 选择二进制操作左侧。
right() 选择二进制操作右侧。
index(i) 从类似busi的结构中选择索引i
node(id) Net中选择具有id的节点。

地址在参数构造函数之后指定。它们可以嵌套到四层。

如果监听器连接到二进制操作,则可以使用leftright方法选择不同的侧面。例如,常量作为标量设置公开

use fundsp::hacker::*;
let (sender, node) = listen(dc(0.5) >> resample(pink()));
sender.try_send(Setting::value(0.6).left()).expect("Cannot send setting.");

如果一个参数有专门的输入,则它不能是设置。要获取通过设置控制的参数的过滤器,需要使用指定所有参数为常数的表单,例如,lowpass_hz

use fundsp::hacker::*;
let (sender, node) = listen(lowpass_hz(1000.0, 1.0));

之后我们可以通过 sender 从任何地方发送过滤器参数。例如,低通滤波器支持设置截止频率和 Q 值。将滤波器截止频率设置为 2 kHz,Q 值为 2.0

sender.try_send(Setting::center_q(2000.0, 2.0)).expect("Cannot send setting.");

以下表格总结了可用的设置。

操作码 Setting 构造函数调用
afollow attack_release 设置攻击和释放时间(秒)
allnest_c delay 在直流处的样本延迟
allpass_hz center_q
allpole_delay delay 在直流处的样本延迟
bandpass_hz center_q
bell_hz center_q_gain
二阶 biquad 设置二阶滤波器系数
butterpass_hz center
constant value 设置所有通道上的标量值
dc value 设置所有通道上的标量值
dcblock_hz center
dsf_saw_r roughness 在 0...1 之间
dsf_square_r roughness 在 0...1 之间
跟随 time 设置跟随时间(秒)
highpass_hz center_q
highpole_hz center
highshelf_hz center_q_gain
hold variability 在 0...1 之间
lowpass_hz center_q
lowpole_hz center
lowshelf_hz center_q_gain
moog_hz center_q
notch_hz center_q
pan pan 设置 -1...1 之间的声像值
peak_hz center_q
resonator_hz center_bandwidth 设置中心和带宽(Hz)

如果节点对 center_q_gain 做出响应,则它也对 center_qcenter 做出响应。如果节点对 center_q 做出响应,则它也对 center 做出响应。


参数均衡器配方

在这个例子中,我们使用峰值 bell 滤波器制作了一个 12 带双精度参数均衡器。样本类型,像往常一样,是 32 位浮点数(f32)。

首先,声明处理管道。在这里,我们从 1 kHz 开始,以 1 kHz 的增量间隔设置波段,将 Q 值设置为 1.0,并将所有波段的增益最初设置为 0 dB

use fundsp::hacker::*;
let mut equalizer = pipei::<U12, _, _>(|i| bell_hz(1000.0 + 1000.0 * i as f32, 1.0, db_amp(0.0)));

均衡器的类型是 An<Chain<U12, FixedSvf<f64, BellMode<f64>>>>。均衡器可以立即使用。滤波器样本

let output_sample = equalizer.filter_mono(input_sample);

我们可以通过 equalizer.node(i)equalizer.node_mut(i) 访问单独的波段,其中 i 的范围从 0 到 11。将波段 0 设置为在 500 Hz 放大 10 dB,Q 值设置为 2.0

equalizer.node_mut(0).set_gain(db_amp(10.0));
equalizer.node_mut(0).set_center(500.0);
equalizer.node_mut(0).set_q(2.0);

为了绘制频率响应,我们可以查询均衡器。查询 1 kHz 处的均衡器增益

let decibel_gain_at_1k = equalizer.response_db(0, 1000.0).unwrap();

默认采样率为 44.1 kHz。将采样率设置为 48 kHz

equalizer.set_sample_rate(48_000.0);

对于实时控制,均衡器可以配备设置监听器。

let (sender, equalizer) = listen(equalizer);

然后,我们可以发送设置以更改中心频率、Q 和增益。由于我们使用 bell_hz 形式构建了过滤器,因此设置是可用的。将波段 1 设置为在 1000 Hz 放大 3 dB,Q 值设置为 2.0

sender.try_send(Setting::center_q_gain(1000.0, 2.0, db_amp(3.0)).index(1)).expect("Cannot send setting.");

evcxr

display 方法返回有关节点信息,包括通道 0 的频率响应的 ASCII 图表。

以下是一个使用 evcxr 交互式检查频率响应的示例

C:\rust>evcxr
Welcome to evcxr. For help, type :help
>> :dep fundsp
>> use fundsp::hacker::*;
>> print!("{}", bell_hz(1000.0, 1.0, db_amp(50.0)).display())
 60 dB ------------------------------------------------  60 dB
 
 50 dB -------------------------.----------------------  50 dB
                                *
 40 dB -------------------------*----------------------  40 dB
                               ** 
 30 dB -----------------------.***---------------------  30 dB
                             ******.
 20 dB -------------------..*********.-----------------  20 dB
                       ..**************.
 10 dB -------------..********************..-----------  10 dB
               ..*****************************...
  0 dB ....***************************************.....   0 dB
       |   |    |    |     |    |    |     |    |    |
       10  50   100  200   500  1k   2k    5k   10k  20k Hz

Peak Magnitude : 50.00 dB (1000 Hz)
Inputs         : 1
Outputs        : 1
Latency        : 0.0 samples
Footprint      : 96 bytes

>>

免费函数

这些免费函数在环境中可用。


组件操作码

表中类型参数指黑客前奏。

IMNOU 是类型级别整数。它们是 U0U1U2...


函数 输入 输出 说明
add(x) x x 将常数 x 添加到信号。
adsr_live(a,d,s,r) 1 1 ADSR 包络。攻击时间 a、衰减时间 d、持续电平 s、释放时间 r。输入 > 0.0 开始攻击,输入 <= 0.0 开始释放。输出在 [0.0, 1.0] 之间。
afollow(a,r) 1 1 具有半攻击时间 a 秒和半释放时间 r 秒的不对称平滑滤波器。
allnest(x) 2 (输入,系数) 1 嵌套全通滤波器,内部全通处理 x
allnest_c(c,x) 1 1 嵌套全通滤波器,前馈系数 c 和内部全通处理 x
全通() 3 (音频,频率,Q) 1 全通滤波器(二阶)。
allpass_hz(f,q) 1 1 f Hz 为中心的二阶全通滤波器,Q 为 q
allpass_q(q) 2 (音频,频率) 1 具有 Q q 的二阶全通滤波器。
全极点() 2 (音频,延迟) 1 一阶全通滤波器。第二个输入是样本延迟(delay > 0)。
allpole_delay(延迟) 1 1 一阶全通滤波器,在直流处有 delay 毫秒(delay > 0)。
带通() 3 (音频,频率,Q) 1 带通滤波器(二阶)。
bandpass_hz(f,q) 1 1 f Hz 为中心的二阶带通滤波器,Q 为 q
bandpass_q(q) 2 (音频,频率) 1 具有 Q q 的二阶带通滤波器。
bandrez() 3 (音频,频率,Q) 1 谐振带通滤波器(二阶)。
bandrez_hz(f,q) 1 1 f Hz 为中心的二阶谐振带通滤波器,谐振 q 在 0...1 之间。
bandrez_q(q) 2 (音频,频率) 1 具有谐振 q 在 0...1 之间的二阶谐振带通滤波器。
钟形() 4 (音频,频率,Q,增益) 1 具有可调幅度增益的峰值滤波器(二阶)。
bell_hz(f,q,增益) 1 1 f Hz 为中心的二阶峰值滤波器,Q 为 q,幅度增益为 gain
bell_q(q,增益) 2 (音频,频率) 1 具有 Q q 和幅度增益 gain 的峰值滤波器(二阶)。
二阶(a1,a2,b0,b1,b2) 1 1 具有归一化系数的任意 二阶滤波器
brown() - 1 布朗噪声。
分支(x,y) x=y x+y 分支到 xy。与 x ^ y 相同。
分支i::<U,_, _>(f) f U*f 从索引生成器 f 分支到 U 节点。
分支f::<U,_, _>(f) f U*f 从分数生成器f分支到U节点,例如:| x | resonator_hz(xerp(20.0, 20_000.0, x), xerp(5.0, 5_000.0, x)).
总线(x,y) x=y x=y 总线xy。与x & y相同。
总线i::<U,_, _>(f) f f 将索引生成器fU节点组合在一起,例如:| i | mul(i as f64 + 1.0) >> sine().
总线f::<U,_, _>(f) f f 将分数生成器fU节点组合在一起。
低通() 2 (音频,频率) 1 巴特沃斯低通滤波器(二阶)。
butterpass_hz(f) 1 1 截止频率为f Hz的巴特沃斯低通滤波器(二阶)。
合唱(种子,分离,变量, 调制) 1 1 具有LFO种子seed、声部分离sep秒、延迟变化var秒和LFO调制频率mod Hz的合唱效果。
clip() 1 1 将信号裁剪到-1...1。
clip_to(min,max) 1 1 将信号裁剪到min...max。
constant(x) - x 常量信号x。同义词为dc
dc(x) - x 常量信号x。同义词为constant
直流阻塞() 1 1 具有10 Hz截止频率的零中心信号。
dcblock_hz(f) 1 1 具有截止频率f的零中心信号。
declick() 1 1 对信号应用10 ms的淡入。
declick_s(t) 1 1 对信号应用t秒的淡入。
延迟(t) 1 1 t秒的延迟。延迟时间四舍五入到最接近的样本。
dsf_saw() 2(频率,粗糙度) 1 类似于锯齿波的离散求和公式振荡器。
dsf_saw_r(r) 1(频率) 1 具有0...1粗糙度的类似于锯齿波的离散求和公式振荡器。
dsf_square() 2(频率,粗糙度) 1 类似于方波的离散求和公式振荡器。
dsf_square_r(r) 1(频率) 1 具有0...1粗糙度的类似于方波的离散求和公式振荡器。
envelope(f) - f 具有标量或元组输出的时间变化控制f,例如:|t| exp(-t)。同义词为lfo
envelope2(f) 1(x) f 时间变化、输入相关的控制f具有标量或元组输出,例如:|t, x| exp(-t * x)。同义词为lfo2
envelope3(f) 2(x,y) f 时变、输入依赖的控制 f,具有标量或元组输出,例如 |t, x, y| y * exp(-t * x)。与 lfo3 同义。
envelope_in(f) f f 时变、输入依赖的控制 f,具有标量或元组输出,例如 |t, i: &Frame<f64, U1>| exp(-t * i[0])。与 lfo_in 同义。
fdn(x) x x 反馈延迟网络:使用扩散的 Hadamard 反馈将反馈电路 x(具有相等数量的输入和输出)包围。
fdn2(x,y) xy xy 反馈延迟网络:使用扩散的 Hadamard 反馈包围反馈电路 x(具有相等数量的输入和输出),并额外处理反馈回路 y。前馈路径不包括 y
feedback(x) x x 包围(单个样本)反馈电路 x(具有相等数量的输入和输出)。
feedback2(x,y) xy xy 包围(单个样本)反馈电路 x(具有相等数量的输入和输出),并额外处理额外的反馈回路 y。前馈路径不包括 y
fir(weights) 1 1 具有指定权重的 FIR 滤波器,例如 fir((0.5, 0.5))
fir3(增益) 1 1 从 Nyquist 频率处所需的 gain 计算出的对称 3 点 FIR。
flanger(fb,min_d,max_d,f) 1 1 具有反馈量 fb、最小延迟 min_d 秒、最大延迟 max_d 秒和延迟函数 f 的 Flanger 效果,例如 |t| lerp11(0.01, 0.02, sin_hz(0.1, t))
跟随(t) 1 1 具有半响应时间 t 秒的平滑滤波器。
hammond() 1(频率) 1 带限制的 Hammond 振荡器。强调前三个谐波。
hammond_hz(f) - 1 f Hz 的带限制的 Hammond 振荡器。强调前三个谐波。
高通() 3 (音频,频率,Q) 1 高通滤波器(二阶)。
highpass_hz(f,q) 1 1 具有截止频率 f Hz 和 Q q 的高通滤波器(二阶)。
highpass_q(q) 2 (音频,频率) 1 具有 Q q 的高通滤波器(二阶)。
高通(一阶)() 2 (音频,频率) 1 高通滤波器(一阶)。
highpole_hz(f) 1 1 具有截止频率 f Hz 的高通滤波器(一阶)。
高通 shelves() 4 (音频,频率,Q,增益) 1 具有可调幅度增益的高通滤波器(二阶)。
highshelf_hz(f,q,增益) 1 1 中心在 f Hz 的高通滤波器(二阶),具有 Q q 和幅度增益 gain
highshelf_q(q,增益) 2 (音频,频率) 1 二阶高 Shelf 滤波器,Q 值为 q,幅度增益为 gain
hold(v) 2(信号,频率) 1 采样保持组件,保持时间在 0...1 之间可变 v
hold_hz(f,v) 1 1 f Hz 处的采样保持组件,保持时间在 0...1 之间可变 v
impulse::<U>() - U U 通道脉冲;每个通道的第一个样本为 1,其余为零。
join::<U>() U 1 U 通道平均在一起。是 split 的逆操作。
lfo(f) - f 时间变化的控制 f,具有标量或元组输出,例如 |t| exp(-t)。与 envelope 同义。
lfo2(f) 1(x) f 时间变化的、输入相关的控制 f,具有标量或元组输出,例如 |t, x| exp(-t * x)。与 envelope2 同义。
lfo3(f) 2(x,y) f 时间变化的、输入相关的控制 f,具有标量或元组输出,例如 |t, x, y| y * exp(-t * x)。与 envelope3 同义。
lfo_in(f) f f 时间变化的、输入相关的控制 f,具有标量或元组输出,例如 |t, i: &Frame<f64, U1>| exp(-t * i[0])。与 envelope_in 同义。
limiter((a,r)) 1 1 具有攻击时间 a 秒和释放时间 r 秒的预览限制器。
limiter_stereo((a,r)) 2 2 具有攻击时间 a 秒和释放时间 r 秒的立体声预览限制器。
lorenz() 1(频率) 1 洛伦兹动力学系统 振荡器。
低通() 3 (音频,频率,Q) 1 低通滤波器(二阶)。
lowpass_hz(f,q) 1 1 具有截止频率 f Hz 和 Q 值 q 的低通滤波器(二阶)。
lowpass_q(q) 2 (音频,频率) 1 具有 Q 值 q 的低通滤波器(二阶)。
低通(一阶)() 2 (音频,频率) 1 1 极低通滤波器(一阶)。
lowpole_hz(f) 1 1 具有截止频率 f Hz 的 1 极低通滤波器(一阶)。
lowrez() 3 (音频,频率,Q) 1 谐振低通滤波器(二阶)。
lowrez_hz(f,q) 1 1 f Hz 为中心,谐振 q 在 0...1 之间的谐振低通滤波器(二阶)。
lowrez_q(q) 2 (音频,频率) 1 谐振 q 在 0...1 之间的谐振低通滤波器(二阶)。
低通 shelves() 4 (音频,频率,Q,增益) 1 可调节幅度增益的低通 Shelf 滤波器(二阶)。
lowshelf_hz(f,q,增益) 1 1 f Hz 为中心,具有 Q 值 q 和幅度增益 gain 的低通 Shelf 滤波器(二阶)。
lowshelf_q(q,增益) 2 (音频,频率) 1 具有 Q 值 q 和幅度增益 gain 的低通 Shelf 滤波器(二阶)。
map(f) f f 自由映射通道,例如:map(|i: &Frame<f64, U2>| max(i[0], i[1]))
(模式) 1 1 (米) 根据计量模式分析输入并输出摘要。
mls() - 1 白色MLS噪声源。
mls_bits(n) - 1 来自n位MLS序列(1 <= n <= 31)的白色MLS噪声源。
监控(&共享,模式) 1 1 通过分析通过的数据,将摘要存储到共享变量中的透传节点。
moog() 3 (音频,频率,Q) 1 Moog谐振低通滤波器(四阶)。
moog_hz(f,q) 1 1 截止频率为f、谐振为q的Moog谐振低通滤波器(四阶)。
moog_q(q) 2 (音频,频率) 1 谐振为q的Moog谐振低通滤波器(四阶)。
morph() 4(音频、频率、Q、形态) 1 具有形态输入在-1...1(-1 = 低通,0 = 谐振,1 = 高通)的形态滤波器。
morph_hz(f,q,morph) 1 1 具有中心频率f、Qq和形态morph在-1...1(-1 = 低通,0 = 谐振,1 = 高通)的形态滤波器。
mul(x) x x 将信号乘以常数x
multijoin::<M,N>() M*N M M通道的N分支平均到一个通道中。是multisplit的逆操作。
multipass::<U>() U U 通过多通道信号。
multisink::<U>() U - 消费多通道信号。
multisplit::<M,N>() M M*N M通道分割成N分支。
multitap::<N>(min_delay,max_delay) N + 1(音频,延迟...) 1 使用立方插值的抽头延迟线。抽头数为N
multitap_linear::<N>(min_delay,max_delay) N + 1(音频,延迟...) 1 使用线性插值的抽头延迟线。抽头数为N
multitick::<U>() U U 多通道单样本延迟。
multizero::<U>() - U 多通道零信号。
noise() - 1 白噪声源。同义词为white
峰谷() 3 (音频,频率,Q) 1 带阻滤波器(二阶)。
notch_hz(f,q) 1 1 f Hz为中心,Q为q的带阻滤波器(二阶)。
notch_q(q) 2 (音频,频率) 1 Q为q的带阻滤波器(二阶)。
organ() 1(频率) 1 带限管风波振荡器。
organ_hz(f) - 1 频率为f Hz的带限管风波振荡器。
oversample(node) node node 2倍过采样封闭的node
pan(pan) 1 2 固定单声道到立体声等功率声像器,声像在-1...1。
panner() 2(音频,声像) 2 单声道到立体声等功率声像器,声像在-1...1。
pass() 1 1 通过信号。
峰值() 3 (音频,频率,Q) 1 峰值滤波器(二阶)。
peak_hz(f,q) 1 1 f Hz为中心,Q为q的峰值滤波器(二阶)。
peak_q(q) 2 (音频,频率) 1 Q为q的峰值滤波器(二阶)。
phaser(fb,f) 1 1 具有反馈量 fb 和调制函数 f 的 Phaser 效果,例如:|t| sin_hz(0.1, t) * 0.5 + 0.5
粉色() - 1 粉红噪声源。
粉色带通() 1 1 粉色滤波器(3 dB/八度低通)。
管道(x,y) x y x 连接到 y。等同于 x >> y
管道i::<U,_, _>(f) f f 从索引生成器 f 连接 U 节点。
管道f::<U,_, _>(f) f f 从分数生成器 f 连接 U 节点。
拨弦(f,增益,阻尼) 1(激励) 1 Karplus-Strong 弦振荡器,频率为 f Hz,每秒增益 gaingain <= 1)和高频阻尼 0...1。
乘积(x,y) x+y x=y 乘节点 xy。等同于 x * y
脉冲() 2(频率,占空比) 1 带限脉冲波,占空比为 0...1。
重采样(node) 1(速度) node 使用从输入获得的速率重采样生成器 node,使用立方插值,其中 1 为原始速度。
谐振器() 3(音频,频率,带宽) 1 恒增益带通谐振器(二阶)。
resonator_hz(f,带宽) 1 1 具有中心频率 f Hz 和带宽 bw Hz 的恒增益带通谐振器(二阶)。
重合成::<I,O,_>(w,f) I O 使用窗口长度 w 和处理函数 f 的频域重合成。
reverb_stereo(r,t,d) 2 2 立体声混响(32通道 FDN),房间大小 r 米(10 为平均),混响时间 t 秒和高频阻尼 d(在 0...1)。
reverb2_stereo(r,t,d,m,f) 2 2 另一种立体声混响(32通道混合 FDN),房间大小 r 米(支持 10-30 米),混响时间 t 秒,扩散量 d(在 0...1),调制速度 m(标称范围 0...1,超出开始起作用),和循环滤波器 f
reverb3_stereo(t,d,f) 2 2 另一种立体声混响(全通环路),混响时间 t 秒,扩散量 d(在 0...1),和循环滤波器 f
reverse::<N>() N N 反转通道顺序,例如,交换左右通道。
rossler() 1(频率) 1 Rössler 动力系统 振荡器。
rotate(a,g) 2 2 以增益 g 旋转立体声信号 a 弧度。
saw() 1(频率) 1 带限锯齿波振荡器。
saw_hz(f) - 1 频率为 f Hz 的带限锯齿波振荡器。
shape(模式) 1 1 使用 waveshaper 模式 mode 形状信号。
shape_fn(f) 1 1 使用 waveshaper 函数 f 形状信号,例如,tanh
sine() 1(频率) 1 正弦振荡器。
sine_hz(f) - 1 频率为 f Hz 的正弦振荡器。
sink() 1 - 消耗信号。
soft_saw() 1(频率) 1 带限软锯齿波振荡器。
软锯齿波(f) - 1 带限软锯齿波振荡器,频率为 f Hz。
拆分::<U>() 1 U 将信号拆分为 U 通道。
方波() 1(频率) 1 带限方波振荡器。
方波_hz(f) - 1 频率为 f Hz 的带限方波振荡器。
堆栈(x,y) x+y x+y 堆叠 xy。与 x | y 相同。
堆叠i::<U,_, _>(f) U*f U*f 从索引生成器 f 中堆叠 U 节点。
堆叠f::<U,_, _>(f) U*f U*f 从分数生成器 f 中堆叠 U 节点,例如,| x | 延迟(xerp(0.1, 0.2, x))
(x) x x 从信号中减去常量 x
总和(x,y) x+y x=y 添加节点 xy。与 x + y 相同。
总和i::<U,_, _>(f) U*f f 从索引生成器 f 中求和 U 节点。
总和f::<U,_, _>(f) U*f f 从分数生成器 f 中求和 U 节点,例如,| x | 延迟(xerp(0.1, 0.2, x))
抽样(min_delay,max_delay) 2 (音频,延迟) 1 使用立方插值的三抽样延迟线。所有时间均以秒为单位。
抽样线性(min_delay,max_delay) 2 (音频,延迟) 1 使用线性插值的三抽样延迟线。所有时间均以秒为单位。
通过(x) x x 输入 通过缺失的输出。与 !x 相同。
滴答() 1 1 单样本延迟。
计时器(&共享) - - 在共享变量中保持当前流时间。
三角形() 1(频率) 1 带限三角形波振荡器。
三角形_hz(f) - 1 频率为 f Hz 的带限三角形波振荡器。
单元::<I,O>(单元) I O AudioUnit 转换为具有 I 个输入和 O 个输出的 AudioNode
更新(x,dt,f) x x 使用更新间隔 dt 秒和更新函数 f(t, dt, x) 更新节点 x
变量(&共享) - 1 共享变量的输出值。
var_fn(&共享,f) - f 通过函数 f 映射共享变量的输出值。
wavech(&wave,通道, 循环) - 1 播放 Arc<Wave> 的一个通道。可选循环点是到达波形末尾时跳转到的索引。
wavech_at(&wave,通道,开始,结束, 循环) - 1 在索引 start(包含)和 end(不包含)之间播放 Arc<Wave> 的一个通道,可选的 循环 索引在波形末尾跳转。
白噪声() - 1 白噪声 源。与 noise 同义。
() - 1 零信号。

子采样控制

envelope是一个采样时间变化控制函数的节点。例如,envelope(|t| exp(-t))是一个指数衰减的包络。控制函数是指预期相对缓慢变化的函数。因此,我们可以通过不在每个样本时调用它来节省时间。

函数的参数是以秒为单位的时间。每当节点重置时,时间重置为零。

envelope是泛型于通道算子的:函数的返回类型(标量或元组)决定了输出的数量。

样本间隔平均为2毫秒,由伪随机相位产生的噪声进行抖动。介于样本之间的值进行线性插值。

lfo(低频振荡器)是envelope的另一个名称。

索引和分数生成函数

branchibusipipeisumistacki是组合相同类型多个节点的操作码。节点的数量是它们的第一个泛型参数。它们接受一个生成函数,该函数从0开始发出u64整数。节点在行内分配,并且每个节点分配一个自己的伪随机相位。

例如,在1 kHz...2 kHz之间创建20个噪声频带

use fundsp::hacker::*;
let partials = busi::<U20, _, _>(|i| noise() >> resonator_hz(xerp(1_000.0, 2_000.0, rnd1(i) as f32), 20.0));

同样,branchfbusfpipefsumfstackf接受一个生成函数,该函数发出均匀分布在单位区间0...1中的值。第一个节点发出值0,最后一个节点发出值1。如果只有一个节点,则它接收值为0.5。

例如,在1 kHz...2 kHz之间均匀分配20个噪声频带

use fundsp::hacker::*;
let partials = busf::<U20, _, _>(|f| noise() >> resonator_hz(xerp(1_000.0, 2_000.0, f), 20.0));

波形变换模式

这些是shape操作码的参数。

  • Clip:将信号裁剪到-1...1。
  • ClipTo(minimum, maximum):将信号裁剪到两个参数之间。
  • Tanh(hardness):应用可配置硬度的tanh失真。将tanh的参数乘以硬度值。
  • Atan(hardness):应用可配置硬度的atan失真,输出范围缩放到-1...1。将atan的参数乘以硬度值。
  • Softsign(hardness):应用可配置硬度的softsign失真。将softsign的参数乘以硬度值。
  • Crush(levels):应用可配置每单位级别的阶梯函数。
  • SoftCrush(levels):应用可配置每单位级别的平滑阶梯函数。
  • AdaptiveTanh::new(timescale, hardness):应用自适应归一化失真,具有timescale秒的平滑。平滑时间尺度是级估计移动到新值一半所需的时间。tanh的参数乘以硬度值,然后除以信号的均方根电平。

测量模式

monitor(&shared, mode)操作码是一个透明节点,显示通过共享变量传递的数据的一些方面。测量模式包括

  • Meter::Sample:存储通过的最新值。
  • Meter::Peak(timescale):具有timescale秒平滑的峰值幅度计。
  • Meter::Rms(timescale):具有timescale秒平滑的均方根计。

平滑时间尺度是级估计移动到新值一半所需的时间。

meter操作码中使用相同的模式。


数学和实用函数


函数 说明
a_weight(f) A加权幅度响应在f Hz(在1 kHz处归一化为1.0)
abs(x) x的绝对值
amp_db(x) 将幅度(又称增益)x转换为分贝,以幅度1.0等于0 dB
bpm_hz(bpm) bpm BPM(每分钟节拍数)转换为Hz
ceil(x) 上限函数
clamp(min,max,x) x限制在minmax之间
clamp01(x) x限制在0和1之间
clamp11(x) x限制在-1和1之间
cos(x) cos
cos_hz(f,t) f Hz(在t秒时)的余弦波
cubed(x) x的三次方
db_amp(x) x dB转换为幅度(又称增益),以0 dB等于幅度1.0
delerp(x0,x1,x) 从插值值恢复0...1中的线性插值量t
delerp11(x0,x1,x) 从插值值中恢复-1...1中的线性插值量t
dexerp(x0,x1,x) 从插值值中恢复0...1中的指数插值量tx0x1x > 0)
dexerp11(x0,x1,x) 从插值值中恢复-1...1中的指数插值量tx0x1x > 0)
dissonance(f0,f1) f0f1 Hz的纯音之间0...1的不和谐程度
dissonance_max(f) f Hz以上的最大不和谐纯频率
downarc(x) 凹的四分之一圆缓和曲线(0...1中uparc的逆函数)
ease_noise(ease,种子,x) 在-1...1之间与缓和函数ease(例如,smooth3)插值的值噪声
ease_noise((rise,fall),种子,x) 在上升段使用缓和函数rise和下降段使用fall的-1...1之间的值噪声,例如,(uparc, downarc)
exp(x) exp
exp10(x) 10的x次方x
exp2(x) 2的x次方x
floor(x) floor函数
fract(x) fract函数
分形噪声(种子,octaves,粗糙度,x) 分形样条噪声(octaves > 0,roughness > 0)
fractal_ease_noise(ease,种子,octaves,粗糙度,x) 分形缓动噪声(octaves > 0,roughness > 0)通过缓动函数ease插值
hash1(x) u64哈希函数,是一个伪随机排列
hash2(x) u64哈希函数,是一个伪随机排列
identity(x) 恒等函数(线性缓动函数)
lerp(x0,x1,t) 在0...1范围内使用tx0x1进行线性插值
lerp11(x0,x1,t) 在-1...1范围内使用tx0x1进行线性插值
log(x) 自然对数
log10(x) 以10为底的对数
log2(x) 二进制对数
midi_hz(x) MIDI音符编号x转换为Hz(69.0 = A4 = 440 Hz)
min(x,y) xy的最小值
max(x,y) xy的最大值
m_weight(f) M-weightedf Hz处的幅度响应(在1 kHz处归一化到1.0)
pow(x,y) xy次方
rnd1(i) 从整数i生成的0...1范围内的伪随机数
rnd2(i) 从整数i生成的0...1范围内的伪随机数
round(x) x四舍五入到最接近的整数
semitone_ratio(x) 将间隔x的半音转换为频率比
signum(x) x的符号
sin(x) sin
sin_hz(f,t) 在时间t秒时以频率f Hz振动的正弦波
smooth3(x) 平滑三次缓动多项式
smooth5(x) 平滑五次缓动多项式(常用于计算机图形学)
smooth7(x) 平滑七次缓动多项式
smooth9(x) 平滑九次缓动多项式
softexp(x) exp的代数多项式替代品
softmix(x,y,bias) 根据bias加权平均xy:当bias < 0时,多项式软min,当bias = 0时,平均,当bias > 0时,多项式softmax
softsign(x) 软符号函数,tanh的多项式替代品
squared(x) x的平方
sqr_hz(f,t) 在时间t秒时以频率f Hz振动的方波(非带限)
sqrt(x) x的平方根
spline(x0,x1,x2,x3,t) Catmull-Rom三次插值在x1x2之间,考虑x0x3
spline_mono(x0,x1,x2,x3,t) 单调三次插值x1x2之间,考虑x0x3
spline_noise(种子,x) 在-1...1范围内使用三次样条插值值噪声,每个整数单元格一个插值点
tan(x) tan
双曲正切(x) 双曲正切函数
tri_hz(f,t) 在时间 t 秒时,频率 f Hz 的非带限三角波(f Hz 在时间 t 秒时振荡)
uparc(x) 凸四分之一圆缓和曲线(0...1 中的 downarc 的逆函数)
xerp(x0,x1,t) 在 0...1 的 t 中,在 x0x1 之间进行指数插值(x0x1 > 0)
xerp11(x0,x1,t) 在 -1...1 的 t 中,在 x0x1 之间进行指数插值(x0x1 > 0)

缓和函数

缓和函数 是将 0...1 范围重新映射的插值曲线。这些数学函数具有缓和函数的形状。

函数 说明
cubed(x) x的三次方
downarc(x) 凹的四分之一圆缓和曲线(0...1中uparc的逆函数)
identity(x) 恒等函数(线性缓动函数)
smooth3(x) 平滑三次缓动多项式
smooth5(x) 平滑五次缓动多项式(常用于计算机图形学)
smooth7(x) 平滑七次缓动多项式
smooth9(x) 平滑九次缓动多项式
squared(x) x的平方
sqrt(x) x的平方根
uparc(x) 凸四分之一圆缓和曲线(0...1 中的 downarc 的逆函数)


例如,如果我们想插值一个过渡,那么插值、反插值和重新插值结合使用缓和函数会很方便。此示例定义了一个用于纯音的无声抑制函数,从 20 kHz 降至默认的奈奎斯特频率 22.05 kHz 的 0。

use fundsp::hacker::*;
fn pure_tone_amp(hz: f32) -> f32 { lerp(1.0, 0.0, smooth5(clamp01(delerp(20_000.0, 22_050.0, hz)))) }

上面是一个重新插值的例子,即反插值后跟插值。首先,我们使用 delerp 和夹具恢复 0...1 中的插值值(这是反插值)。然后,我们使用 lerpsmooth5 缓和,在 1 到 0 的所需最终范围内平滑地插值该值。

对于指数插值和反插值,如常用于处理幅度或频率,请使用 xerpdexerp 函数。


噪声函数


示例

一些图表表达式的示例。


表达式 输入 输出 意义
pass() ^ pass() 1 2 单声道到立体声分割器
拆分::<U2>() 1 2 -..-
mul(0.5) + mul(0.5) 2 1 立体声到单声道的混音(单声道到立体声分割器的逆)
join::<U2>() 2 1 -..-
pass() ^ pass() ^ pass() 1 3 单声道到三声道的分割器
拆分::<U3>() 1 3 -..-
sink() | () 1 1 用静音替换信号
mul(0.0) 1 1 -..-
mul(db_amp(3.0)) 1 1 将信号放大 3 dB
sink() | pass() 2 1 提取右声道
pass() | sink() 2 1 提取左声道
sink() | () | pass() 2 2 用静音替换左声道
mul(0.0) | pass() 2 2 -..-
mul((0.0, 1.0)) 2 2 -..-
pass() | sink() | () 2 2 用静音替换右声道
pass() | mul(0.0) 2 2 -..-
mul((1.0, 0.0)) 2 2 -..-
(pass() & 滴答()) * 0.5 1 1 2 点平均 FIR 滤波器
fir((0.5, 0.5)) 1 1 -..-
fir(::([0.5, 0.5])) 1 1 -..-
!低通() >> 低通(一阶)() 2 1 串联的二阶和一极低通滤波器(三阶)
!低通() >> !低通() >> 低通() 2 1 串联的三重低通滤波器(六阶)
!谐振器() >> 谐振器() 3 1 串联的双共振器(四阶)
oversample(sine_hz(f) *f*m+f>> sine()) - 1 f Hz 的过采样 FM(频率调制) 振荡器,调制指数 m
sine() & mul(2.0) >> sine() 1 1 频率加倍的双正弦波振荡器
envelope(|t| exp(-t)) * noise() - 1 指数衰减的白噪声
lfo(|t| xerp11(0.25, 4.0, spline_noise(1,t))) >> 重采样(粉色()) 0 1 重新采样的粉红噪声。
feedback(延迟(1.0) * db_amp(-3.0)) 1 1 1 秒反馈延迟,衰减 3 dB
feedback((延迟(1.0) | 延迟(1.0)) >> reverse() * db_amp(-6.0)) 2 2 1 秒乒乓延迟,衰减 6 dB。
变量(&湿声) * 延迟(1.0) & (1.0 - 变量(&湿声)) * pass() 1 1 1 秒延迟,由共享变量 wet 控制的湿/干混合。该共享变量可以声明为 let wet = shared(0.5);
sine() & mul(semitone_ratio(4.0)) >> sine() & mul(semitone_ratio(7.0)) >> sine() 1 1 主和弦
dc(midi_hz(72.0)) >> sine() & dc(midi_hz(76.0)) >> sine() & dc(midi_hz(79.0)) >> sine() 0 1 C 主和弦生成器
!() 0 0 A 空节点。将其与另一个节点堆叠会微妙地改变其声音,因为哈希值已更改。
!-!!!--!!!-!!--!() 0 0 配有自定义哈希的热棒空节点。使用更多电力。

前奏曲中的示例

前奏曲本身中的许多函数都定义为图表表达式。


函数 输入 输出 定义
brown() - 1 白噪声() >> lowpole_hz(10.0) * constant(13.7)
mls() - 1 mls_bits(29)
粉色() - 1 白噪声() >> 粉色带通()
saw_hz(f) - 1 constant(f) >> saw()
sine_hz(f) - 1 constant(f) >> sine()
方波_hz(f) - 1 constant(f) >> 方波()
三角形_hz(f) - 1 constant(f) >> 三角形()
() - 1 constant(0.0)

等效表达式

通常有几种方式可以表达特定的图表。以下表达式对是相同的。


表达式 等同于 备注
(pass() ^ mul(2.0)) >> sine() + sine() sine() & mul(2.0) >> sine() 总线通常比显式分支后跟求和更方便。
--sink()-42.0^sink()&---sink()*3.14 sink() 分支、总线和求和操作在节点上是无操作的。
constant(0.0) | dc(1.0) constant((0.0, 1.0)) 堆叠连接通道。
sink() | () () | sink() 订单的顺序不重要,因为 sink() 只添加输入,而 zero() 只添加输出。
(低通() ^ (sink() | pass())) >> 低通() !低通() >> 低通() 手动启用旁路。
!sink() pass() 由于通带操作符会通过它传递缺失的输出,因此抑制器尝试消耗信号的尝试被挫败。
!(noise() | noise()) !noise() 通带操作符使任何生成器无效。

音频图类型

AudioNode 组件系统在类型级别上表示音频网络结构。

该表示包含输入和输出算术,这些算术由 typenum 包编码为类型 U0U1、...。关联类型是 AudioNode::InputsAudioNode::Outputs

前缀使用包含操作符重载和其他特质实现的包装类型 An<X: AudioNode>。包装器还实现了 AudioUnit 特质。

类型编码很简单。例如,在黑客前缀

noise() & constant(440.0) >> sine()

表示为

An<Bus<Noise, Pipe<Constant<U1>, Sine>>>

编译器以不可见的形式报告这些类型。一个附加的实用程序 examples/type.rs 可以解开这样的不可见类型。此调用打印上面的类型

cargo run --example type -- "fundsp::combinator::An<Bus<Noise, Pipe<Constant<typenum::uint::UInt<typenum::uint::UTerm,typenum::bit::B1>>, Sine>>>"

还可以将返回类型声明为 impl AudioNode。然而,此类型也是不可见的,不能作为 AudioNode 存储;在这种情况下,可能需要转换为 AudioUnit

在签名中声明完整算术确实可以启用在进一步组合中使用节点。例如

use fundsp::hacker::*;
fn split_quad() -> An<impl AudioNode<Inputs = U1, Outputs = U4>> {
    pass() ^ pass() ^ pass() ^ pass()
}

许可证

根据您的选择,在 Apache License, Version 2.0MIT license 下许可。

除非您明确声明,否则您提交给 FunDSP 的任何贡献(根据 Apache-2.0 许可证定义),都将如上所述双重许可,没有任何额外的条款或条件。

依赖关系

~14MB
~308K SLoC