-
Notifications
You must be signed in to change notification settings - Fork 1
/
mutate.py
140 lines (103 loc) · 4.73 KB
/
mutate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import copy
import numpy as np
import random
import math
# a genotype gets mutated for mutation_magnitude% of the mutable traits
def mutate(song, mutation_size):
mutation_types = range(1, 6+1)
mutations_tomake = random.sample(mutation_types,
math.ceil(len(mutation_types) * mutation_size))
if 1 in mutations_tomake:
mutate_tempo(song)
if 2 in mutations_tomake:
mutate_preset(song)
if 3 in mutations_tomake:
new_melody_bar(song, 'solo')
if 4 in mutations_tomake:
new_melody_bar(song, 'percussion')
if 5 in mutations_tomake:
symmetric_solo_bar(song)
if 6 in mutations_tomake:
new_chord(song)
def mutate_tempo(song):
# perturbs with a least 20 bpm, otherwise it is hard to notice
perturb = np.random.normal(0, 20, 1)[0]
if perturb > 20:
perturb = 20
if perturb < -20:
perturb = -20
song.tempo = int(min(max(song.tempo + perturb,
song.tempo_pool['min']),
song.tempo_pool['max']))
def mutate_preset(song):
song.preset = random.choice(song.presets)
def new_melody_bar(song, track):
if track == 'solo':
ajs_bars = [i for i in range(0, len(song.roles)) if song.roles[i] == 'aj']
bar = random.choice(ajs_bars)
local_scale_keyboard = song.solo_scale_keyboard()
if track == 'percussion':
bar = random.choice(range(0, song.num_bars))
local_scale_keyboard = song.percussion_scale_keyboard()
melody_timeline = bar * song.times * song.beat
if bar > 0 and len(song.genotype[track][bar-1]) > 0:
idx_previous_pitch = song.genotype[track][bar-1][-1]['pitch']
idx_previous_pitch = local_scale_keyboard.index(idx_previous_pitch)
else:
idx_previous_pitch = None
melody_bar = []
song.get_melody_bar(melody_bar,
local_scale_keyboard,
song.tracks[track],
song.tracks[track],
melody_timeline,
idx_previous_pitch)
song.genotype[track][bar] = melody_bar
def symmetric_solo_bar(song):
track = 'solo'
type_symmetry = random.uniform(0, 1)
bar_content = []
while len(bar_content) == 0:
bar = random.choice(range(0, song.num_bars - 1))
bar_content = song.genotype[track][bar]
# if bar is not silent
if len(bar_content) > 0:
song.genotype[track][bar+1] = []
# repeats bar identically
if type_symmetry <= 0.5:
for note in bar_content:
song.genotype[track][bar+1].append({'track': note['track'],
'channel': note['channel'],
'pitch': note['pitch'],
'duration': note['duration'],
'time': note['time'] + song.times * song.beat})
# repeats bar invertedly
if type_symmetry > 0.5:
bar_content_inverted = copy.copy(bar_content)[::-1]
bar_time = (bar+1) * song.times * song.beat
for note in bar_content_inverted:
song.genotype[track][bar+1].append({'track': note['track'],
'channel': note['channel'],
'pitch': note['pitch'],
'duration': note['duration'],
'time': bar_time})
bar_time += note['duration']
def new_chord(song):
# fixed progressions do not suffer mutation
if song.progression_type == 'free':
# first and last harmony do not change
bar = random.choice(range(1, song.num_bars - 2))
# changes base harmony
harmony_timeline = bar * song.times * song.beat
chord = []
local_scale_keyboard = song.progression_scale_keyboard()
pitch = random.choice(local_scale_keyboard)
song.compose_chord(chord, pitch, song.genotype['harmony'][bar][0]['track'],
song.genotype['harmony'][bar][0]['channel'], harmony_timeline)
song.genotype['harmony'][bar] = chord
# change bass
bass_progression_bar = []
song.compose_bass_progression_bar(bass_progression_bar, pitch,
song.genotype['bass'][bar][0]['track'],
song.genotype['bass'][bar][0]['channel'], harmony_timeline)
song.genotype['bass'][bar] = bass_progression_bar