spiffyscore

Diff
Login

Differences From Artifact [e9b556b4f3]:

To Artifact [6468589fde]:


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
55
56
57
58






























59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
















74
75
76
77

78
79
80
81
82





83


84















85
86
87






88
89
90
91
92
93






94
95

96
97
98


99
100
101


102
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120



































121
122
123
124
125
126












127
128
129




130
131


132
133
134

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

155
156
157




158
159
160
161
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
55
56








57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72






73
74
75
76
77
78
79




80
81
82



83
84
85
86
87
88



89
90
91
92
93
94
95
96
97
98
99






100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129















130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146



147





148
149
150
151
152

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171


172
173
174
175
176
177






178
179
180
181
182
183


184



185
186



187
188
189

190

















191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225






226
227
228
229
230
231
232
233
234
235
236
237



238
239
240
241


242
243
244
245

246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

266
267
268

269
270
271
272
273
274
275
276



+




-





-
-
-
-
-
+
+
+

+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
+
-
-
-
-
-
+
+
+
+
+
-
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
-
-
-
+
+
-
-
-
+
+

-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
-
+
+


-
+



















-
+


-
+
+
+
+




#!/usr/bin/env python

from __future__ import division
import ipdb
import os
import random
import sys
import time
random.seed(time.time())

import parse
import topsort
import yaml

def main():
    key = "A"
    bps = 60/60
    tempo = 1/bps
    max_duration = 1
import tree

random.seed(time.time())

def main():
    composition = yaml.load(open("score.yaml"))

    max_t = 0  # max time encountered so far. Used for movement timing
    progression = "chorus"

    for movement in progression.split():
        for section in ["intro", "core", "outro"]:
    composition = {
        "fm_test": {
            "intro": {
                "melody": {  # Instrument 'melody'
                    "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(note)s 2 6 5 1",
                    "octave": 8,
                    "duration": 10,
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["G/2 G/2 | G/4 G/4 A/4 A/4 | A/2 A/2 | G | G | A | A | A3 (w)"],
                        "w": ["E | E | F | F | G/2 G/2 | G3 (u)"],
                    },
                },
            },
        },
        "verse1": {
            "intro": {
                "melody": {  # Instrument 'melody'
                    "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(note)s 1",
                    "octave": 8,
                    "duration": 10,
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["G/2 G/2 | G/4 G/4 A/4 A/4 | A/2 A/2 | G | G | A | A | A3 (w)"],
                        "w": ["E | E | F | F | G/2 G/2 | G3 (u)"],
                    },
                },
            },
            "body": {
                "melody": {  # Instrument 'melody'
                    "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(note)s 1",
                    "octave": 8,
                    "duration": 10,
            if section in composition[movement].keys():
                try:
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["C | G/2 G/2 | G/2 G/2 | C | B, | F' | C | F | C | B | F | (w)"],
                        "w": ["E/4 A/4 D/4 G/4 | F/4 F/4 B2 | (u)"],
                    },
                },
            },
            "outro": {
                    render_order = topsort.topsort([[composition[movement][section][instrument]["sync"], instrument] if "sync" in composition[movement][section][instrument].keys() else [None, instrument] for instrument in composition[movement][section]])
                except topsort.CycleError as ex:
                    print "Your instruments are synced in a circle! This makes no sense!"
                    print movement, section
                    print ex
                    sys.exit(1)
                

                "melody": {  # Instrument 'melody'
                    "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(note)s 1",
                    "octave": 8,
                    "duration": 10,
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["C/4 C/4 C/4 C/4 | z2"],
                    },
                },
            },
        },
        "verse2": {
            "body": {
                "melody": {  # Instrument 'melody'
                    "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(note)s 1",
                    "octave": 8,
                    "duration": 30,
#    for comp_name in progression.split():
#        comp_start_time = max_t
#        for instr_name, instr in composition[comp_name].iteritems():
#            generated_score = generate_score(instr["score"], instr["grammars"])  # Fill in the scores by generating them based on the grammars
##            print generated_score
#            score = parse.parse(generated_score, default_octave=instr["octave"])  # Return Node/Chord objects
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["C | C | C | C | F/2 F/2 | F/2 F/2 | (u)", "D | D | G/2 A/2 | D | D | (u)"],
                    },
                },
                "harmony": {  # Instrument 'melody'
                    "score_line": "i3 %(time)f %(duration)f 4000 %(octave)d.%(note)s 2 3 5 3",
                    "octave": 8,
#
#            # Generate timestamps for the notes 
#            t = comp_start_time
#            for note in range(len(score)):
                    "duration": 30,
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["C | C | z | C | C | z/2 F/4 F/2 F/2 | F/2 F/2 | z (u)", "D | D | G/2 A/2 | D | D | z (u)"],
#                score[note].time = t
#                score[note].duration *= tempo
#                t += score[note].duration
                    },
                },
                "percussion": {  # Instrument 'melody'
                    "score_line": "i1 %(time)f %(duration)f 7000 %(octave)d.%(note)s %(octave)d.%(note)s 0 6",
                    "octave": 8,
                    "duration": 30,
##                print "time difference =", t-comp_start_time
##                print "instrument duration =",composition[comp_name][instr_name]["duration"]
#                if (t-comp_start_time) > float(composition[comp_name][instr_name]["duration"]):
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["C/4 C/4 C/4 C/4 | F/2 F/2 | F/2 F/2 | (u)", "D/4 D/4 G/4 A/4 | D | D | (v)"],
                        "v": ["C | D | E | F | E | D | C | (u)",],
                    },
                },
            },
            "outro": {
                "percussion": {  # Instrument 'melody'
                    "score_line": "i1 %(time)f %(duration)f 7000 %(octave)d.%(note)s %(octave)d.%(note)s 0 6",
                    "octave": 8,
                    "duration": 30,
##                    print "here"
#                    score = score[:note]
#                    break
#                max_t = t if t > max_t else max_t
#            composition[comp_name][instr_name]["score"] = score

                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["C/4 C/4 C/4 C/4"],
                    },
                },
            },
        },
        "sync_test": {
            "body": {
                "lead_instr": {  # Instrument 'melody'
                    "score_line": "i1 %(time)f %(duration)f 7000 %(octave)d.%(note)s %(octave)d.%(note)s 0 6",
                    "octave": 8,
                    "duration": 30,
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["D/4 D/4 D/4 D/4"],
                        "v": ["C/4 C/4 C/4 C/4"],
                    },
                },
                "follow_instr": {  # Instrument 'melody'
                    "score_line": "i2 %(time)f %(duration)f 7000 %(octave)d.%(note)s 1",
                    "sync": "lead_instr",
                    "octave": 8,
                    "duration": 30,
                    "grammars": {  # Notes for this instrument to use in this piece
                        "u": ["D/4 D/4 D/4 D/4"],
                        "v": ["C/4 C/4 C/4 C/4"],
                    },
                },
            },
        },
    }
    # Must be done after all note times keyed in, else you can't coordinate melodies with the rhythm chords
    print '''f1  0  512  10  1
            f2 0 8192 10 .24 .64 .88 .76 .06 .5 .34 .08
            f3 0 1025 10 1
    '''
    for comp_name in progression.split():
        print "; Movement:", comp_name
        for instr_name, instr in composition[comp_name].iteritems():
            composition[comp_name][instr_name]["score"] = transliterate_score(composition[comp_name][instr_name]["score"], key)
