# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+import logging
+
import gi
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
-import slider
-import meter
+
import abspeak
+import meter
+import slider
from serialization import SerializedObject
try:
except:
phat = None
-button_padding = 1
+log = logging.getLogger(__name__)
+button_padding = 1
css = b"""
.top_label {min-width: 100px;}
button {padding: 0px}
"""
-
css_provider = Gtk.CssProvider()
css_provider.load_from_data(css)
context = Gtk.StyleContext()
screen = Gdk.Screen.get_default()
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
def set_background_color(widget, name, color_string):
css = """
.%s {
widget_context = widget.get_style_context()
widget_context.add_class(name)
+
def random_color():
from random import uniform, seed
seed()
return Gdk.RGBA(uniform(0, 1), uniform(0, 1), uniform(0, 1), 1)
+
class Channel(Gtk.VBox, SerializedObject):
'''Widget with slider and meter used as base class for more specific
channel widgets'''
channel_name = property(get_channel_name, set_channel_name)
def realize(self):
- #print "Realizing channel \"%s\"" % self.channel_name
+ log.debug('Realizing channel "%s".', self.channel_name)
if self.future_out_mute != None:
self.channel.out_mute = self.future_out_mute
self.connect("scroll-event", self.on_scroll)
def unrealize(self):
- #print "Unrealizing channel \"%s\"" % self.channel_name
+ log.debug('Unrealizing channel "%s".', self.channel_name)
pass
def balance_preferred_width(self):
self.slider.show()
def on_default_meter_scale_changed(self, gui_factory, scale):
- #print "Default meter scale change detected."
+ log.debug("Default meter scale change detected.")
self.meter.set_scale(scale)
def on_default_slider_scale_changed(self, gui_factory, scale):
- #print "Default slider scale change detected."
+ log.debug("Default slider scale change detected.")
self.slider_scale = scale
self.slider_adjustment.set_scale(scale)
if self.channel:
self.create_slider_widget()
def on_abspeak_adjust(self, abspeak, adjust):
- #print "abspeak adjust %f" % adjust
+ log.debug("abspeak adjust %f", adjust)
self.slider_adjustment.set_value_db(self.slider_adjustment.get_value_db() + adjust)
self.channel.abspeak = None
#self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
def on_abspeak_reset(self, abspeak):
- #print "abspeak reset"
+ log.debug("abspeak reset")
self.channel.abspeak = None
def on_volume_digits_key_pressed(self, widget, event):
db_text = self.volume_digits.get_text()
try:
db = float(db_text)
- #print "Volume digits confirmation \"%f dBFS\"" % db
+ log.debug('Volume digits confirmation "%f dBFS".', db)
except (ValueError) as e:
- #print "Volume digits confirmation ignore, reset to current"
+ log.debug("Volume digits confirmation ignore, reset to current.")
self.update_volume(False)
return
self.slider_adjustment.set_value_db(db)
#self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
def on_volume_digits_focus_out(self, widget, event):
- #print "volume digits focus out detected"
+ log.debug("Volume digits focus out detected.")
self.update_volume(False)
def read_meter(self):
def on_balance_changed(self, adjustment):
balance = self.balance_adjustment.get_value()
- #print("%s balance: %f" % (self.channel_name, balance))
+ log.debug("%s balance: %f", self.channel_name, balance)
self.channel.balance = balance
self.app.update_monitor(self)
def on_volume_changed_from_midi(self, adjustment):
balance = self.balance_adjustment.get_value()
- #print("%s balance from midi: %f" % (self.channel_name, balance))
+ log.debug("%s balance from midi: %f", self.channel_name, balance)
self.channel.set_balance_from_midi(balance)
self.app.update_monitor(self)
def on_key_pressed(self, widget, event):
if (event.keyval == Gdk.KEY_Up):
- #print self.channel_name + " Up"
+ log.debug(self.channel_name + " Up")
self.slider_adjustment.step_up()
return True
elif (event.keyval == Gdk.KEY_Down):
- #print self.channel_name + " Down"
+ log.debug(self.channel_name + " Down")
self.slider_adjustment.step_down()
return True
self.entry_solo_cc.set_value(-1)
def get_result(self):
- print('minus_inf active?', self.zero_dB.get_active())
+ log.debug('minus_inf active?: %s', self.zero_dB.get_active())
return {'name': self.entry_name.get_text(),
'stereo': self.stereo.get_active(),
'volume_cc': int(self.entry_volume_cc.get_value()),
'value': self.minus_inf.get_active()
}
+
class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
def create_ui(self):
NewChannelDialog.create_ui(self)
inputchannel.update_control_group(self.channel)
-
class NewOutputChannelDialog(NewChannelDialog, OutputChannelPropertiesDialog):
def __init__(self, app):
Gtk.Dialog.__init__(self, 'New Output Channel', app.window)
'value': self.minus_inf.get_active()
}
+
class ControlGroup(Gtk.Alignment):
def __init__(self, output_channel, input_channel):
GObject.GObject.__init__(self)
self.label.set_text(self.output_channel.channel.name)
set_background_color(self.vbox, self.output_channel.css_name, self.output_channel.color.to_string())
-
def on_mute_toggled(self, button):
self.output_channel.channel.set_muted(self.input_channel.channel, button.get_active())
self.app.update_monitor(self)
def on_solo_toggled(self, button):
self.output_channel.channel.set_solo(self.input_channel.channel, button.get_active())
self.app.update_monitor(self)
-
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+import configparser
+import logging
+import os
+
import gi
from gi.repository import GObject
-import os
-import configparser
from serialization import SerializedObject
try:
except:
xdg = None
+
+log = logging.getLogger(__name__)
+
+
def lookup_scale(scales, scale_id):
for scale in scales:
if scale_id == scale.scale_id:
return scale
return None
+
class Factory(GObject.GObject, SerializedObject):
def __init__(self, topwindow, meter_scales, slider_scales):
else:
self.write_preferences()
else:
- print("Cannot load PyXDG. Your preferences will not be preserved across jack_mixer invocations")
+ log.warning("Cannot load PyXDG. Your preferences will not be preserved across "
+ "jack_mixer invocations")
def set_default_preferences(self):
self.default_meter_scale = self.meter_scales[0]
scale_id = self.config['Preferences']['default_meter_scale']
self.default_meter_scale = lookup_scale(self.meter_scales, scale_id)
if not self.default_meter_scale:
- self.default_meter_scale = meter_scales[0]
+ self.default_meter_scale = self.meter_scales[0]
scale_id = self.config['Preferences']['default_slider_scale']
self.default_slider_scale = lookup_scale(self.slider_scales, scale_id)
self.write_preferences()
self.emit("default-meter-scale-changed", self.default_meter_scale)
else:
- print("Ignoring default_meter_scale setting, because \"%s\" scale is not known" % scale_id)
+ log.warning('Ignoring default_meter_scale setting, because "%s" scale is not known.',
+ scale)
def set_default_slider_scale(self, scale):
if scale:
self.write_preferences()
self.emit("default-slider-scale-changed", self.default_slider_scale)
else:
- print("Ignoring default_slider_scale setting, because \"%s\" scale is not known" % scale_id)
+ log.warning('Ignoring default_slider_scale setting, because "%s" scale is not known.',
+ scale)
def set_vumeter_color(self, color):
self.vumeter_color = color
return True
return False
+
GObject.signal_new("default-meter-scale-changed", Factory,
GObject.SignalFlags.RUN_FIRST | GObject.SignalFlags.ACTION,
None, [GObject.TYPE_PYOBJECT])
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+import logging
import os
import signal
import sys
import jack_mixer_c
+import gui
import scale
from channel import *
-
-import gui
-from preferences import PreferencesDialog
-
+from nsmclient import NSMClient
from serialization_xml import XmlSerialization
from serialization import SerializedObject, Serializator
-
-from nsmclient import NSMClient
+from preferences import PreferencesDialog
# restore Python modules lookup path
sys.path = old_path
+log = logging.getLogger("jack_mixer")
+
class JackMixer(SerializedObject):
return False
def sighandler(self, signum, frame):
- #print "Signal %d received" % signum
+ log.debug("Signal %d received.", signum)
if signum == signal.SIGUSR1:
self.save = True
elif signum == signal.SIGTERM:
elif signum == signal.SIGINT:
Gtk.main_quit()
else:
- print("Unknown signal %d received" % signum)
+ log.warning("Unknown signal %d received.", signum)
def cleanup(self):
- print("Cleaning jack_mixer")
+ log.debug("Cleaning jack_mixer.")
if not self.mixer:
return
self.window.show_all()
def on_edit_input_channel(self, widget, channel):
- print('Editing channel "%s"' % channel.channel_name)
+ log.debug('Editing input channel "%s".', channel.channel_name)
channel.on_channel_properties()
def remove_channel_edit_input_menuitem_by_label(self, widget, label):
self.channel_edit_input_menu.remove(widget)
def on_remove_input_channel(self, widget, channel):
- print('Removing channel "%s"' % channel.channel_name)
+ log.debug('Removing input channel "%s".', channel.channel_name)
self.channel_remove_input_menu.remove(widget)
self.channel_edit_input_menu.foreach(
self.remove_channel_edit_input_menuitem_by_label,
self.channel_remove_input_menu_item.set_sensitive(False)
def on_edit_output_channel(self, widget, channel):
- print('Editing channel "%s"' % channel.channel_name)
+ log.debug('Editing output channel "%s".', channel.channel_name)
channel.on_channel_properties()
def remove_channel_edit_output_menuitem_by_label(self, widget, label):
self.channel_edit_output_menu.remove(widget)
def on_remove_output_channel(self, widget, channel):
- print('Removing channel "%s"' % channel.channel_name)
+ log.debug('Removing output channel "%s".', channel.channel_name)
self.channel_remove_output_menu.remove(widget)
self.channel_edit_output_menu.foreach(
self.remove_channel_edit_output_menuitem_by_label,
rename_parameters)
self.channel_remove_output_menu.foreach(self.rename_channels,
rename_parameters)
- #print("Renaming channel from %s to %s\n" % (oldname, newname))
-
+ log.debug('Renaming channel from "%s" to "%s".', oldname, newname)
def on_channels_clear(self, widget):
dlg = Gtk.MessageDialog(parent = self.window,
about.destroy()
def save_to_xml(self, file):
- #print "Saving to XML..."
+ log.debug("Saving to XML...")
b = XmlSerialization()
s = Serializator()
s.serialize(self, b)
b.save(file)
def load_from_xml(self, file, silence_errors=False):
- #print "Loading from XML..."
+ log.debug("Loading from XML...")
self.unserialized_channels = []
b = XmlSerialization()
try:
def main():
parser = ArgumentParser()
parser.add_argument('-c', '--config', help='use a non default configuration file')
+ parser.add_argument('-d', '--debug', action="store_true", help='Enable debug logging messages')
parser.add_argument('client_name', metavar='NAME', nargs='?', default='jack_mixer',
help='set JACK client name')
args = parser.parse_args()
+ logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO,
+ format="%(levelname)s: %(message)s")
+
try:
mixer = JackMixer(args.client_name)
except Exception as e:
+ log.exception("Mixer creation failed.")
err = Gtk.MessageDialog(parent = None,
modal = True,
message_type = Gtk.MessageType.ERROR,
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+import logging
+
+import cairo
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
-import cairo
+
+
+log = logging.getLogger(__name__)
+
class MeterWidget(Gtk.DrawingArea):
def __init__(self, scale):
self.cache_surface = None
def get_preferred_width(self):
- print('get_preferred_width called')
+ log.debug('get_preferred_width called')
return 2
def get_preferred_height(self):
self.cache_surface = None
def on_size_request(self, widget, requisition):
- #print "size-request, %u x %u" % (requisition.width, requisition.height)
+ log.debug("size-request, %u x %u", requisition.width, requisition.height)
requisition.width = 20
return
self.cache_surface = None
self.invalidate_all()
+
class MonoMeterWidget(MeterWidget):
def __init__(self, scale):
MeterWidget.__init__(self, scale)
if (abs(old_value-self.value) * self.height) > 1:
self.invalidate_all()
+
class StereoMeterWidget(MeterWidget):
def __init__(self, scale):
MeterWidget.__init__(self, scale)
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+import logging
import math
+
import jack_mixer_c
+
+log = logging.getLogger(__name__)
+
+
class Mark:
'''Encapsulates scale linear function edge and coefficients for scale = a * dB + b formula'''
def __init__(self, db, scale):
self.scale = scale
self.text = "%.0f" % math.fabs(db)
+
class Base:
'''Scale abstraction, various scale implementation derive from this class'''
def __init__(self, scale_id, description):
def db_to_scale(self, db):
'''Convert dBFS value to number in range 0.0-1.0 used in GUI'''
- #print "db_to_scale(%f)" % db
+ log.debug("db_to_scale(%f)", db)
return self.scale.db_to_scale(db)
def scale_to_db(self, scale):
if i.scale == -1.0:
i.scale = self.db_to_scale(i.db)
+
# IEC 60268-18 Peak programme level meters - Digital audio peak level meter
# Adapted from meterpridge, may be wrong, I'm not buying standards, event if they cost $45
# If someone has the standart, please eighter share it with me or fix the code.
self.calculate_coefficients()
self.scale_marks()
+
class IEC268Minimalistic(Base):
'''IEC 60268-18 Peak programme level meters - Digital audio peak level meter,
fewer marks'''
self.calculate_coefficients()
self.scale_marks()
+
class Linear70dB(Base):
'''Linear scale with range from -70 to 0 dBFS'''
def __init__(self):
self.calculate_coefficients()
self.scale_marks()
+
class Linear30dB(Base):
'''Linear scale with range from -30 to +30 dBFS'''
def __init__(self):
self.calculate_coefficients()
self.scale_marks()
+
def scale_test1(scale):
for i in range(-97 * 2, 1, 1):
db = float(i)/2.0
print("%5.1f dB maps to %f" % (db, scale.db_to_scale(db)))
+
def scale_test2(scale):
for i in range(101):
s = float(i)/100.0
print("%.2f maps to %.1f dB" % (s, scale.scale_to_db(s)))
+
def print_db_to_scale(db):
print("%-.1f dB maps to %f" % (db, scale.db_to_scale(db)))
+
def scale_test3(scale):
print_db_to_scale(+77.0)
print_db_to_scale(+7.0)
print_db_to_scale(0.0)
print_db_to_scale(-107.0)
+
#scale = linear_30dB()
#scale_test2(scale)
#scale_test3(scale)
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+import logging
+
+
+log = logging.getLogger(__name__)
+
+
class SerializationBackend:
'''Base class for serialization backends'''
def get_root_serialization_object(self, name):
# this method should never be called for the base class
raise NotImplementedError
+
class SerializationObjectBackend:
'''Base class for serialization backend objects where real object
properties will be serialized to or unserialized from.'''
def serialization_name(self):
return None
+
class SerializedObject:
'''Base class for object supporting serialization'''
def serialization_name(self):
def unserialize_child(self, name):
return None
+
class Serializator:
def __init__(self):
pass
return self.unserialize_one(backend, root, backend_object)
def unserialize_one(self, backend, object, backend_object):
- #print "Unserializing " + repr(object)
+ log.debug("Unserializing %r.", object)
properties = backend_object.get_properties()
for name, value in properties.items():
- #print "%s = %s" % (name, value)
+ log.debug("%s = %s", name, value)
if not object.unserialize_property(name, value):
return False
object.serialize(backend_object)
childs = object.serialization_get_childs()
for child in childs:
- #print "serializing child " + repr(child)
+ log.debug("Serializing child %r.", child)
self.serialize_one(backend, child,
backend.get_child_serialization_object(
child.serialization_name(), backend_object))
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+import logging
+
+import cairo
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GObject
-import cairo
+
+
+log = logging.getLogger(__name__)
+
class AdjustmentdBFS(Gtk.Adjustment):
def __init__(self, scale, default_db, step_inc):
def on_mouse(self, widget, event):
if event.type == Gdk.EventType.BUTTON_PRESS:
- #print "mouse button %u pressed %u:%u" % (event.button, event.x, event.y)
+ log.debug("Mouse button %u pressed %ux%u", event.button, event.x, event.y)
if event.button == 1:
if event.y >= self.slider_rail_up and event.y < self.slider_rail_up + self.slider_rail_height:
self.adjustment.set_value(1 - float(event.y - self.slider_rail_up)/float(self.slider_rail_height))
elif event.type == Gdk.EventType.MOTION_NOTIFY:
- #print "mouse motion %u:%u" % (event.x, event.y)
+ log.debug("Mouse motion %ux%u", event.x, event.y)
if event.y < self.slider_rail_up:
y = self.slider_rail_up
elif event.y > self.slider_rail_up + self.slider_rail_height: