Skip to content

Commit affa155

Browse files
committed
more readme information, added some comments
1 parent 8ae391f commit affa155

File tree

2 files changed

+101
-29
lines changed

2 files changed

+101
-29
lines changed

README.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ but with syntax inspired by jazz/music theory.
2424

2525
# Features
2626

27-
Textbeat is a new project, but you can already do lots of cool things:
27+
Textbeat is still in development, but you can already do lots of cool things:
2828

2929
- Strumming
3030
- Arpeggiation
@@ -43,19 +43,51 @@ Textbeat is a new project, but you can already do lots of cool things:
4343

4444
# Setup
4545

46+
## Linux
47+
48+
```
49+
git clone https://github.com/flipcoder/textbeat
50+
cd textbeat
51+
sudo python setup.py install
52+
textbeat
53+
```
54+
55+
## Windows (Powershell)
56+
57+
```
58+
git clone https://github.com/flipcoder/textbeat
59+
cd textbeat
60+
pip install -r requirements.txt
61+
./txbt.cmd
62+
```
63+
64+
## Test it out!
65+
66+
Once you're in textbeat, try this:
67+
68+
```
69+
maj&
70+
```
71+
72+
If you don't hear 3 notes, you need to set up midi (this is the case with Linux).
73+
74+
## How to set up midi
75+
4676
You can use the shell with General Midi out-of-the-box on windows, which is great for learning,
4777
but sounds bad without a decent soundfont.
4878

