跳轉到

Configuration and errors

kudio.config

YAML-backed configuration with attribute access.

>>> from kudio import KudioConfig
>>> cfg = KudioConfig.load_config('TrainConfig.yaml')
>>> cfg.train.clean
'data/train/clean'

Classes:

Name Description
KudioConfig

Attribute-style config with YAML load/save and audio-centric defaults.

Dict

Dict(__self, *args, **kwargs)

Bases: dict

dict subclass with attribute access and recursive conversion.

Vendored from the addict <https://github.com/mewwts/addict>_ project (MIT license), trimmed to what kudio needs.

Source code in kudio/config.py
def __init__(__self, *args, **kwargs):
    object.__setattr__(__self, '__parent', kwargs.pop('__parent', None))
    object.__setattr__(__self, '__key', kwargs.pop('__key', None))
    object.__setattr__(__self, '__frozen', False)
    for arg in args:
        if not arg:
            continue
        if isinstance(arg, dict):
            for key, val in arg.items():
                __self[key] = __self._hook(val)
        elif isinstance(arg, tuple) and (not isinstance(arg[0], tuple)):
            __self[arg[0]] = __self._hook(arg[1])
        else:
            for key, val in iter(arg):
                __self[key] = __self._hook(val)

    for key, val in kwargs.items():
        __self[key] = __self._hook(val)

KudioConfig

KudioConfig(__self, *args, **kwargs)

Bases: Dict

Attribute-style config with YAML load/save and audio-centric defaults.

Methods:

Name Description
write_config

Dump the config to file; refuses to overwrite unless exist_ok.

load_config

Load a YAML config file.

default_config

Audio-only defaults. Training/model settings live in the

show

Print one key (or the whole config) to the log.

Source code in kudio/config.py
def __init__(__self, *args, **kwargs):
    object.__setattr__(__self, '__parent', kwargs.pop('__parent', None))
    object.__setattr__(__self, '__key', kwargs.pop('__key', None))
    object.__setattr__(__self, '__frozen', False)
    for arg in args:
        if not arg:
            continue
        if isinstance(arg, dict):
            for key, val in arg.items():
                __self[key] = __self._hook(val)
        elif isinstance(arg, tuple) and (not isinstance(arg[0], tuple)):
            __self[arg[0]] = __self._hook(arg[1])
        else:
            for key, val in iter(arg):
                __self[key] = __self._hook(val)

    for key, val in kwargs.items():
        __self[key] = __self._hook(val)

write_config

write_config(
    file: str = "cfg.yaml", exist_ok: bool = False
) -> bool

Dump the config to file; refuses to overwrite unless exist_ok.

Source code in kudio/config.py
def write_config(self, file: str = 'cfg.yaml', exist_ok: bool = False) -> bool:
    """Dump the config to *file*; refuses to overwrite unless ``exist_ok``."""
    if Path(file).exists() and not exist_ok:
        log.warning("Config exists, not overwritten: %s", file)
        return False
    with open(str(file), 'w', encoding='utf-8') as f:
        yaml.safe_dump(self.to_dict(), f, allow_unicode=True)
    log.info("Config written to %s", file)
    return True

load_config classmethod

load_config(cfg_dir: str = '') -> Optional['KudioConfig']

Load a YAML config file.

cfg_dir may be a file, or a directory containing exactly one cfg.yaml at any depth. Anything else falls back to default_config, with a UserWarning when a path was named or a search found more than one file: a misspelt file name came back as the defaults with nothing said.

A key the file does not have reads as an empty section, which is what lets cfg.a.b = 1 build a new one. Call freeze on the result to make a misspelt key raise KeyError instead.

