sim_lib_plugin_core/
cookbook.rs1use sim_kernel::{Expr, NumberLiteral, Symbol};
4use sim_lib_audio_graph_core::{PortDir, PortMedia};
5
6use crate::{ParameterDescriptor, PluginDescriptor, PluginFormat};
7
8pub fn gain_plugin_demo() -> Expr {
10 let descriptor =
11 PluginDescriptor::audio_effect(PluginFormat::Sim, "org.sim.gain", "SIM Gain", 2)
12 .expect("valid gain plugin descriptor")
13 .with_parameter(
14 ParameterDescriptor::new(0, "gain", "Gain", 0.0, 2.0, 1.0)
15 .expect("valid gain parameter"),
16 );
17 plugin_descriptor_expr(&descriptor)
18}
19
20fn plugin_descriptor_expr(descriptor: &PluginDescriptor) -> Expr {
21 Expr::Map(vec![
22 (field("kind"), sym("plugin-core", "descriptor")),
23 (
24 field("format"),
25 sym("plugin-format", descriptor.id.format.as_str()),
26 ),
27 (field("id"), Expr::String(descriptor.id.stable_id.clone())),
28 (field("name"), Expr::String(descriptor.name.clone())),
29 (
30 field("ports"),
31 Expr::Vector(descriptor.ports.iter().map(port_expr).collect()),
32 ),
33 (
34 field("parameters"),
35 Expr::Vector(
36 descriptor
37 .parameters
38 .iter()
39 .map(|parameter| {
40 Expr::Map(vec![
41 (field("id"), number(parameter.id)),
42 (
43 field("stable-id"),
44 Expr::String(parameter.stable_id.clone()),
45 ),
46 (field("default"), number_f64(parameter.default)),
47 ])
48 })
49 .collect(),
50 ),
51 ),
52 ])
53}
54
55fn port_expr(port: &sim_lib_audio_graph_core::PortDecl) -> Expr {
56 Expr::Map(vec![
57 (field("name"), Expr::String(port.name.clone())),
58 (field("media"), sym("audio-port", port_media(port.media))),
59 (field("dir"), sym("audio-port", port_dir(port.dir))),
60 (field("channels"), number(port.channels)),
61 ])
62}
63
64fn port_media(media: PortMedia) -> &'static str {
65 match media {
66 PortMedia::Audio => "audio",
67 PortMedia::Control => "control",
68 PortMedia::Event => "event",
69 }
70}
71
72fn port_dir(dir: PortDir) -> &'static str {
73 match dir {
74 PortDir::In => "in",
75 PortDir::Out => "out",
76 }
77}
78
79fn field(name: &str) -> Expr {
80 Expr::Symbol(Symbol::qualified("plugin-core", name))
81}
82
83fn sym(namespace: &str, name: &str) -> Expr {
84 Expr::Symbol(Symbol::qualified(namespace, name))
85}
86
87fn number(value: impl ToString) -> Expr {
88 Expr::Number(NumberLiteral {
89 domain: Symbol::qualified("numbers", "i64"),
90 canonical: value.to_string(),
91 })
92}
93
94fn number_f64(value: f64) -> Expr {
95 Expr::Number(NumberLiteral {
96 domain: Symbol::qualified("numbers", "f64"),
97 canonical: value.to_string(),
98 })
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn gain_plugin_demo_carries_stereo_ports_and_gain_parameter() {
107 let Expr::Map(entries) = gain_plugin_demo() else {
108 panic!("gain plugin demo is a map")
109 };
110 let rendered = format!("{entries:?}");
111 assert!(rendered.contains("audio-in"));
112 assert!(rendered.contains("audio-out"));
113 assert!(rendered.contains("gain"));
114 }
115}