Skip to content

Commit fe152a0

Browse files
committed
aync await, callstack behavior change, pop by-label
1 parent 70ba89d commit fe152a0

File tree

5 files changed

+73
-34
lines changed

5 files changed

+73
-34
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,8 @@ Here are the marker/repeat commands:
527527
- :N| goes back to last marker N number of times
528528
- :name*N| goes back to last marker 'name' N number of times
529529
- || return/pop to last position after marker jump
530-
- ||| end the song here
530+
- ||| return/pop to last position after marker jump by-label
531+
- |||| end the song here
531532
```
532533

533534
## Command line parameters (use -):

test/markers.txbt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
; marker test
22
; should play 1 2 1 2 3 4 4 5 6 6 6 7 1' (1') 2' 2' 2'
3-
;:|
4-
;|:
5-
;:|:
6-
;|||
73
1
84
2
95
:|
@@ -15,12 +11,12 @@
1511
:|x:
1612
2'
1713
:x*2|
18-
|||
14+
||||
1915
|a:
2016
4
2117
||
2218
|next:
2319
6
2420
:2|
2521
7
26-
||
22+
|||

textbeat/__main__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from __future__ import absolute_import, unicode_literals, print_function, generators
5151
# try:
5252
from .defs import *
53+
import asyncio
5354
# except:
5455
# from .defs import *
5556
def main():
@@ -288,7 +289,7 @@ def main():
288289
# log(FG.BLUE + 'New? Type help and press enter to start the tutorial.')
289290
log('')
290291

291-
player.run()
292+
asyncio.run(player.run())
292293

293294
if player.midifile:
294295
player.midifile.save(midifn)

textbeat/player.py

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,36 @@
11
from .defs import *
22

3+
from collections import deque
4+
from prompt_toolkit import PromptSession
5+
6+
STACK_SIZE = 64
7+
38
class StackFrame(object):
49
"""
510
Stack frames are items on the Player's call stack.
611
Similar to function calls, the Player uses a stack to track markers/repeats.
712
"""
8-
def __init__(self, row, caller, count):
13+
def __init__(self, name, row, caller, count):
14+
self.name = name
915
self.row = row
1016
self.caller = caller
1117
self.count = count # repeat call counter
1218
self.markers = {} # marker name -> line
1319
self.returns = {} # repeat row -> number of rpts left
1420
# self.returns[row] = 0
21+
def __str__(self):
22+
return 'StackFrame' + str((self.name, self.row, self.caller, self.count, self.markers, self.returns))
1523

1624
class Player(object):
1725
"""
1826
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
2028
be rewritten eventually.
2129
"""
2230

2331
class Flag:
2432
"""
25-
Bitflags for Roman numeral notation, transposition, and looping
33+
Bitflag options for Roman numeral notation, transposition, and looping
2634
"""
2735
ROMAN = bit(0) # use roman numeral notation
2836
TRANSPOSE = bit(1) # allow transposition of note letters
@@ -68,10 +76,12 @@ def __init__(self):
6876
self.ring = False # ring: disables midi muting on program exit, letting notes ring out
6977
self.buf = []
7078
self.markers = {} # markers are for doing jumps and repeats
71-
f = StackFrame(-1,-1,0)
79+
f = StackFrame('',-1,-1,0)
7280
f.returns[''] = 0
7381
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)
7585
# self.separators = [] # separators are currently not supported
7686
self.track_history = ['.'] * NUM_TRACKS # keep track of track history for replaying with " symbol
7787
self.fn = None # filename
@@ -182,7 +192,7 @@ def refresh_devices(self):
182192
pass
183193
self.auto = 'auto' in self.devices
184194

185-
def set_host(self, plugins):
195+
def set_plugins(self, plugins):
186196
self.plugins = plugins
187197
self.refresh_devices()
188198

@@ -251,14 +261,16 @@ def write_midi_tempo(self):
251261
'set_tempo', tempo=mido.bpm2tempo(self.tempo)
252262
))
253263

254-
def run(self):
264+
async def run(self):
255265
for ch in self.tracks:
256266
ch.refresh()
257267

258268
self.header = True
259269
embedded_file = False
260270

261271
self.write_midi_tempo()
272+
273+
prompt_session = PromptSession(history=HISTORY, vi_mode=self.vimode)
262274

263275
while not self.quitflag:
264276
self.follow()
@@ -320,7 +332,7 @@ def run(self):
320332
# if bufline.endswith('.txbt'):
321333
# play file?
322334
# bufline = raw_input(cline)
323-
bufline = prompt(cline, history=HISTORY, vi_mode=self.vimode)
335+
bufline = await prompt_session.prompt_async(cline)
324336
bufline = list(filter(None, bufline.split(' ')))
325337
bufline = list(map(lambda b: b.replace(';',' '), bufline))
326338
# elif self.remote:
@@ -456,7 +468,7 @@ def run(self):
456468
elif var=='R':
457469
if not 'auto' in self.devices:
458470
self.devices = ['auto'] + self.devices
459-
self.set_host(val.split(','))
471+
self.set_plugins(val.split(','))
460472
elif var=='V': self.version = val
461473
elif var=='D':
462474
self.devices = val.split(',')
@@ -575,24 +587,38 @@ def run(self):
575587
continue
576588
# marker AND repeat, continue to repeat parser section
577589

578-
if self.line.startswith('|||'):
590+
if self.line.startswith('||||'):
579591
self.quitflag = True
580592
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)
582604
if len(self.callstack)>1:
583605
frame = self.callstack[-1]
584606
frame.count = max(0,frame.count-1)
585607
if frame.count:
586608
self.row = frame.row + 1
587609
continue
588610
else:
611+
# print('returning to caller', frame.caller)
589612
self.row = frame.caller + 1
590-
self.callstack = self.callstack[:-1]
613+
# self.callstack = self.callstack[:-1]
614+
self.callstack.pop()
591615
continue
592616
else:
593-
self.quitflag = True
617+
# allow bypassing '||' on empty stack
618+
# self.quitflag = True
619+
self.row += 1
594620
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:
596622
jumpline = self.line[1:self.line.index('|')]
597623
frame = self.callstack[-1]
598624
jumpline = jumpline.split('*') # *n = n repeats
@@ -622,28 +648,38 @@ def run(self):
622648
# if bmrow in frame.returns:
623649

624650
# return to marker (no pushing)
625-
# self.callstack.append(StackFrame(bmrow, self.row, count))
651+
# self.callstack.append(StackFrame(bm, bmrow, self.row, count))
626652
# self.markers[jumpline[0]] = bmrow
627653
# self.row = bmrow + 1
628654
# self.last_marker = bmrow
629655

656+
# print('bm', bm)
657+
# print('fm', frame.markers)
658+
# print('fm', frame.returns)
630659
if bmrow==self.last_marker or bm in frame.markers: # call w/o push?
660+
# # if bm in frame.markers:
631661
# ctx already passed bookmark, call w/o pushing (return to mark)
662+
# print(frame.returns, self.row)
632663
if self.row in frame.returns: # have we repeated yet?
664+
# 2nd repeat (count exists)
633665
rpt = frame.returns[self.row]
634666
if rpt>0:
635667
frame.returns[self.row] = rpt - 1
636668
self.row = bmrow + 1 # repeat
637669
else:
670+
# repeating done
671+
# frame.returns[self.row] = rpt - 1
638672
del frame.returns[self.row] # reset
639673
self.row += 1
640674
else:
641675
# 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
643678
self.row = bmrow + 1 # repeat
644679
else:
645680
# 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))
647683
self.markers[bm] = bmrow
648684
self.row = bmrow + 1
649685
self.last_marker = bmrow
@@ -656,7 +692,7 @@ def run(self):
656692
# else:
657693
# self.row += 1
658694
# else:
659-
# self.callstack.append(StackFrame(bmrow, self.row, count))
695+
# self.callstack.append(StackFrame(bm, bmrow, self.row, count))
660696
# self.markers[jumpline[0]] = bmrow
661697
# self.row = bmrow + 1
662698
# self.last_marker = bmrow
@@ -1855,7 +1891,7 @@ def run(self):
18551891
try:
18561892
# don't delay on ctrl lines or file header
18571893
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)
18591895
break
18601896
else:
18611897
break

textbeat/schedule.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from .defs import *
2+
import time
3+
import asyncio
24

35
class Event(object):
46
def __init__(self, t, func, ch):
@@ -16,6 +18,8 @@ def __init__(self, ctx):
1618
self.clock = 0.0
1719
self.last_clock = 0
1820
self.started = False
21+
# self.sleepfunc = time.sleep
22+
# self.sleep = asyncio.sleep
1923
# all note mute and play events should be marked skippable
2024
def pending(self):
2125
return len(self.events)
@@ -27,13 +31,14 @@ def clear(self):
2731
def clear_channel(self, ch):
2832
assert False
2933
self.events = [ev for ev in self.events if ev.ch!=ch]
30-
def logic(self, t):
34+
async def logic(self, t):
3135
processed = 0
3236
self.passed = 0
3337

38+
# tt = time.perf_counter()
3439
# if self.last_clock == 0:
35-
# self.last_clock = time.clock()
36-
# clock = time.clock()
40+
# self.last_clock = tt
41+
# clock = tt
3742
# self.dontsleep = (clock - self.last_clock)
3843
# self.last_clock = clock
3944

@@ -47,8 +52,8 @@ def logic(self, t):
4752
# self.passed = 0.0
4853
# log(self.clock)
4954

50-
# pending_events_count = sum(1 for e in self.events if e.t > 0.0 and e.t < 2.0)
51-
# print(pending_events_count)
55+
pending_events_count = sum(1 for e in self.events if e.t > 0.0 and e.t < 2.0)
56+
print(pending_events_count)
5257

5358
try:
5459
self.events = sorted(self.events, key=lambda e: e.t)
@@ -60,7 +65,7 @@ def logic(self, t):
6065
if ev.t >= 0.0:
6166
if self.ctx.cansleep and self.ctx.startrow == -1:
6267
self.ctx.t += self.ctx.speed * t * (ev.t-self.passed)
63-
time.sleep(max(0,self.ctx.speed * t * (ev.t-self.passed)))
68+
await asyncio.sleep(max(0,self.ctx.speed * t * (ev.t-self.passed)))
6469
ev.func(0)
6570
self.passed = ev.t # only inc if positive
6671
else:
@@ -72,7 +77,7 @@ def logic(self, t):
7277
if slp > 0.0:
7378
self.ctx.t += self.ctx.speed*slp
7479
if self.ctx.cansleep and self.ctx.startrow == -1:
75-
time.sleep(max(0,self.ctx.speed*slp))
80+
await asyncio.sleep(max(0,self.ctx.speed*slp))
7681
self.passed = 0.0
7782

7883
self.events = self.events[processed:]

0 commit comments

Comments
 (0)