feat: add detailed ffprobe logs

This commit is contained in:
Félix Voituret
2019-11-20 14:33:18 +01:00
parent eb32348c34
commit 7b5222a64a
10 changed files with 58 additions and 41 deletions

View File

@@ -16,3 +16,9 @@
__email__ = 'research@deezer.com' __email__ = 'research@deezer.com'
__author__ = 'Deezer Research' __author__ = 'Deezer Research'
__license__ = 'MIT License' __license__ = 'MIT License'
class SpleeterError(Exception):
""" Custom exception for Spleeter related error. """
pass

View File

@@ -10,9 +10,13 @@
import sys import sys
import warnings import warnings
from . import SpleeterError
from .commands import create_argument_parser from .commands import create_argument_parser
from .utils.configuration import load_configuration from .utils.configuration import load_configuration
from .utils.logging import enable_logging, enable_tensorflow_logging from .utils.logging import (
enable_logging,
enable_tensorflow_logging,
get_logger)
__email__ = 'research@deezer.com' __email__ = 'research@deezer.com'
__author__ = 'Deezer Research' __author__ = 'Deezer Research'
@@ -26,19 +30,22 @@ def main(argv):
:param argv: Provided command line arguments. :param argv: Provided command line arguments.
""" """
parser = create_argument_parser() try:
arguments = parser.parse_args(argv[1:]) parser = create_argument_parser()
enable_logging() arguments = parser.parse_args(argv[1:])
if arguments.verbose: enable_logging()
enable_tensorflow_logging() if arguments.verbose:
if arguments.command == 'separate': enable_tensorflow_logging()
from .commands.separate import entrypoint if arguments.command == 'separate':
elif arguments.command == 'train': from .commands.separate import entrypoint
from .commands.train import entrypoint elif arguments.command == 'train':
elif arguments.command == 'evaluate': from .commands.train import entrypoint
from .commands.evaluate import entrypoint elif arguments.command == 'evaluate':
params = load_configuration(arguments.params_filename) from .commands.evaluate import entrypoint
entrypoint(arguments, params) params = load_configuration(arguments.configuration)
entrypoint(arguments, params)
except SpleeterError as e:
get_logger().error(e)
def entrypoint(): def entrypoint():

View File

@@ -16,6 +16,7 @@ import tensorflow as tf
from tensorflow.contrib.signal import stft, hann_window from tensorflow.contrib.signal import stft, hann_window
# pylint: enable=import-error # pylint: enable=import-error
from .. import SpleeterError
from ..utils.logging import get_logger from ..utils.logging import get_logger
__email__ = 'research@deezer.com' __email__ = 'research@deezer.com'
@@ -73,7 +74,8 @@ class AudioAdapter(ABC):
# Defined safe loading function. # Defined safe loading function.
def safe_load(path, offset, duration, sample_rate, dtype): def safe_load(path, offset, duration, sample_rate, dtype):
get_logger().info( logger = get_logger()
logger.info(
f'Loading audio {path} from {offset} to {offset + duration}') f'Loading audio {path} from {offset} to {offset + duration}')
try: try:
(data, _) = self.load( (data, _) = self.load(
@@ -82,10 +84,12 @@ class AudioAdapter(ABC):
duration.numpy(), duration.numpy(),
sample_rate.numpy(), sample_rate.numpy(),
dtype=dtype.numpy()) dtype=dtype.numpy())
get_logger().info('Audio data loaded successfully') logger.info('Audio data loaded successfully')
return (data, False) return (data, False)
except Exception as e: except Exception as e:
get_logger().warning(e) logger.exception(
'An error occurs while loading audio',
exc_info=e)
return (np.float32(-1.0), True) return (np.float32(-1.0), True)
# Execute function and format results. # Execute function and format results.
@@ -140,6 +144,6 @@ def get_audio_adapter(descriptor):
adapter_module = import_module(module_path) adapter_module = import_module(module_path)
adapter_class = getattr(adapter_module, adapter_class_name) adapter_class = getattr(adapter_module, adapter_class_name)
if not isinstance(adapter_class, AudioAdapter): if not isinstance(adapter_class, AudioAdapter):
raise ValueError( raise SpleeterError(
f'{adapter_class_name} is not a valid AudioAdapter class') f'{adapter_class_name} is not a valid AudioAdapter class')
return adapter_class() return adapter_class()

