7个版本
0.6.2 | 2024年1月4日 |
---|---|
0.6.1 | 2022年11月6日 |
0.6.0 | 2022年8月28日 |
0.5.3 | 2022年8月5日 |
0.5.2 | 2022年7月31日 |
#254 in 音频
24 每月下载量
用于 hexodsp
150KB
2.5K SLoC
synfx-dsp-jit
synfx-dsp-jit 是Rust数字(音频)信号处理的专用即时编译器。
此库允许您将简化的抽象语法树(AST)编译成机器代码。此箱子使用 Cranelift JIT编译器 来执行此任务。对于从JIT代码中调用的Rust函数,无论是形式为有状态DSP节点还是无状态DSP函数,它都消除了任何动态调用的开销。
结果被方便地打包在 [DSPFunction] 结构中。
此库覆盖的一个主要特性是状态管理,这些状态管理节点/组件可以从AST中调用。通过为调用有状态组件(即节点)的AST节点附加一个唯一的ID,此库跟踪已初始化的节点。它这样做是为了允许您重新编译 [DSPFunction],并做出更改而不会中断音频(除非您的更改中断了它)。
除了编译过程和状态管理之外,此库还提供了一套(不断增长)的标准DSP算法库。
所有这些意味着此库主要面向实时合成环境中的使用案例。
您实际上可以使用此库构建自己的即时编译 Pure Data 或 SuperCollider 克隆。当然,如果您投入了实现所有DSP节点并在JIT之上放置编译器的精力。
- BitWig的 "The Grid",这似乎在底层使用LLVM,无论是用于AOT编译设备还是甚至JIT编译Grid本身(我不确定这一点)。
- Gammou - 多声部模块化音序合成器
该库被例如 HexoDSP 所使用,这是一个用于在Rust中开发模块化合成器的全面DSP图和合成库,如 HexoSynth。它不是HexoDSP的核心,但只提供了一小部分可选部分。
快速入门API
为了快速入门并学习如何使用API,我建议使用[instant_compile_ast]函数。但请注意,该函数在每次编译时都会重新创建整个[DSPNodeContext],因此没有为您跟踪状态。
use synfx_dsp_jit::build::*;
use synfx_dsp_jit::instant_compile_ast;
let (ctx, mut fun) = instant_compile_ast(
op_add(literal(11.0), var("in1"))
).expect("No compile error");
fun.init(44100.0, None); // Sample rate and optional previous DSPFunction
let (sig1, sig2, res) = fun.exec_2in_2out(31.0, 10.0);
// The result should be 11.0 + 31.0 == 42.0
assert!((res - 42.0).abs() < 0.0001);
// Yes, unfortunately you need to explicitly free this.
// Because DSPFunction might be handed around to other threads.
ctx.borrow_mut().free();
DSP JIT API示例
以下是一个更详细的示例,说明如何使用API进行状态跟踪。
use synfx_dsp_jit::*;
use synfx_dsp_jit::build::*;
// First we need to get a standard library with callable primitives/nodes:
let lib = get_standard_library();
// Then we create a DSPNodeContext to track newly created stateful nodes.
// You need to preserve this context across multiple calls to JIT::new() and JIT::compile().
let ctx = DSPNodeContext::new_ref();
// Create a new JIT compiler instance for compiling. Yes, you need to create a new one
// for each time you compile a DSPFunction.
let jit = JIT::new(lib.clone(), ctx.clone());
// This example shows how to use persistent variables (starting with '*')
// to build a simple phase increment oscillator
let ast = stmts(&[
assign("*phase", op_add(var("*phase"), op_mul(literal(440.0), var("israte")))),
_if(
op_gt(var("*phase"), literal(1.0)),
assign("*phase", op_sub(var("*phase"), literal(1.0))),
None,
),
var("*phase"),
]);
let mut dsp_fun = jit.compile(ASTFun::new(ast)).expect("No compile error");
// Initialize the function after compiling. For proper state tracking
// you will need to provide the previous DSPFunction as second argument to `init` here:
dsp_fun.init(44100.0, None);
// Create some audio samples:
let mut out = vec![];
for i in 0..200 {
let (_, _, ret) = dsp_fun.exec_2in_2out(0.0, 0.0);
if i % 49 == 0 {
out.push(ret);
}
}
// Just to show that this phase clock works:
assert!((out[0] - 0.0099).abs() < 0.0001);
assert!((out[1] - 0.4988).abs() < 0.0001);
assert!((out[2] - 0.9877).abs() < 0.0001);
assert!((out[3] - 0.4766).abs() < 0.0001);
ctx.borrow_mut().free();
DSP引擎API
synfx_dsp_jit::engine::CodeEngine API是一个处理音频/实时线程的便利API。当您想在非实时线程(如GUI或工作线程)上编译函数,并在音频线程中使用生成的DSP函数产生音频样本时,可以使用此API。
use synfx_dsp_jit::engine::CodeEngine;
use synfx_dsp_jit::build::*;
// Create an engine:
let mut engine = CodeEngine::new_stdlib();
// Retrieve the backend:
let mut backend = engine.get_backend();
// This should actually be in some audio thread:
std::thread::spawn(move || {
backend.set_sample_rate(44100.0);
loop {
backend.process_updates(); // Receive updates from the frontend
// Generate some audio samples here:
for frame in 0..64 {
let (s1, s2, ret) = backend.process(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
}
}
});
// Upload a new piece of code whenever you see fit:
engine.upload(call("sin", 1, &[literal(1.0)])).unwrap();
let mut not_done = true;
while not_done {
// Call this regularily!!!!
engine.query_returns();
// Just for ending this example:
not_done = false;
}
许可证:GPL-3.0-or-later
依赖项
~9-20MB
~266K SLoC