Skip to content

Commit

Permalink
[VolumeControl] Rewrite and refactor VolumeControl code (#3410)
Browse files Browse the repository at this point in the history
* [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.
  • Loading branch information
IanSav committed Aug 27, 2024
1 parent 94341c0 commit 6cdac1e
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 166 deletions.
4 changes: 4 additions & 0 deletions data/keymap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@

<map context="GlobalActions">
<key id="KEY_VOLUMEUP" mapto="volumeUp" flags="mr" />
<key id="KEY_VOLUMEUP" mapto="volumeUpLong" flags="l" />
<key id="KEY_VOLUMEUP" mapto="volumeUpStop" flags="s" />
<key id="KEY_VOLUMEDOWN" mapto="volumeDown" flags="mr" />
<key id="KEY_VOLUMEDOWN" mapto="volumeDownLong" flags="l" />
<key id="KEY_VOLUMEDOWN" mapto="volumeDownStop" flags="s" />
<key id="KEY_MUTE" mapto="volumeMute" flags="m" />
<key id="KEY_MUTE" mapto="volumeMuteLong" flags="l" />
<key id="KEY_POWER" mapto="power_long" flags="l" />
Expand Down
8 changes: 3 additions & 5 deletions data/setup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<!-- This is just a placeholder, the Videomode plugin implements this sub menu. -->
</setup>
<setup key="Audio" title="Audio Settings" level="1" showOpenWebif="1">
<item level="1" text="Volume steps" description="Select the size of the volume step when the VOLUME buttons are pressed.">config.volumeControl.pressStep</item>
<item level="1" text="Long press volume steps" description="Select the size of the volume steps when the VOLUME buttons are held down.">config.volumeControl.longStep</item>
<item level="1" text="Volume/Mute display timer" description="Select how long, in seconds, that the volume and mute displays are shown before they automatically hide.">config.volumeControl.hideTimer</item>
<item level="1" text="PCM Multichannel" description="Choose whether multi channel sound tracks should be output as PCM." requires="CanPcmMultichannel">config.av.pcm_multichannel</item>
<item level="1" text="AC3 downmix" description="Choose whether AC3 sound tracks should be downmixed to stereo." requires="CanDownmixAC3">config.av.downmix_ac3</item>
<item level="1" text="AC3 plus transcoding" description="Choose whether AC3 Plus sound tracks should be transcoded to AC3." requires="CanAC3plusTranscode">config.av.transcodeac3plus</item>
Expand All @@ -18,11 +21,6 @@
<item level="1" text="3D Surround" description="This option allows you to enable 3D Surround Sound." requires="Can3DSurround">config.av.surround_3d</item>
<item level="1" text="3D Surround Speaker Position" description="This option allows you to change the virtual loudspeaker position." requires="Can3DSpeaker" conditional="config.av.surround_3d.value != 'none'">config.av.surround_3d_speaker</item>
<item level="1" text="Audio Auto Volume Level" description="This option configures you can set Auto Volume Level." requires="CanAutoVolume">config.av.autovolume</item>
<item level="1" text="Audio volume step size" description="Configure the general audio volume step size (limit 1-10).">config.av.volume_stepsize</item>
<item level="1" text="Audio volume step size fast mode" description="Configure the fast mode audio volume step size (limit 1-10). Activated when volume key permanent press or press fast in a row.">config.av.volume_stepsize_fastmode</item>
<item level="1" text="Use logarithmic steps" description="Select to use logarithmic step size with more steps at the lower volume levels.">config.audio.volumeLogSteps</item>
<item level="1" text="Volume display timer" description="Select how long, in seconds, that the volume display is shown before it automatically hides.">config.audio.volumeHideTimer</item>
<item level="1" text="Hide mute notification" description="If muted, hide mute icon or mute information after few seconds.">config.av.volume_hide_mute</item>
<item level="1" text="Enable BT Audio" description="This Option allows you to switch Audio to BT Speakers." requires="CanBTAudio">config.av.btaudio</item>
<item level="1" text="General BT Audio delay" description="This option configures the general audio delay for BT Speakers." requires="CanBTAudioDelay">config.av.btaudiodelay</item>
</setup>
Expand Down
9 changes: 1 addition & 8 deletions lib/python/Components/AVSwitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
248 changes: 105 additions & 143 deletions lib/python/Components/VolumeControl.py
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions lib/python/Screens/InfoBarGenerics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
5 changes: 0 additions & 5 deletions lib/python/Screens/Mute.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 1 addition & 0 deletions lib/python/StartEnigma.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 6cdac1e

Please sign in to comment.