View File

@@ -16,6 +16,7 @@ import numpy as np
# pylint: enable=import-error # pylint: enable=import-error
from .adapter import AudioAdapter from .adapter import AudioAdapter
from .. import SpleeterError
from ..utils.logging import get_logger from ..utils.logging import get_logger
__email__ = 'research@deezer.com' __email__ = 'research@deezer.com'
@@ -54,12 +55,18 @@ class FFMPEGProcessAudioAdapter(AudioAdapter):
:param sample_rate: (Optional) Sample rate to load audio with. :param sample_rate: (Optional) Sample rate to load audio with.
:param dtype: (Optional) Numpy data type to use, default to float32. :param dtype: (Optional) Numpy data type to use, default to float32.
:returns: Loaded data a (waveform, sample_rate) tuple. :returns: Loaded data a (waveform, sample_rate) tuple.
:raise SpleeterError: If any error occurs while loading audio.
""" """
if not isinstance(path, str): if not isinstance(path, str):
path = path.decode() path = path.decode()
probe = ffmpeg.probe(path) try:
probe = ffmpeg.probe(path)
except ffmpeg._run.Error as e:
raise SpleeterError(
'An error occurs with ffprobe (see ffprobe output below)\n\n{}'
.format(e.stderr.decode()))
if 'streams' not in probe or len(probe['streams']) == 0: if 'streams' not in probe or len(probe['streams']) == 0:
raise IOError('No stream was found with ffprobe') raise SpleeterError('No stream was found with ffprobe')
metadata = next( metadata = next(
stream stream
for stream in probe['streams'] for stream in probe['streams']
@@ -117,5 +124,5 @@ class FFMPEGProcessAudioAdapter(AudioAdapter):
process.stdin.close() process.stdin.close()
process.wait() process.wait()
except IOError: except IOError:
raise IOError(f'FFMPEG error: {process.stderr.read()}') raise SpleeterError(f'FFMPEG error: {process.stderr.read()}')
get_logger().info('File %s written', path) get_logger().info('File %s written', path)

View File

@@ -84,7 +84,6 @@ OPT_CODEC = {
# -b opt specification (separate). # -b opt specification (separate).
OPT_BITRATE = { OPT_BITRATE = {
'dest': 'bitrate', 'dest': 'bitrate',
'type': int,
'default': '128k', 'default': '128k',
'help': 'Audio bitrate to be used for the separated output' 'help': 'Audio bitrate to be used for the separated output'
} }
@@ -177,8 +176,8 @@ def _create_separate_parser(parser_factory):
parser.add_argument('-i', '--inputs', **OPT_INPUT) parser.add_argument('-i', '--inputs', **OPT_INPUT)
parser.add_argument('-o', '--output_path', **OPT_OUTPUT) parser.add_argument('-o', '--output_path', **OPT_OUTPUT)
parser.add_argument('-n', '--output_naming', **OPT_OUTPUT_NAMING) parser.add_argument('-n', '--output_naming', **OPT_OUTPUT_NAMING)
parser.add_
parser.add_argument('-d', '--duration', **OPT_DURATION) parser.add_argument('-d', '--duration', **OPT_DURATION)
parser.add_argument('-s', '--offset', **OPT_OFFSET)
parser.add_argument('-c', '--codec', **OPT_CODEC) parser.add_argument('-c', '--codec', **OPT_CODEC)
parser.add_argument('-b', '--birate', **OPT_BITRATE) parser.add_argument('-b', '--birate', **OPT_BITRATE)
parser.add_argument('-m', '--mwf', **OPT_MWF) parser.add_argument('-m', '--mwf', **OPT_MWF)

View File

@@ -28,13 +28,13 @@ def entrypoint(arguments, params):
# TODO: check with output naming. # TODO: check with output naming.
audio_adapter = get_audio_adapter(arguments.audio_adapter) audio_adapter = get_audio_adapter(arguments.audio_adapter)
separator = Separator(arguments.configuration, arguments.MWF) separator = Separator(arguments.configuration, arguments.MWF)
for filename in arguments.audio_filenames: for filename in arguments.inputs:
separator.separate_to_file( separator.separate_to_file(
filename, filename,
arguments.output_path, arguments.output_path,
audio_adapter=audio_adapter, audio_adapter=audio_adapter,
offset=arguments.offset, offset=arguments.offset,
duration=arguments.max_duration, duration=arguments.duration,
codec=arguments.codec, codec=arguments.codec,
bitrate=arguments.bitrate, bitrate=arguments.bitrate,
synchronous=False synchronous=False

View File

@@ -57,7 +57,7 @@ class Separator(object):
self._predictor = to_predictor(estimator) self._predictor = to_predictor(estimator)
return self._predictor return self._predictor
def join(self, timeout=20): def join(self, timeout=200):
""" Wait for all pending tasks to be finished. """ Wait for all pending tasks to be finished.
:param timeout: (Optional) task waiting timeout. :param timeout: (Optional) task waiting timeout.

View File

@@ -13,7 +13,7 @@ except ImportError:
from os.path import exists from os.path import exists
from .. import resources from .. import resources, SpleeterError
__email__ = 'research@deezer.com' __email__ = 'research@deezer.com'
@@ -31,17 +31,17 @@ def load_configuration(descriptor):
:param descriptor: Configuration descriptor to use for lookup. :param descriptor: Configuration descriptor to use for lookup.
:returns: Loaded description as dict. :returns: Loaded description as dict.
:raise ValueError: If required embedded configuration does not exists. :raise ValueError: If required embedded configuration does not exists.
:raise IOError: If required configuration file does not exists. :raise SpleeterError: If required configuration file does not exists.
""" """
# Embedded configuration reading. # Embedded configuration reading.
if descriptor.startswith(_EMBEDDED_CONFIGURATION_PREFIX): if descriptor.startswith(_EMBEDDED_CONFIGURATION_PREFIX):
name = descriptor[len(_EMBEDDED_CONFIGURATION_PREFIX):] name = descriptor[len(_EMBEDDED_CONFIGURATION_PREFIX):]
if not loader.is_resource(resources, f'{name}.json'): if not loader.is_resource(resources, f'{name}.json'):
raise ValueError(f'No embedded configuration {name} found') raise SpleeterError(f'No embedded configuration {name} found')
with loader.open_text(resources, f'{name}.json') as stream: with loader.open_text(resources, f'{name}.json') as stream:
return json.load(stream) return json.load(stream)
# Standard file reading. # Standard file reading.
if not exists(descriptor): if not exists(descriptor):
raise IOError(f'Configuration file {descriptor} not found') raise SpleeterError(f'Configuration file {descriptor} not found')
with open(descriptor, 'r') as stream: with open(descriptor, 'r') as stream:
return json.load(stream) return json.load(stream)

View File

@@ -1,4 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
# coding: utf8 # coding: utf8
""" TO DOCUMENT """ """ Unit testing package. """
__email__ = 'research@deezer.com'
__author__ = 'Deezer Research'
__license__ = 'MIT License'

View File

@@ -1,10 +0,0 @@
#!/bin/bash
######################################################################
# Script that performs PyPi packaging test.
#
# @author Deezer Research <research@deezer.com>
# @version 1.0.0
######################################################################
twine upload --repository-url https://test.pypi.org/legacy/ dist/*