Sampled Instrument Player with static and monolithic design. All instruments are built-in.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

385 lines
17 KiB

import cbox
from gui_tools import *
class StreamWindow(Gtk.VBox):
def __init__(self, instrument, iobj):
Gtk.Widget.__init__(self)
self.engine = iobj.engine
self.path = self.engine.path
panel = Gtk.VBox(spacing=5)
self.filebutton = Gtk.FileChooserButton("Streamed file")
self.filebutton.set_action(Gtk.FileChooserAction.OPEN)
self.filebutton.set_local_only(True)
self.filebutton.set_filename(self.engine.status().filename)
self.filebutton.add_filter(standard_filter(["*.wav", "*.WAV", "*.ogg", "*.OGG", "*.flac", "*.FLAC"], "All loadable audio files"))
self.filebutton.add_filter(standard_filter(["*.wav", "*.WAV"], "RIFF WAVE files"))
self.filebutton.add_filter(standard_filter(["*.ogg", "*.OGG"], "OGG container files"))
self.filebutton.add_filter(standard_filter(["*.flac", "*.FLAC"], "FLAC files"))
self.filebutton.add_filter(standard_filter(["*"], "All files"))
self.filebutton.connect('file-set', self.file_set)
hpanel = Gtk.HBox(spacing = 5)
hpanel.pack_start(Gtk.Label.new_with_mnemonic("_Play file:"), False, False, 5)
hpanel.pack_start(self.filebutton, True, True, 5)
panel.pack_start(hpanel, False, False, 5)
self.adjustment = Gtk.Adjustment()
self.adjustment_handler = self.adjustment.connect('value-changed', self.pos_slider_moved)
self.progress = standard_hslider(self.adjustment)
panel.pack_start(self.progress, False, False, 5)
self.play_button = Gtk.Button.new_with_mnemonic("_Play")
self.rewind_button = Gtk.Button.new_with_mnemonic("_Rewind")
self.stop_button = Gtk.Button.new_with_mnemonic("_Stop")
buttons = Gtk.HBox(spacing = 5)
buttons.add(self.play_button)
buttons.add(self.rewind_button)
buttons.add(self.stop_button)
panel.pack_start(buttons, False, False, 5)
self.add(panel)
self.play_button.connect('clicked', lambda x: self.engine.play())
self.rewind_button.connect('clicked', lambda x: self.engine.seek(0))
self.stop_button.connect('clicked', lambda x: self.engine.stop())
set_timer(self, 30, self.update)
def update(self):
attribs = cbox.GetThings("%s/status" % self.path, ['filename', 'pos', 'length', 'playing'], [])
self.progress.set_sensitive(attribs.length is not None)
if attribs.length is not None:
try:
self.adjustment.handler_block(self.adjustment_handler)
self.adjustment.set_properties(value = attribs.pos, lower = 0, upper = attribs.length)
#self.adjustment.set_all(attribs.pos, 0, attribs.length, 44100, 44100 * 10, 0)
finally:
self.adjustment.handler_unblock(self.adjustment_handler)
return True
def pos_slider_moved(self, adjustment):
self.engine.seek(adjustment.get_value())
def file_set(self, button):
self.engine.load(button.get_filename())
class WithPatchTable:
def __init__(self, attribs):
self.patches = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_INT)
self.patch_combos = []
self.table = Gtk.Table(2, 16)
self.table.set_col_spacings(5)
for i in range(16):
self.table.attach(bold_label("Channel %s" % (1 + i)), 0, 1, i, i + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
cb = standard_combo(self.patches, None)
cb.connect('changed', self.patch_combo_changed, i + 1)
self.table.attach(cb, 1, 2, i, i + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
self.patch_combos.append(cb)
self.update_model()
set_timer(self, 500, self.patch_combo_update)
def update_model(self):
self.patches.clear()
patches = self.engine.get_patches()
ch_patches = self.engine.status().patches
self.mapping = {}
for id in patches:
self.mapping[id] = len(self.mapping)
self.patches.append((self.fmt_patch_name(patches[id], id), id))
self.patch_combo_update()
def patch_combo_changed(self, combo, channel):
if combo.get_active() == -1:
return
self.engine.set_patch(channel, self.patches[combo.get_active()][1])
def patch_combo_update(self):
patch = self.engine.status().patches
for i in range(16):
cb = self.patch_combos[i]
old_patch_index = cb.get_active() if cb.get_active() >= 0 else -1
patch_id = patch[i + 1][0]
current_patch_index = self.mapping[patch_id] if (patch_id >= 0 and patch_id in self.mapping) else -1
if old_patch_index != current_patch_index:
cb.set_active(current_patch_index)
#self.status_label.set_markup(s)
return True
def fmt_patch_name(self, patch, id):
return "%s (%s)" % (patch, id)
class FluidsynthWindow(Gtk.VBox, WithPatchTable):
def __init__(self, instrument, iobj):
Gtk.VBox.__init__(self)
self.engine = iobj.engine
self.path = self.engine.path
print (iobj.path)
attribs = iobj.status()
panel = Gtk.VBox(spacing=5)
table = Gtk.Table(2, 1)
IntSliderRow("Polyphony", "polyphony", 2, 256).add_row(table, 0, cbox.VarPath(self.path), attribs)
WithPatchTable.__init__(self, attribs)
panel.pack_start(standard_vscroll_window(-1, 160, self.table), True, True, 5)
hpanel = Gtk.HBox(spacing = 5)
self.filebutton = Gtk.FileChooserButton("Soundfont")
self.filebutton.set_action(Gtk.FileChooserAction.OPEN)
self.filebutton.set_local_only(True)
self.filebutton.set_filename(cbox.GetThings("%s/status" % self.path, ['soundfont'], []).soundfont)
self.filebutton.add_filter(standard_filter(["*.sf2", "*.SF2"], "SF2 Soundfonts"))
self.filebutton.add_filter(standard_filter(["*"], "All files"))
hpanel.pack_start(Gtk.Label.new_with_mnemonic("_Load SF2:"), False, False, 5)
hpanel.pack_start(self.filebutton, True, True, 5)
unload = Gtk.Button.new_with_mnemonic("_Unload")
hpanel.pack_start(unload, False, False, 5)
unload.connect('clicked', self.unload)
panel.pack_start(hpanel, False, False, 5)
self.filebutton.connect('file-set', self.file_set)
self.add(panel)
def file_set(self, button):
self.engine.load_soundfont(button.get_filename())
self.update_model()
def unload(self, button):
self.filebutton.set_filename('')
class LoadProgramDialog(SelectObjectDialog):
title = "Load a sampler program"
def __init__(self, parent):
SelectObjectDialog.__init__(self, parent)
def update_model(self, model):
for s in cbox.Config.sections("spgm:"):
title = s["title"]
if s["sfz"] == None:
model.append((s.name[5:], "Program", s.name, title))
else:
model.append((s.name[5:], "SFZ", s.name, title))
class SamplerWindow(Gtk.VBox, WithPatchTable):
def __init__(self, instrument, iobj):
Gtk.VBox.__init__(self)
self.engine = iobj.engine
self.path = self.engine.path
iattribs = iobj.status()
attribs = self.engine.status()
panel = Gtk.VBox(spacing=5)
table = Gtk.Table(2, 2)
table.set_col_spacings(5)
IntSliderRow("Polyphony", "polyphony", 1, 128).add_row(table, 0, cbox.VarPath(self.path), attribs)
self.voices_widget = add_display_row(table, 1, "Voices in use", cbox.VarPath(self.path), attribs, "active_voices")
panel.pack_start(table, False, False, 5)
WithPatchTable.__init__(self, attribs)
panel.pack_start(standard_vscroll_window(-1, 160, self.table), True, True, 5)
self.add(panel)
hpanel = Gtk.HBox(spacing = 5)
hpanel.pack_start(Gtk.Label.new_with_mnemonic("Add from _SFZ:"), False, False, 5)
self.filebutton = Gtk.FileChooserButton("Soundfont")
self.filebutton.set_action(Gtk.FileChooserAction.OPEN)
self.filebutton.set_local_only(True)
#self.filebutton.set_filename(cbox.GetThings("%s/status" % self.path, ['soundfont'], []).soundfont)
self.filebutton.add_filter(standard_filter(["*.sfz", "*.SFZ"], "SFZ Programs"))
self.filebutton.add_filter(standard_filter(["*"], "All files"))
self.filebutton.connect('file-set', self.load_sfz)
hpanel.pack_start(self.filebutton, False, True, 5)
load_button = Gtk.Button.new_with_mnemonic("Add from _config")
load_button.connect('clicked', self.load_config)
hpanel.pack_start(load_button, False, True, 5)
panel.pack_start(hpanel, False, False, 5)
set_timer(self, 200, self.voices_update)
self.output_model = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING)
for i in range(iattribs.outputs):
self.output_model.append((i + 1, "Out %d" % (i + 1)))
self.polyphony_labels = {}
self.output_combos = {}
for i in range(16):
button = Gtk.Button("Dump SFZ")
button.connect("clicked", self.dump_sfz, i + 1)
self.table.attach(button, 2, 3, i, i + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
label = Gtk.Label("")
self.table.attach(label, 3, 4, i, i + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
self.polyphony_labels[i + 1] = label
combo = standard_combo(self.output_model, column = 1)
combo.connect('changed', self.output_combo_changed, i + 1)
self.table.attach(combo, 4, 5, i, i + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
self.output_combos[i + 1] = combo
self.output_combo_update()
def output_combo_update(self):
output = self.engine.status().output
for i in range(16):
cb = self.output_combos[i + 1]
old_channel_index = cb.get_active() if cb.get_active() >= 0 else -1
if old_channel_index != output[1 + i]:
cb.set_active(output[1 + i])
#self.status_label.set_markup(s)
return True
def output_combo_changed(self, combo, channel):
if combo.get_active() == -1:
return
self.engine.set_output(channel, combo.get_active())
def dump_sfz(self, w, channel):
attribs = cbox.GetThings("%s/status" % self.path, ['%patch', 'polyphony', 'active_voices'], [])
prog_no, patch_name = attribs.patch[channel]
pname, uuid, in_use_cnt = cbox.GetThings("%s/patches" % self.path, ['%patch'], []).patch[prog_no]
print ("UUID=%s" % uuid)
patch = cbox.Document.map_uuid(uuid)
groups = patch.get_groups()
for r in groups[0].get_children():
print ("<region> %s" % (r.as_string()))
for grp in patch.get_groups()[1:]:
print ("<group> %s" % (grp.as_string()))
for r in grp.get_children():
print ("<region> %s" % (r.as_string()))
def load_config(self, event):
d = LoadProgramDialog(self.get_toplevel())
response = d.run()
try:
if response == Gtk.ResponseType.OK:
scene = d.get_selected_object()
pgm_id = self.engine.get_unused_program()
self.engine.load_patch_from_cfg(pgm_id, scene[2], scene[2][5:])
self.update_model()
finally:
d.destroy()
def load_sfz(self, button):
pgm_id = self.engine.get_unused_program()
self.engine.load_patch_from_file(pgm_id, self.filebutton.get_filename(), self.filebutton.get_filename())
self.update_model()
def voices_update(self):
status = self.engine.status()
self.voices_widget.set_text("%s voices, %s waiting, %s pipes" % (status.active_voices, status.active_prevoices, status.active_pipes))
for i in range(16):
self.polyphony_labels[i + 1].set_text("%d voices, %d waiting" % (status.channel_voices[i + 1], status.channel_prevoices[i + 1]))
return True
def fmt_patch_name(self, patch, id):
return "%s (%s)" % (patch[0], id)
class TonewheelOrganWindow(Gtk.VBox):
combos = [
(1, 'Upper', 'upper_vibrato', [(0, 'Off'), (1, 'On')]),
(1, 'Lower', 'lower_vibrato', [(0, 'Off'), (1, 'On')]),
(1, 'Mode', 'vibrato_mode', [(0, '1'), (1, '2'), (2, '3')]),
(1, 'Chorus', 'vibrato_chorus', [(0, 'Off'), (1, 'On')]),
(2, 'Enable', 'percussion_enable', [(0, 'Off'), (1, 'On')]),
(2, 'Harmonic', 'percussion_3rd', [(0, '2nd'), (1, '3rd')]),
]
def __init__(self, instrument, iobj):
Gtk.VBox.__init__(self)
self.engine = iobj.engine
self.path = self.engine.path
panel = Gtk.VBox(spacing=10)
table = Gtk.Table(4, 10)
table.props.row_spacing = 10
table.set_col_spacings(5)
self.drawbars = {}
self.hboxes = {}
self.hboxes[1] = Gtk.HBox(spacing = 10)
self.hboxes[1].pack_start(Gtk.Label('Vibrato: '), False, False, 5)
self.hboxes[2] = Gtk.HBox(spacing = 10)
self.hboxes[2].pack_start(Gtk.Label('Percussion: '), False, False, 5)
self.combos = {}
for row, name, flag, options in TonewheelOrganWindow.combos:
label = Gtk.Label(name)
self.hboxes[row].pack_start(label, False, False, 5)
model = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING)
for oval, oname in options:
model.append((oval, oname))
combo = standard_combo(model, column = 1)
self.hboxes[row].pack_start(combo, False, False, 5)
combo.update_handler = combo.connect('changed', lambda w, setter: setter(w.get_model()[w.get_active()][0]), getattr(self.engine, 'set_' + flag))
self.combos[flag] = combo
panel.pack_start(self.hboxes[1], False, False, 5)
panel.pack_start(self.hboxes[2], False, False, 5)
table.attach(Gtk.Label("Upper"), 0, 1, 0, 1)
table.attach(Gtk.Label("Lower"), 0, 1, 1, 2)
for i in range(9):
slider = Gtk.VScale(adjustment = Gtk.Adjustment(0, 0, 8, 1, 1))
slider.props.digits = 0
table.attach(slider, i + 1, i + 2, 0, 1)
self.drawbars['u%d' % i] = slider.get_adjustment()
slider.get_adjustment().connect('value-changed', lambda adj, drawbar: self.engine.set_upper_drawbar(drawbar, int(adj.get_value())), i)
slider = Gtk.VScale(adjustment = Gtk.Adjustment(0, 0, 8, 1, 1))
slider.props.digits = 0
table.attach(slider, i + 1, i + 2, 1, 2)
self.drawbars['l%d' % i] = slider.get_adjustment()
slider.get_adjustment().connect('value-changed', lambda adj, drawbar: self.engine.set_lower_drawbar(drawbar, int(adj.get_value())), i)
panel.add(table)
self.add(panel)
self.refresh()
def refresh(self):
attribs = self.engine.status()
for i in range(9):
self.drawbars['u%d' % i].set_value(attribs.upper_drawbar[i])
self.drawbars['l%d' % i].set_value(attribs.lower_drawbar[i])
for row, name, flag, options in TonewheelOrganWindow.combos:
combo = self.combos[flag]
combo.handler_block(combo.update_handler)
combo.set_active(ls_index(combo.get_model(), getattr(attribs, flag), 0))
combo.handler_unblock(combo.update_handler)
class JackInputWindow(Gtk.VBox):
def __init__(self, instrument, iobj):
Gtk.VBox.__init__(self)
print (iobj.status())
self.engine = iobj.engine
self.path = self.engine.path
table = Gtk.Table(2, 2)
table.props.row_spacing = 10
no_inputs = cbox.JackIO.status().audio_inputs
model = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING)
model.append((0, "Unconnected"))
for i in range(no_inputs):
model.append((1 + i, "Input#%s" % (1 + i)))
self.combos = []
for i, name in ((0, "Left"), (1, "Right")):
table.attach(Gtk.Label(name), 0, 1, i, i + 1)
combo = standard_combo(model, column = 1)
table.attach(combo, 1, 2, i, i + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
combo.update_handler = combo.connect('changed', self.update_inputs)
self.combos.append(combo)
self.pack_start(table, False, False, 5)
self.refresh()
def update_inputs(self, w):
def to_base1(value):
return -1 if value <= 0 else value
left = to_base1(self.combos[0].get_active())
right = to_base1(self.combos[1].get_active())
self.engine.cmd("/inputs", None, left, right)
def refresh(self):
inputs = self.engine.status().inputs
self.combos[0].set_active(max(0, inputs[0]))
self.combos[1].set_active(max(0, inputs[1]))
instrument_window_map = {
'stream_player' : StreamWindow,
'fluidsynth' : FluidsynthWindow,
'sampler' : SamplerWindow,
'tonewheel_organ' : TonewheelOrganWindow
}
if int(cbox.Config.get("io", "use_usb") or "0") == 0 and cbox.JackIO.status().audio_inputs > 0:
instrument_window_map['jack_input'] = JackInputWindow