use rand::Rng;
use crate::composition::TrackBuilder;
pub fn random_walk_sequence(start: u32, steps: usize, min: u32, max: u32) -> Vec<u32> {
if steps == 0 || min >= max {
return Vec::new();
}
let mut sequence = Vec::with_capacity(steps);
let mut current = start.clamp(min, max - 1);
let mut rng = rand::rng();
for _ in 0..steps {
sequence.push(current);
let step = if rng.random::<bool>() {
rng.random_range(1..=2) } else {
-(rng.random_range(1..=2) as i32) };
current = ((current as i32 + step).clamp(min as i32, (max - 1) as i32)) as u32;
}
sequence
}
pub fn biased_random_walk_sequence(
start: u32,
steps: usize,
min: u32,
max: u32,
up_bias: f32,
) -> Vec<u32> {
if steps == 0 || min >= max {
return Vec::new();
}
let mut sequence = Vec::with_capacity(steps);
let mut current = start.clamp(min, max - 1);
let mut rng = rand::rng();
for _ in 0..steps {
sequence.push(current);
let go_up = rng.random::<f32>() < up_bias;
let step = if go_up {
rng.random_range(1..=2) } else {
-(rng.random_range(1..=2) as i32) };
current = ((current as i32 + step).clamp(min as i32, (max - 1) as i32)) as u32;
}
sequence
}
impl<'a> TrackBuilder<'a> {
pub fn random_walk(
mut self,
start_freq: f32,
steps: usize,
note_duration: f32,
scale: &[f32],
) -> Self {
if scale.is_empty() || steps == 0 {
return self;
}
let waveform = self.waveform;
let envelope = self.envelope;
let pitch_bend = self.pitch_bend;
let mut current_idx = scale
.iter()
.position(|&f| (f - start_freq).abs() < 0.1)
.unwrap_or(0);
let mut rng = rand::rng();
for _ in 0..steps {
let cursor = self.cursor;
let freq = scale[current_idx];
self.get_track_mut()
.add_note_with_waveform_envelope_and_bend(
&[freq],
cursor,
note_duration,
waveform,
envelope,
pitch_bend,
);
let swung_duration = self.apply_swing(note_duration);
self.cursor += swung_duration;
let step = if rng.random::<bool>() {
rng.random_range(1..=2) } else {
-(rng.random_range(1..=2)) };
current_idx = ((current_idx as i32 + step)
.max(0)
.min(scale.len() as i32 - 1)) as usize;
}
self.update_section_duration();
self
}
}