aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock43
-rw-r--r--Cargo.toml2
-rw-r--r--src/main.rs169
3 files changed, 192 insertions, 22 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2f53cc0..d6a63d1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 6ae8259..1b17f89 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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);
}
}