//! Rhythmic and melodic pattern generation module //! //! This module provides tools for generating and manipulating musical patterns, //! including rhythm patterns, melodic sequences, and pattern transformations. use crate::scales::Scale; use rand::Rng; /// A rhythmic pattern representation #[derive(Debug, Clone)] pub struct RhythmPattern { pub steps: Vec, pub steps_per_beat: usize, pub length: usize, // Total number of steps } /// A single step in a rhythm pattern #[derive(Debug, Clone, Copy)] pub struct RhythmStep { pub active: bool, pub velocity: f32, pub accent: bool, } impl RhythmStep { pub fn new(active: bool, velocity: f32, accent: bool) -> Self { Self { active, velocity: velocity.clamp(0.0, 1.0), accent, } } pub fn hit(velocity: f32) -> Self { Self::new(true, velocity, false) } pub fn accent_hit(velocity: f32) -> Self { Self::new(true, velocity, true) } pub fn rest() -> Self { Self::new(false, 0.0, false) } } impl RhythmPattern { /// Create a new rhythm pattern /// /// # Arguments /// * `length` - Total number of steps in the pattern /// * `steps_per_beat` - Number of steps per beat (e.g., 4 for sixteenth notes) pub fn new(length: usize, steps_per_beat: usize) -> Self { let steps = vec![RhythmStep::rest(); length]; Self { steps, steps_per_beat, length, } } /// Create a basic kick drum pattern (4/4 time) pub fn kick_pattern() -> Self { let mut pattern = Self::new(16, 4); pattern.steps[0] = RhythmStep::accent_hit(1.0); // Beat 1 pattern.steps[4] = RhythmStep::hit(0.8); // Beat 2 pattern.steps[8] = RhythmStep::accent_hit(1.0); // Beat 3 pattern.steps[12] = RhythmStep::hit(0.8); // Beat 4 pattern } /// Create a basic snare pattern pub fn snare_pattern() -> Self { let mut pattern = Self::new(16, 4); pattern.steps[4] = RhythmStep::accent_hit(0.9); // Beat 2 pattern.steps[12] = RhythmStep::accent_hit(0.9); // Beat 4 pattern } /// Create a hi-hat pattern pub fn hihat_pattern() -> Self { let mut pattern = Self::new(16, 4); for i in 0..16 { if i % 2 == 0 { pattern.steps[i] = RhythmStep::hit(0.6); } else { pattern.steps[i] = RhythmStep::hit(0.4); } } pattern } /// Create a Euclidean rhythm pattern /// /// # Arguments /// * `hits` - Number of hits to distribute /// * `steps` - Total number of steps pub fn euclidean(hits: usize, steps: usize) -> Self { let mut pattern = Self::new(steps, 4); if hits == 0 { return pattern; } let mut remainders = vec![hits]; let mut counts = vec![steps / hits]; let mut divisor = steps % hits; let mut level = 0; while divisor != 0 && level < 16 { let temp_divisor = remainders[level] % divisor; let temp_count = remainders[level] / divisor; counts.push(temp_count); remainders.push(divisor); remainders[level] = temp_divisor; divisor = temp_divisor; level += 1; } // Build the pattern let mut result = Vec::new(); for i in 0..counts.len() { for _ in 0..remainders[i] { if i % 2 == 0 { result.push(true); } else { result.push(false); } for _ in 1..counts[i] { result.push(false); } } } // Fill remaining steps while result.len() < steps { result.push(false); } for (i, &active) in result.iter().enumerate() { if i < pattern.steps.len() { pattern.steps[i] = if active { RhythmStep::hit(0.8) } else { RhythmStep::rest() }; } } pattern } /// Set a step in the pattern pub fn set_step(&mut self, index: usize, step: RhythmStep) { if index < self.steps.len() { self.steps[index] = step; } } /// Get a step from the pattern pub fn get_step(&self, index: usize) -> Option { self.steps.get(index).copied() } /// Rotate the pattern by a number of steps pub fn rotate(&mut self, steps: i32) { if self.steps.is_empty() { return; } let len = self.steps.len() as i32; let effective_steps = ((steps % len) + len) % len; self.steps.rotate_right(effective_steps as usize); } /// Reverse the pattern pub fn reverse(&mut self) { self.steps.reverse(); } /// Add random variations to the pattern pub fn add_variation(&mut self, probability: f32) { let mut rng = rand::thread_rng(); for step in &mut self.steps { if rng.r#gen::() < probability { if step.active { // Randomly remove hits if rng.r#gen::() < 0.3 { step.active = false; } else { // Vary velocity step.velocity = (step.velocity + rng.gen_range(-0.2..0.2)).clamp(0.0, 1.0); } } else { // Randomly add hits if rng.r#gen::() < 0.1 { step.active = true; step.velocity = 0.3 + rng.r#gen::() * 0.4; } } } } } /// Get timing information for active steps /// /// # Arguments /// * `beat_duration` - Duration of one beat in samples /// /// # Returns /// Vector of (step_index, timing_in_samples, velocity) pub fn get_timings(&self, beat_duration: usize) -> Vec<(usize, usize, f32)> { let mut timings = Vec::new(); let step_duration = beat_duration / self.steps_per_beat; for (i, step) in self.steps.iter().enumerate() { if step.active { let timing = (i * step_duration) / self.steps_per_beat; timings.push((i, timing, step.velocity)); } } timings } /// Combine this pattern with another pattern pub fn combine(&self, other: &RhythmPattern, mode: CombineMode) -> RhythmPattern { let max_len = self.steps.len().max(other.steps.len()); let mut combined = RhythmPattern::new(max_len, self.steps_per_beat); for i in 0..max_len { let self_step = self.steps.get(i).copied().unwrap_or(RhythmStep::rest()); let other_step = other.steps.get(i).copied().unwrap_or(RhythmStep::rest()); combined.steps[i] = match mode { CombineMode::Or => { if self_step.active || other_step.active { RhythmStep::hit(self_step.velocity.max(other_step.velocity)) } else { RhythmStep::rest() } } CombineMode::And => { if self_step.active && other_step.active { RhythmStep::hit((self_step.velocity + other_step.velocity) / 2.0) } else { RhythmStep::rest() } } CombineMode::Xor => { if self_step.active ^ other_step.active { RhythmStep::hit(self_step.velocity.max(other_step.velocity)) } else { RhythmStep::rest() } } }; } combined } } /// Modes for combining rhythm patterns #[derive(Debug, Clone, Copy, PartialEq)] pub enum CombineMode { Or, // Either pattern has a hit And, // Both patterns have a hit Xor, // Only one pattern has a hit } /// A melodic pattern representation #[derive(Debug, Clone)] pub struct MelodicPattern { pub notes: Vec, pub scale: Scale, pub octave_range: u8, } /// A note in a melodic pattern #[derive(Debug, Clone, Copy)] pub struct PatternNote { pub scale_degree: usize, // 1-based scale degree pub octave_offset: i8, // Octave offset from base pub duration: f32, // In beats pub velocity: f32, pub is_rest: bool, } impl PatternNote { pub fn new(scale_degree: usize, octave_offset: i8, duration: f32, velocity: f32) -> Self { Self { scale_degree, octave_offset, duration, velocity: velocity.clamp(0.0, 1.0), is_rest: false, } } pub fn rest(duration: f32) -> Self { Self { scale_degree: 1, octave_offset: 0, duration, velocity: 0.0, is_rest: true, } } } impl MelodicPattern { /// Create a new melodic pattern pub fn new(scale: Scale, octave_range: u8) -> Self { Self { notes: Vec::new(), scale, octave_range, } } /// Add a note to the pattern pub fn add_note(&mut self, note: PatternNote) { self.notes.push(note); } /// Generate a random melodic pattern /// /// # Arguments /// * `length` - Number of notes in the pattern /// * `note_durations` - Possible note durations pub fn generate_random(&mut self, length: usize, note_durations: &[f32]) { let mut rng = rand::thread_rng(); self.notes.clear(); let scale_degrees = self.scale.intervals.len(); for _ in 0..length { if rng.r#gen::() < 0.1 { // 10% chance of rest let duration = note_durations[rng.gen_range(0..note_durations.len())]; self.notes.push(PatternNote::rest(duration)); } else { let degree = rng.gen_range(1..=scale_degrees); let octave_offset = rng.gen_range(-1..=1); let duration = note_durations[rng.gen_range(0..note_durations.len())]; let velocity = 0.5 + rng.r#gen::() * 0.4; self.notes .push(PatternNote::new(degree, octave_offset, duration, velocity)); } } } /// Generate an ascending scale pattern pub fn generate_ascending(&mut self, octaves: u8) { self.notes.clear(); for octave in 0..octaves { for (i, _) in self.scale.intervals.iter().enumerate() { let degree = i + 1; let octave_offset = octave as i8; self.notes .push(PatternNote::new(degree, octave_offset, 0.5, 0.8)); } } } /// Generate a descending scale pattern pub fn generate_descending(&mut self, octaves: u8) { self.notes.clear(); for octave in (0..octaves).rev() { for (i, _) in self.scale.intervals.iter().enumerate().rev() { let degree = i + 1; let octave_offset = octave as i8; self.notes .push(PatternNote::new(degree, octave_offset, 0.5, 0.8)); } } } /// Generate an arpeggio pattern /// /// # Arguments /// * `chord_degrees` - Scale degrees that form the chord (e.g., [1, 3, 5]) /// * `direction` - 1 for ascending, -1 for descending pub fn generate_arpeggio( &mut self, chord_degrees: &[usize], direction: i8, repetitions: usize, ) { self.notes.clear(); for _ in 0..repetitions { let degrees = if direction > 0 { chord_degrees.to_vec() } else { let mut rev = chord_degrees.to_vec(); rev.reverse(); rev }; for °ree in °rees { self.notes.push(PatternNote::new(degree, 0, 0.25, 0.7)); } } } /// Apply swing timing to the pattern /// /// # Arguments /// * `swing_ratio` - Swing ratio (0.5 = straight, 0.67 = heavy swing) pub fn apply_swing(&mut self, swing_ratio: f32) { let swing_ratio = swing_ratio.clamp(0.5, 0.8); for (i, note) in self.notes.iter_mut().enumerate() { if i % 2 == 1 && !note.is_rest { // Apply swing to off-beat notes note.duration *= swing_ratio; } } } /// Transpose the pattern by semitones pub fn transpose(&mut self, semitones: i8) { // Note: This would require adjusting the scale root // For now, we'll adjust octave offsets when possible let octave_adjustment = semitones / 12; for note in &mut self.notes { note.octave_offset += octave_adjustment; } } /// Get the MIDI notes for this pattern /// /// # Arguments /// * `base_octave` - Base octave for the pattern /// /// # Returns /// Vector of (midi_note, start_time, duration, velocity) pub fn get_midi_notes(&self, base_octave: u8) -> Vec<(u8, f32, f32, f32)> { let mut midi_notes = Vec::new(); let mut current_time = 0.0; for note in &self.notes { if !note.is_rest { if let Some(midi_note) = self.scale.get_degree( note.scale_degree, (base_octave as i8 + note.octave_offset).max(0) as u8, ) { midi_notes.push((midi_note, current_time, note.duration, note.velocity)); } } current_time += note.duration; } midi_notes } /// Get the total duration of the pattern pub fn total_duration(&self) -> f32 { self.notes.iter().map(|n| n.duration).sum() } /// Repeat the pattern a number of times pub fn repeat(&mut self, times: usize) { if times <= 1 { return; } let original_notes = self.notes.clone(); self.notes.clear(); for _ in 0..times { self.notes.extend(original_notes.iter().cloned()); } } } /// Pattern transformation utilities pub struct PatternTransforms; impl PatternTransforms { /// Create a polyrhythm by combining patterns of different lengths pub fn create_polyrhythm(patterns: Vec) -> RhythmPattern { if patterns.is_empty() { return RhythmPattern::new(16, 4); } // Find the least common multiple of all pattern lengths let lcm = patterns.iter().fold(1, |acc, p| lcm(acc, p.length)); let steps_per_beat = patterns[0].steps_per_beat; let mut result = RhythmPattern::new(lcm, steps_per_beat); for (i, step) in result.steps.iter_mut().enumerate() { let mut combined_velocity = 0.0; let mut has_hit = false; for pattern in &patterns { let pattern_index = i % pattern.length; if pattern.steps[pattern_index].active { has_hit = true; combined_velocity += pattern.steps[pattern_index].velocity; } } if has_hit { *step = RhythmStep::hit(combined_velocity / patterns.len() as f32); } } result } /// Create a rhythmic canon (same pattern with time offsets) pub fn create_canon( pattern: &RhythmPattern, tracks: usize, offset_steps: usize, ) -> Vec { let mut canons = Vec::new(); for track in 0..tracks { let mut canon_pattern = pattern.clone(); let offset = (track * offset_steps) % pattern.length; canon_pattern.rotate(offset as i32); canons.push(canon_pattern); } canons } /// Generate variations of a melodic pattern pub fn generate_melodic_variations( pattern: &MelodicPattern, variations: usize, ) -> Vec { let mut variations_vec = Vec::new(); let mut rng = rand::thread_rng(); for _ in 0..variations { let mut variation = pattern.clone(); // Apply random transformations match rng.gen_range(0..4) { 0 => { // Rhythmic variation for note in &mut variation.notes { if rng.r#gen::() < 0.3 { note.duration *= rng.gen_range(0.5..2.0); } } } 1 => { // Octave displacement for note in &mut variation.notes { if rng.r#gen::() < 0.2 { note.octave_offset += if rng.r#gen::() { 1 } else { -1 }; } } } 2 => { // Add ornaments (grace notes) let mut ornamented = Vec::new(); for note in &variation.notes { if rng.r#gen::() < 0.15 && !note.is_rest { // Add grace note let grace_degree = if note.scale_degree > 1 { note.scale_degree - 1 } else { note.scale_degree + 1 }; ornamented.push(PatternNote::new( grace_degree, note.octave_offset, 0.1, note.velocity * 0.7, )); } ornamented.push(*note); } variation.notes = ornamented; } 3 => { // Dynamic variation for note in &mut variation.notes { note.velocity = (note.velocity + rng.gen_range(-0.2..0.2)).clamp(0.1, 1.0); } } _ => {} } variations_vec.push(variation); } variations_vec } } /// Calculate least common multiple fn lcm(a: usize, b: usize) -> usize { a * b / gcd(a, b) } /// Calculate greatest common divisor fn gcd(a: usize, b: usize) -> usize { if b == 0 { a } else { gcd(b, a % b) } } #[cfg(test)] mod tests { use super::*; use crate::scales::{Scale, ScaleType}; #[test] fn test_rhythm_pattern_creation() { let pattern = RhythmPattern::new(16, 4); assert_eq!(pattern.length, 16); assert_eq!(pattern.steps_per_beat, 4); assert_eq!(pattern.steps.len(), 16); } #[test] fn test_kick_pattern() { let pattern = RhythmPattern::kick_pattern(); assert!(pattern.steps[0].active); assert!(pattern.steps[8].active); assert!(pattern.steps[0].accent); } #[test] fn test_euclidean_rhythm() { let pattern = RhythmPattern::euclidean(3, 8); let active_count = pattern.steps.iter().filter(|s| s.active).count(); assert_eq!(active_count, 3); } #[test] fn test_pattern_rotation() { let mut pattern = RhythmPattern::kick_pattern(); let original_first = pattern.steps[0].active; pattern.rotate(1); assert_eq!(pattern.steps[1].active, original_first); } #[test] fn test_pattern_combination() { let kick = RhythmPattern::kick_pattern(); let snare = RhythmPattern::snare_pattern(); let combined = kick.combine(&snare, CombineMode::Or); // Should have hits from both patterns assert!(combined.steps[0].active); // Kick assert!(combined.steps[4].active); // Snare assert!(combined.steps[8].active); // Kick } #[test] fn test_melodic_pattern_creation() { let scale = Scale::new(ScaleType::Major, 60); let pattern = MelodicPattern::new(scale, 2); assert_eq!(pattern.octave_range, 2); assert_eq!(pattern.notes.len(), 0); } #[test] fn test_ascending_pattern() { let scale = Scale::new(ScaleType::Major, 60); let mut pattern = MelodicPattern::new(scale, 1); pattern.generate_ascending(1); assert_eq!(pattern.notes.len(), 7); // 7 notes in major scale assert_eq!(pattern.notes[0].scale_degree, 1); assert_eq!(pattern.notes[6].scale_degree, 7); } #[test] fn test_arpeggio_pattern() { let scale = Scale::new(ScaleType::Major, 60); let mut pattern = MelodicPattern::new(scale, 1); pattern.generate_arpeggio(&[1, 3, 5], 1, 2); assert_eq!(pattern.notes.len(), 6); // 3 notes × 2 repetitions assert_eq!(pattern.notes[0].scale_degree, 1); assert_eq!(pattern.notes[1].scale_degree, 3); assert_eq!(pattern.notes[2].scale_degree, 5); } #[test] fn test_midi_note_generation() { let scale = Scale::new(ScaleType::Major, 60); let mut pattern = MelodicPattern::new(scale, 1); pattern.add_note(PatternNote::new(1, 0, 1.0, 0.8)); // Root note pattern.add_note(PatternNote::new(3, 0, 1.0, 0.8)); // Third let midi_notes = pattern.get_midi_notes(4); assert_eq!(midi_notes.len(), 2); assert_eq!(midi_notes[0].0, 60); // C4 assert_eq!(midi_notes[1].0, 64); // E4 } #[test] fn test_pattern_total_duration() { let scale = Scale::new(ScaleType::Major, 60); let mut pattern = MelodicPattern::new(scale, 1); pattern.add_note(PatternNote::new(1, 0, 1.0, 0.8)); pattern.add_note(PatternNote::new(3, 0, 2.0, 0.8)); assert_eq!(pattern.total_duration(), 3.0); } #[test] fn test_polyrhythm_creation() { let pattern1 = RhythmPattern::euclidean(3, 4); let pattern2 = RhythmPattern::euclidean(2, 3); let patterns = vec![pattern1, pattern2]; let polyrhythm = PatternTransforms::create_polyrhythm(patterns); assert_eq!(polyrhythm.length, 12); // LCM of 4 and 3 } }