🐛 🎨 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
# coding: utf8
"""
Python oneliner script usage.
""" TO DOCUMENT """
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
import warnings
from .audio import Codec
from .audio.adapter import AudioAdapter
from .separator import Separator
from . import SpleeterError
from .commands import create_argument_parser
from .utils.configuration import load_configuration
from .utils.logging import (
enable_logging,
enable_tensorflow_logging,
get_logger)
# pyright: reportMissingImports=false
# pylint: disable=import-error
from typer import Argument, Option, Typer
from typer.models import OptionInfo
# pylint: enable=import-error
__email__ = 'spleeter@deezer.com'
__author__ = 'Deezer Research'
__license__ = 'MIT License'
spleeter: Typer = Typer()
""" """
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 runner. Parse provided command line arguments
and run entrypoint for required command (either train,
evaluate or separate).
:param argv: Provided command line arguments.
@spleeter.command()
def train(
adapter=None,
verbose: bool = Verbose,
params_filename: str = ModelParameters,
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:
parser = create_argument_parser()
arguments = parser.parse_args(argv[1:])
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)
Train a source separation model
"""
pass
def entrypoint():
""" Command line entrypoint. """
warnings.filterwarnings('ignore')
main(sys.argv)
@spleeter.command()
def evaluate(
adapter: str = AudioAdapterDescriptor,
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__':
entrypoint()
# TODO: warnings.filterwarnings('ignore')
spleeter()

View File

@@ -26,3 +26,11 @@ class Codec(str, Enum):
M4A: str = 'm4a'
WMA: str = 'wma'
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. """
import json
import logging
from argparse import ArgumentParser
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'
__author__ = 'Deezer Research'
__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).
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(
AudioOffset: OptionInfo = Option(
0.,
'--offset',
'-s',
help='Set the starting offset to separate audio from')
Duration: OptionInfo = Option(
AudioDuration: OptionInfo = Option(
600.,
'--duration',
'-d',
@@ -66,16 +46,25 @@ Duration: OptionInfo = Option(
'(only separate offset + duration first seconds of '
'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):
AUTO: str
TENSORFLOW: str
LIBROSA: str
ModelParameters: OptionInfo = Option(
'spleeter:2stems',
'--params_filename',
'-p',
help='JSON filename that contains params')
STFTBackend: OptionInfo = Option(
STFTBackendEnum.AUTO,
AudioSTFTBackend: OptionInfo = Option(
STFTBackend.AUTO,
'--stft-backend',
'-B',
case_sensitive=False,
@@ -84,67 +73,54 @@ STFTBackend: OptionInfo = Option(
'than tensorflow on CPU and uses less memory. "auto" will use '
'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).
OPT_CODEC = {
'dest': 'codec',
'choices': ('wav', 'mp3', 'ogg', 'm4a', 'wma', 'flac'),
'default': 'wav',
'help': 'Audio codec to be used for the separated output'
}
AudioBitrate: OptionInfo = Option(
'128k',
'--bitrate',
'-b',
help='Audio bitrate to be used for the separated output')
# -b opt specification (separate).
OPT_BITRATE = {
'dest': 'bitrate',
'default': '128k',
'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'
}
MWF: OptionInfo = Option(
False,
'--mwf',
help='Whether to use multichannel Wiener filtering for separation')
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(
'spleeter.audio.ffmpeg.FFMPEGProcessAudioAdapter',
'--adapter',
'-a',
help='Name of the audio adapter to use for audio I/O')
# -a opt specification (train, evaluate and separate).
OPT_VERBOSE = {
'action': 'store_true',
'help': 'Shows verbose logs'
}
Verbose: OptionInfo = Option(
False,
'--verbose',
help='Enable verbose logs')
def _add_common_options(parser):

View File

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