Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VolumeAdjust] #3501

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions data/setup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1055,8 +1055,13 @@
<!-- This is just a placeholder, the Videomode plugin implements this sub menu. -->
</setup>
<setup key="VolumeAdjust" title="Volume Adjust Settings">
<item level="0" text="Default volume offset" description="This is the initial / default volume offset applied to any services added the the volume adjust list.">config.volume.defaultOffset</item>
<item level="0" text="Enable Dolby Digital / Dolby AC-3 offsets" description="Select 'Yes' if volume adjustments should be applied to Dolby sound tracks.">config.volume.dolbyEnabled</item>
<item level="0" text="Default Dolby Digital / Dolby AC-3 offset" description="This offset will only be used if the channel does not have its own volume offset.">config.volume.dolbyOffset</item>
<item level="0" text="Operational mode" description="Disabled - VolumeAdjust will not automatically change service volume levels. 'Defined' - VolumeAdjust will assign a defined offset to the service's volume level. 'Remember' - Remember the last used volume level for each service.">config.volume.adjustMode</item>
<if conditional="config.volume.adjustMode.value">
<item level="0" text="Default volume offset" description="This is the initial / default volume offset applied to any services added the the volume adjust list.">config.volume.defaultOffset</item>
<item level="0" text="Enable Dolby Digital / Dolby AC-3 offsets" description="Select 'Yes' if volume adjustments should be applied to Dolby sound tracks.">config.volume.dolbyEnabled</item>
<item level="0" text="Default Dolby Digital / Dolby AC-3 offset" description="This offset will only be used if the channel does not have its own volume offset.">config.volume.dolbyOffset</item>
<item level="0" text="Maximum MPEG volume" description="Set a maximum limit for MPEG audio levels so as to reduce the risk of hearing damage due to an unexpected increased in volume level.">config.volume.mpegMax</item>
<item level="0" text="Auto show volume level pop" description="When 'Yes' this will cause the volume level pop up to be displayed on any change of volume levels.">config.volume.showVolumeBar</item>
</if>
</setup>
</setupxml>
191 changes: 151 additions & 40 deletions lib/python/Screens/VolumeAdjust.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
from copy import deepcopy
from os import unlink
from os.path import isfile
from os.path import isfile, exists
from pickle import dump, load

from enigma import eDVBVolumecontrol, eServiceReference, iPlayableService, iServiceInformation
from enigma import eDVBVolumecontrol, eEnv, eServiceReference, eServiceCenter, iPlayableService, iServiceInformation

from ServiceReference import ServiceReference
from Components.ActionMap import HelpableActionMap
from Components.config import ConfigSelectionNumber, ConfigSubsection, ConfigYesNo, NoSave, config, getConfigListEntry
from Components.config import ConfigSelection, ConfigSelectionNumber, ConfigSubsection, ConfigYesNo, NoSave, config, getConfigListEntry
from Components.ServiceEventTracker import ServiceEventTracker
from Components.Sources.StaticText import StaticText
from Components.VolumeControl import VolumeControl
from Screens.ChannelSelection import ChannelSelectionBase, OFF
from Screens.Setup import Setup
from Tools.Directories import SCOPE_CONFIG, fileReadXML, moveFiles, resolveFilename

MODULE_NAME = "VolumeAdjust"
VOLUME_FILE = resolveFilename(SCOPE_CONFIG, "volume.xml")
SERVICE_VOLUME_FILE = resolveFilename(SCOPE_CONFIG, "ava_volume.cfg")

OFFSET_MIN = -100
OFFSET_MAX = 100
Expand All @@ -26,6 +29,9 @@
config.volume.defaultOffset = ConfigSelectionNumber(min=OFFSET_MIN, max=OFFSET_MAX, stepwidth=1, default=DEFAULT_OFFSET, wraparound=False)
config.volume.dolbyEnabled = ConfigYesNo(default=False)
config.volume.dolbyOffset = ConfigSelectionNumber(min=OFFSET_MIN, max=OFFSET_MAX, stepwidth=1, default=DEFAULT_OFFSET, wraparound=False)
config.volume.adjustMode = ConfigSelection(choices=[(0, _("Disabled")), (1, _("Defined")), (2, _("Remember"))], default=0)
config.volume.mpegMax = ConfigSelectionNumber(10, 100, 5, default=100)
config.volume.showVolumeBar = ConfigYesNo(default=False)


