root/trunk/libffado/support/mixer-qt4/ffado/mixer/presonus_inspire1394.py

Revision 2653, 10.6 kB (checked in by jwoithe, 7 years ago)

This is a first pass at converting the signal handling of ffado-mixer to the
new style implemented by pyqtSignal[1]. It is based on patches provided by
Takashi Sakamoto in January 2015[2], Xavier Forestier in November 2016[3]
and additional minor fixes.

Note that this patchset breaks the matrix mixer due to problems with the
treatment of the valueChanged signal from the MixerNode? class. More work is
needed to determine what the problem is and how best to fix it in the
context of the new signal handling.

[1] http://www.riverbankcomputing.co.uk/news/pyqt-45
[2] https://sourceforge.net/p/ffado/mailman/ffado-devel/thread/1421471004-13162-1-git-send-email-o-takashi%40sakamocchi.jp/#msg33244745
[3] https://sourceforge.net/p/ffado/mailman/message/35457569/

Line 
1 #
2 # presonus_inspire1394.py - Qt4/FFADO application for Presonus Inspire1394
3 # Copyright (C) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp>
4 #
5 # This file is part of FFADO
6 # FFADO = Free Firewire (pro-)audio drivers for linux
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 2 of the License, or
11 # version 3 of the License.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21
22 from PyQt4 import QtGui, QtCore
23 from PyQt4.QtCore import QObject, Qt
24 from PyQt4.QtGui import QWidget, QHBoxLayout, QVBoxLayout, QGridLayout
25 from PyQt4.QtGui import QGroupBox, QLabel, QSizePolicy, QSlider, QDial, QComboBox, QToolButton
26 from math import log10
27 from ffado.config import *
28
29 class Presonus_Inspire1394(QWidget):
30     # feature_id/name
31     inputs = [[1, "Analog in 1/2"],
32               [2, "Analog in 3/4"]]
33
34     # feature_id/name
35     mixer_src = [[3, "Analog in 1/2"],
36                  [4, "Analog in 3/4"],
37                  [5, "Stream in 1/2"]]
38
39     # feature id/name
40     outputs = [[6, "Analog out 1/2"],
41                [7, "HP out 1/2"]]
42
43     # selector_id/sources
44     out_src = [1, ["Mixer out 1/2", "Stream in 1/2"]]
45
46     def __init__(self, parent=None):
47         QWidget.__init__(self, parent)
48
49     def getDisplayTitle(self):
50         return 'Inspire1394'
51
52     def buildMixer(self):
53         self.Selectors = {}
54         self.Volumes = {}
55         self.Preamps = {}
56         self.Pannings = {}
57         self.Mutes = {}
58
59         plain_layout = QHBoxLayout(self)
60
61         left = QGroupBox(self)
62         plain_layout.addWidget(left)
63         self.addAnalogInputs(left)
64
65         center = QGroupBox(self)
66         plain_layout.addWidget(center)
67         self.addInternalMixer(center)
68
69         right = QGroupBox(self)
70         plain_layout.addWidget(right)
71         self.addAnalogOutputs(right)
72
73     def addAnalogInputs(self, box):
74         box_layout = QVBoxLayout()
75         box.setLayout(box_layout)
76         box.setTitle("Analog Inputs")
77
78         grid_layout = QGridLayout()
79         box_layout.addLayout(grid_layout)
80    
81         self.addVolumes(self.inputs, 0, box, grid_layout)
82
83     def addAnalogOutputs(self, box):
84         box_layout = QVBoxLayout()
85         box.setLayout(box_layout)
86         box.setTitle("Analog Outputs")
87
88         grid_layout = QGridLayout()
89         box_layout.addLayout(grid_layout)
90
91         self.addVolumes(self.outputs, 2, box, grid_layout)
92
93     def addInternalMixer(self, box):
94         box_layout = QGridLayout()
95         box.setLayout(box_layout)
96         box.setTitle("Hardware Mixer")
97
98         self.addVolumes(self.mixer_src, 1, box, box_layout)
99
100     def addVolumes(self, elms, kind, parent, layout):
101         def addPreampParam(label, ch, path, layout):
102             button = self.getThinButton(parent, label)
103             layout.addWidget(button)
104             self.Preamps[button] = ["/Preamp/%s" % path, ch]
105             return
106
107         for col in range(len(elms)):
108             label = QLabel(parent)
109             label.setText(elms[col][1])
110             layout.addWidget(label, 0, col * 2, 1, 2, Qt.AlignHCenter | Qt.AlignTop)
111
112             if kind == 0:
113                 if col == 0:
114                     for ch in range(2):
115                         box_layout = QVBoxLayout()
116                         layout.addLayout(box_layout, 1, col * 2 + ch, Qt.AlignHCenter | Qt.AlignBottom)
117                         if col == 0:
118                             addPreampParam("+48V", ch + 1, "PhantomPower", box_layout)
119                             addPreampParam("Boost", ch + 1, "MicBoost", box_layout)
120                             addPreampParam("Limit", ch + 1, "MicLimit", box_layout)
121                 else:
122                     box_layout = QVBoxLayout()
123                     addPreampParam("Phono", 0, "PhonoSwitch", box_layout)
124                     layout.addLayout(box_layout, 1, col * 2, 1, 2, Qt.AlignHCenter | Qt.AlignBottom)
125             elif kind == 1:
126                 l_dial = self.getDial(parent)
127                 r_dial = self.getDial(parent)
128
129                 layout.addWidget(l_dial, 1, col * 2, Qt.AlignHCenter | Qt.AlignBottom)
130                 layout.addWidget(r_dial, 1, col * 2 + 1, Qt.AlignHCenter | Qt.AlignBottom)
131
132                 path = "/Mixer/Feature_LRBalance_%d" % elms[col][0]
133                 self.Pannings[l_dial] = [path, 1]
134                 self.Pannings[r_dial] = [path, 2]
135
136                 if col == 2:
137                     l_dial.setDisabled(True)
138                     r_dial.setDisabled(True)
139
140             elif col == 0:
141                 cmb = QComboBox(parent)
142                 layout.addWidget(cmb, 1, col * 2, 1, 4, Qt.AlignHCenter | Qt.AlignBottom)
143                 for i in range(len(self.out_src[1])):
144                     cmb.addItem(self.out_src[1][i], i)
145                     self.Selectors[cmb] = ["/Mixer/Selector_%d" % self.out_src[0]]
146
147             l_sld = self.getSlider(parent)
148             r_sld = self.getSlider(parent)
149             layout.addWidget(l_sld, 2, col * 2, Qt.AlignHCenter)
150             layout.addWidget(r_sld, 2, col * 2 + 1, Qt.AlignHCenter)
151
152             l_mute = self.getThinButton(parent, "Mute")
153             r_mute = self.getThinButton(parent, "Mute")
154             layout.addWidget(l_mute, 3, col * 2, Qt.AlignHCenter)
155             layout.addWidget(r_mute, 3, col * 2 + 1, Qt.AlignHCenter)
156
157             link = self.getWideButton(parent, "Link")
158             layout.addWidget(link, 4, col * 2, 1, 2, Qt.AlignHCenter)
159
160             path = "/Mixer/Feature_Volume_%d" % elms[col][0]
161             self.Volumes[l_sld] = [path, 1, r_sld, l_mute, link]
162             self.Volumes[r_sld] = [path, 2, l_sld, r_mute, link]
163
164             self.Mutes[l_mute] = [r_mute, l_sld]
165             self.Mutes[r_mute] = [l_mute, r_sld]
166
167     # widget helper functions
168     def getSlider(self, parent):
169         sld = QSlider(parent)
170         sld.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
171         sld.setMinimum(0)
172         sld.setMaximum(99)
173         sld.setPageStep(10)
174         sld.setPageStep(10)
175         sld.setMinimumHeight(50)
176         sld.setTickInterval(25)
177         sld.setTickPosition(QSlider.TicksBothSides)
178         return sld
179
180     def getDial(self, parent):
181         dial = QDial(parent)
182         dial.setNotchesVisible(True)
183         dial.setNotchTarget(25.0)
184         dial.setMaximumHeight(40)
185         return dial;
186  
187     def getThinButton(self, parent, text):
188         button = QToolButton(parent)
189         button.setText(text)
190         button.setCheckable(True)
191         button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
192         return button
193
194     def getWideButton(self, parent, label):
195         button = QToolButton(parent)
196         button.setText(label)
197         button.setCheckable(True)
198         button.setMinimumWidth(100)
199         button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
200         return button
201
202     def initValues(self):
203         for ctl, params in self.Selectors.items():
204            path = params[0]
205            state = self.hw.getDiscrete(path)
206            ctl.setCurrentIndex(state)
207            ctl.activated.connect(self.updateSelector)
208
209         for ctl, params in self.Volumes.items():
210            path = params[0]
211            idx  = params[1]
212            pair = params[2]
213            mute = params[3]
214            link = params[4]
215
216            db = self.hw.getContignuous(path, idx)
217            vol = self.db2vol(db)
218            ctl.setValue(vol)
219            ctl.valueChanged.connect(self.updateVolume)
220
221            if vol == 0:
222                mute.setChecked(True)
223
224            if idx == 2:
225                pair_db = self.hw.getContignuous(path, 1)
226                if pair_db == db:
227                    link.setChecked(True)
228
229         for ctl, params in self.Preamps.items():
230             path = params[0]
231             idx = params[1]
232
233             vol = self.hw.getDiscrete(path, idx)
234             if vol > 0:
235                 ctl.setChecked(True)
236
237             ctl.clicked.connect(self.updatePreamps)
238
239         #       Right - Center - Left
240         # 0x8000 - 0x0000 - 0x0001 - 0x7FFE
241         #        ..., -1, 0, +1, ...
242         for ctl, params in self.Pannings.items():
243             path = params[0]
244             idx = params[1]
245
246             val = self.hw.getContignuous(path, idx)
247             state = -(val / 0x7FFE) * 50 + 50
248             ctl.setValue(state)
249             ctl.valueChanged.connect(self.updatePanning)
250
251         for ctl, params in self.Mutes.items():
252             ctl.clicked.connect(self.updateMute)
253
254     # helper functions
255     def vol2db(self, vol):
256         return (log10(vol + 1) - 2) * 16384
257
258     def db2vol(self, db):
259         return pow(10, db / 16384 + 2) - 1
260
261     def updateSelector(self, state):
262         sender = self.sender()
263         path   = self.Selectors[sender][0]
264         self.hw.setDiscrete(path, state)
265
266     def updatePreamps(self, state):
267         sender = self.sender()
268         path = self.Preamps[sender][0]
269         idx = self.Preamps[sender][1]
270         self.hw.setDiscrete(path, idx, state)
271
272     def updateVolume(self, vol):
273         sender = self.sender()
274         path   = self.Volumes[sender][0]
275         idx    = self.Volumes[sender][1]
276         pair   = self.Volumes[sender][2]
277         mute   = self.Volumes[sender][3]
278         link   = self.Volumes[sender][4]
279
280         if mute.isChecked():
281                 return
282
283         db = self.vol2db(vol)
284         self.hw.setContignuous(path, db, idx)
285
286         if link.isChecked():
287            pair.setValue(vol)
288
289     def updatePanning(self, state):
290         sender = self.sender()
291         path = self.Pannings[sender][0]
292         idx = self.Pannings[sender][1]
293         val  = (state - 50) * 0x7FFE / -50
294         self.hw.setContignuous(path, idx, val)
295
296     def updateMute(self, state):
297         sender = self.sender()
298         pair = self.Mutes[sender][0]
299         sld = self.Mutes[sender][1]
300
301         path = self.Volumes[sld][0]
302         idx = self.Volumes[sld][1]
303         pair_sld = self.Volumes[sld][2]
304         link = self.Volumes[sld][4]
305
306         if state:
307             db = 0x8000
308             vol = 0
309         else:
310             db = 0x0000
311             vol = 99
312
313         self.hw.setContignuous(path, db, idx)
314         sld.setValue(vol)
315         sld.setDisabled(state)
316
317         if link.isChecked():
318             if idx == 1:
319                idx = 2
320             else:
321                idx = 1
322             self.hw.setContignuous(path, db, idx)
323             pair.setChecked(state)
324             pair_sld.setValue(vol)
325             pair_sld.setDisabled(state)
Note: See TracBrowser for help on using the browser.