Commit 7f50430a authored by Jürgen Herrmann's avatar Jürgen Herrmann

on insert:

  choose pulseaudio master sink
on remove:
  choose pulseaudio default sink

will be released as 0.36
parent 771b50d5
2do:
====
more options:
remove ladspa-module-insertion completely from config file
choose pulseaudio master sink
done:
=====
on insert:
choose pulseaudio master sink
on remove:
choose pulseaudio default sink
add proper desktop entry in debian package
react to changes in ladspa-t5-plugins:
added a "magic" number for pulseaudio parametric eq to use in mmap-filenames
......
......@@ -5,6 +5,7 @@ import mmap
import re
import os
import struct
import sys
from math import sin, cos, pi, pow, sqrt
import numpy
......@@ -25,12 +26,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setupUi(self)
## pulsectl import check
self._pulsectlImportCheck()
# instance variable
self._prohibitLineEditUpdates = False
self._disablePresetsComboboxHandler = False
self._loadedPresetName = ""
self._mmaps = []
self._pulseClient = None
self._pulseaudio_moduleindex = -1
self._last_pulseaudio_hw_sink = ""
self._last_pulseaudio_default_sink = ""
self._isInserted = False
self._checkInserted()
self._setupMmaps()
......@@ -290,11 +296,33 @@ class MainWindow(QMainWindow, Ui_MainWindow):
def onInsert(self):
""" insert eq ladspa module and persist into ini """
master_sink = self._pulseaudioMasterSink()
hw_sinks = pulse_get_hw_sinks()
hw_sink_descriptions = map(lambda x: x["description"], hw_sinks)
if self._isInserted:
idx = 0
for i, hw_sink_dict in enumerate(hw_sinks):
if hw_sink_dict['name'] == self._last_pulseaudio_default_sink:
idx = i
break
dlg = QInputDialog()
hw_sink_desc, ok = dlg.getItem(self,
"Choose pulseaudio default sink...",
"Master sinks:",
hw_sink_descriptions,
idx,
False)
if not ok:
return
for hw_sink_dict in hw_sinks:
if hw_sink_dict['description'] == hw_sink_desc:
hw_sink = hw_sink_dict['name']
break
self.pushButtonInsert.setText("Remove")
self._last_pulseaudio_default_sink = hw_sink
self.pushButtonInsert.setText("Insert")
self._mmaps = []
os.system("pactl set-default-sink %s" % master_sink)
cmd = """pacmd "list-sink-inputs" | grep 'index: ' | sed 's/ index: /pacmd move-sink-input /g' | sed 's/$/ %s/g'""" % LADSPA_SINK_NAME
os.system("pactl set-default-sink %s" % hw_sink)
cmd = """pacmd "list-sink-inputs" | grep 'index: ' | sed 's/ index: /pacmd move-sink-input /g' | sed 's/$/ %s/g'""" % hw_sink
ret = os.popen(cmd).read().strip("\n")
cmd = ret.replace("\n", "; ")
os.system(cmd)
......@@ -303,10 +331,29 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self._modifyDefaultPa(action="remove")
self._isInserted = False
else:
idx = 0
for i, hw_sink_dict in enumerate(hw_sinks):
if hw_sink_dict['name'] == self._last_pulseaudio_hw_sink:
idx = i
break
dlg = QInputDialog()
hw_sink_desc, ok = dlg.getItem(self,
"Choose pulseaudio master sink...",
"Master sinks:",
hw_sink_descriptions,
idx,
False)
if not ok:
return
for hw_sink_dict in hw_sinks:
if hw_sink_dict['description'] == hw_sink_desc:
hw_sink = hw_sink_dict['name']
break
self.pushButtonInsert.setText("Remove")
self._last_pulseaudio_hw_sink = hw_sink
cmd = "pactl load-module module-ladspa-sink "
cmd += "sink_name=%s " % LADSPA_SINK_NAME
cmd += "sink_master=%s " % master_sink
cmd += "sink_master=%s " % hw_sink
cmd += "plugin=t5_3band_parameq_with_shelves label=3band_parameq_with_shelves "
cmd += "control=100,0,1,300,0,1,1000,0,1,3000,0,1,10000,0,1,0,%s" % PARAMEQ_MMAP_MAGIC
os.system(cmd)
......@@ -688,6 +735,42 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self._disablePresetsComboboxHandler = False
def _pulsectlImportCheck(self):
try:
import pulsectl
except ImportError:
dlg = QMessageBox()
msg = "WARNING:\n"
msg += " Could not find python module 'pulsectl'.\n"
msg += " Should we try to install it for you?\n"
msg += " (This may take a few seconds...)"
result = dlg.warning(self, "Dependencies warning", msg, QMessageBox.Yes | QMessageBox.No)
if result == QMessageBox.Yes:
ret = os.system("pip3 install pulsectl")
if ret != 0:
msg = "ERROR: module 'pulsectl' not found.\n"
msg += " Manual installation: 'pip3 install pulsectl'"
QMessageBox().critical(self, "ERROR loading dependencies!", msg)
print(msg, file=sys.stderr)
sys.exit(1)
else:
dlg = QMessageBox()
msg = "SUCCESS:\n"
msg += " The pulsectl module was successfully installed.\n"
msg += " The program will be restarted now..."
dlg.information(self, "Need to restart...", msg, QMessageBox.Ok)
fd, fn = mkstemp()
os.close(fd)
os.chmod(fn, 0o700)
f = open(fn, "w")
f.write("#!/bin/sh\nsleep 2\n/usr/bin/pulseaudio-xoverrack")
f.close()
os.system("%s &" % fn)
sys.exit(0)
else:
sys.exit(1)
@staticmethod
def _pulseaudioMasterSink():
""" return name of pulseaudio master sink """
......@@ -705,6 +788,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
s = self._settings
self._bypassed = (s.value("main/bypassed", "false") == "true")
self.pushButtonBypass.setChecked(self._bypassed)
self._last_pulseaudio_hw_sink = s.value("main/last_pulseaudio_hw_sink", "")
self._last_pulseaudio_default_sink = s.value("main/last_pulseaudio_default_sink", "")
# current preset
currentPreset = Preset()
try:
......@@ -828,10 +913,12 @@ class MainWindow(QMainWindow, Ui_MainWindow):
""" write ini settings """
s = self._settings
s.setValue("main/bypassed", self._bypassed and "true" or "false")
s.setValue("main/last_pulseaudio_hw_sink", self._last_pulseaudio_hw_sink)
s.setValue("main/last_pulseaudio_default_sink", self._last_pulseaudio_default_sink)
preset = self._valuesToPreset()
jsonString = preset.serialize()
s.setValue("main/currentPreset", base64.b64encode(jsonString.encode("utf-8")))
# store presets
# store presetsf
toDelete = filter(lambda x: x.startswith("presets/"), s.allKeys())
for k in toDelete:
s.remove(k)
......
import math
import os
CURRENT_VERSION = "0.35"
CURRENT_VERSION = "0.36"
DIALSTEPS = 10000
......@@ -22,7 +22,7 @@ GRAPH_HEIGHT = 250
INPUT_SUFFIXES = ("Low", "Param1", "Param2", "Param3", "High")
LADSPA_SINK_NAME = "ladspa_output.t5_3band_parameq_with_shelves"
LADSPA_SINK_NAME = "PulseaudioParamEq.Input"
LADSPA_LABEL = "3band_parameq_with_shelves"
LADSPA_LIBRARY = "t5_3band_parameq_with_shelves"
......@@ -35,4 +35,6 @@ MAX_GAIN = 12.0
PARAMEQ_MMAP_MAGIC = 12345678
PA_CLIENT_NAME = "PaParamEq-%s" % os.getpid()
SR = 44100 # Simulated samplerate for drawing frequency responses
from helpers.constants import *
try:
import pulsectl
except ImportError:
pulsectl = None # the import error is handled in MainWindow.py
def frequency2dialSteps(hz):
""" convert frequency to dial steps """
ratio = (MAX_FREQ - MIN_FREQ) / hz
ret = (MINMAX_FREQ_RATIO_LOG2 - math.log(ratio, 2)) / MINMAX_FREQ_RATIO_LOG2
return int(round(ret * DIALSTEPS))
_PULSE = None
def dialSteps2frequency(dialSteps):
......@@ -14,22 +15,45 @@ def dialSteps2frequency(dialSteps):
return int(round(math.pow(2, ratio) * MIN_FREQ))
def dialSteps2gain(dialSteps):
""" convert dial steps to gqin """
return MIN_GAIN + dialSteps / DIALSTEPS * (MAX_GAIN - MIN_GAIN)
def dialSteps2q(dialSteps):
""" convert dial steps to q """
return math.pow(10, -1 + dialSteps / (DIALSTEPS/2))
def frequency2dialSteps(hz):
""" convert frequency to dial steps """
ratio = (MAX_FREQ - MIN_FREQ) / hz
ret = (MINMAX_FREQ_RATIO_LOG2 - math.log(ratio, 2)) / MINMAX_FREQ_RATIO_LOG2
return int(round(ret * DIALSTEPS))
def gain2dialSteps(gain):
""" convert gain to dial steps """
ret = int(round(DIALSTEPS/2 + gain / (MAX_GAIN - MIN_GAIN) * DIALSTEPS))
return ret
def dialSteps2gain(dialSteps):
""" convert dial steps to gqin """
return MIN_GAIN + dialSteps / DIALSTEPS * (MAX_GAIN - MIN_GAIN)
def q2dialSteps(q):
""" convert q to dial steps """
return int(round((DIALSTEPS/2) + math.log(q, 10) * (DIALSTEPS/2)))
def dialSteps2q(dialSteps):
""" convert dial steps to q """
return math.pow(10, -1 + dialSteps / (DIALSTEPS/2))
def pulse_get_hw_sinks():
""" return hardware sink names """
global _PULSE
try:
if _PULSE is None:
_PULSE = pulsectl.Pulse(PA_CLIENT_NAME)
ret = []
for _sinkinfo in _PULSE.sink_list():
if _sinkinfo.proplist.get('device.master_device', None) is None:
ret.append({'name': _sinkinfo.name, 'description': _sinkinfo.description})
return ret
except pulsectl.pulsectl.PulseError:
_PULSE = None
return []
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment