Skip to content

Commit c497447

Browse files
authored
Merge pull request #58 from exa04/test-channel-buffers
New `Bus` system to replace naive `Arc<Mutex<Buffer>>` System
2 parents b1ca68f + 5b34978 commit c497447

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2877
-7097
lines changed

Cargo.lock

Lines changed: 1035 additions & 592 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ description = "Composable views and associated data structures for nih-plug UIs
88
resolver = "2"
99
members = [
1010
"xtask",
11-
"examples/visualizers", "examples/peak_graph", "examples/histogram",
11+
"examples/visualizers",
12+
"examples/peak_graph",
13+
"examples/benchmark_lots_of_visualizers"
1214
]
1315

1416
[lib]
@@ -20,6 +22,12 @@ nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git" }
2022
lazy_static = "1.4.0"
2123
realfft = "3.3.0"
2224
triple_buffer = "7.0.0"
25+
crossbeam-channel = "0.5.13"
26+
crossbeam-queue = "0.3.11"
27+
arc-swap = "1.7.1"
28+
atomic_bitfield = "0.1.0"
29+
blinkcast = "0.2.0"
30+
atomic-bus = "0.1.0"
2331

2432
[dev-dependencies]
2533
rand = "0.8.5"

README.md

Lines changed: 116 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ plug-in user interfaces with ease. It uses various custom data structures for
2929
real-time visualizers, allowing you to easily build beautiful, performant
3030
plug-in UIs.
3131

32-
Here's a demo ([YouTube mirror](https://www.youtube.com/watch?v=DQ-9XRBLpB4))
32+
Here's a demo ([YouTube mirror](https://www.youtube.com/watch?v=He70jwvdjFU))
3333

34-
https://github.com/223230/cyma/assets/68156346/dea8ee3e-7162-4752-a569-6dc4218b0745
34+
https://github.com/user-attachments/assets/456a6705-5936-4118-a527-fb8999a91041
3535

3636
Wanna see the code behind this? It's [this example!](./examples/visualizers)
3737

@@ -61,80 +61,137 @@ feature request so it can be added!
6161

6262
## ❓ Example
6363

64-
Here's how to create a basic oscilloscope with a grid background.
64+
Here's how to create a basic oscilloscope.
6565

6666
![Oscilloscope](doc/example.png)
6767

68+
Visualizers communicate with your plugin via busses. One bus can feed multiple
69+
visualizers. Just add it to your plugin like so:
70+
6871
```rust
69-
Oscilloscope::new(
70-
cx,
71-
Data::oscilloscope_buffer,
72-
(-1.2, 1.2),
73-
ValueScaling::Linear,
74-
)
75-
.background_color(Color::rgba(120, 120, 120));
72+
pub struct OscopePlugin {
73+
params: Arc<OscopeParams>,
74+
bus: Arc<MonoBus>,
75+
}
76+
77+
impl Plugin for OscopePlugin {
78+
fn initialize(
79+
&mut self,
80+
_: &AudioIOLayout,
81+
buffer_config: &BufferConfig,
82+
_: &mut impl InitContext<Self>,
83+
) -> bool {
84+
self.bus.set_sample_rate(buffer_config.sample_rate);
85+
true
86+
}
87+
88+
fn process(
89+
&mut self,
90+
buffer: &mut Buffer,
91+
_: &mut AuxiliaryBuffers,
92+
_: &mut impl ProcessContext<Self>,
93+
) -> ProcessStatus {
94+
if self.params.editor_state.is_open() {
95+
self.bus.send_buffer_summing(buffer);
96+
}
97+
ProcessStatus::Normal
98+
}
99+
100+
fn editor(&mut self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
101+
editor::create(
102+
self.bus.clone(),
103+
self.params.editor_state.clone(),
104+
)
105+
}
106+
107+
...
108+
}
76109
```
77110

78-
Here, `Data::oscilloscope_buffer` is an `Arc<Mutex<WaveformBuffer>>`, a buffer
79-
that allows for your audio to be sent to the `Oscilloscope` in a much smaller
80-
package, while retaining peak information. Here, it's configured to be 512
81-
samples long, and it represents 10 seconds of audio at 44.1 kHz.
111+
Now, in your editor code, you just need to subscribe to the bus. Then, you can
112+
use it for visualizers like this oscilloscope:
113+
114+
```rust
115+
pub fn create(
116+
bus: Arc<MonoBus>,
117+
editor_state: Arc<ViziaState>,
118+
) -> Option<Box<dyn Editor>> {
119+
create_vizia_editor(editor_state, ViziaTheming::default(), move |cx, _| {
120+
bus.subscribe(cx);
121+
Oscilloscope::new(cx, bus.clone(), 4.0, (-1.0, 1.0), ValueScaling::Linear)
122+
.color(Color::rgb(120, 120, 120));
123+
})
124+
}
125+
```
82126

