Skip to content

Commit

Permalink
added PhaseMeter widget
Browse files Browse the repository at this point in the history
  • Loading branch information
BillyDM committed Jun 15, 2020
1 parent 2f72997 commit d481b08
Show file tree
Hide file tree
Showing 16 changed files with 1,040 additions and 29 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "iced_audio"
version = "0.2.5"
version = "0.2.6"
authors = ["Billy Messenger <https://github.com/BillyDM>"]
license = "MIT"
edition = "2018"
Expand Down Expand Up @@ -29,6 +29,7 @@ name = "simple"
members = [
"examples/db_meter",
"examples/reduction_meter",
"examples/phase_meter",
]

# See more keys and their definitions at
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ An extension to the [Iced] GUI library with useful widgets for audio application
[more screenshots]

## Widgets implemented
### Inputs
* [x] `HSlider` - Horizontal Slider
* [x] `VSlider` - Vertical Slider
* [x] `Ramp` - Ramp used to control the easing between two points in time
* [x] `XYPad`- XY Pad for controlling two parameters at once
* [x] `ModRangeInput` - A dot used to control the range of modulation for a parameter. Styles for visual feedback of the modulation range exist for the `HSlider`, `VSlider`, and `Knob` widgets.
* [x] `DBMeter` - A decibel meter. Note that any DSP and animation must be done manually.
* [x] `ReductionMeter` - a meter that displays the reduction of loudness in a signal. Note that any DSP and animation must be done manually.
### Visualizers
Note that these visualizers do not contain any DSP or animation. That must be done manually.
* [x] `DBMeter` - A decibel meter.
* [x] `ReductionMeter` - a meter that displays the reduction of loudness in a signal.
* [x] `PhaseMeter` - a meter that displays the phase correlation of an audio signal.