class VolumeAdjust(Setup):
Expand Down Expand Up @@ -100,6 +106,7 @@ def changedEntry(self): # Override the Setup method that calls createSetup() wh
def keySave(self):
if self.serviceVolumeOffsets != self.initialVolumeOffsets: # Save the volume configuration data if there are any changes.
VolumeInstance.setServiceVolumeOffsets(self.serviceVolumeOffsets)
VolumeInstance.refreshSettings()
Setup.keySave(self)

def cancelConfirm(self, result):
Expand Down Expand Up @@ -209,11 +216,48 @@ def __init__(self, session): # Autostart instance, comes active when info is up
self.serviceVolumeOffsets = self.loadXML() # Load the volume configuration data.
self.volumeControl = eDVBVolumecontrol.getInstance()
self.normalVolume = None
self.previousServiceReference = None
self.previousOffset = 0
self.lastAdjustedValue = 0 # Remember delta from last automatic volume up/down
self.currentVolume = 0 # Only set when AC3 or DTS is available
self.newService = [False, None]
self.pluginStarted = False
self.eventTracker = ServiceEventTracker(screen=self, eventmap={
iPlayableService.evUpdatedInfo: self.processVolumeOffset,
})
iPlayableService.evUpdatedInfo: self.processVolumeOffset,
iPlayableService.evStart: self.eventStart,
iPlayableService.evEnd: self.eventEnd
})

self.serviceList = {}
self.adjustMode = config.volume.adjustMode.value
self.mpegMax = config.volume.mpegMax.value
self.dolbyEnabled = config.volume.dolbyEnabled.value
self.defaultOffset = config.volume.defaultOffset.value
if self.adjustMode == 1: # Automatic volume adjust mode
for name, ref, offset in self.serviceVolumeOffsets:
self.serviceList[ref] = offset
else: # Remember channel volume mode
if exists(SERVICE_VOLUME_FILE):
with open(SERVICE_VOLUME_FILE, "rb") as fd:
self.serviceList = load(fd)

def refreshSettings(self):
self.defaultOffset = config.volume.defaultOffset.value
self.adjustMode = config.volume.adjustMode.value
self.mpegMax = config.volume.mpegMax.value
self.dolbyEnabled = config.volume.dolbyEnabled.value

def eventStart(self):
self.newService = [True, None]

def eventEnd(self):
if self.adjustMode == 1: # Automatic volume adjust mode
# if played service had AC3||DTS audio and volume value was changed with RC, take new delta value from the config
if self.currentVolume and self.volumeControl.getVolume() != self.currentVolume:
self.lastAdjustedValue = self.newService[1] and self.serviceList.get(self.newService[1].toString(), self.defaultOffset) or self.defaultOffset
elif self.adjustMode == 2: # Remember channel volume mode
ref = self.newService[1]
if ref and ref.valid():
self.serviceList[ref.toString()] = self.volumeControl.getVolume()
self.newService = [False, None]

def loadXML(self): # Load the volume configuration data.
serviceVolumeOffsets = []
Expand Down Expand Up @@ -263,36 +307,69 @@ def getNormalVolume(self):
return self.normalVolume or DEFAULT_VOLUME

