root/branches/libffado-2.0/support/mixer/ffadomixer.in

Revision 1325, 19.2 kB (checked in by wagi, 12 years ago)

Add mixer control panel for FA 101. Also update the FA 66
to look and behave identical to the FA 101.

  • Property svn:executable set to *
Line 
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2005-2007 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 import sys
26
27 # Add the path of the installed ffado-mixer-modules
28 sys.path.append( "$PYTHONDIR" )
29
30 from ffadomixer_config import FFADO_VERSION
31
32 import os
33 import time
34 import dbus
35
36 from qt import *
37
38 from ffado_registration import *
39
40 from mixer_phase88 import *
41 from mixer_phase24 import *
42 from mixer_saffire import *
43 from mixer_saffirele import *
44 from mixer_saffirepro import *
45 from mixer_audiofire import *
46 from mixer_bcoaudio5 import *
47 from mixer_edirolfa66 import *
48 from mixer_edirolfa101 import *
49 from mixer_mackie_generic import *
50 from mixer_quatafire import *
51 from mixer_motu import *
52 from mixer_dummy import *
53 from mixer_global import *
54
55 use_generic = False
56 try:
57     from mixer_generic import *
58     print "The generic mixer is found, seems to be a developer using ffadomixer..."
59 except ImportError:
60     pass
61 else:
62     use_generic = True
63
64 SupportedDevices=[
65     [(0x000aac, 0x00000003),'Phase88Control'],
66     [(0x000aac, 0x00000004),'Phase24Control'],
67     [(0x000aac, 0x00000007),'Phase24Control'],
68     [(0x00130e, 0x00000003),'SaffireProMixer'],
69     [(0x00130e, 0x00000006),'SaffireProMixer'],
70     [(0x00130e, 0x00000000),'SaffireMixer'],
71     [(0x001486, 0x00000af2),'AudioFireMixer'],
72     [(0x001486, 0x00000af4),'AudioFireMixer'],
73     [(0x001486, 0x00000af8),'AudioFireMixer'],
74     [(0x001486, 0x0000af12),'AudioFireMixer'],
75     [(0x0007f5, 0x00010049),'BCoAudio5Control'],
76     [(0x0040AB, 0x00010049),'EdirolFa66Control'],
77     [(0x0040AB, 0x00010048),'EdirolFa101Control'],
78     [(0x00000f, 0x00010067),'MackieGenericControl'],
79     [(0x000f1b, 0x00010064),'QuataFireMixer'],
80     [(0x0001f2, 0x00000000),'MotuMixer'],
81     ]
82
83 POLL_SLEEP_TIME_MSEC = 100 # 100ms
84
85 class ControlInterface:
86     def __init__(self, servername, basepath):
87         self.basepath=basepath
88         self.servername=servername
89         self.bus=dbus.SessionBus()
90
91     def setContignuous(self, subpath, v, idx=None):
92         try:
93             path = self.basepath + subpath
94             dev = self.bus.get_object(self.servername, path)
95             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Continuous')
96             if idx == None:
97                 dev_cont.setValue(v)
98             else:
99                 dev_cont.setValueIdx(idx,v)
100         except:
101             print "Failed to set Continuous %s on server %s" % (path, self.servername)
102
103     def getContignuous(self, subpath, idx=None):
104         try:
105             path = self.basepath + subpath
106             dev = self.bus.get_object(self.servername, path)
107             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Continuous')
108             if idx == None:
109                 return dev_cont.getValue()
110             else:
111                 return dev_cont.getValueIdx(idx)
112         except:
113             print "Failed to get Continuous %s on server %s" % (path, self.servername)
114             return 0
115
116     def setDiscrete(self, subpath, v):
117         try:
118             path = self.basepath + subpath
119             dev = self.bus.get_object(self.servername, path)
120             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Discrete')
121             dev_cont.setValue(v)
122         except:
123             print "Failed to set Discrete %s on server %s" % (path, self.servername)
124
125     def getDiscrete(self, subpath):
126         try:
127             path = self.basepath + subpath
128             dev = self.bus.get_object(self.servername, path)
129             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Discrete')
130             return dev_cont.getValue()
131         except:
132             print "Failed to get Discrete %s on server %s" % (path, self.servername)
133             return 0
134
135     def setText(self, subpath, v):
136         try:
137             path = self.basepath + subpath
138             dev = self.bus.get_object(self.servername, path)
139             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Text')
140             dev_cont.setValue(v)
141         except:
142             print "Failed to set Text %s on server %s" % (path, self.servername)
143
144     def getText(self, subpath):
145         try:
146             path = self.basepath + subpath
147             dev = self.bus.get_object(self.servername, path)
148             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Text')
149             return dev_cont.getValue()
150         except:
151             print "Failed to get Text %s on server %s" % (path, self.servername)
152             return 0
153
154     def setMatrixMixerValue(self, subpath, row, col, v):
155         try:
156             path = self.basepath + subpath
157             dev = self.bus.get_object(self.servername, path)
158             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.MatrixMixer')
159             dev_cont.setValue(row, col, v)
160         except:
161             print "Failed to set MatrixMixer %s on server %s" % (path, self.servername)
162
163     def getMatrixMixerValue(self, subpath, row, col):
164         try:
165             path = self.basepath + subpath
166             dev = self.bus.get_object(self.servername, path)
167             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.MatrixMixer')
168             return dev_cont.getValue(row, col)
169         except:
170             print "Failed to get MatrixMixer %s on server %s" % (path, self.servername)
171             return 0
172
173     def enumSelect(self, subpath, v):
174         try:
175             path = self.basepath + subpath
176             dev = self.bus.get_object(self.servername, path)
177             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum')
178             dev_cont.select(v)
179         except:
180             print "Failed to select %s on server %s" % (path, self.servername)
181
182     def enumSelected(self, subpath):
183         try:
184             path = self.basepath + subpath
185             dev = self.bus.get_object(self.servername, path)
186             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum')
187             return dev_cont.selected()
188         except:
189             print "Failed to get selected enum %s on server %s" % (path, self.servername)
190             return 0
191
192     def enumGetLabel(self, subpath, v):
193         try:
194             path = self.basepath + subpath
195             dev = self.bus.get_object(self.servername, path)
196             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum')
197             return dev_cont.getEnumLabel(v)
198         except:
199             print "Failed to get enum label %s on server %s" % (path, self.servername)
200             return 0
201
202     def enumCount(self, subpath):
203         try:
204             path = self.basepath + subpath
205             dev = self.bus.get_object(self.servername, path)
206             dev_cont = dbus.Interface(dev, dbus_interface='org.ffado.Control.Element.Enum')
207             return dev_cont.count()
208         except:
209             print "Failed to get enum count %s on server %s" % (path, self.servername)
210             return 0
211
212 class DeviceManagerInterface:
213     def __init__(self, servername, basepath):
214         self.basepath=basepath + '/DeviceManager'
215         self.servername=servername
216         self.bus=dbus.SessionBus()
217         self.dev = self.bus.get_object(self.servername, self.basepath)
218         self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.Container')
219         # signal reception does not work yet since we need a mainloop for that
220         # and qt3 doesn't provide one for python/dbus
221         #try:
222             #self.dev.connect_to_signal("Updated", self.updateSignal, \
223                                        #dbus_interface="org.ffado.Control.Element.Container", arg0=self)
224         #except dbus.DBusException:
225             #traceback.print_exc()
226
227     #def updateSignal(self):
228         #print ("Received update signal")
229
230     def getNbDevices(self):
231         return self.iface.getNbElements()
232     def getDeviceName(self, idx):
233         return self.iface.getElementName(idx)
234
235
236 class ConfigRomInterface:
237     def __init__(self, servername, devicepath):
238         self.basepath=devicepath + '/ConfigRom'
239         self.servername=servername
240         self.bus=dbus.SessionBus()
241         self.dev = self.bus.get_object(self.servername, self.basepath)
242         self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.ConfigRomX')
243     def getGUID(self):
244         return self.iface.getGUID()
245     def getVendorName(self):
246         return self.iface.getVendorName()
247     def getModelName(self):
248         return self.iface.getModelName()
249     def getVendorId(self):
250         return self.iface.getVendorId()
251     def getModelId(self):
252         return self.iface.getModelId()
253     def getUnitVersion(self):
254         return self.iface.getUnitVersion()
255
256 class ClockSelectInterface:
257     def __init__(self, servername, devicepath):
258         self.basepath=devicepath + '/Generic/ClockSelect'
259         self.servername=servername
260         self.bus=dbus.SessionBus()
261         self.dev = self.bus.get_object(self.servername, self.basepath)
262         self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.AttributeEnum')
263     def count(self):
264         return self.iface.count()
265     def select(self, idx):
266         return self.iface.select(idx)
267     def selected(self):
268         return self.iface.selected()
269     def getEnumLabel(self, idx):
270         return self.iface.getEnumLabel(idx)
271     def attributeCount(self):
272         return self.iface.attributeCount()
273     def getAttributeValue(self, idx):
274         return self.iface.getAttributeValue(idx)
275     def getAttributeName(self, idx):
276         return self.iface.getAttributeName(idx)
277
278 class SamplerateSelectInterface:
279     def __init__(self, servername, devicepath):
280         self.basepath=devicepath + '/Generic/SamplerateSelect'
281         self.servername=servername
282         self.bus=dbus.SessionBus()
283         self.dev = self.bus.get_object(self.servername, self.basepath)
284         self.iface = dbus.Interface(self.dev, dbus_interface='org.ffado.Control.Element.Enum')
285     def count(self):
286         return self.iface.count()
287     def select(self, idx):
288         return self.iface.select(idx)
289     def selected(self):
290         return self.iface.selected()
291     def getEnumLabel(self, idx):
292         return self.iface.getEnumLabel(idx)
293
294 class TextInterface:
295     def __init__(self, servername, basepath):
296         self.basepath=basepath
297         self.servername=servername
298         self.bus=dbus.SessionBus()
299         self.dev = self.bus.get_object( self.servername, self.basepath )
300         self.iface = dbus.Interface( self.dev, dbus_interface="org.ffado.Control.Element.Text" )
301
302     def text(self):
303         return self.iface.getValue()
304
305     def setText(self,text):
306         self.iface.setValue(text)
307
308 class HLine( QFrame ):
309     def __init__( self, parent ):
310         QFrame.__init__( self, parent )
311         self.setFrameShape( QFrame.HLine )
312         self.setLineWidth( 2 )
313         self.setMinimumHeight( 10 )
314
315 class PollUpdateWidgets(QObject):
316     def __init__(self, widgets):
317         QObject.__init__(self)
318         self.widgets = widgets
319
320     def updateWidgets(self):
321         for w in self.widgets:
322             if 'polledUpdate' in dir(w):
323                 try:
324                     w.polledUpdate()
325                 except:
326                     print "error in polled update"
327
328 if __name__ == "__main__":
329
330     server='org.ffado.Control'
331     basepath='/org/ffado/Control'
332
333     app = QApplication(sys.argv)
334
335     msg = QMessageBox()
336
337     repeat = 1
338     while repeat > 0:
339         try:
340             devmgr=DeviceManagerInterface(server, basepath)
341             nbDevices=devmgr.getNbDevices()
342             repeat -= 1
343         except dbus.DBusException, ex:
344             print "\n"
345             print "==========================================================="
346             print "ERROR: Could not communicate with the FFADO DBus service..."
347             print "==========================================================="
348             print "\n"
349             tmp = msg.question( msg, "FFADO-DBus not found", "<qt><b>The connection to FFADOs DBus service could not be established.</b><p>Probably you didn't start the ffado-dbus-server. Should I try this now?</qt>", QMessageBox.Yes, QMessageBox.No )
350             if tmp == 4:
351                 sys.exit(-1)
352             else:
353                 os.spawnlp( os.P_NOWAIT, "ffado-dbus-server" )
354                 nb_checks = 20
355                 while nb_checks > 0:
356                     nb_checks = nb_checks - 1
357                     try:
358                         devmgr=DeviceManagerInterface(server, basepath)
359                         nbDevices=devmgr.getNbDevices()
360                         nb_checks = 0
361                         repeat = 0
362                     except dbus.DBusException, ex:
363                         time.sleep( 1 )
364
365     if nbDevices == 0:
366         print "No supported device found..."
367         msg.information( msg, "No mixer found", "No devices with mixer support discovered." )
368         sys.exit( -1 )
369
370     mw = QTabWidget()
371
372     mixerwidgets = []
373
374     for idx in range(nbDevices):
375         path=devmgr.getDeviceName(idx)
376         print "Found device %d: %s" % (idx, path)
377
378         cfgrom = ConfigRomInterface(server, basepath+'/DeviceManager/'+path)
379         vendorId = cfgrom.getVendorId()
380         modelId = cfgrom.getModelId()
381         unitVersion = cfgrom.getUnitVersion()
382         GUID = cfgrom.getGUID()
383         vendorName = cfgrom.getVendorName()
384         modelName = cfgrom.getModelName()
385         print " Found (%s, %X, %X) %s %s" % (str(GUID), vendorId, modelId, vendorName, modelName)
386
387         # check whether this has already been registered at ffado.org
388         reg = ffado_registration(FFADO_VERSION, int(GUID, 16),
389                                  vendorId, modelId,
390                                  vendorName, modelName)
391         reg.check_for_registration()
392
393         thisdev=(vendorId, modelId);
394         # The MOTU devices use unitVersion to differentiate models.  For the
395         # moment thought we don't need to know precisely which model we're
396         # using.
397         if vendorId == 0x1f2:
398             thisdev=(vendorId, 0x00000000)
399
400         dev = None
401         for d in SupportedDevices:
402             if d[0] == thisdev:
403                 dev = d
404
405         w = QWidget( mw )
406         l = QVBoxLayout( w )
407
408         # create a control object
409         hw = ControlInterface(server, basepath+'/DeviceManager/'+path)
410         clockselect = ClockSelectInterface( server, basepath+"/DeviceManager/"+path )
411         samplerateselect = SamplerateSelectInterface( server, basepath+"/DeviceManager/"+path )
412         nickname = TextInterface( server, basepath+"/DeviceManager/"+path+"/Generic/Nickname" )
413
414         #
415         # Generic elements for all mixers follow here:
416         #
417         tmp = GlobalMixer( w )
418         tmp.configrom = cfgrom
419         tmp.clockselect = clockselect
420         tmp.samplerateselect = samplerateselect
421         tmp.nickname = nickname
422         tmp.hw = hw
423         tmp.initValues()
424         l.addWidget( tmp, 1 )
425
426         #
427         # Line to separate
428         #
429         l.addWidget( HLine( w ) )
430
431         #
432         # Specific (or dummy) mixer widgets get loaded in the following
433         #
434         if dev != None:
435             mixerapp = dev[1]
436             if vendorId == 0x00130e:
437                 is_saffirele = False
438                 is_saffirepro = False
439
440                 # hack for the focusrite devices
441                 # Saffire:        0x130e010001????
442                 # SaffireLE:    0x130e010004????
443                 if modelId == 0x00000000:
444                     if int(GUID, 16) >= 0x130e0100040000:
445                         is_saffirele = True
446                         print "Found SaffireLE GUID"
447                     else:
448                         is_saffirele = False
449                         print "Found Saffire GUID"
450
451                 # different panel for different clock frequency
452                 selected = samplerateselect.selected()
453                 samplerate = int(samplerateselect.getEnumLabel( selected ))
454
455                 # adat on PRO26 makes a difference
456                 have_adat = False
457                 if modelId == 0x00000003: # PRO26
458                     is_saffirepro = True
459                     state = hw.getDiscrete('/Control/ADATDisable')
460                     if state:
461                         have_adat = False
462                         print "detected PRO26, ADAT disabled"
463                     else:
464                         have_adat = True
465                         print "detected PRO26, ADAT enabled"
466                 elif modelId == 0x00000006: # PRO10
467                     is_saffirepro = True
468
469                 suffix = ''
470                 if samplerate <= 48000:
471                     suffix = "Large"
472                 # on the saffire pro 26, the large panel can be used
473                 # at 96k when adat is disabled. pro10 = pro26 w/o ADAT
474                 elif samplerate <= 96000 and is_saffirepro and have_adat:
475                     suffix = "Small"
476                 elif samplerate <= 96000 and is_saffirepro:
477                     suffix = "Large"
478                 # higher samplerates need the small panel
479                 else:
480                     suffix = "Small"
481
482                 if is_saffirepro:
483                     mixerapp = "SaffireProMixer" + suffix
484                 elif is_saffirele:
485                     mixerapp = "SaffireLEMixer" + suffix
486                 else:
487                     mixerapp = "SaffireMixer"
488
489             exec( "mixerwidget = "+mixerapp+"( w )" )
490
491         else:
492             mixerwidget = DummyMixer( w )
493             mixerapp = modelName+" (Dummy)"
494
495         #
496         # The same for all mixers
497         #
498         l.addWidget( mixerwidget, 10 )
499         mixerwidget.configrom = cfgrom
500         mixerwidget.clockselect = clockselect
501         mixerwidget.samplerateselect = samplerateselect
502         mixerwidget.nickname = nickname
503         mixerwidget.hw = hw
504         if 'buildMixer' in dir(mixerwidget):
505             mixerwidget.buildMixer()
506         if 'initValues' in dir(mixerwidget):
507             mixerwidget.initValues()
508         mixerwidgets.append(mixerwidget)
509         mw.addTab( w, mixerapp )
510
511     #
512     # Show the generic (development) mixer if it is available
513     #
514     if nbDevices > 0 and use_generic:
515         mw.addTab( GenericMixer( devmgr.bus, server, mw ), "Generic Mixer" )
516
517     #
518     # Only really show the mainwindow and start the mixer when at least on mixer is shown
519     #
520     if mw.count() > 0:
521         puw = PollUpdateWidgets(mixerwidgets)
522         timer = QTimer(puw)
523         QObject.connect( timer, SIGNAL('timeout()'), puw.updateWidgets );
524         timer.start( POLL_SLEEP_TIME_MSEC );
525
526         # Adjust size of mixer window to the requirements of the selected device mixer(s)
527         mw.adjustSize()
528
529         mw.show()
530
531         QObject.connect(app,SIGNAL("lastWindowClosed()"),app,SLOT("quit()"))
532
533         app.exec_loop()
Note: See TracBrowser for help on using the browser.