Skip to content

Commit e28ca6b

Browse files
committed
Various quality of life features
1 parent 384f034 commit e28ca6b

File tree

8 files changed

+160
-22
lines changed

8 files changed

+160
-22
lines changed

caw/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,12 @@ pub mod prelude {
7474

7575
#[cfg(feature = "caw_viz_udp_app_lib")]
7676
pub use super::viz_udp_app_lib::*;
77+
78+
/// Shorthand for `Default::default`
79+
pub fn default<T>() -> T
80+
where
81+
T: Default,
82+
{
83+
Default::default()
84+
}
7785
}

core/src/cell.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,25 @@ where
164164
cell(T::default())
165165
}
166166

167-
impl<T> Stereo<Cell<T>, Cell<T>>
167+
pub type StereoCell<T> = Stereo<Cell<T>, Cell<T>>;
168+
pub type StereoCellF32 = StereoCell<f32>;
169+
170+
pub fn stereo_cell_fn<S, F>(mut f: F) -> StereoCell<S::Item>
171+
where
172+
S: SigT + Sync + Send + 'static,
173+
F: FnMut() -> S,
174+
{
175+
Stereo::new_fn(|| cell(f()))
176+
}
177+
178+
pub fn stereo_cell_default<T>() -> StereoCell<T>
179+
where
180+
T: SigT<Item = T> + Clone + Default + Sync + Send + 'static,
181+
{
182+
stereo_cell_fn(T::default)
183+
}
184+
185+
impl<T> StereoCell<T>
168186
where
169187
T: Clone,
170188
{
@@ -183,9 +201,19 @@ where
183201
{
184202
self.as_ref().with_channel(|channel, s| s.set(f(channel)));
185203
}
204+
205+
pub fn set_stereo<U, V>(&self, stereo: Stereo<U, V>)
206+
where
207+
U: SigT<Item = T> + Sync + Send + 'static,
208+
V: SigT<Item = T> + Sync + Send + 'static,
209+
{
210+
let ref_ = self.as_ref();
211+
ref_.left.set(stereo.left);
212+
ref_.right.set(stereo.right);
213+
}
186214
}
187215

188-
impl Stereo<Cell<f32>, Cell<f32>> {
216+
impl StereoCellF32 {
189217
pub fn with_volume<V>(self, volume: V) -> Self
190218
where
191219
V: SigT<Item = f32> + Sync + Send + 'static,

core/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ pub use sig::{
88
sig_shared, variable,
99
};
1010
pub mod cell;
11-
pub use cell::{Cell, CellF32, cell, cell_default, cell_f32};
11+
pub use cell::{
12+
Cell, CellF32, StereoCell, StereoCellF32, cell, cell_default, cell_f32,
13+
stereo_cell_default, stereo_cell_fn,
14+
};
1215
pub mod stereo;
1316
pub use stereo::{Channel, Stereo, StereoPair};

core/src/sig.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,11 @@ where
744744
},
745745
)
746746
}
747+
748+
/// A signal with the same values as `self` but every value is wrapped in `Some`.
749+
pub fn some(self) -> Sig<impl SigT<Item = Option<S::Item>>> {
750+
self.map(Some)
751+
}
747752
}
748753

749754
impl<S, A, B> Sig<S>
@@ -1011,6 +1016,11 @@ where
10111016
self.divide_with_offset(by, 0)
10121017
}
10131018

1019+
/// On a gate or trigger signal, this method returns a signal where each "on" value includes a
1020+
/// count of the number of times the signal has transitioned from off to on. This "counting
1021+
/// gate" can be used to generate beats with divided clocks where the offset of the divided
1022+
/// signal remains fixed, even when a divided signal is "restarted" part-way through a cycle,
1023+
/// such as is the case for live-coding sessions (e.g. in Jupyter).
10141024
pub fn counted(self) -> Sig<impl SigT<Item = Option<u64>>> {
10151025
let mut prev = false;
10161026
let mut count = 0;
@@ -1140,6 +1150,10 @@ where
11401150
{
11411151
self.zip(other).map(|(s, o)| s.or(o))
11421152
}
1153+
1154+
pub fn is_some(self) -> Sig<impl SigT<Item = bool>> {
1155+
self.map(|o| o.is_some())
1156+
}
11431157
}
11441158

