diff options
| -rw-r--r-- | Cargo.lock | 27 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | crates/renderer/Cargo.toml | 4 | ||||
| -rw-r--r-- | crates/renderer/src/main.rs | 1 | ||||
| -rw-r--r-- | crates/renderer/src/overlay.rs | 1 | ||||
| -rw-r--r-- | crates/renderer/src/overlay/automata.rs | 1 | ||||
| -rw-r--r-- | crates/renderer/src/overlay/loopback.rs | 87 | ||||
| -rw-r--r-- | flake.nix | 5 | ||||
| -rwxr-xr-x | launch.sh | 8 |
9 files changed, 131 insertions, 4 deletions
@@ -2050,6 +2050,7 @@ dependencies = [ "bitflags 2.8.0", "byteorder", "clap", + "cpal", "device_query", "env_logger", "glam", @@ -2061,6 +2062,7 @@ dependencies = [ "newton_shader", "polling", "rand", + "redis", "strum", "teleia", "termion", @@ -2660,6 +2662,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be105c72a1e6a5a1198acee3d5b506a15676b74a02ecd78060042a447f408d94" [[package]] +name = "redis" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f0f6a8c53351d89a3869a703459995a0bcadcfa846002707fbc7e5cca235c4a" +dependencies = [ + "combine", + "itoa", + "num-bigint", + "percent-encoding", + "ryu", + "sha1_smol", + "socket2", + "url", +] + +[[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3024,6 +3042,12 @@ dependencies = [ ] [[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + +[[package]] name = "sharded-slab" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3361,10 +3385,11 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "teleia" version = "0.1.0" +source = "git+https://github.com/lcolonq/teleia#47ce5c2a8583f91711bdb4c69cd50021e6737595" dependencies = [ "bimap", "bincode", - "bitflags 1.3.2", + "bitflags 2.8.0", "byteorder", "bytes", "color-eyre", @@ -18,4 +18,5 @@ strip = true # debug = "full" [profile.dev.package."*"] +debug = 0 opt-level = 2 diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml index e66e198..6bf77a7 100644 --- a/crates/renderer/Cargo.toml +++ b/crates/renderer/Cargo.toml @@ -26,4 +26,6 @@ polling = "*" # polling sockets termion = "*" # terminal escapes device_query = "*" # get pressed keys when unfocused byteorder = "*" # read little-endian numbers -image = "*" # read and write image files
\ No newline at end of file +image = "*" # read and write image files +cpal = "*" # record microphone +redis = "*" # database
\ No newline at end of file diff --git a/crates/renderer/src/main.rs b/crates/renderer/src/main.rs index 1e16092..b6103fc 100644 --- a/crates/renderer/src/main.rs +++ b/crates/renderer/src/main.rs @@ -32,6 +32,7 @@ pub fn main() -> Erm<()> { Box::new(overlay::drawing::Overlay::new(ctx)), Box::new(overlay::irish::Overlay::new(ctx)), // Box::new(overlay::model::Overlay::new(ctx)), + Box::new(overlay::loopback::Overlay::new(ctx)), ]) })?; }, diff --git a/crates/renderer/src/overlay.rs b/crates/renderer/src/overlay.rs index 9d1f722..38e75c5 100644 --- a/crates/renderer/src/overlay.rs +++ b/crates/renderer/src/overlay.rs @@ -3,6 +3,7 @@ pub mod shader; pub mod drawing; pub mod automata; pub mod irish; +pub mod loopback; use teleia::*; diff --git a/crates/renderer/src/overlay/automata.rs b/crates/renderer/src/overlay/automata.rs index 22f7884..fbbc138 100644 --- a/crates/renderer/src/overlay/automata.rs +++ b/crates/renderer/src/overlay/automata.rs @@ -115,6 +115,7 @@ impl CellBuffer { pub fn get(&self, x: i32, y: i32) -> Cell { self.buf[Self::idx(x, y)] } + pub fn neighbors(&self, x: i32, y: i32) -> [Cell; 8] { [ self.get(x-1, y-1), self.get(x, y-1), diff --git a/crates/renderer/src/overlay/loopback.rs b/crates/renderer/src/overlay/loopback.rs new file mode 100644 index 0000000..b026eb3 --- /dev/null +++ b/crates/renderer/src/overlay/loopback.rs @@ -0,0 +1,87 @@ +use redis::Commands; +use teleia::*; +use std::{io::{Read, Write}, process}; +use byteorder::WriteBytesExt; +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; + +use crate::overlay; + +fn ffmpeg_to_adts(sample_rate: u32, samples: &[f32]) -> Option<Vec<u8>> { + let proc = process::Command::new("ffmpeg") + .args([ + "-f", "f32le", + "-ar", &format!("{sample_rate}"), + "-ac", "2", + "-i", "pipe:0", + "-vn", + "-c:a", "aac", + "-f", "mpegts", + "pipe:1" + ]) + .stdin(process::Stdio::piped()) + .stdout(process::Stdio::piped()) + .stderr(process::Stdio::null()) + .spawn().ok()?; + { + let mut inp = proc.stdin?; + for s in samples { + inp.write_f32::<byteorder::LE>(*s).ok()?; + } + inp.flush().ok()?; + } + let mut out = proc.stdout?; + let mut ret = Vec::new(); + out.read_to_end(&mut ret).ok()?; + Some(ret) +} + +fn upload_sample(conn: &mut redis::Connection, sequence: u32, sample_rate: u32, sample: &[f32]) { + let max: f32 = *sample.iter().max_by(|x, y| f32::total_cmp(x, y)).unwrap(); + let cells = (max / 0.1) as usize; + let adts = ffmpeg_to_adts(sample_rate, sample).unwrap(); + println!("{} {} {}", sample.len(), adts.len(), "#".repeat(cells)); + let _: () = conn.lpush("hlssamples", adts).unwrap(); + let _: () = conn.ltrim("hlssamples", 0, 10).unwrap(); + let _: () = conn.set("hlssequence", sequence).unwrap(); +} + +pub struct Overlay { + stream: cpal::Stream, +} + +impl Overlay { + pub fn new(ctx: &context::Context) -> Self { + let redis = redis::Client::open("redis://shiro").unwrap(); + let mut redis_conn = redis.get_connection().unwrap(); + let host = cpal::default_host(); + let device = host.default_input_device().unwrap(); + let config = device.default_input_config().unwrap(); + let sample_rate = config.sample_rate().0; + let mut buf: Vec<f32> = Vec::new(); + let mut sequence = 0; + let _: () = redis_conn.del("hlssamples").unwrap(); + let _: () = redis_conn.set("hlssequence", 0).unwrap(); + let stream = device.build_input_stream( + &config.into(), + move |samples: &[f32], info| { + buf.extend_from_slice(samples); + let upload_size = (3 * 2 * sample_rate) as usize; + if buf.len() > upload_size { + upload_sample(&mut redis_conn, sequence, sample_rate, &buf[0..upload_size]); + buf.drain(0..upload_size); + sequence += 1; + } + }, + |err| { + println!("error: {}", err); + }, + None, + ).unwrap(); + stream.play().unwrap(); + Self { + stream, + } + } +} + +impl overlay::Overlay for Overlay {} @@ -44,6 +44,9 @@ inherit native wasm; st = inputs.st.packages.x86_64-linux.st; }; - devShells.${system}.default = inputs.teleia.shell; + devShells.${system}.default = inputs.teleia.shell.overrideAttrs (final: prev: { + buildInputs = prev.buildInputs ++ [ + ]; + }); }; } @@ -1,2 +1,8 @@ #!/bin/sh -./target/debug/newton_renderer overlay +set -xu +pactl load-module module-null-sink sink_name=lcolonq-hls +./target/debug/newton_renderer overlay & +sleep 3 +pw-link lcolonq-hls:monitor_FL alsa_capture.newton_renderer:input_FL +pw-link lcolonq-hls:monitor_FR alsa_capture.newton_renderer:input_FR +wait |
