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

Revision 1683, 9.0 kB (checked in by arnonym, 14 years ago)

Kind of a usable panner. In theory this should allow panning several sources to several sinks. In practice only mono-to-stereo and stereo-to-stereo is currently possible. Not because of the panner but because of the way it works in the matrixmixer. Will change that soon.

One of the current draw-backs is that I am using numpy. Standard pythons matrix handling doesn't seem so good.

  • 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, numpy
23
24 from ffado.widgets.ntompanner import N2MPanner
25
26 import logging
27 log = logging.getLogger("matrixmixer")
28
29 class MixerNode(QtGui.QFrame):
30     def __init__(self, inputs, outputs, parent):
31         """inputs  = list of input-channel numbers
32            outputs = list of output-channel numbers
33            """
34         QtGui.QFrame.__init__(self, parent)
35         self.setLineWidth(2)
36         self.setFrameStyle(QtGui.QFrame.Panel|QtGui.QFrame.Raised)
37
38         self.inputs = []
39         self.outputs = []
40
41         self.fader = QtGui.QDial(self)
42         self.fader.setRange(0,pow(2,16)-1)
43         self.connect(self.fader, QtCore.SIGNAL("valueChanged(int)"), self.valuesChanged)
44
45         self.layout = QtGui.QGridLayout(self)
46         self.setLayout(self.layout)
47
48         self.layout.addWidget(self.fader, 0, 0)
49
50         self.addInputs(inputs)
51         self.addOutputs(outputs)
52
53     def valuesChanged(self):
54         #log.debug("MixerNode.valuesChanged")
55         fader = self.fader.value()
56         values = numpy.ones((1, len(self.inputs)))
57         if len(self.outputs) > 1:
58             values = self.panner.values
59             if values.size == 0:
60                 return
61             values = numpy.minimum(values, 2)
62             values = 1 - numpy.power(values/2, 2)
63         #print values
64         #print numpy.exp(-values)
65         values = values * fader
66         #print values
67         ret = []
68         for i in range(len(self.inputs)):
69             for j in range(len(self.outputs)):
70                 ret.append( (self.inputs[i], self.outputs[j], values[i,j]) )
71         #print ret
72         self.emit(QtCore.SIGNAL("valueChanged"), ret)
73
74     def addInputs(self, inputs, add=True):
75         if not isinstance(inputs, list):
76             inputs = [inputs]
77         if add:
78             self.inputs += inputs
79         else:
80             for item in inputs:
81                 self.inputs.remove(item)
82         self.checkWidgets()
83     def removeInputs(self, inputs):
84         self.addInputs(inputs, False)
85
86     def addOutputs(self, outputs, add=True):
87         if not isinstance(outputs, list):
88             outputs = [outputs]
89         if add:
90             self.outputs += outputs
91         else:
92             for item in outputs:
93                 self.outputs.remove(item)
94         self.checkWidgets()
95     def removeOutputs(self, outputs):
96         self.addOutputs(outputs, False)
97
98     def checkWidgets(self):
99         if not hasattr(self, "panner") and len(self.outputs)>1:
100             self.panner = N2MPanner(self)
101             self.layout.addWidget(self.panner, 1, 0)
102             self.connect(self.panner, QtCore.SIGNAL("valuesChanged"), self.valuesChanged)
103         if hasattr(self, "panner"):
104             self.panner.setVisible(len(self.outputs)>1)
105             self.panner.setNumberOfSources(len(self.inputs))
106             self.panner.setNumberOfSinks(len(self.outputs))
107         if len(self.inputs) and len(self.outputs):
108             valuematrix = []
109             for row in self.outputs:
110                 valuematrix.append( [] )
111                 for col in self.inputs:
112                     valuematrix[self.outputs.index(row)] = self.parent().interface.getValue(row, col)
113             self.fader.setValue(max(valuematrix))
114
115
116 class MixerChannel(QtGui.QWidget):
117     def __init__(self, number, parent=None, name=""):
118         QtGui.QWidget.__init__(self, parent)
119         layout = QtGui.QGridLayout(self)
120         self.number = number
121         if name is not "":
122             name = " (%s)" % name
123         self.lbl = QtGui.QLabel("Ch. %i%s" % (self.number, name), self)
124         layout.addWidget(self.lbl, 0, 0, 1, 2)
125
126         self.btnHide = QtGui.QToolButton(self)
127         self.btnHide.setText("Hide")
128         self.btnHide.setCheckable(True)
129         self.connect(self.btnHide, QtCore.SIGNAL("clicked(bool)"), self.hideChannel)
130         layout.addWidget(self.btnHide, 1, 0)
131
132         self.btnCouple = QtGui.QToolButton(self)
133         self.btnCouple.setText("Stereo")
134         self.btnCouple.setCheckable(True)
135         self.connect(self.btnCouple, QtCore.SIGNAL("toggled(bool)"), self.coupleWithNext)
136         layout.addWidget(self.btnCouple, 1, 1)
137
138     def hideChannel(self, hide):
139         self.btnCouple.setHidden(hide)
140         self.emit(QtCore.SIGNAL("hide"), self.number, hide)
141
142     def coupleWithNext(self, couple):
143         self.emit(QtCore.SIGNAL("couple"), self.number, couple)
144
145     def canCouple(self, cancouple):
146         #self.btnCouple.setEnabled(cancouple)
147         self.btnCouple.setHidden(not cancouple)
148
149 class MatrixMixer(QtGui.QWidget):
150     def __init__(self, servername, basepath, parent=None):
151         QtGui.QWidget.__init__(self, parent)
152         self.bus = dbus.SessionBus()
153         self.dev = self.bus.get_object(servername, basepath)
154         self.interface = dbus.Interface(self.dev, dbus_interface="org.ffado.Control.Element.MatrixMixer")
155
156         palette = self.palette()
157         palette.setColor(QtGui.QPalette.Window, palette.color(QtGui.QPalette.Window).darker());
158         self.setPalette(palette)
159
160         rows = self.interface.getColCount()
161         cols = self.interface.getRowCount()
162         log.debug("Mixer has %i rows and %i columns" % (rows, cols))
163
164         layout = QtGui.QGridLayout(self)
165         self.setLayout(layout)
166
167         self.rowHeaders = []
168         self.columnHeaders = []
169         self.items = []
170
171         # Add row/column headers
172         for i in range(cols):
173             ch = MixerChannel(i, self, self.interface.getColName(i))
174             self.connect(ch, QtCore.SIGNAL("couple"), self.coupleColumn)
175             self.connect(ch, QtCore.SIGNAL("hide"), self.hideColumn)
176             ch.canCouple(i+1!=cols)
177             layout.addWidget(ch, 0, i+1)
178             self.columnHeaders.append( ch )
179         for i in range(rows):
180             ch = MixerChannel(i, self, self.interface.getRowName(i))
181             self.connect(ch, QtCore.SIGNAL("couple"), self.coupleRow)
182             self.connect(ch, QtCore.SIGNAL("hide"), self.hideRow)
183             ch.canCouple(i+1!=rows)
184             layout.addWidget(ch, i+1, 0)
185             self.rowHeaders.append( ch )
186
187         # Add node-widgets
188         for i in range(rows):
189             self.items.append([])
190             for j in range(cols):
191                 node = MixerNode(j, i, self)
192                 self.connect(node, QtCore.SIGNAL("valueChanged"), self.valueChanged)
193                 layout.addWidget(node, i+1, j+1)
194                 self.items[i].append(node)
195
196         self.hiddenRows = []
197         self.hiddenCols = []
198         self.coupledRows = []
199         self.coupledCols = []
200
201
202     def checkVisibilities(self):
203         for x in range(len(self.items)):
204             for y in range(len(self.items[x])):
205                 self.items[x][y].setHidden( (x in self.hiddenRows) | (x in self.coupledRows) | (y in self.hiddenCols) | (y in self.coupledCols) )
206
207     def coupleColumn(self, column, couple):
208         if column+1 < len(self.columnHeaders):
209             self.columnHeaders[column+1].setHidden(couple)
210         if column > 0:
211             self.columnHeaders[column-1].canCouple(not couple)
212         if couple:
213             self.coupledCols.append(column+1)
214         else:
215             self.coupledCols.remove(column+1)
216         for row in self.items:
217             row[column].addInputs(column+1,couple)
218             row[column+1].addInputs(column+1,not couple)
219         self.checkVisibilities()
220
221     def coupleRow(self, row, couple):
222         if row+1 < len(self.rowHeaders):
223             self.rowHeaders[row+1].setHidden(couple)
224         if row > 0:
225             self.rowHeaders[row-1].canCouple(not couple)
226         if couple:
227             self.coupledRows.append(row+1)
228         else:
229             self.coupledRows.remove(row+1)
230         for col in self.items[row]:
231             col.addOutputs(row+1,couple)
232         for col in self.items[row+1]:
233             col.addOutputs(row+1,not couple)
234         self.checkVisibilities()
235
236     def hideColumn(self, column, hide):
237         if hide:
238             self.hiddenCols.append(column)
239         else:
240             self.hiddenCols.remove(column)
241         self.checkVisibilities()
242     def hideRow(self, row, hide):
243         if hide:
244             self.hiddenRows.append(row)
245         else:
246             self.hiddenRows.remove(row)
247         self.checkVisibilities()
248
249     def valueChanged(self, n):
250         #log.debug("MatrixNode.valueChanged( %s )" % str(n))
251         for tmp in n:
252             self.interface.setValue(tmp[1], tmp[0], tmp[2])
253
254
255 #
256 # vim: et ts=4 sw=4
Note: See TracBrowser for help on using the browser.