1
1
from .defs import *
2
2
3
+ from collections import deque
4
+ from prompt_toolkit import PromptSession
5
+
6
+ STACK_SIZE = 64
7
+
3
8
class StackFrame (object ):
4
9
"""
5
10
Stack frames are items on the Player's call stack.
6
11
Similar to function calls, the Player uses a stack to track markers/repeats.
7
12
"""
8
- def __init__ (self , row , caller , count ):
13
+ def __init__ (self , name , row , caller , count ):
14
+ self .name = name
9
15
self .row = row
10
16
self .caller = caller
11
17
self .count = count # repeat call counter
12
18
self .markers = {} # marker name -> line
13
19
self .returns = {} # repeat row -> number of rpts left
14
20
# self.returns[row] = 0
21
+ def __str__ (self ):
22
+ return 'StackFrame' + str ((self .name , self .row , self .caller , self .count , self .markers , self .returns ))
15
23
16
24
class Player (object ):
17
25
"""
18
26
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
27
+ Beware! This is the most "quick and dirty" part of textbeat, so it will probably
20
28
be rewritten eventually.
21
29
"""
22
30
23
31
class Flag :
24
32
"""
25
- Bitflags for Roman numeral notation, transposition, and looping
33
+ Bitflag options for Roman numeral notation, transposition, and looping
26
34
"""
27
35
ROMAN = bit (0 ) # use roman numeral notation
28
36
TRANSPOSE = bit (1 ) # allow transposition of note letters
@@ -68,10 +76,12 @@ def __init__(self):
68
76
self .ring = False # ring: disables midi muting on program exit, letting notes ring out
69
77
self .buf = []
70
78
self .markers = {} # markers are for doing jumps and repeats
71
- f = StackFrame (- 1 ,- 1 ,0 )
79
+ f = StackFrame ('' , - 1 ,- 1 ,0 )
72
80
f .returns ['' ] = 0
73
81
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
82
+ # self.callstack = [f] # callstack for moving around the file using markers/repeats
83
+ self .callstack = deque (maxlen = STACK_SIZE ) # callstack for moving around the file using markers/repeats
84
+ self .callstack .append (f )
75
85
# self.separators = [] # separators are currently not supported
76
86
self .track_history = ['.' ] * NUM_TRACKS # keep track of track history for replaying with " symbol
77
87
self .fn = None # filename
@@ -182,7 +192,7 @@ def refresh_devices(self):
182
192
pass
183
193
self .auto = 'auto' in self .devices
184
194
185
- def set_host (self , plugins ):
195
+ def set_plugins (self , plugins ):
186
196
self .plugins = plugins
187
197
self .refresh_devices ()
188
198
@@ -251,14 +261,16 @@ def write_midi_tempo(self):
251
261
'set_tempo' , tempo = mido .bpm2tempo (self .tempo )
252
262
))
253
263
254
- def run (self ):
264
+ async def run (self ):
255
265
for ch in self .tracks :
256
266
ch .refresh ()
257
267
258
268
self .header = True
259
269
embedded_file = False
260
270
261
271
self .write_midi_tempo ()
272
+
273
+ prompt_session = PromptSession (history = HISTORY , vi_mode = self .vimode )
262
274
263
275
while not self .quitflag :
264
276
self .follow ()
@@ -320,7 +332,7 @@ def run(self):
320
332
# if bufline.endswith('.txbt'):
321
333
# play file?
322
334
# bufline = raw_input(cline)
323
- bufline = prompt ( cline , history = HISTORY , vi_mode = self . vimode )
335
+ bufline = await prompt_session . prompt_async ( cline )
324
336
bufline = list (filter (None , bufline .split (' ' )))
325
337
bufline = list (map (lambda b : b .replace (';' ,' ' ), bufline ))
326
338
# elif self.remote:
@@ -456,7 +468,7 @@ def run(self):
456
468
elif var == 'R' :
457
469
if not 'auto' in self .devices :
458
470
self .devices = ['auto' ] + self .devices
459
- self .set_host (val .split (',' ))
471
+ self .set_plugins (val .split (',' ))
460
472
elif var == 'V' : self .version = val
461
473
elif var == 'D' :
462
474
self .devices = val .split (',' )
@@ -575,24 +587,38 @@ def run(self):
575
587
continue
576
588
# marker AND repeat, continue to repeat parser section
577
589
578
- if self .line .startswith ('|||' ):
590
+ if self .line .startswith ('|||| ' ):
579
591
self .quitflag = True
580
592
continue
581
- elif self .line .startswith ('||' ):
593
+ elif self .line .startswith ('|||' ):
594
+ p = None
595
+ while True :
596
+ assert self .callstack
597
+ p = self .callstack .pop ()
598
+ if p .name != '' :
599
+ break
600
+ self .row = p .caller + 1
601
+ continue
602
+ elif self .line .startswith ('||' ): # return/pop
603
+ # print(self.callstack)
582
604
if len (self .callstack )> 1 :
583
605
frame = self .callstack [- 1 ]
584
606
frame .count = max (0 ,frame .count - 1 )
585
607
if frame .count :
586
608
self .row = frame .row + 1
587
609
continue
588
610
else :
611
+ # print('returning to caller', frame.caller)
589
612
self .row = frame .caller + 1
590
- self .callstack = self .callstack [:- 1 ]
613
+ # self.callstack = self.callstack[:-1]
614
+ self .callstack .pop ()
591
615
continue
592
616
else :
593
- self .quitflag = True
617
+ # allow bypassing '||' on empty stack
618
+ # self.quitflag = True
619
+ self .row += 1
594
620
continue
595
- if self .line [0 ]== ':' and self .line [- 1 ] in '|:' and '|' in self .line :
621
+ elif self .line [0 ]== ':' and self .line [- 1 ] in '|:' and '|' in self .line :
596
622
jumpline = self .line [1 :self .line .index ('|' )]
597
623
frame = self .callstack [- 1 ]
598
624
jumpline = jumpline .split ('*' ) # *n = n repeats
@@ -622,28 +648,38 @@ def run(self):
622
648
# if bmrow in frame.returns:
623
649
624
650
# return to marker (no pushing)
625
- # self.callstack.append(StackFrame(bmrow, self.row, count))
651
+ # self.callstack.append(StackFrame(bm, bmrow, self.row, count))
626
652
# self.markers[jumpline[0]] = bmrow
627
653
# self.row = bmrow + 1
628
654
# self.last_marker = bmrow
629
655
656
+ # print('bm', bm)
657
+ # print('fm', frame.markers)
658
+ # print('fm', frame.returns)
630
659
if bmrow == self .last_marker or bm in frame .markers : # call w/o push?
660
+ # # if bm in frame.markers:
631
661
# ctx already passed bookmark, call w/o pushing (return to mark)
662
+ # print(frame.returns, self.row)
632
663
if self .row in frame .returns : # have we repeated yet?
664
+ # 2nd repeat (count exists)
633
665
rpt = frame .returns [self .row ]
634
666
if rpt > 0 :
635
667
frame .returns [self .row ] = rpt - 1
636
668
self .row = bmrow + 1 # repeat
637
669
else :
670
+ # repeating done
671
+ # frame.returns[self.row] = rpt - 1
638
672
del frame .returns [self .row ] # reset
639
673
self .row += 1
640
674
else :
641
675
# start return count
642
- frame .returns [self .row ] = count - 1
676
+ self .callstack .append (StackFrame (bm , bmrow , self .row , count ))
677
+ self .callstack [- 1 ].returns [self .row ] = count - 1
643
678
self .row = bmrow + 1 # repeat
644
679
else :
645
680
# mark not yet passed, do push/pop
646
- self .callstack .append (StackFrame (bmrow , self .row , count ))
681
+ # print(bm)
682
+ self .callstack .append (StackFrame (bm , bmrow , self .row , count ))
647
683
self .markers [bm ] = bmrow
648
684
self .row = bmrow + 1
649
685
self .last_marker = bmrow
@@ -656,7 +692,7 @@ def run(self):
656
692
# else:
657
693
# self.row += 1
658
694
# else:
659
- # self.callstack.append(StackFrame(bmrow, self.row, count))
695
+ # self.callstack.append(StackFrame(bm, bmrow, self.row, count))
660
696
# self.markers[jumpline[0]] = bmrow
661
697
# self.row = bmrow + 1
662
698
# self.last_marker = bmrow
@@ -1855,7 +1891,7 @@ def run(self):
1855
1891
try :
1856
1892
# don't delay on ctrl lines or file header
1857
1893
if not ctrl and not self .header :
1858
- self .schedule .logic (60.0 / self .tempo / self .grid )
1894
+ await self .schedule .logic (60.0 / self .tempo / self .grid )
1859
1895
break
1860
1896
else :
1861
1897
break
0 commit comments