]> git.0d.be Git - jack_mixer.git/commitdiff
Add midi behavior modes: "pick up" and "jump to value"
authorDaniel Sheeler <dsheeler@pobox.com>
Sun, 28 Jun 2020 21:39:58 +0000 (16:39 -0500)
committerDaniel Sheeler <dsheeler@pobox.com>
Sun, 28 Jun 2020 21:39:58 +0000 (16:39 -0500)
gui.py
jack_mixer.c
jack_mixer.h
jack_mixer.py
jack_mixer_c.c
preferences.py

diff --git a/gui.py b/gui.py
index c335e1cfb2735b4ebfe4372a0611bb2de44924ab..bdfa49036aad01cb299ce0693432519dbfec9245 100644 (file)
--- a/gui.py
+++ b/gui.py
@@ -19,6 +19,7 @@ import gi
 from gi.repository import GObject
 import os
 import configparser
+from serialization import SerializedObject
 
 try:
     import xdg
@@ -32,24 +33,24 @@ def lookup_scale(scales, scale_id):
             return scale
     return None
 
-class Factory(GObject.GObject):
+class Factory(GObject.GObject, SerializedObject):
+
     def __init__(self, topwindow, meter_scales, slider_scales):
+        self.midi_behavior_modes = { 'Jump To Value' : 0, 'Pick Up' : 1 }
         GObject.GObject.__init__(self)
         self.topwindow = topwindow
         self.meter_scales = meter_scales
         self.slider_scales = slider_scales
-
+        self.set_default_preferences()
         if xdg:
             self.config = configparser.ConfigParser()
             self.path = os.path.join(BaseDirectory.save_config_path('jack_mixer'), 'preferences.ini')
             if os.path.isfile(self.path):
                 self.read_preferences()
             else:
-                self.set_default_preferences()
                 self.write_preferences()
         else:
             print("Cannot load PyXDG. Your preferences will not be preserved across jack_mixer invocations")
-            self.set_default_preferences()
 
     def set_default_preferences(self):
         self.default_meter_scale = self.meter_scales[0]
@@ -57,6 +58,7 @@ class Factory(GObject.GObject):
         self.vumeter_color = '#ccb300'
         self.vumeter_color_scheme = 'default'
         self.use_custom_widgets = False
+        self.midi_behavior_mode = 'Jump To Value'
 
     def read_preferences(self):
         self.config.read(self.path)
@@ -126,6 +128,10 @@ class Factory(GObject.GObject):
             self.write_preferences()
         self.emit('use-custom-widgets-changed', self.use_custom_widgets)
 
+    def set_midi_behavior_mode(self, mode):
+        self.midi_behavior_mode = mode
+        self.emit("midi-behavior-mode-changed", self.midi_behavior_modes[self.midi_behavior_mode])
+
     def get_default_meter_scale(self):
         return self.default_meter_scale
 
@@ -141,6 +147,22 @@ class Factory(GObject.GObject):
     def get_use_custom_widgets(self):
         return self.use_custom_widgets
 
+    def get_midi_behavior_mode(self):
+        return self.midi_behavior_mode
+
+    @classmethod
+    def serialization_name(cls):
+        return 'gui_factory'
+
+    def serialize(self, object_backend):
+        object_backend.add_property("midi_behavior_mode", self.get_midi_behavior_mode())
+
+    def unserialize_property(self, name, value):
+        if name == "midi_behavior_mode":
+            self.set_midi_behavior_mode(value)
+            return True
+        return False
+
 GObject.signal_new("default-meter-scale-changed", Factory,
                 GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION,
                 None, [GObject.TYPE_PYOBJECT])
@@ -156,3 +178,6 @@ GObject.signal_new('vumeter-color-scheme-changed', Factory,
 GObject.signal_new('use-custom-widgets-changed', Factory,
                 GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION,
                 None, [bool])
+GObject.signal_new('midi-behavior-mode-changed', Factory,
+                GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION,
+                None, [int])
index e18ea8af12ecdbbaf8f65831a0c3e96809a56d4c..95a586b44ae7131fbc766fe253956f3cb23529b7 100644 (file)
 #include "jack_compat.h"
 
 #define VOLUME_TRANSITION_SECONDS 0.01
+
+#define MIDI_PICK_UP_DIFF 20
+
 #define PEAK_FRAMES_CHUNK 4800
+
 // we don't know how much to allocate, but we don't want to wait with 
 // allocating until we're in the process() callback, so we just take a 
 // fairly big chunk: 4 periods per buffer, 4096 samples per period.
@@ -125,6 +129,7 @@ struct jack_mixer
   jack_port_t * port_midi_in;
   jack_port_t * port_midi_out;
   int last_midi_channel;
+  enum midi_behavior_mode midi_behavior;
 
   struct channel* midi_cc_map[128];
 };
