Skip to content

Commit 9efac20

Browse files
committed
tutorial yaml and more plugin code migration and stubs
1 parent ce6f262 commit 9efac20

File tree

10 files changed

+476
-208
lines changed

10 files changed

+476
-208
lines changed

textbeat/def/style.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# playstyle variables like vibrato speed and depth

textbeat/player.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def __init__(self):
6666
self.stoprow = -1
6767
self.cmdmode = 'n' # n normal c command s sequence
6868
self.schedule = Schedule(self)
69-
self.rack = []
69+
self.host = []
7070
self.tracks = []
7171
self.shell = False
7272
self.remote = False
@@ -143,25 +143,26 @@ def init(self):
143143
def refresh_devices(self):
144144
# determine output device support and load external programs
145145
# try:
146-
from .support import supports, support_init
146+
from .support import supports, SUPPORT_PLUGINS
147147
# except:
148148
# import textbeat.support as support
149149
for dev in self.devices:
150150
if not supports(dev):
151151
if dev!='auto':
152152
out('Device not supported by system: ' + dev)
153153
else:
154-
out('Loading instrument presets requires Carla.')
154+
out('Loading instrument presets requires a compatible host module.')
155155
assert False
156156
try:
157-
support_init[dev](self.rack)
157+
# support_enable[dev](self.rack)
158+
SUPPORT_PLUGINS[dev].enable(self.host)
158159
except KeyError:
159160
# no init needed, silent
160161
pass
161162
self.auto = 'auto' in self.devices
162163

163-
def set_rack(self, plugins):
164-
self.rack = plugins
164+
def set_host(self, plugins):
165+
self.host = plugins
165166
self.refresh_devices()
166167

167168
# def remove_flags(self, f):
@@ -421,7 +422,7 @@ def run(self):
421422
elif var=='R':
422423
if not 'auto' in self.devices:
423424
self.devices = ['auto'] + self.devices
424-
self.set_rack(val.split(','))
425+
self.set_host(val.split(','))
425426
elif var=='V': self.version = val
426427
elif var=='D':
427428
self.devices = val.split(',')

