Skip to content

Commit

Permalink
Merge pull request #58 from exa04/test-channel-buffers
Browse files Browse the repository at this point in the history
New `Bus` system to replace naive `Arc<Mutex<Buffer>>` System
  • Loading branch information
exa04 authored Jan 16, 2025
2 parents b1ca68f + 5b34978 commit c497447
Show file tree
Hide file tree
Showing 50 changed files with 2,877 additions and 7,097 deletions.
1,627 changes: 1,035 additions & 592 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ description = "Composable views and associated data structures for nih-plug UIs
resolver = "2"
members = [
"xtask",
"examples/visualizers", "examples/peak_graph", "examples/histogram",
"examples/visualizers",
"examples/peak_graph",
"examples/benchmark_lots_of_visualizers"
]

[lib]
Expand All @@ -20,6 +22,12 @@ nih_plug = { git = "https://github.com/robbert-vdh/nih-plug.git" }
lazy_static = "1.4.0"
realfft = "3.3.0"
triple_buffer = "7.0.0"
crossbeam-channel = "0.5.13"
crossbeam-queue = "0.3.11"
arc-swap = "1.7.1"
atomic_bitfield = "0.1.0"
blinkcast = "0.2.0"
atomic-bus = "0.1.0"

[dev-dependencies]
rand = "0.8.5"
175 changes: 116 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ plug-in user interfaces with ease. It uses various custom data structures for
real-time visualizers, allowing you to easily build beautiful, performant
plug-in UIs.

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

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

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

Expand Down Expand Up @@ -61,80 +61,137 @@ feature request so it can be added!

## ❓ Example

Here's how to create a basic oscilloscope with a grid background.
Here's how to create a basic oscilloscope.

![Oscilloscope](doc/example.png)

Visualizers communicate with your plugin via busses. One bus can feed multiple
visualizers. Just add it to your plugin like so:

```rust
Oscilloscope::new(
cx,
Data::oscilloscope_buffer,
(-1.2, 1.2),
ValueScaling::Linear,
)
.background_color(Color::rgba(120, 120, 120));
pub struct OscopePlugin {
params: Arc<OscopeParams>,
bus: Arc<MonoBus>,
}

impl Plugin for OscopePlugin {
fn initialize(
&mut self,
_: &AudioIOLayout,
buffer_config: &BufferConfig,
_: &mut impl InitContext<Self>,
) -> bool {
self.bus.set_sample_rate(buffer_config.sample_rate);
true
}

fn process(
&mut self,
buffer: &mut Buffer,
_: &mut AuxiliaryBuffers,
_: &mut impl ProcessContext<Self>,
) -> ProcessStatus {
if self.params.editor_state.is_open() {
self.bus.send_buffer_summing(buffer);
}
ProcessStatus::Normal
}

fn editor(&mut self, _async_executor: AsyncExecutor<Self>) -> Option<Box<dyn Editor>> {
editor::create(
self.bus.clone(),
self.params.editor_state.clone(),
)
}

...
}
```

Here, `Data::oscilloscope_buffer` is an `Arc<Mutex<WaveformBuffer>>`, a buffer
that allows for your audio to be sent to the `Oscilloscope` in a much smaller
package, while retaining peak information. Here, it's configured to be 512
samples long, and it represents 10 seconds of audio at 44.1 kHz.
Now, in your editor code, you just need to subscribe to the bus. Then, you can
use it for visualizers like this oscilloscope:

```rust
pub fn create(
bus: Arc<MonoBus>,
editor_state: Arc<ViziaState>,
) -> Option<Box<dyn Editor>> {
create_vizia_editor(editor_state, ViziaTheming::default(), move |cx, _| {
bus.subscribe(cx);
Oscilloscope::new(cx, bus.clone(), 4.0, (-1.0, 1.0), ValueScaling::Linear)
.color(Color::rgb(120, 120, 120));
})
}
```

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

Check out the book, or the [examples](examples) to learn how to work with these
buffers.
Check out the book or the [examples](examples) to familiarize yourself with this
system.

## 🍔 Composing views

A core feature of Cyma is composability.

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

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

