Skip to content

Commit

Permalink
simplify cycle iteration skipping by deriving a cycle's seeded random…
Browse files Browse the repository at this point in the history
… state from the iteration count
  • Loading branch information
emuell committed Jul 3, 2024
1 parent d8c3354 commit 322f760
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 31 deletions.
7 changes: 2 additions & 5 deletions src/event/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,9 @@ impl CycleEventIter {
timed_note_events.into_event_iter_items()
}

/// Generate next batch of events from the next cycle run but ignore the results.
/// Move the cycle event generator without generating any events
fn omit_events(&mut self) {
// run the cycle event generator
if let Err(err) = self.cycle.omit() {
panic!("Cycle runtime error: {err}");
}
self.cycle.omit();
}
}

Expand Down
7 changes: 2 additions & 5 deletions src/event/scripted_cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,9 @@ impl ScriptedCycleEventIter {
timed_note_events.into_event_iter_items()
}

/// Generate next batch of events from the next cycle run but ignore the results.
/// Move the cycle event generator without generating any events
fn omit_events(&mut self) {
// run the cycle event generator
if let Err(err) = self.cycle.omit() {
add_lua_callback_error("cycle", &LuaError::RuntimeError(err));
}
self.cycle.omit();
}
}

Expand Down
59 changes: 38 additions & 21 deletions src/tidal/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub struct Cycle {
root: Step,
event_limit: usize,
input: String,
seed: Option<[u8; 32]>,
seed: Option<u64>,
state: CycleState,
}
impl Cycle {
Expand Down Expand Up @@ -77,11 +77,10 @@ impl Cycle {
self.state.iteration == 0,
"Should not reconfigure seed of running cycle"
);
// create a u64 seed value from the given seed array
// TODO: should pass a u64 here directly
let seed: u64 = Xoshiro256PlusPlus::from_seed(seed).gen();
Self {
state: CycleState {
rng: Xoshiro256PlusPlus::from_seed(seed),
..self.state
},
seed: Some(seed),
..self
}
Expand All @@ -107,34 +106,26 @@ impl Cycle {
pub fn generate(&mut self) -> Result<Vec<Vec<Event>>, String> {
let cycle = self.state.iteration;
self.state.events = 0;
if let Some(seed) = self.seed {
self.state.rng = Xoshiro256PlusPlus::seed_from_u64(seed.wrapping_add(cycle as u64));
}
let mut events = Self::output(&self.root, &mut self.state, cycle, self.event_limit)?;
self.state.iteration += 1;
events.transform_spans(&Span::default());
Ok(events.export())
}

/// Calculate next iteration of output without actually generating events.
///
/// For stateful inputs, this generates output events, discarding resulting events,
/// for stateless inputs, this simply moves the iteration counter.
/// Move cycle iteration without generating any events.
///
/// Returns error when the number of generated events exceed the configured event limit.
pub fn omit(&mut self) -> Result<(), String> {
let cycle = self.state.iteration;
self.state.events = 0;
if self.is_stateful() {
let _ = Self::output(&self.root, &mut self.state, cycle, self.event_limit)?;
}
/// This simply moves the cycle iteration count.
pub fn omit(&mut self) {
self.state.iteration += 1;
Ok(())
}

/// reset state to initial state
pub fn reset(&mut self) {
self.state.iteration = 0;
self.state.events = 0;
self.state.rng =
Xoshiro256PlusPlus::from_seed(self.seed.unwrap_or_else(|| thread_rng().gen()));
}
}

Expand Down Expand Up @@ -1543,7 +1534,7 @@ impl Cycle {
}
Step::Choices(cs) => {
let choice = state.rng.gen_range(0..cs.choices.len());
Self::output(&cs.choices[choice], state, cycle, limit)? // TODO seed the rng properly
Self::output(&cs.choices[choice], state, cycle, limit)?
}
Step::Polymeter(pm) => {
let step = pm.steps.as_ref();
Expand Down Expand Up @@ -1581,7 +1572,6 @@ impl Cycle {
let mut out = Self::output(e.left.as_ref(), state, cycle, limit)?;
out.mutate_events(&mut |event: &mut Event| {
if let Some(chance) = e.right.to_chance() {
// TODO seed the rng properly
if chance < state.rng.gen_range(0.0..1.0) {
event.value = Value::Rest
}
Expand Down Expand Up @@ -1744,6 +1734,20 @@ mod test {
Ok(())
}

fn assert_cycle_seeking(input: &str) -> Result<(), String> {
let seed = rand::thread_rng().gen();
for number_of_seeks in 1..9 {
let mut cycle1 = Cycle::from(input)?.with_seed(seed);
let mut cycle2 = Cycle::from(input)?.with_seed(seed);
for _ in 0..number_of_seeks {
let _ = cycle1.generate()?;
cycle2.omit();
}
assert_eq!(cycle1.generate()?, cycle2.generate()?);
}
Ok(())
}

#[test]

fn span() -> Result<(), String> {
Expand Down Expand Up @@ -2203,4 +2207,17 @@ mod test {
.is_ok());
Ok(())
}

#[test]
fn seeking() -> Result<(), String> {
assert_cycle_seeking("[a b c d]")?; // stateless
assert_cycle_seeking("[a b], [c d]")?;
assert_cycle_seeking("{a b}%2 {a b}*5")?; // stateful
assert_cycle_seeking("[a b]*5 [a b]/5")?;
assert_cycle_seeking("[a b c d]<c d>")?;
assert_cycle_seeking("a <b c>")?;
assert_cycle_seeking("[a b? c d]|[c? d?]")?;
assert_cycle_seeking("[{a b}/2 c d], <c d> e? {a b}*2")?;
Ok(())
}
}

0 comments on commit 322f760

Please sign in to comment.