Differences From Artifact [8ccb7c15cbf62012]:
- Executable file
cfg.py
- 2010-11-04 20:07:05 - part of checkin [6bfc2449ff] on branch ply - Generates a score randomly. Too little guarantee of program length (lots of empty or single-phrase strings) (user: spiffytech@gmail.com) [annotate]
To Artifact [6468589fdee69e24]:
- Executable file
cfg.py
- 2011-10-20 18:37:19 - part of checkin [4f5bc3936f] on branch refactor - Syncing now works (user: brian) [annotate]
- 2011-10-20 18:38:08 - part of checkin [8c559c112b] on branch develop - Merged the refactor branch into the main develop branch (user: brian) [annotate]
- 2011-10-20 18:44:05 - part of checkin [ba64e400ba] on branch ply - Yay, first alpha release. program supports syncing and basic section/subsection ordering. (user: brian) [annotate]
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 2
> 3 from __future__ import division
> 4 import ipdb
3 import os 5 import os
4 import random 6 import random
5 import sys 7 import sys
6 import time 8 import time
> 9
> 10 import parse
> 11 import topsort
> 12 import yaml
> 13
> 14 import tree
> 15
7 random.seed(time.time()) 16 random.seed(time.time())
8 17
9 grammars = { <
10 "u": ["I V I IV u", "e"], <
> 18 def main():
> 19 composition = {
> 20 "fm_test": {
> 21 "intro": {
> 22 "melody": { # Instrument 'melody'
> 23 "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(no
11 "e": [""], | 24 "octave": 8,
> 25 "duration": 10,
> 26 "grammars": { # Notes for this instrument to use in this pi
> 27 "u": ["G/2 G/2 | G/4 G/4 A/4 A/4 | A/2 A/2 | G | G | A |
> 28 "w": ["E | E | F | F | G/2 G/2 | G3 (u)"],
12 } | 29 },
> 30 },
> 31 },
> 32 },
> 33 "verse1": {
> 34 "intro": {
> 35 "melody": { # Instrument 'melody'
> 36 "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(no
> 37 "octave": 8,
> 38 "duration": 10,
> 39 "grammars": { # Notes for this instrument to use in this pi
> 40 "u": ["G/2 G/2 | G/4 G/4 A/4 A/4 | A/2 A/2 | G | G | A |
> 41 "w": ["E | E | F | F | G/2 G/2 | G3 (u)"],
> 42 },
> 43 },
> 44 },
> 45 "body": {
> 46 "melody": { # Instrument 'melody'
> 47 "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(no
> 48 "octave": 8,
> 49 "duration": 10,
> 50 "grammars": { # Notes for this instrument to use in this pi
> 51 "u": ["C | G/2 G/2 | G/2 G/2 | C | B, | F' | C | F | C |
> 52 "w": ["E/4 A/4 D/4 G/4 | F/4 F/4 B2 | (u)"],
> 53 },
> 54 },
> 55 },
> 56 "outro": {
> 57 "melody": { # Instrument 'melody'
> 58 "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(no
> 59 "octave": 8,
> 60 "duration": 10,
> 61 "grammars": { # Notes for this instrument to use in this pi
> 62 "u": ["C/4 C/4 C/4 C/4 | z2"],
> 63 },
> 64 },
> 65 },
> 66 },
> 67 "verse2": {
> 68 "body": {
> 69 "melody": { # Instrument 'melody'
> 70 "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(no
> 71 "octave": 8,
> 72 "duration": 30,
> 73 "grammars": { # Notes for this instrument to use in this pi
> 74 "u": ["C | C | C | C | F/2 F/2 | F/2 F/2 | (u)", "D | D
> 75 },
> 76 },
> 77 "harmony": { # Instrument 'melody'
> 78 "score_line": "i3 %(time)f %(duration)f 4000 %(octave)d.%(no
> 79 "octave": 8,
> 80 "duration": 30,
> 81 "grammars": { # Notes for this instrument to use in this pi
> 82 "u": ["C | C | z | C | C | z/2 F/4 F/2 F/2 | F/2 F/2 | z
> 83 },
> 84 },
> 85 "percussion": { # Instrument 'melody'
> 86 "score_line": "i1 %(time)f %(duration)f 7000 %(octave)d.%(no
> 87 "octave": 8,
> 88 "duration": 30,
> 89 "grammars": { # Notes for this instrument to use in this pi
> 90 "u": ["C/4 C/4 C/4 C/4 | F/2 F/2 | F/2 F/2 | (u)", "D/4
> 91 "v": ["C | D | E | F | E | D | C | (u)",],
> 92 },
> 93 },
> 94 },
> 95 "outro": {
> 96 "percussion": { # Instrument 'melody'
> 97 "score_line": "i1 %(time)f %(duration)f 7000 %(octave)d.%(no
> 98 "octave": 8,
> 99 "duration": 30,
> 100 "grammars": { # Notes for this instrument to use in this pi
> 101 "u": ["C/4 C/4 C/4 C/4"],
> 102 },
> 103 },
> 104 },
> 105 },
> 106 "sync_test": {
> 107 "body": {
> 108 "lead_instr": { # Instrument 'melody'
> 109 "score_line": "i1 %(time)f %(duration)f 7000 %(octave)d.%(no
> 110 "octave": 8,
> 111 "duration": 30,
> 112 "grammars": { # Notes for this instrument to use in this pi
> 113 "u": ["D/4 D/4 D/4 D/4"],
> 114 "v": ["C/4 C/4 C/4 C/4"],
> 115 },
> 116 },
> 117 "follow_instr": { # Instrument 'melody'
> 118 "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(no
> 119 "sync": "lead_instr",
> 120 "octave": 8,
> 121 "duration": 30,
> 122 "grammars": { # Notes for this instrument to use in this pi
> 123 "u": ["D/4 D/4 D/4 D/4"],
> 124 "v": ["C/4 C/4 C/4 C/4"],
> 125 },
> 126 },
> 127 },
> 128 },
13 | 129 }
14 score = "u" <
> 130 print '''f1 0 512 10 1
> 131 f2 0 8192 10 .24 .64 .88 .76 .06 .5 .34 .08
> 132 f3 0 1025 10 1
> 133 t 0 100
> 134 '''
15 135
> 136 section_start = 0
> 137 # for section in ["verse1", "verse2"]:
> 138 for section in ["sync_test"]:
> 139 print "; Section " + section
> 140 subsection_start = section_start
> 141 section = composition[section]
> 142 for subsection in ["intro", "body", "outro"]:
> 143 try:
> 144 print "; Subsection " + subsection
> 145 subsection = section[subsection]
> 146
> 147 unordered_instrs = []
> 148 for instr in subsection:
> 149 subsection[instr]["name"] = instr
> 150 if not "sync" in subsection[instr].keys():
> 151 subsection[instr]["sync"] = None
> 152 unordered_instrs.append([subsection[instr]["sync"], instr])
> 153 ordered_instrs = topsort.topsort(unordered_instrs)
> 154 ordered_instrs.remove(None) # None used as a placeholder for so
> 155
> 156 instrs = []
> 157 syncs = {}
> 158 for instr in ordered_instrs:
> 159 print ";Instrument " + instr
> 160 instr = subsection[instr]
> 161 max_time = instr["duration"]
> 162 instr_score, syncs = render_instr(instr, syncs, max_time)
> 163 instrs.append(instr_score)
> 164 for line in generate_csound_score(instr_score, instr["score_
16 print score | 165 print line
> 166 longest_score = max(instrs, key=lambda i: score_len(i))
> 167 subsection_start += score_len(longest_score)
> 168 section_start += score_len(longest_score)
> 169 except KeyError:
> 170 pass
> 171
> 172
> 173 def render_instr(instr, syncs, max_time):
> 174 for g in instr["grammars"]:
> 175 for i in range(len(instr["grammars"][g])):
> 176 instr["grammars"][g][i] = parse.parse(instr["grammars"][g][i])
> 177
> 178 score= []
> 179 try:
> 180 score, syncs = choose_phrase(instr, syncs, 0, max_time)
> 181
17 while 1: | 182 while True:
18 found_substitution = False <
> 183 score_index_to_replace = None
> 184 for item in range(len(score)): # Optimize this by caching the index
> 185 if isinstance(score[item], tree.Tree):
> 186 score_index_to_replace = item
> 187 if score_index_to_replace is None:
> 188 raise ValueError("No more nodes to fill in")
> 189
> 190 time_remaining = max_time - score_len(score)
> 191 new_phrase, syncs = choose_phrase(instr, syncs, score_len(score), ti
> 192 score = score[:node_index-1] + new_phrase + score[node_index+1:]
> 193
> 194 except ValueError:
> 195 return (score, syncs)
> 196
> 197
> 198 def choose_phrase(instr, syncs, current_time, time_remaining):
> 199 '''Filters grammars for ones that match the sync option, and phrases that fi
> 200 time_filtered_grammars = {}
> 201 for grammar in instr["grammars"]:
> 202 time_filtered_grammars[grammar] = get_phrases_that_fit(instr["grammars"]
> 203 if len(time_filtered_grammars.keys()) == 0:
> 204 raise ValueError("No available grammars that will fit in the score")
> 205
> 206 grammar = None
> 207 # if instr["name"] == "follow_instr":
> 208 # ipdb.set_trace()
> 209 if instr["sync"] is not None:
> 210 guiding_instr = instr["sync"]
> 211 sync_node = get_sync_node_at_time(syncs[guiding_instr], current_time)
> 212 if sync_node in time_filtered_grammars.keys():
> 213 grammar = sync_node
> 214 if grammar is None:
> 215 grammar = random.choice(time_filtered_grammars.keys())
> 216 phrases = time_filtered_grammars[grammar]
> 217 if instr["name"] not in syncs.keys():
> 218 syncs[instr["name"]] = []
> 219 syncs[instr["name"]].append({"node": grammar, "time": current_time})
> 220
> 221 return random.choice(phrases), syncs
> 222
> 223
> 224 def get_phrases_that_fit(grammar, time_remaining):
> 225 valid_phrases = []
19 for key,value in grammars.iteritems(): | 226 for phrase in grammar:
20 print "key, value =", key, value <
21 if score.find(key) != -1: <
22 print "here" <
23 found_substitution = True <
24 while score.find(key) != -1: <
25 score = score.replace(key, random.choice(grammars[key]), 1) <
> 227 if score_len(phrase) <= time_remaining:
> 228 valid_phrases.append(phrase)
> 229 return valid_phrases
> 230
> 231
> 232 def get_sync_node_at_time(syncs, t):
> 233 for s in range(len(syncs)):
> 234 if syncs[s]["time"] >= t:
> 235 return syncs[s]["node"]
> 236
> 237
> 238 def score_len(score):
> 239 total = 0
26 print score | 240 for n in score:
27 time.sleep(.25) <
28 if found_substitution is False: <
29 break <
> 241 if not isinstance(n, tree.Tree):
> 242 total += n.duration
> 243 return total
> 244
> 245
> 246 def generate_csound_score(score, score_line, t):
> 247 csound_note_values = {
> 248 "C": "00",
> 249 "C#": "01",
> 250 "D": "02",
> 251 "D#": "03",
> 252 "E": "04",
> 253 "F": "05",
> 254 "F#": "06",
> 255 "G": "07",
> 256 "G#": "08",
> 257 "A": "09",
> 258 "A#": "10",
> 259 "B": "11",
> 260 }
> 261 csound_score = []
30 print score | 262 for token in score:
> 263 if isinstance(token, parse.Chord): # Chords
> 264 for note in token.chord:
> 265 note = csound_note_values[note]
> 266 csound_score.append(score_line % {"time": t, "octave": token.oct
> 267 elif isinstance(token, parse.Note): # Individual notes
> 268 note = csound_note_values[token.value]
> 269 csound_score.append(score_line % {"time": t, "octave": token.octave,
> 270 elif isinstance(token, tree.Tree):
> 271 continue
> 272 t += token.duration
> 273 return csound_score
> 274
> 275
> 276 if __name__ == "__main__": main()