11451159
impl<T, S> Sig<S>
@@ -1164,6 +1178,16 @@ where
11641178
})
11651179
.unzip()
11661180
}
1181+
1182+
pub fn or_last_some(self) -> Sig<impl SigT<Item = T>> {
1183+
let mut prev = Default::default();
1184+
self.map_mut(move |o| {
1185+
if let Some(x) = o {
1186+
prev = x;
1187+
}
1188+
prev.clone()
1189+
})
1190+
}
11671191
}
11681192

11691193
pub struct WithBuf<S, F>

core/src/stereo.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::fmt::Display;
1+
use std::{
2+
fmt::Display,
3+
ops::{Add, Mul},
4+
};
25

36
use crate::{Buf, Sig, SigCtx, SigSampleIntoBufT, SigT, sig_ops::sig_add};
47

@@ -220,4 +223,47 @@ impl<S> Stereo<S, S> {
220223
right: f(&self.right),
221224
}
222225
}
226+
227+
pub fn map_pair_channel<O, F>(self, mut f: F) -> Stereo<O, O>
228+
where
229+
F: FnMut(S, Channel) -> O,
230+
{
231+
Stereo {
232+
left: f(self.left, Channel::Left),
233+
right: f(self.right, Channel::Right),
234+
}
235+
}
236+
237+
pub fn map_ref_pair_channel<O, F>(&self, mut f: F) -> Stereo<O, O>
238+
where
239+
F: FnMut(&S, Channel) -> O,
240+
{
241+
Stereo {
242+
left: f(&self.left, Channel::Left),
243+
right: f(&self.right, Channel::Right),
244+
}
245+
}
246+
}
247+
248+
impl<LL, LR, RL, RR> Add<Stereo<RL, RR>> for Stereo<LL, LR>
249+
where
250+
LL: Add<RL>,
251+
LR: Add<RR>,
252+
{
253+
type Output = Stereo<LL::Output, LR::Output>;
254+
fn add(self, rhs: Stereo<RL, RR>) -> Self::Output {
255+
Stereo::new(self.left + rhs.left, self.right + rhs.right)
256+
}
257+
}
258+
259+
impl<L, R, RHS> Mul<RHS> for Stereo<L, R>
260+
where
261+
RHS: Clone,
262+
L: Mul<RHS>,
263+
R: Mul<RHS>,
264+
{
265+
type Output = Stereo<L::Output, R::Output>;
266+
fn mul(self, rhs: RHS) -> Self::Output {
267+
Stereo::new(self.left * rhs.clone(), self.right * rhs)
268+
}
223269
}

keyboard/src/a440_12tet.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,9 @@ pub mod chord {
475475
}
476476
}
477477

