Merge pull request #3137 from samuelnilsson/replaygain

per_disc option for the ReplayGain plugin
This commit is contained in:
Adrian Sampson 2019-06-08 16:15:25 -04:00 committed by GitHub
commit aea54e2487
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 43 deletions

View file

@ -83,7 +83,7 @@ class Backend(object):
def compute_track_gain(self, items):
raise NotImplementedError()
def compute_album_gain(self, album):
def compute_album_gain(self, items):
# TODO: implement album gain in terms of track gain of the
# individual tracks which can be used for any backend.
raise NotImplementedError()
@ -125,15 +125,14 @@ class Bs1770gainBackend(Backend):
output = self.compute_gain(items, False)
return output
def compute_album_gain(self, album):
def compute_album_gain(self, items):
"""Computes the album gain of the given album, returns an
AlbumGain object.
"""
# TODO: What should be done when not all tracks in the album are
# supported?
supported_items = album.items()
output = self.compute_gain(supported_items, True)
output = self.compute_gain(items, True)
if not output:
raise ReplayGainError(u'no output from bs1770gain')
@ -323,15 +322,15 @@ class CommandBackend(Backend):
output = self.compute_gain(supported_items, False)
return output
def compute_album_gain(self, album):
def compute_album_gain(self, items):
"""Computes the album gain of the given album, returns an
AlbumGain object.
"""
# TODO: What should be done when not all tracks in the album are
# supported?
supported_items = list(filter(self.format_supported, album.items()))
if len(supported_items) != len(album.items()):
supported_items = list(filter(self.format_supported, items))
if len(supported_items) != len(items):
self._log.debug(u'tracks are of unsupported format')
return AlbumGain(None, [])
@ -528,8 +527,8 @@ class GStreamerBackend(Backend):
return ret
def compute_album_gain(self, album):
items = list(album.items())
def compute_album_gain(self, items):
items = list(items)
self.compute(items, True)
if len(self._file_tags) != len(items):
raise ReplayGainError(u"Some items in album did not receive tags")
@ -784,22 +783,20 @@ class AudioToolsBackend(Backend):
item.artist, item.title, rg_track_gain, rg_track_peak)
return Gain(gain=rg_track_gain, peak=rg_track_peak)
def compute_album_gain(self, album):
def compute_album_gain(self, items):
"""Compute ReplayGain values for the requested album and its items.
:rtype: :class:`AlbumGain`
"""
self._log.debug(u'Analysing album {0}', album)
# The first item is taken and opened to get the sample rate to
# initialize the replaygain object. The object is used for all the
# tracks in the album to get the album values.
item = list(album.items())[0]
item = list(items)[0]
audiofile = self.open_audio_file(item)
rg = self.init_replaygain(audiofile, item)
track_gains = []
for item in album.items():
for item in items:
audiofile = self.open_audio_file(item)
rg_track_gain, rg_track_peak = self._title_gain(rg, audiofile)
track_gains.append(
@ -812,7 +809,7 @@ class AudioToolsBackend(Backend):
# album values.
rg_album_gain, rg_album_peak = rg.album_gain()
self._log.debug(u'ReplayGain for album {0}: {1:.2f}, {2:.2f}',
album, rg_album_gain, rg_album_peak)
items[0].album, rg_album_gain, rg_album_peak)
return AlbumGain(
Gain(gain=rg_album_gain, peak=rg_album_peak),
@ -843,9 +840,11 @@ class ReplayGainPlugin(BeetsPlugin):
'backend': u'command',
'targetlevel': 89,
'r128': ['Opus'],
'per_disc': False
})
self.overwrite = self.config['overwrite'].get(bool)
self.per_disc = self.config['per_disc'].get(bool)
backend_name = self.config['backend'].as_str()
if backend_name not in self.backends:
raise ui.UserError(
@ -912,19 +911,18 @@ class ReplayGainPlugin(BeetsPlugin):
self._log.debug(u'applied r128 track gain {0}', item.r128_track_gain)
def store_album_gain(self, album, album_gain):
album.rg_album_gain = album_gain.gain
album.rg_album_peak = album_gain.peak
album.store()
def store_album_gain(self, item, album_gain):
item.rg_album_gain = album_gain.gain
item.rg_album_peak = album_gain.peak
item.store()
self._log.debug(u'applied album gain {0}, peak {1}',
album.rg_album_gain, album.rg_album_peak)
item.rg_album_gain, item.rg_album_peak)
def store_album_r128_gain(self, album, album_gain):
album.r128_album_gain = int(round(album_gain.gain * pow(2, 8)))
album.store()
self._log.debug(u'applied r128 album gain {0}', album.r128_album_gain)
def store_album_r128_gain(self, item, album_gain):
item.r128_album_gain = album_gain.gain
item.store()
self._log.debug(u'applied r128 album gain {0}',
item.r128_album_gain)
def handle_album(self, album, write, force=False):
"""Compute album and track replay gain store it in all of the
@ -958,24 +956,34 @@ class ReplayGainPlugin(BeetsPlugin):
store_track_gain = self.store_track_gain
store_album_gain = self.store_album_gain
try:
album_gain = backend_instance.compute_album_gain(album)
if len(album_gain.track_gains) != len(album.items()):
raise ReplayGainError(
u"ReplayGain backend failed "
u"for some tracks in album {0}".format(album)
)
discs = dict()
if self.per_disc:
for item in album.items():
if discs.get(item.disc) is None:
discs[item.disc] = []
discs[item.disc].append(item)
else:
discs[1] = album.items()
store_album_gain(album, album_gain.album_gain)
for item, track_gain in zip(album.items(), album_gain.track_gains):
store_track_gain(item, track_gain)
if write:
item.try_write()
except ReplayGainError as e:
self._log.info(u"ReplayGain error: {0}", e)
except FatalReplayGainError as e:
raise ui.UserError(
u"Fatal replay gain error: {0}".format(e))
for discnumber, items in discs.items():
try:
album_gain = backend_instance.compute_album_gain(items)
if len(album_gain.track_gains) != len(items):
raise ReplayGainError(
u"ReplayGain backend failed "
u"for some tracks in album {0}".format(album)
)
for item, track_gain in zip(items, album_gain.track_gains):
store_track_gain(item, track_gain)
store_album_gain(item, album_gain.album_gain)
if write:
item.try_write()
except ReplayGainError as e:
self._log.info(u"ReplayGain error: {0}", e)
except FatalReplayGainError as e:
raise ui.UserError(
u"Fatal replay gain error: {0}".format(e))
def handle_track(self, item, write, force=False):
"""Compute track replay gain and store it in the item.

View file

@ -17,6 +17,11 @@ New features:
to MPD commands we don't support yet. Let us know if you find an MPD client
that doesn't get along with BPD!
:bug:`3214` :bug:`800`
* :doc:`/plugins/replaygain`: The plugin now supports a ``per_disc`` option
which enables calculation of album ReplayGain on disc level instead of album
level.
Thanks to :user:`samuelnilsson`
:bug:`293`
Fixes:

View file

@ -93,6 +93,8 @@ configuration file. The available options are:
integer values instead of the common ``REPLAYGAIN_`` tags with floating point
values. Requires the "ffmpeg" backend.
Default: ``Opus``.
- **per_disc**: Calculate album ReplayGain on disc level instead of album level.
Default: ``no``
These options only work with the "command" backend: