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