# graph_to_music.py -rw-r--r-- 1.7 KiB View raw
                                                                                
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
# Better version of this https://paste.sr.ht/~jasper/59b874bc90e521ef100e7691204ef82cd55cabbc
#
# .. many possible variations on this, just edit the code.
#
# (obvious)Ideas:
# * output to a synthesizer.
# * like drawing it as it goes would be good.

# Played the midi with:
# Ran fluidsynth
# `fluidsynth -is -a pulseaudio -m alsa_seq -r 48000 "$SOME_SOUNDFONT.sf"`
# `/usr/share/soundfonts/FluidR3_GM.sf2` was on my disc, not ideal.
# Look for ports with `aplaymidi -l`
# `aplaymidi --port $PORT $FILE.mid`

from midiutil import MIDIFile  # These two lines are dependencies
import pygraphviz

import sys, random, os
inp_file= sys.argv[1]  # Graphviz .dot file.

def midi_graph_shared(midi, g, start=None, max_t=1000,
                      duration=1, volume=100, track=0, channel=0, dt=1):
    cur = random.choice(list(g.iternodes())) if start is None is None else start
    cnts = dict()
    opts = list(g.iterneighbors(cur))  # NOTE: assuming its constant.
    t = 0
    while t < max_t:
        cnt = cnts.get(cur, 0)
        cnts[cur] = cnt + 1
        cur = opts[(cnt - 1)%len(opts)]
        next_opts = list(g.iterneighbors(cur))
        n_overlap = len(set(next_opts).intersection(opts))
        midi.addNote(track, channel, 52 + len(opts) + 8*n_overlap, t, duration, volume)
        opts = next_opts

        t += dt

midi = MIDIFile(1)
midi.addTempo(0, 0, 240)

g = pygraphviz.AGraph(filename=inp_file)

print("generate midi")
midi_graph_shared(midi, g)

print(f"write midi {inp_file}.mid")
with open(f"{inp_file}.mid", "wb") as output_file:
    midi.writeFile(output_file)

midi.close()

print(f"Graphviz image `{inp_file}.png` of midi for good measure")
os.execvp('dot', ['dot', inp_file, '-Tpng', '-o', inp_file + ".png"])