🐛 🎨 WIP: typer integration

This commit is contained in:
Faylixe
2020-12-07 16:08:12 +01:00
parent 3b72b6a0ff
commit f02bcbd9c7
4 changed files with 245 additions and 143 deletions

View File

@@ -1,58 +1,174 @@
#!/usr/bin/env python #!/usr/bin/env python
# coding: utf8 # coding: utf8
""" """ TO DOCUMENT """
Python oneliner script usage.
USAGE: python -m spleeter {train,evaluate,separate} ... from pathlib import Path
""" from os.path import join
from spleeter.separator import STFTBackend
from tempfile import gettempdir
from typing import List
import sys from .audio import Codec
import warnings from .audio.adapter import AudioAdapter
from .separator import Separator
from . import SpleeterError # pyright: reportMissingImports=false
from .commands import create_argument_parser # pylint: disable=import-error
from .utils.configuration import load_configuration from typer import Argument, Option, Typer
from .utils.logging import ( from typer.models import OptionInfo
enable_logging, # pylint: enable=import-error
enable_tensorflow_logging,
get_logger)
__email__ = 'spleeter@deezer.com' spleeter: Typer = Typer()
__author__ = 'Deezer Research' """ """
__license__ = 'MIT License'
AudioOutput: OptionInfo = Option(
join(gettempdir(), 'separated_audio'),
help='Path of the output directory to write audio files in')
AudioSTFTBackend: OptionInfo = Option(
STFTBackend.AUTO,
'--stft-backend',
'-B',
case_sensitive=False,
help=(
'Who should be in charge of computing the stfts. Librosa is faster '
'than tensorflow on CPU and uses less memory. "auto" will use '
'tensorflow when GPU acceleration is available and librosa when not'))
AudioAdapterDescriptor: OptionInfo = Option(
'spleeter.audio.ffmpeg.FFMPEGProcessAudioAdapter',
help='Name of the audio adapter to use for audio I/O')
MWF: OptionInfo = Option(
False,
'--mwf',
help='Whether to use multichannel Wiener filtering for separation')
ModelParameters: OptionInfo = Option(
'spleeter:2stems',
help='JSON filename that contains params')
Verbose: OptionInfo = Option(
False,
'--verbose',
help='Enable verbose logs')
def main(argv): @spleeter.command()
""" Spleeter runner. Parse provided command line arguments def train(
and run entrypoint for required command (either train, adapter=None,
evaluate or separate). verbose: bool = Verbose,
params_filename: str = ModelParameters,
:param argv: Provided command line arguments. data: Path = Option(
...,
exists=True,
dir_okay=True,
file_okay=False,
readable=True,
resolve_path=True,
help='Path of the folder containing audio data for training')
) -> None:
""" """
try: Train a source separation model
parser = create_argument_parser() """
arguments = parser.parse_args(argv[1:]) pass
enable_logging()
if arguments.verbose:
enable_tensorflow_logging()
if arguments.command == 'separate':
from .commands.separate import entrypoint
elif arguments.command == 'train':
from .commands.train import entrypoint
elif arguments.command == 'evaluate':
from .commands.evaluate import entrypoint
params = load_configuration(arguments.configuration)
entrypoint(arguments, params)
except SpleeterError as e:
get_logger().error(e)
def entrypoint(): @spleeter.command()
""" Command line entrypoint. """ def evaluate(
warnings.filterwarnings('ignore') adapter: str = AudioAdapterDescriptor,
main(sys.argv) output_path: Path = AudioOutput,
stft_backend: STFTBackend = AudioSTFTBackend,
params_filename: str = ModelParameters,
mwf: bool = MWF,
verbose: bool = Verbose,
mus_dir: Path = Option(
...,
'--mus_dir',
exists=True,
dir_okay=True,
file_okay=False,
readable=True,
resolve_path=True,
help='Path to musDB dataset directory')
) -> None:
"""
Evaluate a model on the musDB test dataset
"""
pass
@spleeter.commmand()
def separate(
adapter: str = AudioAdapterDescriptor,
output_path: Path = AudioOutput,
stft_backend: STFTBackend = AudioSTFTBackend,
params_filename: str = ModelParameters,
mwf: bool = MWF,
verbose: bool = Verbose,
files: List[Path] = Argument(
...,
help='List of input audio file path',
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
resolve_path=True),
filename_format: str = Option(
'{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')),
duration: float = Option(
600.,
help=(
'Set a maximum duration for processing audio '
'(only separate offset + duration first seconds of '
'the input file)')),
offset: float = Option(
0.,
'--offset',
'-s',
help='Set the starting offset to separate audio from'),
codec: Codec = Option(
Codec.WAV,
help='Audio codec to be used for the separated output'),
bitrate: str = Option(
'128k',
help='Audio bitrate to be used for the separated output')
) -> None:
"""
Separate audio file(s)
"""
# TODO: try / catch or custom decorator for function handling.
# TODO: enable_logging()
# TODO: handle MWF
if verbose:
# TODO: enable_tensorflow_logging()
pass
# PREV: params = load_configuration(arguments.configuration)
audio_adapter: AudioAdapter = AudioAdapter.get(adapter)
separator: Separator = Separator(
params_filename,
MWF=MWF,
stft_backend=stft_backend)
for filename in files:
separator.separate_to_file(
filename,
output_path,
audio_adapter=audio_adapter,
offset=offset,
duration=duration,
codec=codec,
bitrate=bitrate,
filename_format=filename_format,
synchronous=False)
separator.join()
if __name__ == '__main__': if __name__ == '__main__':
entrypoint() # TODO: warnings.filterwarnings('ignore')
spleeter()

