feat: use a function to convert volume set by the user to a scale of -60~0 dB. Then convert dB to a factor from 0 to 1 used as the real volume factor.

This commit is contained in:
Terry Geng 2020-06-08 17:30:37 +08:00
parent 32e32774eb
commit 5f67517dc3
No known key found for this signature in database
GPG Key ID: F982F8EA1DF720E7
3 changed files with 64 additions and 42 deletions

View File

@ -594,12 +594,12 @@ def cmd_volume(bot, user, text, command, parameter):
# The volume is a percentage # The volume is a percentage
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100: if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
bot.volume_set = float(float(parameter) / 100) bot.convert_set_volume(float(parameter) / 100.0)
bot.send_msg(constants.strings('change_volume', volume=int(bot.volume_set * 100), user=bot.mumble.users[text.actor]['name']), text) bot.send_msg(constants.strings('change_volume', volume=parameter, user=bot.mumble.users[text.actor]['name']), text)
var.db.set('bot', 'volume', str(bot.volume_set)) var.db.set('bot', 'volume', str(float(parameter) / 100.0))
log.info(f'cmd: volume set to {bot.volume_set * 100}') log.info(f'cmd: volume set to {float(parameter) / 100.0}')
else: else:
bot.send_msg(constants.strings('current_volume', volume=int(bot.volume_set * 100)), text) bot.send_msg(constants.strings('current_volume', volume=int(bot.get_displayed_volume() * 100)), text)
def cmd_ducking(bot, user, text, command, parameter): def cmd_ducking(bot, user, text, command, parameter):
@ -608,7 +608,7 @@ def cmd_ducking(bot, user, text, command, parameter):
if parameter == "" or parameter == "on": if parameter == "" or parameter == "on":
bot.is_ducking = True bot.is_ducking = True
var.db.set('bot', 'ducking', True) var.db.set('bot', 'ducking', True)
bot.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05) 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.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.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED, bot.ducking_sound_received)
bot.mumble.set_receive_sound(True) bot.mumble.set_receive_sound(True)
@ -642,12 +642,12 @@ def cmd_ducking_volume(bot, user, text, command, parameter):
# The volume is a percentage # The volume is a percentage
if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100: if parameter and parameter.isdigit() and 0 <= int(parameter) <= 100:
bot.ducking_volume = float(float(parameter) / 100) bot.convert_set_ducking_volume(float(parameter) / 100.0)
bot.send_msg(constants.strings('change_ducking_volume', volume=int(bot.ducking_volume * 100), user=bot.mumble.users[text.actor]['name']), text) bot.send_msg(constants.strings('change_ducking_volume', volume=parameter, user=bot.mumble.users[text.actor]['name']), text)
var.db.set('bot', 'ducking_volume', str(bot.ducking_volume)) var.db.set('bot', 'ducking_volume', parameter)
log.info(f'cmd: volume on ducking set to {bot.ducking_volume * 100}') log.info(f'cmd: volume on ducking set to {parameter}')
else: else:
bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.ducking_volume * 100)), text) bot.send_msg(constants.strings('current_ducking_volume', volume=int(bot.get_displayed_ducking_volume() * 100)), text)
def cmd_current_music(bot, user, text, command, parameter): def cmd_current_music(bot, user, text, command, parameter):

View File

@ -319,7 +319,7 @@ def status():
'empty': False, 'empty': False,
'play': not var.bot.is_pause, 'play': not var.bot.is_pause,
'mode': var.playlist.mode, 'mode': var.playlist.mode,
'volume': var.bot.volume_set, 'volume': var.bot.get_displayed_volume(),
'playhead': var.bot.playhead 'playhead': var.bot.playhead
}) })
@ -329,7 +329,7 @@ def status():
'empty': True, 'empty': True,
'play': not var.bot.is_pause, 'play': not var.bot.is_pause,
'mode': var.playlist.mode, 'mode': var.playlist.mode,
'volume': var.bot.volume_set, 'volume': var.bot.get_displayed_volume(),
'playhead': 0 'playhead': 0
}) })
@ -486,31 +486,31 @@ def post():
elif action == "clear": elif action == "clear":
var.bot.clear() var.bot.clear()
elif action == "volume_up": elif action == "volume_up":
if var.bot.volume_set + 0.03 < 1.0: if var.bot.get_displayed_volume() + 0.03 < 1.0:
var.bot.volume_set = var.bot.volume_set + 0.03 var.bot.convert_set_volume(var.bot.unconverted_volume + 0.03)
else: else:
var.bot.volume_set = 1.0 var.bot.convert_set_volume(1.0)
var.db.set('bot', 'volume', str(var.bot.volume_set)) var.db.set('bot', 'volume', str(var.bot.get_displayed_volume()))
log.info("web: volume up to %d" % (var.bot.volume_set * 100)) log.info("web: volume up to %d" % (var.bot.get_displayed_volume() * 100))
elif action == "volume_down": elif action == "volume_down":
if var.bot.volume_set - 0.03 > 0: if var.bot.get_displayed_volume() - 0.03 > 0:
var.bot.volume_set = var.bot.volume_set - 0.03 var.bot.convert_set_volume(var.bot.unconverted_volume - 0.03)
else: else:
var.bot.volume_set = 0 var.bot.convert_set_volume(1.0)
var.db.set('bot', 'volume', str(var.bot.volume_set)) var.db.set('bot', 'volume', str(var.bot.get_displayed_volume()))
log.info("web: volume up to %d" % (var.bot.volume_set * 100)) log.info("web: volume down to %d" % (var.bot.get_displayed_volume() * 100))
elif action == "volume_set_value": elif action == "volume_set_value":
if 'new_volume' in request.form: if 'new_volume' in request.form:
if float(request.form['new_volume']) > 1: if float(request.form['new_volume']) > 1:
var.bot.volume_set = 1 var.bot.convert_set_volume(1.0)
elif float(request.form['new_volume']) < 0: elif float(request.form['new_volume']) < 0:
var.bot.volume_set = 0 var.bot.convert_set_volume(0)
else: else:
# value for new volume is between 0 and 1, round to two decimal digits # value for new volume is between 0 and 1, round to two decimal digits
var.bot.volume_set = round(float(request.form['new_volume']), 2) var.bot.convert_set_volume(round(float(request.form['new_volume']), 2))
var.db.set('bot', 'volume', str(var.bot.volume_set)) var.db.set('bot', 'volume', str(var.bot.get_displayed_volume()))
log.info("web: volume set to %d" % (var.bot.volume_set * 100)) log.info("web: volume set to %d" % (var.bot.get_displayed_volume() * 100))
return status() return status()