83-
It's very plug-and-play, you only need to call `enqueue_buffer()` in your
84-
plugin's process function to use it!
85127

86-
Check out the book, or the [examples](examples) to learn how to work with these
87-
buffers.
128+
Check out the book or the [examples](examples) to familiarize yourself with this
129+
system.
88130

89131
## 🍔 Composing views
90132

91133
A core feature of Cyma is composability.
92134

93-
For example, by combining views such as the `Grid`, `UnitRuler`, and
94-
`PeakGraph`, you can make this real-time peak analyzer.
135+
For example, by combining views such as the `Grid`, `UnitRuler`, `Graph`, and
136+
`Histogram` you can make this real-time peak graph with an RMS plot and a
137+
histogram overlay.
95138

96139
![Peak visualizer](doc/composability_demo.png)
97140

98141
```rust
99-
fn peak_graph(cx: &mut Context) {
100-
HStack::new(cx, |cx| {
101-
ZStack::new(cx, |cx| {
102-
Grid::new(
103-
cx,
104-
ValueScaling::Linear,
105-
(-32., 8.),
106-
vec![6.0, 0.0, -6.0, -12.0, -18.0, -24.0, -30.0],
107-
Orientation::Horizontal,
108-
)
109-
.color(Color::rgb(60, 60, 60));
110-
111-
Graph::new(cx, Data::peak_buffer, (-32.0, 8.0), ValueScaling::Decibels)
112-
.color(Color::rgba(255, 255, 255, 160))
113-
.background_color(Color::rgba(255, 255, 255, 60));
114-
})
115-
.background_color(Color::rgb(16, 16, 16));
116-
117-
UnitRuler::new(
118-
cx,
119-
(-32.0, 8.0),
120-
ValueScaling::Linear,
121-
vec![
122-
(6.0, "6db"),
123-
(0.0, "0db"),
124-
(-6.0, "-6db"),
125-
(-12.0, "-12db"),
126-
(-18.0, "-18db"),
127-
(-24.0, "-24db"),
128-
(-30.0, "-30db"),
129-
],
130-
Orientation::Vertical,
131-
)
132-
.font_size(12.)
133-
.color(Color::rgb(160, 160, 160))
134-
.width(Pixels(32.));
135-
})
136-
.col_between(Pixels(8.));
137-
}
142+
ZStack::new(cx, |cx| {
143+
Grid::new(
144+
cx,
145+
ValueScaling::Linear,
146+
(-32., 8.0),
147+
vec![6.0, 0.0, -6.0, -12.0, -18.0, -24.0, -30.0],
148+
Orientation::Horizontal,
149+
)
150+
.border_width(Pixels(0.5))
151+
.color(Color::rgb(30, 30, 30));
152+
Graph::peak(
153+
cx,
154+
bus.clone(),
155+
10.0,
156+
50.0,
157+
(-32.0, 8.0),
158+
ValueScaling::Decibels,
159+
)
160+
.color(Color::rgba(255, 255, 255, 60))
161+
.background_color(Color::rgba(255, 255, 255, 30));
162+
Graph::rms(
163+
cx,
164+
bus.clone(),
165+
10.0,
166+
250.0,
167+
(-32.0, 8.0),
168+
ValueScaling::Decibels,
169+
)
170+
.color(Color::rgba(255, 92, 92, 128));
171+
Histogram::new(cx, bus.clone(), 250.0, (-32.0, 8.0), ValueScaling::Decibels)
172+
.width(Pixels(64.0))
173+
.color(Color::rgba(64, 128, 255, 64))
174+
.background_color(Color::rgba(64, 128, 255, 32));
175+
UnitRuler::new(
176+
cx,
177+
(-32.0, 8.0),
178+
ValueScaling::Linear,
179+
vec![
180+
(6.0, "6 dB"),
181+
(0.0, "0 dB"),
182+
(-6.0, "-6 dB"),
183+
(-12.0, "-12 dB"),
184+
(-18.0, "-18 dB"),
185+
(-24.0, "-24 dB"),
186+
(-30.0, "-30 dB"),
187+
],
188+
Orientation::Vertical,
189+
)
190+
.font_size(12.)
191+
.color(Color::rgb(220, 220, 220))
192+
.right(Pixels(8.0))
193+
.left(Stretch(1.0));
194+
});
138195
```
139196

140197
## 🙋 Contributing

