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

Replaygain backend r128gain #3055

Closed
wants to merge 6 commits into from
Closed
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
82 changes: 80 additions & 2 deletions beetsplug/replaygain.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ def compute_album_gain(self, album):
# individual tracks which can be used for any backend.
raise NotImplementedError()

def use_ebu_r128(self):
"""Set this Backend up to use EBU R128."""
pass


# bsg1770gain backend
class Bs1770gainBackend(Backend):
Expand Down Expand Up @@ -271,6 +275,10 @@ def end_element_handler(name):
out.append(album_gain["album"])
return out

def use_ebu_r128(self):
"""Set this Backend up to use EBU R128."""
self.method = '--ebu'


# mpgain/aacgain CLI tool backend.
class CommandBackend(Backend):
Expand Down Expand Up @@ -813,6 +821,71 @@ def compute_album_gain(self, album):
)


# r128gain (https://github.com/desbma/r128gain) backend
class R128gainBackend(Backend):
"""r128gain (https://github.com/desbma/r128gain) ReplayGain backend.

Provides a Backend to the replaygain plugin using the r128gain python
module.
"""

def __init__(self, config, log):
super(R128gainBackend, self).__init__(config, log)
self.__import_r128gain()

def __import_r128gain(self):
"""Try to import the r128gain module to self._mod_r128gain.
"""
try:
import r128gain
except ImportError:
raise FatalReplayGainError(
u"failed to load r128gain: please install r128gain"
)
self._mod_r128gain = r128gain

def compute_track_gain(self, items):
"""Computes the track gain of the given tracks, returns a list
of Gain objects (the track gains).
"""
gains, album_gain = self.compute_gain(items, False)
return gains

def compute_album_gain(self, album):
"""Computes the album gain of the given album, returns an
AlbumGain object.
"""
items = album.items()
gains, album_gain = self.compute_gain(items, True)
return AlbumGain(album_gain, gains)

def compute_gain(self, items, is_album):
"""Computes the track (and optionally album gain) of a list of
items, returns a pair of a list of Gain objects (the track gains)
and a optional Gain object (the album gain).
"""
# scan files
paths = [i.path.decode() for i in items]
self._log.debug(u"scanning {0} with r128gain".format(paths))
output = self._mod_r128gain.scan(paths, album_gain=is_album)

# track gains
try:
gains = [Gain(*output[path]) for path in paths]
except KeyError as e:
raise ReplayGainError(u"failed to analyse file: {0}".format(e))

# album gain
album_gain_key = self._mod_r128gain.ALBUM_GAIN_KEY
album_gain = output.pop(album_gain_key, None)
if is_album:
if album_gain is None:
raise ReplayGainError(u"failed to analyse album")
album_gain = Gain(*album_gain)

return gains, album_gain


# Main plugin logic.

class ReplayGainPlugin(BeetsPlugin):
Expand All @@ -824,8 +897,11 @@ class ReplayGainPlugin(BeetsPlugin):
"gstreamer": GStreamerBackend,
"audiotools": AudioToolsBackend,
"bs1770gain": Bs1770gainBackend,
"r128gain": R128gainBackend,
}

r128_backend_names = ["bs1770gain", "r128gain"]

def __init__(self):
super(ReplayGainPlugin, self).__init__()

Expand Down Expand Up @@ -1009,7 +1085,9 @@ def handle_track(self, item, write, force=False):
u"Fatal replay gain error: {0}".format(e))

def init_r128_backend(self):
backend_name = 'bs1770gain'
backend_name = self.config["backend"].as_str()
if backend_name not in self.r128_backend_names:
backend_name = "bs1770gain"

try:
self.r128_backend_instance = self.backends[backend_name](
Expand All @@ -1019,7 +1097,7 @@ def init_r128_backend(self):
raise ui.UserError(
u'replaygain initialization failed: {0}'.format(e))

self.r128_backend_instance.method = '--ebu'
self.r128_backend_instance.use_ebu_r128()

def imported(self, session, task):
"""Add replay gain info to items or albums of ``task``.
Expand Down
24 changes: 19 additions & 5 deletions docs/plugins/replaygain.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ playback levels.
Installation
------------

This plugin can use one of four backends to compute the ReplayGain values:
GStreamer, mp3gain (and its cousin, aacgain), Python Audio Tools and bs1770gain. mp3gain
can be easier to install but GStreamer, Audio Tools and bs1770gain support more audio
formats.
This plugin can use one of five backends to compute the ReplayGain values:
GStreamer, mp3gain (and its cousin, aacgain), Python Audio Tools, bs1770gain and
r128gain. mp3gain can be easier to install but GStreamer, Audio Tools, bs1770gain
and r128gain support more audio formats.

Once installed, this plugin analyzes all files during the import process. This
can be a slow process; to instead analyze after the fact, disable automatic
Expand Down Expand Up @@ -94,6 +94,20 @@ For Windows users: the tool currently has issues with long and non-ASCII path
names. You may want to use the :ref:`asciify-paths` configuration option until
this is resolved.

r128gain
````````

This backend uses the python module `r128gain`_. It can be installed from PyPI::

pip3 install r128gain

Also see the `r128gain installation`_ page.
Please note that `r128gain`_ describes itself as "beta software", this backend
could possibly break.

.. _r128gain: https://github.com/desbma/r128gain
.. _r128gain installation: https://github.com/desbma/r128gain/blob/master/README.md#installation

Configuration
-------------

Expand All @@ -110,7 +124,7 @@ configuration file. The available options are:
Default: 89.
- **r128**: A space separated list of formats that will use ``R128_`` tags with
integer values instead of the common ``REPLAYGAIN_`` tags with floating point
values. Requires the "bs1770gain" backend.
values. Requires the "bs1770gain" or "r128gain" backend.
Default: ``Opus``.

These options only work with the "command" backend:
Expand Down
11 changes: 11 additions & 0 deletions test/test_replaygain.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
else:
LOUDNESS_PROG_AVAILABLE = False

try:
import r128gain
R128GAIN_AVAILABLE = hasattr(r128gain, 'scan')
except ImportError:
R128GAIN_AVAILABLE = False


class ReplayGainCliTestBase(TestHelper):

Expand Down Expand Up @@ -166,6 +172,11 @@ class ReplayGainLdnsCliTest(ReplayGainCliTestBase, unittest.TestCase):
backend = u'bs1770gain'


@unittest.skipIf(not R128GAIN_AVAILABLE, u'r128gain cannot be found')
class ReplayGainR128gainCliTest(ReplayGainCliTestBase, unittest.TestCase):
backend = u'r128gain'


def suite():
return unittest.TestLoader().loadTestsFromName(__name__)

Expand Down