diff options
author | metamuffin <metamuffin@disroot.org> | 2025-04-02 00:12:48 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-04-02 00:12:48 +0200 |
commit | 0efb182072cc6bb564e2e278069d620bc13be600 (patch) | |
tree | 9114aef3a75194fe99232ccb7d753a1efc56722a | |
parent | 6ae973130eca4482e7048e55cd0aeb447533c296 (diff) | |
download | oscosu-0efb182072cc6bb564e2e278069d620bc13be600.tar oscosu-0efb182072cc6bb564e2e278069d620bc13be600.tar.bz2 oscosu-0efb182072cc6bb564e2e278069d620bc13be600.tar.zst |
oscosu
-rw-r--r-- | Cargo.lock | 43 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/main.rs | 169 |
3 files changed, 192 insertions, 22 deletions
@@ -214,6 +214,12 @@ dependencies = [ ] [[package]] +name = "glam" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3aa70d918d2b234126ff4f850f628f172542bf0603ded26b8ee36e5e22d5f9" + +[[package]] name = "glob" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -287,6 +293,12 @@ dependencies = [ ] [[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] name = "libc" version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -443,6 +455,8 @@ name = "oscosu" version = "0.1.0" dependencies = [ "cpal", + "glam", + "sdl2", ] [[package]] @@ -535,6 +549,29 @@ dependencies = [ ] [[package]] +name = "sdl2" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380" +dependencies = [ + "bitflags 1.3.2", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3" +dependencies = [ + "cfg-if", + "libc", + "version-compare", +] + +[[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -595,6 +632,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5,3 +5,5 @@ edition = "2024" [dependencies] cpal = "0.15.3" +glam = "0.30.1" +sdl2 = "0.37.0" diff --git a/src/main.rs b/src/main.rs index 6e9f760..59c4a24 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,40 +1,165 @@ +#![feature(array_chunks)] +use cpal::{ + BufferSize, SampleRate, StreamConfig, + traits::{DeviceTrait, HostTrait, StreamTrait}, +}; +use glam::{Vec2, vec2}; +use sdl2::event::Event; use std::{ + collections::VecDeque, env::args, f32::consts::PI, fs::read_to_string, - io::{BufWriter, Write, stdout}, - time::Instant, + sync::mpsc::{Receiver, channel}, + thread::sleep, + time::{Duration, Instant}, }; fn main() { - let mut out = BufWriter::new(stdout()); - let objects = parse_file(&read_to_string(&args().nth(1).unwrap()).unwrap()); - let start = Instant::now(); - loop { - let time = start.elapsed().as_secs_f32(); - let mut points = Vec::new(); + let mut points = VecDeque::new(); + let (mouse_tx, mouse_rx) = channel(); + + let mut state = State::new(mouse_rx); + let mut frames = 0; + let mut last_stats = Instant::now(); + + let host = cpal::default_host(); + let outdev = host.default_output_device().unwrap(); + let outstream = outdev + .build_output_stream( + &StreamConfig { + buffer_size: BufferSize::Default, + channels: 2, + sample_rate: SampleRate(48_000), + }, + move |data: &mut [f32], _| { + let time = start.elapsed().as_secs_f32(); + + while points.len() < data.len() / 2 { + state.draw(&mut points, time); + frames += 1; + } + if last_stats.elapsed().as_secs() >= 1 { + last_stats = Instant::now(); + eprintln!("FPS = {frames}"); + frames = 0; + } + for [x, y] in data.array_chunks_mut::<2>() { + let p = points.pop_front().unwrap(); + *x = p.x; + *y = p.y; + } + }, + |err| eprintln!("{err}"), + None, + ) + .unwrap(); + + outstream.play().unwrap(); + + let sdl = sdl2::init().unwrap(); + let video = sdl.video().unwrap(); + + let mut window = video + .window("Input capture", 256, 256) + .resizable() + .build() + .unwrap(); - for h in &objects { - let t = h.time as f32 / 1000.; - let trel = time - t; - if trel > 0. || trel < -1. { - continue; + let mut events = sdl.event_pump().unwrap(); + + let mut last_draw = Instant::now(); + 'blub: loop { + for event in events.poll_iter() { + match event { + Event::Window { .. } => {} + Event::MouseButtonDown { .. } => { + window.set_mouse_grab(true); + mouse_tx.send(Vec2::NEG_INFINITY).unwrap(); + } + Event::MouseMotion { xrel, yrel, .. } => { + mouse_tx.send(vec2(xrel as f32, -yrel as f32)).unwrap(); + } + Event::Quit { .. } => break 'blub, + _ => (), } - let x = h.x as f32 / 512.; - let y = h.y as f32 / 384.; + } + + if last_draw.elapsed().as_secs_f32() > 0.1 { + last_draw = Instant::now(); + let mut surf = window.surface(&events).unwrap(); + surf.fill_rect( + sdl2::rect::Rect::new(0, 0, 100, 100), + sdl2::pixels::Color::RED, + ) + .unwrap(); + surf.finish().unwrap(); + } + + sleep(Duration::from_millis(1)); + } +} - let r = 0.1 + trel / -10.; - for i in 0..20 { - let t = i as f32 / 20. * PI * 2.; - points.push((x + t.sin() * r, y + t.cos() * r)); +struct State { + mouse: Vec2, + mouse_rx: Receiver<Vec2>, + objects: Vec<HitObject>, +} + +impl State { + pub fn new(mouse_rx: Receiver<Vec2>) -> Self { + let objects = parse_file(&read_to_string(&args().nth(1).unwrap()).unwrap()); + Self { + mouse: Vec2::ZERO, + mouse_rx, + objects, + } + } + pub fn draw(&mut self, points: &mut VecDeque<Vec2>, time: f32) { + for x in self.mouse_rx.try_iter() { + if x.is_finite() { + self.mouse += x / 100.; + } else { + self.mouse *= 0.; } } + for i in 0..200 { + let t = i as f32 / 30. * PI * 2.; + points.push_back((Vec2::from_angle(t) * 2.).clamp(Vec2::NEG_ONE, Vec2::ONE)); + } + for _ in 0..200 { + points.push_back(self.mouse); + } + render_frame(&self.objects, points, time); + } +} - for (x, y) in points { - out.write_all(&f32::to_le_bytes(x)).unwrap(); - out.write_all(&f32::to_le_bytes(y)).unwrap(); +fn render_frame(obs: &[HitObject], points: &mut VecDeque<Vec2>, time: f32) { + let mut render_any = false; + for h in obs { + let t = h.time as f32 / 1000.; + let trel = time - t; + if trel > 0. || trel < -1. { + continue; } + render_any = true; + let center = vec2(h.x as f32 / 512., h.y as f32 / 384.) * 2. - 1.; + + let base_r = 0.1; + let approach_r = base_r + trel / -5.; + draw_circle(points, center, base_r, 200); + draw_circle(points, center, approach_r, 200); + } + if !render_any { + points.extend((0..100).map(|_| Vec2::ZERO)); + } +} + +fn draw_circle(points: &mut VecDeque<Vec2>, center: Vec2, r: f32, res: usize) { + for i in 0..res { + let t = i as f32 / res as f32 * PI * 2.; + points.push_back(center + Vec2::from_angle(t) * r); } } |