Skip to content

Commit f9c7d0f

Browse files
committed
Refactor viz into app with subcommands
1 parent ded9a75 commit f9c7d0f

File tree

6 files changed

+26
-198
lines changed

6 files changed

+26
-198
lines changed

core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod sig;
33
pub mod sig_ops;
44
pub use sig::{
55
Buf, Const, ConstBuf, Filter, GateToTrigRisingEdge, Sig, SigAbs, SigBoxed,
6-
SigBoxedVar, SigCtx, SigSampleIntoBufT, SigShared, SigT, SigVar,
6+
SigBoxedVar, SigConst, SigCtx, SigSampleIntoBufT, SigShared, SigT, SigVar,
77
Triggerable, Zip, Zip3, Zip4, sig_boxed, sig_boxed_var,
88
sig_option_first_some, sig_shared, sig_var,
99
};

interactive/examples/keyboard_and_mouse_polyphonic_interactive.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ use caw_core::*;
22
use caw_interactive::{Input, Visualization, Window};
33
use caw_keyboard::{IntoNoteFreqHz, KeyEventsT, MonoVoice, Note};
44
use caw_modules::*;
5+
use caw_player::PlayerConfig;
56

67
fn sig(input: Input, ch: Channel) -> Sig<impl SigT<Item = f32> + Send> {
78
input
89
.clone()
910
.keyboard
10-
.opinionated_key_events(Note::B2)
11-
.poly_voices(12)
11+
.opinionated_key_events(Note::B1)
12+
.poly_voices(2)
1213
.into_iter()
1314
.map(
1415
move |MonoVoice {
@@ -60,13 +61,16 @@ fn main() -> anyhow::Result<()> {
6061
let window = Window::builder()
6162
.sane_default()
6263
.visualization(Visualization::StereoOscillographics)
63-
.line_width(2)
64+
.line_width(3)
6465
.scale(0.7)
6566
.stride(2)
6667
.build();
6768
let input = window.input();
6869
window.play_stereo(
6970
Stereo::new_fn_channel(|ch| sig(input.clone(), ch)),
70-
Default::default(),
71+
PlayerConfig {
72+
system_latency_s: 0.05,
73+
..Default::default()
74+
},
7175
)
7276
}

live/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use caw_core::{Sig, SigBoxedVar, Stereo, StereoPair, svf32};
22
use caw_player::{
33
PlayerConfig, PlayerVisualizationData, VisualizationDataPolicy, play_stereo,
44
};
5-
use caw_viz_udp_app_lib::{VizAppConfig, VizUdpServer};
5+
use caw_viz_udp_app_lib::oscilloscope;
66
use lazy_static::lazy_static;
77
use std::{sync::Mutex, thread, time::Duration};
88

@@ -38,9 +38,9 @@ pub fn live_stereo_viz(
3838
(OUT.clone(), visualization_data_ref.clone())
3939
}
4040

41-
pub fn live_stereo_viz_udp(config: VizAppConfig) -> LiveStereoOut {
41+
pub fn live_stereo_viz_udp(config: oscilloscope::Config) -> LiveStereoOut {
4242
let (out, viz_data) = live_stereo_viz(VisualizationDataPolicy::All);
43-
let mut viz = VizUdpServer::new(config).unwrap();
43+
let mut viz = oscilloscope::Server::new(config).unwrap();
4444
thread::spawn(move || {
4545
loop {
4646
// TODO: Is it ok to ignore errors here?

viz-udp-app-lib/examples/viz_udp_app_lib_handshake.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use caw_viz_udp_app_lib::{VizAppConfig, VizUdpServer};
1+
use caw_viz_udp_app_lib::oscilloscope;
22
use std::time::Duration;
33

44
fn main() -> anyhow::Result<()> {
5-
let mut server = VizUdpServer::new(VizAppConfig {
5+
let mut server = oscilloscope::Server::new(oscilloscope::Config {
66
..Default::default()
77
})?;
88
loop {

viz-udp-app-lib/src/lib.rs

Lines changed: 8 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -1,186 +1,8 @@
1-
use std::{
2-
net::{Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket},
3-
process::{Child, Command},
4-
};
5-
6-
pub const HANDSHAKE: [u8; 4] = [0x43, 0x41, 0x57, 0x0];
7-
8-
// Based on the max number of bytes that can somewhat reliably be sent over UDP (508) divided by 4
9-
// as we'll be sending f32's. That gives us 127, but since we're actually sending pairs of f32's,
10-
// reduce this to an even number.
11-
const MAX_SAMPLES_TO_SEND: usize = 126;
12-
13-
const PROGRAM_NAME: &str = "caw_viz_udp_app";
14-
15-
pub struct VizAppConfig {
16-
pub program_name: String,
17-
pub width: u32,
18-
pub height: u32,
19-
pub scale: f32,
20-
pub max_num_samples: usize,
21-
pub line_width: u32,
22-
pub alpha_scale: u8,
23-
pub red: u8,
24-
pub green: u8,
25-
pub blue: u8,
26-
}
27-
28-
impl Default for VizAppConfig {
29-
fn default() -> Self {
30-
Self {
31-
program_name: PROGRAM_NAME.to_string(),
32-
width: 640,
33-
height: 480,
34-
scale: 500.0,
35-
max_num_samples: 5000,
36-
line_width: 1,
37-
alpha_scale: 255,
38-
red: 0,
39-
green: 255,
40-
blue: 0,
41-
}
42-
}
43-
}
44-
45-
fn wait_for_handshake(socket: &UdpSocket) -> anyhow::Result<SocketAddr> {
46-
let mut buf = [0; 8];
47-
loop {
48-
let (size, client_addr) = socket.recv_from(&mut buf)?;
49-
if size == HANDSHAKE.len() && buf[0..HANDSHAKE.len()] == HANDSHAKE {
50-
return Ok(client_addr);
51-
}
52-
}
53-
}
54-
55-
fn samples_to_ne_bytes(samples: &[f32], out: &mut Vec<u8>) {
56-
out.clear();
57-
for sample in samples {
58-
out.extend_from_slice(&sample.to_ne_bytes());
59-
}
60-
}
61-
62-
fn samples_from_ne_bytes(bytes: &[u8], out: &mut Vec<f32>) {
63-
if bytes.len() % 4 != 0 {
64-
log::error!(
65-
"Received a buffer of bytes that is not a multiple of 4 bytes in length (length is {}).",
66-
bytes.len()
67-
);
68-
}
69-
out.clear();
70-
let mut buf = [0; 4];
71-
for w in bytes.chunks_exact(4) {
72-
buf.copy_from_slice(w);
73-
out.push(f32::from_ne_bytes(buf));
74-
}
75-
}
76-
77-
fn start_client_app(
78-
socket: &UdpSocket,
79-
config: &VizAppConfig,
80-
) -> anyhow::Result<Child> {
81-
let mut command = Command::new(&config.program_name);
82-
command.args([
83-
format!("--server={}", socket.local_addr()?.to_string()),
84-
format!("--width={}", config.width),
85-
format!("--height={}", config.height),
86-
format!("--scale={}", config.scale),
87-
format!("--max-num-samples={}", config.max_num_samples),
88-
format!("--line-width={}", config.line_width),
89-
format!("--alpha-scale={}", config.alpha_scale),
90-
format!("--red={}", config.red),
91-
format!("--green={}", config.green),
92-
format!("--blue={}", config.blue),
93-
]);
94-
Ok(command.spawn()?)
95-
}
96-
97-
pub struct VizUdpServer {
98-
socket: UdpSocket,
99-
buf: Vec<u8>,
100-
client_running: bool,
101-
}
102-
103-
impl VizUdpServer {
104-
pub fn new(config: VizAppConfig) -> anyhow::Result<Self> {
105-
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
106-
log::info!("Viz udp server address: {:?}", socket.local_addr());
107-
let _client_process = start_client_app(&socket, &config)?;
108-
let client_address = wait_for_handshake(&socket)?;
109-
log::info!("Viz udp client address: {:?}", client_address);
110-
socket.connect(client_address)?;
111-
socket.set_nonblocking(true)?;
112-
Ok(Self {
113-
socket,
114-
buf: Vec::new(),
115-
client_running: true,
116-
})
117-
}
118-
119-
pub fn send_samples(&mut self, samples: &[f32]) -> anyhow::Result<()> {
120-
if !self.client_running {
121-
// If the client window is closed then there is nothing to do. Possibly we could
122-
// restart the client at this point in the future?
123-
return Ok(());
124-
}
125-
for samples_chunk in samples.chunks(MAX_SAMPLES_TO_SEND) {
126-
samples_to_ne_bytes(samples_chunk, &mut self.buf);
127-
match self.socket.send(&self.buf) {
128-
Ok(bytes_sent) => {
129-
if bytes_sent != self.buf.len() {
130-
log::error!(
131-
"Failed to send all samples to viz udp client. Tried to send {} bytes. Actually sent {} bytes.",
132-
self.buf.len(),
133-
bytes_sent
134-
);
135-
}
136-
}
137-
Err(e) => match e.kind() {
138-
std::io::ErrorKind::ConnectionRefused => {
139-
self.client_running = false;
140-
log::error!("Viz udp client has closed!");
141-
break;
142-
}
143-
_ => anyhow::bail!(e),
144-
},
145-
}
146-
}
147-
Ok(())
148-
}
149-
}
150-
151-
pub struct VizUdpClient {
152-
socket: UdpSocket,
153-
buf_raw: Vec<u8>,
154-
buf: Vec<f32>,
155-
}
156-
157-
impl VizUdpClient {
158-
pub fn new<A: ToSocketAddrs>(server_address: A) -> anyhow::Result<Self> {
159-
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
160-
socket.connect(server_address)?;
161-
assert_eq!(socket.send(&HANDSHAKE)?, HANDSHAKE.len());
162-
socket.set_nonblocking(true)?;
163-
Ok(Self {
164-
socket,
165-
buf_raw: vec![0; 32768],
166-
buf: Vec::new(),
167-
})
168-
}
169-
170-
pub fn recv_sample(&mut self) -> anyhow::Result<bool> {
171-
match self.socket.recv(&mut self.buf_raw) {
172-
Ok(size) => {
173-
samples_from_ne_bytes(&self.buf_raw[0..size], &mut self.buf);
174-
Ok(true)
175-
}
176-
Err(e) => match e.kind() {
177-
std::io::ErrorKind::WouldBlock => Ok(false),
178-
_ => anyhow::bail!(e),
179-
},
180-
}
181-
}
182-
183-
pub fn pairs(&self) -> impl Iterator<Item = (f32, f32)> {
184-
self.buf.chunks_exact(2).map(|c| (c[0], c[1]))
185-
}
186-
}
1+
/// A wrapper for the `caw_viz_udp_app` command. That command starts a udp client and opens a
2+
/// window. The client listens for messages from a server representing vizualizations and renders
3+
/// them in the window. This library provides the server implementation. Synthesizer code is
4+
/// expected to use this library to create a udp server and spawn a client which connects to it,
5+
/// and then manipulate the server to send visualization commands to the client. This library also
6+
/// contains helper code for writing client applications.
7+
mod common;
8+
pub mod oscilloscope;

viz-udp-app/src/main.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
/// A udp client which receives visualization data from a corresponding udp server and renders
2+
/// visualizations in a graphical window.
13
use anyhow::anyhow;
2-
use caw_viz_udp_app_lib::VizUdpClient;
4+
use caw_viz_udp_app_lib::oscilloscope;
35
use clap::{Parser, Subcommand};
46
use line_2d::Coord;
57
use sdl2::{event::Event, pixels::Color, rect::Rect};
@@ -65,7 +67,7 @@ fn main() -> anyhow::Result<()> {
6567
.build()?;
6668
canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
6769
let mut event_pump = sdl_context.event_pump().map_err(|e| anyhow!(e))?;
68-
let mut viz_udp_client = VizUdpClient::new(server)?;
70+
let mut viz_udp_client = oscilloscope::Client::new(server)?;
6971
let mut scope_state = ScopeState::default();
7072
loop {
7173
for event in event_pump.poll_iter() {

0 commit comments

Comments
 (0)