From d771fbae34a5e4cd9dcc732e42c72491afa42b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Voituret?= Date: Wed, 20 Nov 2019 15:07:12 +0100 Subject: [PATCH] feat: add filename format --- spleeter/commands/__init__.py | 31 +++++++++++++------------------ spleeter/commands/evaluate.py | 2 -- spleeter/commands/separate.py | 5 ++++- spleeter/separator.py | 22 +++++++++++++++++++--- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/spleeter/commands/__init__.py b/spleeter/commands/__init__.py index 47e8f53..2bbc974 100644 --- a/spleeter/commands/__init__.py +++ b/spleeter/commands/__init__.py @@ -28,6 +28,18 @@ OPT_OUTPUT = { 'help': 'Path of the output directory to write audio files in' } +# -f opt specification (separate). +OPT_FORMAT = { + 'dest': 'filename_format', + 'default': '{filename}/{instrument}.{codec}', + 'help': ( + 'Template string that will be formatted to generated' + 'output filename. Such template should be Python formattable' + 'string, and could use {filename}, {instrument}, and {codec}' + 'variables.' + ) +} + # -p opt specification (train, evaluate and separate). OPT_PARAMS = { 'dest': 'configuration', @@ -37,23 +49,6 @@ OPT_PARAMS = { 'help': 'JSON filename that contains params' } -# -n opt specification (separate). -OPT_OUTPUT_NAMING = { - 'dest': 'output_naming', - 'default': 'filename', - 'choices': ('directory', 'filename'), - 'help': ( - 'Choice for naming the output base path: ' - '"filename" (use the input filename, i.e ' - '/path/to/audio/mix.wav will be separated to ' - '/mix/.wav, ' - '/mix/.wav...) or ' - '"directory" (use the name of the input last level' - ' directory, for instance /path/to/audio/mix.wav ' - 'will be separated to /audio/.wav' - ', /audio/.wav)') -} - # -s opt specification (separate). OPT_OFFSET = { 'dest': 'offset', @@ -175,7 +170,7 @@ def _create_separate_parser(parser_factory): _add_common_options(parser) parser.add_argument('-i', '--inputs', **OPT_INPUT) parser.add_argument('-o', '--output_path', **OPT_OUTPUT) - parser.add_argument('-n', '--output_naming', **OPT_OUTPUT_NAMING) + parser.add_argument('-f', '--filename_format', **OPT_FORMAT) parser.add_argument('-d', '--duration', **OPT_DURATION) parser.add_argument('-s', '--offset', **OPT_OFFSET) parser.add_argument('-c', '--codec', **OPT_CODEC) diff --git a/spleeter/commands/evaluate.py b/spleeter/commands/evaluate.py index c2fe789..9bc6d96 100644 --- a/spleeter/commands/evaluate.py +++ b/spleeter/commands/evaluate.py @@ -44,7 +44,6 @@ __license__ = 'MIT License' _SPLIT = 'test' _MIXTURE = 'mixture.wav' -_NAMING = 'directory' _AUDIO_DIRECTORY = 'audio' _METRICS_DIRECTORY = 'metrics' _INSTRUMENTS = ('vocals', 'drums', 'bass', 'other') @@ -71,7 +70,6 @@ def _separate_evaluation_dataset(arguments, musdb_root_directory, params): audio_filenames=mixtures, audio_codec='wav', output_path=join(audio_output_directory, _SPLIT), - output_naming=_NAMING, max_duration=600., MWF=arguments.MWF, verbose=arguments.verbose), diff --git a/spleeter/commands/separate.py b/spleeter/commands/separate.py index b501205..b1f41ab 100644 --- a/spleeter/commands/separate.py +++ b/spleeter/commands/separate.py @@ -27,7 +27,9 @@ def entrypoint(arguments, params): """ # TODO: check with output naming. audio_adapter = get_audio_adapter(arguments.audio_adapter) - separator = Separator(arguments.configuration, arguments.MWF) + separator = Separator( + arguments.configuration, + arguments.MWF) for filename in arguments.inputs: separator.separate_to_file( filename, @@ -37,6 +39,7 @@ def entrypoint(arguments, params): duration=arguments.duration, codec=arguments.codec, bitrate=arguments.bitrate, + filename_format=arguments.filename_format, synchronous=False ) separator.join() diff --git a/spleeter/separator.py b/spleeter/separator.py index 79478d9..6c46315 100644 --- a/spleeter/separator.py +++ b/spleeter/separator.py @@ -18,8 +18,9 @@ import json from functools import partial from multiprocessing import Pool from pathlib import Path -from os.path import join +from os.path import basename, join +from . import SpleeterError from .audio.adapter import get_default_audio_adapter from .audio.convertor import to_stereo from .model import model_fn @@ -93,10 +94,13 @@ class Separator(object): self, audio_descriptor, destination, audio_adapter=get_default_audio_adapter(), offset=0, duration=600., codec='wav', bitrate='128k', - synchronous=True): + filename_format='{filename}/{instrument}.{codec}', synchronous=True): """ Performs source separation and export result to file using given audio adapter. + Filename format should be a Python formattable string that could use + following parameters : {instrument}, {filename} and {codec}. + :param audio_descriptor: Describe song to separate, used by audio adapter to retrieve and load audio data, in case of file based audio adapter, such @@ -107,6 +111,7 @@ class Separator(object): :param duration: (Optional) Duration of loaded song. :param codec: (Optional) Export codec. :param bitrate: (Optional) Export bitrate. + :param filename_format: (Optional) Filename format. :param synchronous: (Optional) True is should by synchronous. """ waveform, _ = audio_adapter.load( @@ -115,9 +120,20 @@ class Separator(object): duration=duration, sample_rate=self._sample_rate) sources = self.separate(waveform) + filename = basename(audio_descriptor) + generated = [] for instrument, data in sources.items(): + path = join(destination, filename_format.format( + filename=filename, + instrument=instrument, + codec=codec)) + if path in generated: + raise SpleeterError(( + f'Separated source path conflict : {path},' + 'please check your filename format')) + generated.append(path) task = self._pool.apply_async(audio_adapter.save, ( - join(destination, f'{instrument}.{codec}'), + path, data, self._sample_rate, codec,