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 |
---|