Skip to content

Commit

Permalink
Get MiniSequencer sequencing. WIP.
Browse files Browse the repository at this point in the history
- Introduce Serializable trait, which I think will solve the problem of
ephemeral struct fields getting reconstituted when we deserialize a
Serde struct. (Note serde-rs/serde#642, which could
eventually supersede this stuff.)
- Added a few more hardcoded [MidiNote]s.
- More [MusicalTime] math ops.
  • Loading branch information
sowbug committed Jul 11, 2023
1 parent 78b3606 commit 49b845a
Show file tree
Hide file tree
Showing 39 changed files with 428 additions and 100 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ version = "0.1.0"
anyhow = "1.0"
async-std = "1.0"
atomic-counter = "1.0.1"
btreemultimap = "0.1.1"
clap = { version = "4.0", features = ["derive"] }
crossbeam-channel = { version = "0.5", optional = true }
derive_more = "0.99"
Expand Down
5 changes: 5 additions & 0 deletions core/src/midi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,15 @@ pub enum MidiNote {
D3 = 50,
#[default]
C4 = 60,
D4 = 62,
E4 = 64,
F4 = 65,
G4 = 67,
A4 = 69,
B4 = 71,
C5 = 72,
D5 = 74,
E5 = 76,
D6 = 86,
G9 = 127,
}
Expand Down
50 changes: 39 additions & 11 deletions core/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use groove_proc_macros::{Control, Params, Uid};
use std::{
cmp::Ordering,
fmt::Display,
ops::{Add, AddAssign, Mul, Range},
ops::{Add, AddAssign, Mul, Range, Sub},
};
use strum_macros::{FromRepr, IntoStaticStr};

Expand Down Expand Up @@ -538,6 +538,15 @@ impl MusicalTime {
pub const UNITS_IN_PART: u64 = 4096;
pub const UNITS_IN_BEAT: u64 = Self::PARTS_IN_BEAT * Self::UNITS_IN_PART;

pub const DURATION_WHOLE: MusicalTime = Self::new_with_beats(1);
pub const DURATION_HALF: MusicalTime = Self::new_with_parts(8);
pub const DURATION_QUARTER: MusicalTime = Self::new_with_parts(4);
pub const DURATION_EIGHTH: MusicalTime = Self::new_with_parts(2);
pub const DURATION_SIXTEENTH: MusicalTime = Self::new_with_parts(1);
pub const TIME_ZERO: MusicalTime = Self::new_with_units(0);
pub const TIME_END_OF_FIRST_BEAT: MusicalTime = Self::new_with_beats(1);
pub const TIME_MAX: MusicalTime = Self::new_with_units(u64::MAX);

pub const START: MusicalTime = MusicalTime { units: 0 };

pub fn new(
Expand Down Expand Up @@ -667,18 +676,10 @@ impl MusicalTime {
(frames_per_beat * (units as f64 / Self::UNITS_IN_BEAT as f64) + 0.5) as usize
}

pub const fn start_of_time() -> Self {
Self { units: u64::MIN }
}

pub const fn end_of_time() -> Self {
Self { units: u64::MAX }
}

pub fn end_of_time_range() -> Range<Self> {
Range {
start: Self::end_of_time(),
end: Self::end_of_time(),
start: Self::TIME_MAX,
end: Self::TIME_MAX,
}
}

Expand Down Expand Up @@ -708,11 +709,38 @@ impl Add<Self> for MusicalTime {
}
}
}
impl Add<u64> for MusicalTime {
type Output = Self;

fn add(self, rhs: u64) -> Self::Output {
Self {
units: self.units + rhs,
}
}
}
impl AddAssign<Self> for MusicalTime {
fn add_assign(&mut self, rhs: Self) {
self.units += rhs.units;
}
}
impl Mul<u64> for MusicalTime {
type Output = Self;

fn mul(self, rhs: u64) -> Self::Output {
Self {
units: self.units * rhs,
}
}
}
impl Sub<Self> for MusicalTime {
type Output = Self;

fn sub(self, rhs: Self) -> Self::Output {
Self {
units: self.units - rhs.units,
}
}
}
impl From<PerfectTimeUnit> for MusicalTime {
fn from(value: PerfectTimeUnit) -> Self {
// TODO: this is not exactly right, but we need it just long enough to
Expand Down
9 changes: 8 additions & 1 deletion core/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,15 @@ pub trait Performs {
fn set_loop_enabled(&mut self, is_enabled: bool) {}
}

/// Something that is [Serializable] might need to do work right before
/// serialization, or right after deserialization. These are the hooks.
pub trait Serializable {
fn before_ser(&mut self) {}
fn after_deser(&mut self) {}
}

#[typetag::serde(tag = "type")]
pub trait Thing: HasUid + Shows + Configurable + std::fmt::Debug + Send {
pub trait Thing: HasUid + Shows + Configurable + Serializable + std::fmt::Debug + Send {
fn as_controller(&self) -> Option<&dyn IsController> {
None
}
Expand Down
3 changes: 2 additions & 1 deletion entities/src/controllers/arpeggiator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{sequencers::Sequencer, SequencerParams};
use groove_core::{
midi::{new_note_off, new_note_on, HandlesMidi, MidiChannel, MidiMessage, MidiMessagesFn},
time::{MusicalTime, PerfectTimeUnit, SampleRate},
traits::{Configurable, ControlEventsFn, Controls, Performs},
traits::{Configurable, ControlEventsFn, Controls, Performs, Serializable},
ParameterType,
};
use groove_proc_macros::{Control, IsController, Params, Uid};
Expand Down Expand Up @@ -37,6 +37,7 @@ pub struct Arpeggiator {
// arpeggiator would frequently get clipped.
note_semaphore: i16,
}
impl Serializable for Arpeggiator {}
impl Configurable for Arpeggiator {
fn update_sample_rate(&mut self, sample_rate: SampleRate) {
self.sequencer.update_sample_rate(sample_rate);
Expand Down
6 changes: 5 additions & 1 deletion entities/src/controllers/calculator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ use groove_core::{
instruments::Synthesizer,
midi::{note_to_frequency, MidiChannel, MidiMessage, MidiMessagesFn},
time::{Clock, ClockParams, MusicalTime, PerfectTimeUnit, SampleRate, TimeSignatureParams},
traits::{Configurable, ControlEventsFn, Controls, Generates, HandlesMidi, Performs, Ticks},
traits::{
Configurable, ControlEventsFn, Controls, Generates, HandlesMidi, Performs, Serializable,
Ticks,
},
voices::VoicePerNoteStore,
ParameterType, StereoSample,
};
Expand Down Expand Up @@ -463,6 +466,7 @@ pub struct Calculator {
#[cfg_attr(feature = "serialization", serde(skip))]
last_handled_step: usize,
}
impl Serializable for Calculator {}
impl Performs for Calculator {
fn play(&mut self) {
// We don't have resume, so play always skips to start.
Expand Down
3 changes: 2 additions & 1 deletion entities/src/controllers/control_trip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use groove_core::{
BeatValue, Clock, ClockParams, ClockTimeUnit, MusicalTime, PerfectTimeUnit, SampleRate,
TimeSignature, TimeSignatureParams,
},
traits::{Configurable, ControlEventsFn, Controls, Performs},
traits::{Configurable, ControlEventsFn, Controls, Performs, Serializable},
ParameterType, SignalType,
};
use groove_proc_macros::{Control, IsController, Params, Uid};
Expand Down Expand Up @@ -68,6 +68,7 @@ pub struct ControlTrip {
is_finished: bool,
is_performing: bool,
}
impl Serializable for ControlTrip {}
impl HandlesMidi for ControlTrip {}
impl Performs for ControlTrip {
fn play(&mut self) {
Expand Down
6 changes: 5 additions & 1 deletion entities/src/controllers/lfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use groove_core::{
generators::{Oscillator, OscillatorParams, Waveform},
midi::HandlesMidi,
time::{MusicalTime, SampleRate, Tempo},
traits::{Configurable, ControlEventsFn, Controls, Generates, Performs, ThingEvent, Ticks},
traits::{
Configurable, ControlEventsFn, Controls, Generates, Performs, Serializable, ThingEvent,
Ticks,
},
FrequencyHz, ParameterType,
};
use groove_proc_macros::{Control, IsController, Params, Uid};
Expand Down Expand Up @@ -45,6 +48,7 @@ pub struct LfoController {
#[cfg_attr(feature = "serialization", serde(skip))]
last_frame: usize,
}
impl Serializable for LfoController {}
impl Configurable for LfoController {
fn update_sample_rate(&mut self, sample_rate: SampleRate) {
self.oscillator.update_sample_rate(sample_rate);
Expand Down
11 changes: 9 additions & 2 deletions entities/src/controllers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ mod sequencers;
use groove_core::{
midi::{new_note_off, new_note_on, HandlesMidi, MidiChannel, MidiMessagesFn},
time::{ClockTimeUnit, MusicalTime, MusicalTimeParams, SampleRate},
traits::{Configurable, ControlEventsFn, Controls, Performs, ThingEvent, TransformsAudio},
traits::{
Configurable, ControlEventsFn, Controls, Performs, Serializable, ThingEvent,
TransformsAudio,
},
BipolarNormal, Normal, Sample, StereoSample,
};
use groove_proc_macros::{Control, IsController, IsControllerEffect, Params, Uid};
Expand Down Expand Up @@ -88,6 +91,7 @@ pub struct Timer {
#[cfg_attr(feature = "serialization", serde(skip))]
end_time: Option<MusicalTime>,
}
impl Serializable for Timer {}
impl Timer {
pub fn new_with(params: &TimerParams) -> Self {
Self {
Expand Down Expand Up @@ -173,6 +177,7 @@ pub struct Trigger {
has_triggered: bool,
is_performing: bool,
}
impl Serializable for Trigger {}
impl Controls for Trigger {
fn update_time(&mut self, range: &Range<MusicalTime>) {
self.timer.update_time(range)
Expand Down Expand Up @@ -247,6 +252,7 @@ pub struct SignalPassthroughController {
#[cfg_attr(feature = "serialization", serde(skip))]
is_performing: bool,
}
impl Serializable for SignalPassthroughController {}
impl Configurable for SignalPassthroughController {}
impl Controls for SignalPassthroughController {
fn update_time(&mut self, _range: &Range<MusicalTime>) {
Expand Down Expand Up @@ -368,6 +374,7 @@ pub struct ToyController {
#[cfg_attr(feature = "serialization", serde(skip))]
last_time_handled: MusicalTime,
}
impl Serializable for ToyController {}
impl Controls for ToyController {
fn update_time(&mut self, range: &Range<MusicalTime>) {
self.time_range = range.clone();
Expand Down Expand Up @@ -467,7 +474,7 @@ impl ToyController {
checkpoint_delta,
time_unit,
time_range: MusicalTime::end_of_time_range(),
last_time_handled: MusicalTime::end_of_time(),
last_time_handled: MusicalTime::TIME_MAX,
}
}

Expand Down
3 changes: 2 additions & 1 deletion entities/src/controllers/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::Sequencer;
use groove_core::{
midi::{HandlesMidi, MidiChannel, MidiMessage},
time::{BeatValue, MusicalTime, PerfectTimeUnit, TimeSignature, TimeSignatureParams},
traits::{Configurable, ControlEventsFn, Controls, Performs},
traits::{Configurable, ControlEventsFn, Controls, Performs, Serializable},
};
use groove_proc_macros::{Control, IsController, Params, Uid};
use std::{cmp, fmt::Debug, ops::Range};
Expand Down Expand Up @@ -60,6 +60,7 @@ pub struct PatternManager {
patterns: Vec<Pattern<Note>>,
selected_pattern: usize,
}
impl Serializable for PatternManager {}
impl HandlesMidi for PatternManager {}
impl Configurable for PatternManager {}
impl Controls for PatternManager {
Expand Down
3 changes: 2 additions & 1 deletion entities/src/controllers/sequencers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use btreemultimap::BTreeMultiMap;
use groove_core::{
midi::{HandlesMidi, MidiChannel, MidiMessage, MidiMessagesFn, MidiNoteMinder},
time::{Clock, ClockParams, MusicalTime, PerfectTimeUnit, SampleRate, TimeSignatureParams},
traits::{Configurable, ControlEventsFn, Controls, Performs, ThingEvent},
traits::{Configurable, ControlEventsFn, Controls, Performs, Serializable, ThingEvent},
ParameterType,
};
use groove_proc_macros::{Control, IsController, Params, Uid};
Expand Down Expand Up @@ -54,6 +54,7 @@ pub struct Sequencer {
#[cfg_attr(feature = "serialization", serde(skip))]
time_range_handled: bool,
}
impl Serializable for Sequencer {}
impl HandlesMidi for Sequencer {}
impl Performs for Sequencer {
fn play(&mut self) {
Expand Down
3 changes: 2 additions & 1 deletion entities/src/effects/bitcrusher.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2023 Mike Tsao. All rights reserved.

use groove_core::{
traits::{Configurable, TransformsAudio},
traits::{Configurable, Serializable, TransformsAudio},
Sample, SampleType,
};
use groove_proc_macros::{Control, IsEffect, Params, Uid};
Expand All @@ -23,6 +23,7 @@ pub struct Bitcrusher {

c: SampleType,
}
impl Serializable for Bitcrusher {}
impl TransformsAudio for Bitcrusher {
fn transform_channel(&mut self, _channel: usize, input_sample: Sample) -> Sample {
const I16_SCALE: SampleType = i16::MAX as SampleType;
Expand Down
3 changes: 2 additions & 1 deletion entities/src/effects/chorus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use super::delay::{DelayLine, Delays};
use groove_core::{
time::SampleRate,
traits::{Configurable, TransformsAudio},
traits::{Configurable, Serializable, TransformsAudio},
ParameterType, Sample,
};
use groove_proc_macros::{Control, IsEffect, Params, Uid};
Expand All @@ -29,6 +29,7 @@ pub struct Chorus {
#[cfg_attr(feature = "serialization", serde(skip))]
delay: DelayLine,
}
impl Serializable for Chorus {}
impl TransformsAudio for Chorus {
fn transform_channel(&mut self, _channel: usize, input_sample: Sample) -> Sample {
let index_offset = self.delay_seconds / self.voices as ParameterType;
Expand Down
3 changes: 2 additions & 1 deletion entities/src/effects/compressor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2023 Mike Tsao. All rights reserved.

use groove_core::{
traits::{Configurable, TransformsAudio},
traits::{Configurable, Serializable, TransformsAudio},
Normal, ParameterType, Sample,
};
use groove_proc_macros::{Control, IsEffect, Params, Uid};
Expand Down Expand Up @@ -46,6 +46,7 @@ pub struct Compressor {
#[allow(dead_code)]
current_gain: f32,
}
impl Serializable for Compressor {}
impl TransformsAudio for Compressor {
fn transform_channel(&mut self, _channel: usize, input_sample: Sample) -> Sample {
let input_sample_positive = input_sample.0.abs();
Expand Down
3 changes: 2 additions & 1 deletion entities/src/effects/delay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use groove_core::{
time::SampleRate,
traits::{Configurable, TransformsAudio},
traits::{Configurable, Serializable, TransformsAudio},
Normal, ParameterType, Sample, SignalType,
};
use groove_proc_macros::{Control, IsEffect, Params, Uid};
Expand Down Expand Up @@ -208,6 +208,7 @@ pub struct Delay {
#[cfg_attr(feature = "serialization", serde(skip))]
delay: DelayLine,
}
impl Serializable for Delay {}
impl Configurable for Delay {
fn update_sample_rate(&mut self, sample_rate: SampleRate) {
self.delay.update_sample_rate(sample_rate);
Expand Down
Loading

0 comments on commit 49b845a

Please sign in to comment.