Skip to content

Commit 85f135c

Browse files
committed
Add blink visualization
1 parent 81b203d commit 85f135c

File tree

11 files changed

+510
-64
lines changed

11 files changed

+510
-64
lines changed

caw/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,7 @@ required-features = [ "interactive", "audio_file" ]
7373
[[example]]
7474
name = "udp_viz_demo"
7575
required-features = [ "viz", "player" ]
76+
77+
[[example]]
78+
name = "live_example"
79+
required-features = [ "live", "widgets", "viz" ]

caw/examples/live_example.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use caw::prelude::*;
2+
use std::thread;
3+
4+
fn main() {
5+
let volume = sv_default();
6+
let out: LiveStereoOut = live_stereo_viz_udp(oscilloscope::Config {
7+
..Default::default()
8+
})
9+
.with_volume(volume.clone());
10+
volume.set(knob("volume").initial_value_01(0.2).build());
11+
let tempo_s = sv(knob("tempo s").build() * 2.);
12+
let clock = sv(periodic_trig_s(tempo_s.clone()).build());
13+
let env = sv({
14+
let mut scope = blink::Server::new("env", Default::default()).unwrap();
15+
adsr(clock.clone().trig_to_gate(tempo_s.clone() / 2.))
16+
.a(tempo_s.clone() / 8.)
17+
.r(tempo_s.clone() / 2.)
18+
.build()
19+
.exp_01(1.)
20+
.with_buf(move |buf| {
21+
let _ = scope.send_samples(buf);
22+
})
23+
});
24+
out.set(|| {
25+
super_saw(60.)
26+
.build()
27+
.filter(low_pass::default(100. + env.clone() * 4000.).q(0.5))
28+
});
29+
thread::park();
30+
}

core/src/sig.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ where
1919
{
2020
fn iter(&self) -> impl Iterator<Item = T>;
2121

22+
/// Clears `out` and populates it with the contents of `self`.
2223
fn clone_to_vec(&self, out: &mut Vec<T>) {
2324
out.clear();
2425
for x in self.iter() {
@@ -586,6 +587,19 @@ where
586587
})
587588
}
588589

590+
/// Calls `f` once per frame on all the samples computed during that frame, returning the
591+
/// original signal unchanged.
592+
pub fn with_buf<F>(self, f: F) -> Sig<WithBuf<S, F>>
593+
where
594+
F: FnMut(&[S::Item]),
595+
{
596+
Sig(WithBuf {
597+
sig: self.0,
598+
f,
599+
buf: Vec::new(),
600+
})
601+
}
602+
589603
pub fn zip<O>(self, other: O) -> Sig<Zip<S, O>>
590604
where
591605
O: SigT,
@@ -1002,6 +1016,31 @@ where
10021016
}
10031017
}
10041018

1019+
pub struct WithBuf<S, F>
1020+
where
1021+
S: SigT,
1022+
F: FnMut(&[S::Item]),
1023+
{
1024+
sig: S,
1025+
f: F,
1026+
buf: Vec<S::Item>,
1027+
}
1028+
1029+
impl<S, F> SigT for WithBuf<S, F>
1030+
where
1031+
S: SigT,
1032+
F: FnMut(&[S::Item]),
1033+
{
1034+
type Item = S::Item;
1035+
1036+
fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
1037+
let ret = self.sig.sample(ctx);
1038+
ret.clone_to_vec(&mut self.buf);
1039+
(self.f)(&self.buf);
1040+
ret
1041+
}
1042+
}
1043+
10051044
pub struct MapMut<S, T, F>
10061045
where
10071046
S: SigT,

live/src/lib.rs

Lines changed: 13 additions & 4 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::oscilloscope;
5+
use caw_viz_udp_app_lib::{SendStatus, oscilloscope};
66
use lazy_static::lazy_static;
77
use std::{sync::Mutex, thread, time::Duration};
88

