Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/tidal/cycle.pest
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ split_op = {"."}
sections = _{ section ~ ((stack_op | split_op | choice_op) ~ section)* }

/// groups
subdivision = { "[" ~ sections? ~ "]" }
alternating = { "<" ~ sections? ~ ">" }
subdivision = { "[" ~ sections? ~ "]" ~ index_tail? }
alternating = { "<" ~ sections? ~ ">" ~ index_tail? }

polymeter_tail = { "%" ~ parameter }
polymeter = { "{" ~ sections? ~ "}" ~ polymeter_tail? }
index_tail = { "%" ~ parameter }
polymeter = { "{" ~ sections? ~ "}" ~ index_tail? }

group = _{ subdivision | alternating | polymeter }

Expand Down
153 changes: 134 additions & 19 deletions src/tidal/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,19 @@ impl Pitch {

// -------------------------------------------------------------------------------------------------

#[derive(Clone, Debug, PartialEq)]
pub struct Indexed {
steps: Box<Step>,
index: Box<Step>,
alternating: bool,
}

#[derive(Clone, Debug, PartialEq)]
enum Step {
Var(Rc<str>),
Single(Single),
Subdivision(Subdivision),
Indexed(Indexed),
Polymeter(Polymeter),
Stack(Stack),
Choices(Choices),
Expand Down Expand Up @@ -398,6 +406,7 @@ impl Step {
Step::Single(_s) => vec![],
Step::Polymeter(p) => p.stack.iter().collect(),
Step::Subdivision(sd) => sd.steps.iter().collect(),
Step::Indexed(sd) => sd.steps.inner_steps(),
Step::Choices(cs) => cs.choices.iter().collect(),
Step::Stack(st) => st.stack.iter().collect(),
Step::SpeedExpression(e) => vec![&e.step, &e.mult],
Expand All @@ -423,6 +432,10 @@ impl Step {
vars.insert(Rc::clone(name));
}
Step::Single(_s) => (),
Step::Indexed(i) => {
i.steps.get_vars(vars);
i.index.get_vars(vars);
}
Step::Polymeter(pm) => {
pm.stack.iter().for_each(|s| s.get_vars(vars));
if let Some(c) = pm.count.as_ref() {
Expand Down Expand Up @@ -484,6 +497,30 @@ impl Step {
Self::Subdivision(Subdivision { steps })
}

fn subdivision_with_index(steps: Vec<Self>, index: Option<Self>) -> Self {
if let Some(index) = index {
Self::Indexed(Indexed {
steps: Box::new(Self::subdivision(steps)),
index: Box::new(index),
alternating: false,
})
} else {
Self::subdivision(steps)
}
}

fn alternating_with_index(steps: Vec<Self>, index: Option<Self>) -> Self {
if let Some(index) = index {
Self::Indexed(Indexed {
steps: Box::new(Self::subdivision(steps)),
index: Box::new(index),
alternating: true,
})
} else {
Self::alternating(steps)
}
}

fn alternating(steps: Vec<Self>) -> Self {
Self::polymeter(
vec![steps],
Expand Down Expand Up @@ -761,6 +798,17 @@ impl Constant {
}
}

fn to_index(&self, len: usize) -> Option<usize> {
match *self {
Self::Float(float) | Self::Target(Target::NamedFloat(_, float)) => {
Some((float.rem_euclid(1.0) * len as f64).floor() as usize)
}
_ => self
.to_integer()
.map(|int| (if int > 0 { int - 1 } else { 0 } as usize).rem_euclid(len)),
}
}

fn to_chance(&self) -> Option<f64> {
match &self {
Self::Null => None,
Expand Down Expand Up @@ -1064,6 +1112,15 @@ impl Events {
}
}

fn set_span(&mut self, span: Span) {
match self {
Events::Single(s) => s.span = span,
Events::Multi(m) => m.span = span,
Events::Poly(p) => p.span = span,
}
// self.set_length(span.length());
}

fn first(&self) -> Option<&Event> {
match self {
Events::Single(s) => Some(s),
Expand Down Expand Up @@ -1170,6 +1227,27 @@ impl Events {
}
}

fn mutate_singles<F>(&mut self, fun: &mut F) -> Result<(), String>
where
F: FnMut(Event, &mut Events) -> Result<(), String>,
{
match self {
Events::Single(s) => fun(s.clone(), self),
Events::Multi(m) => {
for e in &mut m.events {
e.mutate_singles(fun)?;
}
Ok(())
}
Events::Poly(p) => {
for e in &mut p.channels {
e.mutate_singles(fun)?;
}
Ok(())
}
}
}

/// recursively transform the spans of events from 0..1 to a given span
fn transform_spans(&mut self, span: &Span) {
let unit = span.length();
Expand Down Expand Up @@ -1361,8 +1439,8 @@ impl CycleParser {
Rule::variable => Self::variable(pair),
Rule::single => Ok(Self::single(pair)?),
Rule::repeat => Ok(Step::Repeat),
Rule::subdivision | Rule::mini => Self::group(pair, Step::subdivision),
Rule::alternating => Self::group(pair, Step::alternating),
Rule::subdivision | Rule::mini => Self::group(pair, Step::subdivision_with_index),
Rule::alternating => Self::group(pair, Step::alternating_with_index),
Rule::polymeter => Self::polymeter(pair),
Rule::range => Self::range(pair),
Rule::expression => Self::expression(pair),
Expand Down Expand Up @@ -1572,8 +1650,14 @@ impl CycleParser {
Ok(stacks)
}

fn group(pair: Pair<Rule>, fun: fn(Vec<Step>) -> Step) -> Result<Step, String> {
let stacks = Self::stacks(pair.into_inner().collect())?;
fn group(pair: Pair<Rule>, fun: fn(Vec<Step>, Option<Step>) -> Step) -> Result<Step, String> {
let (stacked_pairs, count_pairs): (Vec<Pair<Rule>>, Vec<Pair<Rule>>) = pair
.into_inner()
.partition(|p| p.as_rule() != Rule::index_tail);

let stacks = Self::stacks(stacked_pairs)?;

let index = Self::index_tail(count_pairs)?;

match stacks.len() {
0 => Ok(Step::rest()),
Expand All @@ -1582,36 +1666,40 @@ impl CycleParser {
if steps.is_empty() {
Ok(Step::rest())
} else {
Ok(fun(steps.to_owned()))
Ok(fun(steps.to_owned(), index))
}
}
_ => Ok(Step::Stack(Stack {
stack: stacks.into_iter().map(fun).collect(),
stack: stacks
.into_iter()
.map(|steps| fun(steps, index.clone()))
.collect(),
})),
}
}

fn polymeter_tail(pair: Pair<Rule>) -> Result<Step, String> {
if let Some(count) = pair.clone().into_inner().next() {
Self::step(count)
fn index_tail(pairs: Vec<Pair<Rule>>) -> Result<Option<Step>, String> {
if let Some(pair) = pairs.first() {
if let Some(count) = pair.clone().into_inner().next() {
Ok(Some(Self::step(count)?))
} else {
Err(format!(
"error in grammar, missing polymeter count '{}'",
pair.as_str()
))
}
// Some(Self::index_tail(pair.to_owned())?)
} else {
Err(format!(
"error in grammar, missing polymeter count '{}'",
pair.as_str()
))
Ok(None)
}
}

fn polymeter(pair: Pair<Rule>) -> Result<Step, String> {
let (stacked_pairs, count_pairs): (Vec<Pair<Rule>>, Vec<Pair<Rule>>) = pair
.into_inner()
.partition(|p| p.as_rule() != Rule::polymeter_tail);
.partition(|p| p.as_rule() != Rule::index_tail);

let count: Option<Step> = if let Some(pair) = count_pairs.first() {
Some(Self::polymeter_tail(pair.to_owned())?)
} else {
None
};
let count = Self::index_tail(count_pairs)?;

let stacks = Self::stacks(stacked_pairs)?;

Expand Down Expand Up @@ -2148,6 +2236,32 @@ impl Cycle {
targets: vec![],
})
}
Step::Indexed(s) => {
let mut indices =
Self::output(s.index.as_ref(), state, cycle, limit, overlap, vars)?;
let length = Self::sub_length(&s.steps, state, cycle, limit, overlap, vars)?;
let offset = if s.alternating { cycle } else { 0 };
indices.mutate_singles(&mut |event: Event, events: &mut Events| {
*events = if let Some(i) = event.value.to_index(length.to_integer() as usize) {
let mut out = Self::output_with_speed(
&s.steps,
&SpeedOp::Fit(length),
&Step::constant(Constant::Integer(1), None),
state,
offset + i as u32,
limit,
overlap,
vars,
)?;
out.set_span(events.get_span());
out
} else {
Events::empty()
};
Ok(())
})?;
indices
}
Step::Subdivision(sd) => {
if sd.steps.is_empty() {
Events::empty()
Expand Down Expand Up @@ -2386,6 +2500,7 @@ impl Cycle {
_ => format!("{:?} {:?}", s.value, s.string),
},
Step::Subdivision(sd) => format!("Subdivision [{}]", sd.steps.len()),
Step::Indexed(sd) => format!("Indexed [{:?}]", sd.index.as_ref()),
Step::Polymeter(pm) => format!("Polymeter {{{:?}}}", pm.count),
Step::Choices(cs) => format!("Choices |{}|", cs.choices.len()),
Step::Stack(st) => format!("Stack ({})", st.stack.len()),
Expand Down
34 changes: 34 additions & 0 deletions src/tidal/cycle/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ fn assert_cycle_equality(a: &str, b: &str) -> Result<(), String> {
);
Ok(())
}
fn assert_cycles_equality(a: &str, bs: &[&str]) -> Result<(), String> {
let seed = rand::rng().random();
let mut cycle = Cycle::from(a)?.with_seed(seed);
for b in bs {
assert_eq!(
cycle.generate()?,
Cycle::from(b)?.with_seed(seed).generate()?,
);
}
Ok(())
}

fn assert_cycle_advancing(input: &str) -> Result<(), String> {
let seed = rand::rng().random();
Expand Down Expand Up @@ -60,6 +71,29 @@ fn weight_and_replicate() -> Result<(), String> {
Ok(())
}

#[test]
fn indexing() -> Result<(), String> {
assert_cycle_equality("[a b c d]%0", "a")?;
assert_cycle_equality("[a b c d]%1", "a")?;
assert_cycle_equality("[a b c d]%2", "b")?;
assert_cycle_equality("[a b c d]%3", "c")?;
assert_cycle_equality("[a b c d]%4", "d")?;
assert_cycle_equality("[a b c d]%5", "a")?;
assert_cycle_equality("[a b c d]%0.1", "a")?;
assert_cycle_equality("[a b c d]%0.25", "b")?;
assert_cycle_equality("[a b c d]%0.499", "b")?;
assert_cycle_equality("[a b c d]%0.5", "c")?;
assert_cycle_equality("[a b c d]%0.999", "d")?;
assert_cycle_equality("[a b c d]%[1 3 3]", "a c c")?;
assert_cycle_equality("[a b c d]%[5 4 3]", "a d c")?;
assert_cycle_equality("[a b c d]%[1, 3, 4]", "a, c, d")?;
assert_cycle_equality("[a b c d]%<1 2 3>", "a")?;
assert_cycle_equality("[a b c d]%<[1*8 2*4 3*2 4]>", "a*8 b*4 c*2 d")?;

assert_cycles_equality("<a b c d>%2", &["b", "c", "d", "a"])?;
Ok(())
}

#[test]
fn variables() -> Result<(), String> {
assert!(SubCycle::from("a b c d").is_ok());
Expand Down