From 6cdac1ea4df720cc26951e33132ded9c1d433b09 Mon Sep 17 00:00:00 2001 From: IanSav Date: Tue, 27 Aug 2024 23:45:38 +1000 Subject: [PATCH] [VolumeControl] Rewrite and refactor VolumeControl code (#3410) * [VolumeControl] Rewrite and refactor VolumeControl code [StartEnigma.py] - Save the current volume state once on shutdown of Enigma2 rather than saving the volume on every step/change. [AVSwitch.py] - Remove the duplicated volume control code. [VolumeControl.py] - Rewrite and simplify the code. - Remove some of the Python code and use the existing C++ code in eDVBVolumecontrol. - Don't remember the current volume but rather get it directly from the C++ code. This resolves issues where some code communicates directly with the C++ code. - Save the mute state over shutdown, reboots, and GUI restarts. If mute was active when Enigma2 was ended then mute will be activated on restart. The mute icon will be shown and remain on the screen to warn users that mute is active. - Allow the mute and volume control screens to time out from 1 to 10 seconds (user selectable) and respect the user's selection. - Allow the volume steps on a single press of VOL-/+ to be defined from 1 to 10 (user selectable) per button press. - Allow the rate of volume change to be accelerated to a rate defined from 1 to 10 (user selectable) when the VOL-/+ button is long pressed. When the button is released the single button press rate is restored. - Allow the mute icon to remain on screen by long pressing the MUTE button. It will hide on pressing MUTE again or VOL-/+. [InfoBarGenerics.py] - Use the updated method names in VolumeControl.py. [Mute.py] [Volume.py] [VolumeControl.py] - Fold Mute.py and Volume.py screens into a new screen VolumeControl.py. [keymap.xml] - Add new action map entries for long VOL-/+ and the VOL-/+ Stop events. [setup.xml] - Remove old settings and replace them with the new setting. --- data/keymap.xml | 4 + data/setup.xml | 8 +- lib/python/Components/AVSwitch.py | 9 +- lib/python/Components/VolumeControl.py | 248 ++++++++---------- lib/python/Screens/InfoBarGenerics.py | 6 +- lib/python/Screens/Mute.py | 5 - .../Screens/{Volume.py => VolumeControl.py} | 8 +- lib/python/StartEnigma.py | 1 + 8 files changed, 123 insertions(+), 166 deletions(-) delete mode 100644 lib/python/Screens/Mute.py rename lib/python/Screens/{Volume.py => VolumeControl.py} (93%) diff --git a/data/keymap.xml b/data/keymap.xml index b9e86eea38a..d0a4a0992f2 100644 --- a/data/keymap.xml +++ b/data/keymap.xml @@ -15,7 +15,11 @@ + + + + diff --git a/data/setup.xml b/data/setup.xml index 8f762d9a441..a9eeb8e3262 100644 --- a/data/setup.xml +++ b/data/setup.xml @@ -3,6 +3,9 @@ + config.volumeControl.pressStep + config.volumeControl.longStep + config.volumeControl.hideTimer config.av.pcm_multichannel config.av.downmix_ac3 config.av.transcodeac3plus @@ -18,11 +21,6 @@ config.av.surround_3d config.av.surround_3d_speaker config.av.autovolume - config.av.volume_stepsize - config.av.volume_stepsize_fastmode - config.audio.volumeLogSteps - config.audio.volumeHideTimer - config.av.volume_hide_mute config.av.btaudio config.av.btaudiodelay diff --git a/lib/python/Components/AVSwitch.py b/lib/python/Components/AVSwitch.py index 331c07e07da..d80eeb36dc3 100644 --- a/lib/python/Components/AVSwitch.py +++ b/lib/python/Components/AVSwitch.py @@ -2,7 +2,7 @@ from os import W_OK, access, system from time import sleep -from enigma import eAVControl, eDVBVolumecontrol, getDesktop +from enigma import eAVControl, getDesktop from Components.config import ConfigBoolean, ConfigEnableDisable, ConfigInteger, ConfigNothing, ConfigOnOff, ConfigSelection, ConfigSelectionInteger, ConfigSelectionNumber, ConfigSlider, ConfigSubDict, ConfigSubsection, ConfigText, ConfigYesNo, NoSave, config from Components.About import about @@ -611,13 +611,6 @@ def setPCMMultichannel(configElement): config.av.pcm_multichannel = ConfigYesNo(default=False) config.av.pcm_multichannel.addNotifier(setPCMMultichannel) - def setVolumeStepSize(configElement): - eDVBVolumecontrol.getInstance().setVolumeSteps(int(configElement.value)) - - config.av.volume_stepsize = ConfigSelectionNumber(min=1, max=10, stepwidth=1, default=5) - config.av.volume_stepsize.addNotifier(setVolumeStepSize) - config.av.volume_stepsize_fastmode = ConfigSelectionNumber(min=1, max=10, stepwidth=1, default=5) - config.av.volume_hide_mute = ConfigYesNo(default=True) if AMLOGIC: downmixAC3 = True BoxInfo.setItem("CanPcmMultichannel", True) diff --git a/lib/python/Components/VolumeControl.py b/lib/python/Components/VolumeControl.py index 9357f1b1e30..c4548f8b823 100644 --- a/lib/python/Components/VolumeControl.py +++ b/lib/python/Components/VolumeControl.py @@ -1,161 +1,123 @@ from enigma import eDVBVolumecontrol, eTimer from GlobalActions import globalActionMap -from Components.config import ConfigInteger, ConfigSelectionNumber, ConfigSubsection, ConfigYesNo, config -from Screens.Mute import Mute -from Screens.Volume import Volume +from Components.config import ConfigBoolean, ConfigInteger, ConfigSelectionNumber, ConfigSubsection, config +from Screens.VolumeControl import Mute, Volume +# NOTE: This code does not remember the current volume as other code can change +# the volume directly. Always get the current volume from the driver. +# class VolumeControl: - """Volume control, handles volUp, volDown, volMute actions and display a corresponding dialog.""" + """Volume control, handles volumeUp, volumeDown, volumeMute, and other actions and display a corresponding dialog.""" instance = None def __init__(self, session): - global globalActionMap - globalActionMap.actions["volumeUp"] = self.volUp - globalActionMap.actions["volumeDown"] = self.volDown - globalActionMap.actions["volumeMute"] = self.volMute - globalActionMap.actions["volumeMuteLong"] = self.volMuteLong - assert not VolumeControl.instance, "[VolumeControl] Error: Only one VolumeControl instance is allowed!" - VolumeControl.instance = self - config.audio = ConfigSubsection() - config.audio.volume = ConfigInteger(default=50, limits=(0, 100)) - config.audio.volumeLogSteps = ConfigYesNo(default=True) - config.audio.volumeHideTimer = ConfigSelectionNumber(1, 10, 1, default=3) - self.volumeDialog = session.instantiateDialog(Volume) - self.volumeDialog.setAnimationMode(0) - self.muteDialog = session.instantiateDialog(Mute) - self.muteDialog.setAnimationMode(0) - self.hideVolTimer = eTimer() - self.hideVolTimer.callback.append(self.volHide) - self.stepVolTimer = eTimer() - self.repeat = 500 - self.delay = 3000 - vol = config.audio.volume.value - self.volumeDialog.setValue(vol) - self.volctrl = eDVBVolumecontrol.getInstance() - self.volctrl.setVolume(vol, vol) - self.last_vol = vol + def updateStep(configElement): + self.dvbVolumeControl.setVolumeSteps(configElement.value) - def volSave(self): - if self.volctrl.isMuted(): - config.audio.volume.setValue(0) + if VolumeControl.instance: + print("[VolumeControl] Error: Only one VolumeControl instance is allowed!") else: - config.audio.volume.setValue(self.volctrl.getVolume()) - config.audio.volume.save() + VolumeControl.instance = self + global globalActionMap + globalActionMap.actions["volumeUp"] = self.keyVolumeUp + globalActionMap.actions["volumeUpLong"] = self.keyVolumeLong + globalActionMap.actions["volumeUpStop"] = self.keyVolumeStop + globalActionMap.actions["volumeDown"] = self.keyVolumeDown + globalActionMap.actions["volumeDownLong"] = self.keyVolumeLong + globalActionMap.actions["volumeDownStop"] = self.keyVolumeStop + globalActionMap.actions["volumeMute"] = self.keyVolumeMute + globalActionMap.actions["volumeMuteLong"] = self.keyVolumeMuteLong + self.dvbVolumeControl = eDVBVolumecontrol.getInstance() + config.volumeControl = ConfigSubsection() + config.volumeControl.volume = ConfigInteger(default=20, limits=(0, 100)) + config.volumeControl.mute = ConfigBoolean(default=False) + config.volumeControl.pressStep = ConfigSelectionNumber(1, 10, 1, default=1) + config.volumeControl.pressStep.addNotifier(updateStep, initial_call=True, immediate_feedback=True) + config.volumeControl.longStep = ConfigSelectionNumber(1, 10, 1, default=5) + config.volumeControl.hideTimer = ConfigSelectionNumber(1, 10, 1, default=3) + self.muteDialog = session.instantiateDialog(Mute) + self.muteDialog.setAnimationMode(0) + self.volumeDialog = session.instantiateDialog(Volume) + self.volumeDialog.setAnimationMode(0) + self.hideTimer = eTimer() + self.hideTimer.callback.append(self.hideVolume) + if config.volumeControl.mute.value: + self.dvbVolumeControl.volumeMute() + self.muteDialog.show() + volume = config.volumeControl.volume.value + self.volumeDialog.setValue(volume) + self.dvbVolumeControl.setVolume(volume, volume) + # Compatibility interface for shared plugins. + self.volctrl = self.dvbVolumeControl + self.hideVolTimer = self.hideTimer - def volUp(self): - vol = self.volctrl.getVolume() - step = self.stepVolume() - if config.audio.volumeLogSteps.value: - if vol < 3: - step = 1 - elif vol < 9: - if step > 2: - step = 2 - elif vol < 18: - if step > 3: - step = 3 - elif vol < 30: - if step > 4: - step = 4 - self.setVolume(vol + step) + def keyVolumeUp(self): + self.dvbVolumeControl.volumeUp(0, 0) + self.updateVolume() - def volDown(self): - vol = self.volctrl.getVolume() - step = self.stepVolume() - if config.audio.volumeLogSteps.value: - if vol <= 3: - step = 1 - elif vol <= 9: - if step > 2: - step = 2 - elif vol <= 18: - if step > 3: - step = 3 - elif vol <= 30: - if step > 4: - step = 4 - self.setVolume(vol - step) - - def stepVolume(self): - if self.stepVolTimer.isActive(): - step = config.av.volume_stepsize_fastmode.value - else: - self.getInputConfig() - step = config.av.volume_stepsize.value - self.stepVolTimer.start(self.repeat, True) - return step - - def getInputConfig(self): - if self.hideVolTimer.isActive(): - return - try: - inputconfig = config.inputDevices.getSavedValue() - except KeyError: - return - delay = 0 - repeat = 0 - - for device in inputconfig.values(): - if "enabled" in device and bool(device["enabled"]): - if "delay" in device: - val = int(device["delay"]) - if val > delay: - delay = val - if "repeat" in device: - val = int(device["repeat"]) - if val > repeat: - repeat = val - if repeat + 100 > self.repeat: - self.repeat = repeat + 100 - if delay + 100 > self.delay: - self.delay = delay + 100 - - def setVolume(self, newvol): - self.volctrl.setVolume(newvol, newvol) - is_muted = self.volctrl.isMuted() - vol = self.volctrl.getVolume() - self.last_vol = vol - self.volumeDialog.show() - if is_muted: - self.volMute() # Unmute. - elif not vol: - self.volMute(False, True) # Mute but don't show mute symbol. - if self.volctrl.isMuted(): - self.volumeDialog.setValue(0) - else: - self.volumeDialog.setValue(self.volctrl.getVolume()) - self.volSave() - self.hideVolTimer.start(self.delay, True) + def keyVolumeDown(self): + self.dvbVolumeControl.volumeDown(0, 0) + self.updateVolume() - def volHide(self): - self.volumeDialog.hide() - # Set volume on if muted and volume is changed in OpenWebif. - vol = self.volctrl.getVolume() - if self.volctrl.isMuted() and self.last_vol != vol: - self.volctrl.volumeUnMute() - self.last_vol = vol - # - if not self.volctrl.isMuted() or config.av.volume_hide_mute.value: - self.muteDialog.hide() - - def showMute(self): - if self.volctrl.isMuted(): - self.muteDialog.show() - self.hideVolTimer.start(int(config.audio.volumeHideTimer.value) * 1000, True) - - def volMute(self, showMuteSymbol=True, force=False): - vol = self.volctrl.getVolume() - if vol or force: - self.volctrl.volumeToggleMute() - if self.volctrl.isMuted(): - if showMuteSymbol: - self.showMute() - self.volumeDialog.setValue(0) + def keyVolumeLong(self): + self.dvbVolumeControl.setVolumeSteps(config.volumeControl.longStep.value) + + def keyVolumeStop(self): + self.dvbVolumeControl.setVolumeSteps(config.volumeControl.pressStep.value) + + def keyVolumeMute(self): # This will toggle the current mute status. Mute will not be activated if the volume is at 0. + volume = self.dvbVolumeControl.getVolume() + isMuted = self.dvbVolumeControl.isMuted() + if volume or (volume == 0 and isMuted): + self.dvbVolumeControl.volumeToggleMute() + if self.dvbVolumeControl.isMuted(): + self.muteDialog.show() + self.volumeDialog.hide() else: self.muteDialog.hide() - self.volumeDialog.setValue(vol) + self.volumeDialog.setValue(volume) + self.volumeDialog.show() + self.hideTimer.start(config.volumeControl.hideTimer.value * 1000, True) + + def keyVolumeMuteLong(self): # Long press MUTE will keep the mute icon on-screen without a timeout. + if self.dvbVolumeControl.isMuted(): + self.hideTimer.stop() + + def updateVolume(self): + if self.dvbVolumeControl.isMuted(): + self.keyVolumeMute() # Unmute. + else: + self.volumeDialog.setValue(self.dvbVolumeControl.getVolume()) + self.volumeDialog.show() + self.hideTimer.start(config.volumeControl.hideTimer.value * 1000, True) - def volMuteLong(self): + def hideVolume(self): self.muteDialog.hide() + self.volumeDialog.hide() + + def saveVolumeState(self): + config.volumeControl.mute.value = self.dvbVolumeControl.isMuted() + config.volumeControl.volume.setValue(self.dvbVolumeControl.getVolume()) + config.volumeControl.save() + + def showMute(self): # This method is only called by InfoBarGenerics.py: + if self.dvbVolumeControl.isMuted(): + self.muteDialog.show() + self.hideTimer.start(config.volumeControl.hideTimer.value * 1000, True) + + # These methods are provided for compatibly with shared plugins. + # + def volUp(self): + self.keyVolumeUp() + + def volDown(self): + self.keyVolumeDown() + + def volMute(self): + self.keyVolumeMute() + + def volSave(self): + # Volume (and mute) saving is now done when Enigma2 shuts down. + pass diff --git a/lib/python/Screens/InfoBarGenerics.py b/lib/python/Screens/InfoBarGenerics.py index 11274e5ecb4..48b3bf87893 100644 --- a/lib/python/Screens/InfoBarGenerics.py +++ b/lib/python/Screens/InfoBarGenerics.py @@ -10,7 +10,7 @@ from sys import maxsize from time import localtime, strftime, time -from enigma import eActionMap, eAVControl, eDBoxLCD, eDVBDB, eDVBServicePMTHandler, eDVBVolumecontrol, eEPGCache, eServiceCenter, eServiceReference, eTimer, getBsodCounter, getDesktop, iPlayableService, iServiceInformation, quitMainloop, resetBsodCounter, eDVBVolumecontrol +from enigma import eActionMap, eAVControl, eDBoxLCD, eDVBDB, eDVBServicePMTHandler, eDVBVolumecontrol, eEPGCache, eServiceCenter, eServiceReference, eTimer, getBsodCounter, getDesktop, iPlayableService, iServiceInformation, quitMainloop, resetBsodCounter from keyids import KEYFLAGS, KEYIDNAMES, KEYIDS from RecordTimer import AFTEREVENT, RecordTimer, RecordTimerEntry, findSafeRecordPath, parseEvent @@ -1756,10 +1756,10 @@ def zapDown(self): self["SeekActionsPTS"].setEnabled(True) def volumeUp(self): # Called from ButtonSetup - VolumeControl.instance.volUp() + VolumeControl.instance.keyVolumeUp() def volumeDown(self): # Called from ButtonSetup - VolumeControl.instance.volDown() + VolumeControl.instance.keyVolumeDown() class InfoBarMenu: diff --git a/lib/python/Screens/Mute.py b/lib/python/Screens/Mute.py deleted file mode 100644 index d99050d8611..00000000000 --- a/lib/python/Screens/Mute.py +++ /dev/null @@ -1,5 +0,0 @@ -from Screens.Screen import Screen - - -class Mute(Screen): - pass diff --git a/lib/python/Screens/Volume.py b/lib/python/Screens/VolumeControl.py similarity index 93% rename from lib/python/Screens/Volume.py rename to lib/python/Screens/VolumeControl.py index abe978fe4dd..1d9d3b0464d 100644 --- a/lib/python/Screens/Volume.py +++ b/lib/python/Screens/VolumeControl.py @@ -3,13 +3,17 @@ from Screens.Screen import Screen +class Mute(Screen): + pass + + class Volume(Screen): def __init__(self, session): Screen.__init__(self, session) - self["Volume"] = VolumeBar() self["VolumeText"] = Label() + self["Volume"] = VolumeBar() def setValue(self, volume): print(f"[Volume] Volume set to {volume}.") - self["Volume"].setValue(volume) self["VolumeText"].setText(str(volume)) + self["Volume"].setValue(volume) diff --git a/lib/python/StartEnigma.py b/lib/python/StartEnigma.py index 8d2f83fd1c5..02df9d4eb1b 100644 --- a/lib/python/StartEnigma.py +++ b/lib/python/StartEnigma.py @@ -559,6 +559,7 @@ def runNextScreen(session, screensToRun, *result): print("=" * 100) session.nav.stopService() session.nav.shutdown() + VolumeControl.instance.saveVolumeState() configfile.save() from Screens.InfoBarGenerics import saveResumePoints saveResumePoints()