Created
October 10, 2020 05:03
-
-
Save wilm0x42/dab076ba861bd439d4d26a82bc977717 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import itertools | |
import random | |
import time | |
import rtmidi | |
midiout = rtmidi.MidiOut() | |
available_ports = midiout.get_ports() | |
if available_ports: | |
midiout.open_port(0) | |
else: | |
midiout.open_virtual_port("Ambient MIDI stuff :)") | |
interval_dissonances = [9.0, 3.0, 0.7, 0.2, 0.1, 0.3, 3.0, 0.1, 1.5, 0.3, 1.7, 0.5, 0.0, 3.0, 0.3, 1.4, 0.1, 1.7, 5.0, 0.1] | |
scalar_tensions = [0.0, 5.0, 0.4, 5.0, 0.2, 1.0, 4.0, 0.1, 3.6, 0.4, 2.4, 0.7, 0.0] | |
prev_chord = [0, 7, 12, 16, 19, 24] | |
def octave_clamp(val, low, high): | |
while val < low: | |
val += 12 | |
while val > high: | |
val -= 12 | |
return val | |
def rate_keycenter_tension(chord): | |
total_tension = 0 | |
for v in chord: | |
v = octave_clamp(v, 0, len(scalar_tensions)-1) | |
total_tension += scalar_tensions[v] | |
# pay special attention to bass and soprano notes | |
soprano = octave_clamp(max(chord), 0, len(scalar_tensions)-1) | |
total_tension += scalar_tensions[soprano] * 2 | |
bass = octave_clamp(min(chord), 0, len(scalar_tensions)-1) | |
total_tension += scalar_tensions[bass] * 2 | |
return total_tension/(len(chord)/3) | |
def rate_intervalic_dissonance(chord): | |
total_dissonance = 0 | |
for biad in itertools.permutations(chord, 2): | |
interval = abs(biad[0] - biad[1]) | |
interval = octave_clamp(interval, 0, len(interval_dissonances)-1) | |
total_dissonance += interval_dissonances[interval] | |
return total_dissonance/(len(chord)/3) | |
def rate_forward_similitude(chord): | |
shared_tones = len(list(set(prev_chord).intersection(chord))) | |
if min(prev_chord) == min(chord): # bass and soprano notes are more important | |
shared_tones += 2 | |
if max(prev_chord) == max(chord): | |
shared_tones += 2 | |
intervalic_dissonance_diff = abs(rate_intervalic_dissonance(chord) - rate_intervalic_dissonance(prev_chord)) | |
return intervalic_dissonance_diff + shared_tones | |
def rate_enthusiasm(chord): | |
overall_spread = abs(min(chord) - max(chord)) | |
voice_count = len(chord) | |
forward_motion = max(rate_forward_similitude(chord), 1) | |
return (overall_spread + voice_count)/forward_motion | |
def decide_chord(target_enth, vox_lead_strength): | |
best_enth = 1000 | |
new_chord = prev_chord | |
best_chord = new_chord | |
for n in range(20000): # number of randomized attempts | |
new_chord = prev_chord[:] | |
for v in range(len(new_chord)): | |
diff = random.randint(int(vox_lead_strength)*-1, int(vox_lead_strength)) | |
new_chord[v] += diff | |
enth = rate_enthusiasm(new_chord) + rate_intervalic_dissonance(new_chord)*4 + rate_keycenter_tension(new_chord)*3 + len(list(set(prev_chord).intersection(new_chord))) | |
#print(enth) | |
if abs(target_enth - enth) < abs(target_enth - best_enth): | |
best_enth = enth | |
best_chord = new_chord | |
print("Final variation: %f" % best_enth) | |
return best_chord | |
time.sleep(1) | |
cur_chord = [] | |
try: | |
while True: | |
target = random.randint(10, 50) / 1.0 | |
print("Target: %f" % target) | |
cur_chord = decide_chord(target, 3) | |
prev_chord = cur_chord | |
print(cur_chord) | |
for n in cur_chord: | |
n = octave_clamp(n, 0, 128) | |
note_on = [0x80, 60 + n - 12, 0] | |
midiout.send_message(note_on) | |
for n in cur_chord: | |
n = octave_clamp(n, 0, 128) | |
note_on = [0x90, 60 + n - 12, 64] | |
midiout.send_message(note_on) | |
time.sleep(2*random.randint(1,2)) | |
except KeyboardInterrupt: | |
for n in cur_chord: | |
n = octave_clamp(n, 0, 128) | |
note_on = [0x80, 60 + n - 12, 0] | |
midiout.send_message(note_on) | |
print("\nMIDI notes yeeted") | |
del midiout | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment