Iced Audio
An extension to the Iced GUI library with useful widgets for audio applications such as VST / LV2 plugins.
more screenshots
Widgets implemented
Widgets partially implemented
Take a look at the roadmap for a list of planned widgets.
Each parameter can be mapped to one of four ranges:
FloatRange
- a linear range of f32 values
IntRange
- a discrete range of i32 values. This will cause the widget to "step" when moved.
DBRange
- a logarithmic range of decibel values. Values around 0 dB will increment slower than values farther away from 0 dB.
FreqRange
- a logarithmic range of frequency values. Each octave in the 10 octave spectrum (from 20 Hz to 20480 Hz) is spaced evenly.
Run examples with
cargo run --example basic_inputs --release
cargo run --example simple --release
cargo run --package db_meter --release
Installation
Add iced
and iced_audio
as dependencies in your Cargo.toml
:
iced = { version = "0.1", features = ["image"] }
iced_audio = "0.2"
Both Iced Audio and Iced move fast and the master
branch can contain breaking changes! If
you want to learn about a specific release, check out the release list.
Simple Usage Example
This crate assumes you know the basics of how to use Iced. If you haven't alreay, please check it out here.
use iced::{
Align, Column, Container, Element, Length, Sandbox, Settings, Text,
};
use iced_audio::{
h_slider, knob, v_slider, xy_pad, DBRange, FloatRange, FreqRange, HSlider,
IntRange, Knob, TickMark, TickMarkGroup, TickMarkTier, VSlider, XYPad,
};
#[derive(Debug, Copy, Clone)]
pub enum ParamID {
HSliderInt,
VSliderDB,
KnobFreq,
XYPadFloatX,
XYPadFloatY,
}
#[derive(Debug, Clone)]
pub enum Message {
ParamMoved(ParamID),
}
pub fn main() {
App::run(Settings::default())
}
pub struct App {
float_range: FloatRange,
int_range: IntRange,
db_range: DBRange,
freq_range: FreqRange,
h_slider_state: h_slider::State<ParamID>,
v_slider_state: v_slider::State<ParamID>,
knob_state: knob::State<ParamID>,
xy_pad_state: xy_pad::State<ParamID>,
center_tick_mark: TickMarkGroup,
output_text: String,
}
impl Sandbox for App {
type Message = Message;
fn new() -> App {
let float_range = FloatRange::default_bipolar();
let int_range = IntRange::new(0, 10);
let db_range = DBRange::new(-12.0, 12.0, 0.5.into());
let freq_range = FreqRange::default();
App {
float_range,
int_range,
db_range,
freq_range,
h_slider_state: h_slider::State::new(int_range.create_param(
ParamID::HSliderInt,
5,
5,
)),
v_slider_state: v_slider::State::new(
db_range.create_param_default(ParamID::VSliderDB),
),
knob_state: knob::State::new(freq_range.create_param(
ParamID::KnobFreq,
1000.0,
1000.0,
)),
xy_pad_state: xy_pad::State::new(
float_range.create_param_default(ParamID::XYPadFloatX),
float_range.create_param_default(ParamID::XYPadFloatY),
),
center_tick_mark: vec![TickMark::center(TickMarkTier::Two)].into(),
output_text: "Move a widget!".into(),
}
}
fn title(&self) -> String {
format!("Simple Example - Iced Audio")
}
fn update(&mut self, event: Message) {
match event {
Message::ParamMoved(id) => {
match id {
ParamID::HSliderInt => {
self.int_range
.snap_normal(&mut self.h_slider_state.param.normal);
let value = self
.int_range
.to_value(self.h_slider_state.param.normal);
self.output_text = format!("{:?}: {}", id, value);
}
ParamID::VSliderDB => {
let value = self
.db_range
.to_value(self.v_slider_state.param.normal);
self.output_text = format!("{:?}: {:.3}", id, value);
}
ParamID::KnobFreq => {
let value = self
.freq_range
.to_value(self.knob_state.param.normal);
self.output_text = format!("{:?}: {:.3}", id, value);
}
ParamID::XYPadFloatX => {
let value = self
.float_range
.to_value(self.xy_pad_state.param_x.normal);
self.output_text = format!("{:?}: {:.3}", id, value);
}
ParamID::XYPadFloatY => {
let value = self
.float_range
.to_value(self.xy_pad_state.param_y.normal);
self.output_text = format!("{:?}: {:.3}", id, value);
}
}
}
}
}
fn view(&mut self) -> Element<Message> {
let h_slider_widget =
HSlider::new(&mut self.h_slider_state, Message::ParamMoved)
.tick_marks(&self.center_tick_mark);
let v_slider_widget =
VSlider::new(&mut self.v_slider_state, Message::ParamMoved)
.tick_marks(&self.center_tick_mark);
let knob_widget = Knob::new(&mut self.knob_state, Message::ParamMoved);
let xy_pad_widget =
XYPad::new(&mut self.xy_pad_state, Message::ParamMoved);
let content: Element<_> = Column::new()
.max_width(250)
.max_height(400)
.spacing(20)
.padding(20)
.align_items(Align::Center)
.push(h_slider_widget)
.push(v_slider_widget)
.push(knob_widget)
.push(xy_pad_widget)
.push(
Container::new(Text::new(&self.output_text))
.width(Length::Fill),
)
.into();
Container::new(content)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into()
}
}
Contributing / Feedback
Contributions are greatly appreciated! If you want to contribute, please
read the official Iced contributing guidelines for more details.
Feedback is also welcome! You can open an issue or, if you want to talk,
come chat to our Zulip server. Moreover, you can find me (and a bunch of
awesome folks) over the #gui-and-ui
channels in
the Rust Community Discord. I go by BillyDM#3892
there.