@@ -1098,15 +1103,18 @@ process(
       }
       else if (channel_ptr->midi_cc_volume_index == in_event.buffer[1])
       {
-        if (channel_ptr->volume_new != channel_ptr->volume) {
-          channel_ptr->volume = channel_ptr->volume + channel_ptr->volume_idx *
-           (channel_ptr->volume_new - channel_ptr->volume) /
-           channel_ptr->num_volume_transition_steps;
+        int current_midi =  (int)(127 * scale_db_to_scale(channel_ptr->midi_scale, value_to_db(channel_ptr->volume)));
+        if ((mixer_ptr->midi_behavior == Pick_Up && abs(in_event.buffer[2] -current_midi) <= MIDI_PICK_UP_DIFF) || mixer_ptr->midi_behavior == Jump_To_Value) {
+            if (channel_ptr->volume_new != channel_ptr->volume) {
+              channel_ptr->volume = channel_ptr->volume + channel_ptr->volume_idx *
+               (channel_ptr->volume_new - channel_ptr->volume) /
+               channel_ptr->num_volume_transition_steps;
+            }
+            channel_ptr->volume_idx = 0;
+            channel_ptr->volume_new = db_to_value(scale_scale_to_db(channel_ptr->midi_scale,
+             (double)in_event.buffer[2] / 127));
+            LOG_DEBUG("\"%s\" volume -> %f", channel_ptr->name, channel_ptr->volume_new);
         }
-        channel_ptr->volume_idx = 0;
-        channel_ptr->volume_new = db_to_value(scale_scale_to_db(channel_ptr->midi_scale,
-         (double)in_event.buffer[2] / 127));
-        LOG_DEBUG("\"%s\" volume -> %f", channel_ptr->name, channel_ptr->volume_new);
       }
       else if (channel_ptr->midi_cc_mute_index == in_event.buffer[1])
       {
@@ -1212,6 +1220,8 @@ create(
 
   mixer_ptr->last_midi_channel = -1;
 
+  mixer_ptr->midi_behavior = Jump_To_Value;
+
   for (i = 0 ; i < 128 ; i++)
   {
     mixer_ptr->midi_cc_map[i] = NULL;
@@ -1324,6 +1334,22 @@ set_last_midi_channel(
   return 0;
 }
 
+int
+get_midi_behavior_mode(
+  jack_mixer_t mixer)
+{
+  return mixer_ctx_ptr->midi_behavior;
+}
+
+unsigned int
+set_midi_behavior_mode(
+  jack_mixer_t mixer,
+  enum midi_behavior_mode mode)
+{
+  mixer_ctx_ptr->midi_behavior = mode;
+  return 0;
+}
+
 jack_mixer_channel_t
 add_channel(
   jack_mixer_t mixer,
index df96b11bbc33dbe48ec466b13f7be7719e9b472a..2efa186e53d2b31676105b7648a5835242d27830 100644 (file)
@@ -40,6 +40,8 @@ typedef void * jack_mixer_channel_t;
 typedef void * jack_mixer_output_channel_t;
 typedef void * jack_mixer_threshold_t;
 
+enum midi_behavior_mode { Jump_To_Value, Pick_Up };
+
 jack_mixer_t
 create(
   const char * jack_client_name_ptr,
@@ -66,6 +68,16 @@ set_last_midi_channel(
   jack_mixer_t mixer,
   int new_channel);
 
+
+int
+get_midi_behavior_mode(
+  jack_mixer_t mixer);
+
+unsigned int
+set_midi_behavior_mode(
+  jack_mixer_t mixer,
+  enum midi_behavior_mode mode);
+
 jack_mixer_channel_t
 add_channel(
   jack_mixer_t mixer,
index 038fdac87011a2811f24589fb3669b0c1ff5d30b..4b60f6bec4f8357b36e2a998a85c7d3e507983b4 100755 (executable)
@@ -89,6 +89,9 @@ class JackMixer(SerializedObject):
         self.mixer = jack_mixer_c.Mixer(client_name)
         if not self.mixer:
             sys.exit(1)
+
+        #self.mixer.midi_behavior_mode = self.gui_factory.midi_behavior_modes[self.gui_factory.get_midi_behavior_mode()]
+
         self.window.set_title(client_name)
 
         self.monitor_channel = self.mixer.add_output_channel("Monitor", True, True)
@@ -105,7 +108,7 @@ class JackMixer(SerializedObject):
         self.window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
         self.window.set_icon_name('jack_mixer')
         self.gui_factory = gui.Factory(self.window, self.meter_scales, self.slider_scales)
-
+        self.gui_factory.connect('midi-behavior-mode-changed', self.on_midi_behavior_mode_changed)
         self.vbox_top = Gtk.VBox()
         self.window.add(self.vbox_top)
 
@@ -251,6 +254,10 @@ class JackMixer(SerializedObject):
     def nsm_exit_cb(self, path, session_name, client_name):
         Gtk.main_quit()
 
+    def on_midi_behavior_mode_changed(self, gui_factory, value):
+        print('on_midi_behavior_mode_changed', value)
+        self.mixer.midi_behavior_mode = value
+
     def on_delete_event(self, widget, event):
         return False
 
@@ -700,9 +707,14 @@ Franklin Street, Fifth Floor, Boston, MA 02110-130159 USA''')
             self.unserialized_channels.append(channel)
             return channel
 
+        if name == gui.Factory.serialization_name():
+            self.gui_factory = gui.Factory(self.window, self.meter_scales, self.slider_scales)
+            self.gui_factory.connect('midi-behavior-mode-changed', self.on_midi_behavior_mode_changed)
+            return self.gui_factory
+
     def serialization_get_childs(self):
-        '''Get child objects tha required and support serialization'''
-        childs = self.channels[:] + self.output_channels[:]
+        '''Get child objects that required and support serialization'''
+        childs = self.channels[:] + self.output_channels[:] + [self.gui_factory]
         return childs
 
     def serialization_name(self):
index be419db01417c8096c77db7b976422c6330e77e8..0ff0ef7cbb8ad3204bbc49580a9b332f6ed63275 100644 (file)
@@ -925,11 +925,35 @@ Mixer_set_last_midi_channel(MixerObject *self, PyObject *value, void *closure)
        return -1;
 }
 
+static PyObject*
+Mixer_get_midi_behavior_mode(MixerObject *self, void *closure)
+{
+       return PyLong_FromLong(get_midi_behavior_mode(self->mixer));
+}
+
+static int
+Mixer_set_midi_behavior_mode(MixerObject *self, PyObject *value, void *closure)
+{
+       int mode;
+       unsigned int result;
+       
+       mode = PyLong_AsLong(value);
+       result = set_midi_behavior_mode(self->mixer, mode);
+       if (result == 0) {
+               return 0;
+       }
+       return -1;
+}
+
+
+
 static PyGetSetDef Mixer_getseters[] = {
        {"channels_count", (getter)Mixer_get_channels_count, NULL,
                "channels count", NULL},
        {"last_midi_channel", (getter)Mixer_get_last_midi_channel, (setter)Mixer_set_last_midi_channel,
                "last midi channel", NULL},
+       {"midi_behavior_mode", (getter)Mixer_get_midi_behavior_mode, (setter)Mixer_set_midi_behavior_mode,
+               "midi behavior mode", NULL},
        {NULL}
 };
 
index b07051f823699d8cf3597ff336de214d14b16b8c..1805543a42e03ae08aa3b3d18537692c7d8f6e79 100644 (file)
@@ -89,6 +89,15 @@ class PreferencesDialog(Gtk.Dialog):
 
         vbox.pack_start(self.create_frame('Scales', table), True, True, 0)
 
+        table = Gtk.Table(1, 2, False)
+        table.set_row_spacings(5)
+        table.set_col_spacings(5)
+
+        table.attach(Gtk.Label(label='Midi behavior'), 0, 1, 0, 1)
+        self.midi_behavior_combo = self.create_midi_behavior_combo()
+        table.attach(self.midi_behavior_combo, 1, 2, 0, 1)
+
+        vbox.pack_start(self.create_frame('Midi Behavior', table), True, True, 0)
         self.vbox.show_all()
 
         self.add_button(Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE)
@@ -131,6 +140,14 @@ class PreferencesDialog(Gtk.Dialog):
 
         return slider_scale_combo
 
+    def create_midi_behavior_combo(self):
+        combo = Gtk.ComboBoxText()
+        combo.append('Jump To Value', 'Jump To Value')
+        combo.append('Pick Up', 'Pick Up')
+        combo.set_active_id(self.app.gui_factory.get_midi_behavior_mode())
+        combo.connect('changed', self.on_midi_behavior_combo_changed)
+        return combo
+
     def on_response_cb(self, dlg, response_id, *args):
         self.app.preferences_dialog = None
         self.destroy()
@@ -145,6 +162,10 @@ class PreferencesDialog(Gtk.Dialog):
         scale = self.slider_store.get(active_iter, 1)[0]
         self.app.gui_factory.set_default_slider_scale(scale)
 
+    def on_midi_behavior_combo_changed(self, *args):
+        active_id = self.midi_behavior_combo.get_active_id()
+        self.app.gui_factory.set_midi_behavior_mode(active_id)
+
     def on_vumeter_color_change(self, *args):
         color_scheme = 'default'
         if self.vumeter_color_checkbutton.get_active():