colabcola 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.
- colabcola/__init__.py +195 -0
- colabcola/__main__.py +3 -0
- colabcola/_hifigan/__init__.py +1 -0
- colabcola/_hifigan/audio_diagnostic.py +292 -0
- colabcola/_hifigan/extract_audio_diagnostic_input.py +117 -0
- colabcola/_hifigan/measure_full_transport.py +128 -0
- colabcola/_hifigan/profile.json +127 -0
- colabcola/_hifigan/resume_state.py +218 -0
- colabcola/_hifigan/train_colabcola.py +709 -0
- colabcola/_vendor/__init__.py +1 -0
- colabcola/_vendor/colab_cli/LICENSE +202 -0
- colabcola/_vendor/colab_cli/__init__.py +1 -0
- colabcola/_vendor/colab_cli/client.py +361 -0
- colabcola/_vendor/colab_cli/consumption.py +56 -0
- colabcola/_vendor/colab_cli/oauth_config.json +11 -0
- colabcola/_vendor/colab_cli/runtime.py +279 -0
- colabcola/_vendor/colab_cli/state.py +159 -0
- colabcola/_vendor/colab_cli/utils.py +104 -0
- colabcola/_vendor/jupyter_kernel_client/LICENSE +29 -0
- colabcola/_vendor/jupyter_kernel_client/__init__.py +27 -0
- colabcola/_vendor/jupyter_kernel_client/_version.py +7 -0
- colabcola/_vendor/jupyter_kernel_client/client.py +624 -0
- colabcola/_vendor/jupyter_kernel_client/constants.py +7 -0
- colabcola/_vendor/jupyter_kernel_client/konsoleapp.py +227 -0
- colabcola/_vendor/jupyter_kernel_client/log.py +37 -0
- colabcola/_vendor/jupyter_kernel_client/manager.py +336 -0
- colabcola/_vendor/jupyter_kernel_client/models.py +13 -0
- colabcola/_vendor/jupyter_kernel_client/shell.py +64 -0
- colabcola/_vendor/jupyter_kernel_client/snippets.py +221 -0
- colabcola/_vendor/jupyter_kernel_client/utils.py +171 -0
- colabcola/_vendor/jupyter_kernel_client/wsclient.py +1361 -0
- colabcola/_vendor/provenance.json +124 -0
- colabcola/_version.py +3 -0
- colabcola/bundle.py +637 -0
- colabcola/checkpoint_runtime.py +615 -0
- colabcola/cli.py +470 -0
- colabcola/client_downloads.py +276 -0
- colabcola/client_handoff.py +129 -0
- colabcola/cloud_project.py +351 -0
- colabcola/compiler.py +202 -0
- colabcola/console.py +63 -0
- colabcola/controller.py +330 -0
- colabcola/drive.py +564 -0
- colabcola/drive_oauth.py +208 -0
- colabcola/errors.py +14 -0
- colabcola/google_drive.py +575 -0
- colabcola/gpu_check.py +30 -0
- colabcola/hifigan.py +467 -0
- colabcola/invocation.py +60 -0
- colabcola/isolation_runtime.py +195 -0
- colabcola/lifecycle.py +637 -0
- colabcola/lifecycle_auth.py +166 -0
- colabcola/lifecycle_io.py +459 -0
- colabcola/lifecycle_kernel.py +189 -0
- colabcola/lifecycle_provider.py +417 -0
- colabcola/lifecycle_rest.py +213 -0
- colabcola/lifecycle_worker.py +462 -0
- colabcola/ljspeech.py +786 -0
- colabcola/managed.py +397 -0
- colabcola/notebook.py +286 -0
- colabcola/onboarding.py +497 -0
- colabcola/package_guide.py +379 -0
- colabcola/packager.py +390 -0
- colabcola/preflight.py +270 -0
- colabcola/profiles.py +145 -0
- colabcola/project_setup.py +395 -0
- colabcola/publisher_oauth.json +1 -0
- colabcola/python_profiles.py +8 -0
- colabcola/runtime.py +1515 -0
- colabcola/spec.py +750 -0
- colabcola/transport.py +188 -0
- colabcola/workflow.py +227 -0
- colabcola-0.1.0.dist-info/METADATA +133 -0
- colabcola-0.1.0.dist-info/RECORD +81 -0
- colabcola-0.1.0.dist-info/WHEEL +5 -0
- colabcola-0.1.0.dist-info/entry_points.txt +2 -0
- colabcola-0.1.0.dist-info/licenses/LICENSE +202 -0
- colabcola-0.1.0.dist-info/licenses/NOTICE +24 -0
- colabcola-0.1.0.dist-info/licenses/src/colabcola/_vendor/colab_cli/LICENSE +202 -0
- colabcola-0.1.0.dist-info/licenses/src/colabcola/_vendor/jupyter_kernel_client/LICENSE +29 -0
- colabcola-0.1.0.dist-info/top_level.txt +1 -0
colabcola/__init__.py
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Compile trusted batch jobs into deterministic Google Colab notebooks."""
|
|
2
|
+
|
|
3
|
+
from ._version import __version__
|
|
4
|
+
from .bundle import (
|
|
5
|
+
ARCHIVE_WARNING_BYTES,
|
|
6
|
+
Bundle,
|
|
7
|
+
FileRecord,
|
|
8
|
+
archive_size_warnings,
|
|
9
|
+
build_bundle,
|
|
10
|
+
canonical_json_bytes,
|
|
11
|
+
verify_bundle,
|
|
12
|
+
)
|
|
13
|
+
from .compiler import (
|
|
14
|
+
CompilationResult,
|
|
15
|
+
DriveControllerResult,
|
|
16
|
+
StagedBundleResult,
|
|
17
|
+
compile_drive_controller,
|
|
18
|
+
compile_drive_controller_invocation,
|
|
19
|
+
compile_invocation,
|
|
20
|
+
compile_notebook,
|
|
21
|
+
prepare_drive_bundle,
|
|
22
|
+
prepare_drive_invocation,
|
|
23
|
+
write_notebook,
|
|
24
|
+
)
|
|
25
|
+
from .controller import (
|
|
26
|
+
DriveControllerAuth,
|
|
27
|
+
DriveControllerArtifact,
|
|
28
|
+
build_drive_controller,
|
|
29
|
+
controller_auth_from_settings,
|
|
30
|
+
verify_drive_controller,
|
|
31
|
+
write_drive_controller,
|
|
32
|
+
)
|
|
33
|
+
from .drive import (
|
|
34
|
+
DRIVE_ROOT_FOLDER_NAME,
|
|
35
|
+
DRIVE_STAGING_FOLDER_NAME,
|
|
36
|
+
DriveBackend,
|
|
37
|
+
DriveFile,
|
|
38
|
+
DriveFolder,
|
|
39
|
+
DriveStagingReceipt,
|
|
40
|
+
VerifiedResult,
|
|
41
|
+
authorize_abandoned_cleanup,
|
|
42
|
+
cleanup_drive_staging,
|
|
43
|
+
drive_receipt_for_observed_upload,
|
|
44
|
+
drive_receipt_json_bytes,
|
|
45
|
+
load_drive_receipt,
|
|
46
|
+
record_verified_result,
|
|
47
|
+
stage_drive_bundle,
|
|
48
|
+
verify_drive_receipt,
|
|
49
|
+
write_drive_receipt,
|
|
50
|
+
)
|
|
51
|
+
from .errors import BundleError, DriveTransportError, NotebookError, SpecError
|
|
52
|
+
from .google_drive import (
|
|
53
|
+
DEFAULT_COLAB_SECRET_NAME,
|
|
54
|
+
GOOGLE_DRIVE_SCOPE,
|
|
55
|
+
GoogleDriveApiBackend,
|
|
56
|
+
GoogleDriveSettings,
|
|
57
|
+
GoogleOAuthClient,
|
|
58
|
+
create_google_drive_backend,
|
|
59
|
+
default_google_drive_settings_path,
|
|
60
|
+
google_drive_settings_from_clients,
|
|
61
|
+
load_google_drive_settings,
|
|
62
|
+
load_google_oauth_client,
|
|
63
|
+
verify_google_drive_settings,
|
|
64
|
+
write_google_drive_settings,
|
|
65
|
+
)
|
|
66
|
+
from .hifigan import (
|
|
67
|
+
HiFiGanOverlay,
|
|
68
|
+
HiFiGanProfile,
|
|
69
|
+
HiFiGanRuntimeLock,
|
|
70
|
+
HiFiGanSourceFile,
|
|
71
|
+
default_hifigan_profile_path,
|
|
72
|
+
hifigan_source_payload,
|
|
73
|
+
load_hifigan_profile,
|
|
74
|
+
verify_hifigan_checkout,
|
|
75
|
+
verify_hifigan_profile_assets,
|
|
76
|
+
)
|
|
77
|
+
from .invocation import JobInvocation, bind_job_arguments, load_job_invocation
|
|
78
|
+
from .ljspeech import (
|
|
79
|
+
LJSpeechSelection,
|
|
80
|
+
LJSpeechSubset,
|
|
81
|
+
build_ljspeech_subset,
|
|
82
|
+
ljspeech_subset_payload,
|
|
83
|
+
select_ljspeech_ids,
|
|
84
|
+
verify_ljspeech_subset,
|
|
85
|
+
)
|
|
86
|
+
from .notebook import (
|
|
87
|
+
NotebookArtifact,
|
|
88
|
+
build_notebook,
|
|
89
|
+
encode_archive_chunks,
|
|
90
|
+
verify_notebook,
|
|
91
|
+
)
|
|
92
|
+
from .spec import JobSpec, RemoteInputSpec, RuntimeSpec, load_job_spec
|
|
93
|
+
from .transport import (
|
|
94
|
+
STAGED_BUNDLE_HEADER_BYTES,
|
|
95
|
+
STAGED_BUNDLE_MAGIC,
|
|
96
|
+
STAGED_BUNDLE_SUFFIX,
|
|
97
|
+
StagedBundleArtifact,
|
|
98
|
+
build_staged_bundle,
|
|
99
|
+
verify_staged_bundle,
|
|
100
|
+
verify_staged_bundle_file,
|
|
101
|
+
write_staged_bundle,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
__all__ = [
|
|
105
|
+
"ARCHIVE_WARNING_BYTES",
|
|
106
|
+
"Bundle",
|
|
107
|
+
"BundleError",
|
|
108
|
+
"CompilationResult",
|
|
109
|
+
"DRIVE_ROOT_FOLDER_NAME",
|
|
110
|
+
"DRIVE_STAGING_FOLDER_NAME",
|
|
111
|
+
"DEFAULT_COLAB_SECRET_NAME",
|
|
112
|
+
"DriveBackend",
|
|
113
|
+
"DriveControllerAuth",
|
|
114
|
+
"DriveControllerArtifact",
|
|
115
|
+
"DriveControllerResult",
|
|
116
|
+
"DriveFile",
|
|
117
|
+
"DriveFolder",
|
|
118
|
+
"DriveStagingReceipt",
|
|
119
|
+
"DriveTransportError",
|
|
120
|
+
"FileRecord",
|
|
121
|
+
"JobInvocation",
|
|
122
|
+
"JobSpec",
|
|
123
|
+
"LJSpeechSelection",
|
|
124
|
+
"LJSpeechSubset",
|
|
125
|
+
"HiFiGanOverlay",
|
|
126
|
+
"HiFiGanProfile",
|
|
127
|
+
"HiFiGanRuntimeLock",
|
|
128
|
+
"HiFiGanSourceFile",
|
|
129
|
+
"NotebookArtifact",
|
|
130
|
+
"NotebookError",
|
|
131
|
+
"RemoteInputSpec",
|
|
132
|
+
"STAGED_BUNDLE_HEADER_BYTES",
|
|
133
|
+
"STAGED_BUNDLE_MAGIC",
|
|
134
|
+
"STAGED_BUNDLE_SUFFIX",
|
|
135
|
+
"SpecError",
|
|
136
|
+
"StagedBundleArtifact",
|
|
137
|
+
"StagedBundleResult",
|
|
138
|
+
"VerifiedResult",
|
|
139
|
+
"authorize_abandoned_cleanup",
|
|
140
|
+
"bind_job_arguments",
|
|
141
|
+
"archive_size_warnings",
|
|
142
|
+
"build_bundle",
|
|
143
|
+
"build_drive_controller",
|
|
144
|
+
"build_notebook",
|
|
145
|
+
"build_staged_bundle",
|
|
146
|
+
"build_ljspeech_subset",
|
|
147
|
+
"canonical_json_bytes",
|
|
148
|
+
"cleanup_drive_staging",
|
|
149
|
+
"compile_drive_controller",
|
|
150
|
+
"compile_drive_controller_invocation",
|
|
151
|
+
"compile_invocation",
|
|
152
|
+
"compile_notebook",
|
|
153
|
+
"default_hifigan_profile_path",
|
|
154
|
+
"drive_receipt_for_observed_upload",
|
|
155
|
+
"drive_receipt_json_bytes",
|
|
156
|
+
"encode_archive_chunks",
|
|
157
|
+
"load_job_invocation",
|
|
158
|
+
"load_job_spec",
|
|
159
|
+
"load_drive_receipt",
|
|
160
|
+
"hifigan_source_payload",
|
|
161
|
+
"load_hifigan_profile",
|
|
162
|
+
"ljspeech_subset_payload",
|
|
163
|
+
"prepare_drive_bundle",
|
|
164
|
+
"prepare_drive_invocation",
|
|
165
|
+
"record_verified_result",
|
|
166
|
+
"select_ljspeech_ids",
|
|
167
|
+
"stage_drive_bundle",
|
|
168
|
+
"verify_bundle",
|
|
169
|
+
"verify_drive_controller",
|
|
170
|
+
"verify_drive_receipt",
|
|
171
|
+
"verify_notebook",
|
|
172
|
+
"verify_staged_bundle",
|
|
173
|
+
"verify_staged_bundle_file",
|
|
174
|
+
"verify_hifigan_checkout",
|
|
175
|
+
"verify_hifigan_profile_assets",
|
|
176
|
+
"verify_ljspeech_subset",
|
|
177
|
+
"write_drive_controller",
|
|
178
|
+
"write_drive_receipt",
|
|
179
|
+
"write_notebook",
|
|
180
|
+
"write_staged_bundle",
|
|
181
|
+
"__version__",
|
|
182
|
+
"GOOGLE_DRIVE_SCOPE",
|
|
183
|
+
"GoogleDriveApiBackend",
|
|
184
|
+
"GoogleDriveSettings",
|
|
185
|
+
"GoogleOAuthClient",
|
|
186
|
+
"RuntimeSpec",
|
|
187
|
+
"controller_auth_from_settings",
|
|
188
|
+
"create_google_drive_backend",
|
|
189
|
+
"default_google_drive_settings_path",
|
|
190
|
+
"google_drive_settings_from_clients",
|
|
191
|
+
"load_google_drive_settings",
|
|
192
|
+
"load_google_oauth_client",
|
|
193
|
+
"verify_google_drive_settings",
|
|
194
|
+
"write_google_drive_settings",
|
|
195
|
+
]
|
colabcola/__main__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Checked-in assets for the pinned HiFi-GAN Phase 2-3 workload."""
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"""Bounded HiFi-GAN generated-audio diagnostic for a trusted checkpoint.
|
|
2
|
+
|
|
3
|
+
Derived from ``inference.py`` and ``meldataset.py`` in jik876/hifi-gan commit
|
|
4
|
+
4769534d45265d52a904b850da5a622601885777. The bundled upstream LICENSE
|
|
5
|
+
applies to the derived portions of this file.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import pathlib
|
|
15
|
+
import shutil
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
import torch
|
|
19
|
+
from scipy.io.wavfile import read, write
|
|
20
|
+
|
|
21
|
+
import meldataset as upstream_meldataset
|
|
22
|
+
from env import AttrDict
|
|
23
|
+
from models import Generator
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
MAX_WAV_VALUE = 32768.0
|
|
27
|
+
UPSTREAM_COMMIT = "4769534d45265d52a904b850da5a622601885777"
|
|
28
|
+
EXPECTED_INPUTS = {
|
|
29
|
+
"LJ001-0028.wav": (
|
|
30
|
+
261_478,
|
|
31
|
+
"58831256095edbcc72346a2db9be39efd39662f442f8115f19da4d55876cfa0e",
|
|
32
|
+
),
|
|
33
|
+
"LJ001-0068.wav": (
|
|
34
|
+
402_278,
|
|
35
|
+
"9ec4f7944fdf753a62792aef59da5f2145c0a69d8877fc07c4c77c7189dcdc0e",
|
|
36
|
+
),
|
|
37
|
+
"LJ002-0149.wav": (
|
|
38
|
+
349_030,
|
|
39
|
+
"d71743df2a5842ae0130920afe910e53516374dae97e8f24da403efdc4cc9c60",
|
|
40
|
+
),
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def mel_spectrogram(
|
|
45
|
+
y,
|
|
46
|
+
n_fft,
|
|
47
|
+
num_mels,
|
|
48
|
+
sampling_rate,
|
|
49
|
+
hop_size,
|
|
50
|
+
win_size,
|
|
51
|
+
fmin,
|
|
52
|
+
fmax,
|
|
53
|
+
center=False,
|
|
54
|
+
):
|
|
55
|
+
"""Pinned upstream transform adapted to current librosa and PyTorch APIs."""
|
|
56
|
+
|
|
57
|
+
cache_key = f"{fmax}_{y.device}"
|
|
58
|
+
if cache_key not in upstream_meldataset.mel_basis:
|
|
59
|
+
mel = upstream_meldataset.librosa_mel_fn(
|
|
60
|
+
sr=sampling_rate,
|
|
61
|
+
n_fft=n_fft,
|
|
62
|
+
n_mels=num_mels,
|
|
63
|
+
fmin=fmin,
|
|
64
|
+
fmax=fmax,
|
|
65
|
+
)
|
|
66
|
+
upstream_meldataset.mel_basis[cache_key] = torch.from_numpy(mel).float().to(
|
|
67
|
+
y.device
|
|
68
|
+
)
|
|
69
|
+
upstream_meldataset.hann_window[str(y.device)] = torch.hann_window(win_size).to(
|
|
70
|
+
y.device
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
padded = torch.nn.functional.pad(
|
|
74
|
+
y.unsqueeze(1),
|
|
75
|
+
(int((n_fft - hop_size) / 2), int((n_fft - hop_size) / 2)),
|
|
76
|
+
mode="reflect",
|
|
77
|
+
).squeeze(1)
|
|
78
|
+
spectrum = torch.stft(
|
|
79
|
+
padded,
|
|
80
|
+
n_fft,
|
|
81
|
+
hop_length=hop_size,
|
|
82
|
+
win_length=win_size,
|
|
83
|
+
window=upstream_meldataset.hann_window[str(y.device)],
|
|
84
|
+
center=center,
|
|
85
|
+
pad_mode="reflect",
|
|
86
|
+
normalized=False,
|
|
87
|
+
onesided=True,
|
|
88
|
+
return_complex=True,
|
|
89
|
+
).abs()
|
|
90
|
+
spectrum = torch.matmul(upstream_meldataset.mel_basis[cache_key], spectrum)
|
|
91
|
+
return upstream_meldataset.spectral_normalize_torch(spectrum)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _sha256(path: pathlib.Path) -> str:
|
|
95
|
+
digest = hashlib.sha256()
|
|
96
|
+
with path.open("rb") as stream:
|
|
97
|
+
while block := stream.read(1024 * 1024):
|
|
98
|
+
digest.update(block)
|
|
99
|
+
return digest.hexdigest()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _audio_stats(samples: np.ndarray) -> dict[str, object]:
|
|
103
|
+
normalized = samples.astype(np.float64) / MAX_WAV_VALUE
|
|
104
|
+
return {
|
|
105
|
+
"frames": int(samples.size),
|
|
106
|
+
"peak_absolute": float(np.max(np.abs(normalized))) if samples.size else 0.0,
|
|
107
|
+
"rms": float(np.sqrt(np.mean(np.square(normalized)))) if samples.size else 0.0,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _atomic_json(path: pathlib.Path, value: object) -> None:
|
|
112
|
+
content = (
|
|
113
|
+
json.dumps(
|
|
114
|
+
value,
|
|
115
|
+
allow_nan=False,
|
|
116
|
+
ensure_ascii=False,
|
|
117
|
+
separators=(",", ":"),
|
|
118
|
+
sort_keys=True,
|
|
119
|
+
).encode("utf-8")
|
|
120
|
+
+ b"\n"
|
|
121
|
+
)
|
|
122
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
123
|
+
temporary = path.with_name("." + path.name + ".tmp")
|
|
124
|
+
try:
|
|
125
|
+
with temporary.open("xb") as stream:
|
|
126
|
+
stream.write(content)
|
|
127
|
+
stream.flush()
|
|
128
|
+
os.fsync(stream.fileno())
|
|
129
|
+
os.replace(temporary, path)
|
|
130
|
+
finally:
|
|
131
|
+
temporary.unlink(missing_ok=True)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def run(args: argparse.Namespace) -> dict[str, object]:
|
|
135
|
+
checkpoint = pathlib.Path(args.checkpoint)
|
|
136
|
+
checkpoint_sha256 = _sha256(checkpoint)
|
|
137
|
+
if checkpoint_sha256 != args.expected_checkpoint_sha256:
|
|
138
|
+
raise RuntimeError("generator checkpoint SHA-256 does not match")
|
|
139
|
+
if not torch.cuda.is_available():
|
|
140
|
+
raise RuntimeError("audio diagnostic requires one CUDA GPU")
|
|
141
|
+
|
|
142
|
+
config_value = json.loads(pathlib.Path(args.config).read_text(encoding="utf-8"))
|
|
143
|
+
config = AttrDict(config_value)
|
|
144
|
+
device = torch.device("cuda")
|
|
145
|
+
torch.manual_seed(config.seed)
|
|
146
|
+
torch.cuda.manual_seed_all(config.seed)
|
|
147
|
+
torch.backends.cudnn.benchmark = False
|
|
148
|
+
|
|
149
|
+
generator = Generator(config).to(device)
|
|
150
|
+
checkpoint_value = torch.load(checkpoint, map_location=device, weights_only=False)
|
|
151
|
+
if not isinstance(checkpoint_value, dict) or "generator" not in checkpoint_value:
|
|
152
|
+
raise RuntimeError("generator checkpoint has an unexpected structure")
|
|
153
|
+
generator.load_state_dict(checkpoint_value["generator"], strict=True)
|
|
154
|
+
generator.eval()
|
|
155
|
+
generator.remove_weight_norm()
|
|
156
|
+
|
|
157
|
+
output_root = pathlib.Path(args.output_root)
|
|
158
|
+
reference_root = output_root / "reference"
|
|
159
|
+
generated_root = output_root / "generated"
|
|
160
|
+
reference_root.mkdir(parents=True, exist_ok=False)
|
|
161
|
+
generated_root.mkdir(parents=True, exist_ok=False)
|
|
162
|
+
|
|
163
|
+
records: list[dict[str, object]] = []
|
|
164
|
+
if [pathlib.Path(value).name for value in args.input_wav] != list(EXPECTED_INPUTS):
|
|
165
|
+
raise RuntimeError("audio diagnostic inputs do not match the pinned ordered clip set")
|
|
166
|
+
with torch.no_grad():
|
|
167
|
+
for input_value in args.input_wav:
|
|
168
|
+
source = pathlib.Path(input_value)
|
|
169
|
+
expected_size, expected_sha256 = EXPECTED_INPUTS[source.name]
|
|
170
|
+
if source.stat().st_size != expected_size or _sha256(source) != expected_sha256:
|
|
171
|
+
raise RuntimeError(f"validation WAV identity does not match for {source.name}")
|
|
172
|
+
sampling_rate, samples = read(source)
|
|
173
|
+
if sampling_rate != config.sampling_rate:
|
|
174
|
+
raise RuntimeError(f"unexpected sampling rate for {source.name}")
|
|
175
|
+
if samples.dtype != np.int16 or samples.ndim != 1 or samples.size == 0:
|
|
176
|
+
raise RuntimeError(f"unexpected WAV format for {source.name}")
|
|
177
|
+
|
|
178
|
+
reference_path = reference_root / source.name
|
|
179
|
+
generated_path = generated_root / f"{source.stem}_generated.wav"
|
|
180
|
+
shutil.copyfile(source, reference_path)
|
|
181
|
+
|
|
182
|
+
reference_tensor = (
|
|
183
|
+
torch.from_numpy(samples.astype(np.float32)).to(device) / MAX_WAV_VALUE
|
|
184
|
+
).unsqueeze(0)
|
|
185
|
+
input_mel = mel_spectrogram(
|
|
186
|
+
reference_tensor,
|
|
187
|
+
config.n_fft,
|
|
188
|
+
config.num_mels,
|
|
189
|
+
config.sampling_rate,
|
|
190
|
+
config.hop_size,
|
|
191
|
+
config.win_size,
|
|
192
|
+
config.fmin,
|
|
193
|
+
config.fmax,
|
|
194
|
+
)
|
|
195
|
+
generated_tensor = generator(input_mel).squeeze(0).squeeze(0)
|
|
196
|
+
if not bool(torch.isfinite(generated_tensor).all().item()):
|
|
197
|
+
raise RuntimeError(f"generated non-finite audio for {source.name}")
|
|
198
|
+
|
|
199
|
+
generated_float = generated_tensor.detach().cpu().numpy()
|
|
200
|
+
clipped_samples = int(np.count_nonzero(np.abs(generated_float) > 1.0))
|
|
201
|
+
generated_samples = np.rint(
|
|
202
|
+
np.clip(generated_float, -1.0, 1.0) * 32767.0
|
|
203
|
+
).astype(np.int16)
|
|
204
|
+
write(generated_path, config.sampling_rate, generated_samples)
|
|
205
|
+
|
|
206
|
+
generated_for_mel = torch.from_numpy(generated_float).to(device).unsqueeze(0)
|
|
207
|
+
reference_loss_mel = mel_spectrogram(
|
|
208
|
+
reference_tensor,
|
|
209
|
+
config.n_fft,
|
|
210
|
+
config.num_mels,
|
|
211
|
+
config.sampling_rate,
|
|
212
|
+
config.hop_size,
|
|
213
|
+
config.win_size,
|
|
214
|
+
config.fmin,
|
|
215
|
+
config.fmax_for_loss,
|
|
216
|
+
)
|
|
217
|
+
generated_loss_mel = mel_spectrogram(
|
|
218
|
+
generated_for_mel,
|
|
219
|
+
config.n_fft,
|
|
220
|
+
config.num_mels,
|
|
221
|
+
config.sampling_rate,
|
|
222
|
+
config.hop_size,
|
|
223
|
+
config.win_size,
|
|
224
|
+
config.fmin,
|
|
225
|
+
config.fmax_for_loss,
|
|
226
|
+
)
|
|
227
|
+
aligned_frames = min(reference_loss_mel.shape[-1], generated_loss_mel.shape[-1])
|
|
228
|
+
mel_l1 = torch.nn.functional.l1_loss(
|
|
229
|
+
reference_loss_mel[..., :aligned_frames],
|
|
230
|
+
generated_loss_mel[..., :aligned_frames],
|
|
231
|
+
).item()
|
|
232
|
+
|
|
233
|
+
records.append(
|
|
234
|
+
{
|
|
235
|
+
"clip_id": source.stem,
|
|
236
|
+
"generated": {
|
|
237
|
+
**_audio_stats(generated_samples),
|
|
238
|
+
"clipped_samples_before_clamp": clipped_samples,
|
|
239
|
+
"path": generated_path.as_posix(),
|
|
240
|
+
"sha256": _sha256(generated_path),
|
|
241
|
+
"size": generated_path.stat().st_size,
|
|
242
|
+
},
|
|
243
|
+
"mel_l1_aligned": float(mel_l1),
|
|
244
|
+
"mel_l1_frames": int(aligned_frames),
|
|
245
|
+
"reference": {
|
|
246
|
+
**_audio_stats(samples),
|
|
247
|
+
"path": reference_path.as_posix(),
|
|
248
|
+
"sha256": _sha256(reference_path),
|
|
249
|
+
"size": reference_path.stat().st_size,
|
|
250
|
+
},
|
|
251
|
+
"sampling_rate": int(sampling_rate),
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
report = {
|
|
256
|
+
"checkpoint": {
|
|
257
|
+
"path": checkpoint.as_posix(),
|
|
258
|
+
"sha256": checkpoint_sha256,
|
|
259
|
+
"size": checkpoint.stat().st_size,
|
|
260
|
+
},
|
|
261
|
+
"clips": records,
|
|
262
|
+
"diagnostic_boundary": (
|
|
263
|
+
"Operational generated-audio evidence only; not a convergence, perceptual-quality, "
|
|
264
|
+
"MOS, or paper-reproduction result."
|
|
265
|
+
),
|
|
266
|
+
"gpu": {
|
|
267
|
+
"count": torch.cuda.device_count(),
|
|
268
|
+
"memory_bytes": torch.cuda.get_device_properties(0).total_memory,
|
|
269
|
+
"name": torch.cuda.get_device_name(0),
|
|
270
|
+
},
|
|
271
|
+
"sample_count": len(records),
|
|
272
|
+
"state": "audio_diagnostic_complete",
|
|
273
|
+
"upstream_commit": UPSTREAM_COMMIT,
|
|
274
|
+
}
|
|
275
|
+
_atomic_json(pathlib.Path(args.report), report)
|
|
276
|
+
print(json.dumps(report, allow_nan=False, separators=(",", ":"), sort_keys=True))
|
|
277
|
+
return report
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _parser() -> argparse.ArgumentParser:
|
|
281
|
+
parser = argparse.ArgumentParser()
|
|
282
|
+
parser.add_argument("--config", required=True)
|
|
283
|
+
parser.add_argument("--checkpoint", required=True)
|
|
284
|
+
parser.add_argument("--expected-checkpoint-sha256", required=True)
|
|
285
|
+
parser.add_argument("--input-wav", action="append", required=True)
|
|
286
|
+
parser.add_argument("--output-root", required=True)
|
|
287
|
+
parser.add_argument("--report", required=True)
|
|
288
|
+
return parser
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
if __name__ == "__main__":
|
|
292
|
+
run(_parser().parse_args())
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Extract the one verified HiFi-GAN checkpoint needed by the audio diagnostic."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import hashlib
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import tempfile
|
|
11
|
+
import zipfile
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
CHECKPOINT_MEMBER = "outputs/cp_hifigan/g_00005000"
|
|
15
|
+
CHECKPOINT_SIZE = 55_825_339
|
|
16
|
+
CHECKPOINT_SHA256 = "836627cc247ec688589593d2324aae2cfadc8958eb17304c9426c44de56b46e3"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _sha256(path: Path) -> str:
|
|
20
|
+
digest = hashlib.sha256()
|
|
21
|
+
with path.open("rb") as stream:
|
|
22
|
+
for block in iter(lambda: stream.read(1024 * 1024), b""):
|
|
23
|
+
digest.update(block)
|
|
24
|
+
return digest.hexdigest()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def extract_checkpoint(
|
|
28
|
+
artifacts_zip: Path,
|
|
29
|
+
output_path: Path,
|
|
30
|
+
*,
|
|
31
|
+
member: str = CHECKPOINT_MEMBER,
|
|
32
|
+
expected_size: int = CHECKPOINT_SIZE,
|
|
33
|
+
expected_sha256: str = CHECKPOINT_SHA256,
|
|
34
|
+
) -> dict[str, object]:
|
|
35
|
+
artifacts_zip = artifacts_zip.resolve(strict=True)
|
|
36
|
+
if not artifacts_zip.is_file() or artifacts_zip.is_symlink():
|
|
37
|
+
raise ValueError(f"artifacts path is not a regular file: {artifacts_zip}")
|
|
38
|
+
|
|
39
|
+
output_path = output_path.resolve(strict=False)
|
|
40
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
if output_path.exists():
|
|
42
|
+
if not output_path.is_file() or output_path.is_symlink():
|
|
43
|
+
raise ValueError(f"output path is not a regular file: {output_path}")
|
|
44
|
+
actual_size = output_path.stat().st_size
|
|
45
|
+
actual_sha256 = _sha256(output_path)
|
|
46
|
+
if actual_size != expected_size or actual_sha256 != expected_sha256:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
"existing output does not match the verified checkpoint: "
|
|
49
|
+
f"size={actual_size}, sha256={actual_sha256}"
|
|
50
|
+
)
|
|
51
|
+
return {
|
|
52
|
+
"artifacts_zip": artifacts_zip.as_posix(),
|
|
53
|
+
"checkpoint_path": output_path.as_posix(),
|
|
54
|
+
"checkpoint_size": actual_size,
|
|
55
|
+
"checkpoint_sha256": actual_sha256,
|
|
56
|
+
"member": member,
|
|
57
|
+
"reused": True,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
with zipfile.ZipFile(artifacts_zip) as archive:
|
|
61
|
+
matching = [info for info in archive.infolist() if info.filename == member]
|
|
62
|
+
if len(matching) != 1:
|
|
63
|
+
raise ValueError(f"expected exactly one {member!r} member; found {len(matching)}")
|
|
64
|
+
info = matching[0]
|
|
65
|
+
if info.is_dir() or info.file_size != expected_size:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
f"checkpoint member size mismatch: expected {expected_size}, got {info.file_size}"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
handle, temporary_name = tempfile.mkstemp(
|
|
71
|
+
dir=output_path.parent,
|
|
72
|
+
prefix=f".{output_path.name}.",
|
|
73
|
+
suffix=".tmp",
|
|
74
|
+
)
|
|
75
|
+
os.close(handle)
|
|
76
|
+
temporary_path = Path(temporary_name)
|
|
77
|
+
try:
|
|
78
|
+
digest = hashlib.sha256()
|
|
79
|
+
with archive.open(info, "r") as source, temporary_path.open("wb") as destination:
|
|
80
|
+
while block := source.read(1024 * 1024):
|
|
81
|
+
destination.write(block)
|
|
82
|
+
digest.update(block)
|
|
83
|
+
destination.flush()
|
|
84
|
+
os.fsync(destination.fileno())
|
|
85
|
+
actual_size = temporary_path.stat().st_size
|
|
86
|
+
actual_sha256 = digest.hexdigest()
|
|
87
|
+
if actual_size != expected_size or actual_sha256 != expected_sha256:
|
|
88
|
+
raise ValueError(
|
|
89
|
+
"extracted checkpoint failed verification: "
|
|
90
|
+
f"size={actual_size}, sha256={actual_sha256}"
|
|
91
|
+
)
|
|
92
|
+
os.replace(temporary_path, output_path)
|
|
93
|
+
finally:
|
|
94
|
+
temporary_path.unlink(missing_ok=True)
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
"artifacts_zip": artifacts_zip.as_posix(),
|
|
98
|
+
"checkpoint_path": output_path.as_posix(),
|
|
99
|
+
"checkpoint_size": expected_size,
|
|
100
|
+
"checkpoint_sha256": expected_sha256,
|
|
101
|
+
"member": member,
|
|
102
|
+
"reused": False,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def main() -> int:
|
|
107
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
108
|
+
parser.add_argument("artifacts_zip", type=Path)
|
|
109
|
+
parser.add_argument("output_path", type=Path)
|
|
110
|
+
args = parser.parse_args()
|
|
111
|
+
result = extract_checkpoint(args.artifacts_zip, args.output_path)
|
|
112
|
+
print(json.dumps(result, sort_keys=True, separators=(",", ":")))
|
|
113
|
+
return 0
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
raise SystemExit(main())
|