From a6f8c459d00a13d3b0bcfe6ee7cbe35069a83d0f Mon Sep 17 00:00:00 2001 From: Terry Geng Date: Mon, 15 Jun 2020 20:07:17 +0800 Subject: [PATCH] refactor: move volume part into a helper --- command.py | 12 ++++------ interface.py | 34 +++++++++++++-------------- mumbleBot.py | 65 +++++++++++++++++++++------------------------------- util.py | 31 +++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 63 deletions(-) diff --git a/command.py b/command.py index b18e6f7..e86866a 100644 --- a/command.py +++ b/command.py @@ -594,12 +594,12 @@ def cmd_volume(bot, user, text, command, parameter): # The volume is a percentage if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100: - bot.convert_set_volume(float(parameter) / 100.0) + bot.volume_helper.set_volume(float(parameter) / 100.0) bot.send_msg(constants.strings('change_volume', volume=parameter, user=bot.mumble.users[text.actor]['name']), text) var.db.set('bot', 'volume', str(float(parameter) / 100.0)) log.info(f'cmd: volume set to {float(parameter) / 100.0}') else: - bot.send_msg(constants.strings('current_volume', volume=int(bot.get_displayed_volume() * 100)), text) + bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_helper.plain_volume_set * 100)), text) def cmd_ducking(bot, user, text, command, parameter): @@ -608,8 +608,6 @@ def cmd_ducking(bot, user, text, command, parameter): if parameter == "" or parameter == "on": bot.is_ducking = True var.db.set('bot', 'ducking', True) - bot.convert_set_ducking_volume(var.config.getfloat("bot", "ducking_volume", fallback=0.05)) - bot.ducking_threshold = var.config.getint("bot", "ducking_threshold", fallback=5000) bot.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED, bot.ducking_sound_received) bot.mumble.set_receive_sound(True) log.info('cmd: ducking is on') @@ -642,12 +640,12 @@ def cmd_ducking_volume(bot, user, text, command, parameter): # The volume is a percentage if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100: - bot.convert_set_ducking_volume(float(parameter) / 100.0) + bot.volume_helper.set_ducking_volume(float(parameter) / 100.0) bot.send_msg(constants.strings('change_ducking_volume', volume=parameter, user=bot.mumble.users[text.actor]['name']), text) - var.db.set('bot', 'ducking_volume', parameter) + var.db.set('bot', 'ducking_volume', float(parameter) / 100.0) log.info(f'cmd: volume on ducking set to {parameter}') else: - bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.get_displayed_ducking_volume() * 100)), text) + bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.volume_helper.plain_ducking_volume_set * 100)), text) def cmd_current_music(bot, user, text, command, parameter): diff --git a/interface.py b/interface.py index dc97d2a..65ef2af 100644 --- a/interface.py +++ b/interface.py @@ -319,7 +319,7 @@ def status(): 'empty': False, 'play': not var.bot.is_pause, 'mode': var.playlist.mode, - 'volume': var.bot.get_displayed_volume(), + 'volume': var.bot.volume_helper.plain_volume_set, 'playhead': var.bot.playhead }) @@ -329,7 +329,7 @@ def status(): 'empty': True, 'play': not var.bot.is_pause, 'mode': var.playlist.mode, - 'volume': var.bot.get_displayed_volume(), + 'volume': var.bot.volume_helper.plain_volume_set, 'playhead': 0 }) @@ -486,31 +486,31 @@ def post(): elif action == "clear": var.bot.clear() elif action == "volume_up": - if var.bot.get_displayed_volume() + 0.03 < 1.0: - var.bot.convert_set_volume(var.bot.unconverted_volume + 0.03) + if var.bot.volume_helper.plain_volume_set + 0.03 < 1.0: + var.bot.volume_helper.set_volume(var.bot.volume_helper.plain_volume_set + 0.03) else: - var.bot.convert_set_volume(1.0) - var.db.set('bot', 'volume', str(var.bot.get_displayed_volume())) - log.info("web: volume up to %d" % (var.bot.get_displayed_volume() * 100)) + var.bot.volume_helper.set_volume(1.0) + var.db.set('bot', 'volume', str(var.bot.volume_helper.plain_volume_set)) + log.info("web: volume up to %d" % (var.bot.volume_helper.plain_volume_set * 100)) elif action == "volume_down": - if var.bot.get_displayed_volume() - 0.03 > 0: - var.bot.convert_set_volume(var.bot.unconverted_volume - 0.03) + if var.bot.volume_helper.plain_volume_set - 0.03 > 0: + var.bot.volume_helper.set_volume(var.bot.unconverted_volume - 0.03) else: - var.bot.convert_set_volume(1.0) - var.db.set('bot', 'volume', str(var.bot.get_displayed_volume())) - log.info("web: volume down to %d" % (var.bot.get_displayed_volume() * 100)) + var.bot.volume_helper.set_volume(1.0) + var.db.set('bot', 'volume', str(var.bot.volume_helper.plain_volume_set)) + log.info("web: volume down to %d" % (var.bot.volume_helper.plain_volume_set * 100)) elif action == "volume_set_value": if 'new_volume' in request.form: if float(request.form['new_volume']) > 1: - var.bot.convert_set_volume(1.0) + var.bot.volume_helper.set_volume(1.0) elif float(request.form['new_volume']) < 0: - var.bot.convert_set_volume(0) + var.bot.volume_helper.set_volume(0) else: # value for new volume is between 0 and 1, round to two decimal digits - var.bot.convert_set_volume(round(float(request.form['new_volume']), 2)) + var.bot.volume_helper.set_volume(round(float(request.form['new_volume']), 2)) - var.db.set('bot', 'volume', str(var.bot.get_displayed_volume())) - log.info("web: volume set to %d" % (var.bot.get_displayed_volume() * 100)) + var.db.set('bot', 'volume', str(var.bot.volume_helper.plain_volume_set)) + log.info("web: volume set to %d" % (var.bot.volume_helper.plain_volume_set * 100)) return status() diff --git a/mumbleBot.py b/mumbleBot.py index c1a4563..62fb609 100644 --- a/mumbleBot.py +++ b/mumbleBot.py @@ -39,13 +39,6 @@ class MumbleBot: signal.signal(signal.SIGINT, self.ctrl_caught) self.cmd_handle = {} - self._volume_set = 0 - self._volume = 0 - self.unconverted_volume = var.config.getfloat('bot', 'volume', fallback=0.1) - if var.db.has_option('bot', 'volume'): - self.unconverted_volume = var.db.getfloat('bot', 'volume') - self.convert_set_volume(self.unconverted_volume) - self.stereo = var.config.getboolean('bot', 'stereo', fallback=True) if args.channel: @@ -126,21 +119,30 @@ class MumbleBot: self.join_channel() self.mumble.set_bandwidth(200000) + # ====== Volume ====== + self.volume_helper = util.VolumeHelper() + + _volume = var.config.getfloat('bot', 'volume', fallback=0.1) + if var.db.has_option('bot', 'volume'): + _volume = var.db.getfloat('bot', 'volume') + self.volume_helper.set_volume(_volume) + self.is_ducking = False self.on_ducking = False self.ducking_release = time.time() self.last_volume_cycle_time = time.time() self._ducking_volume = 0 + _ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05) + _ducking_volume = var.db.getfloat("bot", "ducking_volume", fallback=_ducking_volume) + self.volume_helper.set_ducking_volume(_ducking_volume) + + self.ducking_threshold = var.config.getfloat("bot", "ducking_threshold", fallback=5000) + self.ducking_threshold = var.db.getfloat("bot", "ducking_threshold", fallback=self.ducking_threshold) + if not var.db.has_option("bot", "ducking") and var.config.getboolean("bot", "ducking", fallback=False) \ or var.config.getboolean("bot", "ducking"): self.is_ducking = True - self.unconverted_ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05) - self.unconverted_ducking_volume = var.db.getfloat("bot", "ducking_volume", - fallback=self.unconverted_ducking_volume) - self.convert_set_ducking_volume(self.unconverted_ducking_volume) - self.ducking_threshold = var.config.getfloat("bot", "ducking_threshold", fallback=5000) - self.ducking_threshold = var.db.getfloat("bot", "ducking_threshold", fallback=self.ducking_threshold) self.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED, self.ducking_sound_received) self.mumble.set_receive_sound(True) @@ -479,7 +481,8 @@ class MumbleBot: if raw_music: # Adjust the volume and send it to mumble self.volume_cycle() - self.mumble.sound_output.add_sound(audioop.mul(raw_music, 2, self._volume)) + self.mumble.sound_output.add_sound( + audioop.mul(raw_music, 2, self.volume_helper.real_volume)) else: time.sleep(0.1) else: @@ -558,11 +561,12 @@ class MumbleBot: if delta > 0.001: if self.is_ducking and self.on_ducking: - self._volume = (self._volume - self._ducking_volume) * math.exp(- delta / 0.2) + self._ducking_volume - elif self.on_killing.locked(): - self._volume = self._volume_set - (self._volume_set - self._volume) * math.exp(- delta / 0.05) + self.volume_helper.real_volume = \ + (self.volume_helper.real_volume - self.volume_helper.ducking_volume_set) * math.exp(- delta / 0.2) \ + + self.volume_helper.ducking_volume_set else: - self._volume = self._volume_set - (self._volume_set - self._volume) * math.exp(- delta / 0.5) + self.volume_helper.real_volume = self.volume_helper.volume_set - \ + (self.volume_helper.volume_set - self.volume_helper.real_volume) * math.exp(- delta / 0.5) self.last_volume_cycle_time = time.time() @@ -619,15 +623,15 @@ class MumbleBot: if not self.on_killing.locked(): self.on_killing.acquire() if self.thread: - volume_set = self._volume_set - self._volume_set = 0 + volume_set = self.volume_helper.plain_volume_set + self.volume_helper.plain_volume_set = 0 - while self._volume > 0.01 and self.thread: # Waiting for volume_cycle to gradually tune volume to 0. + while self.volume_helper.real_volume > 0.01 and self.thread: # Waiting for volume_cycle to gradually tune volume to 0. time.sleep(0.01) self.thread.kill() self.thread = None - self._volume_set = volume_set + self.volume_helper.plain_volume_set = volume_set self.on_killing.release() self.song_start_at = -1 @@ -658,23 +662,6 @@ class MumbleBot: self.wait_for_ready = True self.pause_at_id = "" - # map the volume_before_converting(0~1) into -60~0 dB, convert dB into a factor between 0~1. - def convert_set_volume(self, volume_before_converting): - self.unconverted_volume = volume_before_converting - dB = -60 + volume_before_converting * 60 - self._volume_set = 10 ** (dB / 20) - - def get_displayed_volume(self): - return self.unconverted_volume - - def convert_set_ducking_volume(self, volume_before_converting): - self.unconverted_ducking_volume = volume_before_converting - dB = -60 + volume_before_converting * 60 - self._ducking_volume = 10**(dB/20) - - def get_displayed_duck_volume(self): - return self.unconverted_ducking_volume - def start_web_interface(addr, port): global formatter diff --git a/util.py b/util.py index 44333a5..3b9359a 100644 --- a/util.py +++ b/util.py @@ -414,3 +414,34 @@ class LoggerIOWrapper(io.TextIOWrapper): else: self.logger.log(self.logging_level, text.rstrip()) super().write(text + "\n") + + +class VolumeHelper: + def __init__(self, plain_volume = 0, ducking_plain_volume = 0): + self.plain_volume_set = 0 + self.plain_ducking_volume_set = 0 + self.volume_set = 0 + self.ducking_volume_set = 0 + + self.real_volume = 0 + + self.set_volume(plain_volume) + self.set_ducking_volume(ducking_plain_volume) + + def set_volume(self, plain_volume): + self.volume_set = self._convert_volume(plain_volume) + self.plain_volume_set = plain_volume + + def set_ducking_volume(self, plain_volume): + self.ducking_volume_set = self._convert_volume(plain_volume) + self.plain_ducking_volume_set = plain_volume + + def _convert_volume(self, volume): + if volume == 0: + return 0 + + # convert input of 0~1 into -35~5 dB + dB = -35 + volume * 40 + + # Some dirty trick to stretch the function, to make to be 0 when input is -35 dB + return (10 ** (dB / 20) - 10 ** (-35 / 20)) / (1 - 10 ** (-35 / 20))