2 # Copyright (C) 2009, 2011 by Jonathan Woithe
4 # This file is part of FFADO
5 # FFADO = Free Firewire (pro-)audio drivers for linux
7 # FFADO is based upon FreeBoB.
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.
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.
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/>.
23 from PyQt4
import QtGui
25 from PyQt4
.QtCore
import SIGNAL
, SLOT
, QObject
, Qt
, QTimer
26 from PyQt4
.QtGui
import QWidget
, QApplication
27 from ffado
.config
import *
29 from ffado
.widgets
.matrixmixer
import MatrixMixer
32 log
= logging
.getLogger('rme')
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
40 def __init__(self
,parent
= None):
41 QWidget
.__init
__(self
,parent
)
42 uicLoad("ffado/mixer/rme", self
)
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],
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'],
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],
68 self
.level_in_lo_gain
: ['/Control/Input_level', 0],
69 self
.level_in_p4dBu
: ['/Control/Input_level', 2],
70 self
.level_in_m10dBV
: ['/Control/Input_level', 1],
72 self
.level_out_hi_gain
: ['/Control/Output_level', 2],
73 self
.level_out_p4dBu
: ['/Control/Output_level', 1],
74 self
.level_out_m10dBV
: ['/Control/Output_level', 0],
76 self
.spdif_input_coax
: ['/Control/SPDIF_input_mode', 0],
77 self
.spdif_input_optical
: ['/Control/SPDIF_input_mode', 1],
79 self
.phones_hi_gain
: ['/Control/Phones_level', 0],
80 self
.phones_p4dBu
: ['/Control/Phones_level', 1],
81 self
.phones_m10dBV
: ['/Control/Phones_level', 2],
83 self
.clock_mode_autosync
: ['/Control/Clock_mode', 1],
84 self
.clock_mode_master
: ['/Control/Clock_mode', 0],
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],
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],
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],
107 self
.ff800_ch1_src
: ['/Control/Chan1_source'],
108 self
.ff800_ch7_src
: ['/Control/Chan7_source'],
109 self
.ff800_ch8_src
: ['/Control/Chan8_source'],
112 self
.CommandButtons
={
113 self
.control_load
: ['/Control/Flash_control', 0],
114 self
.control_save
: ['/Control/Flash_control', 1],
115 self
.mixer_load
: ['/Control/Flash_control', 2],
116 self
.mixer_save
: ['/Control/Flash_control', 3],
117 self
.mixer_preset_ffado_default
: ['/Control/Mixer_preset', 0],
120 # Other mixer variables
121 self
.is_streaming
= 0
126 # Public slot: update phantom power hardware switches
127 def updatePhantomSwitch(self
, a0
):
128 sender
= self
.sender()
129 # Value is the phantom switch value, with a corresponding enable
130 # bit in the high 16 bit word
131 val
= (a0
<< self
.PhantomSwitches
[sender
][1]) |
(0x00010000 << self
.PhantomSwitches
[sender
][1])
132 log
.debug("phantom switch %d set to %d" % (self
.PhantomSwitches
[sender
][1], a0
))
133 self
.hw
.setDiscrete(self
.PhantomSwitches
[sender
][0], val
)
135 # Public slot: update generic switches
136 def updateSwitch(self
, a0
):
137 sender
= self
.sender()
138 log
.debug("switch %s set to %d" % (self
.Switches
[sender
][0], a0
))
139 self
.hw
.setDiscrete(self
.Switches
[sender
][0], a0
)
141 # Public slot: update generic radiobuttons
142 def updateRadiobutton(self
, a0
):
143 sender
= self
.sender()
145 # Only change the control state on a button being "checked"
146 log
.debug("radiobutton group %s set to %d" % (self
.Radiobuttons
[sender
][0], self
.Radiobuttons
[sender
][1]))
147 self
.hw
.setDiscrete(self
.Radiobuttons
[sender
][0], self
.Radiobuttons
[sender
][1])
149 def updateCheckboxes(self
, a0
):
150 sender
= self
.sender()
151 val
= self
.hw
.getDiscrete(self
.Checkboxes
[sender
][0]);
153 val
= val | self
.Checkboxes
[sender
][1]
155 val
= val
& ~self
.Checkboxes
[sender
][1]
156 log
.debug("checkbox group %s set to %d" % (self
.Checkboxes
[sender
][0], val
));
157 self
.hw
.setDiscrete(self
.Checkboxes
[sender
][0], val
)
159 # Public slot: update gains
160 def updateGain(self
, a0
):
161 sender
= self
.sender()
162 log
.debug("gain %s[%d] set to %d" % (self
.Gains
[sender
][0], self
.Gains
[sender
][1], a0
))
163 self
.hw
.setMatrixMixerValue(self
.Gains
[sender
][0], 0, self
.Gains
[sender
][1], a0
)
165 def updateBandwidthLimit(self
, a0
):
166 # Account for the "No ADAT-2" item which will not be present on
168 if (self
.model
==RME_MODEL_FF400
and a0
>0):
170 # log.debug("limit update: %d" % (a0));
171 self
.hw
.setDiscrete('/Control/Bandwidth_limit', a0
);
173 # Public slot: send command
174 def sendCommand(self
, a0
):
175 sender
= self
.sender()
176 log
.debug("command %d sent to %s" % (self
.CommandButtons
[sender
][1], self
.CommandButtons
[sender
][0]))
177 self
.hw
.setDiscrete(self
.CommandButtons
[sender
][0], self
.CommandButtons
[sender
][1])
179 # If mixer values have been reloaded, refresh the mixer GUI. This
180 # will also commit the new values to the hardware via the "changed"
181 # signal handlers of the mixer elements.
182 if (self
.CommandButtons
[sender
][1] == 2):
183 self
.inputmatrix
.refreshValues()
184 self
.outputmatrix
.refreshValues()
185 self
.playbackmatrix
.refreshValues()
187 # If settings have been reloaded from flash, refresh the GUI. The
188 # settings will be made active in the hardware via the "changed"
189 # signal handlers of the respective GUI control widgets.
190 if (self
.CommandButtons
[sender
][1] == 0):
191 self
.getValuesFromFF()
193 def updateCombo(self
, a0
):
194 sender
= self
.sender()
195 log
.debug("combo %s set to %d" % (self
.Combos
[sender
][0], a0
))
196 self
.hw
.setDiscrete(self
.Combos
[sender
][0], a0
)
198 # Enable the limiter control only when the front source is active
199 if (sender
== self
.ff800_ch1_src
):
200 self
.ch1_instr_limiter
.setEnabled(a0
==0)
202 def updateStreamingState(self
):
203 ss
= self
.streamingstatus
.selected()
204 ss_txt
= self
.streamingstatus
.getEnumLabel(ss
)
206 self
.is_streaming
= True
208 self
.is_streaming
= False
209 if (self
.last_streaming_state
!= self
.is_streaming
):
210 self
.bandwidth_limit
.setEnabled(not(self
.is_streaming
));
211 self
.control_load
.setEnabled(not(self
.is_streaming
));
212 self
.last_streaming_state
= self
.is_streaming
214 def status_update(self
):
215 # log.debug("timer event")
216 self
.updateStreamingState()
217 clk_mode
= ['Master', 'Slave']
218 src_str
= ['None', 'ADAT 1', 'ADAT 2', 'SPDIF', 'Wordclock', 'TCO']
219 sync_stat
= ['No lock', 'Locked', 'Synced']
220 sysclock_mode
= self
.hw
.getDiscrete('/Control/sysclock_mode')
221 sysclock_freq
= self
.hw
.getDiscrete('/Control/sysclock_freq')
222 autosync_freq
= self
.hw
.getDiscrete('/Control/autosync_freq')
223 autosync_src
= self
.hw
.getDiscrete('/Control/autosync_src')
224 sync_status
= self
.hw
.getDiscrete('/Control/sync_status')
225 spdif_freq
= self
.hw
.getDiscrete('/Control/spdif_freq')
226 self
.sysclock_freq
.setText("%d Hz" % (sysclock_freq
))
227 self
.sysclock_mode
.setText(clk_mode
[sysclock_mode
])
228 self
.autosync_freq
.setText("%d Hz" % (autosync_freq
))
229 self
.autosync_src
.setText(src_str
[autosync_src
])
230 self
.sync_check_adat1_status
.setText(sync_stat
[sync_status
& 0x03])
231 self
.sync_check_adat2_status
.setText(sync_stat
[(sync_status
>> 2) & 0x03])
232 self
.sync_check_spdif_status
.setText(sync_stat
[(sync_status
>> 4) & 0x03])
233 self
.sync_check_wclk_status
.setText(sync_stat
[(sync_status
>> 6) & 0x03])
234 self
.sync_check_tco_status
.setText(sync_stat
[(sync_status
>> 8) & 0x03])
235 self
.spdif_freq
.setText("%d Hz" % (spdif_freq
))
237 # Hide and disable a control
238 def disable_hide(self
,widget
):
239 for w
in widget
.children():
240 if isinstance(w
, QWidget
):
244 widget
.setEnabled(False)
246 def setupSignals(self
):
248 # Connect signal handlers for all command buttons
249 for ctrl
, info
in self
.CommandButtons
.iteritems():
250 if (not(ctrl
.isEnabled())):
252 QObject
.connect(ctrl
, SIGNAL('clicked(bool)'), self
.sendCommand
)
254 for ctrl
, info
in self
.Combos
.iteritems():
255 if (not(ctrl
.isEnabled())):
257 QObject
.connect(ctrl
, SIGNAL('currentIndexChanged(int)'), self
.updateCombo
)
259 QObject
.connect(self
.bandwidth_limit
, SIGNAL('currentIndexChanged(int)'), self
.updateBandwidthLimit
)
261 # Get current hardware values and connect GUI element signals to
262 # their respective slots
263 for ctrl
, info
in self
.PhantomSwitches
.iteritems():
264 if (not(ctrl
.isEnabled())):
266 QObject
.connect(ctrl
, SIGNAL('toggled(bool)'), self
.updatePhantomSwitch
)
268 for ctrl
, info
in self
.Switches
.iteritems():
269 if (not(ctrl
.isEnabled())):
271 QObject
.connect(ctrl
, SIGNAL('toggled(bool)'), self
.updateSwitch
)
273 for ctrl
, info
in self
.Radiobuttons
.iteritems():
274 if (not(ctrl
.isEnabled())):
276 QObject
.connect(ctrl
, SIGNAL('toggled(bool)'), self
.updateRadiobutton
)
278 for ctrl
, info
in self
.Checkboxes
.iteritems():
279 if (not(ctrl
.isEnabled())):
281 QObject
.connect(ctrl
, SIGNAL('toggled(bool)'), self
.updateCheckboxes
)
283 for ctrl
, info
in self
.Gains
.iteritems():
284 if (not(ctrl
.isEnabled())):
286 QObject
.connect(ctrl
, SIGNAL('valueChanged(int)'), self
.updateGain
)
288 # Obtain control values from the Fireface and make the GUI reflect these
289 def getValuesFromFF(self
):
290 for ctrl
, info
in self
.Combos
.iteritems():
291 if (not(ctrl
.isEnabled())):
293 val
= self
.hw
.getDiscrete(info
[0])
294 log
.debug("combo %s is %d" % (info
[0], val
));
295 ctrl
.setCurrentIndex(val
);
297 # Set the bandwidth limit control to reflect the current device
298 # setting, allowing for the additional "No ADAT-2" item which is
299 # present on the FF800.
300 val
= self
.hw
.getDiscrete('/Control/Bandwidth_limit')
301 if (self
.model
==RME_MODEL_FF400
and val
>1):
303 self
.bandwidth_limit
.setCurrentIndex(val
);
305 # Get current hardware values
306 for ctrl
, info
in self
.PhantomSwitches
.iteritems():
307 if (not(ctrl
.isEnabled())):
309 val
= (self
.hw
.getDiscrete(info
[0]) >> info
[1]) & 0x01
310 log
.debug("phantom switch %d is %d" % (info
[1], val
))
312 ctrl
.setChecked(True)
314 ctrl
.setChecked(False)
316 for ctrl
, info
in self
.Switches
.iteritems():
317 if (not(ctrl
.isEnabled())):
319 val
= self
.hw
.getDiscrete(info
[0])
320 log
.debug("switch %s is %d" % (info
[0], val
))
322 ctrl
.setChecked(True)
324 ctrl
.setChecked(False)
326 for ctrl
, info
in self
.Radiobuttons
.iteritems():
327 if (not(ctrl
.isEnabled())):
329 # This is a touch wasteful since it means we retrieve the control
330 # value once per radio button rather than once per radio button
331 # group. In time we might introduce radiobutton groupings in the
332 # self.* datastructures to avoid this, but for the moment this is
334 val
= self
.hw
.getDiscrete(info
[0])
340 log
.debug("Radiobutton %s[%d] is %d" % (info
[0], info
[1], val
))
342 # Make sure the Limiter control can receive a value
343 if (self
.ff800_ch1_src
.isEnabled()):
344 self
.ch1_instr_limiter
.setEnabled(1)
346 for ctrl
, info
in self
.Checkboxes
.iteritems():
347 if (not(ctrl
.isEnabled())):
349 # This is a touch wasteful since it means we retrieve the control
350 # value once per checkbox button rather than once per checkbox
351 # group. In time we might introduce checkbox groupings in the
352 # self.* datastructures to avoid this, but for the moment this is
354 val
= self
.hw
.getDiscrete(info
[0])
360 log
.debug("Checkbox %s[%d] is %d" % (info
[0], info
[1], val
))
362 # The limiter (a checkbox) can only be controlled if the front
363 # source is selected for channel 1.
364 ch1_src
= self
.ff800_ch1_src
.currentIndex()
365 if (self
.ff800_ch1_src
.isEnabled()):
366 self
.ch1_instr_limiter
.setEnabled(ch1_src
==0)
368 for ctrl
, info
in self
.Gains
.iteritems():
369 if (not(ctrl
.isEnabled())):
371 val
= self
.hw
.getMatrixMixerValue(info
[0], 0, info
[1])
372 log
.debug("gain %s[%d] is %d" % (info
[0], info
[1], val
))
377 def initValues(self
):
379 # print self.hw.servername
380 # print self.hw.basepath
381 self
.inputmatrix
= MatrixMixer(self
.hw
.servername
, self
.hw
.basepath
+"/Mixer/InputFaders", self
, "Columns_are_inputs", 0x8000, self
.hw
.basepath
+"/Mixer/InputMutes", self
.hw
.basepath
+"/Mixer/InputInverts", True)
382 layout
= QtGui
.QVBoxLayout()
383 layout
.addWidget(self
.inputmatrix
)
384 self
.mixer
.setLayout(layout
)
386 self
.playbackmatrix
= MatrixMixer(self
.hw
.servername
, self
.hw
.basepath
+"/Mixer/PlaybackFaders", self
, "Columns_are_inputs", 0x8000, self
.hw
.basepath
+"/Mixer/PlaybackMutes", self
.hw
.basepath
+"/Mixer/PlaybackInverts", True)
387 layout
= QtGui
.QVBoxLayout()
388 layout
.addWidget(self
.playbackmatrix
)
389 self
.playbackmixer
.setLayout(layout
)
391 self
.outputmatrix
= MatrixMixer(self
.hw
.servername
, self
.hw
.basepath
+"/Mixer/OutputFaders", self
, "Columns_are_inputs", 0x8000, self
.hw
.basepath
+"/Mixer/OutputMutes", None, True)
392 layout
= QtGui
.QVBoxLayout()
394 # This is a bit of a hack, but it works to ensure this single-row
395 # matrix mixer doesn't fill the entire screen but also doesn't end
396 # up with a pointless scrollbar. The matrix mixer's minimum height
397 # is 0 according to minimumHeight(), which is probably the
398 # fundamental issue here; however, I've already wasted too much time
399 # trying to get this to work so if the hack is effective we'll run
401 self
.outputmatrix
.setMinimumHeight(150)
402 layout
.addWidget(self
.outputmatrix
, 0, Qt
.AlignTop
)
403 self
.outputmixer
.setLayout(layout
)
405 self
.is_streaming
= False
406 self
.last_streaming_state
= False
408 # Disable the "load settings" button if streaming is active. Its
409 # enable state will be kept up to date by updateStreamingState().
410 self
.control_load
.setEnabled(not(self
.is_streaming
))
412 # Also disable other controls which are not yet implemented.
413 self
.mixer_preset_ffado_default
.setEnabled(False)
415 # Retrieve other device settings as needed and customise the UI
416 # based on these options.
417 self
.model
= self
.hw
.getDiscrete('/Control/Model')
418 log
.debug("device model identifier: %d" % (self
.model
))
419 self
.tco_present
= self
.hw
.getDiscrete('/Control/TCO_present')
420 log
.debug("device has TCO: %d" % (self
.tco_present
))
421 #self.sample_rate = self.hw.getDiscrete('/Mixer/Info/SampleRate')
422 #log.debug("device sample rate: %d" % (self.sample_rate))
424 # The Fireface-400 only has 2 phantom-capable channels
425 if (self
.model
== RME_MODEL_FF400
):
426 self
.disable_hide(self
.phantom_2
)
427 self
.disable_hide(self
.phantom_3
)
429 self
.phantom_0
.setText("Mic 7")
430 self
.phantom_1
.setText("Mic 8")
431 self
.phantom_2
.setText("Mic 9")
432 self
.phantom_3
.setText("Mic 10")
434 # Instrument options, input jack selection controls and an ADAT2
435 # input are applicable only to the FF800
436 if (self
.model
!= RME_MODEL_FF800
):
437 self
.instrument_options_group
.setEnabled(False)
438 self
.input_plug_select_group
.setEnabled(False)
439 self
.sync_ref_adat2
.setEnabled(False)
440 self
.sync_check_adat2_label
.setEnabled(False)
441 self
.sync_check_adat2_status
.setEnabled(False)
443 if (not(self
.tco_present
)):
444 self
.sync_check_tco_label
.setEnabled(False)
445 self
.sync_check_tco_status
.setEnabled(False)
446 self
.sync_ref_tco
.setEnabled(False)
448 # Only the FF400 has specific channel 3/4 options, input gain
449 # controls and switchable phones level
450 if (self
.model
!= RME_MODEL_FF400
):
451 # Hide the upper-level frame (and everything in it) to ensure it
452 # requests no vertical space when its contents aren't needed.
453 self
.disable_hide(self
.igains_chan34_opts_frame
)
454 self
.phones_level_group
.setEnabled(False)
456 # Add the "No ADAT-2" item to the bandwidth limit control if the
457 # device is not a FF400.
458 if (self
.model
!= RME_MODEL_FF400
):
459 self
.bandwidth_limit
.insertItem(1, "No ADAT-2")
461 self
.getValuesFromFF()
464 # Ensure the limiter checkbox has a signal handler associated with
465 # it. If getValuesFromFF() disabled it because the front input was
466 # not selected, setupSignals() would not have configured a handler.
467 if (not(self
.ch1_instr_limiter
.isEnabled())):
468 QObject
.connect(self
.ch1_instr_limiter
, SIGNAL('toggled(bool)'), self
.updateCheckboxes
)
470 self
.updateStreamingState()
471 #log.debug("device streaming flag: %d" % (self.is_streaming))
473 self
.update_timer
= QTimer(self
)
474 QObject
.connect(self
.update_timer
, SIGNAL('timeout()'), self
.status_update
)
475 self
.update_timer
.start(1000)
477 def saveSettings(self
, indent
):
480 saveString
.append('%s<inputmatrix>\n' % indent
)
481 saveString
.extend(self
.inputmatrix
.saveSettings(idt
))
482 # Do not forget to mention the adopted rule for matrix columns mixer
483 # This might be useful for future import function
484 saveString
.append("%s <col_rule>\n" % indent
)
485 saveString
.append("%s Columns_are_inputs\n" % indent
)
486 saveString
.append("%s </col_rule>\n" % indent
)
487 saveString
.append('%s</inputmatrix>\n' % indent
)
489 saveString
.append('%s<playbackmatrix>\n' % indent
)
490 saveString
.extend(self
.inputmatrix
.saveSettings(idt
))
491 # Do not forget to mention the adopted rule for matrix columns mixer
492 # This might be useful for future import function
493 saveString
.append("%s <col_rule>\n" % indent
)
494 saveString
.append("%s Columns_are_inputs\n" % indent
)
495 saveString
.append("%s </col_rule>\n" % indent
)
496 saveString
.append('%s</playbackmatrix>\n' % indent
)
498 saveString
.append('%s<outputmatrix>\n' % indent
)
499 saveString
.extend(self
.inputmatrix
.saveSettings(idt
))
500 # Do not forget to mention the adopted rule for matrix columns mixer
501 # This might be useful for future import function
502 saveString
.append("%s <col_rule>\n" % indent
)
503 saveString
.append("%s Columns_are_inputs\n" % indent
)
504 saveString
.append("%s </col_rule>\n" % indent
)
505 saveString
.append('%s</outputmatrix>\n' % indent
)
508 def readSettings(self
, readString
):
510 idxb
= readString
.index('<inputmatrix>')
511 idxe
= readString
.index('</inputmatrix>')
513 log
.debug("No Input matrix settings found")
519 for s
in readString
[idxb
+1:idxe
]:
520 stringMixer
.append(s
)
521 # When trying to import from a different device, the rule for column interpretation is
522 # not necessarily the same
524 idx
= stringMixer
.index('<col_rule>')
526 log
.debug('Do not know how to handle column versus input/output')
528 transpose_coeff
= False
530 if stringMixer
[idx
+1].find("Columns_are_inputs") == -1:
531 log
.debug('Transposing the matrix coefficient; you are importing, are not you ?')
532 transpose_coeff
= True
533 if self
.inputmatrix
.readSettings(stringMixer
, transpose_coeff
):
534 log
.debug("Input matrix settings modified")
538 idxb
= readString
.index('<playbackmatrix>')
539 idxe
= readString
.index('</playbackmatrix>')
541 log
.debug("No Plaback matrix settings found")
547 for s
in readString
[idxb
+1:idxe
]:
548 stringMixer
.append(s
)
549 # When trying to import from a different device, the rule for column interpretation is
550 # not necessarily the same
552 idx
= stringMixer
.index('<col_rule>')
554 log
.debug('Do not know how to handle column versus input/output')
556 transpose_coeff
= False
558 if stringMixer
[idx
+1].find("Columns_are_inputs") == -1:
559 log
.debug('Transposing the matrix coefficient; you are importing, are not you ?')
560 transpose_coeff
= True
561 if self
.playbackmatrix
.readSettings(stringMixer
, transpose_coeff
):
562 log
.debug("Plaback matrix settings modified")
566 idxb
= readString
.index('<outputmatrix>')
567 idxe
= readString
.index('</outputmatrix>')
569 log
.debug("No Output matrix settings found")
575 for s
in readString
[idxb
+1:idxe
]:
576 stringMixer
.append(s
)
577 # When trying to import from a different device, the rule for column interpretation is
578 # not necessarily the same
580 idx
= stringMixer
.index('<col_rule>')
582 log
.debug('Do not know how to handle column versus input/output')
584 transpose_coeff
= False
586 if stringMixer
[idx
+1].find("Columns_are_inputs") == -1:
587 log
.debug('Transposing the matrix coefficient; you are importing, are not you ?')
588 transpose_coeff
= True
589 if self
.outputmatrix
.readSettings(stringMixer
, transpose_coeff
):
590 log
.debug("Output matrix settings modified")