def processVolumeOffset(self): # This is the routine to change the volume offset.
serviceRef = self.session.nav.getCurrentlyPlayingServiceReference()
if serviceRef:
serviceReference = serviceRef.toCompareString()
if serviceReference != self.previousServiceReference: # Check if the service has changed.
self.previousServiceReference = serviceReference
index = self.findCurrentService(serviceReference)
name, ref, offset = [ServiceReference(serviceRef).getServiceName().replace("\xc2\x87", "").replace("\xc2\x86", ""), serviceReference, 0] if index == -1 else self.serviceVolumeOffsets[index]
serviceVolume = self.volumeControl.getVolume()
if self.previousOffset:
self.normalVolume = serviceVolume - self.previousOffset
print(f"[VolumeAdjust] Volume offset of {self.previousOffset} is currently in effect. Normal volume is {self.normalVolume}.")
else:
self.normalVolume = serviceVolume
print(f"[VolumeAdjust] Normal volume is {self.normalVolume}.")
if index == -1: # Service not found, check if Dolby Digital volume needs to be offset.
if config.volume.dolbyEnabled.value and self.isCurrentAudioAC3DTS():
offset = config.volume.dolbyOffset.value
newVolume = self.normalVolume + offset
if serviceVolume != newVolume:
self.volumeControl.setVolume(newVolume, newVolume)
print(f"[VolumeAdjust] New volume of {newVolume}, including an offset of {offset}, set for Dolby Digital / Dolby AC-3 service '{name}'.")
elif serviceVolume != self.normalVolume:
self.volumeControl.setVolume(self.normalVolume, self.normalVolume)
print(f"[VolumeAdjust] Normal volume of {self.normalVolume} restored for service '{name}'.")
else: # Service found in serviceVolumeOffsets list, use volume offset to change the volume.
newVolume = self.normalVolume + offset
if serviceVolume != newVolume:
self.volumeControl.setVolume(newVolume, newVolume)
print(f"[VolumeAdjust] New volume of {newVolume}, including an offset of {offset}, set for service '{name}'.")
self.previousOffset = offset
# taken from the plugin !!!!
if self.adjustMode and self.newService[0]:
serviceRef = self.session.nav.getCurrentlyPlayingServiceReference()
if serviceRef:
print("[VolumeAdjustment] service changed")
self.newService = [False, serviceRef]
self.currentVolume = 0 # init
if self.adjustMode == 1: # Automatic volume adjust mode
self.currentAC3DTS = self.isCurrentAudioAC3DTS()
if self.pluginStarted:
if self.currentAC3DTS: # ac3 dts?
ref = self.getPlayingServiceReference()
vol = self.volumeControl.getVolume()
currentvol = vol # remember current vol
vol -= self.lastAdjustedValue # go back to origin value first
ajvol = self.serviceList.get(ref.toString(), self.defaultOffset) # get delta from config
if ajvol < 0: # adjust vol down
if vol + ajvol < 0:
ajvol = (-1) * vol
else: # adjust vol up
if vol >= 100 - ajvol: # check if delta + vol < 100
ajvol = 100 - vol # correct delta value
self.lastAdjustedValue = ajvol # save delta value
if (vol + ajvol != currentvol):
if ajvol == 0:
ajvol = vol - currentvol # correction for debug -print(only)
self.setVolume(vol + self.lastAdjustedValue)
print(f"[VolumeAdjustment] Change volume for service: '{self.getServiceName(ref)}' (+{ajvol}) to {self.volumeControl.getVolume()}")
self.currentVolume = self.volumeControl.getVolume() # ac3||dts service , save current volume
else:
# mpeg or whatever audio
if self.lastAdjustedValue != 0:
# go back to origin value
vol = self.volumeControl.getVolume()
ajvol = vol - self.lastAdjustedValue
if ajvol > self.mpegMax:
ajvol = self.mpegMax
self.setVolume(ajvol)
print(f"[VolumeAdjustment] Change volume for service: '{self.getServiceName(self.session.nav.getCurrentlyPlayingServiceReference())}' (-{vol - ajvol}) to {self.volumeControl.getVolume()}")
self.lastAdjustedValue = 0 # mpeg audio, no delta here
return # get out of here, nothing to do anymore
elif self.adjustMode == 2: # modus = Remember channel volume
if self.pluginStarted:
ref = self.getPlayingServiceReference()
if ref.valid():
# get value from dict
lastvol = self.serviceList.get(ref.toString(), -1)
if lastvol != -1 and lastvol != self.volumeControl.getVolume():
# set volume value
self.setVolume(lastvol)
print(f"[VolumeAdjustment] Set last used volume value for service '{self.getServiceName(ref)}' to {self.volumeControl.getVolume()}")
return # get out of here, nothing to do anymore

