Skip to content

Instantly share code, notes, and snippets.

@SonOfLilit
Created January 17, 2015 14:56
Show Gist options
  • Save SonOfLilit/85d4e9e76153a96819ad to your computer and use it in GitHub Desktop.
Save SonOfLilit/85d4e9e76153a96819ad to your computer and use it in GitHub Desktop.
# a beginning of a song, using my work-in-progress midi API `melody` at https://github.com/SonOfLilit/melody
from music import *
def walk(duration, scale, bassline):
result = []
for i in xrange(len(bassline) - 1):
n = dict(bassline[i])
result.append(n)
if n['pitch'] == play.EMPTY:
continue
n['duration'] -= 2*duration
t = n['time'] + n['duration']
p1 = n['pitch']
p2 = bassline[i+1]['pitch']
direction = 1 if p1 > p2 else -1
p1 = scale_up(direction, scale, p1)
p2 = scale_up(-direction, scale, p2)
result += notes([duration], [p2, p1], time=t, like=n)
# I like the bug where before EMPTY there is half a walk, but not at the end
if result[-1]['pitch'] == play.EMPTY:
result[-2]['pitch'] = play.EMPTY
result.append(bassline[-1])
return result
scale = SCALE_C_MAJ
progression = [C_0, C_1, B_0, G_0] * 2 + [D_0, D_1, C_1, A_0] + [D_0, E_1, A_1, play.EMPTY]
bass_progression = p_transpose(progression, octaves=2)
bass = notes([700, 800, 800, 800, -100], bass_progression, channel=1)
bass = walk(100, scale, bass)
chords = harmonize(notes([800], progression, vel=80, channel=0), [CHORD_MAJ], scale, octave=4)
click = notes([400, 400, 400, 100, 100, 100, 100], [A_4, A_4, A_4, A_3, A_3, A_3, A_3] * 8, vel=50, channel=2)
for i in xrange(1, 5): click[-i]['pitch'] = 0
def sax_arpeggio(x):
sax = notes([100], x, channel=3)
sax = join([
sax,
scale_up(3, scale, sax),
scale_up(6, scale, sax),
notes([100, 100, 600], x[3:5] + [play.EMPTY], channel=3),
])
return sax
sax = join([
sax_arpeggio([C_5, G_5, A_5, C_6, D_6, C_6, A_5, G_5]),
sax_arpeggio([C_5, G_5, A_5, D_6, E_6, C_6, A_5, G_5]),
sax_arpeggio([C_5, E_6, C_6, A_5, G_5, G_5, A_5, D_6]),
],
time=800 - 100)
sax[-1]['duration'] -= 200
sax = join([
sax,
sax_arpeggio([E_6, C_6, A_5, G_5] + [play.EMPTY] * 4),
])
sax[-3]['pitch'] = E_5
sax[-2]['pitch'] = C_6
sax[-3]['time'] -= 100
sax[-2]['time'] -= 100
rythm = join([
drums(100, [
(36, [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, .5, .5, 0, 0.3, 0, 0.3]),
(38, [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0.5]),
(44, [1, 0] * 8),
],
k=(i/64. + 0.25)**2)
for i in xrange(32)])
pattern = play.pattern(tempo=200)
pattern.append(rythm)
for track in [bass, chords]:
track = join([track] * 4)
pattern.append(track)
pattern.append(click)
pattern.append(join([sax], time=1600*8))
play.write(pattern, 'attempto.mid')
import play
from midi import *
import random
from itertools import cycle, islice
random.seed(0)
def notes(durations, pitches, time=0, vel=100, channel=0, like=None):
if like:
vel = like['velocity']
channel = like['channel']
result = []
durations = cycle(durations)
pitches = iter(pitches)
for duration in durations:
if duration < 0:
duration = -duration
chord = play.EMPTY
else:
try:
chord = pitches.next()
except StopIteration:
break
if isinstance(chord, int):
chord = (chord,)
for pitch in chord:
note = {'channel': channel, 'time': time, 'duration': duration, 'pitch': pitch, 'velocity': vel}
result.append(note)
time += duration
return result
def delay(time, segment):
result = []
for note in segment:
note = dict(note)
note['time'] += time
result.append(note)
return result
def join(segments, time=0):
result = []
for segment in segments:
if not segment:
continue
result += delay(time, segment)
time += segment[-1]['time'] + segment[-1]['duration']
return result
def repeat(n, segment):
return join([segment] * n)
def important(n, importance, notes):
return [note for i, note in enumerate(notes) if importance[i] is not None and importance[i] < n]
def flatten(lists):
return [x for l in lists for x in l]
assert flatten([[0, 1, 2], [3], [4, 5]]) == range(6)
def weighted(how_many, weights):
result = [False] * len(weights)
while sum(result) < how_many:
left = how_many - sum(result)
population = flatten([i] * weights[i] for i in xrange(len(weights)) if not result[i])
for i in random.sample(population, left):
result[i] = True
return result
def mask(m, sequence):
assert len(m) == len(sequence), (len(m), len(sequence))
return [s for i, s in enumerate(sequence) if m[i]]
assert mask([True, False, True, False, False, True], 'abcdef') == list('acf')
def connect(notes):
result = []
last = {'time': 0}
for note in notes:
last['duration'] = note['time'] - last['time']
last = dict(note)
result.append(last)
return result
def harmonize(notes, chords, scale, octave=None):
result = []
chords = cycle(chords)
for note, chord in zip(notes, chords):
if note['pitch'] == play.EMPTY:
result.append(note)
continue
for delta in (0,) + chord:
note = dict(note)
note['pitch'] = scale_up(delta, scale, note['pitch'])
if octave:
note['pitch'] = note['pitch'] % 12 + octave * 12
result.append(note)
return result
SCALE_UP = {}
def scale_up(amount, scale, notes):
if isinstance(notes, int):
return scale_up_pitch(amount, scale, notes)
result = []
for note in notes:
note = dict(note)
note['pitch'] = scale_up_pitch(amount, scale, note['pitch'])
result.append(note)
return result
def scale_up_pitch(amount, scale, pitch):
if pitch == play.EMPTY:
return pitch
if scale not in SCALE_UP:
SCALE_UP[scale] = {scale[i]: scale[(i+1)%len(scale)] for i in xrange(len(scale))}
octave, pitch = pitch / 12, pitch % 12
octave += amount / len(scale)
amount = amount % len(scale)
for _ in xrange(amount):
if pitch > SCALE_UP[scale][pitch]:
octave += 1
pitch = SCALE_UP[scale][pitch]
return octave * 12 + pitch
C_PENTA = (C_0, D_0, E_0, G_0, A_0)
assert scale_up(1, C_PENTA, C_0) == D_0
assert scale_up(1, C_PENTA, E_0) == G_0
assert scale_up(1, C_PENTA, A_0) == C_1
assert scale_up(1, C_PENTA, C_3) == D_3
assert scale_up(3, C_PENTA, C_3) == G_3
assert scale_up(8, C_PENTA, C_3) == G_4
assert scale_up(-1, C_PENTA, D_3) == C_3
assert scale_up(-1, C_PENTA, G_3) == E_3
assert scale_up(-1, C_PENTA, C_3) == A_2
assert scale_up(-5, C_PENTA, C_3) == C_2
def transpose(notes, delta=0, octaves=0):
delta += 12 * octaves
result = []
for note in notes:
note = dict(note)
if note['pitch'] != play.EMPTY:
note['pitch'] += delta
result.append(note)
return result
def p_transpose(pitches, delta=0, octaves=0):
delta += 12 * octaves
result = []
for pitch in pitches:
if pitch != play.EMPTY:
pitch += delta
result.append(pitch)
return result
SCALE_INDEXES = {}
def rescale(old, new, notes):
if old not in SCALE_INDEXES:
SCALE_INDEXES[old] = {old[i]: i for i in xrange(len(old))}
result = []
for note in notes:
note = dict(note)
pitch = note['pitch']
if note['pitch'] != play.EMPTY:
octave, pitch = pitch / 12, pitch % 12
note['pitch'] = 12 * octave + new[SCALE_INDEXES[old][pitch]]
result.append(note)
return result
SCALE_C_MAJ = (C_0, D_0, E_0, F_0, G_0, A_0, B_0)
SCALE_C_MIN = (C_0, D_0, Ds_0, F_0, G_0, A_0, As_0)
SCALE_G_GYP = (G_0, A_0, As_0, C_1, Ds_1, F_1, Fs_1)
CHORD_MAJ = (2, 2)
CHORD_7 = (2, 2, 2)
def drums(step, tracks, repeat=1, k=1.0):
assert k <= 1
result = []
time = 0
length = len(tracks[0][1] if tracks else [])
for _ in xrange(repeat):
for i in xrange(length):
for instrument, track in tracks:
x = random.random()
y = x ** (0.1 / (track[i] * k + 0.005))
assert 0 <= y <= 1, (x, y)
vel = int(127 * y)
print x, vel
if vel < 15:
inst = play.EMPTY
vel = 100
else:
inst = instrument
result.append({'channel': 9, 'pitch': inst, 'time': time, 'duration': step, 'velocity': vel})
time += step
return result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment