galdr 0.1.0: terminal grid synthesizer (MIDI in, PipeWire out)
Release / build (x86_64, ubuntu-latest, /sccache, x86_64-unknown-linux-gnu) (push) Successful in 2m3s
Release / build (aarch64, aarch64, /var/lib/gitea-runner/sccache, aarch64-unknown-linux-gnu) (push) Successful in 2m46s
Release / update-aur (push) Successful in 23s

Signal flows downward through single-glyph unit generators; each MIDI note
voices the whole grid; the mix renders low-latency to PipeWire (reusing the
native path from spectrust). Grid edits recompile and hot-swap without
dropping audio. Pairs with orca (note/CC source) and spectrust (visualizer).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GLUwWE2KmFPzhKaf67tWbx
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-13 13:24:18 +02:00
co-authored by Claude Opus 4.8
commit d2778e169a
15 changed files with 2467 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
//! MIDI input. galdr owns a virtual input port named "galdr" (symmetric with
//! orca's virtual output), so orca or any client connects to it in the graph.
use anyhow::{anyhow, Result};
use midir::os::unix::VirtualInput;
use midir::{MidiInput, MidiInputConnection};
use rtrb::Producer;
use crate::engine::Event;
pub fn connect(prod: Producer<Event>, port_name: Option<String>) -> Result<MidiInputConnection<Producer<Event>>> {
let mut input = MidiInput::new("galdr").map_err(|e| anyhow!("MIDI init: {e}"))?;
input.ignore(midir::Ignore::None);
let cb = move |_stamp: u64, msg: &[u8], prod: &mut Producer<Event>| {
if msg.len() < 2 {
return;
}
let status = msg[0] & 0xF0;
let ev = match status {
0x90 if msg[2] > 0 => Event::NoteOn { note: msg[1], vel: msg[2] },
0x80 => Event::NoteOff { note: msg[1] },
0x90 => Event::NoteOff { note: msg[1] }, // note-on velocity 0
0xB0 => Event::Cc { idx: msg[1], val: msg[2] },
_ => return,
};
let _ = prod.push(ev);
};
if let Some(name) = port_name {
let ports = input.ports();
let port = ports
.iter()
.find(|p| input.port_name(p).map(|n| n.contains(&name)).unwrap_or(false))
.cloned()
.ok_or_else(|| anyhow!("no MIDI input port matching {name:?}"))?;
input.connect(&port, "galdr-in", cb, prod).map_err(|e| anyhow!("MIDI connect: {e}"))
} else {
input.create_virtual("galdr", cb, prod).map_err(|e| anyhow!("create virtual MIDI port: {e}"))
}
}