if not self.pluginStarted:
if self.adjustMode == 1: # Automatic volume adjust mode
# starting plugin, if service audio is ac3 or dts --> get delta from config...volume value is set by enigma2-system at start
if self.currentAC3DTS:
self.lastAdjustedValue = self.serviceList.get(self.session.nav.getCurrentlyPlayingServiceReference().toString(), self.defaultOffset)
self.currentVolume = self.volumeControl.getVolume() # ac3||dts service , save current volume
self.pluginStarted = True # plugin started...

def getServiceName(self, ref):
return ServiceReference(ref).getServiceName().replace("\xc2\x86", "").replace("\xc2\x87", "") if ref else ""

def findCurrentService(self, serviceReference):
for index, (name, ref, offset) in enumerate(self.serviceVolumeOffsets):
Expand All @@ -307,19 +384,53 @@ def isCurrentAudioAC3DTS(self):
try: # Uhh, servicemp3 leads sometimes to OverflowError Error.
description = audio.getTrackInfo(audio.getCurrentTrack()).getDescription()
print(f"[VolumeAdjust] Description: '{description}'.")
if "AC3" in description or "DTS" in description or "Dolby Digital" == description:
print("[VolumeAdjust] AudioAC3Dolby = YES")
return True
if self.dolbyEnabled:
if "AC3" in description or "DTS" in description or "Dolby Digital" == description:
print("[VolumeAdjust] AudioAC3Dolby = YES")
return True
else:
if description and description.split()[0] in ("AC3", "AC-3", "A_AC3", "A_AC-3", "A-AC-3", "E-AC-3", "A_EAC3", "DTS", "DTS-HD", "AC4", "AAC-HE"):
print("[VolumeAdjust] AudioAC3Dolby = YES")
return True
except Exception:
print("[VolumeAdjust] Exception: AudioAC3Dolby = NO")
return False
print("[VolumeAdjust] AudioAC3Dolby = NO")
return False

def getPlayingServiceReference(self):
ref = self.session.nav.getCurrentlyPlayingServiceReference()
if ref:
refstr = ref.toString()
if "%3a//" not in refstr and refstr.rsplit(":", 1)[1].startswith("/"): # check if a movie is playing
# it is , get the eServicereference if available
self.serviceHandler = eServiceCenter.getInstance()
info = self.serviceHandler.info(ref)
if info:
# no need here to know if eServiceReference is valid...
ref = eServiceReference(info.getInfoString(ref, iServiceInformation.sServiceref)) # get new eServicereference from meta file
return ref

def setVolume(self, value):
self.volumeControl.setVolume(value, value) # Set new volume
if VolumeControl.instance:
VolumeControl.instance.volumeDialog.setValue(value) # Update progressbar value
if config.volume.showVolumeBar.value:
VolumeControl.instance.volumeDialog.show()
VolumeControl.instance.hideVolTimer.start(3000, True)
config.volumeControl.volume.value = self.volumeControl.getVolume()
config.volumeControl.volume.save()


VolumeInstance = None


def shutdown():
if VolumeInstance:
with open(SERVICE_VOLUME_FILE, "wb") as fd:
dump(VolumeInstance.serviceList, fd)


def autostart(session):
oldFile = "/etc/volume.xml" # This code is for old versions of "volume.xml". This can be removed after a reasonable period for users to update.
if isfile(oldFile):
Expand Down
2 changes: 2 additions & 0 deletions lib/python/StartEnigma.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,8 @@ def runNextScreen(session, screensToRun, *result):
session.nav.shutdown()
session.doShutdown()
VolumeControl.instance.saveVolumeState()
from Screens.VolumeAdjust import shutdown
shutdown()
configfile.save()
from Screens.InfoBarGenerics import saveResumePoints
saveResumePoints()
Expand Down