devalang_wasm/engine/audio/
midi_native.rs1use crate::engine::events::EventRegistry;
8use crate::language::syntax::ast::Value;
9#[cfg(feature = "cli")]
10use midir::{MidiInput, MidiInputConnection, MidiOutput, MidiOutputConnection};
11use std::collections::HashMap;
12#[cfg(feature = "cli")]
13use std::sync::{Arc, Mutex};
14
15#[cfg(feature = "cli")]
16pub struct MidiManager {
17 in_connections: Vec<MidiInputConnection<()>>,
19 out_connections: HashMap<String, MidiOutputConnection>,
20 registry: Arc<Mutex<EventRegistry>>,
21 start: Arc<std::time::Instant>,
22}
23
24#[cfg(feature = "cli")]
25impl MidiManager {
26 pub fn new(registry: Arc<Mutex<EventRegistry>>) -> Self {
27 MidiManager {
28 in_connections: Vec::new(),
29 out_connections: HashMap::new(),
30 registry,
31 start: Arc::new(std::time::Instant::now()),
32 }
33 }
34
35 pub fn list_input_ports() -> Vec<String> {
36 if let Ok(midi_in) = MidiInput::new("devalang-in") {
37 let ports = midi_in.ports();
38 ports
39 .iter()
40 .map(|p| {
41 midi_in
42 .port_name(p)
43 .unwrap_or_else(|_| "unknown".to_string())
44 })
45 .collect()
46 } else {
47 Vec::new()
48 }
49 }
50
51 pub fn open_input_by_index(&mut self, index: usize, name: &str) -> Result<(), String> {
52 let midi_in = MidiInput::new("devalang-in").map_err(|e| format!("midi_in: {}", e))?;
53 let ports = midi_in.ports();
55 if index >= ports.len() {
56 return Err("port index out of range".to_string());
57 }
58 let port = ports[index].clone();
59 let registry = self.registry.clone();
60 let device_name = name.to_string();
61 let device_name_inner = device_name.clone();
62 let start = self.start.clone();
63 let conn_in = midi_in
64 .connect(
65 &port,
66 &device_name,
67 move |_stamp, message, _| {
68 if message[0] != 248 {
70 let status = message[0];
71 let cmd = status & 0xF0;
72 let channel = (status & 0x0F) as u8;
73 match cmd {
74 0x90 => {
75 let note = message[1] as u8;
76 let vel = message[2] as u8;
77 let mut data = HashMap::new();
78 data.insert("note".to_string(), Value::Number(note as f32));
79 data.insert("velocity".to_string(), Value::Number(vel as f32));
80 data.insert("channel".to_string(), Value::Number(channel as f32));
81 let event_name = format!("mapping.in.{}.noteOn", device_name_inner);
83 let elapsed = std::time::Instant::now().duration_since(*start);
84 let ts = elapsed.as_secs_f32();
85 if let Ok(mut reg) = registry.lock() {
86 reg.emit(event_name, data, ts);
87 }
88 }
89 0x80 => {
90 let note = message[1] as u8;
91 let vel = message[2] as u8;
92 let mut data = HashMap::new();
93 data.insert("note".to_string(), Value::Number(note as f32));
94 data.insert("velocity".to_string(), Value::Number(vel as f32));
95 data.insert("channel".to_string(), Value::Number(channel as f32));
96 let event_name =
97 format!("mapping.in.{}.noteOff", device_name_inner);
98 let elapsed = std::time::Instant::now().duration_since(*start);
99 let ts = elapsed.as_secs_f32();
100 if let Ok(mut reg) = registry.lock() {
101 reg.emit(event_name, data, ts);
102 }
103 }
104 _ => {}
105 }
106 }
107 },
108 (),
109 )
110 .map_err(|e| format!("connect error: {}", e))?;
111
112 self.in_connections.push(conn_in);
113 Ok(())
114 }
115
116 pub fn open_output_by_name(&mut self, name: &str, index: usize) -> Result<(), String> {
117 let midi_out = MidiOutput::new("devalang-out").map_err(|e| format!("midi_out: {}", e))?;
118 let ports = midi_out.ports();
119 if index >= ports.len() {
120 return Err("port index out of range".to_string());
121 }
122 let port = &ports[index];
123 let _port_name = midi_out
124 .port_name(port)
125 .unwrap_or_else(|_| "out".to_string());
126 let conn_out = midi_out
127 .connect(port, name)
128 .map_err(|e| format!("connect out: {}", e))?;
129 self.out_connections.insert(name.to_string(), conn_out);
130 Ok(())
131 }
132
133 pub fn send_note_on(
134 &mut self,
135 device_name: &str,
136 channel: u8,
137 note: u8,
138 vel: u8,
139 ) -> Result<(), String> {
140 if let Some(conn) = self.out_connections.get_mut(device_name) {
141 let status = 0x90 | (channel & 0x0F);
142 let _ = conn.send(&[status, note, vel]);
143 Ok(())
144 } else {
145 Err("output connection not found".to_string())
146 }
147 }
148}
149
150#[cfg(not(feature = "cli"))]
151pub struct MidiManager;
152
153#[cfg(not(feature = "cli"))]
154impl MidiManager {
155 pub fn new(_registry: Arc<Mutex<EventRegistry>>) -> Self {
156 MidiManager
157 }
158 pub fn list_input_ports() -> Vec<String> {
159 Vec::new()
160 }
161 pub fn open_input_by_index(&mut self, _index: usize, _name: &str) -> Result<(), String> {
162 Ok(())
163 }
164 pub fn open_output_by_name(&mut self, _name: &str, _index: usize) -> Result<(), String> {
165 Ok(())
166 }
167 pub fn send_note_on(
168 &mut self,
169 _device_name: &str,
170 _channel: u8,
171 _note: u8,
172 _vel: u8,
173 ) -> Result<(), String> {
174 Ok(())
175 }
176}