@@ -40,11 +40,20 @@ pub fn live_stereo_viz(
4040

4141
pub fn live_stereo_viz_udp(config: oscilloscope::Config) -> LiveStereoOut {
4242
let (out, viz_data) = live_stereo_viz(VisualizationDataPolicy::All);
43-
let mut viz = oscilloscope::Server::new("CAW Synthesizer", config).unwrap();
43+
let make_viz = move || {
44+
oscilloscope::Server::new("CAW Synthesizer", config.clone()).unwrap()
45+
};
46+
let mut viz = make_viz();
4447
thread::spawn(move || {
4548
loop {
46-
// TODO: Is it ok to ignore errors here?
47-
let _ = viz_data.with_and_clear(|buf| viz.send_samples(buf));
49+
match viz_data.with_and_clear(|buf| viz.send_samples(buf)) {
50+
Ok(SendStatus::Success) => (),
51+
Ok(SendStatus::Disconnected) => {
52+
// re-open the visualization if it was closed
53+
viz = make_viz();
54+
}
55+
Err(e) => eprintln!("{}", e),
56+
}
4857
thread::sleep(Duration::from_millis(16));
4958
}
5059
});

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

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::common::{
2-
MAX_F32_SAMPLES_TO_SEND, PROGRAM_NAME, VizUdpClient, VizUdpServer,
2+
MAX_F32_SAMPLES_TO_SEND, PROGRAM_NAME, SendStatus, VizUdpClient,
3+
VizUdpServer, samples_from_ne_bytes, samples_to_ne_bytes,
34
};
45
use std::{
56
net::{SocketAddr, ToSocketAddrs},
@@ -19,8 +20,8 @@ impl Default for Config {
1920
Self {
2021
width: 100,
2122
height: 100,
22-
red: 0,
23-
green: 255,
23+
red: 127,
24+
green: 0,
2425
blue: 0,
2526
}
2627
}
@@ -36,7 +37,7 @@ fn start_client_app(
3637
command.args([
3738
format!("--server={}", server_addr.to_string()),
3839
format!("--title={}", title.to_string()),
39-
"oscilloscope".to_string(),
40+
"blink".to_string(),
4041
format!("--width={}", config.width),
4142
format!("--height={}", config.height),
4243
format!("--red={}", config.red),
@@ -45,3 +46,85 @@ fn start_client_app(
4546
]);
4647
Ok(command.spawn()?)
4748
}
49+
50+
pub struct Server {
51+
raw: VizUdpServer,
52+
buf: Vec<f32>,
53+
}
54+
55+
fn send_samples(
56+
server: &mut VizUdpServer,
57+
samples: &[f32],
58+
) -> anyhow::Result<SendStatus> {
59+
for samples_chunk in samples.chunks(MAX_F32_SAMPLES_TO_SEND) {
60+
samples_to_ne_bytes(samples_chunk, &mut server.buf);
61+
if !server.send_buf()? {
62+
return Ok(SendStatus::Disconnected);
63+
}
64+
}
65+
Ok(SendStatus::Success)
66+
}
67+
68+
impl Server {
69+
pub fn new(title: &str, config: Config) -> anyhow::Result<Self> {
70+
Ok(Self {
71+
raw: VizUdpServer::new(|server_addr| {
72+
let _client_process = start_client_app(
73+
server_addr,
74+
PROGRAM_NAME,
75+
title,
76+
&config,
77+
)?;
78+
Ok(())
79+
})?,
80+
buf: Vec::new(),
81+
})
82+
}
83+
84+
pub fn send_samples(
85+
&mut self,
86+
samples: &[f32],
87+
) -> anyhow::Result<SendStatus> {
88+
send_samples(&mut self.raw, samples)
89+
}
90+
91+
pub fn send_samples_batched(
92+
&mut self,
93+
samples: &[f32],
94+
) -> anyhow::Result<()> {
95+
self.buf.extend_from_slice(samples);
96+
if self.buf.len() >= MAX_F32_SAMPLES_TO_SEND {
97+
send_samples(&mut self.raw, &self.buf)?;
98+
self.buf.clear();
99+
}
100+
Ok(())
101+
}
102+
}
103+
104+
pub struct Client {
105+
raw: VizUdpClient,
106+
buf: Vec<f32>,
107+
}
108+
109+
impl Client {
110+
pub fn new<A: ToSocketAddrs>(server_address: A) -> anyhow::Result<Self> {
111+
Ok(Self {
112+
raw: VizUdpClient::new(server_address)?,
113+
buf: Vec::new(),
114+
})
115+
}
116+
117+
pub fn recv_sample(&mut self) -> anyhow::Result<bool> {
118+
Ok(match self.raw.recv()? {
119+
Some(buf_raw) => {
120+
samples_from_ne_bytes(buf_raw, &mut self.buf);
121+
true
122+
}
123+
None => false,
124+
})
125+
}
126+
127+
pub fn iter(&self) -> impl Iterator<Item = f32> {
128+
self.buf.iter().cloned()
129+
}
130+
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,8 @@ impl VizUdpClient {
123123
}
124124
}
125125
}
126+
127+
pub enum SendStatus {
128+
Success,
129+
Disconnected,
130+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
pub mod blink;
99
mod common;
1010
pub mod oscilloscope;
11+
pub use common::SendStatus;

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

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
use crate::common::{
2-
MAX_F32_SAMPLES_TO_SEND, PROGRAM_NAME, VizUdpClient, VizUdpServer,
3-
samples_from_ne_bytes, samples_to_ne_bytes,
2+
MAX_F32_SAMPLES_TO_SEND, PROGRAM_NAME, SendStatus, VizUdpClient,
3+
VizUdpServer, samples_from_ne_bytes, samples_to_ne_bytes,
44
};
55
use std::{
66
net::{SocketAddr, ToSocketAddrs},
77
process::{Child, Command},
88
};
99

10+
#[derive(Clone, Copy, Debug)]
11+
pub enum OscilloscopeStyle {
12+
Xy,
13+
TimeDomain,
14+
TimeDomainStereo,
15+
}
16+
impl ToString for OscilloscopeStyle {
17+
fn to_string(&self) -> String {
18+
match self {
19+
OscilloscopeStyle::Xy => format!("xy"),
20+
OscilloscopeStyle::TimeDomain => format!("time-domain"),
21+
OscilloscopeStyle::TimeDomainStereo => {
22+
format!("time-domain-stereo")
23+
}
24+
}
25+
}
26+
}
27+
28+
#[derive(Clone)]
1029
pub struct Config {
1130
pub width: u32,
1231
pub height: u32,
@@ -17,6 +36,7 @@ pub struct Config {
1736
pub red: u8,
1837
pub green: u8,
1938
pub blue: u8,
39+
pub style: OscilloscopeStyle,
2040
}
2141

2242
impl Default for Config {
@@ -31,6 +51,7 @@ impl Default for Config {
3151
red: 0,
3252
green: 255,
3353
blue: 0,
54+
style: OscilloscopeStyle::Xy,
3455
}
3556
}
3657
}
@@ -67,14 +88,14 @@ pub struct Server {
6788
fn send_samples(
6889
server: &mut VizUdpServer,
6990
samples: &[f32],
70-
) -> anyhow::Result<()> {
91+
) -> anyhow::Result<SendStatus> {
7192
for samples_chunk in samples.chunks(MAX_F32_SAMPLES_TO_SEND) {
7293
samples_to_ne_bytes(samples_chunk, &mut server.buf);
7394
if !server.send_buf()? {
74-
break;
95+
return Ok(SendStatus::Disconnected);
7596
}
7697
}
77-
Ok(())
98+
Ok(SendStatus::Success)
7899
}
79100

80101
impl Server {
@@ -93,7 +114,10 @@ impl Server {
93114
})
94115
}
95116

96-
pub fn send_samples(&mut self, samples: &[f32]) -> anyhow::Result<()> {
117+
pub fn send_samples(
118+
&mut self,
119+
samples: &[f32],
120+
) -> anyhow::Result<SendStatus> {
97121
send_samples(&mut self.raw, samples)
98122
}
99123

viz-udp-app/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ documentation = "https://docs.rs/caw_viz_udp_app"
1111

1212
[dependencies]
1313
anyhow = "1.0"
14-
sdl2 = { version = "0.38" }
14+
sdl2 = { version = "0.38", features = ["ttf"] }
1515
line_2d = "0.5"
1616
clap = { version = "4", features = ["derive"] }
1717
caw_viz_udp_app_lib = { version = "0.1", path = "../viz-udp-app-lib" }
18+
lazy_static = "1.5"
80.4 KB
Binary file not shown.

0 commit comments

Comments
 (0)