book/src/SUMMARY.md

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,8 @@
66

77
# Guides
88

9-
- [Composing a Peak Graph](./peak_graph/overview.md)
10-
- [Setting up a PeakBuffer](./peak_graph/peak_buffer_setup.md)
11-
- [Displaying a Graph](./peak_graph/graph_setup.md)
12-
- [Adding Grid Lines](./peak_graph/composing.md)
9+
- [Composing a Peak Graph]()
10+
- [Setting up a PeakBuffer]()
11+
- [Displaying a Graph]()
12+
- [Adding Grid Lines]()
1313
- [Plotting Loudness]()
14-
- [Building a Filter UI]()
15-
- [Adding Some Knobs]()
16-
- [Adding a Spectrogram]()
17-
- [Styling the Spectrogram]()
18-
- [Plotting the Filter Curve]()
19-
- [Making it All Interactive]()

doc/composability_demo.png

116 KB
Loading

doc/example.png

41.5 KB
Loading

doc/visualizers.png

300 KB
Loading

examples/histogram/Cargo.toml renamed to examples/benchmark_lots_of_visualizers/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
2-
name = "histogram"
2+
name = "benchmark_lots_of_visualizers"
33
version = "0.1.0"
44
edition = "2021"
5-
description = "A histogram built using Cyma"
5+
description = "Lots of peak graphs"
66

77
[lib]
88
crate-type = ["cdylib", "lib"]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Visualizers
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use cyma::prelude::*;
2+
use nih_plug::editor::Editor;
3+
use nih_plug_vizia::widgets::ResizeHandle;
4+
use nih_plug_vizia::{assets, create_vizia_editor, vizia::prelude::*, ViziaState, ViziaTheming};
5+
use std::sync::Arc;
6+
7+
#[derive(Lens, Clone)]
8+
pub(crate) struct Data {
9+
bus: Arc<MonoBus>,
10+
}
11+
12+
impl Data {
13+
pub(crate) fn new(bus: Arc<MonoBus>) -> Self {
14+
Self { bus }
15+
}
16+
}
17+
18+
impl Model for Data {}
19+
20+
pub(crate) fn default_state() -> Arc<ViziaState> {
21+
ViziaState::new(|| (1920, 1080))
22+
}
23+
24+
const W: usize = 16;
25+
const H: usize = 12;
26+
27+
pub(crate) fn create(editor_data: Data, editor_state: Arc<ViziaState>) -> Option<Box<dyn Editor>> {
28+
const SPACING: Units = Pixels(1.0);
29+
30+
create_vizia_editor(editor_state, ViziaTheming::default(), move |cx, _| {
31+
assets::register_noto_sans_light(cx);
32+
editor_data.clone().build(cx);
33+
VStack::new(cx, |cx| {
34+
for _ in 0..H {
35+
HStack::new(cx, |cx| {
36+
for _ in 0..W {
37+
visualizer(cx);
38+
}
39+
})
40+
.row_between(SPACING);
41+
}
42+
43+
Label::new(
44+
cx,
45+
format!("Cyma {} - Lots of Visualizers", env!("CARGO_PKG_VERSION")).as_str(),
46+
)
47+
.space(Pixels(8.0))
48+
.color(Color::rgb(180, 180, 180));
49+
})
50+
.background_color(Color::rgb(0, 0, 0))
51+
.row_between(SPACING);
52+
53+
ResizeHandle::new(cx);
54+
})
55+
}
56+
57+
fn visualizer(cx: &mut Context) {
58+
ZStack::new(cx, |cx| {
59+
Grid::new(
60+
cx,
61+
ValueScaling::Linear,
62+
(-32., 8.0),
63+
vec![6.0, 0.0, -6.0, -12.0, -18.0, -24.0, -30.0],
64+
Orientation::Horizontal,
65+
)
66+
.border_width(Pixels(0.5))
67+
.color(Color::rgb(30, 30, 30));
68+
Graph::peak(
69+
cx,
70+
Data::bus,
71+
10.0,
72+
50.0,
73+
(-32.0, 8.0),
74+
ValueScaling::Decibels,
75+
)
76+
.color(Color::rgba(255, 255, 255, 60))
77+
.background_color(Color::rgba(255, 255, 255, 30));
78+
Graph::rms(
79+
cx,
80+
Data::bus,
81+
10.0,
82+
250.0,
83+
(-32.0, 8.0),
84+
ValueScaling::Decibels,
85+
)
86+
.color(Color::rgba(255, 92, 92, 128));
87+
})
88+
.background_color(Color::rgb(16, 16, 16));
89+
}

0 commit comments

Comments
 (0)