audio-super-resolution 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- audio_super_resolution/__init__.py +142 -0
- audio_super_resolution/audiosr_backend.py +17 -0
- audio_super_resolution/backends/__init__.py +32 -0
- audio_super_resolution/backends/audiosr_external.py +128 -0
- audio_super_resolution/backends/base.py +68 -0
- audio_super_resolution/backends/lavasr_compat.py +83 -0
- audio_super_resolution/backends/registry.py +75 -0
- audio_super_resolution/backends/sinc.py +48 -0
- audio_super_resolution/chunking.py +155 -0
- audio_super_resolution/cli.py +527 -0
- audio_super_resolution/config.py +125 -0
- audio_super_resolution/devices.py +65 -0
- audio_super_resolution/downloads.py +200 -0
- audio_super_resolution/manifest.py +318 -0
- audio_super_resolution/model_weights.py +69 -0
- audio_super_resolution/models.py +121 -0
- audio_super_resolution/preprocess.py +55 -0
- audio_super_resolution/quality.py +129 -0
- audio_super_resolution/resolver.py +319 -0
- audio_super_resolution/specs.py +66 -0
- audio_super_resolution/weight_store.py +156 -0
- audio_super_resolution/weights.py +279 -0
- audio_super_resolution-0.1.0.dist-info/METADATA +248 -0
- audio_super_resolution-0.1.0.dist-info/RECORD +27 -0
- audio_super_resolution-0.1.0.dist-info/WHEEL +4 -0
- audio_super_resolution-0.1.0.dist-info/entry_points.txt +3 -0
- audio_super_resolution-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Audio super-resolution tools for Python."""
|
|
2
|
+
|
|
3
|
+
from .audiosr_backend import AUDIOSR_MODEL_NAMES, AUDIOSR_SAMPLE_RATE, AudiosrBackend
|
|
4
|
+
from .backends import register_backend, registered_backend_types
|
|
5
|
+
from .config import InferenceConfig, default_model_cache_dir
|
|
6
|
+
from .devices import DeviceInfo, available_devices, resolve_device
|
|
7
|
+
from .downloads import (
|
|
8
|
+
download_model_weights as download_model_weights_for_spec,
|
|
9
|
+
)
|
|
10
|
+
from .downloads import (
|
|
11
|
+
download_weights_for_spec,
|
|
12
|
+
register_weight_provider,
|
|
13
|
+
)
|
|
14
|
+
from .manifest import (
|
|
15
|
+
ManifestComparison,
|
|
16
|
+
ManifestDifference,
|
|
17
|
+
build_manifest,
|
|
18
|
+
compare_manifests,
|
|
19
|
+
format_manifest_comparison,
|
|
20
|
+
load_manifest,
|
|
21
|
+
manifest_comparison_to_dict,
|
|
22
|
+
write_manifest,
|
|
23
|
+
)
|
|
24
|
+
from .model_weights import (
|
|
25
|
+
download_model_weights,
|
|
26
|
+
resolve_backend_model_weights,
|
|
27
|
+
resolve_model_weights,
|
|
28
|
+
verify_model_weights,
|
|
29
|
+
)
|
|
30
|
+
from .models import ModelInfo, find_model_spec, get_model_spec, list_models
|
|
31
|
+
from .preprocess import DEFAULT_LOWPASS_CUTOFF_HZ, apply_preprocessing, lowpass_filter
|
|
32
|
+
from .quality import (
|
|
33
|
+
AudioQualityReport,
|
|
34
|
+
build_quality_report_bundle,
|
|
35
|
+
format_quality_report,
|
|
36
|
+
inspect_audio_quality,
|
|
37
|
+
quality_report_to_dict,
|
|
38
|
+
write_quality_report_bundle,
|
|
39
|
+
)
|
|
40
|
+
from .resolver import (
|
|
41
|
+
DEFAULT_AUDIO_EXTENSIONS,
|
|
42
|
+
AudioSuperResolver,
|
|
43
|
+
BackendInfo,
|
|
44
|
+
EnhancementResult,
|
|
45
|
+
PlannedEnhancement,
|
|
46
|
+
available_backends,
|
|
47
|
+
discover_audio_files,
|
|
48
|
+
get_backend,
|
|
49
|
+
plan_enhancements,
|
|
50
|
+
)
|
|
51
|
+
from .specs import BackendCapability, ModelSpec, WeightFileSpec
|
|
52
|
+
from .weight_store import (
|
|
53
|
+
ResolvedWeights,
|
|
54
|
+
resolve_weights_for_spec,
|
|
55
|
+
validate_weight_manifest_matches_spec,
|
|
56
|
+
verify_weights_for_spec,
|
|
57
|
+
)
|
|
58
|
+
from .weights import (
|
|
59
|
+
WeightFile,
|
|
60
|
+
WeightManifest,
|
|
61
|
+
load_safetensors,
|
|
62
|
+
read_weight_manifest,
|
|
63
|
+
resolve_manifest_file_paths,
|
|
64
|
+
resolve_weight_file_path,
|
|
65
|
+
resolve_weight_path,
|
|
66
|
+
sha256_file,
|
|
67
|
+
validate_weight_file_path,
|
|
68
|
+
verify_weight_file,
|
|
69
|
+
verify_weight_manifest,
|
|
70
|
+
write_weight_manifest,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
__all__ = [
|
|
74
|
+
"AUDIOSR_MODEL_NAMES",
|
|
75
|
+
"AUDIOSR_SAMPLE_RATE",
|
|
76
|
+
"DEFAULT_AUDIO_EXTENSIONS",
|
|
77
|
+
"DEFAULT_LOWPASS_CUTOFF_HZ",
|
|
78
|
+
"AudioQualityReport",
|
|
79
|
+
"AudioSuperResolver",
|
|
80
|
+
"AudiosrBackend",
|
|
81
|
+
"BackendCapability",
|
|
82
|
+
"BackendInfo",
|
|
83
|
+
"DeviceInfo",
|
|
84
|
+
"EnhancementResult",
|
|
85
|
+
"InferenceConfig",
|
|
86
|
+
"ManifestComparison",
|
|
87
|
+
"ManifestDifference",
|
|
88
|
+
"ModelInfo",
|
|
89
|
+
"ModelSpec",
|
|
90
|
+
"PlannedEnhancement",
|
|
91
|
+
"ResolvedWeights",
|
|
92
|
+
"WeightFile",
|
|
93
|
+
"WeightFileSpec",
|
|
94
|
+
"WeightManifest",
|
|
95
|
+
"apply_preprocessing",
|
|
96
|
+
"available_backends",
|
|
97
|
+
"available_devices",
|
|
98
|
+
"build_manifest",
|
|
99
|
+
"build_quality_report_bundle",
|
|
100
|
+
"compare_manifests",
|
|
101
|
+
"default_model_cache_dir",
|
|
102
|
+
"discover_audio_files",
|
|
103
|
+
"download_model_weights",
|
|
104
|
+
"download_model_weights_for_spec",
|
|
105
|
+
"download_weights_for_spec",
|
|
106
|
+
"find_model_spec",
|
|
107
|
+
"format_manifest_comparison",
|
|
108
|
+
"format_quality_report",
|
|
109
|
+
"get_backend",
|
|
110
|
+
"get_model_spec",
|
|
111
|
+
"inspect_audio_quality",
|
|
112
|
+
"list_models",
|
|
113
|
+
"load_manifest",
|
|
114
|
+
"load_safetensors",
|
|
115
|
+
"lowpass_filter",
|
|
116
|
+
"manifest_comparison_to_dict",
|
|
117
|
+
"plan_enhancements",
|
|
118
|
+
"quality_report_to_dict",
|
|
119
|
+
"read_weight_manifest",
|
|
120
|
+
"register_backend",
|
|
121
|
+
"register_weight_provider",
|
|
122
|
+
"registered_backend_types",
|
|
123
|
+
"resolve_backend_model_weights",
|
|
124
|
+
"resolve_device",
|
|
125
|
+
"resolve_manifest_file_paths",
|
|
126
|
+
"resolve_model_weights",
|
|
127
|
+
"resolve_weights_for_spec",
|
|
128
|
+
"resolve_weight_file_path",
|
|
129
|
+
"resolve_weight_path",
|
|
130
|
+
"sha256_file",
|
|
131
|
+
"validate_weight_file_path",
|
|
132
|
+
"validate_weight_manifest_matches_spec",
|
|
133
|
+
"verify_model_weights",
|
|
134
|
+
"verify_weights_for_spec",
|
|
135
|
+
"verify_weight_file",
|
|
136
|
+
"verify_weight_manifest",
|
|
137
|
+
"write_manifest",
|
|
138
|
+
"write_quality_report_bundle",
|
|
139
|
+
"write_weight_manifest",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .backends.audiosr_external import (
|
|
4
|
+
AUDIOSR_MODEL_NAMES,
|
|
5
|
+
AUDIOSR_SAMPLE_RATE,
|
|
6
|
+
AudiosrBackend,
|
|
7
|
+
_import_audiosr,
|
|
8
|
+
_waveform_to_audio_array,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"AUDIOSR_MODEL_NAMES",
|
|
13
|
+
"AUDIOSR_SAMPLE_RATE",
|
|
14
|
+
"AudiosrBackend",
|
|
15
|
+
"_import_audiosr",
|
|
16
|
+
"_waveform_to_audio_array",
|
|
17
|
+
]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .audiosr_external import AUDIOSR_MODEL_NAMES, AUDIOSR_SAMPLE_RATE, AudiosrBackend
|
|
4
|
+
from .base import BackendInfo, EnhancementBackend, FileEnhancementBackend
|
|
5
|
+
from .lavasr_compat import LAVASR_MODEL_ID, LAVASR_REVISION, LAVASR_SAMPLE_RATE, LavaSRCompatBackend
|
|
6
|
+
from .registry import (
|
|
7
|
+
available_backends,
|
|
8
|
+
get_backend,
|
|
9
|
+
list_backend_model_specs,
|
|
10
|
+
register_backend,
|
|
11
|
+
registered_backend_types,
|
|
12
|
+
)
|
|
13
|
+
from .sinc import SincResampleBackend
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"AUDIOSR_MODEL_NAMES",
|
|
17
|
+
"AUDIOSR_SAMPLE_RATE",
|
|
18
|
+
"LAVASR_MODEL_ID",
|
|
19
|
+
"LAVASR_REVISION",
|
|
20
|
+
"LAVASR_SAMPLE_RATE",
|
|
21
|
+
"AudiosrBackend",
|
|
22
|
+
"BackendInfo",
|
|
23
|
+
"EnhancementBackend",
|
|
24
|
+
"FileEnhancementBackend",
|
|
25
|
+
"LavaSRCompatBackend",
|
|
26
|
+
"SincResampleBackend",
|
|
27
|
+
"available_backends",
|
|
28
|
+
"get_backend",
|
|
29
|
+
"list_backend_model_specs",
|
|
30
|
+
"register_backend",
|
|
31
|
+
"registered_backend_types",
|
|
32
|
+
]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import importlib.util
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from types import ModuleType
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import soundfile as sf
|
|
11
|
+
|
|
12
|
+
from ..config import InferenceConfig
|
|
13
|
+
from ..specs import ModelSpec
|
|
14
|
+
from .base import DEFAULT_FILE_BACKEND_CAPABILITY
|
|
15
|
+
|
|
16
|
+
AUDIOSR_SAMPLE_RATE = 48000
|
|
17
|
+
AUDIOSR_MODEL_NAMES = ("basic", "speech")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AudiosrBackend:
|
|
21
|
+
"""AudioSR latent diffusion backend.
|
|
22
|
+
|
|
23
|
+
The heavy `audiosr` dependency is imported only when this backend is selected.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
name = "audiosr"
|
|
27
|
+
description = "AudioSR latent diffusion backend. Requires the optional audiosr dependency."
|
|
28
|
+
optional_dependency = "audiosr"
|
|
29
|
+
package_extra = "audiosr"
|
|
30
|
+
|
|
31
|
+
def __init__(self, config: InferenceConfig | None = None) -> None:
|
|
32
|
+
self.config = config or InferenceConfig()
|
|
33
|
+
self._model = None
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def is_available(cls) -> bool:
|
|
37
|
+
try:
|
|
38
|
+
return importlib.util.find_spec("audiosr") is not None
|
|
39
|
+
except (ImportError, ValueError):
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def model_specs(cls) -> tuple[ModelSpec, ...]:
|
|
44
|
+
return tuple(
|
|
45
|
+
ModelSpec(
|
|
46
|
+
id=f"audiosr-{model_name}",
|
|
47
|
+
backend=cls.name,
|
|
48
|
+
model_name=model_name,
|
|
49
|
+
name=f"AudioSR {model_name.title()}",
|
|
50
|
+
description=f"AudioSR latent diffusion {model_name} model.",
|
|
51
|
+
implementation="external_package",
|
|
52
|
+
domain=("speech", "music", "sfx", "general"),
|
|
53
|
+
architecture="latent-diffusion-audio-super-resolution",
|
|
54
|
+
target_sample_rates=(AUDIOSR_SAMPLE_RATE,),
|
|
55
|
+
weights_license="unknown",
|
|
56
|
+
weights_source="Hugging Face download managed by the audiosr package",
|
|
57
|
+
maturity="experimental",
|
|
58
|
+
capability=DEFAULT_FILE_BACKEND_CAPABILITY,
|
|
59
|
+
)
|
|
60
|
+
for model_name in AUDIOSR_MODEL_NAMES
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def enhance(self, audio: np.ndarray, sample_rate: int, target_sample_rate: int) -> np.ndarray:
|
|
64
|
+
raise RuntimeError("The audiosr backend requires file-based enhancement through AudioSuperResolver.enhance().")
|
|
65
|
+
|
|
66
|
+
def enhance_file(self, input_path: str | Path, output_path: str | Path, target_sample_rate: int) -> None:
|
|
67
|
+
if target_sample_rate != AUDIOSR_SAMPLE_RATE:
|
|
68
|
+
raise ValueError("The audiosr backend outputs 48000 Hz audio; set target_sr=48000.")
|
|
69
|
+
if self.config.device == "directml":
|
|
70
|
+
raise ValueError("The audiosr backend does not support directml; use cpu, cuda, mps, or auto.")
|
|
71
|
+
if self.config.precision not in {"float32", "auto"}:
|
|
72
|
+
raise ValueError("The audiosr backend currently supports only float32 or auto precision.")
|
|
73
|
+
if self.config.model_name not in AUDIOSR_MODEL_NAMES:
|
|
74
|
+
choices = ", ".join(AUDIOSR_MODEL_NAMES)
|
|
75
|
+
raise ValueError(f"The audiosr backend model_name must be one of: {choices}")
|
|
76
|
+
|
|
77
|
+
audiosr = _import_audiosr()
|
|
78
|
+
self._prepare_cache_environment()
|
|
79
|
+
|
|
80
|
+
model = self._load_model(audiosr)
|
|
81
|
+
waveform = audiosr.super_resolution(
|
|
82
|
+
model,
|
|
83
|
+
str(input_path),
|
|
84
|
+
seed=self.config.seed,
|
|
85
|
+
guidance_scale=self.config.guidance_scale,
|
|
86
|
+
ddim_steps=self.config.ddim_steps,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
output_path = Path(output_path)
|
|
90
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
91
|
+
sf.write(output_path, _waveform_to_audio_array(waveform), samplerate=AUDIOSR_SAMPLE_RATE)
|
|
92
|
+
|
|
93
|
+
def _prepare_cache_environment(self) -> None:
|
|
94
|
+
cache_dir = self.config.ensure_model_cache_dir()
|
|
95
|
+
os.environ.setdefault("HF_HOME", str(cache_dir / "huggingface"))
|
|
96
|
+
|
|
97
|
+
def _load_model(self, audiosr: ModuleType):
|
|
98
|
+
if self._model is None:
|
|
99
|
+
self._model = audiosr.build_model(model_name=self.config.model_name, device=self.config.device)
|
|
100
|
+
return self._model
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _import_audiosr() -> ModuleType:
|
|
104
|
+
try:
|
|
105
|
+
return importlib.import_module("audiosr")
|
|
106
|
+
except ImportError as exc:
|
|
107
|
+
raise RuntimeError(
|
|
108
|
+
"The audiosr backend requires the optional audiosr dependency. "
|
|
109
|
+
"Install it with `pip install audio-super-resolution[audiosr]`."
|
|
110
|
+
) from exc
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _waveform_to_audio_array(waveform) -> np.ndarray:
|
|
114
|
+
if hasattr(waveform, "detach"):
|
|
115
|
+
waveform = waveform.detach().cpu().numpy()
|
|
116
|
+
|
|
117
|
+
audio = np.asarray(waveform, dtype=np.float32)
|
|
118
|
+
|
|
119
|
+
if audio.ndim == 3:
|
|
120
|
+
audio = audio[0]
|
|
121
|
+
if audio.ndim == 2 and audio.shape[0] <= 8:
|
|
122
|
+
audio = audio.T
|
|
123
|
+
if audio.ndim == 2 and audio.shape[1] == 1:
|
|
124
|
+
audio = audio[:, 0]
|
|
125
|
+
if audio.ndim not in {1, 2}:
|
|
126
|
+
raise ValueError(f"Unsupported AudioSR waveform shape: {audio.shape}")
|
|
127
|
+
|
|
128
|
+
return audio
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Protocol
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from ..config import InferenceConfig
|
|
10
|
+
from ..specs import BackendCapability, ModelSpec
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class BackendInfo:
|
|
15
|
+
"""User-facing metadata for an enhancement backend."""
|
|
16
|
+
|
|
17
|
+
name: str
|
|
18
|
+
description: str
|
|
19
|
+
installed: bool
|
|
20
|
+
optional_dependency: str | None = None
|
|
21
|
+
package_extra: str | None = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class EnhancementBackend(Protocol):
|
|
25
|
+
"""Interface implemented by audio super-resolution backends."""
|
|
26
|
+
|
|
27
|
+
name: str
|
|
28
|
+
description: str
|
|
29
|
+
config: InferenceConfig
|
|
30
|
+
|
|
31
|
+
def enhance(self, audio: np.ndarray, sample_rate: int, target_sample_rate: int) -> np.ndarray:
|
|
32
|
+
"""Return audio enhanced to target_sample_rate."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class FileEnhancementBackend(EnhancementBackend, Protocol):
|
|
36
|
+
"""Optional file-native backend interface."""
|
|
37
|
+
|
|
38
|
+
def enhance_file(self, input_path: str | Path, output_path: str | Path, target_sample_rate: int) -> None:
|
|
39
|
+
"""Write enhanced audio to output_path."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
DEFAULT_ARRAY_BACKEND_CAPABILITY = BackendCapability(
|
|
43
|
+
supports_array_io=True,
|
|
44
|
+
supports_file_io=False,
|
|
45
|
+
supports_chunking=True,
|
|
46
|
+
deterministic=True,
|
|
47
|
+
supports_cpu=True,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
DEFAULT_FILE_BACKEND_CAPABILITY = BackendCapability(
|
|
52
|
+
supports_array_io=False,
|
|
53
|
+
supports_file_io=True,
|
|
54
|
+
supports_chunking=True,
|
|
55
|
+
deterministic=False,
|
|
56
|
+
supports_cpu=True,
|
|
57
|
+
supports_cuda=True,
|
|
58
|
+
supports_mps=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def backend_model_specs(backend_type: type[EnhancementBackend]) -> tuple[ModelSpec, ...]:
|
|
63
|
+
"""Return model specs exposed by a backend type."""
|
|
64
|
+
|
|
65
|
+
model_specs = getattr(backend_type, "model_specs", None)
|
|
66
|
+
if callable(model_specs):
|
|
67
|
+
return tuple(model_specs())
|
|
68
|
+
return ()
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib.util
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from ..config import InferenceConfig
|
|
8
|
+
from ..specs import BackendCapability, ModelSpec, WeightFileSpec
|
|
9
|
+
|
|
10
|
+
LAVASR_SAMPLE_RATE = 48000
|
|
11
|
+
LAVASR_MODEL_ID = "lavasr-v2-bwe"
|
|
12
|
+
LAVASR_REVISION = "b98dc8be472da45ab7b6346ad7997e1dfeb5911d"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class LavaSRCompatBackend:
|
|
16
|
+
"""Self-contained LavaSR-compatible backend placeholder for verified BWE weights."""
|
|
17
|
+
|
|
18
|
+
name = "lavasr-compat"
|
|
19
|
+
description = "Self-contained LavaSR-compatible speech bandwidth extension backend."
|
|
20
|
+
optional_dependency = "torch"
|
|
21
|
+
package_extra = "lavasr"
|
|
22
|
+
|
|
23
|
+
def __init__(self, config: InferenceConfig | None = None) -> None:
|
|
24
|
+
self.config = config or InferenceConfig()
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def is_available(cls) -> bool:
|
|
28
|
+
return importlib.util.find_spec("torch") is not None and importlib.util.find_spec("yaml") is not None
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def model_specs(cls) -> tuple[ModelSpec, ...]:
|
|
32
|
+
return (
|
|
33
|
+
ModelSpec(
|
|
34
|
+
id=LAVASR_MODEL_ID,
|
|
35
|
+
backend=cls.name,
|
|
36
|
+
model_name=LAVASR_MODEL_ID,
|
|
37
|
+
name="LavaSR v2 BWE",
|
|
38
|
+
description="LavaSR v2 bandwidth extension weights for 48 kHz output.",
|
|
39
|
+
implementation="self_torch",
|
|
40
|
+
domain=("speech",),
|
|
41
|
+
architecture="vocos-istft-bwe",
|
|
42
|
+
target_sample_rates=(LAVASR_SAMPLE_RATE,),
|
|
43
|
+
weights_license="Apache-2.0",
|
|
44
|
+
weights_source="YatharthS/LavaSR",
|
|
45
|
+
weight_provider="huggingface",
|
|
46
|
+
default_weight_revision=LAVASR_REVISION,
|
|
47
|
+
requires_weights=True,
|
|
48
|
+
maturity="experimental",
|
|
49
|
+
weight_files=(
|
|
50
|
+
WeightFileSpec(
|
|
51
|
+
path="enhancer_v2/config.yaml",
|
|
52
|
+
sha256="0d970f5cdc1913730c417b49e476bb09bb8b874583d113f71a7f10c8bb3a4b7d",
|
|
53
|
+
size=526,
|
|
54
|
+
),
|
|
55
|
+
WeightFileSpec(
|
|
56
|
+
path="enhancer_v2/pytorch_model.bin",
|
|
57
|
+
sha256="d100db961b2c125d77a52a12215c689e44cd9926a72f117513395b7e25e6de12",
|
|
58
|
+
size=56316591,
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
capability=BackendCapability(
|
|
62
|
+
supports_array_io=True,
|
|
63
|
+
supports_file_io=False,
|
|
64
|
+
supports_chunking=True,
|
|
65
|
+
deterministic=True,
|
|
66
|
+
supports_cpu=True,
|
|
67
|
+
supports_cuda=True,
|
|
68
|
+
supports_mps=True,
|
|
69
|
+
precision_modes=("float32", "auto"),
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def enhance(self, audio: np.ndarray, sample_rate: int, target_sample_rate: int) -> np.ndarray:
|
|
75
|
+
if target_sample_rate != LAVASR_SAMPLE_RATE:
|
|
76
|
+
raise ValueError("The lavasr-compat backend outputs 48000 Hz audio; set target_sr=48000.")
|
|
77
|
+
if self.config.denoise:
|
|
78
|
+
raise ValueError("denoise is reserved but not supported by lavasr-compat yet")
|
|
79
|
+
|
|
80
|
+
from ..weight_store import resolve_weights_for_spec
|
|
81
|
+
|
|
82
|
+
resolve_weights_for_spec(self.model_specs()[0], self.config, allow_download=self.config.download_weights)
|
|
83
|
+
raise RuntimeError("lavasr-compat weight management is available, but inference is not implemented yet.")
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from ..config import InferenceConfig
|
|
4
|
+
from ..specs import ModelSpec
|
|
5
|
+
from .audiosr_external import AudiosrBackend
|
|
6
|
+
from .base import BackendInfo, EnhancementBackend, backend_model_specs
|
|
7
|
+
from .lavasr_compat import LavaSRCompatBackend
|
|
8
|
+
from .sinc import SincResampleBackend
|
|
9
|
+
|
|
10
|
+
_BACKENDS: dict[str, type[EnhancementBackend]] = {}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def register_backend(backend_type: type[EnhancementBackend], *, replace: bool = False) -> None:
|
|
14
|
+
"""Register an enhancement backend type by its public name."""
|
|
15
|
+
|
|
16
|
+
name = getattr(backend_type, "name", None)
|
|
17
|
+
if not isinstance(name, str) or not name:
|
|
18
|
+
raise ValueError("backend_type must define a non-empty string name")
|
|
19
|
+
if name in _BACKENDS and not replace:
|
|
20
|
+
raise ValueError(f"Backend {name!r} is already registered")
|
|
21
|
+
_BACKENDS[name] = backend_type
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def registered_backend_types() -> dict[str, type[EnhancementBackend]]:
|
|
25
|
+
"""Return a copy of the backend registry."""
|
|
26
|
+
|
|
27
|
+
return dict(_BACKENDS)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def available_backends() -> list[BackendInfo]:
|
|
31
|
+
"""Return the registered enhancement backends."""
|
|
32
|
+
|
|
33
|
+
return [
|
|
34
|
+
BackendInfo(
|
|
35
|
+
name=name,
|
|
36
|
+
description=backend.description,
|
|
37
|
+
installed=_backend_is_available(backend),
|
|
38
|
+
optional_dependency=getattr(backend, "optional_dependency", None),
|
|
39
|
+
package_extra=getattr(backend, "package_extra", None),
|
|
40
|
+
)
|
|
41
|
+
for name, backend in sorted(_BACKENDS.items(), key=lambda item: item[0])
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def get_backend(name: str, config: InferenceConfig | None = None) -> EnhancementBackend:
|
|
46
|
+
"""Create a backend by name."""
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
backend_type = _BACKENDS[name]
|
|
50
|
+
except KeyError as exc:
|
|
51
|
+
choices = ", ".join(sorted(_BACKENDS))
|
|
52
|
+
raise ValueError(f"Unknown backend {name!r}. Available backends: {choices}") from exc
|
|
53
|
+
|
|
54
|
+
return backend_type(config=config)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def list_backend_model_specs() -> list[ModelSpec]:
|
|
58
|
+
"""Return model specs exposed by every registered backend."""
|
|
59
|
+
|
|
60
|
+
specs: list[ModelSpec] = []
|
|
61
|
+
for _, backend_type in sorted(_BACKENDS.items(), key=lambda item: item[0]):
|
|
62
|
+
specs.extend(backend_model_specs(backend_type))
|
|
63
|
+
return specs
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _backend_is_available(backend_type: type[EnhancementBackend]) -> bool:
|
|
67
|
+
checker = getattr(backend_type, "is_available", None)
|
|
68
|
+
if checker is None:
|
|
69
|
+
return True
|
|
70
|
+
return bool(checker())
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
register_backend(AudiosrBackend)
|
|
74
|
+
register_backend(LavaSRCompatBackend)
|
|
75
|
+
register_backend(SincResampleBackend)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.signal import resample_poly
|
|
5
|
+
|
|
6
|
+
from ..config import InferenceConfig
|
|
7
|
+
from ..specs import ModelSpec
|
|
8
|
+
from .base import DEFAULT_ARRAY_BACKEND_CAPABILITY
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SincResampleBackend:
|
|
12
|
+
"""Deterministic baseline backend using polyphase sinc resampling."""
|
|
13
|
+
|
|
14
|
+
name = "sinc-resample"
|
|
15
|
+
description = "Deterministic polyphase sinc resampling baseline."
|
|
16
|
+
optional_dependency = None
|
|
17
|
+
package_extra = None
|
|
18
|
+
|
|
19
|
+
def __init__(self, config: InferenceConfig | None = None) -> None:
|
|
20
|
+
self.config = config or InferenceConfig()
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def model_specs(cls) -> tuple[ModelSpec, ...]:
|
|
24
|
+
return (
|
|
25
|
+
ModelSpec(
|
|
26
|
+
id="sinc-resample",
|
|
27
|
+
backend=cls.name,
|
|
28
|
+
name="Sinc Resample Baseline",
|
|
29
|
+
description=cls.description,
|
|
30
|
+
implementation="baseline",
|
|
31
|
+
domain=("general",),
|
|
32
|
+
architecture="polyphase-sinc-resampling",
|
|
33
|
+
target_sample_rates=None,
|
|
34
|
+
weights_license=None,
|
|
35
|
+
weights_source=None,
|
|
36
|
+
maturity="stable",
|
|
37
|
+
capability=DEFAULT_ARRAY_BACKEND_CAPABILITY,
|
|
38
|
+
),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def enhance(self, audio: np.ndarray, sample_rate: int, target_sample_rate: int) -> np.ndarray:
|
|
42
|
+
if sample_rate == target_sample_rate:
|
|
43
|
+
return np.asarray(audio)
|
|
44
|
+
|
|
45
|
+
gcd = np.gcd(sample_rate, target_sample_rate)
|
|
46
|
+
up = target_sample_rate // gcd
|
|
47
|
+
down = sample_rate // gcd
|
|
48
|
+
return resample_poly(audio, up, down, axis=0)
|