spiffyscore

Check-in [8c559c112b]
Login
Overview
SHA1:8c559c112be0e827f1ebd1cffe517c6e8c1ddb12
Date: 2011-10-20 18:38:08
User: brian
Comment:Merged the refactor branch into the main develop branch
Timelines: family | ancestors | descendants | both | develop
Downloads: Tarball | ZIP archive
Other Links: files | file ages | folders | manifest
Tags And Properties
Context
2011-10-20
19:21
[601339a475] Fixed the issue with the score being cut short. Now the instruments are changing nodes without my say-so. Alos, they're not always printing complete nodes. (user: brian, tags: develop)
18:44
[ba64e400ba] Leaf: Yay, first alpha release. program supports syncing and basic section/subsection ordering. (user: brian, tags: trunk, v0.1)
18:38
[8c559c112b] Merged the refactor branch into the main develop branch (user: brian, tags: develop)
18:37
[4f5bc3936f] Closed-Leaf: Syncing now works (user: brian, tags: refactor)
2011-10-13
18:15
[fde4a012ea] Program now renders instruments according to sync order (user: brian, tags: develop)
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Modified cfg.py from [52bf709d42] to [6468589fde].

     1      1   #!/usr/bin/env python
     2      2   
     3      3   from __future__ import division
            4  +import ipdb
     4      5   import os
     5         -import pdb
     6      6   import random
     7      7   import sys
     8      8   import time
     9      9   
    10     10   import parse
    11     11   import topsort
    12     12   import yaml
................................................................................
    99     99                       "duration": 30,
   100    100                       "grammars": {  # Notes for this instrument to use in this piece
   101    101                           "u": ["C/4 C/4 C/4 C/4"],
   102    102                       },
   103    103                   },
   104    104               },
   105    105           },
          106  +        "sync_test": {
          107  +            "body": {
          108  +                "lead_instr": {  # Instrument 'melody'
          109  +                    "score_line": "i1 %(time)f %(duration)f 7000 %(octave)d.%(note)s %(octave)d.%(note)s 0 6",
          110  +                    "octave": 8,
          111  +                    "duration": 30,
          112  +                    "grammars": {  # Notes for this instrument to use in this piece
          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.%(note)s 1",
          119  +                    "sync": "lead_instr",
          120  +                    "octave": 8,
          121  +                    "duration": 30,
          122  +                    "grammars": {  # Notes for this instrument to use in this piece
          123  +                        "u": ["D/4 D/4 D/4 D/4"],
          124  +                        "v": ["C/4 C/4 C/4 C/4"],
          125  +                    },
          126  +                },
          127  +            },
          128  +        },
   106    129       }
   107    130       print '''f1 0 512 10 1
   108    131   f2 0 8192 10 .24 .64 .88 .76 .06 .5 .34 .08
   109    132   f3 0 1025 10 1
   110    133   t 0 100
   111    134       '''
   112    135   
   113    136       section_start = 0
   114    137   #    for section in ["verse1", "verse2"]:
   115         -    for section in ["fm_test"]:
          138  +    for section in ["sync_test"]:
   116    139           print "; Section " + section
   117    140           subsection_start = section_start
   118    141           section = composition[section]
   119    142           for subsection in ["intro", "body", "outro"]:
   120    143               try:
   121    144                   print "; Subsection " + subsection
   122    145                   subsection = section[subsection]
          146  +
   123    147                   unordered_instrs = []
   124    148                   for instr in subsection:
   125         -                    if not "sync" in subsection[instr]:
          149  +                    subsection[instr]["name"] = instr
          150  +                    if not "sync" in subsection[instr].keys():
   126    151                           subsection[instr]["sync"] = None
   127    152                       unordered_instrs.append([subsection[instr]["sync"], instr])
   128    153                   ordered_instrs = topsort.topsort(unordered_instrs)
   129         -                ordered_instrs.remove(None)
   130         -                pdb.set_trace()
          154  +                ordered_instrs.remove(None)  # None used as a placeholder for sort order for instruments with no sync setting
          155  +
   131    156                   instrs = []
          157  +                syncs = {}
   132    158                   for instr in ordered_instrs:
   133    159                       print ";Instrument " + instr
   134    160                       instr = subsection[instr]
   135         -                    sync = None
   136    161                       max_time = instr["duration"]
   137         -                    instr_score = render_instr(instr, sync, max_time)
          162  +                    instr_score, syncs = render_instr(instr, syncs, max_time)
   138    163                       instrs.append(instr_score)
   139    164                       for line in generate_csound_score(instr_score, instr["score_line"], subsection_start):
   140    165                           print line
   141    166                   longest_score = max(instrs, key=lambda i: score_len(i))
   142    167                   subsection_start += score_len(longest_score)
   143    168                   section_start += score_len(longest_score)
   144    169               except KeyError:
   145    170                   pass
   146         -        
          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  +
          182  +        while True:
          183  +            score_index_to_replace = None
          184  +            for item in range(len(score)):  # Optimize this by caching the index of the last node I replaced and startng there
          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), time_remaining)
          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 fit the time remaining in the score'''
          200  +    time_filtered_grammars = {}
          201  +    for grammar in instr["grammars"]:
          202  +        time_filtered_grammars[grammar] = get_phrases_that_fit(instr["grammars"][grammar], time_remaining)
          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
   147    222   
   148    223   
   149         -def render_instr(instr, sync, max_time):
   150         -    grammars = instr["grammars"]
   151         -    for g in instr["grammars"]:
   152         -        for i in range(len(grammars[g])):
   153         -            grammars[g][i] = parse.parse(grammars[g][i])
   154         -    init_node = random.choice(instr["grammars"].keys())
   155         -    init_score = random.choice(instr["grammars"][init_node])
   156         -    score = init_score
   157         -    while True:
   158         -        time_remaining = max_time - score_len(score)
   159         -        try:
   160         -            score = choose_node(score, grammars, time_remaining, sync)
   161         -        except ValueError:
   162         -            break
   163         -    return score
          224  +def get_phrases_that_fit(grammar, time_remaining):
          225  +    valid_phrases = []
          226  +    for phrase in grammar:
          227  +        if score_len(phrase) <= time_remaining:
          228  +            valid_phrases.append(phrase)
          229  +    return valid_phrases
   164    230   
   165    231   
   166         -def choose_node(score, grammars, time_remaining, sync):
   167         -    if time_remaining <= 0:
   168         -        raise ValueError("No time remaining in the score")
   169         -    node = None
   170         -    node_index = None
   171         -    for item in range(len(score)):
   172         -        if isinstance(score[item], tree.Tree):
   173         -            node = score[item].name
   174         -            node_index = item
   175         -    if node is None:
   176         -        raise ValueError("No more nodes to fill in")
   177         -    options = []
   178         -    for g in range(len(grammars[node])):
   179         -        if score_len(grammars[node][g]) <= time_remaining:
   180         -            options.append(grammars[node][g])
   181         -    if len(options) == 0:
   182         -        raise ValueError("No available grammars that will fit in the score")
   183         -    if sync:
   184         -        pass
   185         -    else:
   186         -        phrase = random.choice(options)
   187         -    score = score[:node_index-1] + phrase + score[node_index+1:]
   188         -    return score
          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"]
   189    236   
   190    237   
   191    238   def score_len(score):
   192    239       total = 0
   193    240       for n in score:
   194    241           if not isinstance(n, tree.Tree):
   195    242               total += n.duration