Skip to content

Commit da3640e

Browse files
committed
Add num_keys_bits_7 widget
1 parent 4b1c65f commit da3640e

File tree

15 files changed

+462
-22
lines changed

15 files changed

+462
-22
lines changed

caw/examples/live_example.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,34 @@ fn main() {
2222
let keyboard = computer_keyboard("keys").start_note(note::B_1).build_();
2323
let space_button = keyboard.space_button().shared();
2424
let key_events = keyboard.key_events().shared();
25-
let length = 16;
25+
let (drum_bits, drum_space) = num_keys_bits_7_with_space("drums").build();
26+
let length = 8;
27+
let drum_bits_loop = value_looper(drum_bits, clock.clone(), drum_space)
28+
.persist_with_name("drums")
29+
.length(length)
30+
.build()
31+
.shared();
32+
let drums = (vec![
33+
clock
34+
.clone()
35+
.and(drum_bits_loop.clone().map(|x| x & (1 << 0) != 0))
36+
.trig(drum::kick())
37+
.boxed(),
38+
clock
39+
.clone()
40+
.and(drum_bits_loop.clone().map(|x| x & (1 << 1) != 0))
41+
.trig(drum::snare())
42+
.boxed(),
43+
clock
44+
.clone()
45+
.and(drum_bits_loop.clone().map(|x| x & (1 << 2) != 0))
46+
.trig(drum::hat_closed())
47+
.boxed(),
48+
]
49+
.into_iter()
50+
.sum::<Sig<_>>()
51+
* knob("drum vol").build())
52+
.shared();
2653
out.set_channel(|channel| {
2754
let voice = key_events.clone().mono_voice();
2855
let (note, gate) = key_looper(voice.triggered_note(), clock.clone())
@@ -82,6 +109,7 @@ fn main() {
82109
.feedback_ratio(0.5),
83110
)
84111
.filter(reverb::default().room_size(0.9).damping(0.1))
112+
+ drums.clone()
85113
});
86114
thread::park();
87115
}

core/src/sig.rs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ where
773773
}
774774
}
775775

