generator.rs (3263B)
1 use crate::data::{Bar, Beat, Chord, NoteEvent, Tonality}; 2 use rand::Rng; 3 use std::collections::HashMap; 4 5 pub struct MusicGenerator { 6 previous_state: Chord, 7 transition_model: HashMap<Chord, Vec<(Chord, u32)>>, 8 } 9 10 impl MusicGenerator { 11 pub fn new() -> Self { 12 use Chord::*; 13 let mut model = HashMap::new(); 14 15 model.insert( 16 First, 17 vec![ 18 (Fourth, 5), // I -> IV (F) に行きやすい 19 (Fifth, 3), // I -> V (G) もあり 20 (Sixth, 2), // I -> VI (Am) もあり 21 (First, 1), // I のまま留まる 22 ], 23 ); 24 25 model.insert( 26 Fourth, 27 vec![ 28 (Fifth, 8), // IV -> V (G) に行きやすい 29 (First, 2), // IV -> I (C) に戻る 30 ], 31 ); 32 33 model.insert( 34 Fifth, 35 vec![ 36 (First, 10), // V -> I (C) に戻るのが基本 37 ], 38 ); 39 40 model.insert(Sixth, vec![(Fourth, 5), (First, 5)]); // Am -> F or C 41 model.insert(Second, vec![(Fifth, 10)]); // Dm -> G 42 43 MusicGenerator { 44 previous_state: First, // 初期状態は I (C) 45 transition_model: model, 46 } 47 } 48 49 pub fn generate_next_bar(&mut self) -> Bar { 50 let next_chord = self.choose_next_state(); 51 52 let next_bar = self.generate_events_for_chord(next_chord); 53 54 self.previous_state = next_chord; 55 next_bar 56 } 57 58 fn choose_next_state(&self) -> Chord { 59 let options = self 60 .transition_model 61 .get(&self.previous_state) 62 .unwrap_or_else(|| panic!("there no code {:?} on this model", self.previous_state)); 63 64 let total_weight: u32 = options.iter().map(|(_, w)| w).sum(); 65 let mut rng = rand::rng(); 66 let mut target = rng.random_range(0..total_weight); 67 68 for (state, weight) in options { 69 if target < *weight { 70 return *state; 71 } 72 target -= weight; 73 } 74 options[0].0 75 } 76 77 fn generate_events_for_chord(&self, chord: Chord) -> Bar { 78 let root_note = match chord { 79 Chord::First => 60, // C4 80 Chord::Second => 62, // D4 81 Chord::Third => 64, // E4 82 Chord::Fourth => 65, // F4 83 Chord::Fifth => 67, // G4 84 Chord::Sixth => 69, // A4 85 Chord::Seventh => 71, // B4 86 }; 87 88 let mut events = Vec::new(); 89 const NOTE_DURATION: u64 = 480; // 500ms - 20ms の隙間 90 91 for i in 0..4 { 92 let time_ms = (i as u64) * 500; 93 94 events.push(( 95 time_ms, 96 NoteEvent::NoteOn { 97 note: root_note, 98 velocity: 90, 99 }, 100 )); 101 events.push(( 102 time_ms + NOTE_DURATION, 103 NoteEvent::NoteOff { note: root_note }, 104 )); 105 } 106 107 Bar { 108 beat: Beat::FourFourth, 109 tonality: Tonality::CM, 110 chord, 111 events, 112 } 113 } 114 }