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

Revision 2063, 16.3 kB (checked in by jwoithe, 9 years ago)

rme: don't make the output matrix mixer take up the full vertical extent of the window - that just looks silly. The solution's a bit of a hack, but I can't see any other way which works at present. Note too that the fix in r2062 works - sending works to all channels now.

  • Property svn:mergeinfo set to
Line 
1 #
2 # Copyright (C) 2009, 2011 by Jonathan Woithe
3 #
4 # This file is part of FFADO
5 # FFADO = Free Firewire (pro-)audio drivers for linux
6 #
7 # FFADO is based upon FreeBoB.
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 2 of the License, or
12 # (at your option) version 3 of the License.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 #
22
23 from PyQt4 import QtGui
24
25 from PyQt4.QtCore import SIGNAL, SLOT, QObject, Qt, QTimer
26 from PyQt4.QtGui import QWidget, QApplication
27 from ffado.config import *
28
29 from ffado.widgets.matrixmixer import MatrixMixer
30
31 import logging
32 log = logging.getLogger('rme')
33
34 # Model defines.  These must agree with what is used in rme_avdevice.h.
35 RME_MODEL_NONE      = 0x0000
36 RME_MODEL_FF800     = 0x0001
37 RME_MODEL_FF400     = 0x0002
38
39 class Rme(QWidget):
40     def __init__(self,parent = None):
41         QWidget.__init__(self,parent)
42         uicLoad("ffado/mixer/rme", self)
43
44         self.init()
45
46     def init(self):
47
48         self.PhantomSwitches={
49             self.phantom_0: ['/Control/Phantom', 0],
50             self.phantom_1: ['/Control/Phantom', 1],
51             self.phantom_2: ['/Control/Phantom', 2],
52             self.phantom_3: ['/Control/Phantom', 3],
53         }
54
55         self.Switches={
56             self.ff400_chan3_opt_instr: ['/Control/Chan3_opt_instr'],
57             self.ff400_chan3_opt_pad:   ['/Control/Chan3_opt_pad'],
58             self.ff400_chan4_opt_instr: ['/Control/Chan4_opt_instr'],
59             self.ff400_chan4_opt_pad:   ['/Control/Chan4_opt_pad'],
60
61             self.spdif_output_optical:  ['/Control/SPDIF_output_optical', 0],
62             self.spdif_output_emphasis: ['/Control/SPDIF_output_emphasis', 0],
63             self.spdif_output_pro:      ['/Control/SPDIF_output_pro', 0],
64             self.spdif_output_nonaudio: ['/Control/SPDIF_output_nonaudio', 0],
65         }
66
67         self.Radiobuttons={
68             self.level_in_lo_gain: ['/Control/Input_level', 1],
69             self.level_in_p4dBu:   ['/Control/Input_level', 2],
70             self.level_in_m10dBV:  ['/Control/Input_level', 3],
71
72             self.level_out_hi_gain: ['/Control/Output_level', 1],
73             self.level_out_p4dBu:   ['/Control/Output_level', 2],
74             self.level_out_m10dBV:  ['/Control/Output_level', 3],
75
76             self.spdif_input_coax:    ['/Control/SPDIF_input_mode', 0],
77             self.spdif_input_optical: ['/Control/SPDIF_input_mode', 1],
78
79             self.phones_hi_gain: ['/Control/Phones_level', 1],
80             self.phones_p4dBu:   ['/Control/Phones_level', 2],
81             self.phones_m10dBV:  ['/Control/Phones_level', 3],
82
83             self.clock_mode_autosync: ['/Control/Clock_mode', 1],
84             self.clock_mode_master: ['/Control/Clock_mode', 0],
85
86             self.sync_ref_wordclk: ['/Control/Sync_ref', 0],
87             self.sync_ref_adat1: ['/Control/Sync_ref', 1],
88             self.sync_ref_adat2: ['/Control/Sync_ref', 2],
89             self.sync_ref_spdif: ['/Control/Sync_ref', 3],
90             self.sync_ref_tco: ['/Control/Sync_ref', 4],
91         }
92
93         self.Checkboxes={
94             self.ch1_instr_fuzz: ['/Control/Chan1_instr_opts', 0x04],
95             self.ch1_instr_limiter: ['/Control/Chan1_instr_opts', 0x08],
96             self.ch1_instr_filter: ['/Control/Chan1_instr_opts', 0x02],
97         }
98
99         self.Gains={
100             self.gain_mic1: ['/Control/Gains', 0],
101             self.gain_mic2: ['/Control/Gains', 1],
102             self.gain_input3: ['/Control/Gains', 2],
103             self.gain_input4: ['/Control/Gains', 3],
104         }
105
106         self.Combos={
107             self.ff800_ch1_src: ['/Control/Chan1_source'],
108             self.ff800_ch7_src: ['/Control/Chan7_source'],
109             self.ff800_ch8_src: ['/Control/Chan8_source'],
110         }
111
112         # Other mixer variables
113         self.is_streaming = 0
114         self.sample_rate = 0
115         self.model = 0
116         self.tco_present = 0
117
118     # Public slot: update phantom power hardware switches
119     def updatePhantomSwitch(self, a0):
120         sender = self.sender()
121         # Value is the phantom switch value, with a corresponding enable
122         # bit in the high 16 bit word
123         val = (a0 << self.PhantomSwitches[sender][1]) | (0x00010000 << self.PhantomSwitches[sender][1])
124         log.debug("phantom switch %d set to %d" % (self.PhantomSwitches[sender][1], a0))
125         self.hw.setDiscrete(self.PhantomSwitches[sender][0], val)
126
127     # Public slot: update generic switches
128     def updateSwitch(self, a0):
129         sender = self.sender()
130         log.debug("switch %s set to %d" % (self.Switches[sender][0], a0))
131         self.hw.setDiscrete(self.Switches[sender][0], a0)
132
133     # Public slot: update generic radiobuttons
134     def updateRadiobutton(self, a0):
135         sender = self.sender()
136         if (a0 != 0):
137             # Only change the control state on a button being "checked"
138             log.debug("radiobutton group %s set to %d" % (self.Radiobuttons[sender][0], self.Radiobuttons[sender][1]))
139             self.hw.setDiscrete(self.Radiobuttons[sender][0], self.Radiobuttons[sender][1])
140
141     def updateCheckboxes(self, a0):
142         sender = self.sender()
143         val = self.hw.getDiscrete(self.Checkboxes[sender][0]);
144         if (a0 != 0):
145             val = val | self.Checkboxes[sender][1]
146         else:
147             val = val & ~self.Checkboxes[sender][1]
148         log.debug("checkbox group %s set to %d" % (self.Checkboxes[sender][0], val));
149         self.hw.setDiscrete(self.Checkboxes[sender][0], val)
150
151     # Public slot: update gains
152     def updateGain(self, a0):
153         sender = self.sender()
154         log.debug("gain %s[%d] set to %d" % (self.Gains[sender][0], self.Gains[sender][1], a0))
155         self.hw.setMatrixMixerValue(self.Gains[sender][0], 0, self.Gains[sender][1], a0)
156
157     def updateBandwidthLimit(self, a0):
158         # Account for the "No ADAT-2" item which will not be present on
159         # a FF400.
160         if (self.model==RME_MODEL_FF400 and a0>0):
161             a0 = a0 + 1
162         # log.debug("limit update: %d" % (a0));
163         self.hw.setDiscrete('/Control/Bandwidth_limit', a0);
164
165     def updateCombo(self, a0):
166         sender = self.sender()
167         log.debug("combo %s set to %d" % (self.Combos[sender][0], a0))
168         self.hw.setDiscrete(self.Combos[sender][0], a0)
169
170     def updateStreamingState(self):
171         ss = self.streamingstatus.selected()
172         ss_txt = self.streamingstatus.getEnumLabel(ss)
173         if ss_txt != 'Idle':
174             self.is_streaming = True
175         else:
176             self.is_streaming = False
177         if (self.last_streaming_state != self.is_streaming):
178             self.bandwidth_limit.setEnabled(not(self.is_streaming));
179         self.last_streaming_state = self.is_streaming
180
181     def status_update(self):
182         # log.debug("timer event")
183         self.updateStreamingState()
184         clk_mode = ['Master', 'Slave']
185         src_str = ['None', 'ADAT 1', 'ADAT 2', 'SPDIF', 'Wordclock', 'TCO']
186         sync_stat = ['No lock', 'Locked', 'Synced']
187         sysclock_mode = self.hw.getDiscrete('/Control/sysclock_mode')
188         sysclock_freq = self.hw.getDiscrete('/Control/sysclock_freq')
189         autosync_freq = self.hw.getDiscrete('/Control/autosync_freq')
190         autosync_src = self.hw.getDiscrete('/Control/autosync_src')
191         sync_status = self.hw.getDiscrete('/Control/sync_status')
192         spdif_freq = self.hw.getDiscrete('/Control/spdif_freq')
193         self.sysclock_freq.setText("%d Hz" % (sysclock_freq))
194         self.sysclock_mode.setText(clk_mode[sysclock_mode])
195         self.autosync_freq.setText("%d Hz" % (autosync_freq))
196         self.autosync_src.setText(src_str[autosync_src])
197         self.sync_check_adat1_status.setText(sync_stat[sync_status & 0x03])
198         self.sync_check_adat2_status.setText(sync_stat[(sync_status >> 2) & 0x03])
199         self.sync_check_spdif_status.setText(sync_stat[(sync_status >> 4) & 0x03])
200         self.sync_check_wclk_status.setText(sync_stat[(sync_status >> 6) & 0x03])
201         self.sync_check_tco_status.setText(sync_stat[(sync_status >> 8) & 0x03])
202         self.spdif_freq.setText("%d Hz" % (spdif_freq))
203
204     # Hide and disable a control
205     def disable_hide(self,widget):
206         widget.hide()
207         widget.setEnabled(False)
208
209     def initValues(self):
210
211         # print self.hw.servername
212         # print self.hw.basepath
213         self.inputmatrix = MatrixMixer(self.hw.servername, self.hw.basepath+"/Mixer/InputFaders", self, 0x8000, self.hw.basepath+"/Mixer/InputMutes", self.hw.basepath+"/Mixer/InputInverts")
214         layout = QtGui.QVBoxLayout()
215         scrollarea = QtGui.QScrollArea()
216         scrollarea.setWidgetResizable(True)
217         scrollarea.setWidget(self.inputmatrix)
218         layout.addWidget(scrollarea)
219         self.mixer.setLayout(layout)
220
221         self.playbackmatrix = MatrixMixer(self.hw.servername, self.hw.basepath+"/Mixer/PlaybackFaders", self, 0x8000, self.hw.basepath+"/Mixer/PlaybackMutes", self.hw.basepath+"/Mixer/PlaybackInverts")
222         layout = QtGui.QVBoxLayout()
223         scrollarea = QtGui.QScrollArea()
224         scrollarea.setWidgetResizable(True)
225         scrollarea.setWidget(self.playbackmatrix)
226         layout.addWidget(scrollarea)
227         self.playbackmixer.setLayout(layout)
228
229         self.outputmatrix = MatrixMixer(self.hw.servername, self.hw.basepath+"/Mixer/OutputFaders", self, 0x8000, self.hw.basepath+"/Mixer/OutputMutes")
230         layout = QtGui.QVBoxLayout()
231         scrollarea = QtGui.QScrollArea()
232         scrollarea.setWidget(self.outputmatrix)
233         scrollarea.setWidgetResizable(True)
234
235         # This is a bit of a hack, but it works to ensure this single-row
236         # matrix mixer doesn't fill the entire screen but also doesn't end
237         # up with a pointless scrollbar.  The matrix mixer's minimum height
238         # is 0 according to minimumHeight(), which is probably the
239         # fundamental issue here; however, I've already wasted too much time
240         # trying to get this to work so if the hack is effective we'll run
241         # with it.
242         scrollarea.setMinimumHeight(150)
243         layout.addWidget(scrollarea, 0, Qt.AlignTop)
244         self.outputmixer.setLayout(layout)
245
246         self.is_streaming = False
247         self.last_streaming_state = False
248
249         # Retrieve other device settings as needed and customise the UI
250         # based on these options.
251         self.model = self.hw.getDiscrete('/Control/Model')
252         log.debug("device model identifier: %d" % (self.model))
253         self.tco_present = self.hw.getDiscrete('/Control/TCO_present')
254         log.debug("device has TCO: %d" % (self.tco_present))
255         #self.sample_rate = self.hw.getDiscrete('/Mixer/Info/SampleRate')
256         #log.debug("device sample rate: %d" % (self.sample_rate))
257
258         # The Fireface-400 only has 2 phantom-capable channels
259         if (self.model == RME_MODEL_FF400):
260             self.disable_hide(self.phantom_2)
261             self.disable_hide(self.phantom_3)
262         else:
263             self.phantom_0.setText("Mic 7")
264             self.phantom_1.setText("Mic 8")
265             self.phantom_2.setText("Mic 9")
266             self.phantom_3.setText("Mic 10")
267
268         # Instrument options, input jack selection controls and an ADAT2
269         # input are applicable only to the FF800
270         if (self.model != RME_MODEL_FF800):
271             self.instrument_options_group.setEnabled(False)
272             self.input_plug_select_group.setEnabled(False)
273             self.sync_ref_adat2.setEnabled(False)
274             self.sync_check_adat2_label.setEnabled(False)
275             self.sync_check_adat2_status.setEnabled(False)
276
277         for ctrl, info in self.Combos.iteritems():
278             if (not(ctrl.isEnabled())):
279                 continue;
280             val = self.hw.getDiscrete(info[0])
281             log.debug("combo %s is %d" % (info[0], val));
282             ctrl.setCurrentIndex(val);
283             QObject.connect(ctrl, SIGNAL('currentIndexChanged(int)'), self.updateCombo)
284
285         if (not(self.tco_present)):
286             self.sync_check_tco_label.setEnabled(False)
287             self.sync_check_tco_status.setEnabled(False)
288             self.sync_ref_tco.setEnabled(False)
289
290         # Only the FF400 has specific channel 3/4 options, input gain
291         # controls and switchable phones level
292         if (self.model != RME_MODEL_FF400):
293             self.disable_hide(self.input_gains_group)
294             self.disable_hide(self.channel_3_4_options_group)
295             self.phones_level_group.setEnabled(False)
296
297         # Add the "No ADAT-2" item to the bandwidth limit control if the
298         # device is not a FF400.  Set the control to reflect the current
299         # device setting and connect an update signal.
300         if (self.model != RME_MODEL_FF400):
301             self.bandwidth_limit.insertItem(1, "No ADAT-2")
302         val = self.hw.getDiscrete('/Control/Bandwidth_limit')
303         if (self.model==RME_MODEL_FF400 and val>1):
304             val = val - 1
305         self.bandwidth_limit.setCurrentIndex(val);
306         QObject.connect(self.bandwidth_limit, SIGNAL('currentIndexChanged(int)'), self.updateBandwidthLimit)
307
308         # Get current hardware values and connect GUI element signals to
309         # their respective slots
310         for ctrl, info in self.PhantomSwitches.iteritems():
311             if (not(ctrl.isEnabled())):
312                 continue
313             val = (self.hw.getDiscrete(info[0]) >> info[1]) & 0x01
314             log.debug("phantom switch %d is %d" % (info[1], val))
315             if val:
316                 ctrl.setChecked(True)
317             else:
318                 ctrl.setChecked(False)
319             QObject.connect(ctrl, SIGNAL('toggled(bool)'), self.updatePhantomSwitch)
320
321         for ctrl, info in self.Switches.iteritems():
322             if (not(ctrl.isEnabled())):
323                 continue
324             val = self.hw.getDiscrete(info[0])
325             log.debug("switch %s is %d" % (info[0], val))
326             if val:
327                 ctrl.setChecked(True)
328             else:
329                 ctrl.setChecked(False)
330             QObject.connect(ctrl, SIGNAL('toggled(bool)'), self.updateSwitch)
331
332         for ctrl, info in self.Radiobuttons.iteritems():
333             if (not(ctrl.isEnabled())):
334                 continue;
335             # This is a touch wasteful since it means we retrieve the control
336             # value once per radio button rather than once per radio button
337             # group.  In time we might introduce radiobutton groupings in the
338             # self.* datastructures to avoid this, but for the moment this is
339             # easy and it works.
340             val = self.hw.getDiscrete(info[0])
341             if (val == info[1]):
342                 val = 1
343             else:
344                 val = 0
345             ctrl.setChecked(val)
346             log.debug("Radiobutton %s[%d] is %d" % (info[0], info[1], val))
347             QObject.connect(ctrl, SIGNAL('toggled(bool)'), self.updateRadiobutton)
348
349         for ctrl, info in self.Checkboxes.iteritems():
350             if (not(ctrl.isEnabled())):
351                 continue;
352             # This is a touch wasteful since it means we retrieve the control
353             # value once per checkbox button rather than once per checkbox
354             # group.  In time we might introduce checkbox groupings in the
355             # self.* datastructures to avoid this, but for the moment this is
356             # easy and it works.
357             val = self.hw.getDiscrete(info[0])
358             if (val & info[1]):
359                 val = 1
360             else:
361                 val = 0
362             ctrl.setChecked(val)
363             log.debug("Checkbox %s[%d] is %d" % (info[0], info[1], val))
364             QObject.connect(ctrl, SIGNAL('toggled(bool)'), self.updateCheckboxes)
365
366         for ctrl, info in self.Gains.iteritems():
367             if (not(ctrl.isEnabled())):
368                 continue
369             val = self.hw.getMatrixMixerValue(info[0], 0, info[1])
370             log.debug("gain %s[%d] is %d" % (info[0], info[1], val))
371             ctrl.setValue(val);
372             QObject.connect(ctrl, SIGNAL('valueChanged(int)'), self.updateGain)
373
374         self.updateStreamingState()
375         #log.debug("device streaming flag: %d" % (self.is_streaming))
376
377         self.update_timer = QTimer(self)
378         QObject.connect(self.update_timer, SIGNAL('timeout()'), self.status_update)
379         self.update_timer.start(1000)
380
381 # vim: et
Note: See TracBrowser for help on using the browser.