Skip to content

Commit

Permalink
Merge pull request #12 from Bosun-Josh121/feat/generate-harmony-from-…
Browse files Browse the repository at this point in the history
…array

generate-harmony-from-array
  • Loading branch information
caseywescott authored Dec 2, 2024
2 parents 9d59c3e + e38da78 commit 1b5f028
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 30 deletions.
57 changes: 27 additions & 30 deletions src/midi/core.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ trait MidiTrait {
factor: i32,
new_tempo: u32,
chanel: u32,
steps: i32,
steps_array: Array<i32>,
tonic: PitchClass,
modes: Modes
) -> Midi;
Expand All @@ -58,7 +58,7 @@ trait MidiTrait {
/// Return statistics about notes (e.g., most frequent note, average note duration).
/// =========== ADVANCED MANIPULATION ===========
/// Add harmonies to existing melodies based on specified intervals.
fn generate_harmony(self: @Midi, steps: i32, tonic: PitchClass, modes: Modes) -> Midi;
fn generate_harmony(self: @Midi, steps_array: Array<i32>, tonic: PitchClass, modes: Modes) -> Midi;
/// Convert chords into arpeggios based on a given pattern.
fn arpeggiate_chords(self: @Midi, pattern: ArpPattern) -> Midi;
/// Add or modify dynamics (velocity) of notes based on a specified curve or pattern.
Expand All @@ -77,7 +77,7 @@ impl MidiImpl of MidiTrait {
factor: i32,
new_tempo: u32,
chanel: u32,
steps: i32,
steps_array: Array<i32>,
tonic: PitchClass,
modes: Modes
) -> Midi {
Expand Down Expand Up @@ -150,7 +150,7 @@ impl MidiImpl of MidiTrait {
.change_note_duration(factor)
.change_tempo(new_tempo)
.remap_instruments(chanel)
.generate_harmony(steps, tonic, modes);
.generate_harmony(steps_array, tonic, modes);

if reverse == 0 { //could be a bool
} else {
Expand Down Expand Up @@ -783,83 +783,80 @@ impl MidiImpl of MidiTrait {
outtempo
}

fn generate_harmony(self: @Midi, steps: i32, tonic: PitchClass, modes: Modes) -> Midi {
fn generate_harmony(self: @Midi, steps_array: Array<i32>, tonic: PitchClass, modes: Modes) -> Midi {
let mut ev = self.clone().events;
let mut eventlist = ArrayTrait::<Message>::new();
let currentmode = mode_steps(modes);

let mut steps_index = 0;

loop {
match ev.pop_front() {
Option::Some(currentevent) => {
let steps = steps_array[steps_index];
steps_index = (steps_index + 1) % steps_array.len();

match currentevent {
Message::NOTE_ON(NoteOn) => {
let outnote = keynum_to_pc(*NoteOn.note)
.modal_transposition(
tonic,
currentmode,
steps.try_into().unwrap(),
if steps < 0 {
(*steps).try_into().unwrap(),
if *steps < 0 {
Direction::Up(())
} else {
Direction::Down(())
},
);

let newnote = NoteOn {
channel: *NoteOn.channel,
note: outnote,
velocity: *NoteOn.velocity,
time: *NoteOn.time
};

let notemessage = Message::NOTE_ON((newnote));
eventlist.append(notemessage);
//include original note
// Include the original note
eventlist.append(*currentevent);
},
Message::NOTE_OFF(NoteOff) => {
let outnote = if steps < 0 {
*NoteOff.note - steps.try_into().unwrap()
let outnote = if *steps < 0 {
*NoteOff.note - (*steps).try_into().unwrap()
} else {
*NoteOff.note + steps.try_into().unwrap()
*NoteOff.note + (*steps).try_into().unwrap()
};

let newnote = NoteOff {
channel: *NoteOff.channel,
note: outnote,
velocity: *NoteOff.velocity,
time: *NoteOff.time
};

let notemessage = Message::NOTE_OFF((newnote));
eventlist.append(notemessage);
//include original note
// Include the original note
eventlist.append(*currentevent);
},
Message::SET_TEMPO(_SetTempo) => { eventlist.append(*currentevent); },
Message::TIME_SIGNATURE(_TimeSignature) => {
eventlist.append(*currentevent);
},
Message::CONTROL_CHANGE(_ControlChange) => {
eventlist.append(*currentevent);
},
Message::TIME_SIGNATURE(_TimeSignature) => { eventlist.append(*currentevent); },
Message::CONTROL_CHANGE(_ControlChange) => { eventlist.append(*currentevent); },
Message::PITCH_WHEEL(_PitchWheel) => { eventlist.append(*currentevent); },
Message::AFTER_TOUCH(_AfterTouch) => { eventlist.append(*currentevent); },
Message::POLY_TOUCH(_PolyTouch) => { eventlist.append(*currentevent); },
Message::PROGRAM_CHANGE(_ProgramChange) => {
eventlist.append(*currentevent);
},
Message::SYSTEM_EXCLUSIVE(_SystemExclusive) => {
eventlist.append(*currentevent);
},
Message::PROGRAM_CHANGE(_ProgramChange) => { eventlist.append(*currentevent); },
Message::SYSTEM_EXCLUSIVE(_SystemExclusive) => { eventlist.append(*currentevent); },
}
},
Option::None(_) => { break; }
};
};

Midi { events: eventlist.span() }
}


fn arpeggiate_chords(self: @Midi, pattern: ArpPattern) -> Midi {
panic(array!['not supported yet'])
Expand Down
91 changes: 91 additions & 0 deletions src/tests/test_midi.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -547,4 +547,95 @@ mod tests {
};
};
}

#[test]
#[available_gas(100000000)]
fn generate_harmony_combined_test() {
let mut eventlist = ArrayTrait::<Message>::new();

let note_on1 = NoteOn {
channel: 0,
note: 60,
velocity: 100,
time: FP32x32 { mag: 0, sign: false },
};
let note_off1 = NoteOff {
channel: 0,
note: 60,
velocity: 100,
time: FP32x32 { mag: 1000, sign: false },
};

let note_on2 = NoteOn {
channel: 0,
note: 64,
velocity: 100,
time: FP32x32 { mag: 500, sign: false },
};
let note_off2 = NoteOff {
channel: 0,
note: 64,
velocity: 100,
time: FP32x32 { mag: 1500, sign: false },
};

eventlist.append(Message::NOTE_ON(note_on1));
eventlist.append(Message::NOTE_OFF(note_off1));
eventlist.append(Message::NOTE_ON(note_on2));
eventlist.append(Message::NOTE_OFF(note_off2));

let midi = Midi {
events: eventlist.span(),
};

let mut steps_array = ArrayTrait::<i32>::new();
steps_array.append(4);
steps_array.append(7);
let tonic = PitchClass {
note: 0,
octave: 4,
};
let mode = Modes::Major;

let harmonized_midi = midi.generate_harmony(steps_array.clone(), tonic, mode);

let mut events = harmonized_midi.events;

loop {
match events.pop_front() {
Option::Some(current_event) => match current_event {
Message::NOTE_ON(NoteOn) => {
assert!(
*NoteOn.note != 60, // Ensure it's different from the original (60)
"Generated NOTE_ON is the same as the original note"
);

// Ensure harmonic note is within range
assert!(
*NoteOn.note >= 48 && *NoteOn.note <= 84,
"Generated NOTE_ON out of range"
);

},
Message::NOTE_OFF(NoteOff) => {
// Ensure there is a change in the note (verify it differs from the original)
assert!(
*NoteOff.note != 60, // Ensure it's different from the original (60)
"Generated NOTE_OFF is the same as the original note"
);

// Ensure harmonic note is within range
assert!(
*NoteOff.note >= 48 && *NoteOff.note <= 84,
"Generated NOTE_OFF out of range"
);
},
_ => {}
},
Option::None(_) => {break;},
}
};

}

}

0 comments on commit 1b5f028

Please sign in to comment.