root/trunk/libffado/support/mixer-qt4/ffado_panelmanager.py

Revision 1601, 13.7 kB (checked in by jwoithe, 15 years ago)

RME: implement mixer skeleton to allow device configuration functionality to be tested. Note that none of the included controls are functional yet.

Line 
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2005-2008 by Pieter Palmers
4 #               2007-2008 by Arnold Krille
5 #
6 # This file is part of FFADO
7 # FFADO = Free Firewire (pro-)audio drivers for linux
8 #
9 # FFADO is based upon FreeBoB.
10 #
11 # This program is free software: you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation, either version 3 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 #
24
25 from ffadomixer_config import * #FFADO_VERSION, FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH
26
27 from PyQt4.QtGui import QFrame, QWidget, QTabWidget, QVBoxLayout, QMainWindow, QIcon, QAction, qApp, QStyleOptionTabWidgetFrame
28 from PyQt4.QtCore import QTimer
29
30 from ffado_panelmanagerstatusui import Ui_PanelManagerStatusUI
31
32 from ffado_dbus_util import *
33 from ffado_registration import *
34
35 from ffado_configuration import DeviceList
36
37 from mixer_phase88 import *
38 from mixer_phase24 import *
39 from mixer_saffire import SaffireMixer
40 from mixer_saffirepro import SaffireProMixer
41 from mixer_audiofire import AudioFireMixer
42 from mixer_bcoaudio5 import *
43 from mixer_edirolfa66 import *
44 from mixer_edirolfa101 import *
45 from mixer_mackie_onyxmixer import *
46 from mixer_quatafire import *
47 from mixer_motu import *
48 from mixer_rme import *
49 from mixer_dummy import *
50 from mixer_global import GlobalMixer
51
52 import time
53
54 import logging
55 log = logging.getLogger('panelmanager')
56
57 use_generic = False
58 try:
59     from mixer_generic import *
60     log.info("The generic mixer is found, seems to be a developer using ffadomixer...")
61 except ImportError:
62     pass
63 else:
64     use_generic = True
65
66 # pseudo-guid
67 GUID_GENERIC_MIXER = 0
68
69
70 class HLine( QFrame ):
71     def __init__( self, parent ):
72         QFrame.__init__( self, parent )
73         self.setFrameShape( QFrame.HLine )
74         self.setLineWidth( 2 )
75         self.setMinimumHeight( 10 )
76
77 class PanelManagerStatus(QWidget, Ui_PanelManagerStatusUI):
78     def __init__(self, parent):
79         QWidget.__init__(self,parent)
80         self.setupUi(self)
81
82 class OwnTabWidget(QTabWidget):
83     def __init__(self,parent):
84         QTabWidget.__init__(self,parent)
85
86     def tabInserted(self,index):
87         self.checkTabBar()
88
89     def tabRemoved(self,index):
90         self.checkTabBar()
91
92     def checkTabBar(self):
93         if self.count()<2:
94             self.tabBar().hide()
95         else:
96             self.tabBar().show()
97
98 class PanelManager(QWidget):
99     def __init__(self, parent, devmgr=None):
100         QMainWindow.__init__(self, parent)
101         self.setObjectName("PanelManager")
102
103         # maps a device GUID to a QT panel
104         self.panels = {}
105
106         # a layout for ourselves
107         self.layout = QVBoxLayout(self)
108
109         # the tabs
110         self.tabs = OwnTabWidget(self)
111         self.tabs.hide()
112         self.layout.addWidget(self.tabs)
113
114         # a dialog that is shown during update
115         self.status = PanelManagerStatus(self)
116         self.layout.addWidget(self.status)
117         self.status.show()
118
119         self.devices = DeviceList( SYSTEM_CONFIG_FILE )
120         self.devices.updateFromFile( USER_CONFIG_FILE )
121
122         if devmgr is not None:
123             self.setManager(devmgr)
124
125     def setManager(self,devmgr):
126         self.devmgr = devmgr
127         self.devmgr.registerPreUpdateCallback(self.devlistPreUpdate)
128         self.devmgr.registerPostUpdateCallback(self.devlistPostUpdate)
129         self.devmgr.registerUpdateCallback(self.devlistUpdate)
130         self.devmgr.registerDestroyedCallback(self.devmgrDestroyed)
131         # create a timer to poll the panels
132         self.polltimer = QTimer()
133         self.connect( self.polltimer, SIGNAL('timeout()'), self.pollPanels )
134         self.polltimer.start( POLL_SLEEP_TIME_MSEC )
135
136         # create a timer to initialize the panel after the main form is shown
137         # since initialization can take a while
138         QTimer.singleShot( POLL_SLEEP_TIME_MSEC, self.updatePanels )
139
140         # live check timer
141         self.alivetimer = QTimer()
142         QObject.connect( self.alivetimer, SIGNAL('timeout()'), self.commCheck )
143         self.alivetimer.start( 2000 )
144
145     def count(self):
146         return self.tabs.count()
147
148     def pollPanels(self):
149         #log.debug("PanelManager::pollPanels()")
150         # only when not modifying the tabs
151         if self.tabs.isEnabled():
152             for guid in self.panels.keys():
153                 w = self.panels[guid]
154                 for child in w.children():
155                     #log.debug("poll child %s,%s" % (guid,child))
156                     if 'polledUpdate' in dir(child):
157                         try:
158                             child.polledUpdate()
159                         except:
160                             log.error("error in polled update")
161                             raise
162
163     def devlistPreUpdate(self):
164         log.debug("devlistPreUpdate")
165         self.tabs.setEnabled(False)
166         self.tabs.hide()
167         self.status.lblMessage.setText("Bus reconfiguration in progress, please wait...")
168         self.status.show()
169         #self.statusBar().showMessage("bus reconfiguration in progress...", 5000)
170
171     def devlistPostUpdate(self):
172         log.debug("devlistPostUpdate")
173         # this can fail if multiple busresets happen in fast succession
174         ntries = 10
175         while ntries > 0:
176             try:
177                 self.updatePanels()
178                 return
179             except:
180                 log.debug("devlistPostUpdate failed (%d)" % ntries)
181                 for guid in self.panels.keys():
182                     w = self.panels[guid]
183                     del self.panels[guid] # remove from the list
184                     idx = self.tabs.indexOf(w)
185                     self.tabs.removeTab(idx)
186                     del w # GC might also take care of that
187
188                 ntries = ntries - 1
189                 time.sleep(2) # sleep a few seconds
190
191         log.debug("devlistPostUpdate failed completely")
192         self.tabs.setEnabled(False)
193         self.tabs.hide()
194         self.status.lblMessage.setText("Error while reconfiguring. Please restart ffadomixer.")
195         self.status.show()
196
197
198     def devlistUpdate(self):
199         log.debug("devlistUpdate")
200
201     def devmgrDestroyed(self):
202         log.debug("devmgrDestroyed")
203         self.alivetimer.stop()
204         self.tabs.setEnabled(False)
205         self.tabs.hide()
206         self.status.lblMessage.setText("DBUS server was shut down, please restart it and restart ffadomixer...")
207         self.status.show()
208
209     def commCheck(self):
210         try:
211             nbDevices = self.devmgr.getNbDevices()
212         except:
213             log.debug("comms lost")
214             self.tabs.setEnabled(False)
215             self.tabs.hide()
216             self.status.lblMessage.setText("Failed to communicate with DBUS server. Please restart it and restart ffadomixer...")
217             self.status.show()
218             self.alivetimer.stop()
219
220     def updatePanels(self):
221         log.debug("PanelManager::updatePanels()")
222         nbDevices = self.devmgr.getNbDevices()
223         #self.statusBar().showMessage("Reconfiguring the mixer panels...")
224
225         # list of panels present
226         guids_with_tabs = self.panels.keys()
227
228         # build list of guids on the bus now
229         guids_present = []
230         guid_indexes = {}
231         for idx in range(nbDevices):
232             path = self.devmgr.getDeviceName(idx)
233             cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path)
234             guid = cfgrom.getGUID()
235             guids_present.append(guid)
236             guid_indexes[guid] = idx
237
238         # figure out what to remove
239         # the special panel (generic)
240         # that has (pseudo-)GUID 0
241         # is also automatically removed
242         to_remove = []
243         for guid in guids_with_tabs:
244             if not guid in guids_present:
245                 to_remove.append(guid)
246                 log.debug("going to remove %s" % str(guid))
247             else:
248                 log.debug("going to keep %s" % str(guid))
249
250         # figure out what to add
251         to_add = []
252         for guid in guids_present:
253             if not guid in guids_with_tabs:
254                 to_add.append(guid)
255                 log.debug("going to add %s" % str(guid))
256
257         # update the widget
258         for guid in to_remove:
259             w = self.panels[guid]
260             del self.panels[guid] # remove from the list
261             idx = self.tabs.indexOf(w)
262             self.tabs.removeTab(idx)
263             del w # GC might also take care of that
264
265         for guid in to_add:
266             # retrieve the device manager index
267             idx = guid_indexes[guid]
268             path = self.devmgr.getDeviceName(idx)
269             log.debug("Adding device %d: %s" % (idx, path))
270
271             cfgrom = ConfigRomInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path)
272             vendorId = cfgrom.getVendorId()
273             modelId = cfgrom.getModelId()
274             unitVersion = cfgrom.getUnitVersion()
275             guid = cfgrom.getGUID()
276             vendorName = cfgrom.getVendorName()
277             modelName = cfgrom.getModelName()
278             log.debug(" Found (%s, %X, %X) %s %s" % (str(guid), vendorId, modelId, vendorName, modelName))
279
280             # check whether this has already been registered at ffado.org
281             reg = ffado_registration(FFADO_VERSION, int(guid, 16),
282                                      vendorId, modelId,
283                                      vendorName, modelName)
284             reg.check_for_registration()
285
286             # The MOTU devices use unitVersion to differentiate models.  For the
287             # moment though we don't need to know precisely which model we're
288             # using.
289             if vendorId == 0x1f2:
290                 modelId = 0x00000000
291
292             # The RME devices use unitVersion to differentiate models.
293             # Therefore in the configuration file we use the config file's
294             # modelid field to store the unit version.  As a result we must
295             # override the modelId with the unit version here so the correct
296             # configuration file entry (and hense mixer widget) is identified.
297             if vendorId == 0xa35:
298                 modelId = unitVersion;
299
300             dev = self.devices.getDeviceById( vendorId, modelId )
301
302             w = QWidget( )
303             l = QVBoxLayout( w )
304
305             # create a control object
306             hw = ControlInterface(FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+'/DeviceManager/'+path)
307             clockselect = ClockSelectInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path )
308             samplerateselect = SamplerateSelectInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path )
309             streamingstatus = StreamingStatusInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path )
310             nickname = TextInterface( FFADO_DBUS_SERVER, FFADO_DBUS_BASEPATH+"/DeviceManager/"+path+"/Generic/Nickname" )
311
312             #
313             # Generic elements for all mixers follow here:
314             #
315             globalmixer = GlobalMixer( w )
316             globalmixer.configrom = cfgrom
317             globalmixer.clockselect = clockselect
318             globalmixer.samplerateselect = samplerateselect
319             globalmixer.streamingstatus = streamingstatus
320             globalmixer.nickname = nickname
321             globalmixer.hw = hw
322             globalmixer.initValues()
323             l.addWidget( globalmixer, 1 )
324
325             #
326             # Line to separate
327             #
328             l.addWidget( HLine( w ) )
329
330             #
331             # Specific (or dummy) mixer widgets get loaded in the following
332             #
333             if 'mixer' in dev and dev['mixer'] != None:
334                 mixerapp = dev['mixer']
335                 exec( "mixerwidget = "+mixerapp+"( w )" )
336             else:
337                 mixerwidget = DummyMixer( w )
338                 mixerapp = modelName+" (Dummy)"
339
340             #
341             # The same for all mixers
342             #
343             l.addWidget( mixerwidget, 10 )
344             mixerwidget.configrom = cfgrom
345             mixerwidget.clockselect = clockselect
346             mixerwidget.samplerateselect = samplerateselect
347             mixerwidget.streamingstatus = streamingstatus
348             mixerwidget.nickname = nickname
349             mixerwidget.hw = hw
350             if 'buildMixer' in dir(mixerwidget):
351                 mixerwidget.buildMixer()
352             if 'initValues' in dir(mixerwidget):
353                 mixerwidget.initValues()
354             if 'getDisplayTitle' in dir(mixerwidget):
355                 title = mixerwidget.getDisplayTitle()
356             else:
357                 title = mixerapp
358
359             globalmixer.setName(title)
360             self.tabs.addTab( w, title )
361             self.panels[guid] = w
362
363         # if there is no panel, add the no-device message
364         # else make sure it is not present
365         if self.count() == 0:
366             self.tabs.hide()
367             self.tabs.setEnabled(False)
368             self.status.lblMessage.setText("No supported device found.")
369             self.status.show()
370             #self.statusBar().showMessage("No supported device found.", 5000)
371         else:
372             self.tabs.show()
373             self.tabs.setEnabled(True)
374             self.status.hide()
375             #self.statusBar().showMessage("Configured the mixer for %i devices." % self.tabs.count())
376             if use_generic:
377                 #
378                 # Show the generic (development) mixer if it is available
379                 #
380                 w = GenericMixer( devmgr.bus, FFADO_DBUS_SERVER, mw )
381                 self.tabs.addTab( w, "Generic Mixer" )
382                 self.panels[GUID_GENERIC_MIXER] = w
383
384 # vim: et
Note: See TracBrowser for help on using the browser.