2
2
3
3
from collections import deque
4
4
from prompt_toolkit import PromptSession
5
+ from enum import Enum
5
6
6
7
STACK_SIZE = 64
7
8
9
+ class LoopResult (Enum ):
10
+ """When parsing a line, this enum types help us decide whether we should continue in the loop,
11
+ or break, or proceed further
12
+ This type is needed so we can choose how to deal interact with a loop from within functions
13
+ """
14
+ PROCEED = 0 # Continue parsing the current line
15
+ CONTINUE = 1 # Skips the current line
16
+ BREAK = 2 # Stop playback
17
+
8
18
class StackFrame (object ):
9
19
"""
10
20
Stack frames are items on the Player's call stack.
@@ -261,6 +271,7 @@ def write_midi_tempo(self):
261
271
'set_tempo' , tempo = mido .bpm2tempo (self .tempo )
262
272
))
263
273
274
+
264
275
async def run (self ):
265
276
for ch in self .tracks :
266
277
ch .refresh ()
@@ -277,76 +288,10 @@ async def run(self):
277
288
278
289
try :
279
290
self .line = '.'
280
- try :
281
- self .line = self .buf [self .row ]
282
- if self .row == self .startrow :
283
- self .startrow = - 1
284
- if self .stoprow != - 1 and self .row == self .stoprow :
285
- self .buf = []
286
- raise IndexError
287
- except IndexError :
288
- if self .has_flags (Player .Flag .LOOP ):
289
- self .row = 0
290
- continue
291
-
292
- self .row = len (self .buf )
293
- # done with file, finish playing some stuff
294
-
295
- arps_remaining = 0
296
- if self .interactive or self .cmdmode in ['c' ,'l' ]: # finish arps in shell mode
297
- for ch in self .tracks [:self .tracks_active ]:
298
- if ch .arp_enabled :
299
- if ch .arp_cycle_limit or not ch .arp_once :
300
- arps_remaining += 1
301
- self .line = '.'
302
- if not arps_remaining and not self .shell and self .cmdmode not in ['c' ,'l' ]:
303
- break
304
- self .line = '.'
305
-
306
- if not arps_remaining and not self .schedule .pending ():
307
- if self .interactive :
308
- for ch in self .tracks [:self .tracks_active ]:
309
- ch .release_all ()
310
-
311
- if self .shell :
312
- # self.shell PROMPT
313
- # log(orr(self.tracks[0].scale,self.scale).mode_name(orr(self.tracks[0].mode,self.mode)))
314
- # cur_oct = self.tracks[0].octave
315
- # cline = FG.GREEN + 'txbt> '+FG.BLUE+ '('+str(int(self.tempo))+'bpm x'+str(int(self.grid))+' '+\
316
- # note_name(self.tracks[0].transpose) + ' ' +\
317
- # orr(self.tracks[0].scale,self.scale).mode_name(orr(self.tracks[0].mode,self.mode,-1))+\
318
- # ')> '
319
- modename = orr (self .tracks [0 ].scale ,self .scale ).mode_name (orr (self .tracks [0 ].mode ,self .mode ,- 1 ))
320
-
321
- keynote = note_name (self .transpose + self .tracks [0 ].transpose )
322
- keynote = keynote if keynote != 'C' else ''
323
- parts = [
324
- str (int (self .tempo ))+ 'bpm' , # tempo
325
- 'x' + str (int (self .grid )), # subdiv
326
- keynote ,
327
- ('' if modename == 'ionian' else modename )
328
- ]
329
- cline = 'txbt> (' + \
330
- ' ' .join (filter (lambda x : x , parts ))+ \
331
- ')> '
332
- # if bufline.endswith('.txbt'):
333
- # play file?
334
- # bufline = raw_input(cline)
335
- bufline = await prompt_session .prompt_async (cline )
336
- bufline = list (filter (None , bufline .split (' ' )))
337
- bufline = list (map (lambda b : b .replace (';' ,' ' ), bufline ))
338
- # elif self.remote:
339
- # pass # not yet implemented
340
- else :
341
- assert False
342
-
343
- self .buf += bufline
344
-
345
- continue
346
-
347
- else :
348
- break
349
-
291
+ loop_result = await self .try_stop_on_index_error (prompt_session )
292
+ if loop_result == LoopResult .CONTINUE : continue
293
+ elif loop_result == LoopResult .BREAK : break
294
+
350
295
log (FG .MAGENTA + self .line )
351
296
352
297
# cells = line.split(' '*2)
@@ -1930,3 +1875,74 @@ async def run(self):
1930
1875
1931
1876
self .row += 1
1932
1877
1878
+ async def try_stop_on_index_error (self , prompt_session :PromptSession ) -> LoopResult :
1879
+ try :
1880
+ self .line = self .buf [self .row ]
1881
+ if self .row == self .startrow :
1882
+ self .startrow = - 1
1883
+ if self .stoprow != - 1 and self .row == self .stoprow :
1884
+ self .buf = []
1885
+ raise IndexError
1886
+ return LoopResult .PROCEED
1887
+ except IndexError :
1888
+ if self .has_flags (Player .Flag .LOOP ):
1889
+ self .row = 0
1890
+ return LoopResult .CONTINUE
1891
+
1892
+ self .row = len (self .buf )
1893
+ # done with file, finish playing some stuff
1894
+
1895
+ arps_remaining = 0
1896
+ if self .interactive or self .cmdmode in ['c' ,'l' ]: # finish arps in shell mode
1897
+ for ch in self .tracks [:self .tracks_active ]:
1898
+ if ch .arp_enabled :
1899
+ if ch .arp_cycle_limit or not ch .arp_once :
1900
+ arps_remaining += 1
1901
+ self .line = '.'
1902
+ if not arps_remaining and not self .shell and self .cmdmode not in ['c' ,'l' ]:
1903
+ return LoopResult .BREAK
1904
+ self .line = '.'
1905
+
1906
+ if not arps_remaining and not self .schedule .pending ():
1907
+ if self .interactive :
1908
+ for ch in self .tracks [:self .tracks_active ]:
1909
+ ch .release_all ()
1910
+
1911
+ if self .shell :
1912
+ # self.shell PROMPT
1913
+ # log(orr(self.tracks[0].scale,self.scale).mode_name(orr(self.tracks[0].mode,self.mode)))
1914
+ # cur_oct = self.tracks[0].octave
1915
+ # cline = FG.GREEN + 'txbt> '+FG.BLUE+ '('+str(int(self.tempo))+'bpm x'+str(int(self.grid))+' '+\
1916
+ # note_name(self.tracks[0].transpose) + ' ' +\
1917
+ # orr(self.tracks[0].scale,self.scale).mode_name(orr(self.tracks[0].mode,self.mode,-1))+\
1918
+ # ')> '
1919
+ modename = orr (self .tracks [0 ].scale ,self .scale ).mode_name (orr (self .tracks [0 ].mode ,self .mode ,- 1 ))
1920
+
1921
+ keynote = note_name (self .transpose + self .tracks [0 ].transpose )
1922
+ keynote = keynote if keynote != 'C' else ''
1923
+ parts = [
1924
+ str (int (self .tempo ))+ 'bpm' , # tempo
1925
+ 'x' + str (int (self .grid )), # subdiv
1926
+ keynote ,
1927
+ ('' if modename == 'ionian' else modename )
1928
+ ]
1929
+ cline = 'txbt> (' + \
1930
+ ' ' .join (filter (lambda x : x , parts ))+ \
1931
+ ')> '
1932
+ # if bufline.endswith('.txbt'):
1933
+ # play file?
1934
+ # bufline = raw_input(cline)
1935
+ bufline = await prompt_session .prompt_async (cline )
1936
+ bufline = list (filter (None , bufline .split (' ' )))
1937
+ bufline = list (map (lambda b : b .replace (';' ,' ' ), bufline ))
1938
+ # elif self.remote:
1939
+ # pass # not yet implemented
1940
+ else :
1941
+ assert False
1942
+
1943
+ self .buf += bufline
1944
+
1945
+ return LoopResult .CONTINUE
1946
+
1947
+ else :
1948
+ return LoopResult .BREAK
0 commit comments