Spaces:
Build error
Build error
import random | |
import numpy as np | |
from configurations.read_configuration import get_conf_n_sample, get_conf_sample_rate, get_conf_max_n_notes | |
def get_random_note_type_index(distribution): | |
"""A helper method that randomly chooses next note type based on a distribution | |
Parameters | |
---------- | |
distribution: List[float] | |
Note type distribution. | |
Returns | |
------- | |
midi: int | |
Random type index. | |
""" | |
r = np.random.random() | |
for i in range(len(distribution)): | |
r = r - distribution[i] | |
if r < 0: | |
return i | |
return len(distribution) - 1 | |
# Todo: rewrite this part | |
def to_onsets_in_seconds(bpm, notes): | |
"""A helper method that transform a list of note types into a list of note onsets (in seconds) | |
Parameters | |
---------- | |
bpm: float | |
BPM | |
notes: List[int] | |
Returns | |
------- | |
midi: int | |
Random type index. | |
""" | |
full_note_length = 4 * 60 / bpm | |
onsets = [0] | |
for i in range(len(notes)): | |
onsets.append(onsets[i] + full_note_length * notes[i]) | |
return onsets | |
class RandomRhythm: | |
"""First component in the random midi pipeline responsible for random rhythm (note onsets) generating""" | |
def __init__(self): | |
self.note_types = [0, 1, 3 / 4, 0.5, 3 / 8, 0.25, 1 / 8] | |
self.first_note_type_distribution = np.array([0, 0.2, 0.05, 0.25, 0.05, 0.3, 0.15]) | |
self.rhythm_generation_matrix = np.array([ | |
[0.1, 0.1, 0.25, 0.1, 0.25, 0.2], | |
[0.05, 0.25, 0.25, 0.05, 0.3, 0.1], | |
[0.1, 0.1, 0.3, 0.05, 0.35, 0.1], | |
[0.05, 0.05, 0.2, 0.2, 0.25, 0.25], | |
[0.1, 0.05, 0.1, 0.05, 0.4, 0.3], | |
[0.1, 0.05, 0.1, 0.1, 0.3, 0.35], | |
]) | |
# self.bpm = bpm | |
self.rhythm_duration = np.array([0, 1, 3 / 4, 0.5, 3 / 8, 0.25]) | |
self.audio_length = get_conf_n_sample() / get_conf_sample_rate() | |
self.bpm_range = [90, 100, 110, 120, 130, 140, 150, 160, 170] | |
self.max_n_notes = get_conf_max_n_notes() | |
def __call__(self, strategy: str, *args, **kwargs): | |
"""Choose required strategy to generate random rhythm (note onsets). | |
Parameters | |
---------- | |
strategy: str | |
Strategy names for random rhythm (see Readme). | |
Returns | |
------- | |
onsets: List[float] | |
A list of floats referring to note onsets in seconds. | |
""" | |
if strategy == 'bpm_based_rhythm': | |
rhythm = self.get_bpm_based_rhythm() | |
elif strategy == 'free_rhythm': | |
rhythm = self.get_free_rhythm() | |
elif strategy == 'single_note_rhythm': | |
rhythm = self.get_single_note() | |
else: | |
rhythm = [0.0, 1, 2, 3, 4] | |
return rhythm[:self.max_n_notes] | |
def get_bpm_based_rhythm(self): | |
"""Uses "bpm_based_rhythm" strategy to generate random rhythm (see Readme).""" | |
# Todo: clean up this part | |
bpm = random.choice(self.bpm_range) | |
first_note = get_random_note_type_index(self.first_note_type_distribution) | |
note_type_indexes = [first_note] | |
current_note_type = first_note | |
while True: | |
current_note_type = get_random_note_type_index(self.rhythm_generation_matrix[current_note_type - 1]) + 1 | |
note_type_indexes.append(current_note_type) | |
# Random early stop | |
if np.random.random() < 9 / bpm: | |
break | |
notes = [self.note_types[note_type_index] for note_type_index in note_type_indexes] | |
onsets = to_onsets_in_seconds(bpm, notes) | |
return onsets | |
def get_free_rhythm(self): | |
"""Uses "free_rhythm" strategy to generate random rhythm (see Readme).""" | |
n_notes = np.random.randint(int(self.max_n_notes * 0.6), self.max_n_notes) | |
# n_notes = np.random.randint(int(1), self.max_n_notes) | |
onsets = np.random.rand(n_notes) | |
onsets.sort() | |
# Avoid notes too close together | |
pre = onsets[0] | |
n_removed = 0 | |
for i in range(len(onsets)-1): | |
index = i - n_removed + 1 | |
if (onsets[index] - pre) < 0.05: | |
new_onsets = np.delete(onsets, index) | |
onsets = new_onsets | |
n_removed = n_removed + 1 | |
else: | |
pre = onsets[index] | |
return ((onsets - onsets[0])*self.audio_length).tolist() | |
def get_single_note(self): | |
return [0.0] | |