## Widgets partially implemented
* [x] `Knob` - A basic knob widget. No texture style yet. There is also a known bug where input will stop when the mouse leaves the window under some conditions.
Expand All @@ -41,6 +45,7 @@ cargo run --example inputs_tour --release
cargo run --example simple --release
cargo run --package db_meter --release
cargo run --package reduction_meter --release
cargo run --package phase_meter --release
```

## Installation
Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* [x] `DBMeter` - a meter that displays peak loudness of a signal. This can have optional colors for good headroom (green), low headroom (yellow), and peaking (red). It can be either vertical or horizontal. It can also have an optional line showing the peak loudness.
* [x] `ModRangeInput` - A dot used to control the range of modulation for a parameter. Styles for visual feedback of the modulation range exist for the `HSlider`, `VSlider`, and `Knob` widgets.
* [x] `ReductionMeter` - a meter that displays the reduction of loudness in a signal. It can be either vertical or horizontal. It can also have an optional line showing the average loudness.
* [ ] `PhaseMeter` - a line that shows the phase correlation of an audio signal. It can be horizontal or vertical.
* [x] `PhaseMeter` - a meter that displays the phase correlation of an audio signal. It can be horizontal or vertical.
* [ ] `EnvelopeEditor` - adjustable points connected by lines that represent automation / envelopes / lfo`s. Lines can be straight or curved. Extra points can be added or removed.
* [ ] `Oscilloscope` - displays oscillations of an audio signal in a given time window
* [ ] `Spectrometer` - displays the amplitude of a range of frequencies from 20hz to 20000hz.
Expand Down
20 changes: 8 additions & 12 deletions examples/db_meter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,18 +192,14 @@ impl Application for DBMeterApp {
let right_peak_knob =
Knob::new(&mut self.right_peak_db_knob_state, Message::ParamMoved);

let db_meter = DBMeter::new(
&mut self.db_meter_state,
)
.tick_marks(&self.tick_marks);

let db_meter_custom = DBMeter::new(
&mut self.db_meter_custom_state,
)
.orientation(db_meter::Orientation::Horizontal)
.height(Length::from(Length::Units(24)))
.tick_marks(&self.tick_marks)
.style(CustomDBMeterStyle);
let db_meter =
DBMeter::new(&mut self.db_meter_state).tick_marks(&self.tick_marks);

let db_meter_custom = DBMeter::new(&mut self.db_meter_custom_state)
.orientation(db_meter::Orientation::Horizontal)
.height(Length::from(Length::Units(24)))
.tick_marks(&self.tick_marks)
.style(CustomDBMeterStyle);

let row = Row::new()
.width(Length::Fill)
Expand Down
14 changes: 14 additions & 0 deletions examples/phase_meter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "phase_meter"
version = "0.1.0"
authors = ["Billy Messenger <[email protected]>"]
edition = "2018"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
iced_audio = { path="../../" }
iced = { version = "0.1", features = ["canvas", "async-std", "debug"] }
iced_native = { version = "0.2" }
async-std = { version = "1.6", features = ["unstable"] }
243 changes: 243 additions & 0 deletions examples/phase_meter/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
use iced::{
executor, Application, Color, Column, Command, Container, Element, Length,
Row, Settings, Subscription, Text,
};

use iced_audio::{
knob, FloatRange, Knob, phase_meter, PhaseMeter,
};

use std::time::Instant;

// Create a unique identifier for each parameter. Note you may also use any
// type you want such as u32, i32, Strings, etc.
#[derive(Debug, Copy, Clone)]
pub enum ParamID {
PhaseKnob,
}

pub fn main() {
PhaseMeterApp::run(Settings {
antialiasing: true,
..Settings::default()
})
}

#[derive(Debug, Clone, Copy)]
enum Message {
Tick(Instant),
ParamMoved(ParamID),
}

struct PhaseMeterApp {
#[allow(dead_code)]
float_range: FloatRange,

phase_knob_state: knob::State<ParamID>,

phase_meter_default_state: phase_meter::State,
phase_meter_custom_state: phase_meter::State,

current: Instant,
}

impl PhaseMeterApp {
pub fn update(&mut self, now: Instant) {
self.current = now;
}
}

impl Application for PhaseMeterApp {
type Executor = executor::Default;
type Message = Message;
type Flags = ();

fn new(_flags: ()) -> (Self, Command<Message>) {
let float_range = FloatRange::default_bipolar();

(
PhaseMeterApp {
float_range,

phase_knob_state: knob::State::new(
float_range.create_param_default(ParamID::PhaseKnob),
),

phase_meter_default_state: phase_meter::State::default(),

phase_meter_custom_state: phase_meter::State::new(
0.5.into(),
phase_meter::TierPositions {
poor: 0.55.into(),
good: 0.45.into(),
},
),

current: Instant::now(),
},
Command::none(),
)
}

fn title(&self) -> String {
String::from("Phase Correlation Meter - Iced Audio")
}

fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Tick(instant) => {
self.update(instant);

// Normally you would animate the meter here, but basic
// knobs are used instead for demonstration.
}
Message::ParamMoved(id) => match id {
ParamID::PhaseKnob => {
let normal = self.phase_knob_state.param.normal;
self.phase_meter_default_state.normal = normal;
self.phase_meter_custom_state.normal = normal;
}
},
}

Command::none()
}

fn subscription(&self) -> Subscription<Message> {
time::every(std::time::Duration::from_millis(10))
.map(|instant| Message::Tick(instant))
}

fn view(&mut self) -> Element<Message> {
let phase_knob =
Knob::new(&mut self.phase_knob_state, Message::ParamMoved);

let phase_meter_default = PhaseMeter::new(&mut self.phase_meter_default_state);

let phase_meter_custom = PhaseMeter::new(&mut self.phase_meter_custom_state)
.orientation(phase_meter::Orientation::Vertical)
.style(CustomPhaseMeterStyle)
.width(Length::from(Length::Units(14)));

let row = Row::new()
.width(Length::Fill)
.height(Length::Fill)
.spacing(20)
.padding(20)
.push(
Column::new()
.max_width(60)
.height(Length::Fill)
.spacing(20)
.push(phase_knob)
.push(Text::new("Phase")),
)
.push(
Column::new()
.width(Length::Fill)
.height(Length::Fill)
.spacing(20)
.push(phase_meter_default),
)
.push(
Column::new()
.max_width(100)
.max_height(400)
.spacing(20)
.push(phase_meter_custom),
);

Container::new(row)
.width(Length::Fill)
.height(Length::Fill)
.center_x()
.center_y()
.into()
}
}

