Handle bs1770gain v0.6.0 XML output

* Remove `0%\x08\x08` from output (backspace code doesn't resolve; progress percentages get spliced in)

* Handle changed attributes/fields:
  * `sample-peak` attribute `factor` is called `amplitude` instead
  * Album summary is not included in a `summary` tag now, but in two separate `integrated` and `sample-peak` tags

* Handle `lu` attribute

* Get bs1770gain version
  * If v0.6.0 or later, add `--unit=ebu` flag to convert `db` attributes to LUFS
  * May be useful later on

### Output examples

Track:
```
<!-- analyzing ... -->
<bs1770gain norm="-18.00">
  <track total="1" number="1" file="02 tïtle 0.mp3">
    <integrated lufs="-70.00" lu="52.00"/>
    <sample-peak spfs="-72.28" amplitude="0.00"/>
  </track>
  <integrated lufs="-70.00" lu="52.00"/>
  <sample-peak spfs="-72.28" amplitude="0.00"/>
</bs1770gain>
<!-- done. -->
```

Album:
```
<!-- analyzing ... -->
<bs1770gain norm="-18.00">
  <track total="2" number="1" file="02 tïtle 0.mp3">
    <integrated dbfs="-70.00" db="52.00"/>
    <sample-peak dbfs="-72.28" amplitude="0.00"/>
  </track>
  <track total="2" number="2" file="02 tïtle 1.mp3">
    <integrated dbfs="-70.00" db="52.00"/>
    <sample-peak dbfs="-72.28" amplitude="0.00"/>
  </track>
  <integrated dbfs="-70.00" db="52.00"/>
  <sample-peak dbfs="-72.28" amplitude="0.00"/>
</bs1770gain>
<!-- done. -->
```
This commit is contained in:
ybnd 2020-01-28 09:45:20 +01:00
parent ca3142b1d9
commit 53820c0a98
2 changed files with 31 additions and 4 deletions

View file

@ -22,6 +22,7 @@ import math
import sys
import warnings
import enum
import re
import xml.parsers.expat
from six.moves import zip
@ -135,6 +136,11 @@ class Bs1770gainBackend(Backend):
-18: "replaygain",
}
version = re.search(
'bs1770gain ([0-9]+.[0-9]+.[0-9]+), ',
call(['bs1770gain', '--version']).stdout.decode('utf-8')
).group(1)
def __init__(self, config, log):
super(Bs1770gainBackend, self).__init__(config, log)
config.add({
@ -252,6 +258,8 @@ class Bs1770gainBackend(Backend):
cmd = [self.command]
cmd += ["--" + method]
cmd += ['--xml', '-p']
if self.version >= '0.6.0':
cmd += ['--unit=ebu'] # set units to LU
# Workaround for Windows: the underlying tool fails on paths
# with the \\?\ prefix, so we don't use it here. This
@ -286,6 +294,7 @@ class Bs1770gainBackend(Backend):
album_gain = {} # mutable variable so it can be set from handlers
parser = xml.parsers.expat.ParserCreate(encoding='utf-8')
state = {'file': None, 'gain': None, 'peak': None}
album_state = {'gain': None, 'peak': None}
def start_element_handler(name, attrs):
if name == u'track':
@ -294,9 +303,13 @@ class Bs1770gainBackend(Backend):
raise ReplayGainError(
u'duplicate filename in bs1770gain output')
elif name == u'integrated':
state['gain'] = float(attrs[u'lu'])
if 'lu' in attrs:
state['gain'] = float(attrs[u'lu'])
elif name == u'sample-peak':
state['peak'] = float(attrs[u'factor'])
if 'factor' in attrs:
state['peak'] = float(attrs[u'factor'])
elif 'amplitude' in attrs:
state['peak'] = float(attrs[u'amplitude'])
def end_element_handler(name):
if name == u'track':
@ -312,10 +325,25 @@ class Bs1770gainBackend(Backend):
'the output of bs1770gain')
album_gain["album"] = Gain(state['gain'], state['peak'])
state['gain'] = state['peak'] = None
elif len(per_file_gain) == len(path_list):
if state['gain'] is not None:
album_state['gain'] = state['gain']
if state['peak'] is not None:
album_state['peak'] = state['peak']
if album_state['gain'] is not None \
and album_state['peak'] is not None:
album_gain["album"] = Gain(
album_state['gain'], album_state['peak'])
state['gain'] = state['peak'] = None
parser.StartElementHandler = start_element_handler
parser.EndElementHandler = end_element_handler
try:
if type(text) == bytes:
text = text.decode('utf-8')
while '\x08' in text:
text = re.sub('[^\x08]\x08', '', text)
parser.Parse(text, True)
except xml.parsers.expat.ExpatError:
raise ReplayGainError(

View file

@ -40,7 +40,7 @@ if any(has_program(cmd, ['-v']) for cmd in ['mp3gain', 'aacgain']):
else:
GAIN_PROG_AVAILABLE = False
if has_program('bs1770gain', ['--replaygain']):
if has_program('bs1770gain'):
LOUDNESS_PROG_AVAILABLE = True
else:
LOUDNESS_PROG_AVAILABLE = False
@ -58,7 +58,6 @@ def reset_replaygain(item):
class ReplayGainCliTestBase(TestHelper):
def setUp(self):
self.setup_beets()
self.config['replaygain']['backend'] = self.backend