sim_lib_stream_bridge/
model.rs1use sim_citizen_derive::Citizen;
2use sim_kernel::{Diagnostic, Error, Result, Symbol};
3use sim_lib_midi_core::DEFAULT_US_PER_QUARTER;
4use sim_lib_stream_core::StreamValue;
5
6pub struct BridgeOutput {
8 pub stream: StreamValue,
10 pub diagnostics: Vec<Diagnostic>,
12}
13
14#[derive(Clone, Debug, PartialEq, Citizen)]
26#[citizen(symbol = "stream-bridge/RenderOptions", version = 1)]
27pub struct StreamBridgeRenderOptions {
28 pub sample_rate: u32,
30 pub channels: u8,
32 pub chunk_frames: usize,
34}
35
36impl StreamBridgeRenderOptions {
37 pub fn new(sample_rate: u32, channels: u8, chunk_frames: usize) -> Result<Self> {
39 let options = Self {
40 sample_rate,
41 channels,
42 chunk_frames,
43 };
44 options.validate()?;
45 Ok(options)
46 }
47
48 pub fn validate(&self) -> Result<()> {
50 if self.sample_rate == 0 {
51 return Err(Error::Eval(
52 "stream/bridge render sample_rate must be greater than zero".to_owned(),
53 ));
54 }
55 if !(1..=2).contains(&self.channels) {
56 return Err(Error::Eval(
57 "stream/bridge render channels must be 1 or 2".to_owned(),
58 ));
59 }
60 if self.chunk_frames == 0 {
61 return Err(Error::Eval(
62 "stream/bridge render chunk_frames must be greater than zero".to_owned(),
63 ));
64 }
65 Ok(())
66 }
67}
68
69impl Default for StreamBridgeRenderOptions {
70 fn default() -> Self {
71 Self {
72 sample_rate: 48_000,
73 channels: 2,
74 chunk_frames: 512,
75 }
76 }
77}
78
79#[derive(Clone, Debug, PartialEq, Citizen)]
81#[citizen(symbol = "stream-bridge/LiftMidiOptions", version = 1)]
82pub struct StreamBridgeLiftMidiOptions {
83 pub sample_rate: u32,
85 pub tpq: u16,
87 pub us_per_quarter: u32,
89 pub min_confidence: f64,
91 pub window_size: usize,
93 pub hop_size: usize,
95 pub max_events_per_packet: usize,
97}
98
99impl StreamBridgeLiftMidiOptions {
100 pub fn validate(&self) -> Result<()> {
102 if self.sample_rate == 0 {
103 return Err(Error::Eval(
104 "stream/bridge lift-midi sample_rate must be greater than zero".to_owned(),
105 ));
106 }
107 if self.tpq == 0 {
108 return Err(Error::Eval(
109 "stream/bridge lift-midi tpq must be greater than zero".to_owned(),
110 ));
111 }
112 if self.us_per_quarter == 0 {
113 return Err(Error::Eval(
114 "stream/bridge lift-midi us_per_quarter must be greater than zero".to_owned(),
115 ));
116 }
117 if !self.min_confidence.is_finite() || !(0.0..=1.0).contains(&self.min_confidence) {
118 return Err(Error::Eval(
119 "stream/bridge lift-midi min_confidence must be between 0 and 1".to_owned(),
120 ));
121 }
122 if self.window_size == 0 {
123 return Err(Error::Eval(
124 "stream/bridge lift-midi window_size must be greater than zero".to_owned(),
125 ));
126 }
127 if self.hop_size == 0 {
128 return Err(Error::Eval(
129 "stream/bridge lift-midi hop_size must be greater than zero".to_owned(),
130 ));
131 }
132 if self.max_events_per_packet == 0 {
133 return Err(Error::Eval(
134 "stream/bridge lift-midi max_events_per_packet must be greater than zero"
135 .to_owned(),
136 ));
137 }
138 Ok(())
139 }
140}
141
142impl Default for StreamBridgeLiftMidiOptions {
143 fn default() -> Self {
144 Self {
145 sample_rate: 48_000,
146 tpq: 480,
147 us_per_quarter: DEFAULT_US_PER_QUARTER,
148 min_confidence: 0.75,
149 window_size: 2048,
150 hop_size: 512,
151 max_events_per_packet: 64,
152 }
153 }
154}
155
156pub fn stream_bridge_symbol() -> Symbol {
165 Symbol::qualified("stream", "bridge")
166}
167
168pub fn stream_bridge_render_options_class_symbol() -> Symbol {
170 Symbol::qualified("stream-bridge", "RenderOptions")
171}
172
173pub fn stream_bridge_lift_midi_options_class_symbol() -> Symbol {
175 Symbol::qualified("stream-bridge", "LiftMidiOptions")
176}