mirror of
https://github.com/YuzuZensai/spleeter.git
synced 2026-01-31 14:58:23 +00:00
🐛 🎨 WIP: typer integration
This commit is contained in:
@@ -1,58 +1,174 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf8
|
# coding: utf8
|
||||||
|
|
||||||
|
""" TO DOCUMENT """
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from os.path import join
|
||||||
|
from spleeter.separator import STFTBackend
|
||||||
|
from tempfile import gettempdir
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from .audio import Codec
|
||||||
|
from .audio.adapter import AudioAdapter
|
||||||
|
from .separator import Separator
|
||||||
|
|
||||||
|
# pyright: reportMissingImports=false
|
||||||
|
# pylint: disable=import-error
|
||||||
|
from typer import Argument, Option, Typer
|
||||||
|
from typer.models import OptionInfo
|
||||||
|
# pylint: enable=import-error
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
|
||||||
|
@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:
|
||||||
"""
|
"""
|
||||||
Python oneliner script usage.
|
Train a source separation model
|
||||||
|
|
||||||
USAGE: python -m spleeter {train,evaluate,separate} ...
|
|
||||||
"""
|
"""
|
||||||
|
pass
|
||||||
import sys
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
__email__ = 'spleeter@deezer.com'
|
|
||||||
__author__ = 'Deezer Research'
|
|
||||||
__license__ = 'MIT License'
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
@spleeter.command()
|
||||||
""" Spleeter runner. Parse provided command line arguments
|
def evaluate(
|
||||||
and run entrypoint for required command (either train,
|
adapter: str = AudioAdapterDescriptor,
|
||||||
evaluate or separate).
|
output_path: Path = AudioOutput,
|
||||||
|
stft_backend: STFTBackend = AudioSTFTBackend,
|
||||||
:param argv: Provided command line arguments.
|
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:
|
||||||
"""
|
"""
|
||||||
try:
|
Evaluate a model on the musDB test dataset
|
||||||
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.commmand()
|
||||||
""" Command line entrypoint. """
|
def separate(
|
||||||
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,
|
||||||
|
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()
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user