Skip to content

Instantly share code, notes, and snippets.

@wilm0x42
Created October 10, 2020 05:03
Show Gist options
  • Save wilm0x42/dab076ba861bd439d4d26a82bc977717 to your computer and use it in GitHub Desktop.
Save wilm0x42/dab076ba861bd439d4d26a82bc977717 to your computer and use it in GitHub Desktop.
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