root/trunk/libffado/support/mixer-qt4/ffado/widgets/matrixmixer.py

Revision 1708, 9.0 kB (checked in by arnonym, 11 years ago)

Add a context menu with some predefined values and a spinbox for direct entry of custom values.

And display the values in dB. The calculation is currently fixed on the values of the DICE, for more flexibility the dbus-interface needs an extension. Then the range and the definition of 0 dB could be device-dependant.

  • Property svn:mergeinfo set to
Line 
1 #
2 # Copyright (C) 2009 by Arnold Krille
3 #
4 # This file is part of FFADO
5 # FFADO = Free Firewire (pro-)audio drivers for linux
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 2 of the License, or
10 # (at your option) version 3 of the License.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 from PyQt4 import QtGui, QtCore, Qt
22 import dbus, math, decimal
23
24 import logging
25 log = logging.getLogger("matrixmixer")
26
27 class ColorForNumber:
28     def __init__(self):
29         self.colors = dict()
30
31     def addColor(self, n, color):
32         self.colors[n] = color
33
34     def getColor(self, n):
35         #print "ColorForNumber.getColor( %g )" % (n)
36         keys = self.colors.keys()
37         keys.sort()
38         low = keys[-1]
39         high = keys[-1]
40         for i in range(len(keys)-1):
41             if keys[i] <= n and keys[i+1] > n:
42                 low = keys[i]
43                 high = keys[i+1]
44         #print "%g is between %g and %g" % (n, low, high)
45         f = 0
46         if high != low:
47             f = (n-low) / (high-low)
48         lc = self.colors[low]
49         hc = self.colors[high]
50         return QtGui.QColor(
51                 (1-f)*lc.red()   + f*hc.red(),
52                 (1-f)*lc.green() + f*hc.green(),
53                 (1-f)*lc.blue()  + f*hc.blue() )
54
55 class MixerNode(QtGui.QAbstractSlider):
56     def __init__(self, input, output, value, parent):
57         QtGui.QAbstractSlider.__init__(self, parent)
58         #log.debug("MixerNode.__init__( %i, %i, %i, %s )" % (input, output, value, str(parent)) )
59
60         self.pos = QtCore.QPointF(0, 0)
61         self.input = input
62         self.output = output
63         self.setOrientation(Qt.Qt.Vertical)
64         self.setRange(0, pow(2, 16)-1)
65         self.setValue(value)
66         self.connect(self, QtCore.SIGNAL("valueChanged(int)"), self.internalValueChanged)
67
68         self.bgcolors = ColorForNumber()
69         self.bgcolors.addColor(             0.0, QtGui.QColor(  0,   0,   0))
70         self.bgcolors.addColor(             1.0, QtGui.QColor(  0,   0, 128))
71         self.bgcolors.addColor(   math.pow(2,6), QtGui.QColor(  0, 255,   0))
72         self.bgcolors.addColor(  math.pow(2,14), QtGui.QColor(255, 255,   0))
73         self.bgcolors.addColor(math.pow(2,16)-1, QtGui.QColor(255,   0,   0))
74
75         self.setContextMenuPolicy(Qt.Qt.ActionsContextMenu)
76         self.mapper = QtCore.QSignalMapper(self)
77         self.connect(self.mapper, QtCore.SIGNAL("mapped(const QString&)"), self.directValues)
78
79         self.spinbox = QtGui.QDoubleSpinBox(self)
80         self.spinbox.setRange(-40, 12)
81         self.spinbox.setSuffix(" dB")
82         self.connect(self.spinbox, QtCore.SIGNAL("valueChanged(const QString&)"), self.directValues)
83         action = QtGui.QWidgetAction(self)
84         action.setDefaultWidget(self.spinbox)
85         self.addAction(action)
86
87         for text in ["3 dB", "0 dB", "-3 dB", "-20 dB", "-inf dB"]:
88             action = QtGui.QAction(text, self)
89             self.connect(action, QtCore.SIGNAL("triggered()"), self.mapper, QtCore.SLOT("map()"))
90             self.mapper.setMapping(action, text)
91             self.addAction(action)
92
93     def directValues(self,text):
94         #log.debug("MixerNode.directValues( '%s' )" % text)
95         text = text.split(" ")[0].replace(",",".")
96         n = pow(10, (float(text)/20)) * pow(2,14)
97         #log.debug("%g" % n)
98         self.setValue(n)
99
100     def mousePressEvent(self, ev):
101         if ev.buttons() & Qt.Qt.LeftButton:
102             self.pos = ev.posF()
103             self.tmpvalue = self.value()
104             ev.accept()
105             #log.debug("MixerNode.mousePressEvent() %s" % str(self.pos))
106
107     def mouseMoveEvent(self, ev):
108         if hasattr(self, "tmpvalue") and self.pos is not QtCore.QPointF(0, 0):
109             newpos = ev.posF()
110             change = newpos.y() - self.pos.y()
111             #log.debug("MixerNode.mouseReleaseEvent() change %s" % (str(change)))
112             self.setValue( self.tmpvalue - math.copysign(pow(abs(change), 2), change) )
113             ev.accept()
114
115     def mouseReleaseEvent(self, ev):
116         if hasattr(self, "tmpvalue") and self.pos is not QtCore.QPointF(0, 0):
117             newpos = ev.posF()
118             change = newpos.y() - self.pos.y()
119             #log.debug("MixerNode.mouseReleaseEvent() change %s" % (str(change)))
120             self.setValue( self.tmpvalue - math.copysign(pow(abs(change), 2), change) )
121             self.pos = QtCore.QPointF(0, 0)
122             del self.tmpvalue
123             ev.accept()
124
125     def paintEvent(self, ev):
126         p = QtGui.QPainter(self)
127         rect = self.rect()
128         v = self.value()
129         color = self.bgcolors.getColor(v)
130         p.fillRect(rect, color)
131         if color.valueF() < 0.6:
132             p.setPen(QtGui.QColor(255, 255, 255))
133         else:
134             p.setPen(QtGui.QColor(0, 0, 0))
135         lv=decimal.Decimal('-Infinity')
136         if v != 0:
137             lv = 20 * math.log10(v * 1.0 / math.pow(2,14))
138             #log.debug("new value is %g dB" % lv)
139         p.drawText(rect, Qt.Qt.AlignCenter, "%.2g dB" % lv)
140
141     def internalValueChanged(self, value):
142         #log.debug("MixerNode.internalValueChanged( %i )" % value)
143         if value is not 0:
144             dB = 20 * math.log10(value / math.pow(2,14))
145             if self.spinbox.value() is not dB:
146                 self.spinbox.setValue(dB)
147         self.emit(QtCore.SIGNAL("valueChanged"), (self.input, self.output, value) )
148         self.update()
149
150
151
152 class MixerChannel(QtGui.QWidget):
153     def __init__(self, number, parent=None, name=""):
154         QtGui.QWidget.__init__(self, parent)
155         layout = QtGui.QGridLayout(self)
156         self.number = number
157         if name is not "":
158             name = " (%s)" % name
159         self.lbl = QtGui.QLabel("Ch. %i\n%s" % (self.number, name), self)
160         layout.addWidget(self.lbl, 0, 0, 1, 2)
161
162         #self.btnHide = QtGui.QToolButton(self)
163         #self.btnHide.setText("Hide")
164         #self.btnHide.setCheckable(True)
165         #self.connect(self.btnHide, QtCore.SIGNAL("clicked(bool)"), self.hideChannel)
166         #layout.addWidget(self.btnHide, 1, 0)
167
168     def hideChannel(self, hide):
169         self.emit(QtCore.SIGNAL("hide"), self.number, hide)
170
171
172
173 class MatrixMixer(QtGui.QWidget):
174     def __init__(self, servername, basepath, parent=None):
175         QtGui.QWidget.__init__(self, parent)
176         self.bus = dbus.SessionBus()
177         self.dev = self.bus.get_object(servername, basepath)
178         self.interface = dbus.Interface(self.dev, dbus_interface="org.ffado.Control.Element.MatrixMixer")
179
180         #palette = self.palette()
181         #palette.setColor(QtGui.QPalette.Window, palette.color(QtGui.QPalette.Window).darker());
182         #self.setPalette(palette)
183
184         rows = self.interface.getColCount()
185         cols = self.interface.getRowCount()
186         log.debug("Mixer has %i rows and %i columns" % (rows, cols))
187
188         layout = QtGui.QGridLayout(self)
189         self.setLayout(layout)
190
191         self.rowHeaders = []
192         self.columnHeaders = []
193         self.items = []
194
195         # Add row/column headers
196         for i in range(cols):
197             ch = MixerChannel(i, self, self.interface.getColName(i))
198             self.connect(ch, QtCore.SIGNAL("hide"), self.hideColumn)
199             layout.addWidget(ch, 0, i+1)
200             self.columnHeaders.append( ch )
201         for i in range(rows):
202             ch = MixerChannel(i, self, self.interface.getRowName(i))
203             self.connect(ch, QtCore.SIGNAL("hide"), self.hideRow)
204             layout.addWidget(ch, i+1, 0)
205             self.rowHeaders.append( ch )
206
207         # Add node-widgets
208         for i in range(rows):
209             self.items.append([])
210             for j in range(cols):
211                 node = MixerNode(j, i, self.interface.getValue(i,j), self)
212                 self.connect(node, QtCore.SIGNAL("valueChanged"), self.valueChanged)
213                 layout.addWidget(node, i+1, j+1)
214                 self.items[i].append(node)
215
216         self.hiddenRows = []
217         self.hiddenCols = []
218
219
220     def checkVisibilities(self):
221         for x in range(len(self.items)):
222             for y in range(len(self.items[x])):
223                 self.items[x][y].setHidden(
224                         (x in self.hiddenRows)
225                         | (y in self.hiddenCols)
226                         )
227
228
229     def hideColumn(self, column, hide):
230         if hide:
231             self.hiddenCols.append(column)
232         else:
233             self.hiddenCols.remove(column)
234         self.checkVisibilities()
235     def hideRow(self, row, hide):
236         if hide:
237             self.hiddenRows.append(row)
238         else:
239             self.hiddenRows.remove(row)
240         self.checkVisibilities()
241
242     def valueChanged(self, n):
243         #log.debug("MatrixNode.valueChanged( %s )" % str(n))
244         self.interface.setValue(n[1], n[0], n[2])
245
246
247 #
248 # vim: et ts=4 sw=4
Note: See TracBrowser for help on using the browser.