#jit #dsp #real-time #audio-processing #synthesis #jit-compiler

synfx-dsp-jit

DSP JIT(即时编译)引擎,用于Rust的实时音频合成和效果

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

GPL-3.0-or-later

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 DataSuperCollider 克隆。当然,如果您投入了实现所有DSP节点并在JIT之上放置编译器的精力。

该库被例如 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