Source code in kudio/config.py
@classmethod
def load_config(cls, cfg_dir: str = '') -> Optional['KudioConfig']:
    """Load a YAML config file.

    *cfg_dir* may be a file, or a directory containing exactly one
    ``cfg.yaml`` at any depth. Anything else falls back to
    :meth:`default_config`, with a ``UserWarning`` when a path was named
    or a search found more than one file: a misspelt file name came back
    as the defaults with nothing said.

    A key the file does not have reads as an empty section, which is what
    lets ``cfg.a.b = 1`` build a new one. Call :meth:`freeze` on the result
    to make a misspelt key raise ``KeyError`` instead.
    """

    def _load(f_: Path):
        log.info("Load config -> %s", f_.absolute())
        with open(str(f_.absolute()), encoding="utf-8") as f:
            return yaml.safe_load(f)

    _file = Path(cfg_dir)
    if _file.is_file():
        return cls(_load(_file))
    if _file.is_dir():
        candidates = list(_file.rglob("cfg.yaml"))
        if len(candidates) == 1:
            return cls(_load(candidates[0]))
        if candidates or cfg_dir:
            warnings.warn(f"{len(candidates)} cfg.yaml file(s) under "
                          f"{str(cfg_dir) or '.'}, and load_config needs "
                          f"exactly one; using the defaults",
                          UserWarning, stacklevel=2)
    elif cfg_dir:
        warnings.warn(f"no config at {str(cfg_dir)!r}; using the defaults",
                      UserWarning, stacklevel=2)
    log.info("No config file found, using defaults")
    return cls(**cls.default_config())

default_config staticmethod

default_config() -> dict

Audio-only defaults. Training/model settings live in the application layer, not in the audio library.

Source code in kudio/config.py
@staticmethod
def default_config() -> dict:
    """Audio-only defaults. Training/model settings live in the
    application layer, not in the audio library."""
    return {
        'waveform': {
            'type': '.wav',
            'default_rate': 16000,
            'common_rate': [192000, 96000, 88200, 64000, 48000, 44100,
                            32000, 22050, 16000, 11025, 8000, 6000],
            'resample_rate': 16000,
            'channels': 'mono',
            'format': '16bit'},
        'device': {
            'pool_max': 200, 'cpu_cores': 8},
        'f_mfe': {
            'n_mels': 40,
            'bank': 1,
            'f0': 0,
            'f1': 48000},
    }

show

show(key: Optional[str] = None) -> None

Print one key (or the whole config) to the log.

Source code in kudio/config.py
def show(self, key: Optional[str] = None) -> None:
    """Print one key (or the whole config) to the log."""
    if key is None:
        for k, value in self.items():
            log.info("%s: %s", k, value)
    elif key in self:
        log.info("%s: %s", key, self.get(key))
    else:
        log.warning("Unknown config key: %s", key)

kudio.exceptions

Exception hierarchy for kudio.

What goes wrong with a file, a device, a feature, a synthesis run or a missing extra derives from KudioError, so callers can:

try:
    kudio.file_load(path)
except kudio.KudioError:
    ...

A wrong argument -- a negative rate, an unknown metric, two signals too different in length to compare -- raises ValueError or TypeError as the standard library would, and a buffer that waited too long raises TimeoutError.

Classes:

Name Description
KudioError

Base class for every error raised by kudio.

AudioIOError

Reading or writing an audio file failed.

UnsupportedFormatError

The file type / dtype is not supported.

DeviceError

An audio input/output device could not be opened or configured.

FeatureError

A feature, effect, room, channel or enhancement step refused its input

SynthesisError

Noisy-data synthesis failed.

DependencyError

An optional dependency is required for this operation but not installed.

KudioError

Bases: Exception

Base class for every error raised by kudio.

AudioIOError

Bases: KudioError

Reading or writing an audio file failed.

UnsupportedFormatError

Bases: AudioIOError

The file type / dtype is not supported.

Not raised by kudio itself at present: a file soundfile cannot read comes back as a plain AudioIOError. Catch that one.

DeviceError

Bases: KudioError, RuntimeError

An audio input/output device could not be opened or configured.

Also a RuntimeError: kudio.CheckDevice.input_device raised plain RuntimeError for the same failures, and code written against that still catches them.

FeatureError

Bases: KudioError

A feature, effect, room, channel or enhancement step refused its input or its settings: the wrong shape of audio, a parameter out of range, a clip too short to frame.

SynthesisError

Bases: KudioError

Noisy-data synthesis failed.

DependencyError

DependencyError(package: str, extra: str | None = None)

Bases: KudioError, ImportError

An optional dependency is required for this operation but not installed.

Source code in kudio/exceptions.py
def __init__(self, package: str, extra: str | None = None):
    hint = f"pip install kudio[{extra}]" if extra else f"pip install {package}"
    super().__init__(f"'{package}' is required for this feature. Install it with: {hint}")
    self.package = package
    self.extra = extra