1 |
# |
---|
2 |
# presonus_inspire1394.py - Qt4/FFADO application for Presonus Inspire1394 |
---|
3 |
# Copyright (C) 2014 Takashi Sakamoto <o-takashi@sakamocchi.jp> |
---|
4 |
# |
---|
5 |
# This file is part of FFADO |
---|
6 |
# FFADO = Free Firewire (pro-)audio drivers for linux |
---|
7 |
# |
---|
8 |
# This program is free software: you can redistribute it and/or modify |
---|
9 |
# it under the terms of the GNU General Public License as published by |
---|
10 |
# the Free Software Foundation, either version 2 of the License, or |
---|
11 |
# version 3 of the License. |
---|
12 |
# |
---|
13 |
# This program is distributed in the hope that it will be useful, |
---|
14 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
16 |
# GNU General Public License for more details. |
---|
17 |
# |
---|
18 |
# You should have received a copy of the GNU General Public License |
---|
19 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
20 |
# |
---|
21 |
|
---|
22 |
from PyQt4 import QtGui, QtCore |
---|
23 |
from PyQt4.QtCore import QObject, Qt |
---|
24 |
from PyQt4.QtGui import QWidget, QHBoxLayout, QVBoxLayout, QGridLayout |
---|
25 |
from PyQt4.QtGui import QGroupBox, QLabel, QSizePolicy, QSlider, QDial, QComboBox, QToolButton |
---|
26 |
from math import log10 |
---|
27 |
from ffado.config import * |
---|
28 |
|
---|
29 |
class Presonus_Inspire1394(QWidget): |
---|
30 |
# feature_id/name |
---|
31 |
inputs = [[1, "Analog in 1/2"], |
---|
32 |
[2, "Analog in 3/4"]] |
---|
33 |
|
---|
34 |
# feature_id/name |
---|
35 |
mixer_src = [[3, "Analog in 1/2"], |
---|
36 |
[4, "Analog in 3/4"], |
---|
37 |
[5, "Stream in 1/2"]] |
---|
38 |
|
---|
39 |
# feature id/name |
---|
40 |
outputs = [[6, "Analog out 1/2"], |
---|
41 |
[7, "HP out 1/2"]] |
---|
42 |
|
---|
43 |
# selector_id/sources |
---|
44 |
out_src = [1, ["Mixer out 1/2", "Stream in 1/2"]] |
---|
45 |
|
---|
46 |
def __init__(self, parent=None): |
---|
47 |
QWidget.__init__(self, parent) |
---|
48 |
|
---|
49 |
def getDisplayTitle(self): |
---|
50 |
return 'Inspire1394' |
---|
51 |
|
---|
52 |
def buildMixer(self): |
---|
53 |
self.Selectors = {} |
---|
54 |
self.Volumes = {} |
---|
55 |
self.Preamps = {} |
---|
56 |
self.Pannings = {} |
---|
57 |
self.Mutes = {} |
---|
58 |
|
---|
59 |
plain_layout = QHBoxLayout(self) |
---|
60 |
|
---|
61 |
left = QGroupBox(self) |
---|
62 |
plain_layout.addWidget(left) |
---|
63 |
self.addAnalogInputs(left) |
---|
64 |
|
---|
65 |
center = QGroupBox(self) |
---|
66 |
plain_layout.addWidget(center) |
---|
67 |
self.addInternalMixer(center) |
---|
68 |
|
---|
69 |
right = QGroupBox(self) |
---|
70 |
plain_layout.addWidget(right) |
---|
71 |
self.addAnalogOutputs(right) |
---|
72 |
|
---|
73 |
def addAnalogInputs(self, box): |
---|
74 |
box_layout = QVBoxLayout() |
---|
75 |
box.setLayout(box_layout) |
---|
76 |
box.setTitle("Analog Inputs") |
---|
77 |
|
---|
78 |
grid_layout = QGridLayout() |
---|
79 |
box_layout.addLayout(grid_layout) |
---|
80 |
|
---|
81 |
self.addVolumes(self.inputs, 0, box, grid_layout) |
---|
82 |
|
---|
83 |
def addAnalogOutputs(self, box): |
---|
84 |
box_layout = QVBoxLayout() |
---|
85 |
box.setLayout(box_layout) |
---|
86 |
box.setTitle("Analog Outputs") |
---|
87 |
|
---|
88 |
grid_layout = QGridLayout() |
---|
89 |
box_layout.addLayout(grid_layout) |
---|
90 |
|
---|
91 |
self.addVolumes(self.outputs, 2, box, grid_layout) |
---|
92 |
|
---|
93 |
def addInternalMixer(self, box): |
---|
94 |
box_layout = QGridLayout() |
---|
95 |
box.setLayout(box_layout) |
---|
96 |
box.setTitle("Hardware Mixer") |
---|
97 |
|
---|
98 |
self.addVolumes(self.mixer_src, 1, box, box_layout) |
---|
99 |
|
---|
100 |
def addVolumes(self, elms, kind, parent, layout): |
---|
101 |
def addPreampParam(label, ch, path, layout): |
---|
102 |
button = self.getThinButton(parent, label) |
---|
103 |
layout.addWidget(button) |
---|
104 |
self.Preamps[button] = ["/Preamp/%s" % path, ch] |
---|
105 |
return |
---|
106 |
|
---|
107 |
for col in range(len(elms)): |
---|
108 |
label = QLabel(parent) |
---|
109 |
label.setText(elms[col][1]) |
---|
110 |
layout.addWidget(label, 0, col * 2, 1, 2, Qt.AlignHCenter | Qt.AlignTop) |
---|
111 |
|
---|
112 |
if kind == 0: |
---|
113 |
if col == 0: |
---|
114 |
for ch in range(2): |
---|
115 |
box_layout = QVBoxLayout() |
---|
116 |
layout.addLayout(box_layout, 1, col * 2 + ch, Qt.AlignHCenter | Qt.AlignBottom) |
---|
117 |
if col == 0: |
---|
118 |
addPreampParam("+48V", ch + 1, "PhantomPower", box_layout) |
---|
119 |
addPreampParam("Boost", ch + 1, "MicBoost", box_layout) |
---|
120 |
addPreampParam("Limit", ch + 1, "MicLimit", box_layout) |
---|
121 |
else: |
---|
122 |
box_layout = QVBoxLayout() |
---|
123 |
addPreampParam("Phono", 0, "PhonoSwitch", box_layout) |
---|
124 |
layout.addLayout(box_layout, 1, col * 2, 1, 2, Qt.AlignHCenter | Qt.AlignBottom) |
---|
125 |
elif kind == 1: |
---|
126 |
l_dial = self.getDial(parent) |
---|
127 |
r_dial = self.getDial(parent) |
---|
128 |
|
---|
129 |
layout.addWidget(l_dial, 1, col * 2, Qt.AlignHCenter | Qt.AlignBottom) |
---|
130 |
layout.addWidget(r_dial, 1, col * 2 + 1, Qt.AlignHCenter | Qt.AlignBottom) |
---|
131 |
|
---|
132 |
path = "/Mixer/Feature_LRBalance_%d" % elms[col][0] |
---|
133 |
self.Pannings[l_dial] = [path, 1] |
---|
134 |
self.Pannings[r_dial] = [path, 2] |
---|
135 |
|
---|
136 |
if col == 2: |
---|
137 |
l_dial.setDisabled(True) |
---|
138 |
r_dial.setDisabled(True) |
---|
139 |
|
---|
140 |
elif col == 0: |
---|
141 |
cmb = QComboBox(parent) |
---|
142 |
layout.addWidget(cmb, 1, col * 2, 1, 4, Qt.AlignHCenter | Qt.AlignBottom) |
---|
143 |
for i in range(len(self.out_src[1])): |
---|
144 |
cmb.addItem(self.out_src[1][i], i) |
---|
145 |
self.Selectors[cmb] = ["/Mixer/Selector_%d" % self.out_src[0]] |
---|
146 |
|
---|
147 |
l_sld = self.getSlider(parent) |
---|
148 |
r_sld = self.getSlider(parent) |
---|
149 |
layout.addWidget(l_sld, 2, col * 2, Qt.AlignHCenter) |
---|
150 |
layout.addWidget(r_sld, 2, col * 2 + 1, Qt.AlignHCenter) |
---|
151 |
|
---|
152 |
l_mute = self.getThinButton(parent, "Mute") |
---|
153 |
r_mute = self.getThinButton(parent, "Mute") |
---|
154 |
layout.addWidget(l_mute, 3, col * 2, Qt.AlignHCenter) |
---|
155 |
layout.addWidget(r_mute, 3, col * 2 + 1, Qt.AlignHCenter) |
---|
156 |
|
---|
157 |
link = self.getWideButton(parent, "Link") |
---|
158 |
layout.addWidget(link, 4, col * 2, 1, 2, Qt.AlignHCenter) |
---|
159 |
|
---|
160 |
path = "/Mixer/Feature_Volume_%d" % elms[col][0] |
---|
161 |
self.Volumes[l_sld] = [path, 1, r_sld, l_mute, link] |
---|
162 |
self.Volumes[r_sld] = [path, 2, l_sld, r_mute, link] |
---|
163 |
|
---|
164 |
self.Mutes[l_mute] = [r_mute, l_sld] |
---|
165 |
self.Mutes[r_mute] = [l_mute, r_sld] |
---|
166 |
|
---|
167 |
# widget helper functions |
---|
168 |
def getSlider(self, parent): |
---|
169 |
sld = QSlider(parent) |
---|
170 |
sld.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) |
---|
171 |
sld.setMinimum(0) |
---|
172 |
sld.setMaximum(99) |
---|
173 |
sld.setPageStep(10) |
---|
174 |
sld.setPageStep(10) |
---|
175 |
sld.setMinimumHeight(50) |
---|
176 |
sld.setTickInterval(25) |
---|
177 |
sld.setTickPosition(QSlider.TicksBothSides) |
---|
178 |
return sld |
---|
179 |
|
---|
180 |
def getDial(self, parent): |
---|
181 |
dial = QDial(parent) |
---|
182 |
dial.setNotchesVisible(True) |
---|
183 |
dial.setNotchTarget(25.0) |
---|
184 |
dial.setMaximumHeight(40) |
---|
185 |
return dial; |
---|
186 |
|
---|
187 |
def getThinButton(self, parent, text): |
---|
188 |
button = QToolButton(parent) |
---|
189 |
button.setText(text) |
---|
190 |
button.setCheckable(True) |
---|
191 |
button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) |
---|
192 |
return button |
---|
193 |
|
---|
194 |
def getWideButton(self, parent, label): |
---|
195 |
button = QToolButton(parent) |
---|
196 |
button.setText(label) |
---|
197 |
button.setCheckable(True) |
---|
198 |
button.setMinimumWidth(100) |
---|
199 |
button.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed) |
---|
200 |
return button |
---|
201 |
|
---|
202 |
def initValues(self): |
---|
203 |
for ctl, params in self.Selectors.items(): |
---|
204 |
path = params[0] |
---|
205 |
state = self.hw.getDiscrete(path) |
---|
206 |
ctl.setCurrentIndex(state) |
---|
207 |
ctl.activated.connect(self.updateSelector) |
---|
208 |
|
---|
209 |
for ctl, params in self.Volumes.items(): |
---|
210 |
path = params[0] |
---|
211 |
idx = params[1] |
---|
212 |
pair = params[2] |
---|
213 |
mute = params[3] |
---|
214 |
link = params[4] |
---|
215 |
|
---|
216 |
db = self.hw.getContignuous(path, idx) |
---|
217 |
vol = self.db2vol(db) |
---|
218 |
ctl.setValue(vol) |
---|
219 |
ctl.valueChanged.connect(self.updateVolume) |
---|
220 |
|
---|
221 |
if vol == 0: |
---|
222 |
mute.setChecked(True) |
---|
223 |
|
---|
224 |
if idx == 2: |
---|
225 |
pair_db = self.hw.getContignuous(path, 1) |
---|
226 |
if pair_db == db: |
---|
227 |
link.setChecked(True) |
---|
228 |
|
---|
229 |
for ctl, params in self.Preamps.items(): |
---|
230 |
path = params[0] |
---|
231 |
idx = params[1] |
---|
232 |
|
---|
233 |
vol = self.hw.getDiscrete(path, idx) |
---|
234 |
if vol > 0: |
---|
235 |
ctl.setChecked(True) |
---|
236 |
|
---|
237 |
ctl.clicked.connect(self.updatePreamps) |
---|
238 |
|
---|
239 |
# Right - Center - Left |
---|
240 |
# 0x8000 - 0x0000 - 0x0001 - 0x7FFE |
---|
241 |
# ..., -1, 0, +1, ... |
---|
242 |
for ctl, params in self.Pannings.items(): |
---|
243 |
path = params[0] |
---|
244 |
idx = params[1] |
---|
245 |
|
---|
246 |
val = self.hw.getContignuous(path, idx) |
---|
247 |
state = -(val / 0x7FFE) * 50 + 50 |
---|
248 |
ctl.setValue(state) |
---|
249 |
ctl.valueChanged.connect(self.updatePanning) |
---|
250 |
|
---|
251 |
for ctl, params in self.Mutes.items(): |
---|
252 |
ctl.clicked.connect(self.updateMute) |
---|
253 |
|
---|
254 |
# helper functions |
---|
255 |
def vol2db(self, vol): |
---|
256 |
return (log10(vol + 1) - 2) * 16384 |
---|
257 |
|
---|
258 |
def db2vol(self, db): |
---|
259 |
return pow(10, db / 16384 + 2) - 1 |
---|
260 |
|
---|
261 |
def updateSelector(self, state): |
---|
262 |
sender = self.sender() |
---|
263 |
path = self.Selectors[sender][0] |
---|
264 |
self.hw.setDiscrete(path, state) |
---|
265 |
|
---|
266 |
def updatePreamps(self, state): |
---|
267 |
sender = self.sender() |
---|
268 |
path = self.Preamps[sender][0] |
---|
269 |
idx = self.Preamps[sender][1] |
---|
270 |
self.hw.setDiscrete(path, idx, state) |
---|
271 |
|
---|
272 |
def updateVolume(self, vol): |
---|
273 |
sender = self.sender() |
---|
274 |
path = self.Volumes[sender][0] |
---|
275 |
idx = self.Volumes[sender][1] |
---|
276 |
pair = self.Volumes[sender][2] |
---|
277 |
mute = self.Volumes[sender][3] |
---|
278 |
link = self.Volumes[sender][4] |
---|
279 |
|
---|
280 |
if mute.isChecked(): |
---|
281 |
return |
---|
282 |
|
---|
283 |
db = self.vol2db(vol) |
---|
284 |
self.hw.setContignuous(path, db, idx) |
---|
285 |
|
---|
286 |
if link.isChecked(): |
---|
287 |
pair.setValue(vol) |
---|
288 |
|
---|
289 |
def updatePanning(self, state): |
---|
290 |
sender = self.sender() |
---|
291 |
path = self.Pannings[sender][0] |
---|
292 |
idx = self.Pannings[sender][1] |
---|
293 |
val = (state - 50) * 0x7FFE / -50 |
---|
294 |
self.hw.setContignuous(path, idx, val) |
---|
295 |
|
---|
296 |
def updateMute(self, state): |
---|
297 |
sender = self.sender() |
---|
298 |
pair = self.Mutes[sender][0] |
---|
299 |
sld = self.Mutes[sender][1] |
---|
300 |
|
---|
301 |
path = self.Volumes[sld][0] |
---|
302 |
idx = self.Volumes[sld][1] |
---|
303 |
pair_sld = self.Volumes[sld][2] |
---|
304 |
link = self.Volumes[sld][4] |
---|
305 |
|
---|
306 |
if state: |
---|
307 |
db = 0x8000 |
---|
308 |
vol = 0 |
---|
309 |
else: |
---|
310 |
db = 0x0000 |
---|
311 |
vol = 99 |
---|
312 |
|
---|
313 |
self.hw.setContignuous(path, db, idx) |
---|
314 |
sld.setValue(vol) |
---|
315 |
sld.setDisabled(state) |
---|
316 |
|
---|
317 |
if link.isChecked(): |
---|
318 |
if idx == 1: |
---|
319 |
idx = 2 |
---|
320 |
else: |
---|
321 |
idx = 1 |
---|
322 |
self.hw.setContignuous(path, db, idx) |
---|
323 |
pair.setChecked(state) |
---|
324 |
pair_sld.setValue(vol) |
---|
325 |
pair_sld.setDisabled(state) |
---|