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.

346 lines
14 KiB

import cbox
import math
import re
from gi.repository import GObject, Gdk, Gtk, GLib as glib
# Compatibility stuff
Gtk.TreeView.insert_column_with_attributes = lambda self, col, title, renderer, xarg = None, **kwargs: self.insert_column(Gtk.TreeViewColumn(title = title, cell_renderer = renderer, **kwargs), col)
def callback(cmd, cb, args):
print ("%s %s %s" % (cmd, cb, args))
def bold_label(text, halign = 1):
l = Gtk.Label()
l.set_markup("<b>%s</b>" % text)
l.set_alignment(halign, 0.5)
return l
def left_label(text):
l = Gtk.Label(text)
l.set_alignment(0, 0.5)
return l
def standard_hslider(adj):
sc = Gtk.HScale(adjustment = adj)
sc.set_size_request(160, -1)
sc.set_value_pos(Gtk.PositionType.RIGHT)
return sc
notenames = {
'c' : 0, 'c#' : 1, 'db' : 1,
'd' : 2, 'd#' : 3, 'eb' : 3,
'e' : 4, 'e#' : 5, 'fb' : 5,
'f' : 5, 'f#' : 6, 'gb' : 6,
'g' : 7, 'g#' : 8, 'ab' : 8,
'a' : 9, 'a#' : 10, 'bb' : 10,
'b' : 11, 'b#' : 0, 'cb' : 11,
}
def sfznote2value(note):
if re.match("[0-9]+$", note):
return int(rdata['key'])
else:
g = re.match("([cdefgab](#|b)?)(-?[0-9])", note)
if g is None:
raise ValueError(note)
return 12 + int(g.group(3)) * 12 + notenames[g.group(1).lower()]
class LogMapper:
def __init__(self, min, max, format = "%f"):
self.min = min
self.max = max
self.format_string = format
def map(self, value):
return float(self.min) * ((float(self.max) / self.min) ** (value / 100.0))
def unmap(self, value):
return math.log(value / float(self.min)) * 100 / math.log(float(self.max) / self.min)
def format_value(self, value):
return self.format_string % self.map(value)
freq_format = "%0.1f"
ms_format = "%0.1f ms"
lfo_freq_mapper = LogMapper(0.01, 20, "%0.2f")
env_mapper = LogMapper(0.002, 20, "%f")
filter_freq_mapper = LogMapper(20, 20000, "%0.1f Hz")
def standard_mapped_hslider(adj, mapper):
sc = Gtk.HScale(adjustment = adj)
sc.set_size_request(160, -1)
sc.set_value_pos(Gtk.PositionType.RIGHT)
sc.connect('format-value', lambda scale, value, mapper: mapper.format_value(value), mapper)
return sc
def standard_align(w, xo, yo, xs, ys):
a = Gtk.Alignment(xalign = xo, yalign = yo, xscale = xs, yscale = ys)
a.add(w)
return a
def standard_vscroll_window(width, height, content = None):
scroller = Gtk.ScrolledWindow()
scroller.set_size_request(width, height)
scroller.set_shadow_type(Gtk.ShadowType.NONE);
scroller.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
if content is not None:
scroller.add_with_viewport(content)
return scroller
def standard_combo(model, active_item = None, column = 0, active_item_lookup = None, lookup_column = None, width = None):
cb = Gtk.ComboBox(model = model)
if active_item_lookup is not None:
if lookup_column is None:
lookup_column = column
active_item = ls_index(model, active_item_lookup, lookup_column)
if active_item is not None:
cb.set_active(active_item)
cell = Gtk.CellRendererText()
if width is not None:
cb.set_size_request(width, -1)
cb.pack_start(cell, True)
cb.add_attribute(cell, 'text', column)
return cb
def ls_index(list_store, value, column):
for i in range(len(list_store)):
if list_store[i][column] == value:
return i
return None
def standard_filter(patterns, name):
f = Gtk.FileFilter()
for p in patterns:
f.add_pattern(p)
f.set_name(name)
return f
def checkbox_changed_bool(checkbox, vpath):
vpath.set(1 if checkbox.get_active() else 0)
def adjustment_changed_int(adjustment, vpath):
vpath.set(int(adjustment.get_value()))
def adjustment_changed_float(adjustment, vpath):
vpath.set(float(adjustment.get_value()))
def adjustment_changed_float_mapped(adjustment, vpath, mapper):
vpath.set(mapper.map(adjustment.get_value()))
def combo_value_changed(combo, vpath, value_offset = 0):
if combo.get_active() != -1:
vpath.set(value_offset + combo.get_active())
def combo_value_changed_use_column(combo, vpath, column):
if combo.get_active() != -1:
vpath.set(combo.get_model()[combo.get_active()][column])
def tree_toggle_changed_bool(renderer, tree_path, model, opath, column):
model[int(tree_path)][column] = not model[int(tree_path)][column]
cbox.do_cmd(model.make_row_item(opath, tree_path), None, [1 if model[int(tree_path)][column] else 0])
def tree_combo_changed(renderer, tree_path, new_value, model, opath, column):
new_value = renderer.get_property('model')[new_value][0]
model[int(tree_path)][column] = new_value
cbox.do_cmd(model.make_row_item(opath, tree_path), None, [new_value])
def standard_toggle_renderer(list_model, path, column):
toggle = Gtk.CellRendererToggle()
toggle.connect('toggled', tree_toggle_changed_bool, list_model, path, column)
return toggle
def standard_combo_renderer(list_model, model, path, column):
combo = Gtk.CellRendererCombo()
combo.set_property('model', model)
combo.set_property('editable', True)
combo.set_property('has-entry', False)
combo.set_property('text-column', 1)
combo.set_property('mode', Gtk.CellRendererMode.EDITABLE)
combo.connect('changed', tree_combo_changed, list_model, path, column)
return combo
def add_display_row(t, row, label, path, values, item):
t.attach(bold_label(label), 0, 1, row, row+1, Gtk.AttachOptions.SHRINK | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
w = left_label(getattr(values, item))
t.attach(w, 1, 2, row, row+1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
return w
def set_timer(widget, time, func, *args):
refresh_id = glib.timeout_add(time, func, *args)
widget.connect('destroy', lambda obj, id: glib.source_remove(id), refresh_id)
def create_menu(title, items):
menuitem = Gtk.MenuItem.new_with_mnemonic(title)
if items is not None:
menu = Gtk.Menu()
menuitem.set_submenu(menu)
for label, meth in items:
mit = Gtk.MenuItem.new_with_mnemonic(label)
if meth is None:
mit.set_sensitive(False)
else:
mit.connect('activate', meth)
menu.append(mit)
return menuitem
def note_to_name(note):
if note < 0:
return "N/A"
n = note % 12
return ("C C#D D#E F F#G G#A A#B "[n * 2 : n * 2 + 2]) + str((note // 12) - 2)
#################################################################################################################################
class TableRowWidget:
def __init__(self, label, name, **kwargs):
self.label = label
self.name = name
self.kwargs = kwargs
def get_with_default(self, name, def_value):
return self.kwargs[name] if name in self.kwargs else def_value
def create_label(self):
return bold_label(self.label)
def has_attr(self, values):
if type(values) is dict:
return self.name in values
return hasattr(values, self.name)
def get_attr(self, values):
if type(values) is dict:
return values[self.name]
return getattr(values, self.name)
def get_value(self, values, vpath):
if len(vpath.args) == 1:
return self.get_attr(values)[vpath.args[0]]
return self.get_attr(values)
def add_row(self, table, row, vpath, values):
table.attach(self.create_label(), 0, 1, row, row + 1, Gtk.AttachOptions.SHRINK | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
widget, refresher = self.create_widget(vpath)
table.attach(widget, 1, 2, row, row + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
if values is not None:
refresher(values)
return refresher
def update_sensitive(self, widget, values):
sensitive = (values is not None) and (self.get_with_default('setter', 0) is not None) and self.has_attr(values)
widget.set_sensitive(sensitive)
return sensitive
class SliderRow(TableRowWidget):
def __init__(self, label, name, minv, maxv, **kwargs):
TableRowWidget.__init__(self, label, name, **kwargs)
self.minv = minv
self.maxv = maxv
def create_widget(self, vpath):
setter = self.get_with_default('setter', adjustment_changed_float)
step = self.kwargs['step'] if 'step' in self.kwargs else 1
adj = Gtk.Adjustment(self.minv, self.minv, self.maxv, step, 6, 0)
slider = standard_hslider(adj)
if 'digits' in self.kwargs:
slider.set_digits(self.kwargs['digits'])
if setter is not None:
adj.connect("value_changed", setter, vpath.plus(self.name))
else:
slider.set_sensitive(False)
def refresher(values):
if self.update_sensitive(slider, values):
adj.set_value(self.get_value(values, vpath))
return (slider, refresher)
class IntSliderRow(SliderRow):
def __init__(self, label, name, minv, maxv, **kwargs):
SliderRow.__init__(self, label, name, minv, maxv, setter = adjustment_changed_int, **kwargs)
def create_widget(self, vpath):
(slider, refresher) = SliderRow.create_widget(self, vpath)
slider.connect('change-value', self.on_change_value)
slider.set_digits(0)
return slider, refresher
def on_change_value(self, range, scroll, value):
range.set_value(int(value))
return True
class MappedSliderRow(TableRowWidget):
def __init__(self, label, name, mapper, **kwargs):
TableRowWidget.__init__(self, label, name, **kwargs)
self.mapper = mapper
def create_widget(self, vpath):
setter = self.get_with_default('setter', adjustment_changed_float_mapped)
adj = Gtk.Adjustment(0, 0, 100, 1, 6, 0)
slider = standard_mapped_hslider(adj, self.mapper)
if setter is not None:
adj.connect("value_changed", setter, vpath.plus(self.name), self.mapper)
else:
slider.set_sensitive(False)
def refresher(values):
if self.update_sensitive(slider, values):
adj.set_value(self.mapper.unmap(self.get_value(values, vpath)))
return (slider, refresher)
class CheckBoxRow(TableRowWidget):
def create_widget(self, vpath):
widget = Gtk.CheckButton(self.label)
widget.connect("clicked", checkbox_changed_bool, vpath.plus(self.name))
def refresher(values):
if self.update_sensitive(widget, values):
widget.set_active(self.get_value(values, vpath) > 0)
return (widget, refresher)
#################################################################################################################################
class SelectObjectDialog(Gtk.Dialog):
def __init__(self, parent):
Gtk.Dialog.__init__(self, self.title, parent=parent, modal=True)
self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK)
self.set_default_response(Gtk.ResponseType.OK)
model = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING)
self.update_model(model)
scroll = Gtk.ScrolledWindow()
scenes = Gtk.TreeView(model)
scenes.insert_column_with_attributes(0, "Name", Gtk.CellRendererText(), text=0)
scenes.insert_column_with_attributes(1, "Title", Gtk.CellRendererText(), text=3)
scenes.insert_column_with_attributes(2, "Type", Gtk.CellRendererText(), text=1)
scenes.get_column(0).set_property('min_width', 150)
scenes.get_column(1).set_property('min_width', 300)
scenes.get_column(2).set_property('min_width', 150)
scroll.add(scenes)
self.vbox.pack_start(scroll, False, False, 0)
scenes.show()
scroll.set_size_request(640, 500)
scroll.show()
self.scenes = scenes
self.scenes.connect('cursor-changed', lambda w: self.update_default_button())
self.scenes.connect('row-activated', lambda w, path, column: self.response(Gtk.ResponseType.OK))
self.scenes.set_cursor((0,), scenes.get_column(0))
self.scenes.grab_focus()
self.update_default_button()
def get_selected_object(self):
return self.scenes.get_model()[self.scenes.get_cursor()[0].get_indices()[0]]
def update_default_button(self):
self.set_response_sensitive(Gtk.ResponseType.OK, self.scenes.get_cursor()[1] is not None)
#################################################################################################################################
class SaveConfigObjectDialog(Gtk.Dialog):
def __init__(self, parent, title):
Gtk.Dialog.__init__(self, title, parent, Gtk.DialogFlags.MODAL,
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK))
self.set_default_response(Gtk.ResponseType.OK)
l = Gtk.Label()
l.set_text_with_mnemonic("_Name")
e = Gtk.Entry()
self.entry = e
e.set_activates_default(True)
e.connect('changed', self.on_entry_changed)
row = Gtk.HBox()
row.pack_start(l, False, False, 5)
row.pack_start(e, True, True, 5)
row.show_all()
self.vbox.pack_start(row, True, True, 0)
e.grab_focus()
self.on_entry_changed(e)
def get_name(self):
return self.entry.get_text()
def on_entry_changed(self, w):
self.set_response_sensitive(Gtk.ResponseType.OK, w.get_text() != '')