49-
I'm currently working on headless VST rack generation.
50-
5179
If you want to use VST instruments, you'll need to route the MIDI out to something that hosts them, like a DAW.
80+
(I'm currently working on headless VST rack generation.)
5281

5382
For windows, you can use a virtual midi driver, such as [loopMIDI](http://www.tobias-erichsen.de/software/loopmidi.html) for usage with a VST host or DAW.
5483

55-
If you're on Linux, you can use soundfonts through qsynth or use a software instrument like helm or dexed. VSTs should work here as well.
84+
If you're on Linux, you can use soundfonts through qsynth or use a software instrument like helm or dexed. I recommend qsynth.
85+
86+
VSTs should work here as well but you need to pick a host.
5687

5788
If you feed the MIDI into a DAW you'll be able to record the output through the DAW itself.
58-
I'm currently looking into recording via a headless host.
89+
90+
I'm currently looking into export options and recording via a headless host.
5991

6092
# Tutorial
6193

textbeat/player.py

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -221,20 +221,23 @@ def pause(self):
221221
return False
222222
return True
223223

224-
def run(self):
225-
for ch in self.tracks:
226-
ch.refresh()
227-
228-
self.header = True
229-
embedded_file = False
230-
224+
def write_midi_tempo(self):
231225
# set initial midifile tempo
232226
if self.midifile:
233227
if not self.midifile.tracks:
234228
self.midifile.tracks.append(mido.MidiTrack())
235229
self.midifile.tracks[0].append(mido.MetaMessage(
236230
'set_tempo', tempo=mido.bpm2tempo(self.tempo)
237231
))
232+
233+
def run(self):
234+
for ch in self.tracks:
235+
ch.refresh()
236+
237+
self.header = True
238+
embedded_file = False
239+
240+
self.write_midi_tempo()
238241

239242
while not self.quitflag:
240243
self.follow()
@@ -1315,11 +1318,12 @@ def run(self):
13151318
# c = cell[1]
13161319
# shift = int(c) if c.isdigit() else 0
13171320
# p = base + (octave+shift) * 12
1318-
# INVERSION
13191321
ct = 0
1320-
if c2==';;':
1322+
# CELL COMMENTS
1323+
if c2==';;': # cell comment
13211324
cell = []
13221325
break
1326+
#INVERSIONS
13231327
elif c == '>' or c=='<':
13241328
sign = (1 if c=='>' else -1)
13251329
ct = count_seq(cell)
@@ -1333,6 +1337,7 @@ def run(self):
13331337
if ch.arp_enabled:
13341338
ch.arp_notes = ch.arp_notes[1:] + ch.arp_notes[:1]
13351339
cell = cell[ct:]
1340+
# OCTAVE SHIFTING
13361341
elif c == ',' or c=='\'':
13371342
cell = cell[1:]
13381343
sign = 1 if c=='\'' else -1
@@ -1356,7 +1361,8 @@ def run(self):
13561361
shift = 1
13571362
ch.octave = octave
13581363
# row_events += 1
1359-
elif clen>1 and c=='~': # pitch wheel
1364+
# PITCH WHEEL
1365+
elif clen>1 and c=='~':
13601366
cell = cell[1:]
13611367
# sn = 1.0
13621368
if cell[0]=='/' or cell[0]=='\\':
@@ -1379,16 +1385,20 @@ def run(self):
13791385
cell = cell[1:]
13801386
vel = min(127,int(ch.vel + 0.5*(127.0-ch.vel)))
13811387
ch.pitch(vel)
1382-
elif c == '~': # vibrato
1388+
# VIBRATO
1389+
elif c == '~':
13831390
ch.mod(127) # TODO: pitch osc in the future
13841391
cell = cell[1:]
1392+
# MOD WHEEL
13851393
# elif c == '`': # mod wheel -- moved to CC
13861394
# ch.mod(127)
13871395
# cell = cell[1:]
1396+
# MUTE SUSTAIN
13881397
elif cell.startswith('--'):
13891398
num, ct = count_seq('-')
13901399
sustain = ch.sustain = False
13911400
cell = cell[ct:]
1401+
# STOP/PANIC
13921402
elif cell.startswith('='):
13931403
num, ct = count_seq('=')
13941404
if num==2:
@@ -1398,13 +1408,16 @@ def run(self):
13981408
if num<=3:
13991409
sustain = ch.sustain = False
14001410
cell = cell[ct:]
1411+
# SUSTAIN HOLD
14011412
elif c2=='__':
14021413
sustain = ch.sustain = True
14031414
ch.arp_sustain = True
14041415
cell = cell[2:]
1405-
elif c2=='_-': # deprecated
1416+
# [DEPRECATED] STOP SUSTAIN
1417+
elif c2=='_-':
14061418
sustain = False
14071419
cell = cell[2:]
1420+
# SUSTAIN
14081421
elif c=='_':
14091422
ch.arp_sustain = True
14101423
sustain = True
@@ -1422,32 +1435,38 @@ def run(self):
14221435
# cell = cell[len(num):]
14231436
# vel = int((float(num) / float('9'*len(num)))*127)
14241437
# ch.cc(7,vel)
1425-
elif cell.startswith('^^'): # record sequence
1438+
# RECORD SEQ
1439+
elif cell.startswith('^^'):
14261440
cell = cell[2:]
14271441
r,ct = peel_uint(cell,0)
14281442
ch.record(r)
14291443
cell = cell[ct:]
1430-
elif cell.startswith('^'): # replay sequence
1444+
# REPLAY SEQ
1445+
elif cell.startswith('^'):
14311446
cell = cell[1:]
14321447
r,ct = peel_uint(cell,0)
14331448
if self.showtext:
14341449
showtext.append('play sequence: ' + num)
14351450
ch.replay(r)
14361451
cell = cell[ct:]
1437-
elif c2=='ch': # midi channel
1452+
# MIDI CHANNEl
1453+
elif c2=='ch':
14381454
num,ct = peel_uint(cell[1:])
14391455
cell = cell[1+ct:]
14401456
if self.showtext:
14411457
showtext.append('midi channel: ' + num)
14421458
ch.midi_channel(num)
1459+
# SCALE
14431460
elif c=='s':
14441461
# solo if used by itself (?)
14451462
# scale if given args
14461463
# ch.soloed = True
14471464
cell = cell[1:]
1465+
# MUTE
14481466
elif c=='m':
14491467
ch.enable(c=='m')
14501468
cell = cell[1:]
1469+
# MIDI CC
14511470
elif c=='c': # MIDI CC
14521471
# get number
14531472
cell = cell[1:]
@@ -1462,7 +1481,8 @@ def run(self):
14621481
cell = cell[ct:]
14631482
ccval = int(num)
14641483
ch.cc(cc,ccval)
1465-
elif c=='p': # program/patch change
1484+
# PROGRAM/PATCH CHANGE
1485+
elif c=='p':
14661486
# bank select as other args?
14671487
cell = cell[1:]
14681488
p,ct = peel_uint(cell)
@@ -1471,7 +1491,8 @@ def run(self):
14711491
cell = []
14721492
cell = cell[ct:]
14731493
ch.patch(p)
1474-
elif c2=='bs': # program/patch change
1494+
# BANK SELECT
1495+
elif c2=='bs':
14751496
cell = cell[2:]
14761497
num,ct = peel_uint(cell)
14771498
cell = cell[ct:]
@@ -1486,6 +1507,7 @@ def run(self):
14861507
b = num2 # second val -> lsb
14871508
b |= num << 8 # first value -> msb
14881509
ch.bank(b)
1510+
# NOTE LENGTH
14891511
elif c=='*':
14901512
dots = count_seq(cell)
14911513
if notes:
@@ -1507,6 +1529,7 @@ def run(self):
15071529
cell = cell[dots:]
15081530
if self.showtext:
15091531
showtext.append('duration(*)')
1532+
# PLACEHOLDER
15101533
elif c=='.':
15111534
dots = count_seq(cell)
15121535
if len(c)>1 and notes:
@@ -1527,6 +1550,7 @@ def run(self):
15271550
cell = cell[dots:]
15281551
if self.showtext:
15291552
showtext.append('shorten(.)')
1553+
# NOTE TIME SHIFT
15301554
elif c=='(' or c==')': # note shift (early/delay)
15311555
num = ''
15321556
cell = cell[1:]
@@ -1538,11 +1562,14 @@ def run(self):
15381562
error('delay >= 0')
15391563
cell = []
15401564
continue
1565+
# MARKER (ignore -- already parsed)
15411566
elif c=='|':
15421567
cell = []
1568+
# MARKER (ignore -- already parsed)
15431569
elif c==':':
15441570
cell = []
1545-
elif c2=='!!': # full accent
1571+
# FULL ACCENT
1572+
elif c2=='!!':
15461573
accent = '!!'
15471574
cell = cell[2:]
15481575
vel,ct = peel_uint_s(cell,127)
@@ -1556,7 +1583,8 @@ def run(self):
15561583
vel = 127
15571584
if self.showtext:
15581585
showtext.append('accent(!!)')
1559-
elif c=='!': # accent
1586+
# ACCENT
1587+
elif c=='!':
15601588
accent = '!'
15611589
cell = cell[1:]
15621590
# num,ct = peel_uint_s(cell)
@@ -1570,7 +1598,8 @@ def run(self):
15701598
vel = constrain(int(ch.vel + 0.5*(127.0-ch.vel)),127)
15711599
if self.showtext:
15721600
showtext.append('accent(!)')
1573-
elif c2=='??': # ghost
1601+
# GHOST NOTE
1602+
elif c2=='??':
15741603
accent = '??'
15751604
if ch.ghost_vel >= 0:
15761605
vel = ch.ghost_vel
@@ -1579,7 +1608,8 @@ def run(self):
15791608
cell = cell[2:]
15801609
if self.showtext:
15811610
showtext.append('soften(??)')
1582-
elif c=='?': # soft
1611+
# SOFT NOTE
1612+
elif c=='?':
15831613
accent = '?'
15841614
if ch.soft_vel >= 0:
15851615
vel = ch.soft_vel
@@ -1589,7 +1619,8 @@ def run(self):
15891619
if self.showtext:
15901620
showtext.append('soften(?)')
15911621
# elif cell.startswith('$$') or (c=='$' and lennotes==1):
1592-
elif c=='$': # strum/spread/tremolo
1622+
# STRUM/SPREAD/TREMOLO
1623+
elif c=='$':
15931624
sq = count_seq(cell)
15941625
cell = cell[sq:]
15951626
num,ct = peel_uint_s(cell,'0')
@@ -1605,6 +1636,7 @@ def run(self):
16051636
ch.soft_vel = vel
16061637
if self.showtext:
16071638
showtext.append('strum($)')
1639+
# ARPEGGIO
16081640
elif c=='&':
16091641
count = count_seq(cell)
16101642
arpcount,ct = peel_uint(cell[count:],0)
@@ -1632,7 +1664,8 @@ def run(self):
16321664
cell = cell[1+ct:]
16331665
if self.showtext:
16341666
showtext.append('arpeggio(&)')
1635-
elif c=='t': # tuplets
1667+
# TUPLETS
1668+
elif c=='t':
16361669
tuplets = True
16371670
tups = count_seq(cell,'t')
16381671
Tups = count_seq(cell[tups:],'T')
@@ -1661,10 +1694,12 @@ def run(self):
16611694
# if not notes:
16621695
# cell = []
16631696
# continue # ignore marker
1697+
# GLOABL VARS (ignore -- already parsed)
16641698
elif c=='%':
16651699
# ctrl line
16661700
cell = []
16671701
break
1702+
# MIDI CC
16681703
elif c2 in CC:
16691704
cell = cell[2:]
16701705
num,ct = peel_uint_s(cell)
@@ -1676,6 +1711,7 @@ def run(self):
16761711
cell = cell[1:]
16771712
num = 1.0
16781713
ch.cc(CC[c2],constrain(int(num*127.0),127))
1714+
# PERSISTENT VELOCITY
16791715
elif c in '0123456789':
16801716
# set persistent track velocity for accent level
16811717
num,ct = peel_uint(cell)
@@ -1691,6 +1727,7 @@ def run(self):
16911727
else:
16921728
ch.vel = num
16931729
vel = num
1730+
# MIDI CC
16941731
elif c in CC:
16951732
cell = cell[1:]
16961733
num,ct = peel_uint_s(cell)
@@ -1700,6 +1737,7 @@ def run(self):
17001737
else:
17011738
num = 1.0
17021739
ch.cc(CC[c],constrain(int(num*127.0),127))
1740+
# PARSE ERROR
17031741
else:
17041742
# if self.cmdmode in 'cl':
17051743
log(FG.BLUE + self.line)
@@ -1737,7 +1775,7 @@ def run(self):
17371775
strum -= strum
17381776
if strum > EPSILON:
17391777
ln = len(notes)
1740-
delta = (1.0/(ln*forr(duration,1.0))) #t between notes
1778+
delta = (1.0/(ln*forr(duration,1.0))) # t between notes
17411779

17421780
if self.showtext:
17431781
# log(FG.MAGENTA + ', '.join(map(lambda n: note_name(p+n), notes)))
@@ -1766,6 +1804,7 @@ def run(self):
17661804
# if not schedule or (i==0 and strum>=EPSILON and delay<EPSILON):
17671805
ch.note_on(p + n, vel, sustain)
17681806
else:
1807+
# schedule note to occur later
17691808
f = lambda _, p=p,n=n,s=sustain,v=vel: \
17701809
ch.note_on(p + n, v, s)
17711810
self.schedule.add(Event(delay,f,ch))
@@ -1776,6 +1815,7 @@ def run(self):
17761815

17771816
while True:
17781817
try:
1818+
# don't delay on ctrl lines or file header
17791819
if not ctrl and not self.header:
17801820
self.schedule.logic(60.0 / self.tempo / self.grid)
17811821
break

0 commit comments

Comments
 (0)