mod time {
use iced::futures;
use std::time::Instant;

pub fn every(duration: std::time::Duration) -> iced::Subscription<Instant> {
iced::Subscription::from_recipe(Every(duration))
}

struct Every(std::time::Duration);

impl<H, I> iced_native::subscription::Recipe<H, I> for Every
where
H: std::hash::Hasher,
{
type Output = Instant;

fn hash(&self, state: &mut H) {
use std::hash::Hash;

std::any::TypeId::of::<Self>().hash(state);
self.0.hash(state);
}

fn stream(
self: Box<Self>,
_input: futures::stream::BoxStream<'static, I>,
) -> futures::stream::BoxStream<'static, Self::Output> {
use futures::stream::StreamExt;

async_std::stream::interval(self.0)
.map(|_| Instant::now())
.boxed()
}
}
}

pub const BACK_COLOR: Color = Color::from_rgb(
0x42 as f32 / 255.0,
0x46 as f32 / 255.0,
0x4D as f32 / 255.0,
);
pub const BORDER_COLOR: Color = Color::from_rgb(
0x30 as f32 / 255.0,
0x33 as f32 / 255.0,
0x3C as f32 / 255.0,
);
pub const BAD_COLOR: Color = Color::from_rgb(
0xFF as f32 / 255.0,
0x94 as f32 / 255.0,
0x91 as f32 / 255.0,
);
pub const POOR_COLOR: Color = Color::from_rgb(
0xFF as f32 / 255.0,
0x94 as f32 / 255.0,
0x91 as f32 / 255.0,
);
pub const OKAY_COLOR: Color = Color::from_rgb(
0x99 as f32 / 255.0,
0x91 as f32 / 255.0,
0xFF as f32 / 255.0,
);
pub const GOOD_COLOR: Color = Color::from_rgb(
0x91 as f32 / 255.0,
0xBD as f32 / 255.0,
0xFF as f32 / 255.0,
);

// Custom style for a `PhaseMeter`

struct CustomPhaseMeterStyle;
impl phase_meter::StyleSheet for CustomPhaseMeterStyle {
fn style(&self) -> phase_meter::Style {
phase_meter::Style {
back_color: BACK_COLOR,
back_border_width: 2,
back_border_color: BORDER_COLOR,
bad_color: BAD_COLOR,
poor_color: POOR_COLOR,
okay_color: OKAY_COLOR,
good_color: GOOD_COLOR,
center_line_width: 2,
center_line_color: Color::WHITE,
}
}
}
29 changes: 21 additions & 8 deletions examples/reduction_meter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,14 @@ impl Application for ReductionMeterApp {
let peak_knob =
Knob::new(&mut self.peak_knob_state, Message::ParamMoved);

let r_meter_default = ReductionMeter::new(&mut self.r_meter_default_state)
.tick_marks(&self.tick_marks);
let r_meter_default =
ReductionMeter::new(&mut self.r_meter_default_state)
.tick_marks(&self.tick_marks);

let r_meter_custom = ReductionMeter::new(&mut self.r_meter_custom_state)
.orientation(reduction_meter::Orientation::Horizontal)
.style(CustomReductionMeterStyle)
let r_meter_custom =
ReductionMeter::new(&mut self.r_meter_custom_state)
.orientation(reduction_meter::Orientation::Horizontal)
.style(CustomReductionMeterStyle)
.tick_marks(&self.tick_marks);

let row = Row::new()
Expand All @@ -195,8 +197,19 @@ impl Application for ReductionMeterApp {
.push(peak_knob)
.push(Text::new("Peak DB")),
)
.push(Column::new().max_height(400).max_width(100).spacing(20).push(r_meter_default))
.push(Column::new().max_height(400).spacing(20).push(r_meter_custom));
.push(
Column::new()
.max_height(400)
.max_width(100)
.spacing(20)
.push(r_meter_default),
)
.push(
Column::new()
.max_height(400)
.spacing(20)
.push(r_meter_custom),
);

Container::new(row)
.width(Length::Fill)
Expand Down Expand Up @@ -292,4 +305,4 @@ impl reduction_meter::StyleSheet for CustomReductionMeterStyle {
placement: reduction_meter::TickMarkPlacement::Right,
})
}
}
}
Loading

0 comments on commit d481b08

Please sign in to comment.