View File

@ -38,10 +38,13 @@ class MumbleBot:
self.log.info(f"bot: botamusique version {self.version}, starting...") self.log.info(f"bot: botamusique version {self.version}, starting...")
signal.signal(signal.SIGINT, self.ctrl_caught) signal.signal(signal.SIGINT, self.ctrl_caught)
self.cmd_handle = {} self.cmd_handle = {}
self.volume_set = var.config.getfloat('bot', 'volume', fallback=0.1)
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'): if var.db.has_option('bot', 'volume'):
self.volume_set = var.db.getfloat('bot', 'volume') self.unconverted_volume = var.db.getfloat('bot', 'volume')
self.volume = self.volume_set self.convert_set_volume(self.unconverted_volume)
self.stereo = var.config.getboolean('bot', 'stereo', fallback=True) self.stereo = var.config.getboolean('bot', 'stereo', fallback=True)
@ -128,11 +131,14 @@ class MumbleBot:
self.ducking_release = time.time() self.ducking_release = time.time()
self.last_volume_cycle_time = time.time() self.last_volume_cycle_time = time.time()
self._ducking_volume = 0
if not var.db.has_option("bot", "ducking") and var.config.getboolean("bot", "ducking", fallback=False) \ if not var.db.has_option("bot", "ducking") and var.config.getboolean("bot", "ducking", fallback=False) \
or var.config.getboolean("bot", "ducking"): or var.config.getboolean("bot", "ducking"):
self.is_ducking = True self.is_ducking = True
self.ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05) self.unconverted_ducking_volume = var.config.getfloat("bot", "ducking_volume", fallback=0.05)
self.ducking_volume = var.db.getfloat("bot", "ducking_volume", fallback=self.ducking_volume) 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.config.getfloat("bot", "ducking_threshold", fallback=5000)
self.ducking_threshold = var.db.getfloat("bot", "ducking_threshold", fallback=self.ducking_threshold) self.ducking_threshold = var.db.getfloat("bot", "ducking_threshold", fallback=self.ducking_threshold)
self.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED, self.mumble.callbacks.set_callback(pymumble.constants.PYMUMBLE_CLBK_SOUNDRECEIVED,
@ -473,8 +479,7 @@ class MumbleBot:
if raw_music: if raw_music:
# Adjust the volume and send it to mumble # Adjust the volume and send it to mumble
self.volume_cycle() self.volume_cycle()
# https://stackoverflow.com/questions/1165026/what-algorithms-could-i-use-for-audio-volume-level self.mumble.sound_output.add_sound(audioop.mul(raw_music, 2, self._volume))
self.mumble.sound_output.add_sound(audioop.mul(raw_music, 2, math.pow(self.volume, 2.7)))
else: else:
time.sleep(0.1) time.sleep(0.1)
else: else:
@ -553,11 +558,11 @@ class MumbleBot:
if delta > 0.001: if delta > 0.001:
if self.is_ducking and self.on_ducking: if self.is_ducking and self.on_ducking:
self.volume = (self.volume - self.ducking_volume) * math.exp(- delta / 0.2) + self.ducking_volume self._volume = (self._volume - self._ducking_volume) * math.exp(- delta / 0.2) + self._ducking_volume
elif self.on_killing.locked(): elif self.on_killing.locked():
self.volume = self.volume_set - (self.volume_set - self.volume) * math.exp(- delta / 0.05) self._volume = self._volume_set - (self._volume_set - self._volume) * math.exp(- delta / 0.05)
else: else:
self.volume = self.volume_set - (self.volume_set - self.volume) * math.exp(- delta / 0.5) self._volume = self._volume_set - (self._volume_set - self._volume) * math.exp(- delta / 0.5)
self.last_volume_cycle_time = time.time() self.last_volume_cycle_time = time.time()
@ -614,15 +619,15 @@ class MumbleBot:
if not self.on_killing.locked(): if not self.on_killing.locked():
self.on_killing.acquire() self.on_killing.acquire()
if self.thread: if self.thread:
volume_set = self.volume_set volume_set = self._volume_set
self.volume_set = 0 self._volume_set = 0
while self.volume > 0.01 and self.thread: # Waiting for volume_cycle to gradually tune volume to 0. while self._volume > 0.01 and self.thread: # Waiting for volume_cycle to gradually tune volume to 0.
time.sleep(0.01) time.sleep(0.01)
self.thread.kill() self.thread.kill()
self.thread = None self.thread = None
self.volume_set = volume_set self._volume_set = volume_set
self.on_killing.release() self.on_killing.release()
self.song_start_at = -1 self.song_start_at = -1
@ -653,6 +658,23 @@ class MumbleBot:
self.wait_for_ready = True self.wait_for_ready = True
self.pause_at_id = "" 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): def start_web_interface(addr, port):
global formatter global formatter