776-
pub fn sig_boxed<S>(sig: S) -> SigBoxed<S::Item>
776+
pub fn sig_boxed<S>(sig: S) -> Sig<SigBoxed<S::Item>>
777777
where
778778
S: SigT + Send + Sync + 'static,
779779
{
@@ -784,11 +784,11 @@ impl<S> Sig<S>
784784
where
785785
S: SigT + Send + Sync + 'static,
786786
{
787-
pub fn boxed(self) -> SigBoxed<S::Item> {
788-
SigBoxed {
787+
pub fn boxed(self) -> Sig<SigBoxed<S::Item>> {
788+
Sig(SigBoxed {
789789
sig: Arc::new(RwLock::new(self)),
790790
buf: Vec::new(),
791-
}
791+
})
792792
}
793793
}
794794

@@ -987,6 +987,13 @@ where
987987
{
988988
self.zip(other).map(|(s, o)| s || o)
989989
}
990+
991+
pub fn and<O>(self, other: O) -> Sig<impl SigT<Item = bool>>
992+
where
993+
O: SigT<Item = bool>,
994+
{
995+
self.zip(other).map(|(s, o)| s && o)
996+
}
990997
}
991998

992999
/// A counting gate, which is a gate which implements a counter each time it transitions to "on".
@@ -1658,7 +1665,7 @@ impl<T: Clone> SigBoxedVarUnshared<T> {
16581665
S: SigT<Item = T> + Sync + Send + 'static,
16591666
{
16601667
SigBoxedVarUnshared {
1661-
sig_boxed: Arc::new(RwLock::new(sig_boxed(sig))),
1668+
sig_boxed: Arc::new(RwLock::new(sig_boxed(sig).0)),
16621669
buf: Vec::new(),
16631670
}
16641671
}
@@ -1667,7 +1674,7 @@ impl<T: Clone> SigBoxedVarUnshared<T> {
16671674
where
16681675
S: SigT<Item = T> + Sync + Send + 'static,
16691676
{
1670-
*self.sig_boxed.write().unwrap() = sig_boxed(sig);
1677+
*self.sig_boxed.write().unwrap() = sig_boxed(sig).0;
16711678
}
16721679
}
16731680

@@ -1686,7 +1693,7 @@ where
16861693
{
16871694
pub fn new_const(value: T) -> Self {
16881695
SigBoxedVarUnshared {
1689-
sig_boxed: Arc::new(RwLock::new(sig_boxed(Const(value)))),
1696+
sig_boxed: Arc::new(RwLock::new(sig_boxed(Const(value)).0)),
16901697
buf: Vec::new(),
16911698
}
16921699
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn new_button(title: String) -> Sig<SigShared<Button>> {
2424
})
2525
}
2626

27-
mod button_builder {
27+
mod builder {
2828
use super::*;
2929
use caw_builder_proc_macros::builder;
3030
use caw_core::{Sig, SigShared};
@@ -45,4 +45,4 @@ mod button_builder {
4545
}
4646
}
4747

48-
pub use button_builder::button;
48+
pub use builder::button;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ impl ComputerKeyboard {
4444
}
4545
}
4646

47-
mod computer_keyboard_builder {
47+
mod builder {
4848
use super::*;
4949
use caw_builder_proc_macros::builder;
5050
use caw_core::{Sig, SigShared};
@@ -75,4 +75,4 @@ mod computer_keyboard_builder {
7575
}
7676
}
7777

78-
pub use computer_keyboard_builder::computer_keyboard;
78+
pub use builder::computer_keyboard;

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use crate::{
22
midi,
3-
widget::{ByTitle, MidiController01Udp, Widget},
3+
widget::{ByTitle, MidiController01Udp, MidiControllerBoolUdp, Widget},
44
};
5-
use caw_core::{IsPositive, Sig, SigShared};
5+
use caw_core::{Sig, SigShared};
66
use caw_midi::MidiMessagesT;
77
use lazy_static::lazy_static;
88

99
pub type KnobWithSpace = (
1010
Sig<SigShared<MidiController01Udp>>,
11-
Sig<SigShared<IsPositive<MidiController01Udp>>>,
11+
Sig<SigShared<MidiControllerBoolUdp>>,
1212
);
1313

1414
lazy_static! {
@@ -45,10 +45,7 @@ fn new_knob_with_space(
4545
let value = controllers
4646
.get_with_initial_value_01(controller.into(), initial_value_01)
4747
.shared();
48-
let space = controllers
49-
.get_with_initial_value_01(space_controller.into(), 0.0)
50-
.is_positive()
51-
.shared();
48+
let space = controllers.get_bool(space_controller.into()).shared();
5249
(value, space)
5350
})
5451
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ pub use xy::{xy, xy_with_space};
1010
pub mod computer_keyboard;
1111
pub use computer_keyboard::computer_keyboard;
1212

13+
pub mod num_keys_bit_7;
14+
pub use num_keys_bit_7::{num_keys_bits_7, num_keys_bits_7_with_space};
15+
1316
mod midi;
1417
mod server;
1518
mod widget;
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use crate::{
2+
midi,
3+
widget::{ByTitle, MidiControllerBoolUdp, MidiControllerU7Udp, Widget},
4+
};
5+
use caw_core::{Sig, SigShared};
6+
use caw_midi::MidiMessagesT;
7+
use lazy_static::lazy_static;
8+
9+
pub type NumKeysBits7WithSpace = (
10+
Sig<SigShared<MidiControllerU7Udp>>,
11+
Sig<SigShared<MidiControllerBoolUdp>>,
12+
);
13+
14+
lazy_static! {
15+
// Used to prevent multiple windows from opening at the same time with the same name. Note that
16+
// when using with evcxr this seems to get cleared when a cell is recomputed, but this is still
17+
// valuable in the context of stereo signals, where a function is evaluated once for the left
18+
// channel and once for the right channel. In such a case, this prevents each knob openned by
19+
// that function from being openned twice.
20+
static ref BY_TITLE: ByTitle<NumKeysBits7WithSpace> = Default::default();
21+
}
22+
23+
fn new_num_keys_bits_7_with_space(title: String) -> NumKeysBits7WithSpace {
24+
BY_TITLE.get_or_insert(title.as_str(), || {
25+
let channel = midi::alloc_channel();
26+
let controller = midi::alloc_controller(channel);
27+
let space_controller = midi::alloc_controller(channel);
28+
let widget = Widget::new(
29+
title.clone(),
30+
channel,
31+
"num-keys-bits7",
32+
vec![
33+
format!("--controller={}", controller),
34+
format!("--space-controller={}", space_controller),
35+
],
36+
)
37+
.unwrap();
38+
let controllers = widget.channel().controllers();
39+
let value = controllers.get_u7(controller.into()).shared();
40+
let space = controllers.get_bool(space_controller.into()).shared();
41+
(value, space)
42+
})
43+
}
44+
45+
fn new_num_keys_bits_7(title: String) -> Sig<SigShared<MidiControllerU7Udp>> {
46+
new_num_keys_bits_7_with_space(title).0
47+
}
48+
49+
mod builder {
50+
use super::*;
51+
use caw_builder_proc_macros::builder;
52+
use caw_core::{Sig, SigShared};
53+
54+
builder! {
55+
#[constructor = "num_keys_bits_7_"]
56+
#[constructor_doc = "A 7-bit integer representing the state of the first 7 number keys"]
57+
#[generic_setter_type_name = "X"]
58+
#[build_fn = "new_num_keys_bits_7"]
59+
#[build_ty = "Sig<SigShared<MidiControllerU7Udp>>"]
60+
pub struct Props {
61+
title: String,
62+
}
63+
}
64+
65+
pub fn num_keys_bits_7(title: impl Into<String>) -> Props {
66+
num_keys_bits_7_(title.into())
67+
}
68+
69+
builder! {
70+
#[constructor = "num_keys_bits_7_with_space_"]
71+
#[constructor_doc = "A 7-bit integer representing the state of the first 7 number keys"]
72+
#[generic_setter_type_name = "X"]
73+
#[build_fn = "new_num_keys_bits_7_with_space"]
74+
#[build_ty = "NumKeysBits7WithSpace"]
75+
pub struct WithSpaceProps {
76+
title: String,
77+
}
78+
}
79+
80+
pub fn num_keys_bits_7_with_space(
81+
title: impl Into<String>,
82+
) -> WithSpaceProps {
83+
num_keys_bits_7_with_space_(title.into())
84+
}
85+
}
86+
87+
pub use builder::{num_keys_bits_7, num_keys_bits_7_with_space};

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::server::{self, MidiChannelUdp};
22
use caw_core::Sig;
3-
use caw_midi::MidiController01;
3+
use caw_midi::{MidiController01, MidiControllerBool, MidiControllerU7};
44
use midly::num::u4;
55
use std::{collections::HashMap, process::Command, sync::Mutex};
66

@@ -73,3 +73,5 @@ impl<T: Clone> ByTitle<T> {
7373
}
7474

7575
pub type MidiController01Udp = MidiController01<MidiChannelUdp>;
76+
pub type MidiControllerU7Udp = MidiControllerU7<MidiChannelUdp>;
77+
pub type MidiControllerBoolUdp = MidiControllerBool<MidiChannelUdp>;

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

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use std::time::{Duration, Instant};
77
use caw_keyboard::{Note, note};
88
use caw_midi::MidiEvent;
99
use caw_midi_udp_client::*;
10-
use caw_widgets::{AxisLabels, Button, ComputerKeyboard, Knob, Xy};
10+
use caw_widgets::{
11+
AxisLabels, Button, ComputerKeyboard, Knob, NumKeysBits7, Xy,
12+
};
1113
use clap::{Parser, Subcommand};
1214
use midly::num::u7;
1315

@@ -40,6 +42,12 @@ enum Command {
4042
#[arg(long, default_value_t = note::B_2)]
4143
start_note: Note,
4244
},
45+
NumKeysBits7 {
46+
#[arg(short, long, default_value_t = 0)]
47+
controller: u8,
48+
#[arg(long)]
49+
space_controller: Option<u8>,
50+
},
4351
}
4452

4553
#[derive(Parser)]
@@ -226,5 +234,54 @@ fn main() {
226234
}
227235
}
228236
}
237+
Command::NumKeysBits7 {
238+
controller,
239+
space_controller,
240+
} => {
241+
let mut num_keys_bits_7 =
242+
NumKeysBits7::new(cli.title.as_deref()).unwrap();
243+
num_keys_bits_7.tick().unwrap();
244+
let send = |value| {
245+
client
246+
.send(MidiEvent {
247+
channel,
248+
message: MidiMessage::Controller {
249+
controller: controller.into(),
250+
value,
251+
},
252+
})
253+
.unwrap()
254+
};
255+
let mut prev = num_keys_bits_7.value();
256+
let mut prev_space = false;
257+
loop {
258+
num_keys_bits_7.tick().unwrap();
259+
let value = num_keys_bits_7.value();
260+
if is_spam() || value != prev {
261+
send(value);
262+
prev = value;
263+
}
264+
let space = num_keys_bits_7.is_space_pressed();
265+
if let Some(space_controller) = space_controller {
266+
if is_spam() || space != prev_space {
267+
client
268+
.send(MidiEvent {
269+
channel,
270+
message: MidiMessage::Controller {
271+
controller: space_controller.into(),
272+
value: if space {
273+
u7::max_value()
274+
} else {
275+
0.into()
276+
},
277+
},
278+
})
279+
.unwrap();
280+
281+
prev_space = space;
282+
}
283+
}
284+
}
285+
}
229286
}
230287
}

0 commit comments

Comments
 (0)