/ challenges-old / string
Sign in
challenges-old / string
string / tools / challenge_tools / wav.py
52 lines · 2.0 KBCodeBlameHistory
ddfc839Add string-pluck-v1 challenge, solution schema, and scoring toolsJeremy Magland 1"""Reading and checking wav deliverables."""
2from __future__ import annotations
4import hashlib
5import os
6import urllib.request
7from pathlib import Path
9import numpy as np
10from scipy.io import wavfile
13def resolve_output(output: str, json_dir: Path, cache_dir: Path | None = None) -> Path:
14 """Return a local path for a solution output, downloading URLs into cache_dir."""
15 if output.startswith("http://") or output.startswith("https://"):
16 cache_dir = cache_dir or Path(os.environ.get("CHALLENGE_CACHE", ".challenge-cache"))
17 cache_dir.mkdir(parents=True, exist_ok=True)
18 name = hashlib.sha256(output.encode()).hexdigest()[:16] + ".wav"
19 dest = cache_dir / name
20 if not dest.exists():
21 urllib.request.urlretrieve(output, dest)
22 return dest
23 p = (json_dir / output).resolve()
24 return p
27def read_wav(path: Path, spec: dict) -> np.ndarray:
28 """Read a wav and check it against the challenge's output spec. Returns float64 samples (mono)."""
29 rate, data = wavfile.read(path)
30 problems = []
31 if rate != spec["sample_rate_hz"]:
32 problems.append(f"sample rate {rate} != {spec['sample_rate_hz']}")
33 if spec.get("sample_format") == "float32" and data.dtype != np.float32:
34 problems.append(f"sample format {data.dtype} != float32")
35 channels = 1 if data.ndim == 1 else data.shape[1]
36 if channels != spec["channels"]:
37 problems.append(f"channels {channels} != {spec['channels']}")
38 n = data.shape[0]
39 if n != spec["num_samples"]:
40 problems.append(f"num samples {n} != {spec['num_samples']}")
41 if not np.all(np.isfinite(data)):
42 problems.append("non-finite samples")
43 if problems:
44 raise ValueError(f"{path}: " + "; ".join(problems))
45 if data.ndim > 1:
46 data = data[:, 0]
47 return data.astype(np.float64)
50def write_wav(path: Path, samples: np.ndarray, sample_rate: int) -> None:
51 path.parent.mkdir(parents=True, exist_ok=True)
52 wavfile.write(path, sample_rate, np.asarray(samples, dtype=np.float32))
↑↓ move↵openescclose