#            print "\nMovement %s instrument %s" % (comp_name, instr_name)
#            print composition[comp_name][instr_name]["score"] 
            final_score = generate_csound_score(composition[comp_name][instr_name]["score"], composition[comp_name][instr_name]["score_line"])
            for line in final_score:
                print line
            
    print '''f1 0 512 10 1
f2 0 8192 10 .24 .64 .88 .76 .06 .5 .34 .08
f3 0 1025 10 1
t 0 100
    '''

    section_start = 0
#    for section in ["verse1", "verse2"]:
    for section in ["sync_test"]:
        print "; Section " + section
        subsection_start = section_start
        section = composition[section]
        for subsection in ["intro", "body", "outro"]:
            try:
                print "; Subsection " + subsection
                subsection = section[subsection]

def make_scale(key):
    notes = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"]
    scale = [key]
                unordered_instrs = []
    pos = notes.index(key)
    progression = [2,2,1,2,2,2,1]
    for p in progression:
        pos = (pos + p) % 12
        scale.append(notes[pos])
                for instr in subsection:
                    subsection[instr]["name"] = instr
                    if not "sync" in subsection[instr].keys():
                        subsection[instr]["sync"] = None
                    unordered_instrs.append([subsection[instr]["sync"], instr])
    return scale
                ordered_instrs = topsort.topsort(unordered_instrs)
                ordered_instrs.remove(None)  # None used as a placeholder for sort order for instruments with no sync setting

                instrs = []
                syncs = {}
                for instr in ordered_instrs:
                    print ";Instrument " + instr
                    instr = subsection[instr]
                    max_time = instr["duration"]
                    instr_score, syncs = render_instr(instr, syncs, max_time)
                    instrs.append(instr_score)
                    for line in generate_csound_score(instr_score, instr["score_line"], subsection_start):
                        print line
                longest_score = max(instrs, key=lambda i: score_len(i))
                subsection_start += score_len(longest_score)
                section_start += score_len(longest_score)
            except KeyError:
                pass

def generate_score(score, grammars):
    while 1:

def render_instr(instr, syncs, max_time):
    for g in instr["grammars"]:
        for i in range(len(instr["grammars"][g])):
            instr["grammars"][g][i] = parse.parse(instr["grammars"][g][i])

        found_substitution = False
        for key,value in grammars.iteritems():
            if score.find(key) != -1:
                found_substitution = True
                while score.find(key) != -1:
                    score = score.replace(key, random.choice(grammars[key]), 1)
    score= []
    try:
        score, syncs = choose_phrase(instr, syncs, 0, max_time)

        while True:
            score_index_to_replace = None
#                    print scoe
                    if len(score.split()) > 2000:
            for item in range(len(score)):  # Optimize this by caching the index of the last node I replaced and startng there
                        for k in grammars.keys():
                            score = score.replace(k, "")
                        return score
                if isinstance(score[item], tree.Tree):
                    score_index_to_replace = item
        if found_substitution is False:
            break
    return score
            if score_index_to_replace is None:
                raise ValueError("No more nodes to fill in")

def transliterate_score(score, key):
            time_remaining = max_time - score_len(score)
    scale = make_scale(key)
    scale_conversion = {
        "I": 1,
        "II": 2,
        "III": 3,
        "IV": 4,
        "V": 5,
        "VI": 6,
        "VII": 7,
        "VIII": 8,
    }
    keyed_score = []
    for i in range(len(score)):
        if isinstance(score[i], parse.Note):
            score[i].value = scale[scale_conversion[score[i].value]-1]
        elif isinstance(score[i], parse.Chord):
            chord = []
            new_phrase, syncs = choose_phrase(instr, syncs, score_len(score), time_remaining)
            score = score[:node_index-1] + new_phrase + score[node_index+1:]

    except ValueError:
        return (score, syncs)


def choose_phrase(instr, syncs, current_time, time_remaining):
    '''Filters grammars for ones that match the sync option, and phrases that fit the time remaining in the score'''
    time_filtered_grammars = {}
    for grammar in instr["grammars"]:
        time_filtered_grammars[grammar] = get_phrases_that_fit(instr["grammars"][grammar], time_remaining)
    if len(time_filtered_grammars.keys()) == 0:
        raise ValueError("No available grammars that will fit in the score")

    grammar = None
#    if instr["name"] == "follow_instr":
#        ipdb.set_trace()
    if instr["sync"] is not None:
        guiding_instr = instr["sync"]
        sync_node = get_sync_node_at_time(syncs[guiding_instr], current_time)
        if sync_node in time_filtered_grammars.keys():
            grammar = sync_node
    if grammar is None:
        grammar = random.choice(time_filtered_grammars.keys())
    phrases = time_filtered_grammars[grammar]
    if instr["name"] not in syncs.keys():
        syncs[instr["name"]] = []
    syncs[instr["name"]].append({"node": grammar, "time": current_time})

    return random.choice(phrases), syncs


def get_phrases_that_fit(grammar, time_remaining):
    valid_phrases = []
            root_note_index = scale.index(key) + scale_conversion[score[i].value]
            chord.append(scale[root_note_index])
            chord.append(scale[(root_note_index+3) % 8])
            if score[i].chord_type == "m":  # Minor chords, flat the 5th
                chord.append(scale[(root_note_index+4) % 8])
            else:
    for phrase in grammar:
        if score_len(phrase) <= time_remaining:
            valid_phrases.append(phrase)
    return valid_phrases


def get_sync_node_at_time(syncs, t):
    for s in range(len(syncs)):
        if syncs[s]["time"] >= t:
            return syncs[s]["node"]


                chord.append(scale[(root_note_index+5) % 8])
            score[i].chord = chord
        elif isinstance(score[i], parse.Rest):
def score_len(score):
    total = 0
    for n in score:
        if not isinstance(n, tree.Tree):
            pass
    return score
            total += n.duration
    return total


def generate_csound_score(score, score_line):
def generate_csound_score(score, score_line, t):
    csound_note_values = {
        "C": "00",
        "C#": "01",
        "D": "02",
        "D#": "03",
        "E": "04",
        "F": "05",
        "F#": "06",
        "G": "07",
        "G#": "08",
        "A": "09",
        "A#": "10",
        "B": "11",
    }
    csound_score = []
    for token in score:
        if isinstance(token, parse.Chord):  # Chords
            for note in token.chord: 
                note = csound_note_values[note]
                csound_score.append(score_line % {"time": token.time, "octave": token.octave, "note": note, "duration": token.duration})
                csound_score.append(score_line % {"time": t, "octave": token.octave, "note": note, "duration": token.duration})
        elif isinstance(token, parse.Note):  # Individual notes
            note = csound_note_values[token.value]
            csound_score.append(score_line % {"time": token.time, "octave": token.octave, "note": note, "duration": token.duration})
            csound_score.append(score_line % {"time": t, "octave": token.octave, "note": note, "duration": token.duration})
        elif isinstance(token, tree.Tree):
            continue
        t += token.duration
    return csound_score


if __name__ == "__main__": main()