diff --git a/jack_mixer/app.py b/jack_mixer/app.py index 15e5eea..a5f8129 100644 --- a/jack_mixer/app.py +++ b/jack_mixer/app.py @@ -361,6 +361,10 @@ def create_ui(self, with_nsm): self.window.connect("destroy", Gtk.main_quit) self.window.connect("delete-event", self.on_delete_event) + def show_all(self): + self.window.show_all() + for channel in self.output_channels + self.channels: + channel.on_metering_toggled(channel.metering_button) # --------------------------------------------------------------------------------------------- # Channel creation @@ -486,7 +490,7 @@ def nsm_hide_cb(self, *args): def nsm_show_cb(self): width, height = self.window.get_size() - self.window.show_all() + self.show_all() self.paned.set_position(self.paned_position / self.width * width) self.visible = True @@ -782,7 +786,7 @@ def on_add_channel(self, inout="input", default_name="Input"): setattr(self, "_add_{}_values".format(inout), result) (self.add_channel if inout == "input" else self.add_output_channel)(**result) if self.visible or self.nsm_client is None: - self.window.show_all() + self.show_all() def on_add_input_channel(self, widget): return self.on_add_channel("input", _("Input")) @@ -1054,7 +1058,7 @@ def load_from_xml(self, file, silence_errors=False, from_nsm=False): del self.unserialized_channels width, height = self.window.get_size() if self.visible or not from_nsm: - self.window.show_all() + self.show_all() if self.output_channels: self.output_channels[-1].volume_digits.select_region(0, 0) @@ -1128,7 +1132,7 @@ def main(self): if self.visible or self.nsm_client is None: width, height = self.window.get_size() - self.window.show_all() + self.show_all() if hasattr(self, "paned_position"): self.paned.set_position(self.paned_position / self.width * width) diff --git a/jack_mixer/channel.py b/jack_mixer/channel.py index 7943f20..5181d51 100644 --- a/jack_mixer/channel.py +++ b/jack_mixer/channel.py @@ -63,6 +63,7 @@ def __init__(self, app, name, stereo=True, direct_output=True, initial_vol=None) self.css_name = "css_name_%d" % Channel.num_instances self.label_name = None self.wide = True + self.future_metering = None self.meter_prefader = False self.prefader_button = None self.label_chars_wide = 12 @@ -137,6 +138,11 @@ def create_fader(self): pre.connect("toggled", self.on_prefader_metering_toggled) pre.set_active(self.meter_prefader) + self.metering_button = mb = Gtk.ToggleButton(_("METER")) + mb.get_style_context().add_class("metering") + mb.set_tooltip_text(_("Show (on) / Hide (off) metering")) + mb.connect("toggled", self.on_metering_toggled) + self.hbox_readouts = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.hbox_readouts.set_homogeneous(True) self.hbox_readouts.pack_start(self.volume_digits, False, True, 0) @@ -154,9 +160,11 @@ def create_fader(self): self.event_box_fader.connect("scroll-event", self.on_scroll) self.event_box_fader.add(self.hbox_fader) self.vbox_fader.pack_start(self.event_box_fader, True, True, 0) + self.vbox_fader.pack_end(self.metering_button, False, False, 0) self.vbox_fader.pack_end(self.balance, False, True, 0) self.pack_start(self.vbox_fader, True, True, 0) + self.metering_button.set_active(self.channel.metering) def create_slider_widget(self): parent = None @@ -180,6 +188,8 @@ def realize(self): log.debug('Realizing channel "%s".', self.channel_name) if self.future_out_mute is not None: self.channel.out_mute = self.future_out_mute + if self.future_metering is not None: + self.channel.metering = self.future_metering # Widgets # Channel strip label @@ -320,6 +330,17 @@ def on_custom_widgets_changed(self, gui_factory, value): self.create_slider_widget() # balance slider has no custom variant, no need to re-create it. + def on_metering_toggled(self, button): + log.debug(f'on_metering_toggled: {button.get_active()}') + if not button.get_active(): + self.channel.metering = False + self.vbox_meter.hide() + self.abspeak.hide() + else: + self.channel.metering = True + self.abspeak.show() + self.vbox_meter.show() + def on_prefader_metering_toggled(self, button): self.use_prefader_metering(button.get_active()) @@ -509,7 +530,7 @@ def use_prefader_metering(self, flag=True): self.channel.kmeter_reset() def read_meter(self): - if not self.channel: + if not self.channel or self.channel.metering is False: return if self.stereo: @@ -574,7 +595,8 @@ def serialize(self, object_backend): object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value()) object_backend.add_property("wide", "%s" % str(self.wide)) object_backend.add_property("meter_prefader", "%s" % str(self.meter_prefader)) - + if hasattr(self.channel, "metering"): + object_backend.add_property("metering", "%s" % str(self.channel.metering)) if hasattr(self.channel, "out_mute"): object_backend.add_property("out_mute", str(self.channel.out_mute)) if self.channel.volume_midi_cc != -1: @@ -599,8 +621,10 @@ def unserialize_property(self, name, value): elif name == "meter_prefader": self.meter_prefader = (value == "True") return True + elif name == "metering": + self.future_metering = (value == "True") + return True elif name == "volume_midi_cc": - self.future_volume_midi_cc = int(value) return True elif name == "balance_midi_cc": diff --git a/jack_mixer/styling.py b/jack_mixer/styling.py index 40cbc9c..21c40e5 100644 --- a/jack_mixer/styling.py +++ b/jack_mixer/styling.py @@ -36,7 +36,8 @@ @define-color solo_bgcolor_checked #26A269; @define-color prefader_bgcolor_hover #A6C2E4; @define-color prefader_bgcolor_checked #3584E4; - +@define-color metering_bgcolor_hover #906090; +@define-color metering_bgcolor_checked #663366; /* Channel strips */ @@ -85,7 +86,9 @@ button.mute:checked, button.solo:checked, button.prefader:checked, -button.prefader_meter:checked { +button.prefader_meter:checked, +button.metering:hover, +button.metering:checked { color: white; text-shadow: unset; background-image: none; @@ -120,7 +123,12 @@ button.prefader_meter:checked { background-color: @prefader_bgcolor_checked; } - +button.metering:hover { + background-color: @metering_bgcolor_hover; +} +button.metering:checked { + background-color: @metering_bgcolor_checked; +} /* Control groups */ diff --git a/src/_jack_mixer.pxd b/src/_jack_mixer.pxd index 4065707..b92a230 100644 --- a/src/_jack_mixer.pxd +++ b/src/_jack_mixer.pxd @@ -73,6 +73,9 @@ cdef extern from "jack_mixer.h": cdef const char * channel_get_name(jack_mixer_channel_t channel) cdef int channel_rename(jack_mixer_channel_t channel, const char * name) + cdef bool channel_get_metering(jack_mixer_channel_t channel) + cdef void channel_set_metering(jack_mixer_channel_t channel, bool flag) + cdef double channel_abspeak_read(jack_mixer_channel_t channel, meter_mode mode) cdef void channel_abspeak_reset(jack_mixer_channel_t channel, meter_mode mode) diff --git a/src/_jack_mixer.pyx b/src/_jack_mixer.pyx index 0a977dc..2da9a37 100644 --- a/src/_jack_mixer.pyx +++ b/src/_jack_mixer.pyx @@ -332,6 +332,15 @@ cdef class Channel: channel_mono_meter_read(self._channel, &left, MeterMode.POST_FADER) return (left,) + @property + def metering(self): + """Using meters.""" + return channel_get_metering(self._channel) + + @metering.setter + def metering(self, bool flag): + channel_set_metering(self._channel, flag) + @property def midi_change_callback(self): """Function to be called when a channel property is changed via MIDI. diff --git a/src/jack_mixer.c b/src/jack_mixer.c index b8a9cc5..91ae8ae 100644 --- a/src/jack_mixer.c +++ b/src/jack_mixer.c @@ -60,6 +60,7 @@ struct channel { char * name; bool stereo; bool out_mute; + bool metering; float volume_transition_seconds; unsigned int num_volume_transition_steps; float volume; @@ -666,6 +667,21 @@ remove_channel( free(channel_ptr); } +bool channel_get_metering( + jack_mixer_channel_t channel) +{ + assert(channel_ptr); + return channel_ptr->metering; +} + +void channel_set_metering( + jack_mixer_channel_t channel, + bool metering) +{ + assert(channel_ptr); + channel_ptr->metering = metering; +} + void channel_stereo_meter_read( jack_mixer_channel_t channel, @@ -1108,78 +1124,80 @@ mix_one( mix_channel->tmp_mixed_frames_right[i] *= vol_r; } - - /* Get peak signal, left/right and combined */ - frame_left = fabsf(mix_channel->tmp_mixed_frames_left[i]); - frame_left_pre = fabsf(mix_channel->prefader_frames_left[i]); - - if (mix_channel->peak_left_prefader < frame_left_pre) - { - mix_channel->peak_left_prefader = frame_left_pre; - } - - if (mix_channel->peak_left_postfader < frame_left) - { - mix_channel->peak_left_postfader = frame_left; - } - - if (frame_left > mix_channel->abspeak_postfader) - { - mix_channel->abspeak_postfader = frame_left; - } - - if (frame_left_pre > mix_channel->abspeak_prefader) + if (mix_channel->metering) { - mix_channel->abspeak_prefader = frame_left_pre; - } - - /* This seems to duplicate what was already done right above? */ - if (mix_channel->stereo) - { - frame_right = fabsf(mix_channel->tmp_mixed_frames_right[i]); - frame_right_pre = fabsf(mix_channel->prefader_frames_right[i]); + /* Get peak signal, left/right and combined */ + frame_left = fabsf(mix_channel->tmp_mixed_frames_left[i]); + frame_left_pre = fabsf(mix_channel->prefader_frames_left[i]); - if (mix_channel->peak_right_prefader < frame_right_pre) + if (mix_channel->peak_left_prefader < frame_left_pre) { - mix_channel->peak_right_prefader = frame_right_pre; + mix_channel->peak_left_prefader = frame_left_pre; } - if (mix_channel->peak_right_postfader < frame_right) + if (mix_channel->peak_left_postfader < frame_left) { - mix_channel->peak_right_postfader = frame_right; + mix_channel->peak_left_postfader = frame_left; } - if (frame_right > mix_channel->abspeak_postfader) + if (frame_left > mix_channel->abspeak_postfader) { - mix_channel->abspeak_postfader = frame_right; + mix_channel->abspeak_postfader = frame_left; } - if (frame_right_pre > mix_channel->abspeak_prefader) + if (frame_left_pre > mix_channel->abspeak_prefader) { - mix_channel->abspeak_prefader = frame_right_pre; + mix_channel->abspeak_prefader = frame_left_pre; } - } - - /* update left/right peak values every so often */ - mix_channel->peak_frames++; - if (mix_channel->peak_frames >= PEAK_FRAMES_CHUNK) - { - mix_channel->meter_left_prefader = mix_channel->peak_left_prefader; - mix_channel->peak_left_prefader = 0.0; - - mix_channel->meter_left_postfader = mix_channel->peak_left_postfader; - mix_channel->peak_left_postfader = 0.0; + /* This seems to duplicate what was already done right above? */ if (mix_channel->stereo) { - mix_channel->meter_right_prefader = mix_channel->peak_right_prefader; - mix_channel->peak_right_prefader = 0.0; + frame_right = fabsf(mix_channel->tmp_mixed_frames_right[i]); + frame_right_pre = fabsf(mix_channel->prefader_frames_right[i]); + + if (mix_channel->peak_right_prefader < frame_right_pre) + { + mix_channel->peak_right_prefader = frame_right_pre; + } + + if (mix_channel->peak_right_postfader < frame_right) + { + mix_channel->peak_right_postfader = frame_right; + } - mix_channel->meter_right_postfader = mix_channel->peak_right_postfader; - mix_channel->peak_right_postfader = 0.0; + if (frame_right > mix_channel->abspeak_postfader) + { + mix_channel->abspeak_postfader = frame_right; + } + + if (frame_right_pre > mix_channel->abspeak_prefader) + { + mix_channel->abspeak_prefader = frame_right_pre; + } } - mix_channel->peak_frames = 0; + /* update left/right peak values every so often */ + mix_channel->peak_frames++; + if (mix_channel->peak_frames >= PEAK_FRAMES_CHUNK) + { + mix_channel->meter_left_prefader = mix_channel->peak_left_prefader; + mix_channel->peak_left_prefader = 0.0; + + mix_channel->meter_left_postfader = mix_channel->peak_left_postfader; + mix_channel->peak_left_postfader = 0.0; + + if (mix_channel->stereo) + { + mix_channel->meter_right_prefader = mix_channel->peak_right_prefader; + mix_channel->peak_right_prefader = 0.0; + + mix_channel->meter_right_postfader = mix_channel->peak_right_postfader; + mix_channel->peak_right_postfader = 0.0; + } + + mix_channel->peak_frames = 0; + } } /* Finish off volume interpolation */ @@ -1205,7 +1223,7 @@ mix_one( /* Calculate k-metering for output channel*/ - if (mix_channel->mixer_ptr->kmetering) { + if (mix_channel->metering && mix_channel->mixer_ptr->kmetering) { kmeter_process(&mix_channel->kmeter_left, mix_channel->tmp_mixed_frames_left, start, end); kmeter_process(&mix_channel->kmeter_right, mix_channel->tmp_mixed_frames_right, start, end); kmeter_process(&mix_channel->kmeter_prefader_left, mix_channel->prefader_frames_left, start, end); @@ -1310,96 +1328,99 @@ calc_channel_frames( /* Calculate left+right peak-level and, if need be, * update abspeak level */ - if (channel_ptr->stereo) + if (channel_ptr->metering) { - frame_left = fabsf(frame_left); - frame_right = fabsf(frame_right); - frame_left_pre = fabsf(frame_left_pre); - frame_right_pre = fabsf(frame_right_pre); - - if (channel_ptr->peak_left_prefader < frame_left_pre) + if (channel_ptr->stereo) { - channel_ptr->peak_left_prefader = frame_left_pre; - } + frame_left = fabsf(frame_left); + frame_right = fabsf(frame_right); + frame_left_pre = fabsf(frame_left_pre); + frame_right_pre = fabsf(frame_right_pre); - if (channel_ptr->peak_left_postfader < frame_left) - { - channel_ptr->peak_left_postfader = frame_left; - } + if (channel_ptr->peak_left_prefader < frame_left_pre) + { + channel_ptr->peak_left_prefader = frame_left_pre; + } - if (frame_left > channel_ptr->abspeak_postfader) - { - channel_ptr->abspeak_postfader = frame_left; - } + if (channel_ptr->peak_left_postfader < frame_left) + { + channel_ptr->peak_left_postfader = frame_left; + } - if (frame_left_pre > channel_ptr->abspeak_prefader) - { - channel_ptr->abspeak_prefader = frame_left_pre; - } + if (frame_left > channel_ptr->abspeak_postfader) + { + channel_ptr->abspeak_postfader = frame_left; + } - if (channel_ptr->peak_right_prefader < frame_right_pre) - { - channel_ptr->peak_right_prefader = frame_right_pre; - } + if (frame_left_pre > channel_ptr->abspeak_prefader) + { + channel_ptr->abspeak_prefader = frame_left_pre; + } - if (channel_ptr->peak_right_postfader < frame_right) - { - channel_ptr->peak_right_postfader = frame_right; - } + if (channel_ptr->peak_right_prefader < frame_right_pre) + { + channel_ptr->peak_right_prefader = frame_right_pre; + } - if (frame_right > channel_ptr->abspeak_postfader) - { - channel_ptr->abspeak_postfader = frame_right; - } + if (channel_ptr->peak_right_postfader < frame_right) + { + channel_ptr->peak_right_postfader = frame_right; + } - if (frame_right_pre > channel_ptr->abspeak_prefader) - { - channel_ptr->abspeak_prefader = frame_right_pre; - } - } - else - { - frame_left = (fabsf(frame_left) + fabsf(frame_right)) / 2; - frame_left_pre = fabsf(frame_left_pre); + if (frame_right > channel_ptr->abspeak_postfader) + { + channel_ptr->abspeak_postfader = frame_right; + } - if (channel_ptr->peak_left_prefader < frame_left_pre) - { - channel_ptr->peak_left_prefader = frame_left_pre; + if (frame_right_pre > channel_ptr->abspeak_prefader) + { + channel_ptr->abspeak_prefader = frame_right_pre; + } } - - if (channel_ptr->peak_left_postfader < frame_left) + else { - channel_ptr->peak_left_postfader = frame_left; - } + frame_left = (fabsf(frame_left) + fabsf(frame_right)) / 2; + frame_left_pre = fabsf(frame_left_pre); - if (frame_left > channel_ptr->abspeak_postfader) - { - channel_ptr->abspeak_postfader = frame_left; - } + if (channel_ptr->peak_left_prefader < frame_left_pre) + { + channel_ptr->peak_left_prefader = frame_left_pre; + } - if (frame_left_pre > channel_ptr->abspeak_prefader) - { - channel_ptr->abspeak_prefader = frame_left_pre; + if (channel_ptr->peak_left_postfader < frame_left) + { + channel_ptr->peak_left_postfader = frame_left; + } + + if (frame_left > channel_ptr->abspeak_postfader) + { + channel_ptr->abspeak_postfader = frame_left; + } + + if (frame_left_pre > channel_ptr->abspeak_prefader) + { + channel_ptr->abspeak_prefader = frame_left_pre; + } } - } - /* Update input channel volume meter every so often */ - channel_ptr->peak_frames++; - if (channel_ptr->peak_frames >= PEAK_FRAMES_CHUNK) - { - channel_ptr->meter_left_postfader = channel_ptr->peak_left_postfader; - channel_ptr->peak_left_postfader = 0.0; - channel_ptr->meter_left_prefader = channel_ptr->peak_left_prefader; - channel_ptr->peak_left_prefader = 0.0; - if (channel_ptr->stereo) + /* Update input channel volume meter every so often */ + channel_ptr->peak_frames++; + if (channel_ptr->peak_frames >= PEAK_FRAMES_CHUNK) { - channel_ptr->meter_right_postfader = channel_ptr->peak_right_postfader; - channel_ptr->peak_right_postfader = 0.0; - channel_ptr->meter_right_prefader = channel_ptr->peak_right_prefader; - channel_ptr->peak_right_prefader = 0.0; - } + channel_ptr->meter_left_postfader = channel_ptr->peak_left_postfader; + channel_ptr->peak_left_postfader = 0.0; + channel_ptr->meter_left_prefader = channel_ptr->peak_left_prefader; + channel_ptr->peak_left_prefader = 0.0; + if (channel_ptr->stereo) + { + channel_ptr->meter_right_postfader = channel_ptr->peak_right_postfader; + channel_ptr->peak_right_postfader = 0.0; + channel_ptr->meter_right_prefader = channel_ptr->peak_right_prefader; + channel_ptr->peak_right_prefader = 0.0; + } - channel_ptr->peak_frames = 0; + channel_ptr->peak_frames = 0; + } } /* Finish off volume & balance level interpolation */ @@ -1418,7 +1439,7 @@ calc_channel_frames( } /* Calculate k-metering for input channel */ - if (channel_ptr->mixer_ptr->kmetering) { + if (channel_ptr->metering && channel_ptr->mixer_ptr->kmetering) { kmeter_process(&channel_ptr->kmeter_left, channel_ptr->frames_left, start, end); if (channel_ptr->stereo) { kmeter_process(&channel_ptr->kmeter_right, channel_ptr->frames_right, start, end); diff --git a/src/jack_mixer.h b/src/jack_mixer.h index 8bf334d..8ad8b14 100644 --- a/src/jack_mixer.h +++ b/src/jack_mixer.h @@ -166,6 +166,15 @@ const char * channel_get_name( jack_mixer_channel_t channel); +bool +channel_get_metering( + jack_mixer_channel_t channel); + +void +channel_set_metering( + jack_mixer_channel_t channel, + bool metering); + /* returned values are in dBFS */ void channel_stereo_meter_read(