View File

@@ -26,3 +26,11 @@ class Codec(str, Enum):
M4A: str = 'm4a' M4A: str = 'm4a'
WMA: str = 'wma' WMA: str = 'wma'
FLAC: str = 'flac' FLAC: str = 'flac'
class STFTBackend(str, Enum):
""" Enumeration of supported STFT backend. """
AUTO: str = 'auto'
TENSORFLOW: str = 'tensorflow'
LIBROSA: str = 'librosa'

View File

@@ -3,61 +3,41 @@
""" This modules provides spleeter command as well as CLI parsing methods. """ """ This modules provides spleeter command as well as CLI parsing methods. """
import json
import logging
from argparse import ArgumentParser
from tempfile import gettempdir from tempfile import gettempdir
from os.path import exists, join from os.path import join
from ..separator import STFTBackend
from ..audio import Codec
from typer import Argument, Option
from typer.models import ArgumentInfo, OptionInfo
__email__ = 'spleeter@deezer.com' __email__ = 'spleeter@deezer.com'
__author__ = 'Deezer Research' __author__ = 'Deezer Research'
__license__ = 'MIT License' __license__ = 'MIT License'
AudioInput: ArgumentInfo = Argument(
...,
help='List of input audio file path',
exists=True,
file_okay=True,
dir_okay=False,
readable=True,
resolve_path=True)
AudioOutput: OptionInfo = Option(
join(gettempdir(), 'separated_audio'),
'--output_path',
'-o',
help='Path of the output directory to write audio files in')
# -i opt specification (separate). AudioOffset: OptionInfo = Option(
OPT_INPUT = {
'dest': 'inputs',
'nargs': '+',
'help': 'List of input audio filenames',
'required': True
}
# -o opt specification (evaluate and separate).
OPT_OUTPUT = {
'dest': 'output_path',
'default': join(gettempdir(), 'separated_audio'),
'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',
'default': 'spleeter:2stems',
'type': str,
'action': 'store',
'help': 'JSON filename that contains params'
}
Offset: OptionInfo = Option(
0., 0.,
'--offset', '--offset',
'-s', '-s',
help='Set the starting offset to separate audio from') help='Set the starting offset to separate audio from')
Duration: OptionInfo = Option( AudioDuration: OptionInfo = Option(
600., 600.,
'--duration', '--duration',
'-d', '-d',
@@ -66,16 +46,25 @@ Duration: OptionInfo = Option(
'(only separate offset + duration first seconds of ' '(only separate offset + duration first seconds of '
'the input file)')) 'the input file)'))
FilenameFormat: OptionInfo = Option(
'{filename}/{instrument}.{codec}',
'--filename_format',
'-f',
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'))
class STFTBackendEnum(Enum, str): ModelParameters: OptionInfo = Option(
'spleeter:2stems',
AUTO: str '--params_filename',
TENSORFLOW: str '-p',
LIBROSA: str help='JSON filename that contains params')
STFTBackend: OptionInfo = Option( AudioSTFTBackend: OptionInfo = Option(
STFTBackendEnum.AUTO, STFTBackend.AUTO,
'--stft-backend', '--stft-backend',
'-B', '-B',
case_sensitive=False, case_sensitive=False,
@@ -84,67 +73,54 @@ STFTBackend: OptionInfo = Option(
'than tensorflow on CPU and uses less memory. "auto" will use ' 'than tensorflow on CPU and uses less memory. "auto" will use '
'tensorflow when GPU acceleration is available and librosa when not')) 'tensorflow when GPU acceleration is available and librosa when not'))
AudioCodec: OptionInfo = Option(
Codec.WAV,
'--codec',
'-c',
help='Audio codec to be used for the separated output')
# -c opt specification (separate). AudioBitrate: OptionInfo = Option(
OPT_CODEC = { '128k',
'dest': 'codec', '--bitrate',
'choices': ('wav', 'mp3', 'ogg', 'm4a', 'wma', 'flac'), '-b',
'default': 'wav', help='Audio bitrate to be used for the separated output')
'help': 'Audio codec to be used for the separated output'
}
# -b opt specification (separate). MWF: OptionInfo = Option(
OPT_BITRATE = { False,
'dest': 'bitrate', '--mwf',
'default': '128k', help='Whether to use multichannel Wiener filtering for separation')
'help': 'Audio bitrate to be used for the separated output'
}
# -m opt specification (evaluate and separate).
OPT_MWF = {
'dest': 'MWF',
'action': 'store_const',
'const': True,
'default': False,
'help': 'Whether to use multichannel Wiener filtering for separation',
}
# --mus_dir opt specification (evaluate).
OPT_MUSDB = {
'dest': 'mus_dir',
'type': str,
'required': True,
'help': 'Path to folder with musDB'
}
# -d opt specification (train).
OPT_DATA = {
'dest': 'audio_path',
'type': str,
'required': True,
'help': 'Path of the folder containing audio data for training'
}
# -a opt specification (train, evaluate and separate).
OPT_ADAPTER = {
'dest': 'audio_adapter',
'type': str,
'help': 'Name of the audio adapter to use for audio I/O'
}
MUSDBDirectory: OptionInfo = Option(
...,
'--mus_dir',
exists=True,
dir_okay=True,
file_okay=False,
readable=True,
resolve_path=True,
help='Path to musDB dataset directory')
TrainingDataDirectory: OptionInfo = Option(
...,
'--data',
'-d',
exists=True,
dir_okay=True,
file_okay=False,
readable=True,
resolve_path=True,
help='Path of the folder containing audio data for training')
AudioAdapter: OptionInfo = Option( AudioAdapter: OptionInfo = Option(
'spleeter.audio.ffmpeg.FFMPEGProcessAudioAdapter', 'spleeter.audio.ffmpeg.FFMPEGProcessAudioAdapter',
'--adapter', '--adapter',
'-a',
help='Name of the audio adapter to use for audio I/O') help='Name of the audio adapter to use for audio I/O')
Verbose: OptionInfo = Option(
# -a opt specification (train, evaluate and separate). False,
OPT_VERBOSE = { '--verbose',
'action': 'store_true', help='Enable verbose logs')
'help': 'Shows verbose logs'
}
def _add_common_options(parser): def _add_common_options(parser):

View File

@@ -16,6 +16,8 @@ import atexit
import os import os
import logging import logging
from enum import Enum
from multiprocessing import Pool from multiprocessing import Pool
from os.path import basename, join, splitext, dirname from os.path import basename, join, splitext, dirname
from time import time from time import time