```rust
fn peak_graph(cx: &mut Context) {
HStack::new(cx, |cx| {
ZStack::new(cx, |cx| {
Grid::new(
cx,
ValueScaling::Linear,
(-32., 8.),
vec![6.0, 0.0, -6.0, -12.0, -18.0, -24.0, -30.0],
Orientation::Horizontal,
)
.color(Color::rgb(60, 60, 60));

Graph::new(cx, Data::peak_buffer, (-32.0, 8.0), ValueScaling::Decibels)
.color(Color::rgba(255, 255, 255, 160))
.background_color(Color::rgba(255, 255, 255, 60));
})
.background_color(Color::rgb(16, 16, 16));

UnitRuler::new(
cx,
(-32.0, 8.0),
ValueScaling::Linear,
vec![
(6.0, "6db"),
(0.0, "0db"),
(-6.0, "-6db"),
(-12.0, "-12db"),
(-18.0, "-18db"),
(-24.0, "-24db"),
(-30.0, "-30db"),
],
Orientation::Vertical,
)
.font_size(12.)
.color(Color::rgb(160, 160, 160))
.width(Pixels(32.));
})
.col_between(Pixels(8.));
}
ZStack::new(cx, |cx| {
Grid::new(
cx,
ValueScaling::Linear,
(-32., 8.0),
vec![6.0, 0.0, -6.0, -12.0, -18.0, -24.0, -30.0],
Orientation::Horizontal,
)
.border_width(Pixels(0.5))
.color(Color::rgb(30, 30, 30));
Graph::peak(
cx,
bus.clone(),
10.0,
50.0,
(-32.0, 8.0),
ValueScaling::Decibels,
)
.color(Color::rgba(255, 255, 255, 60))
.background_color(Color::rgba(255, 255, 255, 30));
Graph::rms(
cx,
bus.clone(),
10.0,
250.0,
(-32.0, 8.0),
ValueScaling::Decibels,
)
.color(Color::rgba(255, 92, 92, 128));
Histogram::new(cx, bus.clone(), 250.0, (-32.0, 8.0), ValueScaling::Decibels)
.width(Pixels(64.0))
.color(Color::rgba(64, 128, 255, 64))
.background_color(Color::rgba(64, 128, 255, 32));
UnitRuler::new(
cx,
(-32.0, 8.0),
ValueScaling::Linear,
vec![
(6.0, "6 dB"),
(0.0, "0 dB"),
(-6.0, "-6 dB"),
(-12.0, "-12 dB"),
(-18.0, "-18 dB"),
(-24.0, "-24 dB"),
(-30.0, "-30 dB"),
],
Orientation::Vertical,
)
.font_size(12.)
.color(Color::rgb(220, 220, 220))
.right(Pixels(8.0))
.left(Stretch(1.0));
});
```

## 🙋 Contributing
Expand Down
14 changes: 4 additions & 10 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,8 @@

# Guides

- [Composing a Peak Graph](./peak_graph/overview.md)
- [Setting up a PeakBuffer](./peak_graph/peak_buffer_setup.md)
- [Displaying a Graph](./peak_graph/graph_setup.md)
- [Adding Grid Lines](./peak_graph/composing.md)
- [Composing a Peak Graph]()
- [Setting up a PeakBuffer]()
- [Displaying a Graph]()
- [Adding Grid Lines]()
- [Plotting Loudness]()
- [Building a Filter UI]()
- [Adding Some Knobs]()
- [Adding a Spectrogram]()
- [Styling the Spectrogram]()
- [Plotting the Filter Curve]()
- [Making it All Interactive]()
Binary file modified doc/composability_demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified doc/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/visualizers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "histogram"
name = "benchmark_lots_of_visualizers"
version = "0.1.0"
edition = "2021"
description = "A histogram built using Cyma"
description = "Lots of peak graphs"

[lib]
crate-type = ["cdylib", "lib"]
Expand Down
1 change: 1 addition & 0 deletions examples/benchmark_lots_of_visualizers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Visualizers
89 changes: 89 additions & 0 deletions examples/benchmark_lots_of_visualizers/src/editor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use cyma::prelude::*;
use nih_plug::editor::Editor;
use nih_plug_vizia::widgets::ResizeHandle;
use nih_plug_vizia::{assets, create_vizia_editor, vizia::prelude::*, ViziaState, ViziaTheming};
use std::sync::Arc;

#[derive(Lens, Clone)]
pub(crate) struct Data {
bus: Arc<MonoBus>,
}

impl Data {
pub(crate) fn new(bus: Arc<MonoBus>) -> Self {
Self { bus }
}
}

impl Model for Data {}

pub(crate) fn default_state() -> Arc<ViziaState> {
ViziaState::new(|| (1920, 1080))
}

const W: usize = 16;
const H: usize = 12;

pub(crate) fn create(editor_data: Data, editor_state: Arc<ViziaState>) -> Option<Box<dyn Editor>> {
const SPACING: Units = Pixels(1.0);

create_vizia_editor(editor_state, ViziaTheming::default(), move |cx, _| {
assets::register_noto_sans_light(cx);
editor_data.clone().build(cx);
VStack::new(cx, |cx| {
for _ in 0..H {
HStack::new(cx, |cx| {
for _ in 0..W {
visualizer(cx);
}
})
.row_between(SPACING);
}

Label::new(
cx,
format!("Cyma {} - Lots of Visualizers", env!("CARGO_PKG_VERSION")).as_str(),
)
.space(Pixels(8.0))
.color(Color::rgb(180, 180, 180));
})
.background_color(Color::rgb(0, 0, 0))
.row_between(SPACING);

ResizeHandle::new(cx);
})
}

fn visualizer(cx: &mut Context) {
ZStack::new(cx, |cx| {
Grid::new(
cx,
ValueScaling::Linear,
(-32., 8.0),
vec![6.0, 0.0, -6.0, -12.0, -18.0, -24.0, -30.0],
Orientation::Horizontal,
)
.border_width(Pixels(0.5))
.color(Color::rgb(30, 30, 30));
Graph::peak(
cx,
Data::bus,
10.0,
50.0,
(-32.0, 8.0),
ValueScaling::Decibels,
)
.color(Color::rgba(255, 255, 255, 60))
.background_color(Color::rgba(255, 255, 255, 30));
Graph::rms(
cx,
Data::bus,
10.0,
250.0,
(-32.0, 8.0),
ValueScaling::Decibels,
)
.color(Color::rgba(255, 92, 92, 128));
})
.background_color(Color::rgb(16, 16, 16));
}
Loading

0 comments on commit c497447

Please sign in to comment.