Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Add midi channel #157

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions jack_mixer/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ def add_channel(
name,
stereo=True,
direct_output=True,
midi_channel_cc=1,
volume_cc=-1,
balance_cc=-1,
mute_cc=-1,
Expand All @@ -386,7 +387,7 @@ def add_channel(
error_dialog(self.window, _("Input channel creation failed."))
return

channel.assign_midi_ccs(volume_cc, balance_cc, mute_cc, solo_cc)
channel.assign_midi_ccs(midi_channel_cc, volume_cc, balance_cc, mute_cc, solo_cc)
return channel

def add_channel_precreated(self, channel):
Expand Down Expand Up @@ -432,6 +433,7 @@ def add_output_channel(
self,
name,
stereo=True,
midi_channel_cc=1,
volume_cc=-1,
balance_cc=-1,
mute_cc=-1,
Expand All @@ -448,7 +450,7 @@ def add_output_channel(
error_dialog(self.window, _("Output channel creation failed."))
return

channel.assign_midi_ccs(volume_cc, balance_cc, mute_cc)
channel.assign_midi_ccs(midi_channel_cc, volume_cc, balance_cc, mute_cc)
return channel

def add_output_channel_precreated(self, channel):
Expand Down Expand Up @@ -1048,6 +1050,7 @@ def save_to_xml(self, file):
b = self.get_xml_serialization()
b.save(file)

# I think I broke loading XML (or perhaps saving) with my midi channel changes
def load_from_xml(self, file, silence_errors=False, from_nsm=False):
log.debug("Loading from XML...")
self.unserialized_channels = []
Expand Down
67 changes: 51 additions & 16 deletions jack_mixer/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def __init__(self, app, name, stereo=True, direct_output=True, initial_vol=None)
self.wants_direct_output = direct_output
self.post_fader_output_channel = None
self.future_out_mute = None
self.future_midi_channel_midi_cc = None
self.future_volume_midi_cc = None
self.future_balance_midi_cc = None
self.future_mute_midi_cc = None
Expand Down Expand Up @@ -275,6 +276,7 @@ def unrealize(self):
# ---------------------------------------------------------------------------------------------
# Signal/event handlers

# this is where the double click happens for channel props
def on_label_mouse(self, widget, event):
if event.type == Gdk.EventType._2BUTTON_PRESS:
if event.button == 1:
Expand Down Expand Up @@ -419,7 +421,17 @@ def on_monitor_button_toggled(self, button):
if self.app.monitored_channel is self:
self.app.monitored_channel = None

def assign_midi_ccs(self, volume_cc, balance_cc, mute_cc, solo_cc=None):
# should probs have a param here for MIDI channel
def assign_midi_ccs(self, midi_channel_cc, volume_cc, balance_cc, mute_cc, solo_cc=None):
try:
if midi_channel_cc != -1:
self.channel.midi_channel_midi_cc = midi_channel_cc
else:
midi_channel_cc = self.channel.autoset_midi_channel_midi_cc()

log.debug("Channel '%s' MIDI channel assigned to #%s.", self.channel.name, midi_channel_cc)
except Exception as exc:
log.error("Channel '%s' MIDI channel assignment failed: %s", self.channel.name, exc)
try:
if volume_cc != -1:
self.channel.volume_midi_cc = volume_cc
Expand Down Expand Up @@ -577,6 +589,8 @@ def serialize(self, object_backend):

if hasattr(self.channel, "out_mute"):
object_backend.add_property("out_mute", str(self.channel.out_mute))
if self.channel.midi_channel_midi_cc != -1:
object_backend.add_property("midi_channel_midi_cc", str(self.channel.midi_channel_midi_cc))
if self.channel.volume_midi_cc != -1:
object_backend.add_property("volume_midi_cc", str(self.channel.volume_midi_cc))
if self.channel.balance_midi_cc != -1:
Expand All @@ -599,8 +613,10 @@ def unserialize_property(self, name, value):
elif name == "meter_prefader":
self.meter_prefader = (value == "True")
return True
elif name == "midi_channel":
self.future_midi_channel_midi_cc = int(value)
return True
elif name == "volume_midi_cc":

self.future_volume_midi_cc = int(value)
return True
elif name == "balance_midi_cc":
Expand Down Expand Up @@ -642,6 +658,8 @@ def realize(self):

super().realize()

if self.future_midi_channel_midi_cc is not None:
self.channel.midi_channel = self.future_midi_channel_midi_cc
if self.future_volume_midi_cc is not None:
self.channel.volume_midi_cc = self.future_volume_midi_cc
if self.future_balance_midi_cc is not None:
Expand Down Expand Up @@ -859,6 +877,8 @@ def realize(self):

super().realize()

if self.future_midi_channel_midi_cc is not None:
self.channel.midi_channel_midi_cc = self.future_midi_channel_midi_cc
if self.future_volume_midi_cc is not None:
self.channel.volume_midi_cc = self.future_volume_midi_cc
if self.future_balance_midi_cc is not None:
Expand Down Expand Up @@ -1073,38 +1093,47 @@ def create_ui(self):
cc_tooltip = _(
"{param} MIDI Control Change number " "(0-127, set to -1 to assign next free CC #)"
)
channel_label = Gtk.Label.new_with_mnemonic(_("_Channel"))
channel_label.set_halign(Gtk.Align.START)
grid.attach(channel_label,0,0,1,1)
self.entry_midi_channel_cc = Gtk.SpinButton.new_with_range(1, 16, 1)
self.entry_midi_channel_cc.set_tooltip_text(cc_tooltip.format(param=_("Channel")))
channel_label.set_mnemonic_widget(self.entry_midi_channel_cc)
grid.attach(self.entry_midi_channel_cc, 1, 0, 1, 1)

volume_label = Gtk.Label.new_with_mnemonic(_("_Volume"))
volume_label.set_halign(Gtk.Align.START)
grid.attach(volume_label, 0, 0, 1, 1)
grid.attach(volume_label, 0, 1, 1, 1)
self.entry_volume_cc = Gtk.SpinButton.new_with_range(-1, 127, 1)
self.entry_volume_cc.set_tooltip_text(cc_tooltip.format(param=_("Volume")))
volume_label.set_mnemonic_widget(self.entry_volume_cc)
grid.attach(self.entry_volume_cc, 1, 0, 1, 1)
grid.attach(self.entry_volume_cc, 1, 1, 1, 1)
self.button_sense_midi_volume = Gtk.Button(_("Learn"))
self.button_sense_midi_volume.connect("clicked", self.on_sense_midi_volume_clicked)
grid.attach(self.button_sense_midi_volume, 2, 0, 1, 1)
grid.attach(self.button_sense_midi_volume, 2, 1, 1, 1)

balance_label = Gtk.Label.new_with_mnemonic(_("_Balance"))
balance_label.set_halign(Gtk.Align.START)
grid.attach(balance_label, 0, 1, 1, 1)
grid.attach(balance_label, 0, 2, 1, 1)
self.entry_balance_cc = Gtk.SpinButton.new_with_range(-1, 127, 1)
self.entry_balance_cc.set_tooltip_text(cc_tooltip.format(param=_("Balance")))
balance_label.set_mnemonic_widget(self.entry_balance_cc)
grid.attach(self.entry_balance_cc, 1, 1, 1, 1)
grid.attach(self.entry_balance_cc, 1, 2, 1, 1)

self.button_sense_midi_balance = Gtk.Button(_("Learn"))
self.button_sense_midi_balance.connect("clicked", self.on_sense_midi_balance_clicked)
grid.attach(self.button_sense_midi_balance, 2, 1, 1, 1)
grid.attach(self.button_sense_midi_balance, 2, 2, 1, 1)

mute_label = Gtk.Label.new_with_mnemonic(_("M_ute"))
mute_label.set_halign(Gtk.Align.START)
grid.attach(mute_label, 0, 2, 1, 1)
grid.attach(mute_label, 0, 3, 1, 1)
self.entry_mute_cc = Gtk.SpinButton.new_with_range(-1, 127, 1)
self.entry_mute_cc.set_tooltip_text(cc_tooltip.format(param=_("Mute")))
mute_label.set_mnemonic_widget(self.entry_mute_cc)
grid.attach(self.entry_mute_cc, 1, 2, 1, 1)
grid.attach(self.entry_mute_cc, 1, 3, 1, 1)
self.button_sense_midi_mute = Gtk.Button(_("Learn"))
self.button_sense_midi_mute.connect("clicked", self.on_sense_midi_mute_clicked)
grid.attach(self.button_sense_midi_mute, 2, 2, 1, 1)
grid.attach(self.button_sense_midi_mute, 2, 3, 1, 1)

if isinstance(self, NewInputChannelDialog) or (
self.channel and isinstance(self.channel, InputChannel)
Expand All @@ -1122,14 +1151,14 @@ def create_ui(self):
):
solo_label = Gtk.Label.new_with_mnemonic(_("S_olo"))
solo_label.set_halign(Gtk.Align.START)
grid.attach(solo_label, 0, 3, 1, 1)
grid.attach(solo_label, 0, 4, 1, 1)
self.entry_solo_cc = Gtk.SpinButton.new_with_range(-1, 127, 1)
self.entry_solo_cc.set_tooltip_text(cc_tooltip.format(param=_("Solo")))
solo_label.set_mnemonic_widget(self.entry_solo_cc)
grid.attach(self.entry_solo_cc, 1, 3, 1, 1)
grid.attach(self.entry_solo_cc, 1, 4, 1, 1)
self.button_sense_midi_solo = Gtk.Button(_("Learn"))
self.button_sense_midi_solo.connect("clicked", self.on_sense_midi_solo_clicked)
grid.attach(self.button_sense_midi_solo, 2, 3, 1, 1)
grid.attach(self.button_sense_midi_solo, 2, 4, 1, 1)

self.vbox.show_all()

Expand All @@ -1141,6 +1170,7 @@ def fill_ui(self):
self.mono.set_active(True)
self.mono.set_sensitive(False)
self.stereo.set_sensitive(False)
self.entry_midi_channel_cc.set_value(self.channel.channel.midi_channel_midi_cc)
self.entry_volume_cc.set_value(self.channel.channel.volume_midi_cc)
self.entry_balance_cc.set_value(self.channel.channel.balance_midi_cc)
self.entry_mute_cc.set_value(self.channel.channel.mute_midi_cc)
Expand Down Expand Up @@ -1215,11 +1245,12 @@ def on_response_cb(self, dlg, response_id, *args):
self.channel.post_fader_output_channel.remove()
self.channel.post_fader_output_channel = None

for control in ("volume", "balance", "mute", "solo"):
for control in ("midi_channel", "volume", "balance", "mute", "solo"):
widget = getattr(self, "entry_{}_cc".format(control), None)
if widget is not None:
value = int(widget.get_value())
if value != -1:
# looking for where midi_channel_midi_cc can persist in a running session. Is this in the C function?
setattr(self.channel.channel, "{}_midi_cc".format(control), value)

self.hide()
Expand All @@ -1236,7 +1267,7 @@ def on_entry_name_changed(self, entry):
sensitive = True
self.ok_button.set_sensitive(sensitive)


# prolly where the values of the dialog persist?
class NewChannelDialog(ChannelPropertiesDialog):
def create_ui(self):
super().create_ui()
Expand Down Expand Up @@ -1267,6 +1298,7 @@ def fill_ui(self, **values):
self.direct_output.set_active(values.get("direct_output", True))
# don't set MIDI CCs to previously used values, because they
# would overwrite existing mappings, if accepted.
self.entry_midi_channel_cc.set_value(1)
self.entry_volume_cc.set_value(-1)
self.entry_balance_cc.set_value(-1)
self.entry_mute_cc.set_value(-1)
Expand All @@ -1280,6 +1312,7 @@ def get_result(self):
"name": self.entry_name.get_text(),
"stereo": self.stereo.get_active(),
"direct_output": self.direct_output.get_active(),
"midi_channel_cc": int(self.entry_midi_channel_cc.get_value()),
"volume_cc": int(self.entry_volume_cc.get_value()),
"balance_cc": int(self.entry_balance_cc.get_value()),
"mute_cc": int(self.entry_mute_cc.get_value()),
Expand Down Expand Up @@ -1338,6 +1371,7 @@ def fill_ui(self, **values):

# don't set MIDI CCs to previously used values, because they
# would overwrite existing mappings, if accepted.
self.entry_midi_channel_cc.set_value(-1)
self.entry_volume_cc.set_value(-1)
self.entry_balance_cc.set_value(-1)
self.entry_mute_cc.set_value(-1)
Expand All @@ -1352,6 +1386,7 @@ def get_result(self):
return {
"name": self.entry_name.get_text(),
"stereo": self.stereo.get_active(),
"midi_channel_cc": int(self.entry_midi_channel_cc.get_value()),
"volume_cc": int(self.entry_volume_cc.get_value()),
"balance_cc": int(self.entry_balance_cc.get_value()),
"mute_cc": int(self.entry_mute_cc.get_value()),
Expand Down
2 changes: 1 addition & 1 deletion meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ option('check-py-modules',
)
option('verbose',
type: 'boolean',
value: false,
value: true,
description: 'Turn on debug logging (for development)'
)
option('wheel',
Expand Down
3 changes: 3 additions & 0 deletions src/_jack_mixer.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,18 @@ cdef extern from "jack_mixer.h":
cdef int channel_autoset_mute_midi_cc(jack_mixer_channel_t channel)
cdef int channel_autoset_solo_midi_cc(jack_mixer_channel_t channel)
cdef int channel_autoset_volume_midi_cc(jack_mixer_channel_t channel)
cdef int channel_autoset_midi_channel_midi_cc(jack_mixer_channel_t channel)

cdef int channel_get_balance_midi_cc(jack_mixer_channel_t channel)
cdef int channel_get_mute_midi_cc(jack_mixer_channel_t channel)
cdef int channel_get_solo_midi_cc(jack_mixer_channel_t channel)
cdef int channel_get_volume_midi_cc(jack_mixer_channel_t channel)
cdef int channel_get_midi_channel_midi_cc(jack_mixer_channel_t channel)
cdef int channel_set_balance_midi_cc(jack_mixer_channel_t channel, int new_cc)
cdef int channel_set_mute_midi_cc(jack_mixer_channel_t channel, int new_cc)
cdef int channel_set_solo_midi_cc(jack_mixer_channel_t channel, int new_cc)
cdef int channel_set_volume_midi_cc(jack_mixer_channel_t channel, int new_cc)
cdef int channel_set_midi_channel_midi_cc(jack_mixer_channel_t channel, int new_channel)

cdef void channel_set_midi_scale(jack_mixer_channel_t channel, jack_mixer_scale_t scale)

Expand Down
14 changes: 14 additions & 0 deletions src/_jack_mixer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,16 @@ cdef class Channel:
if channel_set_solo_midi_cc(self._channel, cc) != 0:
raise ValueError(jack_mixer_error_str().decode('utf-8'))

@property
def midi_channel_midi_cc(self):
"""MIDI channel to control audio channel."""
return channel_get_midi_channel_midi_cc(self._channel)

@midi_channel_midi_cc.setter
def midi_channel_midi_cc(self, int channel):
if channel_set_midi_channel_midi_cc(self._channel, channel) != 0:
raise ValueError(jack_mixer_error_str().decode('utf-8'))

@property
def volume_midi_cc(self):
"""MIDI CC assigned to control channel volume."""
Expand All @@ -464,6 +474,10 @@ cdef class Channel:
"""Auto assign MIDI CC for channel solo status."""
return channel_autoset_solo_midi_cc(self._channel)

def autoset_midi_channel_midi_cc(self):
"""Auto assign MIDI channel."""
return channel_autoset_midi_channel_midi_cc(self._channel)

def autoset_volume_midi_cc(self):
"""Auto assign MIDI CC for channel volume."""
return channel_autoset_volume_midi_cc(self._channel)
Expand Down
Loading