Documentation | Book | Examples | Contributing
Composable views and associated data structures for nih-plug UIs made using VIZIA.
CYMA is a collection of flexible, composable views that you can use to make rich 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)
2025-01-16.22-57-38.mp4
Wanna see the code behind this? It's this example!
Tip
A version of Cyma that experimentally supports vizia-plug
instead of nih-plug-vizia
is in the works.
You can check it out on the vizia-plug
branch.
Do you think something's missing? File a feature request so it can be added!
General/Utility
- Grid backdrop
- Unit ruler
Peak/Waveform Analysis
- Meter
- Graph
- Oscilloscope
- Static waveform
Stereo imaging
- Lissajous
Spectral analysis
- Spectrum Analyzer
Here's how to create a basic oscilloscope.
Visualizers communicate with your plugin via busses. One bus can feed multiple visualizers. Just add it to your plugin like so:
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(),
)
}
...
}
Now, in your editor code, you just need to subscribe to the bus. Then, you can use it for visualizers like this oscilloscope:
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));
})
}
Check out the book or the examples to familiarize yourself with this system.
A core feature of CYMA is composability.
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.
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));
});
If you want to contribute through issues and code contributions, read the Contributing Guidelines first.
This project is licensed under the MPL.