textbeat/plugins/carla.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env python
2+
import textbeat.instrument as instrument
3+
from textbeat.instrument import Instrument
4+
from shutilwhich import which
5+
6+
ERROR = False
7+
if which('carla'):
8+
ERROR = True
9+
10+
class Carla(Instrument):
11+
NAME = 'carla'
12+
def __init__(self, args):
13+
Instrument.__init__(self, Carla.NAME)
14+
self.initialized = False
15+
self.enabled = False
16+
self.soundfonts = []
17+
self.proc = None
18+
self.args = args
19+
self.gen_inited = False
20+
def enabled(self):
21+
return self.initialized
22+
def enable(self, rack):
23+
if not self.proc:
24+
fn = self.args['SONGNAME']
25+
if not fn:
26+
fn = 'default'
27+
if rack:
28+
self.self.temp_proj = tempfile.mkstemp('.carxp',fn)
29+
os.close(self.temp_proj[0])
30+
self.temp_proj = self.temp_proj[1]
31+
os.unlink(self.temp_proj)
32+
base_proj = os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)),'presets','default.carxp'))
33+
shutil.copy2(base_proj, self.temp_proj)
34+
35+
# add instruments to temp proj file
36+
filebuf = ''
37+
with open(self.temp_proj,'r') as f:
38+
filebuf = f.read()
39+
instrumentxml = ''
40+
i = 0
41+
for instrument in rack:
42+
fnparts = instrument.split('.')
43+
name = fnparts[0]
44+
try:
45+
ext = fnparts[1].upper()
46+
except IndexError:
47+
ext = 'LV2'
48+
instrumentxml += '<!--'+name+'-->\n'+\
49+
'<Plugin><Info>\n'+\
50+
'<Type>'+ext+'</Type>\n'+\
51+
'<Name>'+name+'</Name>\n'+\
52+
'<URI>x</URI>'+\
53+
'</Info>\n'+\
54+
'<Data>\n'+\
55+
'<ControlChannel>N</ControlChannel>\n'+\
56+
'<Active>Yes</Active>\n'+\
57+
'<Options>'+hex(i)+'</Options>\n'+\
58+
'</Data>'+\
59+
'</Plugin>\n\n'
60+
i += 1
61+
filebuf = filebuf.replace('</EngineSettings>', '</EngineSettings>'+instrumentxml)
62+
with open(self.temp_proj,'w') as f:
63+
f.write(filebuf)
64+
65+
self.proj = self.temp_proj
66+
self.gen_inited = True
67+
else:
68+
self.proj = fn.split('.')[0]+'.carxp'
69+
if os.path.exists(proj):
70+
log(proj)
71+
self.proc = subprocess.Popen(['carla',proj], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # '--nogui',
72+
elif not rack:
73+
log('To load a Carla project headless, create a \'%s\' file.' % proj)
74+
75+
self.initialized = True
76+
def supported(self):
77+
return not ERROR
78+
def support(self):
79+
return ['auto','carla']
80+
def stop(self):
81+
if self.gen_inited and self.proj:
82+
try:
83+
os.remove(carla_proj[1])
84+
except OSError:
85+
pass
86+
except FileNotFoundError:
87+
pass
88+
89+
if self.self.temp_proj:
90+
os.unlink(self.temp_proj)
91+
if self.self.proc:
92+
self.proc.kill()
93+
94+
# instrument.export(FluidSynth)
95+
export = Carla
96+

textbeat/plugins/csound.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env python
2+
import textbeat.instrument as instrument
3+
from textbeat.instrument import Instrument
4+
from shutilwhich import which
5+
import subprocess
6+
7+
ERROR = False
8+
if not which('csound'):
9+
ERROR = True
10+
11+
class CSound(Instrument):
12+
NAME = 'csound'
13+
def __init__(self, args):
14+
Instrument.__init__(self, CSound.NAME)
15+
self.initialized = False
16+
self.proc = None
17+
self.csound = None
18+
def enable(self):
19+
if not initialized:
20+
self.proc = subprocess.Popen(['csound', '-odac', '--port='+str(CSOUND_PORT)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
21+
self.csound = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
22+
self.initialized = True
23+
def enabled(self):
24+
return self.initialized
25+
def supported(self):
26+
return not ERROR
27+
def support(self):
28+
return ['csound']
29+
def send(self, s):
30+
assert self.initialized
31+
return csound.sendto(s,('localhost',CSOUND_PORT))
32+
# def note_on(self, t, n, v):
33+
# self.fs.noteon(t, n, v)
34+
# def note_off(self, t, n, v):
35+
# self.fs.noteoff(t, v)
36+
# pass
37+
def stop(self):
38+
self.proc.kill()
39+
pass
40+
41+
export = CSound
42+

textbeat/plugins/espeak.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#!/usr/bin/env python
2+
from textbeat.defs import *
3+
import textbeat.instrument as instrument
4+
from textbeat.instrument import Instrument
5+
from shutilwhich import which
6+
import subprocess
7+
8+
ERROR = False
9+
if not which('espeak'):
10+
ERROR = True
11+
12+
# Currently not used, caches text to speech stuff in a way compatible with jack
13+
# current super slow, need to write stabilizer first
14+
class BackgroundProcess(object):
15+
def __init__(self, con):
16+
self.con = con
17+
self.words = {}
18+
self.processes = []
19+
def cache(self,word):
20+
try:
21+
tmp = self.words[word]
22+
except:
23+
tmp = tempfile.NamedTemporaryFile()
24+
p = subprocess.Popen(['espeak', '\"'+pipes.quote(word)+'\"','--stdout'], stdout=tmp)
25+
p.wait()
26+
self.words[tmp.name] = tmp
27+
return tmp
28+
def run(self):
29+
devnull = open(os.devnull, 'w')
30+
while True:
31+
msg = self.con.recv()
32+
# log(msg)
33+
if msg[0]==BGCMD.SAY:
34+
tmp = self.cache(msg[1])
35+
# super slow, better option needed
36+
self.processes.append(subprocess.Popen(['mpv','-ao','jack',tmp.name],stdout=devnull,stderr=devnull))
37+
elif msg[0]==BGCMD.CACHE:
38+
self.cache(msg[1])
39+
elif msg[0]==BGCMD.QUIT:
40+
break
41+
elif msg[0]==BGCMD.CLEAR:
42+
self.words.clear()
43+
else:
44+
log('BAD COMMAND: ' + msg[0])
45+
self.processses = list(filter(lambda p: p.poll()==None, self.processes))
46+
self.con.close()
47+
for tmp in self.words:
48+
tmp.close()
49+
for proc in self.processes:
50+
proc.wait()
51+
52+
def bgproc_run(con):
53+
self.proc = BackgroundProcess(con)
54+
self.proc.run()
55+
56+
class ESpeak(Instrument):
57+
NAME = 'espeak'
58+
def __init__(self, args):
59+
Instrument.__init__(self, ESpeak.NAME)
60+
self.initialized = False
61+
self.proc = None
62+
self.espeak = None
63+
def enable(self):
64+
if not initialized:
65+
self.pipe, child = Pipe()
66+
self.proc = Process(target=bgproc_run, args=(child,))
67+
self.proc.start()
68+
69+
self.initialized = True
70+
def enabled(self):
71+
return self.initialized
72+
def supported(self):
73+
return not ERROR
74+
def support(self):
75+
return ['espeak']
76+
# def note_on(self, t, n, v):
77+
# self.fs.noteon(t, n, v)
78+
# def note_off(self, t, n, v):
79+
# self.fs.noteoff(t, v)
80+
# pass
81+
def stop(self):
82+
if self.proc:
83+
self.pipe.send((BGCMD.QUIT,))
84+
self.proc.join()
85+
86+
# self.proc.kill()
87+
pass
88+
89+
export = ESpeak
90+

textbeat/plugins/fluidsynth.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python
2+
import textbeat.instrument as instrument
3+
from textbeat.instrument import Instrument
4+
from shutilwhich import which
5+
6+
ERROR = False
7+
if which('fluidsynth'):
8+
try:
9+
import fluidsynth # https://github.com/flipcoder/pyfluidsynth
10+
except:
11+
ERROR = True
12+
else:
13+
ERROR = True
14+
15+
class FluidSynth(Instrument):
16+
NAME = 'fluidsynth'
17+
def __init__(self, args):
18+
Instrument.__init__(self, FluidSynth.NAME)
19+
self.initialized = False
20+
self.enabled = False
21+
self.soundfonts = []
22+
def init(self):
23+
self.initialized = True
24+
def inited(self):
25+
return self.initialized
26+
def enabled(self):
27+
return self.enabled
28+
def enable(self):
29+
self.fs = fluidsynth.Synth()
30+
self.enabled = True
31+
def soundfont(self, fn, track, bank, preset):
32+
sfid = self.fs.sfload(fn)
33+
self.fs.program_select(track, sfid, bank, preset)
34+
return sfid
35+
def supported(self):
36+
return not ERROR
37+
def support(self):
38+
return ['fluidsynth','soundfonts']
39+
def note_on(self, t, n, v):
40+
self.fs.noteon(t, n, v)
41+
def note_off(self, t, n, v):
42+
self.fs.noteoff(t, v)
43+
pass
44+
def stop(self):
45+
pass
46+
47+
# instrument.export(FluidSynth)
48+
export = FluidSynth
49+

textbeat/plugins/sonicpi.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010

1111
class SonicPi(Instrument):
1212
NAME = 'sonicpi'
13-
def __init__(self):
13+
def __init__(self, args):
1414
Instrument.__init__(self, SonicPi.NAME)
1515
self.initalized = False
16-
def init(self):
16+
def enable(self):
1717
self.initalized = True
18-
def inited(self):
19-
return self.initalized
18+
def enabled(self):
19+
return self.initialized
2020
def supported(self):
2121
return not ERROR
2222
def support(self):

textbeat/plugins/supercollider.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414

1515
class SuperCollider(Instrument):
1616
NAME = 'supercollider'
17-
def __init__(self):
17+
def __init__(self, args):
1818
Instrument.__init__(self, SuperCollider.NAME)
1919
self.initalized = False
20-
def init(self):
20+
def enable(self):
2121
self.initalized = True
22-
def inited(self):
23-
return self.initalized
22+
def enabled(self):
23+
return self.enabled
2424
def supported(self):
2525
return not ERROR
2626
def support(self):

0 commit comments

Comments
 (0)