mirror of
https://github.com/YuzuZensai/spleeter.git
synced 2026-01-31 14:58:23 +00:00
feat: add detailed ffprobe logs
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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():
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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/*
|
|
||||||
Reference in New Issue
Block a user