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

Revision 1691, 13.3 kB (checked in by arnonym, 11 years ago)

Only load the mixerwidgets that are actually needed.

To automate this, a rules are imposed:

The filename has to be the lower-case variant of the mixer name

This should speed up loading time of ffado-mixer. Please everybody test this and report any errors as I don't have all the devices to test everything fully.

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