4 from gui_tools
import *
7 #sample_dir = "/media/resources/samples/dooleydrums/"
8 sample_dir
= cbox
.Config
.get("init", "sample_dir")
10 ####################################################################################################################################################
12 class SampleDirsModel(Gtk
.ListStore
):
14 Gtk
.ListStore
.__init
__(self
, GObject
.TYPE_STRING
, GObject
.TYPE_STRING
)
16 for entry
in cbox
.Config
.keys("sample_dirs"):
17 path
= cbox
.Config
.get("sample_dirs", entry
)
18 self
.append((entry
, path
))
21 print ("Warning: no sample directories defined. Please add one or more entries of a form: 'name=/path/to/my/samples' to [sample_dirs] section of .cboxrc")
22 self
.append(("home", os
.getenv("HOME")))
23 self
.append(("/", "/"))
24 def has_dir(self
, dir):
25 return dir in [path
for entry
, path
in self
]
27 ####################################################################################################################################################
29 class SampleFilesModel(Gtk
.ListStore
):
30 def __init__(self
, dirs_model
):
31 self
.dirs_model
= dirs_model
32 self
.is_refreshing
= False
33 Gtk
.ListStore
.__init
__(self
, GObject
.TYPE_STRING
, GObject
.TYPE_STRING
)
35 def refresh(self
, sample_dir
):
37 self
.is_refreshing
= True
39 if sample_dir
is not None:
40 if not self
.dirs_model
.has_dir(sample_dir
):
41 self
.append((os
.path
.dirname(sample_dir
.rstrip("/")) + "/", "(up)"))
42 filelist
= sorted(glob
.glob("%s/*" % sample_dir
))
43 for f
in sorted(filelist
):
44 if os
.path
.isdir(f
) and not self
.dirs_model
.has_dir(f
+ "/"):
45 self
.append((f
+ "/", os
.path
.basename(f
)+"/"))
46 for f
in sorted(filelist
):
47 if f
.lower().endswith(".wav") and not os
.path
.isdir(f
):
48 self
.append((f
,os
.path
.basename(f
)))
50 self
.is_refreshing
= False
52 ####################################################################################################################################################
54 class KeyModelPath(object):
55 def __init__(self
, controller
, var
= None):
56 self
.controller
= controller
60 if self
.var
is not None:
61 print ("Warning: key model plus used twice with %s and %s" % (self
.var
, var
))
62 return KeyModelPath(self
.controller
, var
)
64 model
= self
.controller
.get_current_layer_model()
65 oldval
= model
.attribs
[self
.var
]
66 model
.attribs
[self
.var
] = value
67 if value
!= oldval
and not self
.controller
.no_sfz_update
:
68 print ("%s: set %s to %s" % (self
.controller
, self
.var
, value
))
69 self
.controller
.update_kit_later()
71 ####################################################################################################################################################
76 'ampeg_attack' : 0.001,
78 'ampeg_decay' : 0.001,
79 'ampeg_sustain' : 100.0,
80 'ampeg_release' : 0.1,
86 'fileg_attack' : 0.001,
88 'fileg_decay' : 0.001,
89 'fileg_sustain' : 100.0,
90 'fileg_release' : 0.1,
100 ####################################################################################################################################################
102 class KeySampleModel(object):
103 def __init__(self
, key
, sample
, filename
):
106 self
.filename
= filename
107 self
.mode
= "one_shot"
108 self
.attribs
= layer_attribs
.copy()
109 def set_sample(self
, sample
, filename
):
111 self
.filename
= filename
113 if self
.filename
== '':
115 s
= "<region> key=%d sample=%s loop_mode=%s" % (self
.key
, self
.filename
, self
.mode
)
116 s
+= "".join([" %s=%s" % item
for item
in self
.attribs
.items()])
119 return "<small>%s</small>" % self
.sample
121 ####################################################################################################################################################
123 class KeyModel(Gtk
.ListStore
):
124 def __init__(self
, key
):
126 Gtk
.ListStore
.__init
__(self
, GObject
.TYPE_STRING
, GObject
.TYPE_PYOBJECT
)
128 return "".join([ksm
.to_sfz() for name
, ksm
in self
])
130 return "\n".join([ksm
.to_markup() for name
, ksm
in self
])
132 ####################################################################################################################################################
134 class BankModel(dict):
141 s
+= self
[key
].to_sfz()
145 for b
in range(36, 36 + 16):
146 self
[b
] = KeyModel(b
)
147 def from_sfz(self
, data
, path
):
149 sfz
= sfzparser
.SFZ()
151 for r
in sfz
.regions
:
153 if ('key' in rdata
) and ('sample' in rdata
) and (rdata
['sample'] != ''):
154 key
= sfznote2value(rdata
['key'])
155 sample
= rdata
['sample']
156 sample_short
= os
.path
.basename(sample
)
158 ksm
= KeySampleModel(key
, sample_short
, sfzparser
.find_sample_in_path(path
, sample
))
159 for k
, v
in rdata
.items():
161 if type(layer_attribs
[k
]) is float:
162 ksm
.attribs
[k
] = float(v
)
163 elif type(layer_attribs
[k
]) is int:
164 ksm
.attribs
[k
] = int(float(v
))
167 self
[key
].append((sample_short
, ksm
))
169 ####################################################################################################################################################
171 class LayerListView(Gtk
.TreeView
):
172 def __init__(self
, controller
):
173 Gtk
.TreeView
.__init
__(self
, None)
174 self
.controller
= controller
175 self
.insert_column_with_attributes(0, "Name", Gtk
.CellRendererText(), text
=0)
176 self
.set_cursor((0,))
177 #self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [("text/plain", 0, 1)], Gdk.DragAction.COPY)
178 self
.connect('cursor-changed', self
.cursor_changed
)
179 #self.connect('drag-data-get', self.drag_data_get)
180 self
.drag_dest_set(Gtk
.DestDefaults
.ALL
, [], Gdk
.DragAction
.COPY
)
181 self
.drag_dest_set_target_list([])
182 self
.drag_dest_add_text_targets()
183 self
.connect('drag_data_received', self
.drag_data_received
)
184 def cursor_changed(self
, w
):
185 self
.controller
.on_layer_changed()
186 def drag_data_received(self
, widget
, context
, x
, y
, selection
, info
, etime
):
187 sample
, filename
= selection
.get_text().split("|")
188 pad_model
= self
.controller
.get_current_pad_model()
189 pad_model
.append((sample
, KeySampleModel(pad_model
.key
, sample
, filename
)))
190 self
.controller
.current_pad
.update_label()
191 self
.controller
.on_sample_dragged(self
)
193 ####################################################################################################################################################
195 class LayerEditor(Gtk
.VBox
):
196 def __init__(self
, controller
, bank_model
):
197 Gtk
.VBox
.__init
__(self
)
198 self
.table
= Gtk
.Table(len(self
.fields
) + 1, 2)
199 self
.table
.set_size_request(240, -1)
200 self
.controller
= controller
201 self
.bank_model
= bank_model
202 self
.name_widget
= Gtk
.Label()
203 self
.table
.attach(self
.name_widget
, 0, 2, 0, 1)
205 for i
in range(len(self
.fields
)):
206 self
.refreshers
.append(self
.fields
[i
].add_row(self
.table
, i
+ 1, KeyModelPath(controller
), None))
207 #self.table.attach(left_label(self.fields[i].label), 0, 1, i + 1, i + 2)
208 self
.pack_start(self
.table
, False, False, 0)
211 data
= self
.controller
.get_current_layer_model()
213 self
.name_widget
.set_text("")
215 self
.name_widget
.set_text(data
.sample
)
217 for r
in self
.refreshers
:
221 SliderRow("Volume", "volume", -100, 0),
222 SliderRow("Pan", "pan", -100, 100),
223 SliderRow("Effect 1", "effect1", 0, 100),
224 SliderRow("Effect 2", "effect2", 0, 100),
225 IntSliderRow("Output", "output", 0, 7),
226 SliderRow("Tune", "tune", -100, 100),
227 IntSliderRow("Transpose", "transpose", -48, 48),
228 IntSliderRow("Low velocity", "lovel", 1, 127),
229 IntSliderRow("High velocity", "hivel", 1, 127),
230 MappedSliderRow("Amp Attack", "ampeg_attack", env_mapper
),
231 MappedSliderRow("Amp Hold", "ampeg_hold", env_mapper
),
232 MappedSliderRow("Amp Decay", "ampeg_decay", env_mapper
),
233 SliderRow("Amp Sustain", "ampeg_sustain", 0, 100),
234 MappedSliderRow("Amp Release", "ampeg_release", env_mapper
),
235 MappedSliderRow("Flt Cutoff", "cutoff", filter_freq_mapper
),
236 MappedSliderRow("Flt Resonance", "resonance", LogMapper(0.707, 16, "%0.1f x")),
237 SliderRow("Flt Depth", "fileg_depth", -4800, 4800),
238 MappedSliderRow("Flt Attack", "fileg_attack", env_mapper
),
239 MappedSliderRow("Flt Hold", "fileg_hold", env_mapper
),
240 MappedSliderRow("Flt Decay", "fileg_decay", env_mapper
),
241 SliderRow("Flt Sustain", "fileg_sustain", 0, 100),
242 MappedSliderRow("Flt Release", "fileg_release", env_mapper
),
243 IntSliderRow("Group", "group", 0, 15),
244 IntSliderRow("Off by group", "off_by", 0, 15),
247 ####################################################################################################################################################
249 class PadButton(Gtk
.RadioButton
):
250 def __init__(self
, controller
, bank_model
, key
):
251 Gtk
.RadioButton
.__init
__(self
, use_underline
= False)
253 self
.controller
= controller
254 self
.bank_model
= bank_model
256 self
.set_size_request(100, 100)
258 self
.drag_dest_set(Gtk
.DestDefaults
.ALL
, [], Gdk
.DragAction
.COPY
)
259 self
.drag_dest_set_target_list([])
260 self
.drag_dest_add_text_targets()
261 self
.connect('drag_data_received', self
.drag_data_received
)
262 #self.connect('toggled', lambda widget: widget.controller.on_pad_selected(widget) if widget.get_active() else None)
263 self
.connect('pressed', self
.on_clicked
)
264 def get_key_model(self
):
265 return self
.bank_model
[self
.key
]
266 def drag_data_received(self
, widget
, context
, x
, y
, selection
, info
, etime
):
267 sample
, filename
= selection
.get_text().split("|")
268 self
.get_key_model().clear()
269 self
.get_key_model().append((sample
, KeySampleModel(self
.key
, sample
, filename
)))
271 self
.controller
.on_sample_dragged(self
)
272 def update_label(self
):
273 data
= self
.get_key_model()
278 self
.get_child().set_markup(data
.to_markup())
279 self
.get_child().set_line_wrap(True)
280 def on_clicked(self
, w
):
281 self
.controller
.play_note(self
.key
)
282 w
.controller
.on_pad_selected(w
)
284 ####################################################################################################################################################
286 class PadTable(Gtk
.Table
):
287 def __init__(self
, controller
, bank_model
, rows
, columns
):
288 Gtk
.Table
.__init
__(self
, rows
, columns
, True)
292 for r
in range(0, rows
):
293 for c
in range(0, columns
):
294 key
= 36 + (rows
- r
- 1) * columns
+ c
295 b
= PadButton(controller
, bank_model
, key
)
296 if group
is not None:
298 self
.attach(standard_align(b
, 0.5, 0.5, 0, 0), c
, c
+ 1, r
, r
+ 1)
301 for pad
in self
.keys
.values():
304 ####################################################################################################################################################
306 class FileView(Gtk
.TreeView
):
307 def __init__(self
, dirs_model
, controller
):
308 self
.controller
= controller
309 self
.is_playing
= True
310 self
.dirs_model
= dirs_model
311 self
.files_model
= SampleFilesModel(dirs_model
)
312 Gtk
.TreeView
.__init
__(self
, self
.files_model
)
313 self
.insert_column_with_attributes(0, "Name", Gtk
.CellRendererText(), text
=1)
314 self
.set_cursor((0,))
315 self
.enable_model_drag_source(Gdk
.ModifierType
.BUTTON1_MASK
, [], Gdk
.DragAction
.COPY
)
316 self
.drag_source_add_text_targets()
317 self
.cursor_changed_handler
= self
.connect('cursor-changed', self
.cursor_changed
)
318 self
.connect('drag-data-get', self
.drag_data_get
)
319 self
.connect('row-activated', self
.on_row_activated
)
321 def stop_playing(self
):
323 self
.controller
.stop_preview()
324 self
.is_playing
= False
326 def start_playing(self
, fn
):
327 self
.is_playing
= True
328 self
.controller
.start_preview(fn
)
330 def cursor_changed(self
, w
):
331 if self
.files_model
.is_refreshing
:
335 c
= self
.get_cursor()
337 fn
= self
.files_model
[c
[0].get_indices()[0]][0]
341 self
.start_playing(fn
)
343 self
.stop_playing(fn
)
345 def drag_data_get(self
, treeview
, context
, selection
, target_id
, etime
):
346 cursor
= treeview
.get_cursor()
347 if cursor
is not None:
348 c
= cursor
[0].get_indices()[0]
349 fr
= self
.files_model
[c
]
350 selection
.set_text(str(fr
[1]+"|"+fr
[0]), -1)
352 def on_row_activated(self
, treeview
, path
, column
):
353 c
= self
.get_cursor()
354 fn
, label
= self
.files_model
[c
[0].get_indices()[0]]
356 self
.files_model
.refresh(fn
)
358 ####################################################################################################################################################
360 class EditorDialog(Gtk
.Dialog
):
361 def __init__(self
, parent
):
363 Gtk
.Dialog
.__init
__(self
, "Drum kit editor", parent
, Gtk
.DialogFlags
.DESTROY_WITH_PARENT
,
366 self
.menu_bar
= Gtk
.MenuBar()
367 self
.menu_bar
.append(create_menu("_Kit", [
368 ("_New", self
.on_kit_new
),
369 ("_Open...", self
.on_kit_open
),
370 ("_Save as...", self
.on_kit_save_as
),
371 ("_Close", lambda w
: self
.response(Gtk
.ResponseType
.OK
)),
373 self
.menu_bar
.append(create_menu("_Layer", [
374 ("_Delete", self
.on_layer_delete
),
376 self
.vbox
.pack_start(self
.menu_bar
, False, False, 0)
378 self
.hbox
= Gtk
.HBox(spacing
= 5)
380 self
.update_source
= None
381 self
.current_pad
= None
382 self
.dirs_model
= SampleDirsModel()
383 self
.bank_model
= BankModel()
384 self
.tree
= FileView(self
.dirs_model
, self
)
385 self
.layer_list
= LayerListView(self
)
386 self
.layer_editor
= LayerEditor(self
, self
.bank_model
)
387 self
.no_sfz_update
= False
389 combo
= Gtk
.ComboBox(model
= self
.dirs_model
)
390 cell
= Gtk
.CellRendererText()
391 combo
.pack_start(cell
, True)
392 combo
.add_attribute(cell
, 'text', 0)
393 combo
.connect('changed', lambda combo
, tree_model
, combo_model
: tree_model
.refresh(combo_model
[combo
.get_active()][1] if combo
.get_active() >= 0 else None), self
.tree
.get_model(), combo
.get_model())
395 sw
= Gtk
.ScrolledWindow()
396 sw
.set_policy(Gtk
.PolicyType
.AUTOMATIC
, Gtk
.PolicyType
.ALWAYS
)
399 left_box
= Gtk
.VBox(spacing
= 5)
400 left_box
.pack_start(combo
, False, False, 0)
401 left_box
.pack_start(sw
, True, True, 5)
402 self
.hbox
.pack_start(left_box
, True, True, 0)
403 sw
.set_size_request(200, -1)
405 self
.pads
= PadTable(self
, self
.bank_model
, 4, 4)
406 self
.hbox
.pack_start(self
.pads
, True, True, 5)
408 right_box
= Gtk
.VBox(spacing
= 5)
409 sw
= Gtk
.ScrolledWindow()
410 sw
.set_policy(Gtk
.PolicyType
.AUTOMATIC
, Gtk
.PolicyType
.ALWAYS
)
411 sw
.set_size_request(320, 100)
412 sw
.set_shadow_type(Gtk
.ShadowType
.ETCHED_IN
)
413 sw
.add(self
.layer_list
)
414 right_box
.pack_start(sw
, True, True, 0)
415 sw
= Gtk
.ScrolledWindow()
416 sw
.set_size_request(320, 200)
417 sw
.set_policy(Gtk
.PolicyType
.NEVER
, Gtk
.PolicyType
.ALWAYS
)
418 sw
.add_with_viewport(self
.layer_editor
)
419 right_box
.pack_start(sw
, True, True, 0)
420 self
.hbox
.pack_start(right_box
, True, True, 0)
422 self
.vbox
.pack_start(self
.hbox
, False, False, 0)
424 widget
= self
.pads
.keys
[36]
425 widget
.set_active(True)
429 def prepare_scene(self
):
431 for scene
in cbox
.Document
.get_engine().status().scenes
:
432 scene_status
= scene
.status()
433 layers
= [layer
.status().instrument_name
for layer
in scene_status
.layers
]
434 if '_preview_sample' in layers
and '_preview_kit' in layers
:
437 if found_scene
is None:
438 self
.scene
= cbox
.Document
.get_engine().new_scene()
439 self
.scene
.add_new_instrument_layer("_preview_sample", "stream_player", pos
= 0)
440 ps
= self
.scene
.status().instruments
['_preview_sample'][1]
441 ps
.cmd('/output/1/gain', None, -12.0)
442 self
.scene
.add_new_instrument_layer("_preview_kit", "sampler", pos
= 1)
444 self
.scene
= found_scene
445 _
, self
._preview
_kit
= self
.scene
.status().instruments
['_preview_kit']
446 _
, self
._preview
_sample
= self
.scene
.status().instruments
['_preview_sample']
448 def update_kit(self
):
449 self
._preview
_kit
.engine
.load_patch_from_string(0, "", self
.bank_model
.to_sfz(), "Preview")
450 self
.update_source
= None
453 def update_kit_later(self
):
454 if self
.update_source
is not None:
455 glib
.source_remove(self
.update_source
)
456 self
.update_source
= glib
.idle_add(self
.update_kit
)
458 def on_sample_dragged(self
, widget
):
460 if widget
== self
.current_pad
:
461 self
.layer_list
.set_cursor(len(self
.layer_list
.get_model()) - 1)
462 # self.pad_editor.refresh()
464 def refresh_layers(self
):
466 self
.no_sfz_update
= True
467 if self
.current_pad
is not None:
468 self
.layer_list
.set_model(self
.bank_model
[self
.current_pad
.key
])
469 self
.layer_list
.set_cursor(0)
471 self
.layer_list
.set_model(None)
472 self
.layer_editor
.refresh()
474 self
.no_sfz_update
= False
476 def on_pad_selected(self
, widget
):
477 self
.current_pad
= widget
478 self
.refresh_layers()
480 def on_layer_changed(self
):
481 self
.layer_editor
.refresh()
483 def on_layer_delete(self
, w
):
484 if self
.layer_list
.get_cursor()[0] is None:
486 model
= self
.layer_list
.get_model()
487 model
.remove(model
.get_iter(self
.layer_list
.get_cursor()[0]))
488 self
.current_pad
.update_label()
489 self
.layer_editor
.refresh()
491 self
.layer_list
.set_cursor(0)
493 def get_current_pad_model(self
):
494 return self
.current_pad
.get_key_model()
496 def get_current_layer_model(self
):
497 if self
.layer_list
.get_cursor()[0] is None:
499 return self
.layer_list
.get_model()[self
.layer_list
.get_cursor()[0]][1]
501 def on_kit_new(self
, widget
):
502 self
.bank_model
.from_sfz('', '')
504 self
.refresh_layers()
507 def on_kit_open(self
, widget
):
508 dlg
= Gtk
.FileChooserDialog('Open a pad bank', self
, Gtk
.FileChooserAction
.OPEN
,
509 (Gtk
.STOCK_CANCEL
, Gtk
.ResponseType
.CANCEL
, Gtk
.STOCK_OPEN
, Gtk
.ResponseType
.APPLY
))
510 dlg
.add_filter(standard_filter(["*.sfz", "*.SFZ"], "SFZ files"))
511 dlg
.add_filter(standard_filter(["*"], "All files"))
513 if dlg
.run() == Gtk
.ResponseType
.APPLY
:
514 sfz_data
= open(dlg
.get_filename(), "r").read()
515 self
.bank_model
.from_sfz(sfz_data
, dlg
.get_current_folder())
517 self
.refresh_layers()
522 def on_kit_save_as(self
, widget
):
523 dlg
= Gtk
.FileChooserDialog('Save a pad bank', self
, Gtk
.FileChooserAction
.SAVE
,
524 (Gtk
.STOCK_CANCEL
, Gtk
.ResponseType
.CANCEL
, Gtk
.STOCK_SAVE
, Gtk
.ResponseType
.APPLY
))
525 dlg
.add_filter(standard_filter(["*.sfz", "*.SFZ"], "SFZ files"))
526 dlg
.add_filter(standard_filter(["*"], "All files"))
528 if dlg
.run() == Gtk
.ResponseType
.APPLY
:
529 open(dlg
.get_filename(), "w").write(self
.bank_model
.to_sfz())
533 def start_preview(self
, filename
):
534 self
._preview
_sample
.engine
.load(filename
)
535 self
._preview
_sample
.engine
.play()
536 def stop_preview(self
):
537 self
._preview
_sample
.engine
.unload()
538 def play_note(self
, note
, vel
= 127):
539 self
.scene
.send_midi_event(0x90, note
, vel
)
540 self
.scene
.send_midi_event(0x80, note
, vel
)