Skip to main content

renderbox_sdk/ops/
source.rs

1use std::cell::RefCell;
2use std::rc::Rc;
3
4use renderbox_dsl::OpNode;
5
6use crate::ops::params;
7use crate::sorts::{Audio, Video};
8use crate::graph::{GraphBuilder, SharedGraph, Stream};
9
10fn new_graph() -> SharedGraph {
11    Rc::new(RefCell::new(GraphBuilder::new_internal()))
12}
13
14pub fn color(color: &str, duration: f64, w: u32, h: u32) -> Stream<Video> {
15    let graph = new_graph();
16    let node_id = graph.borrow_mut().add_node(OpNode::VideoSource {
17        name: "color".into(),
18        params: params! { "color" => color, "d" => duration, "s" => format!("{w}x{h}").as_str() },
19    });
20    Stream::new(node_id, graph)
21}
22
23pub fn testsrc2(duration: f64, w: u32, h: u32) -> Stream<Video> {
24    let graph = new_graph();
25    let node_id = graph.borrow_mut().add_node(OpNode::VideoSource {
26        name: "testsrc2".into(),
27        params: params! { "d" => duration, "s" => format!("{w}x{h}").as_str() },
28    });
29    Stream::new(node_id, graph)
30}
31
32pub fn smptebars(duration: f64, w: u32, h: u32) -> Stream<Video> {
33    let graph = new_graph();
34    let node_id = graph.borrow_mut().add_node(OpNode::VideoSource {
35        name: "smptebars".into(),
36        params: params! { "d" => duration, "s" => format!("{w}x{h}").as_str() },
37    });
38    Stream::new(node_id, graph)
39}
40
41pub fn sine(freq: f64, duration: f64) -> Stream<Audio> {
42    let graph = new_graph();
43    let node_id = graph.borrow_mut().add_node(OpNode::AudioSource {
44        name: "sine".into(),
45        params: params! { "f" => freq, "d" => duration },
46    });
47    Stream::new(node_id, graph)
48}
49
50pub fn anullsrc(duration: f64) -> Stream<Audio> {
51    let graph = new_graph();
52    let node_id = graph.borrow_mut().add_node(OpNode::AudioSource {
53        name: "anullsrc".into(),
54        params: params! { "d" => duration },
55    });
56    Stream::new(node_id, graph)
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use renderbox_dsl::ParamValue;
63
64    #[test]
65    fn color_creates_video_source() {
66        let v = color("red", 5.0, 1920, 1080);
67        let graph = v.graph.borrow();
68        match graph.arena.get(v.node_id) {
69            OpNode::VideoSource { name, params, .. } => {
70                assert_eq!(name, "color");
71                assert_eq!(params["color"], ParamValue::String("red".into()));
72                assert_eq!(params["d"], ParamValue::Number(5.0));
73                assert_eq!(params["s"], ParamValue::String("1920x1080".into()));
74            }
75            other => panic!("expected VideoSource, got {:?}", other),
76        }
77    }
78
79    #[test]
80    fn sine_creates_audio_source() {
81        let a = sine(440.0, 3.0);
82        let graph = a.graph.borrow();
83        match graph.arena.get(a.node_id) {
84            OpNode::AudioSource { name, params, .. } => {
85                assert_eq!(name, "sine");
86                assert_eq!(params["f"], ParamValue::Number(440.0));
87            }
88            other => panic!("expected AudioSource, got {:?}", other),
89        }
90    }
91
92    #[test]
93    fn source_into_pipeline() {
94        let v = color("black", 5.0, 1920, 1080);
95        let a = sine(440.0, 5.0);
96        // Sources create separate graphs — for multi-source pipelines
97        // we'd need graph merging (future work)
98        // For now, verify each produces valid nodes
99        assert_eq!(v.graph.borrow().arena.len(), 1);
100        assert_eq!(a.graph.borrow().arena.len(), 1);
101    }
102}