478+
/// Sets the 7th note of the chord to be the appropriate note for a major 7th chord unless
479+
/// the chord has a minor 3rd note in which case this method sets the 7th note of the chord
480+
/// to be the appropriate note for a minor 7th chord.
478481
pub const fn infer_7(self) -> Self {
479482
let seventh = Some(match self.third {
480483
Some(Third::Minor) => Seventh::Minor,
@@ -585,13 +588,14 @@ pub mod chord {
585588

586589
#[derive(Clone, Copy, Debug)]
587590
pub enum Inversion {
591+
/// The root note of the chord will be in the specified octave. The notes of the chord will
592+
/// be chosen such that `lowest_position` is the lowest note of the chord.
588593
WithRootOctave {
589594
root_octave: Octave,
590595
lowest_position: ChordPosition,
591596
},
592-
InOctave {
593-
octave_base: Note,
594-
},
597+
/// Play all the notes of the chord within this octave.
598+
InOctave { octave_base: Note },
595599
}
596600

597601
impl Default for Inversion {

keyboard/src/event.rs

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -408,21 +408,41 @@ impl ArpState {
408408
}
409409
}
410410

411-
fn insert_note(&mut self, note: Note) {
411+
fn insert_note(&mut self, note: Note, shape: &ArpShape) {
412412
if let Some(index) = self.store.insert(note) {
413-
if self.index > index {
414-
self.index += 1;
413+
match shape {
414+
ArpShape::Indices(_) => {
415+
// If the shape is a list of indices then the `index` field refers to and index
416+
// into _that_ list rather than the store, so don't touch it.
417+
}
418+
_ => {
419+
// If the note referred to by the current index has moved in the store, adjust
420+
// the index to point to that note again.
421+
if self.index > index {
422+
self.index += 1;
423+
}
424+
}
415425
}
416426
}
417427
}
418-
fn remove_note(&mut self, note: Note) {
428+
fn remove_note(&mut self, note: Note, shape: &ArpShape) {
419429
if let Some(index) = self.store.remove(note) {
420-
if self.index > index {
421-
self.index -= 1;
430+
match shape {
431+
ArpShape::Indices(_) => {
432+
// If the shape is a list of indices then the `index` field refers to and index
433+
// into _that_ list rather than the store, so don't touch it.
434+
}
435+
_ => {
436+
// If the note referred to by the current index has moved in the store, adjust
437+
// the index to point to that note again.
438+
if self.index > index {
439+
self.index -= 1;
440+
}
441+
}
422442
}
423443
}
424444
}
425-
fn reset(&mut self, shape: ArpShape) {
445+
fn reset(&mut self, shape: &ArpShape) {
426446
self.index = 0;
427447
use ArpShape::*;
428448
match shape {
@@ -450,7 +470,7 @@ impl ArpState {
450470
self.index -= 1;
451471
self.store.entries[self.index].note
452472
}
453-
fn tick(&mut self, shape: ArpShape) {
473+
fn tick(&mut self, shape: &ArpShape) {
454474
self.current_note = if self.store.entries.is_empty() {
455475
self.reset(shape);
456476
None
@@ -649,35 +669,35 @@ where
649669
let mut events = KeyEvents::empty();
650670
for event in key_events {
651671
if event.pressed {
652-
state.insert_note(event.note);
672+
state.insert_note(event.note, &shape);
653673
for i in 0..extend_octaves_high {
654674
if let Some(note) =
655675
event.note.add_octaves_checked(i as i8 + 1)
656676
{
657-
state.insert_note(note);
677+
state.insert_note(note, &shape);
658678
}
659679
}
660680
for i in 0..extend_octaves_low {
661681
if let Some(note) =
662682
event.note.add_octaves_checked(-(i as i8 + 1))
663683
{
664-
state.insert_note(note);
684+
state.insert_note(note, &shape);
665685
}
666686
}
667687
} else {
668-
state.remove_note(event.note);
688+
state.remove_note(event.note, &shape);
669689
for i in 0..extend_octaves_high {
670690
if let Some(note) =
671691
event.note.add_octaves_checked(i as i8 + 1)
672692
{
673-
state.remove_note(note);
693+
state.remove_note(note, &shape);
674694
}
675695
}
676696
for i in 0..extend_octaves_low {
677697
if let Some(note) =
678698
event.note.add_octaves_checked(-(i as i8 + 1))
679699
{
680-
state.remove_note(note);
700+
state.remove_note(note, &shape);
681701
}
682702
}
683703
}
@@ -690,7 +710,7 @@ where
690710
velocity_01: 0.0,
691711
});
692712
}
693-
state.tick(shape);
713+
state.tick(&shape);
694714
if let Some(note) = state.current_note {
695715
events.push(KeyEvent {
696716
note,

utils/src/noise.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ pub fn white() -> Sig<impl SigT<Item = f32>> {
66
Sig::from_fn(move |_| rng.random::<f32>() * 2. - 1.)
77
}
88

9+
pub fn white_01() -> Sig<impl SigT<Item = f32>> {
10+
let mut rng = StdRng::from_os_rng();
11+
Sig::from_fn(move |_| rng.random::<f32>())
12+
}
13+
914
/// Approximation of brown noise made by integrating white noise with a leaky integrator and
1015
/// hand-tuning.
1116
pub fn brown() -> Sig<impl SigT<Item = f32>> {

0 commit comments

Comments
 (0)