Skip to content

Commit 1efe846

Browse files
committed
Switch widget
1 parent 21f5f78 commit 1efe846

File tree

9 files changed

+328
-8
lines changed

9 files changed

+328
-8
lines changed

caw/examples/live_example.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ fn main() {
5050
]
5151
.into_iter()
5252
.sum::<Sig<_>>()
53-
* knob("drum vol").build())
54-
.shared();
53+
* 0.5) //knob("drum vol").build())
54+
.shared();
5555
out.set_channel(|channel| {
5656
let voice = key_events.clone().mono_voice();
5757
let (note, gate) = key_looper(voice.triggered_note(), clock.clone())
@@ -60,6 +60,12 @@ fn main() {
6060
.persist_with_name("keys")
6161
.build()
6262
.ungated();
63+
let vib_hz = knob("vibrato").build() * 20.;
64+
let max = semitone_ratio_sig(knob("vib scale").build() * 4.).shared();
65+
let min = (1.0 / max.clone()).shared();
66+
let vib = sine(vib_hz).build().signed_to_range(min, max);
67+
let lfo =
68+
sine(10.0).build().signed_to_01() * knob("lfo").build() * 1000.;
6369
let cutoff_hz =
6470
value_looper(cutoff_hz.clone(), clock.clone(), lpf_space.clone())
6571
.persist_with_name("low_pass")
@@ -68,27 +74,27 @@ fn main() {
6874
let env = adsr(gate)
6975
.key_press_trig(clock.clone())
7076
.a(tempo_s.clone() * knob("attack").build() * 4.)
71-
.r(tempo_s.clone() * knob("release").build() * 4.)
77+
// .r(tempo_s.clone() * knob("release").build() * 4.)
7278
.s(knob("sustain").build())
7379
.d(tempo_s.clone() * knob("decay").build() * 4.)
7480
.build()
7581
.exp_01(1.)
7682
.shared();
77-
let voice = (saw(note.freq_hz())
83+
let voice = (saw(note.freq_hz() * vib)
7884
.reset_offset_01(channel.circle_phase_offset_01())
7985
.build()
8086
* env.clone())
8187
.filter(
8288
low_pass::default(
83-
10. + (env.clone()
89+
lfo + (env.clone()
8490
* cutoff_hz
8591
* 15000.
8692
* knob("cutoff_scale").build()),
8793
)
8894
.q(res.clone()),
8995
)
9096
.filter_enable(
91-
true,
97+
switch("band pass").build(),
9298
band_pass::centered::default(
9399
knob("mid").build() * 1_000.,
94100
knob("width").build() * 4.,
@@ -111,6 +117,7 @@ fn main() {
111117
.feedback_ratio(0.5),
112118
)
113119
.filter(reverb::default().room_size(0.9).damping(0.1))
120+
.filter(high_pass::default(1.))
114121
+ drums.clone()
115122
});
116123
thread::park();

core/src/sig.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,20 @@ where
875875
Sig(SignedTo01(self.0))
876876
}
877877

878+
pub fn signed_to_range<L, U>(
879+
self,
880+
lower: L,
881+
upper: U,
882+
) -> Sig<impl SigT<Item = f32>>
883+
where
884+
U: SigT<Item = f32>,
885+
L: SigT<Item = f32>,
886+
{
887+
let lower = sig_shared(lower);
888+
let delta = Sig(upper) - lower.clone();
889+
lower + (self.signed_to_01() * delta)
890+
}
891+
878892
pub fn abs(self) -> Sig<SigAbs<S>> {
879893
Sig(SigAbs(self.0))
880894
}

keyboard/src/a440_12tet.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,22 @@ pub fn freq_hz_of_midi_index(midi_index: u8) -> f32 {
209209
))
210210
}
211211

212+
/// The ratio between a pair of notes one semitone apart:
213+
/// 2.0.powf(1.0 / 12.0)
214+
pub const SEMITONE_RATIO: f32 = 1.0594630943592953;
215+
216+
/// The ratio between a pair of notes one tone (ie. two semitones) apart.
217+
pub const TONE_RATIO: f32 = SEMITONE_RATIO * SEMITONE_RATIO;
218+
212219
pub fn semitone_ratio(num_semitones: f32) -> f32 {
213220
2.0_f32.powf(num_semitones / (NOTES_PER_OCTAVE as f32))
214221
}
215222

216-
pub const TONE_RATIO: f32 = 1.122_462;
223+
pub fn semitone_ratio_sig(
224+
num_semitones: impl SigT<Item = f32>,
225+
) -> Sig<impl SigT<Item = f32>> {
226+
Sig(num_semitones).map(semitone_ratio)
227+
}
217228

218229
/// Definition of notes based on MIDI tuned to A_440
219230
#[derive(

midi-udp-widgets-app-lib/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
pub mod button;
22
pub use button::button;
33

4+
pub mod switch;
5+
pub use switch::switch;
6+
47
pub mod knob;
58
pub use knob::{knob, knob_with_space};
69

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use crate::{
2+
midi,
3+
server::MidiChannelUdp,
4+
widget::{ByTitle, Widget},
5+
};
6+
use caw_core::{Sig, SigShared, sig_shared};
7+
use caw_keyboard::Note;
8+
use caw_midi::{MidiKeyPress, MidiMessagesT};
9+
use lazy_static::lazy_static;
10+
11+
pub type Switch = MidiKeyPress<MidiChannelUdp>;
12+
13+
lazy_static! {
14+
static ref BY_TITLE: ByTitle<Sig<SigShared<Switch>>> = Default::default();
15+
}
16+
17+
fn new_switch(title: String) -> Sig<SigShared<Switch>> {
18+
BY_TITLE.get_or_insert(title.as_str(), || {
19+
let widget =
20+
Widget::new(title.clone(), midi::alloc_channel(), "switch", vec![])
21+
.unwrap();
22+
// The choice of note here is arbitrary.
23+
sig_shared(widget.channel().key_press(Note::default()))
24+
})
25+
}
26+
27+
mod builder {
28+
use super::*;
29+
use caw_builder_proc_macros::builder;
30+
use caw_core::{Sig, SigShared};
31+
32+
builder! {
33+
#[constructor = "switch_"]
34+
#[constructor_doc = "A visual switch in a new window"]
35+
#[generic_setter_type_name = "X"]
36+
#[build_fn = "new_switch"]
37+
#[build_ty = "Sig<SigShared<Switch>>"]
38+
pub struct Props {
39+
title: String,
40+
}
41+
}
42+
43+
pub fn switch(title: impl Into<String>) -> Props {
44+
switch_(title.into())
45+
}
46+
}
47+
48+
pub use builder::switch;

midi-udp-widgets-app/src/main.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ use caw_keyboard::{Note, note};
88
use caw_midi::MidiEvent;
99
use caw_midi_udp_client::*;
1010
use caw_widgets::{
11-
AxisLabels, Button, ComputerKeyboard, Knob, NumKeysBits7, Xy,
11+
AxisLabels, Button, ComputerKeyboard, Knob, NumKeysBits7, Switch, Xy,
1212
};
1313
use clap::{Parser, Subcommand};
1414
use midly::num::u7;
1515

1616
#[derive(Subcommand)]
1717
enum Command {
1818
Button,
19+
Switch,
1920
Knob {
2021
#[arg(short, long, default_value_t = 0)]
2122
controller: u8,
@@ -97,6 +98,24 @@ fn main() {
9798
}
9899
}
99100
}
101+
Command::Switch => {
102+
let mut button = Switch::new(cli.title.as_deref()).unwrap();
103+
let mut prev_pressed = false;
104+
loop {
105+
button.tick().unwrap();
106+
let pressed = button.pressed();
107+
if is_spam() || pressed != prev_pressed {
108+
let key = Note::default().to_midi_index().into();
109+
let message = if button.pressed() {
110+
MidiMessage::NoteOn { key, vel: 0.into() }
111+
} else {
112+
MidiMessage::NoteOff { key, vel: 0.into() }
113+
};
114+
client.send(MidiEvent { channel, message }).unwrap();
115+
prev_pressed = pressed;
116+
}
117+
}
118+
}
100119
Command::Knob {
101120
controller,
102121
initial_value,

widgets/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ documentation = "https://docs.rs/caw_widgets"
1313
anyhow = "1.0"
1414
sdl2 = { version = "0.38", features = ["gfx"] }
1515
line_2d = "0.5"
16+
coord_2d = "0.3"
1617
midly = "0.5"
1718
caw_computer_keyboard = { version = "0.4", path = "../computer-keyboard" }
1819
caw_keyboard = { version = "0.4", path = "../keyboard" }

widgets/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ mod button;
77
mod computer_keyboard;
88
mod knob;
99
mod num_keys_bits_7;
10+
mod switch;
1011
mod window;
1112
mod xy;
1213

1314
pub use button::*;
1415
pub use computer_keyboard::*;
1516
pub use knob::*;
1617
pub use num_keys_bits_7::*;
18+
pub use switch::*;
1719
pub use xy::*;

0 commit comments

Comments
 (0)