Created
January 17, 2015 14:56
-
-
Save SonOfLilit/85d4e9e76153a96819ad 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
# 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') |
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 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