1
- # TODO: This player modulde includes parser and player prototype code
2
- # that may eventually be reorganized into separate modules
3
-
4
1
from .defs import *
5
2
6
3
class StackFrame (object ):
4
+ """
5
+ Stack frames are items on the Player's call stack.
6
+ Similar to function calls, the Player uses a stack to track markers/repeats.
7
+ """
7
8
def __init__ (self , row , caller , count ):
8
9
self .row = row
9
10
self .caller = caller
@@ -13,11 +14,21 @@ def __init__(self, row, caller, count):
13
14
# self.returns[row] = 0
14
15
15
16
class Player (object ):
17
+ """
18
+ The Player is what parses the txbt format and plays it.
19
+ This is the most "quick and dirty" part of textbeat, so it will probably
20
+ be rewritten eventually.
21
+ """
16
22
17
23
class Flag :
18
- ROMAN = bit (0 )
19
- TRANSPOSE = bit (1 )
20
- LOOP = bit (2 )
24
+ """
25
+ Bitflags for Roman numeral notation, transposition, and looping
26
+ """
27
+ ROMAN = bit (0 ) # use roman numeral notation
28
+ TRANSPOSE = bit (1 ) # allow transposition of note letters
29
+ LOOP = bit (2 ) # loop the textbeat file (good for jamtrack, metronome)
30
+
31
+ # string names for the bitflags
21
32
FLAGS = [
22
33
'roman' ,
23
34
'transpose' ,
@@ -30,66 +41,75 @@ class Flag:
30
41
# ])
31
42
32
43
def __init__ (self ):
33
- self .quitflag = False
34
- self .vimode = False
35
- self .bcproc = None
36
- self . log = False
44
+ self .quitflag = False # Set this bool to escape to stop the parser
45
+ self .vimode = False # use vi readline mode for prompt_toolkit
46
+ # self.log = False
47
+ # 'canfollow' means cursor location is printed for interop with vim and others
37
48
self .canfollow = False
49
+ self .last_follow = 0 # the last line 'followed' (printed)
50
+ # sleeping can be disabled for writing to midi files instead of playback
38
51
self .cansleep = True
39
- self .lint = False
40
- self .tracks_active = 1
52
+ self .lint = False # analyze file (not yet implemented)
53
+ self .tracks_active = 1 # the current number of known active tracks found by Player
41
54
self .showmidi = False
42
- self .scale = DIATONIC
43
- self .mode = 1
44
- self .transpose = 0
45
- self .octave = 0
46
- self .tempo = 90.0
55
+ self .scale = DIATONIC # default scale, see other options in theory.py
56
+ self .mode = 1 # musical mode number, 1=ionian
57
+ self .transpose = 0 # current transposion of entire song (in steps)
58
+ self .octave = 0 # relative octave transposition of entire song
59
+ self .tempo = 90.0 # default tempo when unspecified (may be different inside shell)
47
60
self .grid = 4.0 # Grid subdivisions of a beat (4 = sixteenth note)
61
+ # columns/shift is hardcoded column placement and widths for
62
+ # when working in text editors with column highlighting
48
63
self .columns = 0
49
64
self .column_shift = 0
50
- self .showtextstr = []
65
+ # self.showtextstr = []
51
66
self .showtext = False # nice output (-v), only shell and cmd modes by default
52
- self .sustain = False # start sustained
53
- self .ring = False # disables midi muting on program exit
67
+ self .sustain = False # start sustained?
68
+ self .ring = False # ring: disables midi muting on program exit, letting notes ring out
54
69
self .buf = []
55
- self .markers = {}
70
+ self .markers = {} # markers are for doing jumps and repeats
56
71
f = StackFrame (- 1 ,- 1 ,0 )
57
72
f .returns ['' ] = 0
58
- self .tutorial = None
59
- self .callstack = [f ]
60
- self .separators = []
61
- self .track_history = ['.' ] * NUM_TRACKS
62
- self .fn = None
63
- self .row = 0
73
+ self .tutorial = None # Tutorial object to run (should be run inside shell, see -T)
74
+ self .callstack = [f ] # callstack for moving around the file using markers/repeats
75
+ # self.separators = [] # separators are currently not supported
76
+ self .track_history = ['.' ] * NUM_TRACKS # keep track of track history for replaying with " symbol
77
+ self .fn = None # filename
78
+ self .row = 0 # parser location, row #
64
79
# self.rowno = []
65
- self .startrow = - 1
66
- self .stoprow = - 1
80
+ self .startrow = - 1 # last row processed, def -1
81
+ self .stoprow = - 1 # row to stop on, if playing a specific region (-1 is entire file)
67
82
self .cmdmode = 'n' # n normal c command s sequence
68
- self .schedule = Schedule (self )
69
- self .host = []
83
+ self .plugins = [] # (under dev) textbeat interop plugins
70
84
self .tracks = []
71
85
self .shell = False
72
- self .remote = False
86
+ # eventually, text editor interop may be done by controlling txbt through a socket
87
+ # instead of column following
88
+ # self.remote = None
73
89
self .interactive = False
74
90
self .gui = False
75
91
self .portname = ''
76
- self .speed = 1.0
77
- self .muted = False # mute all except for solo tracks
92
+ self .speed = 1.0 # speed multiplier (this one is per file)
93
+ self .muted = False # mute all except for " solo" tracks
78
94
self .midi = []
79
- self .instrument = None
80
- self .t = 0.0 # actual time
81
- self .last_follow = 0
82
- self .last_marker = - 1
83
- self .midifile = None
84
- self .flags = 0
95
+ # self.instrument = None
96
+ self .t = 0.0 # time since file processing started, in sec
97
+ self .last_follow = - 1 # last follow location (row #)
98
+ self .last_marker = - 1 # last marker location (row #)
99
+ self .midifile = None # midi file to write output to
100
+ self .flags = 0 # see FLAGS (bitflags)
85
101
self .version = '0'
86
- self .auto = False
102
+ self .auto = False # (under dev) automatically generate VST rack using a plugin
103
+ # embedded config files (key=filename), for things like synth/plugin customization
87
104
self .embedded_files = {}
88
105
self .vibrato_tracks = set () # tracks that currently have vibrato going
89
106
90
- # require enable at top of file
107
+ # other devices require enabling them at the top of the file
91
108
self .devices = ['midi' ]
92
109
110
+ # (under dev) schedule will eventually decouple the player and parser
111
+ self .schedule = Schedule (self )
112
+
93
113
def init (self ):
94
114
95
115
for i in range (len (sys .argv )):
@@ -156,14 +176,14 @@ def refresh_devices(self):
156
176
assert False
157
177
try :
158
178
# support_enable[dev](self.rack)
159
- SUPPORT_PLUGINS [dev ].enable (self .host )
179
+ SUPPORT_PLUGINS [dev ].enable (self .plugins )
160
180
except KeyError :
161
181
# no init needed, silent
162
182
pass
163
183
self .auto = 'auto' in self .devices
164
184
165
185
def set_host (self , plugins ):
166
- self .host = plugins
186
+ self .plugins = plugins
167
187
self .refresh_devices ()
168
188
169
189
# def remove_flags(self, f):
@@ -303,8 +323,8 @@ def run(self):
303
323
bufline = prompt (cline , history = HISTORY , vi_mode = self .vimode )
304
324
bufline = list (filter (None , bufline .split (' ' )))
305
325
bufline = list (map (lambda b : b .replace (';' ,' ' ), bufline ))
306
- elif self .remote :
307
- pass
326
+ # elif self.remote:
327
+ # pass # not yet implemented
308
328
else :
309
329
assert False
310
330
@@ -655,7 +675,8 @@ def run(self):
655
675
# separate into chunks based on column width
656
676
cells = [cells [i :i + self .columns ] for i in range (0 , len (cells ), self .columns )]
657
677
# log(cells)
658
- elif not self .separators :
678
+ else :
679
+ # elif not self.separators:
659
680
# AUTOGENERATE CELL self.separators
660
681
cells = fullline .split (' ' )
661
682
pos = 0
@@ -670,17 +691,19 @@ def run(self):
670
691
# if fullline.startswith(' '):
671
692
# cells = ['.'] + cells # dont filter first one
672
693
autoseparate = True
673
- else :
674
- # SPLIT BASED ON self.separators
675
- s = 0
676
- seplen = len (self .separators )
677
- # log(seplen)
678
- pos = 0
679
- for i in range (seplen ):
680
- cells .append (fullline [pos :self .separators [i ]].strip ())
681
- pos = self .separators [i ]
682
- lastcell = fullline [pos :].strip ()
683
- if lastcell : cells .append (lastcell )
694
+ # else:
695
+ # log('Track separators are no longer supported.')
696
+ # assert False
697
+ # # SPLIT BASED ON self.separators
698
+ # s = 0
699
+ # seplen = len(self.separators)
700
+ # # log(seplen)
701
+ # pos = 0
702
+ # for i in range(seplen):
703
+ # cells.append(fullline[pos:self.separators[i]].strip())
704
+ # pos = self.separators[i]
705
+ # lastcell = fullline[pos:].strip()
706
+ # if lastcell: cells.append(lastcell)
684
707
685
708
# make sure active tracks get empty cell
686
709
len_cells = len (cells )
0 commit comments