Index: cfg.orc ================================================================== --- cfg.orc +++ cfg.orc @@ -1,8 +1,45 @@ sr=44100 ksmps=20 nchnls=1 + +gifn ftgen 105, 0, 524288, -1, "samples/bass/1.05.wav", 0, 0, 1 ; Middle C +gifn ftgen 106, 0, 524288, -1, "samples/bass/1.06.wav", 0, 0, 1 ; Middle C +gifn ftgen 107, 0, 524288, -1, "samples/bass/1.07.wav", 0, 0, 1 ; Middle C +gifn ftgen 108, 0, 524288, -1, "samples/bass/1.08.wav", 0, 0, 1 ; Middle C +gifn ftgen 109, 0, 524288, -1, "samples/bass/1.09.wav", 0, 0, 1 ; Middle C +gifn ftgen 110, 0, 524288, -1, "samples/bass/1.10.wav", 0, 0, 1 ; Middle C +gifn ftgen 111, 0, 524288, -1, "samples/bass/1.11.wav", 0, 0, 1 ; Middle C +gifn ftgen 200, 0, 524288, -1, "samples/bass/2.01.wav", 0, 0, 1 ; Middle C +gifn ftgen 201, 0, 524288, -1, "samples/bass/2.02.wav", 0, 0, 1 ; Middle C +gifn ftgen 202, 0, 524288, -1, "samples/bass/2.03.wav", 0, 0, 1 ; Middle C +gifn ftgen 203, 0, 524288, -1, "samples/bass/2.04.wav", 0, 0, 1 ; Middle C +gifn ftgen 204, 0, 524288, -1, "samples/bass/2.05.wav", 0, 0, 1 ; Middle C +gifn ftgen 206, 0, 524288, -1, "samples/bass/2.06.wav", 0, 0, 1 ; Middle C +gifn ftgen 207, 0, 524288, -1, "samples/bass/2.07.wav", 0, 0, 1 ; Middle C +gifn ftgen 208, 0, 524288, -1, "samples/bass/2.08.wav", 0, 0, 1 ; Middle C +gifn ftgen 209, 0, 524288, -1, "samples/bass/2.09.wav", 0, 0, 1 ; Middle C +gifn ftgen 210, 0, 524288, -1, "samples/bass/2.10.wav", 0, 0, 1 ; Middle C +gifn ftgen 211, 0, 524288, -1, "samples/bass/2.11.wav", 0, 0, 1 ; Middle C +gifn ftgen 300, 0, 524288, -1, "samples/bass/3.00.wav", 0, 0, 1 ; Middle C +gifn ftgen 301, 0, 524288, -1, "samples/bass/3.01.wav", 0, 0, 1 ; Middle C +gifn ftgen 302, 0, 524288, -1, "samples/bass/3.02.wav", 0, 0, 1 ; Middle C +gifn ftgen 303, 0, 524288, -1, "samples/bass/3.03.wav", 0, 0, 1 ; Middle C +gifn ftgen 304, 0, 524288, -1, "samples/bass/3.04.wav", 0, 0, 1 ; Middle C +gifn ftgen 305, 0, 524288, -1, "samples/bass/3.05.wav", 0, 0, 1 ; Middle C +gifn ftgen 306, 0, 524288, -1, "samples/bass/3.06.wav", 0, 0, 1 ; Middle C +gifn ftgen 307, 0, 524288, -1, "samples/bass/3.07.wav", 0, 0, 1 ; Middle C +gifn ftgen 308, 0, 524288, -1, "samples/bass/3.08.wav", 0, 0, 1 ; Middle C +gifn ftgen 309, 0, 524288, -1, "samples/bass/3.09.wav", 0, 0, 1 ; Middle C +gifn ftgen 310, 0, 524288, -1, "samples/bass/3.10.wav", 0, 0, 1 ; Middle C +gifn ftgen 311, 0, 524288, -1, "samples/bass/3.11.wav", 0, 0, 1 ; Middle C + +instr 3 + inum = p5*100 + asig loscil 1, 1, inum, 1 + outs asig +endin instr 1 asound pluck p4, cpspch(p5), cpspch(p6), p7, p8 p9 p10 out asound endin @@ -11,13 +48,8 @@ kenv linen p4, .1, p3, .2; envelope asound oscili kenv, cpspch(p5), p6; oscillator out asound endin -instr 3 - asound foscili p4, cpspch(p5), 5, 2, 3, p9 - out asound -endin - instr 4 aFMinst foscili p4, cpspch(p5), p6, p7, p8, p9 endin DELETED cfg.py Index: cfg.py ================================================================== --- cfg.py +++ cfg.py @@ -1,285 +0,0 @@ -#!/usr/bin/env python - -from __future__ import division -import ipdb -import os -import random -import sys -import time - -import parse -import topsort -import yaml - -import tree - -random.seed(time.time()) - -def main(): - 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, - "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": { - "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, - "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, - "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)"], - }, - }, - "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, - "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, - "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)"], - "v": ["C/4 C/4 C/4 C/4 (u)"], - }, - }, - "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 (u)"], - "v": ["C/4 C/4 C/4 C/4 (v)"], - }, - }, - }, - }, - } - 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] - - unordered_instrs = [] - 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]) - 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 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]) - - score= [] - try: - score, syncs = choose_phrase(instr, syncs, 0, max_time, None) - - while True: - score_index_to_replace = None - for item in range(len(score)): # Optimize this by caching the index of the last node I replaced and startng there - if isinstance(score[item], tree.Tree): # Also, make this use the find_next_node() function (or whatever I called it) - score_index_to_replace = item - if score_index_to_replace is None: - raise ValueError("No more nodes to fill in") - - time_remaining = max_time - score_len(score) - new_phrase, syncs = choose_phrase(instr, syncs, score_len(score), time_remaining, score) - score = score[:score_index_to_replace] + new_phrase + score[score_index_to_replace+1:] - - except ValueError: - return (score, syncs) - - -def choose_phrase(instr, syncs, current_time, time_remaining, score): - '''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"]: - fitting_phrases = get_phrases_that_fit(instr["grammars"][grammar], time_remaining) - if len(fitting_phrases) > 0: - time_filtered_grammars[grammar] = fitting_phrases - if len(time_filtered_grammars.keys()) == 0: - raise ValueError("No available grammars that will fit in the score") - - grammar = None - 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 - else: - grammar = random.choice(time_filtered_grammars.keys()) - if score is None: - grammar = random.choice(time_filtered_grammars.keys()) - elif instr["sync"] is None: - grammar = get_next_node(score); - 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 = [] - 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"] - -def get_next_node(score): - for token in score: - if isinstance(token, tree.Tree): - return token.name - - -def score_len(score): - total = 0 - for n in score: - if not isinstance(n, tree.Tree): - total += n.duration - return total - - -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": 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": 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() ADDED samples/bass/1.05.wav Index: samples/bass/1.05.wav ================================================================== --- samples/bass/1.05.wav +++ samples/bass/1.05.wav cannot compute difference between binary files ADDED samples/bass/1.06.wav Index: samples/bass/1.06.wav ================================================================== --- samples/bass/1.06.wav +++ samples/bass/1.06.wav cannot compute difference between binary files ADDED samples/bass/1.07.wav Index: samples/bass/1.07.wav ================================================================== --- samples/bass/1.07.wav +++ samples/bass/1.07.wav cannot compute difference between binary files ADDED samples/bass/1.08.wav Index: samples/bass/1.08.wav ================================================================== --- samples/bass/1.08.wav +++ samples/bass/1.08.wav cannot compute difference between binary files ADDED samples/bass/1.09.wav Index: samples/bass/1.09.wav ================================================================== --- samples/bass/1.09.wav +++ samples/bass/1.09.wav cannot compute difference between binary files ADDED samples/bass/1.10.wav Index: samples/bass/1.10.wav ================================================================== --- samples/bass/1.10.wav +++ samples/bass/1.10.wav cannot compute difference between binary files ADDED samples/bass/1.11.wav Index: samples/bass/1.11.wav ================================================================== --- samples/bass/1.11.wav +++ samples/bass/1.11.wav cannot compute difference between binary files ADDED samples/bass/2.00.wav Index: samples/bass/2.00.wav ================================================================== --- samples/bass/2.00.wav +++ samples/bass/2.00.wav cannot compute difference between binary files ADDED samples/bass/2.01.wav Index: samples/bass/2.01.wav ================================================================== --- samples/bass/2.01.wav +++ samples/bass/2.01.wav cannot compute difference between binary files ADDED samples/bass/2.02.wav Index: samples/bass/2.02.wav ================================================================== --- samples/bass/2.02.wav +++ samples/bass/2.02.wav cannot compute difference between binary files ADDED samples/bass/2.03.wav Index: samples/bass/2.03.wav ================================================================== --- samples/bass/2.03.wav +++ samples/bass/2.03.wav cannot compute difference between binary files ADDED samples/bass/2.04.wav Index: samples/bass/2.04.wav ================================================================== --- samples/bass/2.04.wav +++ samples/bass/2.04.wav cannot compute difference between binary files ADDED samples/bass/2.05.wav Index: samples/bass/2.05.wav ================================================================== --- samples/bass/2.05.wav +++ samples/bass/2.05.wav cannot compute difference between binary files ADDED samples/bass/2.06.wav Index: samples/bass/2.06.wav ================================================================== --- samples/bass/2.06.wav +++ samples/bass/2.06.wav cannot compute difference between binary files ADDED samples/bass/2.07.wav Index: samples/bass/2.07.wav ================================================================== --- samples/bass/2.07.wav +++ samples/bass/2.07.wav cannot compute difference between binary files ADDED samples/bass/2.08.wav Index: samples/bass/2.08.wav ================================================================== --- samples/bass/2.08.wav +++ samples/bass/2.08.wav cannot compute difference between binary files ADDED samples/bass/2.09.wav Index: samples/bass/2.09.wav ================================================================== --- samples/bass/2.09.wav +++ samples/bass/2.09.wav cannot compute difference between binary files ADDED samples/bass/2.10.wav Index: samples/bass/2.10.wav ================================================================== --- samples/bass/2.10.wav +++ samples/bass/2.10.wav cannot compute difference between binary files ADDED samples/bass/2.11.wav Index: samples/bass/2.11.wav ================================================================== --- samples/bass/2.11.wav +++ samples/bass/2.11.wav cannot compute difference between binary files ADDED samples/bass/3.00.wav Index: samples/bass/3.00.wav ================================================================== --- samples/bass/3.00.wav +++ samples/bass/3.00.wav cannot compute difference between binary files ADDED samples/bass/3.01.wav Index: samples/bass/3.01.wav ================================================================== --- samples/bass/3.01.wav +++ samples/bass/3.01.wav cannot compute difference between binary files ADDED samples/bass/3.02.wav Index: samples/bass/3.02.wav ================================================================== --- samples/bass/3.02.wav +++ samples/bass/3.02.wav cannot compute difference between binary files ADDED samples/bass/3.03.wav Index: samples/bass/3.03.wav ================================================================== --- samples/bass/3.03.wav +++ samples/bass/3.03.wav cannot compute difference between binary files ADDED samples/bass/3.04.wav Index: samples/bass/3.04.wav ================================================================== --- samples/bass/3.04.wav +++ samples/bass/3.04.wav cannot compute difference between binary files ADDED samples/bass/3.05.wav Index: samples/bass/3.05.wav ================================================================== --- samples/bass/3.05.wav +++ samples/bass/3.05.wav cannot compute difference between binary files ADDED samples/bass/3.06.wav Index: samples/bass/3.06.wav ================================================================== --- samples/bass/3.06.wav +++ samples/bass/3.06.wav cannot compute difference between binary files ADDED samples/bass/3.07-old1.wav Index: samples/bass/3.07-old1.wav ================================================================== --- samples/bass/3.07-old1.wav +++ samples/bass/3.07-old1.wav cannot compute difference between binary files ADDED samples/bass/3.07.wav Index: samples/bass/3.07.wav ================================================================== --- samples/bass/3.07.wav +++ samples/bass/3.07.wav cannot compute difference between binary files ADDED samples/bass/3.08.wav Index: samples/bass/3.08.wav ================================================================== --- samples/bass/3.08.wav +++ samples/bass/3.08.wav cannot compute difference between binary files ADDED samples/bass/3.09.wav Index: samples/bass/3.09.wav ================================================================== --- samples/bass/3.09.wav +++ samples/bass/3.09.wav cannot compute difference between binary files ADDED samples/bass/3.10.wav Index: samples/bass/3.10.wav ================================================================== --- samples/bass/3.10.wav +++ samples/bass/3.10.wav cannot compute difference between binary files ADDED samples/bass/3.11.wav Index: samples/bass/3.11.wav ================================================================== --- samples/bass/3.11.wav +++ samples/bass/3.11.wav cannot compute difference between binary files ADDED samples/bass/Bass.arco.mf.sulE.C2B2.aiff Index: samples/bass/Bass.arco.mf.sulE.C2B2.aiff ================================================================== --- samples/bass/Bass.arco.mf.sulE.C2B2.aiff +++ samples/bass/Bass.arco.mf.sulE.C2B2.aiff cannot compute difference between binary files ADDED samples/bass/Bass.arco.mf.sulE.C3D3.aiff Index: samples/bass/Bass.arco.mf.sulE.C3D3.aiff ================================================================== --- samples/bass/Bass.arco.mf.sulE.C3D3.aiff +++ samples/bass/Bass.arco.mf.sulE.C3D3.aiff cannot compute difference between binary files ADDED samples/bass/Bass.arco.mf.sulG.C3B3.aiff Index: samples/bass/Bass.arco.mf.sulG.C3B3.aiff ================================================================== --- samples/bass/Bass.arco.mf.sulG.C3B3.aiff +++ samples/bass/Bass.arco.mf.sulG.C3B3.aiff cannot compute difference between binary files ADDED samples/bass/Bass.arco.mf.sulG.G2B2.aiff Index: samples/bass/Bass.arco.mf.sulG.G2B2.aiff ================================================================== --- samples/bass/Bass.arco.mf.sulG.G2B2.aiff +++ samples/bass/Bass.arco.mf.sulG.G2B2.aiff cannot compute difference between binary files ADDED samples/bass/test.orc Index: samples/bass/test.orc ================================================================== --- samples/bass/test.orc +++ samples/bass/test.orc @@ -0,0 +1,32 @@ +sr=44100 +ksmps=20 +nchnls=2 + +gifn ftgen 105, 0, 524288, -1, "8.05.wav", 0, 0, 1 ; Middle C +gifn ftgen 106, 0, 524288, -1, "8.06.wav", 0, 0, 1 ; Middle C +gifn ftgen 107, 0, 524288, -1, "8.07.wav", 0, 0, 1 ; Middle C +gifn ftgen 108, 0, 524288, -1, "8.08.wav", 0, 0, 1 ; Middle C +gifn ftgen 109, 0, 524288, -1, "8.09.wav", 0, 0, 1 ; Middle C +gifn ftgen 110, 0, 524288, -1, "8.10.wav", 0, 0, 1 ; Middle C +gifn ftgen 111, 0, 524288, -1, "8.18.wav", 0, 0, 1 ; Middle C + +instr 1 +; if p5=8.05 then +; asig diskin "8.05.wav" +; elseif p5=8.06 then +; asig diskin "8.06.wav" +; elseif p5=8.07 then +; asig diskin "8.07.wav" +; elseif p5=8.08 then +; asig diskin "8.08.wav" +; elseif p5=8.09 then +; asig diskin "8.09.wav" +; elseif p5=8.11 then +; asig diskin "8.10.wav" +; elseif p5=8.11 then +; asig diskin "8.18.wav" +; endif + inum = p5*100 + asig loscil 1, 1, inum, 1 + outs asig, asig +endin ADDED samples/bass/test.sco Index: samples/bass/test.sco ================================================================== --- samples/bass/test.sco +++ samples/bass/test.sco @@ -0,0 +1,7 @@ +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 + +i1 0.000000 0.50000 7000 1.05 +i1 0.500000 1.0000 7000 1.11 ADDED samples/bass/test.wav Index: samples/bass/test.wav ================================================================== --- samples/bass/test.wav +++ samples/bass/test.wav cannot compute difference between binary files ADDED spiffyscore.py Index: spiffyscore.py ================================================================== --- spiffyscore.py +++ spiffyscore.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python + +from __future__ import division +import ipdb +import os +import random +import sys +import time + +import parse +import topsort +import yaml + +import tree + +random.seed(time.time()) + +def main(): + 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, + "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": { + "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, + "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, + "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)"], + }, + }, + "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, + "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, + "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": ["A/4, B/4, C/4 D/4 (u)", "D/4' D/4' D/4' D/4' (v)"], + "v": ["C/4 C/4 C/4 C/4 (w)"], + "w": ["E/4 F/4 E/4 F/4 (u)"], + }, + }, + "follow_instr": { # Instrument 'melody' + "score_line": "i3 %(time)f %(duration)f 7000 %(octave)d.%(note)s", +# "sync": "lead_instr", + "octave": 2, + "duration": 30, + "grammars": { # Notes for this instrument to use in this piece + "u": ["E F G E (u)"], + "v": ["G A A A (e)"], + "e": ["B' A' G' A' (v)"], + }, + }, + }, + }, + } + 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] + + unordered_instrs = [] + 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]) + 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 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], default_octave=instr["octave"]) + + score= [] + try: + score, syncs = choose_phrase(instr, syncs, 0, max_time, None) + + while True: + score_index_to_replace = None + for item in range(len(score)): # Optimize this by caching the index of the last node I replaced and startng there + if isinstance(score[item], tree.Tree): # Also, make this use the find_next_node() function (or whatever I called it) + score_index_to_replace = item + if score_index_to_replace is None: + raise ValueError("No more nodes to fill in") + + time_remaining = max_time - score_len(score) + new_phrase, syncs = choose_phrase(instr, syncs, score_len(score), time_remaining, score) + score = score[:score_index_to_replace] + new_phrase + score[score_index_to_replace+1:] + + except ValueError: + return (score, syncs) + + +def choose_phrase(instr, syncs, current_time, time_remaining, score): + '''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"]: + fitting_phrases = get_phrases_that_fit(instr["grammars"][grammar], time_remaining) + if len(fitting_phrases) > 0: + time_filtered_grammars[grammar] = fitting_phrases + if len(time_filtered_grammars.keys()) == 0: + raise ValueError("No available grammars that will fit in the score") + + grammar = None + 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 + else: + grammar = random.choice(time_filtered_grammars.keys()) + if score is None: + grammar = random.choice(time_filtered_grammars.keys()) + elif instr["sync"] is None: + grammar = get_next_node(score); + 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 = [] + 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"] + +def get_next_node(score): + for token in score: + if isinstance(token, tree.Tree): + return token.name + + +def score_len(score): + total = 0 + for n in score: + if not isinstance(n, tree.Tree): + total += n.duration + return total + + +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": 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": 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() DELETED test.sco Index: test.sco ================================================================== --- test.sco +++ test.sco @@ -1,17 +0,0 @@ -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 fm_test -; Subsection intro -;Instrument melody -i2 0.000000 1.000000 7000 8.04 2 6 5 1 -i2 1.000000 1.000000 7000 8.04 2 6 5 1 -i2 2.000000 1.000000 7000 8.05 2 6 5 1 -i2 3.000000 1.000000 7000 8.05 2 6 5 1 -i2 4.000000 0.500000 7000 8.07 2 6 5 1 -i2 4.500000 0.500000 7000 8.07 2 6 5 1 -i2 5.000000 3.000000 7000 8.07 2 6 5 1 -; Subsection body -; Subsection outro DELETED todo.org Index: todo.org ================================================================== --- todo.org +++ todo.org @@ -1,37 +0,0 @@ -* Features [4/10] -- [X] Top-down composition -- [ ] Coordinate the melody and rhythm -- [X] Set maximum song length of movement per instrument -- [ ] Set minimum song length of movement per instrument -- [ ] Need to support all chord types -- [X] Doesn't handle rest notes -- [ ] Handle full ABC BNF (yeah, right, that's gonna happen...) -- [X] Set instrument octave in score file -- [ ] Output score to separate file -- [ ] Support parallel voices - ensure e.g. a certain chord progression is always - accompanied by a certain melody -- [ ] Syncopation -- [ ] Sustain option for notes (give score notation a sustain symbol, and use it to set how long after the notes's duration to keep holding. With sustain, the next note's start time isn't done by when the current note ends. - -* Bugs [5/6] -- [X] TLD resets clock for each movement -- [X] TLD doesn't accept an ordering for the movements -- [X] Doesn't handle minor chords -- [X] Calculated duration is absolute, not relative to BPM -- [X] Chords don't respect octaves -- [ ] Whole notes not held for proper duration - -* Structure [1/7] -- [ ] Chords should be composed of Notes, not ordinary arrays -- [ ] There should be a Musical_Phrase object that holds phrases (and sets of - phrases), and allows them to nest -- [ ] Does each instrument need a score parameter if the grammars are non-terminating? -- [ ] Duration should be in beats, not seconds. This ensures movements don't - end in the middle of a phrase. -- [ ] Generate score with proper generation tools, not this string-replace nonsense -- [X] Store csound score lines with instruments -- [ ] Upgrade from simple string substitution for the score to something - list-based (maybe). Idea is to keep musical phrases intact in the event of a - truncation of the score. Also, if done right, no need for retroactive length - parsing- a measure is has a specific length, which is known at - generation-time, thus solving the length limit problem.