# adding melody to track for new_note in Voice.midi_score[0]: if isinstance(new_note.pitch, int): MyMIDI.addNote(track, channel, *new_note, 100) strum_ending = random.choice((True, True, True, False)) print(f"Strum ending: {strum_ending}") if strum_ending: time_shift = 0 for voice_index, part in enumerate(Voice.midi_score[2:], 2): time_shift += 90 old_midi_obj = Voice.midi_score[voice_index][-2] new_midi_obj = Voice.Note( old_midi_obj.pitch, old_midi_obj.time + time_shift, old_midi_obj.duration, ) Voice.midi_score[voice_index][-2] = new_midi_obj # adding chords to track for voice_index, part in enumerate(Voice.midi_score[1:]): track += 1 channel += 1 volume = Voice.voice_volumes[voice_index] for new_note in part: if isinstance(new_note.pitch, int): MyMIDI.addNote(track, channel, *new_note, volume) """ 3/4 time sig feels slower at same tempo because beats are in groups of 3 instead of 2
def add_midi_section(self, melody_section, chord_start_index, note_start_indices, index_shift=None): """Add midi notes to the pitch sequence using scale degrees""" object_index = 0 melodic_minor = False add_rest = False melody_section_iter = iter(melody_section) next_scale_group = next(melody_section_iter, None) for chord_index, current_scale_group in enumerate( melody_section, chord_start_index): next_scale_group = next(melody_section_iter, None) current_chord_name = Voice.chord_sequence[chord_index].chord_name if self.mode == "ionian" and current_chord_name in Chord.major_mode_alterations: note_alterations = Chord.major_mode_alterations[ current_chord_name] elif self.mode == "aeolian" and current_chord_name in Chord.minor_mode_alterations: note_alterations = Chord.minor_mode_alterations[ current_chord_name] else: note_alterations = {} if self.mode == "aeolian" and current_chord_name in self.primary_dominants: if melodic_minor: note_alterations[5] = 1 else: # catch raised notes inbetween 2 chords next_chord_name = Voice.chord_sequence[chord_index + 1].chord_name if next_chord_name in self.primary_dominants: affected_scale_group = current_scale_group + next_scale_group else: affected_scale_group = current_scale_group scale_group_str = "".join( str(scale_degree) for scale_degree in affected_scale_group) if any(str_combo in scale_group_str for str_combo in ("56", "65", "-1-2", "-2-1")): melodic_minor = True note_alterations[5] = 1 print("Melodic minor!") for embellish_index, scale_degree in enumerate( current_scale_group, note_start_indices.get(chord_index, 0)): embellish_duration = self.finalized_rhythms[chord_index][ embellish_index] # account for negative scale degrees note_offset = note_alterations.get(scale_degree % 7, 0) embellish_fraction = Fraction(numerator=embellish_duration, denominator=self.unit_length) raw_note_duration = int(Voice.max_note_duration * embellish_fraction) # integers required for midi output if raw_note_duration > 960 and self.rhythm_symbols[ chord_index] >= 0 and self.break_notes: fixed_note_duration = 960 add_rest = True extra_duration = raw_note_duration - 960 else: fixed_note_duration = raw_note_duration midi_pitch = self.melody_range[scale_degree + 3] + note_offset if chord_index == 7 and embellish_index == 0 and self.rhythm_symbols[ 6] == -1: self.midi_notes.append( Voice.Note("Rest", self.current_time, fixed_note_duration)) # needed all numbers in unnested sequence for validation self.unnested_scale_degrees.pop(object_index + index_shift) else: self.midi_notes.append( Voice.Note(midi_pitch, self.current_time, fixed_note_duration)) if add_rest: self.midi_notes.append( Voice.Note("Rest", self.current_time + 960, extra_duration)) self.current_time += raw_note_duration add_rest = False object_index += 1 return object_index
def make_accompanyment(self): """Rhythmically embellish chord progression""" if Voice.pickup: for _ in range(4): Voice.midi_score.append( [Voice.Note("Rest", 0, Voice.pickup_duration)]) Voice.chorale_scale_degrees.append([None]) else: for _ in range(4): Voice.midi_score.append([]) Voice.chorale_scale_degrees.append([]) chord_accompaniments = { (2, 2): [ ((960, 960), ({0, 1, 2, 3}, {})), ((960 * 3 // 2, 480), ({0, 1, 2, 3}, {})), ((480, 480, 480, 480), ({0, 1, 2, 3}, {}, {0, 1, 2, 3}, {})), ( (480, 240, 480, 240, 480), ({0, 1, 2, 3}, {}, {0, 1, 2, 3}, {}, {0, 1, 2, 3}), ), ], (2, 3): [ ((960, 960), ({0, 1, 2, 3}, {})), ((960 * 10 // 6, 960 * 2 // 6), ({0, 1, 2, 3}, {})), ( (960 * 2 // 3, 320, 960 * 2 // 3, 320), ({0, 1, 2, 3}, {}, {0, 1, 2, 3}, {}), ), ], (3, 2): [ ((960, 960 * 2), ({0, 1, 2, 3}, {})), ((960 * 2, 960), ({0, 1, 2, 3}, {})), ( (480, 480, 480, 480, 480, 480), ({0, 1, 2, 3}, {}, {0, 1, 2, 3}, {}, {0, 1, 2, 3}, {}), ), ], (4, 2): [ ((960, 960), ({0, 1, 2, 3}, {})), ((960 * 3 // 2, 480), ({0, 1, 2, 3}, {})), ((480, 480, 480, 480), ({0, 1, 2, 3}, {}, {0, 1, 2, 3}, {})), ( (480, 240, 480, 240, 480), ({0, 1, 2, 3}, {}, {0, 1, 2, 3}, {}, {0, 1, 2, 3}), ), ((960 * 2, 960 * 2), ({0, 1, 2, 3}, {})), ], (4, 3): [((960, 960), ({0, 1, 2, 3}, {})), ((960 * 10 // 6, 960 * 2 // 6), ({0, 1, 2, 3}, {})), ( (960 * 2 // 3, 320, 960 * 2 // 3, 320), ({0, 1, 2, 3}, {}, {0, 1, 2, 3}, {}), ), ((960 * 2, 960 * 2), ({0, 1, 2, 3}, {}))] } chord_accompaniment = chord_accompaniments[Voice.time_sig] if Voice.time_sig[0] == 4: if Voice.chord_acceleration: chord_accompaniment.pop() note_durations, voices_used = random.choice(chord_accompaniment) chord_units_used = sum(note_durations) // Voice.max_note_duration if chord_units_used == 0: chord_units_used = 1 print(f"Chord units used: {chord_units_used}") all_note_durations = [] all_voices_used = [] note_index = 0 for _ in range(chord_units_used): all_note_durations.append([]) all_voices_used.append([]) while sum(all_note_durations[-1]) < Voice.max_note_duration: all_note_durations[-1].append(note_durations[note_index]) all_voices_used[-1].append(voices_used[note_index]) note_index += 1 print(f"All note durations: {all_note_durations}") print(f"All voices used: {all_voices_used}") num_chords = len(Voice.chord_sequence) self.current_time = Voice.pickup_duration self.add_chord_section(0, -2, all_note_durations, all_voices_used, chord_units_used) end_note_durations = ((Voice.max_note_duration, ), (Voice.max_note_duration, )) end_voices_used = [[{0, 1, 2, 3}], [{}]] if self.repeat_ending: self.add_chord_section(-2 % num_chords, num_chords, all_note_durations, all_voices_used, chord_units_used) self.add_chord_section(-4 % num_chords, -2, all_note_durations, all_voices_used, chord_units_used) self.add_chord_section(-2 % num_chords, num_chords, end_note_durations, end_voices_used, 2)