From c2acd8f913443450f38942983370c01c2f5c3ee9 Mon Sep 17 00:00:00 2001 From: hemanpapa-coder Date: Fri, 16 Oct 2026 06:24:44 +0900 Subject: [PATCH 1/6] Add opt-in experimental texture softening with reproducible DSP tests --- .github/workflows/cpu-unit-tests.yml | 2 +- TEXTURE_SOFTENING.md | 50 +++++++++++++++++++++++++++ inference.py | 15 ++++++++ tests/benchmark_texture_softening.py | 51 ++++++++++++++++++++++++++++ tests/test_texture_inference.py | 39 +++++++++++++++++++++ tests/test_texture_softening.py | 45 ++++++++++++++++++++++++ texture_softening.py | 43 +++++++++++++++++++++++ 7 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 TEXTURE_SOFTENING.md create mode 100644 tests/benchmark_texture_softening.py create mode 100644 tests/test_texture_inference.py create mode 100644 tests/test_texture_softening.py create mode 100644 texture_softening.py diff --git a/.github/workflows/cpu-unit-tests.yml b/.github/workflows/cpu-unit-tests.yml index d8efd53..449af90 100644 --- a/.github/workflows/cpu-unit-tests.yml +++ b/.github/workflows/cpu-unit-tests.yml @@ -19,7 +19,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install torch==2.11.0 --index-url https://download.pytorch.org/whl/cpu - python -m pip install numpy==1.26.4 huggingface-hub==1.27.0 soundfile==0.14.0 + python -m pip install numpy==1.26.4 scipy==1.13.1 huggingface-hub==1.27.0 soundfile==0.14.0 - name: Compile and run unit tests run: | python -m py_compile inference.py tests/test_inference.py diff --git a/TEXTURE_SOFTENING.md b/TEXTURE_SOFTENING.md new file mode 100644 index 0000000..0cc5f21 --- /dev/null +++ b/TEXTURE_SOFTENING.md @@ -0,0 +1,50 @@ +# Experimental texture softening + +Contribution from Neuracoust, adapted from its Sound Build-up post-processing. +Apollo remains by Kai Li and Yi Luo / Look2Hear. Existing LICENSE (CC BY-SA 4.0) +applies. This does not modify model weights or claim a new restoration model. + +`python inference.py --in_wav=input.wav --out_wav=output.wav --texture-reduction-db=2` + +The default is 0: the existing inference path and output remain unchanged. +The optional CPU NumPy/SciPy post-process runs once after full-file or chunked +inference. It needs at least 2048 output frames. It adds no model forward passes, +but holds a full-output STFT in memory, so chunked model inference no longer +implies bounded post-processing memory. SciPy is required only when enabled. + +The shared channel gain targets short positive spectral excursions relative to +a roughly 40 ms temporal median. It tapers between 1.2 and 14 kHz, limits gain +reduction to the requested 0–3 dB cap, and gates broad rising-energy frames. +Thresholds are engineering heuristics, not an artifact or vocal detector. +Normal musical modulation can trigger it; attacks can still be affected. +The cap describes STFT gain, not a bound on every time-domain sample difference. +It can reduce wanted detail and is deliberately opt-in. + +## Validation and limits + +Run `python -m unittest discover -s tests -p 'test_texture_softening.py' -v`. +Integration tests additionally verify that the disabled path never calls the +post-process, the enabled path writes its exact result, and the source WAV is +unchanged. Tests check exact zero-strength bypass, stationary tone preservation, common +stereo scaling including polarity, silence, exact odd output length, invalid +input, and a positive modulation case where processing actually engages. + +Run `python tests/benchmark_texture_softening.py --output-dir=texture-comparison` +to generate deterministic synthetic before/after FLOAT WAVs, equal-RMS audition +copies with common peak headroom, SHA-256 provenance and timing JSON. No Apollo +checkpoint, private song, or external download is needed. This tests the +post-process only, not an improvement in Apollo restoration quality. + +Prior local music experiments on a larger combined inference chain found local +maximum reductions of 1.38–1.80 dB and overall RMS reduction below 0.0011 dB. +High-band reference error increased 0.012–0.065 dB and waveform SNR was mixed. +Those private fixtures are not included, and those observations do not isolate +this proposal against current upstream Apollo. There is no controlled listening +study or evidence of universal vocal/piano improvement. Wider upstream-model, +music, transient, backend and listening evaluation remains open. This is a draft +for maintainer discussion, not a recommendation to enable by default. + +Local synthetic benchmark (macOS 26.5.1 arm64, Python 3.9.6, NumPy 2.0.2, +SciPy 1.13.1): 4 s stereo took 0.0301 / 0.0250 / 0.0250 s for the +post-process alone; max STFT gain reduction 1.9105 dB. Timing is hardware and +fixture dependent. Equal-RMS audition copies matched RMS within 0.000001 dB. diff --git a/inference.py b/inference.py index 356bda4..c0fa93a 100644 --- a/inference.py +++ b/inference.py @@ -230,7 +230,10 @@ def run_inference( overlap_seconds=1.0, chunk_batch_size=1, chunk_pad_seconds=0.0, + texture_reduction_db=0.0, ): + if not np.isfinite(texture_reduction_db) or not 0 <= texture_reduction_db <= 3: + raise ValueError("Texture reduction must be finite and between 0 and 3 dB.") input_path = Path(input_wav) output_path = Path(output_wav) if input_path.resolve() == output_path.resolve(): @@ -263,6 +266,13 @@ def run_inference( chunk_batch_size=chunk_batch_size, chunk_pad_samples=chunk_pad_samples, ) + if texture_reduction_db: + from texture_softening import soften_texture + + softened, _ = soften_texture( + output.squeeze(0).numpy(), sample_rate, texture_reduction_db + ) + output = torch.from_numpy(softened).unsqueeze(0) save_audio(output_path, output, sample_rate) return device @@ -310,6 +320,10 @@ def main(): default=1, help="Chunks per model forward pass (default: 1; higher uses more memory)", ) + parser.add_argument( + "--texture-reduction-db", type=float, default=0.0, + help="Experimental texture softening cap, 0-3 dB (default: 0, disabled)", + ) args = parser.parse_args() device = run_inference( @@ -321,6 +335,7 @@ def main(): args.overlap_seconds, args.chunk_batch_size, args.chunk_pad_seconds, + args.texture_reduction_db, ) print(f"Inference completed on {device}: {args.out_wav}") diff --git a/tests/benchmark_texture_softening.py b/tests/benchmark_texture_softening.py new file mode 100644 index 0000000..64b19b0 --- /dev/null +++ b/tests/benchmark_texture_softening.py @@ -0,0 +1,51 @@ +"""Reproducible synthetic DSP comparison, no model or private assets.""" +import argparse +import hashlib +import json +import platform +from pathlib import Path +import sys +import time + +import numpy as np +import scipy +import soundfile as sf + +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from texture_softening import soften_texture + + +def main(): + parser = argparse.ArgumentParser(__doc__) + parser.add_argument('--output-dir', type=Path, required=True) + args = parser.parse_args() + args.output_dir.mkdir(parents=True, exist_ok=True) + sr = 44100 + t = np.arange(4*sr)/sr + low = .4*np.sin(2*np.pi*220*t) + high = .02*(.1+(np.mod(t, .08)<.008))*np.sin(2*np.pi*4000*t) + x = np.asarray([low+high, .7*low-.3*high], dtype=np.float32) + times = [] + for _ in range(3): + start = time.perf_counter() + y, stats = soften_texture(x, max_reduction_db=2) + times.append(time.perf_counter()-start) + report = {'python': platform.python_version(), 'platform': platform.platform(), + 'numpy': np.__version__, 'scipy': scipy.__version__, + 'sample_rate': sr, 'frames': len(t), 'cap_db': 2, + 'postprocess_seconds': times, 'statistics': stats, 'files': {}} + rms = lambda v: float(np.sqrt(np.mean(v.astype(np.float64)**2))) + matched = [v*(rms(x)/max(rms(v), 1e-30)) for v in (x,y)] + common = min(1., 10**(-1/20)/max(float(abs(v).max()) for v in matched)) + for name,v in [('raw-before',x),('raw-after',y), + ('audition-before',matched[0]*common),('audition-after',matched[1]*common)]: + path = args.output_dir / (name+'.wav') + sf.write(path, v.T, sr, subtype='FLOAT') + report['files'][path.name] = {'sha256': hashlib.sha256(path.read_bytes()).hexdigest(), + 'rms': rms(v), 'peak': float(abs(v).max())} + (args.output_dir/'measurements.json').write_text(json.dumps(report, indent=2)+'\n') + print(json.dumps(report, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/tests/test_texture_inference.py b/tests/test_texture_inference.py new file mode 100644 index 0000000..7c737ec --- /dev/null +++ b/tests/test_texture_inference.py @@ -0,0 +1,39 @@ +"""CLI integration without checkpoint download or real model inference.""" +import tempfile +from pathlib import Path +import unittest +from unittest import mock +import numpy as np +import soundfile as sf +import torch +import inference + + +class IntegrationTests(unittest.TestCase): + def test_optional_postprocessing_and_default_bypass(self): + t = np.arange(4410)/44100 + x = np.array([.4*np.sin(2*np.pi*220*t) + .02*(.1+(np.mod(t,.08)<.008))*np.sin(2*np.pi*4000*t)], dtype='float32') + from texture_softening import soften_texture + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + source, dest, checkpoint = root/'in.wav', root/'out.wav', root/'model.pt' + checkpoint.touch() + sf.write(source, x.T, 44100, subtype='FLOAT') + with mock.patch.object(inference.look2hear.models.BaseModel, 'from_pretrain', return_value=torch.nn.Identity()): + with mock.patch('texture_softening.soften_texture', wraps=soften_texture) as process: + inference.run_inference(source, dest, checkpoint, 'cpu') + process.assert_not_called() + y, _ = sf.read(dest, dtype='float32', always_2d=True) + np.testing.assert_array_equal(y.T, x) + inference.run_inference(source, dest, checkpoint, 'cpu', texture_reduction_db=2) + process.assert_called_once() + y, _ = sf.read(dest, dtype='float32', always_2d=True) + expected, _ = soften_texture(x) + np.testing.assert_array_equal(y.T, expected) + original, _ = sf.read(source, dtype='float32', always_2d=True) + np.testing.assert_array_equal(original.T, x) + + def test_invalid_cap_rejected_before_model_load(self): + for cap in (-1, 4, np.nan, np.inf): + with self.assertRaises(ValueError): + inference.run_inference('unused.wav', 'out.wav', 'unused.pt', texture_reduction_db=cap) diff --git a/tests/test_texture_softening.py b/tests/test_texture_softening.py new file mode 100644 index 0000000..8740c29 --- /dev/null +++ b/tests/test_texture_softening.py @@ -0,0 +1,45 @@ +import unittest +import numpy as np +from texture_softening import soften_texture + + +class TextureTests(unittest.TestCase): + def test_exact_bypass(self): + x = np.random.default_rng(4).normal(0, .1, (2, 16000)).astype('float32') + y, _ = soften_texture(x, max_reduction_db=0) + np.testing.assert_array_equal(x, y) + + def test_steady_tone_stereo_and_length(self): + a = .2*np.sin(2*np.pi*3000*np.arange(44101)/44100) + x = np.array([a, -.3*a], dtype='float32') + y, _ = soften_texture(x) + self.assertEqual(y.shape, x.shape) + np.testing.assert_allclose(y[1], -.3*y[0], atol=1e-7) + self.assertLess(np.sqrt(np.mean((x[:,2048:-2048]-y[:,2048:-2048])**2)), 1e-5) + + def test_silence(self): + y, _ = soften_texture(np.zeros((2, 4410), dtype='float32')) + self.assertEqual(float(abs(y).max()), 0) + + def test_actual_modulation_is_bounded(self): + t = np.arange(44100)/44100 + envelope = .02*(.1 + (np.mod(t, .08) < .008)) + x = np.array([.4*np.sin(2*np.pi*220*t) + envelope*np.sin(2*np.pi*4000*t)], dtype='float32') + y, stats = soften_texture(x) + self.assertGreater(stats['max_reduction_db'], .01) + self.assertLessEqual(stats['max_reduction_db'], 2) + self.assertTrue(np.isfinite(y).all()) + self.assertGreater(np.sqrt(np.mean((x-y)**2)), 1e-5) + + def test_invalid(self): + for x in (np.zeros(4410), np.zeros((0,4410)), np.zeros((2,100)), + np.full((2,4410), np.nan), np.zeros((2,4410), dtype='complex64')): + with self.assertRaises(ValueError): + soften_texture(x) + for cap in (-1, 4, np.nan, np.inf): + with self.assertRaises(ValueError): + soften_texture(np.zeros((1,4410)), max_reduction_db=cap) + + +if __name__ == '__main__': + unittest.main() diff --git a/texture_softening.py b/texture_softening.py new file mode 100644 index 0000000..8f5f024 --- /dev/null +++ b/texture_softening.py @@ -0,0 +1,43 @@ +# Adapted from Neuracoust Sound Build-up experimental post-processing. +# Changes: explicit zero-strength bypass and input validation. +# Distributed under the repository's CC BY-SA 4.0 license. +"""Offline candidate: bounded temporal spectral excursion reduction, no dry input. +Not a defect classifier. Musical fluctuations may also trigger the detector. +""" +import numpy as np +from scipy.signal import stft, istft +from scipy.ndimage import median_filter, gaussian_filter1d, maximum_filter1d + + +def soften_texture(audio, sample_rate=44100, max_reduction_db=2.0): + x = np.asarray(audio) + if (x.ndim != 2 or x.shape[0] < 1 or x.shape[-1] < 2048 + or not np.issubdtype(x.dtype, np.floating) or not np.isfinite(x).all()): + raise ValueError('Expected finite channels-by-frames audio, at least 2048 frames') + if not np.isfinite(sample_rate) or sample_rate < 16000 or not 0 <= max_reduction_db <= 3: + raise ValueError('Unsupported rate or attenuation') + if max_reduction_db == 0: + return x.copy(), {'max_reduction_db': 0.0, + 'power_weighted_reduction_db': 0.0, + 'attack_frame_fraction': 0.0} + kw = dict(fs=sample_rate, nperseg=1024, noverlap=768) + f, _, z = stft(np.pad(x, ((0,0),(1024,1024)), mode='reflect'), **kw) + power = np.mean(abs(z)**2, axis=0) + level = 10*np.log10(np.maximum(power, 1e-16)) + # About 40 ms neighborhood: only positive, short-lived excursions. + frames = max(3, int(round(.04*sample_rate/256)) | 1) + baseline = median_filter(level, size=(1,frames), mode='nearest') + excess = np.maximum(level-baseline-3, 0) + # Broad attack gate protects simultaneous rising energy (not guaranteed perfect). + energy = power.sum(axis=0) + attack = np.r_[False, energy[1:] > 1.4*np.maximum(energy[:-1],1e-16)] + gate = 1-maximum_filter1d(attack.astype(float), size=5) + band = np.clip((f-1200)/800,0,1)*np.clip((14000-f)/4000,0,1) + active = power > max(float(power.max())*1e-7,1e-15) + reduction = np.minimum(excess*.35,max_reduction_db)*band[:,None]*gate*active + reduction = gaussian_filter1d(gaussian_filter1d(reduction,.7,axis=0),.8,axis=1) + _, y = istft(z*10**(-reduction[None,:,:]/20), **kw) + y = y[:,1024:1024+x.shape[-1]].astype(np.float32) + return y, {'max_reduction_db':float(reduction.max()), + 'power_weighted_reduction_db':float((reduction*power).sum()/max(power.sum(),1e-30)), + 'attack_frame_fraction':float(np.mean(gate==0))} From de57e73cdd44f1e92a9641876aa5a9fc7b47e8c6 Mon Sep 17 00:00:00 2001 From: hemanpapa-coder Date: Fri, 16 Oct 2026 23:29:12 +0900 Subject: [PATCH 2/6] Validate optional texture requirements before checkpoint loading --- .github/workflows/cpu-unit-tests.yml | 3 ++- TEXTURE_SOFTENING.md | 6 ++++- inference.py | 25 ++++++++++++++++--- tests/test_texture_inference.py | 37 ++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cpu-unit-tests.yml b/.github/workflows/cpu-unit-tests.yml index 449af90..5a62316 100644 --- a/.github/workflows/cpu-unit-tests.yml +++ b/.github/workflows/cpu-unit-tests.yml @@ -2,8 +2,9 @@ name: CPU unit tests on: pull_request: + workflow_dispatch: push: - branches: [main] + branches: [main, experimental/optional-texture-softening] jobs: unit-tests: diff --git a/TEXTURE_SOFTENING.md b/TEXTURE_SOFTENING.md index 0cc5f21..8bdf797 100644 --- a/TEXTURE_SOFTENING.md +++ b/TEXTURE_SOFTENING.md @@ -8,7 +8,11 @@ applies. This does not modify model weights or claim a new restoration model. The default is 0: the existing inference path and output remain unchanged. The optional CPU NumPy/SciPy post-process runs once after full-file or chunked -inference. It needs at least 2048 output frames. It adds no model forward passes, +inference. Optional SciPy is imported before audio/model loading; missing SciPy +raises an actionable error before checkpoint download. Inputs shorter than +2048 frames are rejected before checkpoint download/model loading only when +the option is enabled. The disabled path retains short-clip behavior. It needs +at least 2048 output frames. It adds no model forward passes, but holds a full-output STFT in memory, so chunked model inference no longer implies bounded post-processing memory. SciPy is required only when enabled. diff --git a/inference.py b/inference.py index c0fa93a..936d2d2 100644 --- a/inference.py +++ b/inference.py @@ -1,4 +1,5 @@ import argparse +import importlib from pathlib import Path from huggingface_hub import hf_hub_download @@ -221,6 +222,20 @@ def run_model( return output_sum / weight_sum +def load_texture_processor(): + """Check optional dependencies before checkpoint download/model execution.""" + try: + importlib.import_module("scipy") + return importlib.import_module("texture_softening").soften_texture + except ModuleNotFoundError as exc: + if exc.name == "scipy" or (exc.name or "").startswith("scipy."): + raise RuntimeError( + "Texture softening requires SciPy. Install scipy>=1.13,<2 " + "or use --texture-reduction-db=0. No model was loaded." + ) from exc + raise + + def run_inference( input_wav, output_wav, @@ -245,8 +260,14 @@ def run_inference( chunk_seconds, overlap_seconds, chunk_pad_seconds ) chunk_batch_size = validate_chunk_batch_size(chunk_batch_size) + texture_processor = load_texture_processor() if texture_reduction_db else None device = select_device(requested_device) test_data, sample_rate = load_audio(input_path) + if texture_processor is not None and test_data.shape[-1] < 2048: + raise ValueError( + "Texture softening requires at least 2048 frames; " + "use --texture-reduction-db=0 for shorter clips. No model was loaded." + ) checkpoint_path = resolve_checkpoint(checkpoint) model = look2hear.models.BaseModel.from_pretrain( @@ -267,9 +288,7 @@ def run_inference( chunk_pad_samples=chunk_pad_samples, ) if texture_reduction_db: - from texture_softening import soften_texture - - softened, _ = soften_texture( + softened, _ = texture_processor( output.squeeze(0).numpy(), sample_rate, texture_reduction_db ) output = torch.from_numpy(softened).unsqueeze(0) diff --git a/tests/test_texture_inference.py b/tests/test_texture_inference.py index 7c737ec..c39307c 100644 --- a/tests/test_texture_inference.py +++ b/tests/test_texture_inference.py @@ -37,3 +37,40 @@ def test_invalid_cap_rejected_before_model_load(self): for cap in (-1, 4, np.nan, np.inf): with self.assertRaises(ValueError): inference.run_inference('unused.wav', 'out.wav', 'unused.pt', texture_reduction_db=cap) + + def test_missing_scipy_fails_before_audio_checkpoint_or_model(self): + missing = ModuleNotFoundError("No module named scipy", name="scipy") + with mock.patch.object(inference.importlib, 'import_module', side_effect=missing), \ + mock.patch.object(inference, 'load_audio') as audio, \ + mock.patch.object(inference, 'resolve_checkpoint') as checkpoint, \ + mock.patch.object(inference.look2hear.models.BaseModel, 'from_pretrain') as model: + with self.assertRaisesRegex(RuntimeError, 'requires SciPy'): + inference.run_inference('in.wav', 'out.wav', 'model.pt', 'cpu', texture_reduction_db=2) + audio.assert_not_called() + checkpoint.assert_not_called() + model.assert_not_called() + + def test_short_clip_rejected_before_checkpoint_but_disabled_still_works(self): + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + source, dest = root/'short.wav', root/'out.wav' + x = np.zeros((1024, 1), dtype='float32') + sf.write(source, x, 44100, subtype='FLOAT') + with mock.patch.object(inference, 'resolve_checkpoint') as checkpoint, \ + mock.patch.object(inference.look2hear.models.BaseModel, 'from_pretrain', return_value=torch.nn.Identity()) as model: + with self.assertRaisesRegex(ValueError, 'at least 2048'): + inference.run_inference(source, dest, 'model.pt', 'cpu', texture_reduction_db=2) + checkpoint.assert_not_called() + model.assert_not_called() + self.assertFalse(dest.exists()) + with mock.patch.object(inference, 'load_texture_processor') as optional: + inference.run_inference(source, dest, 'model.pt', 'cpu') + optional.assert_not_called() + y, _ = sf.read(dest, dtype='float32', always_2d=True) + np.testing.assert_array_equal(y, x) + + def test_unrelated_import_errors_are_not_misreported(self): + missing = ModuleNotFoundError('missing other', name='other') + with mock.patch.object(inference.importlib, 'import_module', side_effect=missing): + with self.assertRaises(ModuleNotFoundError): + inference.load_texture_processor() From 9a4cd0875af86d5cc8b8334f44f1f2a8a2e3e16d Mon Sep 17 00:00:00 2001 From: hemanpapa-coder Date: Fri, 16 Oct 2026 23:33:13 +0900 Subject: [PATCH 3/6] Add reproducible upstream music comparisons and song-length memory probe --- tests/build_texture_music_comparison.py | 125 ++++++++++++++++++++++++ tests/measure_texture_memory.py | 50 ++++++++++ 2 files changed, 175 insertions(+) create mode 100644 tests/build_texture_music_comparison.py create mode 100644 tests/measure_texture_memory.py diff --git a/tests/build_texture_music_comparison.py b/tests/build_texture_music_comparison.py new file mode 100644 index 0000000..cba92ee --- /dev/null +++ b/tests/build_texture_music_comparison.py @@ -0,0 +1,125 @@ +"""Build eight aligned real-music comparisons with a fixed upstream Apollo. +Run from repo root after fetching the exact sources recorded in sources.json. +This is objective/AB preparation, not an automated perceptual quality verdict. +""" +import argparse +import hashlib +import importlib.util +import json +import platform +import random +import subprocess +import sys +import time +from pathlib import Path + +import numpy as np +import soundfile as sf +import scipy +from scipy.signal import resample_poly, stft +import torch +import pyloudnorm as pyln +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from texture_softening import soften_texture + +BASE = 'e84bcacc59d5455f05d86a5c97dd4aeb3c14dbb6' +SR = 44100 + +def digest(p): + return hashlib.sha256(Path(p).read_bytes()).hexdigest() + + +def main(): + p = argparse.ArgumentParser(__doc__) + p.add_argument('--sources-dir', type=Path, required=True) + p.add_argument('--checkpoint', type=Path, required=True) + p.add_argument('--output-dir', type=Path, required=True) + p.add_argument('--device', choices=['mps','cpu','cuda'], default='mps') + a = p.parse_args() + a.output_dir.mkdir(parents=True, exist_ok=True) + upstream = a.output_dir/'upstream_inference.py' + upstream.write_bytes(subprocess.check_output(['git','show',BASE+':inference.py'])) + spec = importlib.util.spec_from_file_location('apollo_upstream',upstream) + api = importlib.util.module_from_spec(spec);spec.loader.exec_module(api) + torch.set_num_threads(4) + device = api.select_device(a.device) + model = api.look2hear.models.BaseModel.from_pretrain(str(a.checkpoint),sr=SR,win=20,feature_dim=256,layer=6).to(device).eval() + sources = json.loads((a.sources_dir/'sources.json').read_text()) + for row in sources: + row['sha256'] = digest(a.sources_dir/row['file']) + lookup = {r['id']:r for r in sources} + cases = [('vocal', 'voice',30,False,'female vocal/sibilants; review 2–12 s'), + ('vocal-dense','voice',70,False,'vocal with dense pop mixture; review 0–15 s'), + ('drums','vibe',20,False,'kit/cymbals in jazz mixture; review 0–15 s'), + ('dense','voice',110,False,'dense synth/guitar/drum mixture; review 0–15 s'), + ('piano','piano',15,False,'piano transients/sustains; review 0–15 s'), + ('quiet','piano',65,False,'quiet piano candidate; review decays 0–15 s'), + ('control-piano','piano',40,True,'no added codec degradation negative control; review wanted detail'), + ('control-celesta','celesta',10,True,'no added codec degradation negative control; review attacks')] + report = dict(upstream_commit=BASE,upstream_inference_sha256=digest(upstream), + checkpoint_sha256=digest(a.checkpoint),checkpoint_revision='c68bd80fdd9c0d93d2f4a833cb154624f660a561', + device=str(device),torch=torch.__version__,numpy=np.__version__,scipy=scipy.__version__, + soundfile=sf.__version__,libsndfile=sf.__libsndfile_version__,platform=platform.platform(), + chunk_seconds=6,overlap_seconds=1,chunk_pad_seconds=1,chunk_batch_size=1, + sources=sources,cases=[],notes='Sources are already lossy public recordings; source-proxy is NOT a lossless clean reference. No listener verdict. Controls are no-added-degradation, not certified clean recordings.') + meter = pyln.Meter(SR) + rng = random.Random(441044) + for cid,sid,start,control,focus in cases: + out = a.output_dir/cid;out.mkdir(exist_ok=True) + raw = out/'raw';raw.mkdir(exist_ok=True) + audition = out/'blind';audition.mkdir(exist_ok=True) + source, rate = sf.read(a.sources_dir/lookup[sid]['file'],dtype='float32',always_2d=True) + assert rate==SR and source.shape[1]==2 + context = source[(start-2)*SR:(start+17)*SR] + assert context.shape==(19*SR,2) + sf.write(out/'context-source.wav',context,SR,subtype='FLOAT') + if control: degraded = context.copy() + else: + # libsndfile/LAME constant-bitrate encoding, fixed compression setting. + sf.write(out/'context-degraded.mp3',context,SR,format='MP3',bitrate_mode='CONSTANT',compression_level=.8) + degraded, rate = sf.read(out/'context-degraded.mp3',dtype='float32',always_2d=True) + assert degraded.shape==context.shape and rate==SR + sf.write(out/'context-input.wav',degraded,SR,subtype='FLOAT') + x = torch.from_numpy(np.ascontiguousarray(degraded.T)).unsqueeze(0) + then = time.perf_counter() + with torch.inference_mode(): + prediction = api.run_model(model,x,device,chunk_samples=6*SR,overlap_samples=SR,chunk_pad_samples=SR,chunk_batch_size=1).squeeze(0).numpy() + elapsed = time.perf_counter()-then + sf.write(out/'context-apollo.wav',prediction.T,SR,subtype='FLOAT') + variants = {'source-proxy':context[2*SR:17*SR].T,'degraded':degraded[2*SR:17*SR].T, + 'apollo-0db':prediction[:,2*SR:17*SR]} + stats = {} + for cap in (1,2,3): + then=time.perf_counter();soft,st=soften_texture(prediction,max_reduction_db=cap) + stats[str(cap)]={**st,'seconds':time.perf_counter()-then} + variants[f'apollo-{cap}db']=soft[:,2*SR:17*SR] + metrics={} + for name,v in variants.items(): + sf.write(raw/(name+'.wav'),v.T,SR,subtype='FLOAT') + ref=variants['source-proxy'].astype(np.float64);z=v.astype(np.float64) + gain=float((ref*z).sum()/max((z*z).sum(),1e-30)) + snr=10*np.log10(max((ref*ref).sum(),1e-30)/max(((ref-gain*z)**2).sum(),1e-30)) + _,_,rz=stft(ref,fs=SR,nperseg=2048,noverlap=1024,boundary=None,padded=False) + _,_,vz=stft(z*gain,fs=SR,nperseg=2048,noverlap=1024,boundary=None,padded=False) + spectral=float(np.sqrt(np.mean((20*np.log10(np.maximum(abs(rz),1e-5))-20*np.log10(np.maximum(abs(vz),1e-5)))**2))) + metrics[name]={'proxy_snr_db':float(snr),'proxy_log_spectrum_rmse_db':spectral,'integrated_lufs':float(meter.integrated_loudness(v.T))} + matched={name:v*10**((-23-metrics[name]['integrated_lufs'])/20) for name,v in variants.items()} + truepeak=max(float(abs(resample_poly(v,4,1,axis=-1)).max()) for v in matched.values()) + common=min(1.,10**(-1/20)/truepeak) + names=list(matched);rng.shuffle(names);key={} + for i,name in enumerate(names): + letter=chr(65+i);v=matched[name]*common + sf.write(audition/(letter+'.wav'),v.T,SR,subtype='FLOAT') + key[letter]=name + row=dict(id=cid,source=sid,start_seconds=start,duration=15,no_added_degradation=control, + focus=focus,model_seconds=elapsed,postprocessing=stats,metrics=metrics, + blind_key=key,blind_target_lufs=-23+20*np.log10(common),common_gain_db=20*np.log10(common)) + report['cases'].append(row) + (a.output_dir/'measurements.json').write_text(json.dumps(report,indent=2)+'\n') + print(cid,'model seconds',round(elapsed,2),flush=True) + hashes={str(f.relative_to(a.output_dir)):digest(f) for f in a.output_dir.rglob('*.wav')} + (a.output_dir/'SHA256.json').write_text(json.dumps(hashes,indent=2)+'\n') + (a.output_dir/'sources.json').write_text(json.dumps(sources,indent=2)+'\n') + + +if __name__=='__main__': main() diff --git a/tests/measure_texture_memory.py b/tests/measure_texture_memory.py new file mode 100644 index 0000000..3940423 --- /dev/null +++ b/tests/measure_texture_memory.py @@ -0,0 +1,50 @@ +"""Measure post-process allocations and process high-water RSS in a fresh process. +RSS includes interpreter/dependencies and input; tracemalloc starts after input. +No model is loaded. Supply a song WAV with --input or a deterministic probe. +""" +import argparse +import json +import platform +import resource +import sys +import time +import tracemalloc +from pathlib import Path +import numpy as np +import scipy +import soundfile as sf +sys.path.insert(0, str(Path(__file__).resolve().parents[1])) +from texture_softening import soften_texture + +p = argparse.ArgumentParser(__doc__) +p.add_argument('--seconds', type=float, required=True) +p.add_argument('--input', type=Path) +p.add_argument('--output', type=Path, required=True) +a = p.parse_args() +sr = 44100 +n = int(a.seconds*sr) +if a.input: + source, sr = sf.read(a.input, dtype='float32', always_2d=True) + if sr != 44100 or source.shape[1] != 2: + raise ValueError('Expected 44.1 kHz stereo source') + x = np.tile(source.T, (1, (n+len(source)-1)//len(source)))[:, :n].copy() +else: + t = np.arange(n)/sr + x = np.array([.2*np.sin(2*np.pi*220*t), .15*np.sin(2*np.pi*331*t)], dtype='float32') + del t +unit = 1 if sys.platform == 'darwin' else 1024 +before = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss*unit +tracemalloc.start() +began = time.perf_counter() +y, stats = soften_texture(x) +seconds = time.perf_counter()-began +current, peak = tracemalloc.get_traced_memory() +tracemalloc.stop() +report = dict(duration=a.seconds, frames=n, channels=2, sample_rate=sr, + numpy=np.__version__, scipy=scipy.__version__, platform=platform.platform(), + input_file=a.input.name if a.input else None, input_repeated=bool(a.input and n>len(source)), + seconds=seconds, traced_peak_bytes=peak, process_peak_rss_bytes=resource.getrusage(resource.RUSAGE_SELF).ru_maxrss*unit, + process_peak_before_bytes=before, statistics=stats, finite=bool(np.isfinite(y).all())) +a.output.parent.mkdir(parents=True, exist_ok=True) +a.output.write_text(json.dumps(report, indent=2)+'\n') +print(json.dumps(report), flush=True) From 4a895246b7f634a09fd7dd0b141d1725f4036acb Mon Sep 17 00:00:00 2001 From: hemanpapa-coder Date: Fri, 16 Oct 2026 23:47:34 +0900 Subject: [PATCH 4/6] Document eight real Apollo comparisons, negative controls, memory costs and CI evidence --- TEXTURE_SOFTENING.md | 7 + evaluation/texture-20261016/REPORT.md | 77 ++ evaluation/texture-20261016/SHA256.json | 122 +++ .../texture-20261016/audition-checks.json | 42 ++ evaluation/texture-20261016/fork-ci.json | 7 + evaluation/texture-20261016/measurements.json | 692 ++++++++++++++++++ .../texture-20261016/memory-apollo-180.json | 21 + .../texture-20261016/memory-music-30.json | 21 + evaluation/texture-20261016/sources.json | 46 ++ .../texture-20261016/system-unit-tests.log | 41 ++ tests/build_texture_music_comparison.py | 17 +- 11 files changed, 1090 insertions(+), 3 deletions(-) create mode 100644 evaluation/texture-20261016/REPORT.md create mode 100644 evaluation/texture-20261016/SHA256.json create mode 100644 evaluation/texture-20261016/audition-checks.json create mode 100644 evaluation/texture-20261016/fork-ci.json create mode 100644 evaluation/texture-20261016/measurements.json create mode 100644 evaluation/texture-20261016/memory-apollo-180.json create mode 100644 evaluation/texture-20261016/memory-music-30.json create mode 100644 evaluation/texture-20261016/sources.json create mode 100644 evaluation/texture-20261016/system-unit-tests.log diff --git a/TEXTURE_SOFTENING.md b/TEXTURE_SOFTENING.md index 8bdf797..fffa303 100644 --- a/TEXTURE_SOFTENING.md +++ b/TEXTURE_SOFTENING.md @@ -52,3 +52,10 @@ Local synthetic benchmark (macOS 26.5.1 arm64, Python 3.9.6, NumPy 2.0.2, SciPy 1.13.1): 4 s stereo took 0.0301 / 0.0250 / 0.0250 s for the post-process alone; max STFT gain reduction 1.9105 dB. Timing is hardware and fixture dependent. Equal-RMS audition copies matched RMS within 0.000001 dB. + +## PR44 follow-up + +Early dependency/short-input checks, pinned CPU CI, real-music comparison +methodology and song-length memory evidence are documented in +[evaluation/texture-20261016/REPORT.md](evaluation/texture-20261016/REPORT.md). +The feature remains opt-in and the PR remains Draft pending listening. diff --git a/evaluation/texture-20261016/REPORT.md b/evaluation/texture-20261016/REPORT.md new file mode 100644 index 0000000..ff6e428 --- /dev/null +++ b/evaluation/texture-20261016/REPORT.md @@ -0,0 +1,77 @@ +# PR44 follow-up validation — 2026-10-16 + +## Early validation fixes + +SciPy is imported before audio/model loading only when texture softening is +enabled. A missing dependency produces an actionable RuntimeError before +checkpoint resolution, download or forward inference. Inputs shorter than +2048 frames are rejected before checkpoint resolution/model loading when +enabled. The disabled path retains short-clip behavior and does not import the +optional processor. New tests assert these boundaries and source-file retention. +Local tests and pinned Linux CPU CI each passed all 33 tests. + +Successful CI: https://github.com/hemanpapa-coder/Apollo/actions/runs/38916720169 +Commit tested: 9a4cd0875af86d5cc8b8334f44f1f2a8a2e3e16d +The upstream PR run is action_required (maintainer approval), not a completed +upstream CI pass. The fork uses the same workflow and contribution commit. Its log confirms +33 tests passed in 0.095 s under the pinned dependencies. A validation-only +companion PR is https://github.com/hemanpapa-coder/Apollo/pull/1; no merge is +requested there. + +## Post-process memory + +Fresh-process measurements, model excluded. Tracemalloc starts after input +allocation; process high-water RSS includes Python, imports, input and output. +These are different quantities and must not be compared as if identical. + +| Input | Traced peak | Process peak RSS | DSP time | +|---|---:|---:|---:| +| 30 s real music | 388.74 MiB | 648.77 MiB | 0.344 s | +| 180 s repeated Apollo prediction | 2328.58 MiB | 3042.25 MiB | 2.501 s | + +The 180 s input repeats a 19 s real Apollo prediction; this is a song-length +allocation probe, not a distinct three-minute listening test. Figures are +host/version dependent. Detailed JSON records NumPy/SciPy versions and process +baseline. The full-output STFT remains O(duration), even with chunked model +inference. Multi-minute material can require several GiB in addition to the +model; there is no bounded-memory post-process implementation in this PR. + +Reproduce in separate processes: + +```sh +python tests/measure_texture_memory.py --seconds 30 --input /path/to/music.wav --output /path/to/memory-30.json +python tests/measure_texture_memory.py --seconds 180 --input /path/to/context-apollo.wav --output /path/to/memory-180.json +``` + +## Real music comparison + +Eight 15-second cases from four publicly shareable CC BY recordings. The +comparison includes raw WAVs and six randomized, integrated-loudness-matched +conditions per excerpt; all inference settings and the checkpoint are fixed. +Sources are already lossy public files, so the decoded source proxy is not a +lossless clean master. Metrics against it cannot establish perceptual quality. +Listening validation and maintainer evaluation remain outstanding; keep Draft. + +The quiet case is a deliberate low-level stress test: -18 dB is applied to +source context before encoding and inference (about -33.50 LUFS). All other +cases use unity input gain. Two cases add no further codec degradation; these +are negative controls, not certified artifact-free recordings. + +At 3 dB, the no-added-degradation piano control has a proxy waveform SNR delta +of -0.02348 dB and spectrum RMSE delta of +0.00145 dB. The celesta control has +-0.02973 dB / +0.00846 dB respectively. These are objective adverse/caution +examples, not evidence of audible harm. The other piano case also has a very +small SNR regression. All cases are retained and none is labeled a perceptual +success. A controlled listener must judge roughness versus lost wanted detail. + +The raw Apollo path is taken directly from the fixed upstream inference.py; +there is no app-specific polarity ensemble, gain fusion, dry/wet blend or +phase stabilization. Only this PR's optional texture processor varies. + +Public review bundle: https://github.com/hemanpapa-coder/Apollo/releases/tag/texture-evaluation-20261016 + +Bundle includes 96 raw/blind excerpt WAVs plus context and metadata. Source +credits and source hashes are in sources.json. Codec alignment peak lag is +recorded per excerpt; all are zero samples. Per-case blind loudness spread and +4x-oversampled peaks are in audition-checks.json. These checks do not replace +physical listening. diff --git a/evaluation/texture-20261016/SHA256.json b/evaluation/texture-20261016/SHA256.json new file mode 100644 index 0000000..5908635 --- /dev/null +++ b/evaluation/texture-20261016/SHA256.json @@ -0,0 +1,122 @@ +{ + "control-celesta/context-apollo.wav": "9ebe14061d67a71d87cf5f94a9d0cfdbcd31ad1d5e4e7dfb39dff618278984c6", + "control-celesta/context-input.wav": "3c080aabea76a53722f3161be69cb4ad0b83bdf095d073791e10ca60f72e6c43", + "control-celesta/context-source.wav": "c0cdf66b4ddb210b8600e220f36ab1f7977b8b8dbe1c59b0500b793903aacc8a", + "control-celesta/blind/A.wav": "93586bbf9a5837a96b3f1d8abd681834156e574e93464c84033d0fc31fb3a23e", + "control-celesta/blind/B.wav": "d4b424242e80490cabd93d722f776d8c83c25dc4984336fc15564475cacbc8e2", + "control-celesta/blind/C.wav": "4179da34b723097a80bef3a95093e94cb8a47b40c6155e96b369f87050abc545", + "control-celesta/blind/D.wav": "420b0917bd63d3251af9ee2c4c0410e8824efa0b240bcde137c89d624a9e6a8b", + "control-celesta/blind/E.wav": "480806e7fe79bcf3f02ceab6f5bd2baf6c9b4a9140ef025eb3e663976a0bab09", + "control-celesta/blind/F.wav": "21d09a584e9c84ae1c390c92f024c0f7fbd10d7cd0bd92e7234aefac0e071042", + "control-celesta/raw/apollo-0db.wav": "590be90be5260f132cc26640bb7c33924ffe8a1a8015dd7eb71b573c1ff5837a", + "control-celesta/raw/apollo-1db.wav": "031349ce386f161efa339a896d42d6808164fdd016ed378feba47cea33bb7b9f", + "control-celesta/raw/apollo-2db.wav": "959253e578139ca30110f3d658ab1b1d2af1aedc6225ac22d968b350254ee9b1", + "control-celesta/raw/apollo-3db.wav": "fe256b0143e522d0343bc8a27f7a89f913186f745ac9342ec3bb0e8b81e9b540", + "control-celesta/raw/degraded.wav": "108f37860767da7c92e551108fd529af21a8ee37a359f1164626087e9a7e2688", + "control-celesta/raw/source-proxy.wav": "4702b2871d82be2e3f3af912b7bb171d7829a4e936250d306d6c078d985b1570", + "control-piano/context-apollo.wav": "6f63c26e3e9956afb306b663c3a3145fdd41ac236c632a3c95b1acdcda54e57c", + "control-piano/context-input.wav": "24e023036296d8085ae618bc5bdff32b5d9cc6dcc654e410753220b0238cfc14", + "control-piano/context-source.wav": "61098d36a525ceffb0d7ec0ff7bb052728f468db22993250e8d377531026e064", + "control-piano/blind/A.wav": "6ec6cab695f5bd9ed4360073ea0973acf50088cdd658c5fd477fa4c188abe587", + "control-piano/blind/B.wav": "0e6e580105482021457657a8ad1a60bebab06deb2af0a3f8e185417335ee0f74", + "control-piano/blind/C.wav": "f50e5067ece214434ca0e355f8fb21546ae720f12b576f10f3fb7b369e021818", + "control-piano/blind/D.wav": "c28bd4388ab0aac394b6b6c941265cf85e0276e1f35811891b94a57a4274186b", + "control-piano/blind/E.wav": "dc5c9985bd12bb9789f2ea7fab6b8572f76af6040f959c0b500eadb0a249d110", + "control-piano/blind/F.wav": "f46222522a3f872f64118e5fe80dc32bea035fef5f1744d91623dc66b56d47ca", + "control-piano/raw/apollo-0db.wav": "167932b8e3c36eccc8a6df27e8c405e6e273c2701df7e78cf2092529bd260c11", + "control-piano/raw/apollo-1db.wav": "0ee97569a322a4629fafe5cd1374d2f1554aaa1bef8bd13d9218283c219ad9d5", + "control-piano/raw/apollo-2db.wav": "0c26a66af37692fe5edc1eb0185c1e125266fec4f990544cdffa15e03d830ab2", + "control-piano/raw/apollo-3db.wav": "c6be3fdae12d36fe85b9a452bf6e583f11388231a7421200eacb69f299155d3f", + "control-piano/raw/degraded.wav": "8c83ce459bad335a0061e4bdf3c1952d00c72dcd3b7f428ef8e7e8f8271e71e4", + "control-piano/raw/source-proxy.wav": "aac6ddf344c64ab58341674647e3ae031c139de4c909b28c6faa472633e0de87", + "dense/context-apollo.wav": "291e42e70d3a8ca52eb7339a99ba699f97df18e345d1a4e359b5b17d8bac58f6", + "dense/context-input.wav": "dc6c737cf820f1b594dcf2d5140b75160c1d877d233c8deeb1e1fed98a7e1020", + "dense/context-source.wav": "620d24c6cfb5e6746b6cba161996805730a333c31470f647c23e15d6ce000cc9", + "dense/blind/A.wav": "c1b2d528452a3e99fd2335cd3eea23d88c3c68612fa30c0f963c5f651a660b23", + "dense/blind/B.wav": "d5c67acf110d2356cb415f4669b12a8ec9cb3cd09fb33bef72fa386ff9db493a", + "dense/blind/C.wav": "a2e7d6e58c21e7854d8c1f74554d7e60f10b8c4286ed16680fd8c3ff604591c6", + "dense/blind/D.wav": "4e385d827da2cb04b208254232f6c9c1d6072ae648f180b460283c233c74f57e", + "dense/blind/E.wav": "722322b287d57684bfa5e8f7c3230f6a79e755be63bf5d49c316103b7c72760d", + "dense/blind/F.wav": "3806f40fc0577ff09b7a20c62d2f9d820ea852c79d49dcd87f0722464191f01e", + "dense/raw/apollo-0db.wav": "1b74fcfc74d2e30edadda7560de3e81e28cd67f689a1fa7361d27ebea6ebd102", + "dense/raw/apollo-1db.wav": "5653370f45951da21defe728bc08f4da6feb86976495add3218e6d37110efa8f", + "dense/raw/apollo-2db.wav": "03dd3766ad2d187e7591445be8b11b8f0ba284426cbdd99dde47e591616381a6", + "dense/raw/apollo-3db.wav": "cb4eeefb4d4c80c8c6d97d66642c42769905d84651cad7e0b749fa039ef07612", + "dense/raw/degraded.wav": "bd01491ff46b269e9c86aeedaba364eafaa17b896f5081d03453bcb191902d8a", + "dense/raw/source-proxy.wav": "b63b162d8dfce363f032e18e1735d34146a3bed5817f0a3673965c69f8a19d46", + "drums/context-apollo.wav": "2f2f9fd7392382268ecd909849ae80e235e26e7b782b9f7ab0ff6aeead2b5aad", + "drums/context-input.wav": "4725e8f5d02da6c7fd236725786f1477c4e6992409c4fa0b01763f96d5886ad3", + "drums/context-source.wav": "7524dc5c6807458cafd0f3897a3e7e02d5cc6a764eda410f79c4f4339ea389d8", + "drums/blind/A.wav": "791090336eb920843364fad47d27098f5b34a7f09fb31a5c99ead1ea23f65bea", + "drums/blind/B.wav": "a5ca4c541d6bcaf913b4e6df6e4cdb8e3f9096e7733da17586907b46be873f45", + "drums/blind/C.wav": "88e4cfd227f8e2405ac323222713360d6b0335fd6cf71a89a49b9dcdb9c8c2db", + "drums/blind/D.wav": "7c5767968c5ebc4a4604b3697f61391617dfad3976a7e359a12ec55bde7e7c0b", + "drums/blind/E.wav": "13a0fd6101291907881809eb3ef2851d5b627709a231682539c122934d760904", + "drums/blind/F.wav": "c4d9ee8dec348b9cd493d8837230c863325278e34b57be71ae15a03df6f47562", + "drums/raw/apollo-0db.wav": "6a15bdbf89838e2467b0d89276ebfbf6f5cf3a512fa9e101fe480addf825519d", + "drums/raw/apollo-1db.wav": "9c66d166834eab9e81d8aae31cdae4309b9421481073abf6799c9bb46c7851c3", + "drums/raw/apollo-2db.wav": "dd547346e1554fd1d3dd4ff09d78bd407090eebaefdfb7208c07b9deae83892c", + "drums/raw/apollo-3db.wav": "ff9a5babaa3acaded9a72aa4ee8198a0410182c30512f38535c0d1ce67d520a3", + "drums/raw/degraded.wav": "83eaced3891aa2eb35df9b66c81d33a99d374c4cf085b178dad56b350b7e021e", + "drums/raw/source-proxy.wav": "3482373e6750ce26f3ab45c591a0791f53741860e2519876118acc37477cd6c6", + "piano/context-apollo.wav": "db33132a2c310be7d0a536049727b1c3a7eb8b52eaec062001f93169bf6ce272", + "piano/context-input.wav": "4e27694f9064da72f0ecff3ffb21726661e3d56200cb8c55f7b9145fff2c150e", + "piano/context-source.wav": "76a98b56c8b33c9fe274475a304761e2cfd6fe32ca685aac644d4e553d989309", + "piano/blind/A.wav": "e7c98c2087842ce7a8be1adb754d03fedc0a22c23eb27d91032c8d94480506df", + "piano/blind/B.wav": "53d8ff76ede676fd7a6292be6c1e362031915fffeb4da4200a17507c15609c56", + "piano/blind/C.wav": "0b9db02c9503f54cfdd37bb72e8d5e84b44e893412ad52e05bc89670a894c05c", + "piano/blind/D.wav": "edefdf0e7b6b97a81519ab6c66c7c0d5030d202ac4bb9fda6f397523fc6889bc", + "piano/blind/E.wav": "47345c56397465941ef81a2de8413767a38f952a5e038b9a083870f39dc59d9b", + "piano/blind/F.wav": "585c127f1ddd846f60005855a56713f00c35c0eb234bc79053afabaa7453cf83", + "piano/raw/apollo-0db.wav": "94e784dd96830f444571e5abe24c6df9aa778dc9762386de8765f10e7e01754b", + "piano/raw/apollo-1db.wav": "e8e507f71c89049503c4eb0a7a5e6d20ae8ce049d8cdd3807427135fececf93b", + "piano/raw/apollo-2db.wav": "92d191f8cdfd394610409b700784c63d6ae0f5f8cbec67e1014b1f1fbe81b942", + "piano/raw/apollo-3db.wav": "1365d9b7b2e9ee1b028b6666fe3f0d340e9a3ddd8b74ee1cbf14a4a8e6e6fb7e", + "piano/raw/degraded.wav": "f8f33360cefcea248561571d85e812d737b57a4ea8fd5647a7a0cde2a6bb3c9b", + "piano/raw/source-proxy.wav": "eba7951959e5a3bf9de6197c461f7be9976227dc2631b20c2e2699726d168ed4", + "quiet/context-apollo.wav": "52b5300a41e99cf51c05927f8eda5b1f4c130ca8da6f023d5ec6d7f5c446c2db", + "quiet/context-input.wav": "e489863e1215bbd2b8af381ec8bfe96c0ad6db654fa05b8236e4871e9eccd9d5", + "quiet/context-source.wav": "a0fb7592d03c4a70f707aa496b0c0baebe8693f6a9732cd72daf0a60de39e04c", + "quiet/blind/A.wav": "9e460261288dd0e89722e4f6e7a9cad52eca8f4a89858ffe05cb103e161ced81", + "quiet/blind/B.wav": "f3bdff5e0c1a943bfe6b7096ebf78453c00d15ae8576db6e91681c1e41e30ccd", + "quiet/blind/C.wav": "32e97a8920c9796c76b0fcedc500230a1731ba0cb08dd9b029a78ae2ba65e666", + "quiet/blind/D.wav": "07c4efa159cdf84725930ad8c10323dcc160cef713bd4d358c03c1548de9e46e", + "quiet/blind/E.wav": "813e206cf1f3232a35e1c983c056aad2ed6a6076d6858bfcc83102455e002bfc", + "quiet/blind/F.wav": "179d7c2fbac8161513c359360f2ae3faf1baabc44b21cf225adeff0989b006a9", + "quiet/raw/apollo-0db.wav": "773a881f3cc6f2300ffe79f2357d9a485b9ac3b3b33636dbf99414d4a27f3920", + "quiet/raw/apollo-1db.wav": "509c3b9ea3127ccc2af10d1f7f2c797a8ad1b094664d701cafeb219c02d11383", + "quiet/raw/apollo-2db.wav": "9293833e41511e847e647a2c54606421ee1e3688fa669461652586ccaf6659ae", + "quiet/raw/apollo-3db.wav": "2e4f77d5a6189a98fa62f9f08809d697d4a050723216e164632c1f9ce398b509", + "quiet/raw/degraded.wav": "4026cb7d90415459f538c2e3b9bad228d054619377d053f3fa06b348ca7ca3fb", + "quiet/raw/source-proxy.wav": "43042d3af36d84bf3471960f5c9ed2eb2f8105151ae4309bcf2125966d1b450b", + "vocal/context-apollo.wav": "6b8731d5e1d5bc31835a7a1dbc9edad563c91c5848762b6929f45a84118e9268", + "vocal/context-input.wav": "bed6a94669a3313f561aaa9118e7d922c42c125bdf24f3bda64646059000cef7", + "vocal/context-source.wav": "418eb1a96ca57a1d31f61629f6754c5de92c7950d039c75cc29e5626d88dd5c4", + "vocal/blind/A.wav": "f4c8b5a59fec76e21ede2b4f82cb8641c70e337f77a679d93e4721bcb9063750", + "vocal/blind/B.wav": "9831b315de19c76111e254ed5cd3b367376e8211c357ea527af30dcc198a5025", + "vocal/blind/C.wav": "f44a2f8e78c7ca934e3a2c4d6024bc655955b9def0d0f0c7856ada4f9f868aa9", + "vocal/blind/D.wav": "2cbc6142c269c410180b935fddf140204ebb67eabf317056f01d3c66e976dc6a", + "vocal/blind/E.wav": "a128c56f3e3a18566cfb612e6d4b45ddabdd183672a42ac3ad1e6e16a2e8affd", + "vocal/blind/F.wav": "9e6b814287867219470d34684160f2fc7ea865af1e0d172c0cb034bc097aa864", + "vocal/raw/apollo-0db.wav": "ff36fe6b5d4df94fec766956a3acd8289524ae1b034f30d9fed1c603642406c2", + "vocal/raw/apollo-1db.wav": "b6787f75ff74126955fd961aa8eb6f3f15778cc4bc3b8d1da27673c1487e05b8", + "vocal/raw/apollo-2db.wav": "f6319b7ee14f62009eac02b8724d1bdd839530ce6a35340105020ee4ee4d67e3", + "vocal/raw/apollo-3db.wav": "f4b1dc90bd45f420ffd5fe44568f0f59e1c3f592a03cacdae53435e2e9c18016", + "vocal/raw/degraded.wav": "38a46d742276e1708527fa7a099b0adfee0a3a3671508f5f552ba5c52a25d43c", + "vocal/raw/source-proxy.wav": "e7746796056eb5f97d6073b229867344bf2214f633165cdf1390b77063538039", + "vocal-dense/context-apollo.wav": "596f49bc556d35328efc47be6b0d18de541c74d915bc7e03ca38f762e97d506d", + "vocal-dense/context-input.wav": "6fccc4ef98aa1be8731c04fcce1e97cd7cf8e10d1324e99312ba25a5c6afd04c", + "vocal-dense/context-source.wav": "0c09f73b0459fff082ac3756b057668e83ae767916fe0491c4019b85b5bc1484", + "vocal-dense/blind/A.wav": "615c5c8a110ad7a24682b3af650b345402e43466561f5aa3d5296abcc4359ea1", + "vocal-dense/blind/B.wav": "400c89c4fa1c7a652e12c0c4082b48234fb8ee50d8552e92e45dac0be7288833", + "vocal-dense/blind/C.wav": "578beecc107908e18c79effd5dd539c2f51f9d42f32bc0309ec232e25a1a292c", + "vocal-dense/blind/D.wav": "32858234be87d9cc25c3834e171888b5407d76f9ee20beba5c0020ff609b080c", + "vocal-dense/blind/E.wav": "932a6134703d4dfd5a3b3bd85260b869c7bba7e89dc094cd9b71560a044bd893", + "vocal-dense/blind/F.wav": "4fb7f4f6d635185f7d37966f76fa8e901bda558073ea1e7e54d496054a8f4f64", + "vocal-dense/raw/apollo-0db.wav": "50328e4230e725b77f2a8f7ecd431cd3a06fd552e4300105b145f3725d85ac9d", + "vocal-dense/raw/apollo-1db.wav": "c10983e471c45fd837948f16ada2dc27226305d0036a57b93a3f2e784e7ff579", + "vocal-dense/raw/apollo-2db.wav": "722deec0bccefba5761e452ff01ea906deda1fc86872ac10c45d775b25dee812", + "vocal-dense/raw/apollo-3db.wav": "efc99a3fa3a0c2eaa5b4d712d55dd842e4e288ced2e60326181bbe74f7442dc7", + "vocal-dense/raw/degraded.wav": "bf6588ac83e17debb7660c31a6c2ac30ca8d08a1582d8fdcbca38f503096687f", + "vocal-dense/raw/source-proxy.wav": "cb8a7bb2ab9476d36afb39db844406da2031cc95ba46fa4650e7407a3a577ed2" +} diff --git a/evaluation/texture-20261016/audition-checks.json b/evaluation/texture-20261016/audition-checks.json new file mode 100644 index 0000000..089be8b --- /dev/null +++ b/evaluation/texture-20261016/audition-checks.json @@ -0,0 +1,42 @@ +[ + { + "case": "vocal", + "lufs_spread": 5.384742749470206e-07, + "oversampled_peak_dbfs": -9.971181296221424 + }, + { + "case": "vocal-dense", + "lufs_spread": 6.286101665864408e-07, + "oversampled_peak_dbfs": -14.033596587268264 + }, + { + "case": "drums", + "lufs_spread": 3.5918262142331514e-07, + "oversampled_peak_dbfs": -6.992506179182048 + }, + { + "case": "dense", + "lufs_spread": 4.743841195420373e-07, + "oversampled_peak_dbfs": -12.904741904925334 + }, + { + "case": "piano", + "lufs_spread": 5.04526965983132e-07, + "oversampled_peak_dbfs": -9.414745416715565 + }, + { + "case": "control-piano", + "lufs_spread": 6.514367996146575e-07, + "oversampled_peak_dbfs": -8.86666076546349 + }, + { + "case": "control-celesta", + "lufs_spread": 4.6879371495833766e-07, + "oversampled_peak_dbfs": -6.744472372091744 + }, + { + "case": "quiet", + "lufs_spread": 2.9967262449304144e-07, + "oversampled_peak_dbfs": -9.88741759144698 + } +] diff --git a/evaluation/texture-20261016/fork-ci.json b/evaluation/texture-20261016/fork-ci.json new file mode 100644 index 0000000..0e0f046 --- /dev/null +++ b/evaluation/texture-20261016/fork-ci.json @@ -0,0 +1,7 @@ +{ + "head_sha": "9a4cd0875af86d5cc8b8334f44f1f2a8a2e3e16d", + "status": "completed", + "conclusion": "success", + "html_url": "https://github.com/hemanpapa-coder/Apollo/actions/runs/38916720169", + "event": "push" +} diff --git a/evaluation/texture-20261016/measurements.json b/evaluation/texture-20261016/measurements.json new file mode 100644 index 0000000..638711c --- /dev/null +++ b/evaluation/texture-20261016/measurements.json @@ -0,0 +1,692 @@ +{ + "upstream_commit": "e84bcacc59d5455f05d86a5c97dd4aeb3c14dbb6", + "upstream_inference_sha256": "6410cae56d31fe0e802f0995c14ce390b4256bf5207311cf67db4fbac3bab8c7", + "checkpoint_sha256": "99d9af7f1ff20e63c393035513a655392818d66b4d7fc23d658175c1f15e8d76", + "checkpoint_revision": "c68bd80fdd9c0d93d2f4a833cb154624f660a561", + "pure_python_yaml": true, + "device": "mps", + "torch": "2.8.0", + "numpy": "2.0.2", + "scipy": "1.13.1", + "soundfile": "0.13.1", + "libsndfile": "1.2.2", + "platform": "macOS-26.5.1-arm64-arm-64bit", + "chunk_seconds": 6, + "overlap_seconds": 1, + "chunk_pad_seconds": 1, + "chunk_batch_size": 1, + "sources": [ + { + "id": "voice", + "file": "where-now.mp3", + "title": "Where You Are Now", + "attribution": "Benjamin Orth featuring Wolf Fly (source vocal: Ascension / Rhiannon)", + "license": "CC-BY-3.0", + "license_url": "https://creativecommons.org/licenses/by/3.0/", + "source_page": "https://ccmixter.org/files/Benjamin_Orth/33036", + "download": "https://ccmixter.org/content/Benjamin_Orth/Benjamin_Orth_-_Where_You_Are_Now.mp3", + "sha256": "56e989e49e0ff91b74cfd92da19b64a4b82aa477a7b39822803aa538c617d046" + }, + { + "id": "vibe", + "file": "Kevin_MacLeod_-_Vibe_Ace.hq.ogg", + "title": "Vibe Ace", + "attribution": "Kevin MacLeod", + "license": "CC-BY-4.0", + "license_url": "https://creativecommons.org/licenses/by/4.0/", + "source_page": "https://librosa.org/data/audio/Kevin_MacLeod_-_Vibe_Ace.toml", + "download": "https://librosa.org/data/audio/Kevin_MacLeod_-_Vibe_Ace.hq.ogg", + "sha256": "73d6443ef90a7c022f164e5aa90e56c2291585930b39b1656d0765abbc1f1779" + }, + { + "id": "piano", + "file": "Study2.ogg", + "title": "Studies for Piano", + "attribution": "Richard Kearns", + "license": "CC-BY-3.0", + "license_url": "https://creativecommons.org/licenses/by/3.0/", + "source_page": "https://commons.wikimedia.org/wiki/File:Study2.ogg", + "download": "https://upload.wikimedia.org/wikipedia/commons/d/de/Study2.ogg", + "sha256": "4a70c3a9c699cd174b81b048a70ec69b7d5c171a0e469c05fe1d70484ba5389a" + }, + { + "id": "celesta", + "file": "Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.hq.ogg", + "title": "Dance of the Sugar Plum Fairy", + "attribution": "P.I. Tchaikovsky (composer), Kevin MacLeod (arranger)", + "license": "CC-BY-4.0", + "license_url": "https://creativecommons.org/licenses/by/4.0/", + "source_page": "https://librosa.org/data/audio/Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.toml", + "download": "https://librosa.org/data/audio/Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.hq.ogg", + "sha256": "f062221a56a227cdb7c067cf2e6ac0e250a50012f7693ca0c8e31f05f83e49b1" + } + ], + "cases": [ + { + "id": "vocal", + "source": "voice", + "start_seconds": 30, + "duration": 15, + "no_added_degradation": false, + "focus": "female vocal/sibilants; review 2\u201312 s", + "model_seconds": 9.506508208, + "postprocessing": { + "1": { + "max_reduction_db": 0.9277780193038324, + "power_weighted_reduction_db": 0.006405656677210542, + "attack_frame_fraction": 0.23606457508376485, + "seconds": 0.18501620900000404 + }, + "2": { + "max_reduction_db": 1.7665355749321057, + "power_weighted_reduction_db": 0.007618372300704901, + "attack_frame_fraction": 0.23606457508376485, + "seconds": 0.15007120799999996 + }, + "3": { + "max_reduction_db": 2.3250870674913915, + "power_weighted_reduction_db": 0.007930222658002499, + "attack_frame_fraction": 0.23606457508376485, + "seconds": 0.14990266600000268 + } + }, + "metrics": { + "source-proxy": { + "proxy_snr_db": 345.3335764340887, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -13.146152969119903 + }, + "degraded": { + "proxy_snr_db": 17.84819909156824, + "proxy_log_spectrum_rmse_db": 6.264313244834549, + "integrated_lufs": -13.52753996686618 + }, + "apollo-0db": { + "proxy_snr_db": 17.795616680900224, + "proxy_log_spectrum_rmse_db": 5.29298021890908, + "integrated_lufs": -13.199174859138699 + }, + "apollo-1db": { + "proxy_snr_db": 17.805633251725332, + "proxy_log_spectrum_rmse_db": 5.296966228380251, + "integrated_lufs": -13.211267095718757 + }, + "apollo-2db": { + "proxy_snr_db": 17.80449815598604, + "proxy_log_spectrum_rmse_db": 5.29839798994255, + "integrated_lufs": -13.21351593860634 + }, + "apollo-3db": { + "proxy_snr_db": 17.803378530321503, + "proxy_log_spectrum_rmse_db": 5.298760352251937, + "integrated_lufs": -13.214075860523861 + } + }, + "blind_key": { + "A": "degraded", + "B": "apollo-3db", + "C": "source-proxy", + "D": "apollo-0db", + "E": "apollo-2db", + "F": "apollo-1db" + }, + "blind_target_lufs": -23.0, + "common_gain_db": 0.0, + "codec_alignment_peak_lag_samples": 0, + "largest_change_window_seconds": [ + 13, + 14 + ], + "proxy_snr_delta_3db": 0.007761849421278555, + "proxy_spectral_delta_3db": 0.0057801333428564305 + }, + { + "id": "vocal-dense", + "source": "voice", + "start_seconds": 70, + "duration": 15, + "no_added_degradation": false, + "focus": "vocal with dense pop mixture; review 0\u201315 s", + "model_seconds": 8.839069875000007, + "postprocessing": { + "1": { + "max_reduction_db": 0.9381951735232099, + "power_weighted_reduction_db": 0.004960963929212733, + "attack_frame_fraction": 0.36003655193420653, + "seconds": 0.2037515410000026 + }, + "2": { + "max_reduction_db": 1.7956498706561956, + "power_weighted_reduction_db": 0.005941718101797443, + "attack_frame_fraction": 0.36003655193420653, + "seconds": 0.1563755829999991 + }, + "3": { + "max_reduction_db": 2.4268124378433713, + "power_weighted_reduction_db": 0.00614522027951007, + "attack_frame_fraction": 0.36003655193420653, + "seconds": 0.15161662500000261 + } + }, + "metrics": { + "source-proxy": { + "proxy_snr_db": 351.5158244900671, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -7.670278454925665 + }, + "degraded": { + "proxy_snr_db": 18.16938604175261, + "proxy_log_spectrum_rmse_db": 8.13214966462172, + "integrated_lufs": -8.070041709257842 + }, + "apollo-0db": { + "proxy_snr_db": 17.94836529927291, + "proxy_log_spectrum_rmse_db": 6.96038304457811, + "integrated_lufs": -7.79526875496955 + }, + "apollo-1db": { + "proxy_snr_db": 17.958142610163698, + "proxy_log_spectrum_rmse_db": 6.96473037484198, + "integrated_lufs": -7.80740722841217 + }, + "apollo-2db": { + "proxy_snr_db": 17.957255971095712, + "proxy_log_spectrum_rmse_db": 6.965914567959103, + "integrated_lufs": -7.809689099263081 + }, + "apollo-3db": { + "proxy_snr_db": 17.956408039578804, + "proxy_log_spectrum_rmse_db": 6.966201236106422, + "integrated_lufs": -7.810137634976561 + } + }, + "blind_key": { + "A": "source-proxy", + "B": "apollo-1db", + "C": "apollo-3db", + "D": "apollo-2db", + "E": "degraded", + "F": "apollo-0db" + }, + "blind_target_lufs": -23.0, + "common_gain_db": 0.0, + "codec_alignment_peak_lag_samples": 0, + "largest_change_window_seconds": [ + 9, + 10 + ], + "proxy_snr_delta_3db": 0.008042740305892693, + "proxy_spectral_delta_3db": 0.005818191528311978 + }, + { + "id": "drums", + "source": "vibe", + "start_seconds": 20, + "duration": 15, + "no_added_degradation": false, + "focus": "kit/cymbals in jazz mixture; review 0\u201315 s", + "model_seconds": 8.302095249999994, + "postprocessing": { + "1": { + "max_reduction_db": 0.9233757737189019, + "power_weighted_reduction_db": 0.00012472032349085393, + "attack_frame_fraction": 0.43374961925068534, + "seconds": 0.15764849999999342 + }, + "2": { + "max_reduction_db": 1.7802924918901082, + "power_weighted_reduction_db": 0.00015786187026980153, + "attack_frame_fraction": 0.43374961925068534, + "seconds": 0.15351825000000474 + }, + "3": { + "max_reduction_db": 2.498484735242565, + "power_weighted_reduction_db": 0.00016551712022401562, + "attack_frame_fraction": 0.43374961925068534, + "seconds": 0.15145716600000014 + } + }, + "metrics": { + "source-proxy": { + "proxy_snr_db": 344.1179240078923, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -16.420689771864364 + }, + "degraded": { + "proxy_snr_db": 28.483370656736696, + "proxy_log_spectrum_rmse_db": 5.030928634273675, + "integrated_lufs": -16.85941741193594 + }, + "apollo-0db": { + "proxy_snr_db": 28.00615449160807, + "proxy_log_spectrum_rmse_db": 4.023621021478898, + "integrated_lufs": -16.523815672079053 + }, + "apollo-1db": { + "proxy_snr_db": 28.008189835176616, + "proxy_log_spectrum_rmse_db": 4.0269196981262345, + "integrated_lufs": -16.524208896396086 + }, + "apollo-2db": { + "proxy_snr_db": 28.00822534480985, + "proxy_log_spectrum_rmse_db": 4.028354078076484, + "integrated_lufs": -16.524309022564946 + }, + "apollo-3db": { + "proxy_snr_db": 28.008010948150556, + "proxy_log_spectrum_rmse_db": 4.028889846237043, + "integrated_lufs": -16.524331499164685 + } + }, + "blind_key": { + "A": "source-proxy", + "B": "apollo-1db", + "C": "apollo-0db", + "D": "degraded", + "E": "apollo-2db", + "F": "apollo-3db" + }, + "blind_target_lufs": -23.0, + "common_gain_db": 0.0, + "codec_alignment_peak_lag_samples": 0, + "largest_change_window_seconds": [ + 14, + 15 + ], + "proxy_snr_delta_3db": 0.0018564565424874502, + "proxy_spectral_delta_3db": 0.005268824758144497 + }, + { + "id": "dense", + "source": "voice", + "start_seconds": 110, + "duration": 15, + "no_added_degradation": false, + "focus": "dense synth/guitar/drum mixture; review 0\u201315 s", + "model_seconds": 8.237370834000018, + "postprocessing": { + "1": { + "max_reduction_db": 0.9120900451871284, + "power_weighted_reduction_db": 0.004309473577788946, + "attack_frame_fraction": 0.2598233323180018, + "seconds": 0.15123416599999473 + }, + "2": { + "max_reduction_db": 1.7621566271340454, + "power_weighted_reduction_db": 0.005109912040847642, + "attack_frame_fraction": 0.2598233323180018, + "seconds": 0.22263233300000707 + }, + "3": { + "max_reduction_db": 2.565897819557046, + "power_weighted_reduction_db": 0.0052868646615776595, + "attack_frame_fraction": 0.2598233323180018, + "seconds": 0.16460370899997656 + } + }, + "metrics": { + "source-proxy": { + "proxy_snr_db": 350.39079978812, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -8.32650770833035 + }, + "degraded": { + "proxy_snr_db": 18.650989516675775, + "proxy_log_spectrum_rmse_db": 7.243176641043024, + "integrated_lufs": -8.728852053925175 + }, + "apollo-0db": { + "proxy_snr_db": 18.430476557209875, + "proxy_log_spectrum_rmse_db": 6.55321420324369, + "integrated_lufs": -8.401759731910804 + }, + "apollo-1db": { + "proxy_snr_db": 18.441415491719127, + "proxy_log_spectrum_rmse_db": 6.558932200205994, + "integrated_lufs": -8.411200885319044 + }, + "apollo-2db": { + "proxy_snr_db": 18.44144120578801, + "proxy_log_spectrum_rmse_db": 6.560664703567572, + "integrated_lufs": -8.412877325469987 + }, + "apollo-3db": { + "proxy_snr_db": 18.441063239838577, + "proxy_log_spectrum_rmse_db": 6.561086789245587, + "integrated_lufs": -8.413223169617641 + } + }, + "blind_key": { + "A": "apollo-2db", + "B": "source-proxy", + "C": "apollo-3db", + "D": "apollo-0db", + "E": "apollo-1db", + "F": "degraded" + }, + "blind_target_lufs": -23.0, + "common_gain_db": 0.0, + "codec_alignment_peak_lag_samples": 0, + "largest_change_window_seconds": [ + 3, + 4 + ], + "proxy_snr_delta_3db": 0.010586682628701993, + "proxy_spectral_delta_3db": 0.007872586001896664 + }, + { + "id": "piano", + "source": "piano", + "start_seconds": 15, + "duration": 15, + "no_added_degradation": false, + "focus": "piano transients/sustains; review 0\u201315 s", + "model_seconds": 8.493648957999994, + "postprocessing": { + "1": { + "max_reduction_db": 0.9508322441721178, + "power_weighted_reduction_db": 0.00045141379300632663, + "attack_frame_fraction": 0.1836734693877551, + "seconds": 0.19210229199998707 + }, + "2": { + "max_reduction_db": 1.8980818305936575, + "power_weighted_reduction_db": 0.0004986413529220128, + "attack_frame_fraction": 0.1836734693877551, + "seconds": 0.1550508750000006 + }, + "3": { + "max_reduction_db": 2.7052187879223273, + "power_weighted_reduction_db": 0.0005021784835520333, + "attack_frame_fraction": 0.1836734693877551, + "seconds": 0.16535299999998188 + } + }, + "metrics": { + "source-proxy": { + "proxy_snr_db": 343.2317032986203, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -15.152773119187982 + }, + "degraded": { + "proxy_snr_db": 22.119948343243422, + "proxy_log_spectrum_rmse_db": 4.488636491493283, + "integrated_lufs": -15.590826010004978 + }, + "apollo-0db": { + "proxy_snr_db": 22.45829972331471, + "proxy_log_spectrum_rmse_db": 4.270752712136737, + "integrated_lufs": -15.158116675664862 + }, + "apollo-1db": { + "proxy_snr_db": 22.458025271541764, + "proxy_log_spectrum_rmse_db": 4.271372475557171, + "integrated_lufs": -15.158933378482187 + }, + "apollo-2db": { + "proxy_snr_db": 22.45794316452489, + "proxy_log_spectrum_rmse_db": 4.271515249737962, + "integrated_lufs": -15.15900740164424 + }, + "apollo-3db": { + "proxy_snr_db": 22.457933200560735, + "proxy_log_spectrum_rmse_db": 4.271584967013272, + "integrated_lufs": -15.15901145108523 + } + }, + "blind_key": { + "A": "apollo-3db", + "B": "degraded", + "C": "source-proxy", + "D": "apollo-0db", + "E": "apollo-1db", + "F": "apollo-2db" + }, + "blind_target_lufs": -23.0, + "common_gain_db": 0.0, + "codec_alignment_peak_lag_samples": 0, + "largest_change_window_seconds": [ + 1, + 2 + ], + "proxy_snr_delta_3db": -0.0003665227539748628, + "proxy_spectral_delta_3db": 0.0008322548765349325 + }, + { + "id": "control-piano", + "source": "piano", + "start_seconds": 40, + "duration": 15, + "no_added_degradation": true, + "focus": "no added codec degradation negative control; review wanted detail", + "model_seconds": 8.386500916000045, + "postprocessing": { + "1": { + "max_reduction_db": 0.9514021415209956, + "power_weighted_reduction_db": 0.00054733751255932, + "attack_frame_fraction": 0.16905269570514772, + "seconds": 0.1788028330000202 + }, + "2": { + "max_reduction_db": 1.8381541803499792, + "power_weighted_reduction_db": 0.0005987326947648451, + "attack_frame_fraction": 0.16905269570514772, + "seconds": 0.15105508400000645 + }, + "3": { + "max_reduction_db": 2.627608112536266, + "power_weighted_reduction_db": 0.0006023057328315994, + "attack_frame_fraction": 0.16905269570514772, + "seconds": 0.15169883399994433 + } + }, + "metrics": { + "source-proxy": { + "proxy_snr_db": 343.63278628936285, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -14.794504118250858 + }, + "degraded": { + "proxy_snr_db": 343.63278628936285, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -14.794504118250858 + }, + "apollo-0db": { + "proxy_snr_db": 32.67701580650404, + "proxy_log_spectrum_rmse_db": 2.5706256258761084, + "integrated_lufs": -14.348045783442986 + }, + "apollo-1db": { + "proxy_snr_db": 32.65810836188682, + "proxy_log_spectrum_rmse_db": 2.57155384015423, + "integrated_lufs": -14.349035324858942 + }, + "apollo-2db": { + "proxy_snr_db": 32.654000138846556, + "proxy_log_spectrum_rmse_db": 2.5718817634047624, + "integrated_lufs": -14.349139732325051 + }, + "apollo-3db": { + "proxy_snr_db": 32.6535331204102, + "proxy_log_spectrum_rmse_db": 2.572071769486433, + "integrated_lufs": -14.349147027379429 + } + }, + "blind_key": { + "A": "degraded", + "B": "apollo-1db", + "C": "apollo-2db", + "D": "source-proxy", + "E": "apollo-3db", + "F": "apollo-0db" + }, + "blind_target_lufs": -23.0, + "common_gain_db": 0.0, + "codec_alignment_peak_lag_samples": 0, + "largest_change_window_seconds": [ + 1, + 2 + ], + "proxy_snr_delta_3db": -0.023482686093835525, + "proxy_spectral_delta_3db": 0.0014461436103245795 + }, + { + "id": "control-celesta", + "source": "celesta", + "start_seconds": 10, + "duration": 15, + "no_added_degradation": true, + "focus": "no added codec degradation negative control; review attacks", + "model_seconds": 8.30687341700002, + "postprocessing": { + "1": { + "max_reduction_db": 0.9523644463693307, + "power_weighted_reduction_db": 0.0004061499640822737, + "attack_frame_fraction": 0.20499543100822418, + "seconds": 0.1756770000000074 + }, + "2": { + "max_reduction_db": 1.7935269672681515, + "power_weighted_reduction_db": 0.0004671529406520139, + "attack_frame_fraction": 0.20499543100822418, + "seconds": 0.1520695830000136 + }, + "3": { + "max_reduction_db": 2.618655255984388, + "power_weighted_reduction_db": 0.0004782941493130434, + "attack_frame_fraction": 0.20499543100822418, + "seconds": 0.15150983299997733 + } + }, + "metrics": { + "source-proxy": { + "proxy_snr_db": 336.12060760267525, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -22.635389032198916 + }, + "degraded": { + "proxy_snr_db": 336.12060760267525, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -22.635389032198916 + }, + "apollo-0db": { + "proxy_snr_db": 33.61315576889569, + "proxy_log_spectrum_rmse_db": 1.3162946130946984, + "integrated_lufs": -22.22164050696478 + }, + "apollo-1db": { + "proxy_snr_db": 33.590349744624085, + "proxy_log_spectrum_rmse_db": 1.3225470463593738, + "integrated_lufs": -22.222407965498636 + }, + "apollo-2db": { + "proxy_snr_db": 33.58461991740678, + "proxy_log_spectrum_rmse_db": 1.3243090331203096, + "integrated_lufs": -22.222521140427652 + }, + "apollo-3db": { + "proxy_snr_db": 33.58342788859913, + "proxy_log_spectrum_rmse_db": 1.3247537746249867, + "integrated_lufs": -22.222541913796967 + } + }, + "blind_key": { + "A": "apollo-3db", + "B": "degraded", + "C": "apollo-2db", + "D": "apollo-1db", + "E": "apollo-0db", + "F": "source-proxy" + }, + "blind_target_lufs": -23.0, + "common_gain_db": 0.0, + "codec_alignment_peak_lag_samples": 0, + "largest_change_window_seconds": [ + 13, + 14 + ], + "proxy_snr_delta_3db": -0.029727880296555043, + "proxy_spectral_delta_3db": 0.00845916153028825 + }, + { + "id": "quiet", + "source": "piano", + "input_gain_db": -18.0, + "start_seconds": 65, + "duration": 15, + "no_added_degradation": false, + "focus": "quiet piano candidate; review decays 0\u201315 s", + "model_seconds": 8.358211500000003, + "postprocessing": { + "1": { + "max_reduction_db": 0.9499271894995048, + "power_weighted_reduction_db": 0.0004136056155710113, + "attack_frame_fraction": 0.1839780688394761, + "seconds": 0.19872379199999557 + }, + "2": { + "max_reduction_db": 1.7785922008176083, + "power_weighted_reduction_db": 0.0004456497121363847, + "attack_frame_fraction": 0.1839780688394761, + "seconds": 0.1582491660000045 + }, + "3": { + "max_reduction_db": 2.5186530260478026, + "power_weighted_reduction_db": 0.00044896900937477176, + "attack_frame_fraction": 0.1839780688394761, + "seconds": 0.15844245799999612 + } + }, + "metrics": { + "source-proxy": { + "proxy_snr_db": 325.01836077936616, + "proxy_log_spectrum_rmse_db": 0.0, + "integrated_lufs": -33.49932920875261 + }, + "degraded": { + "proxy_snr_db": 22.03086961638961, + "proxy_log_spectrum_rmse_db": 2.0993352248392325, + "integrated_lufs": -33.93843779897025 + }, + "apollo-0db": { + "proxy_snr_db": 21.869373892818334, + "proxy_log_spectrum_rmse_db": 2.0394451912450218, + "integrated_lufs": -33.518635404188466 + }, + "apollo-1db": { + "proxy_snr_db": 21.870460735482816, + "proxy_log_spectrum_rmse_db": 2.0396993261373693, + "integrated_lufs": -33.51933350552817 + }, + "apollo-2db": { + "proxy_snr_db": 21.87055010005373, + "proxy_log_spectrum_rmse_db": 2.0398698611095307, + "integrated_lufs": -33.51938342323602 + }, + "apollo-3db": { + "proxy_snr_db": 21.87053925175779, + "proxy_log_spectrum_rmse_db": 2.0399275184970955, + "integrated_lufs": -33.51938866459359 + } + }, + "blind_key": { + "A": "source-proxy", + "B": "apollo-3db", + "C": "degraded", + "D": "apollo-0db", + "E": "apollo-2db", + "F": "apollo-1db" + }, + "blind_target_lufs": -23.0, + "common_gain_db": 0.0, + "codec_alignment_peak_lag_samples": 0, + "largest_change_window_seconds": [ + 12, + 13 + ], + "proxy_snr_delta_3db": 0.0011653589394562403, + "proxy_spectral_delta_3db": 0.0004823272520737554 + } + ], + "notes": "Sources are already lossy public recordings; source-proxy is NOT a lossless clean reference. No listener verdict. Controls are no-added-degradation, not certified clean recordings." +} diff --git a/evaluation/texture-20261016/memory-apollo-180.json b/evaluation/texture-20261016/memory-apollo-180.json new file mode 100644 index 0000000..ae54f50 --- /dev/null +++ b/evaluation/texture-20261016/memory-apollo-180.json @@ -0,0 +1,21 @@ +{ + "duration": 180.0, + "frames": 7938000, + "channels": 2, + "sample_rate": 44100, + "numpy": "2.0.2", + "scipy": "1.13.1", + "platform": "macOS-26.5.1-arm64-arm-64bit", + "input_file": "context-apollo.wav", + "input_repeated": true, + "seconds": 2.50147275, + "traced_peak_bytes": 2441695549, + "process_peak_rss_bytes": 3190030336, + "process_peak_before_bytes": 226721792, + "statistics": { + "max_reduction_db": 1.803205901063708, + "power_weighted_reduction_db": 0.007614077796485415, + "attack_frame_fraction": 0.23599961311538833 + }, + "finite": true +} diff --git a/evaluation/texture-20261016/memory-music-30.json b/evaluation/texture-20261016/memory-music-30.json new file mode 100644 index 0000000..c36a039 --- /dev/null +++ b/evaluation/texture-20261016/memory-music-30.json @@ -0,0 +1,21 @@ +{ + "duration": 30.0, + "frames": 1323000, + "channels": 2, + "sample_rate": 44100, + "numpy": "2.0.2", + "scipy": "1.13.1", + "platform": "macOS-26.5.1-arm64-arm-64bit", + "input_file": "SDTEST1.WAV", + "input_repeated": false, + "seconds": 0.3436969579999998, + "traced_peak_bytes": 407622657, + "process_peak_rss_bytes": 680280064, + "process_peak_before_bytes": 160071680, + "statistics": { + "max_reduction_db": 1.8357674293826705, + "power_weighted_reduction_db": 0.006417486371190556, + "attack_frame_fraction": 0.2149893760865366 + }, + "finite": true +} diff --git a/evaluation/texture-20261016/sources.json b/evaluation/texture-20261016/sources.json new file mode 100644 index 0000000..0c6a51b --- /dev/null +++ b/evaluation/texture-20261016/sources.json @@ -0,0 +1,46 @@ +[ + { + "id": "voice", + "file": "where-now.mp3", + "title": "Where You Are Now", + "attribution": "Benjamin Orth featuring Wolf Fly (source vocal: Ascension / Rhiannon)", + "license": "CC-BY-3.0", + "license_url": "https://creativecommons.org/licenses/by/3.0/", + "source_page": "https://ccmixter.org/files/Benjamin_Orth/33036", + "download": "https://ccmixter.org/content/Benjamin_Orth/Benjamin_Orth_-_Where_You_Are_Now.mp3", + "sha256": "56e989e49e0ff91b74cfd92da19b64a4b82aa477a7b39822803aa538c617d046" + }, + { + "id": "vibe", + "file": "Kevin_MacLeod_-_Vibe_Ace.hq.ogg", + "title": "Vibe Ace", + "attribution": "Kevin MacLeod", + "license": "CC-BY-4.0", + "license_url": "https://creativecommons.org/licenses/by/4.0/", + "source_page": "https://librosa.org/data/audio/Kevin_MacLeod_-_Vibe_Ace.toml", + "download": "https://librosa.org/data/audio/Kevin_MacLeod_-_Vibe_Ace.hq.ogg", + "sha256": "73d6443ef90a7c022f164e5aa90e56c2291585930b39b1656d0765abbc1f1779" + }, + { + "id": "piano", + "file": "Study2.ogg", + "title": "Studies for Piano", + "attribution": "Richard Kearns", + "license": "CC-BY-3.0", + "license_url": "https://creativecommons.org/licenses/by/3.0/", + "source_page": "https://commons.wikimedia.org/wiki/File:Study2.ogg", + "download": "https://upload.wikimedia.org/wikipedia/commons/d/de/Study2.ogg", + "sha256": "4a70c3a9c699cd174b81b048a70ec69b7d5c171a0e469c05fe1d70484ba5389a" + }, + { + "id": "celesta", + "file": "Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.hq.ogg", + "title": "Dance of the Sugar Plum Fairy", + "attribution": "P.I. Tchaikovsky (composer), Kevin MacLeod (arranger)", + "license": "CC-BY-4.0", + "license_url": "https://creativecommons.org/licenses/by/4.0/", + "source_page": "https://librosa.org/data/audio/Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.toml", + "download": "https://librosa.org/data/audio/Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.hq.ogg", + "sha256": "f062221a56a227cdb7c067cf2e6ac0e250a50012f7693ca0c8e31f05f83e49b1" + } +] diff --git a/evaluation/texture-20261016/system-unit-tests.log b/evaluation/texture-20261016/system-unit-tests.log new file mode 100644 index 0000000..b0198f5 --- /dev/null +++ b/evaluation/texture-20261016/system-unit-tests.log @@ -0,0 +1,41 @@ +/Volumes/Program Dev/Neuracoust Suno Sound Build-up/contributions/apollo-texture/test-dependencies/urllib3/__init__.py:35: NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 2.8.3'. See: https://github.com/urllib3/urllib3/issues/3020 + warnings.warn( +test_auto_falls_back_to_cpu (test_inference.DeviceSelectionTests) ... ok +test_auto_prefers_cuda_over_mps (test_inference.DeviceSelectionTests) ... ok +test_auto_uses_mps_when_cuda_is_unavailable (test_inference.DeviceSelectionTests) ... ok +test_explicit_unavailable_cuda_fails (test_inference.DeviceSelectionTests) ... ok +test_explicit_unavailable_mps_fails (test_inference.DeviceSelectionTests) ... ok +test_arbitrary_hugging_face_repository_is_rejected (test_inference.InferenceTests) ... ok +test_checkpoint_directory_is_rejected (test_inference.InferenceTests) ... ok +test_chunk_padding_discards_degraded_input_edges (test_inference.InferenceTests) ... ok +test_chunk_padding_wider_than_the_hop_reproduces_the_input (test_inference.InferenceTests) +Regression test ... ok +test_chunk_settings_reject_invalid_durations (test_inference.InferenceTests) ... ok +test_chunked_cpu_inference_preserves_samples_and_limits_chunk_size (test_inference.InferenceTests) ... ok +test_chunking_rejects_invalid_batch_size (test_inference.InferenceTests) ... ok +test_chunking_rejects_short_model_output (test_inference.InferenceTests) ... ok +test_cpu_inference_preserves_input_and_audio_shape (test_inference.InferenceTests) ... ok +test_existing_local_checkpoint_is_accepted (test_inference.InferenceTests) ... ok +test_invalid_batch_size_fails_before_checkpoint_download (test_inference.InferenceTests) ... ok +test_missing_local_checkpoint_has_clear_error (test_inference.InferenceTests) ... ok +test_official_checkpoint_downloads_from_trusted_repository (test_inference.InferenceTests) ... ok +test_refuses_hard_link_output_alias (test_inference.InferenceTests) ... ok +test_refuses_to_overwrite_input (test_inference.InferenceTests) ... ok +test_short_input_uses_original_full_file_path (test_inference.InferenceTests) ... ok +test_unpadded_chunks_leak_degraded_edges (test_inference.InferenceTests) ... ok +test_wrong_sample_rate_fails_before_download_or_device_transfer (test_inference.InferenceTests) ... ok +test_invalid_cap_rejected_before_model_load (test_texture_inference.IntegrationTests) ... ok +test_missing_scipy_fails_before_audio_checkpoint_or_model (test_texture_inference.IntegrationTests) ... ok +test_optional_postprocessing_and_default_bypass (test_texture_inference.IntegrationTests) ... ok +test_short_clip_rejected_before_checkpoint_but_disabled_still_works (test_texture_inference.IntegrationTests) ... ok +test_unrelated_import_errors_are_not_misreported (test_texture_inference.IntegrationTests) ... ok +test_actual_modulation_is_bounded (test_texture_softening.TextureTests) ... ok +test_exact_bypass (test_texture_softening.TextureTests) ... ok +test_invalid (test_texture_softening.TextureTests) ... ok +test_silence (test_texture_softening.TextureTests) ... ok +test_steady_tone_stereo_and_length (test_texture_softening.TextureTests) ... ok + +---------------------------------------------------------------------- +Ran 33 tests in 16.020s + +OK diff --git a/tests/build_texture_music_comparison.py b/tests/build_texture_music_comparison.py index cba92ee..20fa10b 100644 --- a/tests/build_texture_music_comparison.py +++ b/tests/build_texture_music_comparison.py @@ -35,7 +35,11 @@ def main(): p.add_argument('--checkpoint', type=Path, required=True) p.add_argument('--output-dir', type=Path, required=True) p.add_argument('--device', choices=['mps','cpu','cuda'], default='mps') + p.add_argument('--only-case', help='Rebuild one case, preserving completed cases in the existing manifest') + p.add_argument('--pure-python-yaml', action='store_true', help='Use the supported pure-Python PyYAML parser instead of its optional C extension') a = p.parse_args() + if a.pure_python_yaml: + sys.modules['yaml._yaml'] = None a.output_dir.mkdir(parents=True, exist_ok=True) upstream = a.output_dir/'upstream_inference.py' upstream.write_bytes(subprocess.check_output(['git','show',BASE+':inference.py'])) @@ -58,19 +62,26 @@ def main(): ('control-celesta','celesta',10,True,'no added codec degradation negative control; review attacks')] report = dict(upstream_commit=BASE,upstream_inference_sha256=digest(upstream), checkpoint_sha256=digest(a.checkpoint),checkpoint_revision='c68bd80fdd9c0d93d2f4a833cb154624f660a561', - device=str(device),torch=torch.__version__,numpy=np.__version__,scipy=scipy.__version__, + pure_python_yaml=a.pure_python_yaml,device=str(device),torch=torch.__version__,numpy=np.__version__,scipy=scipy.__version__, soundfile=sf.__version__,libsndfile=sf.__libsndfile_version__,platform=platform.platform(), chunk_seconds=6,overlap_seconds=1,chunk_pad_seconds=1,chunk_batch_size=1, sources=sources,cases=[],notes='Sources are already lossy public recordings; source-proxy is NOT a lossless clean reference. No listener verdict. Controls are no-added-degradation, not certified clean recordings.') + if a.only_case: + previous=json.loads((a.output_dir/'measurements.json').read_text()) + report['cases']=[r for r in previous['cases'] if r['id']!=a.only_case] meter = pyln.Meter(SR) rng = random.Random(441044) for cid,sid,start,control,focus in cases: + if a.only_case and cid!=a.only_case: + skipped=list(range(6));rng.shuffle(skipped) + continue out = a.output_dir/cid;out.mkdir(exist_ok=True) raw = out/'raw';raw.mkdir(exist_ok=True) audition = out/'blind';audition.mkdir(exist_ok=True) source, rate = sf.read(a.sources_dir/lookup[sid]['file'],dtype='float32',always_2d=True) assert rate==SR and source.shape[1]==2 - context = source[(start-2)*SR:(start+17)*SR] + input_gain_db=-18.0 if cid=='quiet' else 0.0 + context = source[(start-2)*SR:(start+17)*SR]*10**(input_gain_db/20) assert context.shape==(19*SR,2) sf.write(out/'context-source.wav',context,SR,subtype='FLOAT') if control: degraded = context.copy() @@ -111,7 +122,7 @@ def main(): letter=chr(65+i);v=matched[name]*common sf.write(audition/(letter+'.wav'),v.T,SR,subtype='FLOAT') key[letter]=name - row=dict(id=cid,source=sid,start_seconds=start,duration=15,no_added_degradation=control, + row=dict(id=cid,source=sid,input_gain_db=input_gain_db,start_seconds=start,duration=15,no_added_degradation=control, focus=focus,model_seconds=elapsed,postprocessing=stats,metrics=metrics, blind_key=key,blind_target_lufs=-23+20*np.log10(common),common_gain_db=20*np.log10(common)) report['cases'].append(row) From f160a8a1e267626da2136fc20a487eef99f8cf28 Mon Sep 17 00:00:00 2001 From: hemanpapa-coder Date: Sat, 17 Oct 2026 09:57:11 +0900 Subject: [PATCH 5/6] Document and reproduce full application enhancement separately from texture-only integration --- evaluation/full-enhancement-method/LICENSE | 86 ++++++++++++++++++ evaluation/full-enhancement-method/README.md | 28 ++++++ .../full-enhancement-method/SHA256.json | 14 +++ .../full-enhancement-method/reproduce.py | 20 +++++ .../source/apollo_enhanced.py | 64 +++++++++++++ .../source/apollo_gain_fusion.py | 80 +++++++++++++++++ .../source/apollo_phase_stability.py | 27 ++++++ .../source/apollo_texture_softening.py | 35 ++++++++ evaluation/full-enhancement-method/tests.log | 5 ++ .../tests/test_apollo_enhanced.py | 26 ++++++ .../tests/test_apollo_gain_fusion.py | 90 +++++++++++++++++++ .../tests/test_apollo_phase_stability.py | 27 ++++++ .../tests/test_apollo_texture_softening.py | 20 +++++ 13 files changed, 522 insertions(+) create mode 100644 evaluation/full-enhancement-method/LICENSE create mode 100644 evaluation/full-enhancement-method/README.md create mode 100644 evaluation/full-enhancement-method/SHA256.json create mode 100644 evaluation/full-enhancement-method/reproduce.py create mode 100644 evaluation/full-enhancement-method/source/apollo_enhanced.py create mode 100644 evaluation/full-enhancement-method/source/apollo_gain_fusion.py create mode 100644 evaluation/full-enhancement-method/source/apollo_phase_stability.py create mode 100644 evaluation/full-enhancement-method/source/apollo_texture_softening.py create mode 100644 evaluation/full-enhancement-method/tests.log create mode 100644 evaluation/full-enhancement-method/tests/test_apollo_enhanced.py create mode 100644 evaluation/full-enhancement-method/tests/test_apollo_gain_fusion.py create mode 100644 evaluation/full-enhancement-method/tests/test_apollo_phase_stability.py create mode 100644 evaluation/full-enhancement-method/tests/test_apollo_texture_softening.py diff --git a/evaluation/full-enhancement-method/LICENSE b/evaluation/full-enhancement-method/LICENSE new file mode 100644 index 0000000..8445ce6 --- /dev/null +++ b/evaluation/full-enhancement-method/LICENSE @@ -0,0 +1,86 @@ +Attribution-ShareAlike 4.0 International +By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. + +Section 1 – Definitions. +Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. +Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. +BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses , approved by Creative Commons as essentially the equivalent of this Public License. +Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. +Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. +Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. +License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. +Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. +Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. +Licensor means the individual(s) or entity(ies) granting rights under this Public License. +Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. +Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. +You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. +Section 2 – Scope. +License grant . +Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: +reproduce and Share the Licensed Material, in whole or in part; and +produce, reproduce, and Share Adapted Material. +Exceptions and Limitations . For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. +Term . The term of this Public License is specified in Section 6(a) . +Media and formats; technical modifications allowed . The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. +Downstream recipients . +Offer from the Licensor – Licensed Material . Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. +Additional offer from the Licensor – Adapted Material . Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply. +No downstream restrictions . You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. +No endorsement . Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i) . +Other rights . +Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. +Patent and trademark rights are not licensed under this Public License. +To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. +Section 3 – License Conditions. +Your exercise of the Licensed Rights is expressly made subject to the following conditions. + +Attribution . +If You Share the Licensed Material (including in modified form), You must: + +retain the following if it is supplied by the Licensor with the Licensed Material: +identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); +a copyright notice; +a notice that refers to this Public License; +a notice that refers to the disclaimer of warranties; +a URI or hyperlink to the Licensed Material to the extent reasonably practicable; +indicate if You modified the Licensed Material and retain an indication of any previous modifications; and +indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. +You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. +If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. +ShareAlike . +In addition to the conditions in Section 3(a) , if You Share Adapted Material You produce, the following conditions also apply. + +The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. +You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. +You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. +Section 4 – Sui Generis Database Rights. +Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: + +for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; +if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b) ; and +You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. +For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. +Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. +To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. +The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. +Section 6 – Term and Termination. +This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. +Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: + +automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or +upon express reinstatement by the Licensor. +For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. + +For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. +Sections 1 , 5 , 6 , 7 , and 8 survive termination of this Public License. +Section 7 – Other Terms and Conditions. +The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. +Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. +Section 8 – Interpretation. +For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. +To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. +No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. +Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. diff --git a/evaluation/full-enhancement-method/README.md b/evaluation/full-enhancement-method/README.md new file mode 100644 index 0000000..18f7f76 --- /dev/null +++ b/evaluation/full-enhancement-method/README.md @@ -0,0 +1,28 @@ +# Neuracoust full Apollo enhancement method + +This companion reproduces the application processing chain; PR #44 implements only the final optional texture-softening stage. The full application method is supplied for inspection and evaluation, not represented as already integrated into upstream inference.py. + +## Signal path + +1. Divide the selected audio into 10-second windows with 1-second output overlap. Real neighboring context is used; file edges use reflection padding. +2. Infer positive and negative polarity with 1-second context and combine (F(x)-F(-x))/2. +3. Repeat at +6 dB input gain, then compensate that gain. Fuse high-frequency magnitudes with the baseline phase and shared stereo gain, transitioning at 3–5 kHz and smoothing the gain envelope over 12 ms. +4. Infer eight shifted contexts (0, 110, 220, 330, 55, 165, 275, 385 samples), each with both polarities and 2 seconds of neighboring context. Median consensus corrects the band below 3 kHz. +5. Apply input-guided phase stabilization below 3 kHz, bounded to 0.15 rad and gated around attacks. The reference guides phase; dry waveform/magnitude is not mixed in by this step. +6. Apply the final texture-softening stage (2 dB cap), then overlap-add restored windows. + +This requires 20 neural forward passes per output window. It changes inference policy and pre/post-processing, not learned weights. It can alter wanted musical content and is not a validated artifact detector. The +6 dB branch is internally gain-compensated, not a simple playback-volume boost. + +## Reproduce + +Use an Apollo checkout containing the compatible look2hear model architecture and its dependencies, plus scipy, soundfile and numpy. The model used is the official checkpoint SHA-256 99d9af7f1ff20e63c393035513a655392818d66b4d7fc23d658175c1f15e8d76 (revision c68bd80fdd9c0d93d2f4a833cb154624f660a561). Use upstream e84bcacc59d5455f05d86a5c97dd4aeb3c14dbb6 for the model checkout. Checkpoint weights are not bundled. + +`python reproduce.py --apollo-root /path/to/Apollo --checkpoint /path/to/pytorch_model.bin --input /path/to/Untitled1017.wav --output enhanced.wav --device mps` + +`python -m unittest discover -s tests -p 'test_*.py'` + +The source snapshot is the current application DSP, preserved byte-for-byte. The wrapper calls restore_region directly with a fixed checkpoint and omits GUI, resource throttling, manual research settings and export processing. Device/thread performance can differ from the app. Runtime results are not physical listening evidence. + +## Attribution and scope + +Apollo model and original architecture: JusperLee/Apollo, https://github.com/JusperLee/Apollo. Additional inference/fusion/stabilization code: Neuracoust. Source distributed under CC BY-SA 4.0; see LICENSE. Audio has separate provenance and creator permission; no blanket audio license is asserted. The creator explicitly authorized public sharing of this Suno song and comparison outputs on 2026-10-17. diff --git a/evaluation/full-enhancement-method/SHA256.json b/evaluation/full-enhancement-method/SHA256.json new file mode 100644 index 0000000..83d73b9 --- /dev/null +++ b/evaluation/full-enhancement-method/SHA256.json @@ -0,0 +1,14 @@ +{ + "LICENSE": "eb8364a4474d5b6e207f3800c75b04070dde1f4ea9c210c38cd836f7a3f24203", + "README.md": "ede8be303771add17016ced7a5d5cf4c132c1cd467fe4684bfaa9dd92fe1093b", + "reproduce.py": "b5fe7a3d249851bbfacd154714d38611db6592d424f560845596755ce376ac34", + "tests.log": "d604981d971e6b563efd3d4c03a57a8dc15813336d519cc6d9d85061ac9fd60b", + "source/apollo_enhanced.py": "7a46b1bc8be70954641b87f3f974b891d9e99853ac9be362de086de807f73c1f", + "source/apollo_gain_fusion.py": "5d971695be03b7f31ef9eed6f62a941abe073a9a55587a80904c7720777dee32", + "source/apollo_phase_stability.py": "97f4687413542b0a1054629d9eeb73f666b0009ef089a4a6847982891764d22f", + "source/apollo_texture_softening.py": "dcbcda936416e2a04d6d00df5febbbfabe30382ee31fcba620a6aaf41ca8be4c", + "tests/test_apollo_enhanced.py": "5ed2876f08f14efe3fc0e8a8b21b87f265da122a8084443ed20814f14562a877", + "tests/test_apollo_gain_fusion.py": "a0c38aec851172b0647c6b78fc6699543b25015ee78e6a5329a6e2d3615b6d44", + "tests/test_apollo_phase_stability.py": "7bb819d0255e7bc37ca4cb95cb7e995719a50037f483c97c41a51dd255cfcc5a", + "tests/test_apollo_texture_softening.py": "d418e887e3d2c3a4f299acf8cc89ed1f5076149afdaf9896d16d8f5b002d4833" +} \ No newline at end of file diff --git a/evaluation/full-enhancement-method/reproduce.py b/evaluation/full-enhancement-method/reproduce.py new file mode 100644 index 0000000..02591fd --- /dev/null +++ b/evaluation/full-enhancement-method/reproduce.py @@ -0,0 +1,20 @@ +import argparse,sys,math,hashlib,time,json +from pathlib import Path +p=argparse.ArgumentParser() +p.add_argument('--apollo-root',type=Path,required=True);p.add_argument('--checkpoint',type=Path,required=True);p.add_argument('--input',type=Path,required=True);p.add_argument('--output',type=Path,required=True);p.add_argument('--device',choices=['mps','cpu','cuda'],default='mps');a=p.parse_args() +assert hashlib.sha256(a.checkpoint.read_bytes()).hexdigest()=='99d9af7f1ff20e63c393035513a655392818d66b4d7fc23d658175c1f15e8d76','Unexpected checkpoint' +sys.path.insert(0,str(a.apollo_root));sys.path.insert(0,str(Path(__file__).parent/'source'));sys.modules['yaml._yaml']=None +import numpy as np,torch,soundfile as sf +from scipy.signal import resample_poly +import look2hear.models +from apollo_enhanced import restore_region +x,sr=sf.read(a.input,dtype='float32',always_2d=True) +if sr!=44100:x=resample_poly(x,44100//math.gcd(sr,44100),sr//math.gcd(sr,44100),axis=0).astype(np.float32) +torch.set_num_threads(4);device=torch.device(a.device) +m=look2hear.models.BaseModel.from_pretrain(str(a.checkpoint),sr=44100,win=20,feature_dim=256,layer=6).to(device).eval() +def forward(v): + with torch.no_grad():return m(torch.from_numpy(v).unsqueeze(0).to(device)).squeeze(0).cpu().numpy() +t=time.perf_counter();y=restore_region(x.T,0,len(x),forward) +assert y.shape==x.T.shape and np.isfinite(y).all() +a.output.parent.mkdir(parents=True,exist_ok=True);sf.write(a.output,y.T,44100,subtype='FLOAT') +print(json.dumps({'seconds':time.perf_counter()-t,'frames':len(x),'sample_rate':44100,'device':str(device),'torch':torch.__version__})) diff --git a/evaluation/full-enhancement-method/source/apollo_enhanced.py b/evaluation/full-enhancement-method/source/apollo_enhanced.py new file mode 100644 index 0000000..79dcefd --- /dev/null +++ b/evaluation/full-enhancement-method/source/apollo_enhanced.py @@ -0,0 +1,64 @@ +"""Approved comparison chain, opt-in independently of original Apollo modes.""" +import time +import numpy as np +from apollo_gain_fusion import fuse_preserving_phase, stabilize_vocal_band +from apollo_phase_stability import stabilize_phase +from apollo_texture_softening import soften_texture + + +def restore_region(audio, start, end, forward, progress=None, sample_rate=44100): + if sample_rate != 44100 or audio.ndim != 2 or not 0 <= start < end <= audio.shape[1]: + raise ValueError('Expected 44.1 kHz channels-by-frames region') + if not np.isfinite(audio).all(): + raise ValueError('Nonfinite audio') + # Bound model calls for long selections; overlap only restored predictions. + chunk, overlap = 10*sample_rate, sample_rate + positions = list(range(start, end, chunk-overlap)) + if len(positions)>1 and positions[-2]+chunk>=end: positions.pop() + out=np.zeros((audio.shape[0],end-start),dtype=np.float32) + weights=np.zeros(end-start,dtype=np.float32) + count=len(positions)*20; done=0 + def infer(segment): + nonlocal done + if progress: progress(done/count) + y=forward(np.ascontiguousarray(segment,dtype=np.float32)) + if y.shape != segment.shape or not np.isfinite(y).all(): + raise ValueError('Invalid Apollo prediction') + done+=1 + if progress: progress(done/count) + return y + def segment(lo,hi): + v=audio[:,max(0,lo):min(audio.shape[1],hi)] + return np.pad(v,((0,0),(max(0,-lo),max(0,hi-audio.shape[1]))),mode='reflect' if v.shape[1]>1 else 'edge') + for i,lo in enumerate(positions): + hi=min(lo+chunk,end); n=hi-lo + # Baseline and +6 dB prediction: exact one-second context used in trials. + a=segment(lo-sample_rate,hi+sample_rate) + base=.5*(infer(a)-infer(-a))[:,sample_rate:sample_rate+n] + gain=10**(6/20); boosted=.5*(infer(a*gain)-infer(-a*gain))[:,sample_rate:sample_rate+n]/gain + predictions=[] + for shift in (0,110,220,330,55,165,275,385): + v=segment(lo-2*sample_rate-shift,hi+2*sample_rate-shift) + y=.5*(infer(v)-infer(-v));offset=2*sample_rate+shift + predictions.append(y[:,offset:offset+n]) + y=fuse_preserving_phase(base,boosted,smoothing_ms=12) + y=stabilize_vocal_band(y,base,np.asarray(predictions),cutoff_hz=3000) + y=stabilize_phase(audio[:,lo:hi],y) + y,_=soften_texture(y) + w=np.ones(n,dtype=np.float32) + if i: w[:min(overlap,n)]=np.linspace(0,1,min(overlap,n)) + if hinp.maximum(np.sum(abs(B)**2,axis=0)*.04,1e-12) + # Protect strong rising attacks across all channels. + energy=np.sum(abs(A)**2,axis=(0,1));attack=np.r_[False,energy[1:]>1.5*np.maximum(energy[:-1],1e-15)] + gate=1-gaussian_filter1d(attack.astype(float),1,mode='nearest')/max(gaussian_filter1d(np.array([0.,0,1,0,0]),1).max(),1e-9);gate=np.clip(gate,0,1) + change=np.clip(turn,-.15,.15)*w[:,None]*support*gate[None,:] + _,z=istft(B*np.exp(1j*change),**kw);z=z[:,pad:pad+b.shape[1]] + return z.astype(np.float32) diff --git a/evaluation/full-enhancement-method/source/apollo_texture_softening.py b/evaluation/full-enhancement-method/source/apollo_texture_softening.py new file mode 100644 index 0000000..f914121 --- /dev/null +++ b/evaluation/full-enhancement-method/source/apollo_texture_softening.py @@ -0,0 +1,35 @@ +"""Offline candidate: bounded temporal spectral excursion reduction, no dry input. +Not a defect classifier. Musical fluctuations may also trigger the detector. +""" +import numpy as np +from scipy.signal import stft, istft +from scipy.ndimage import median_filter, gaussian_filter1d, maximum_filter1d + + +def soften_texture(audio, sample_rate=44100, max_reduction_db=2.0): + x = np.asarray(audio) + if x.ndim != 2 or x.shape[-1] < 2048 or not np.isfinite(x).all(): + raise ValueError('Expected finite channels-by-frames audio, at least 2048 frames') + if not np.isfinite(sample_rate) or sample_rate < 16000 or not 0 <= max_reduction_db <= 3: + raise ValueError('Unsupported rate or attenuation') + kw = dict(fs=sample_rate, nperseg=1024, noverlap=768) + f, _, z = stft(np.pad(x, ((0,0),(1024,1024)), mode='reflect'), **kw) + power = np.mean(abs(z)**2, axis=0) + level = 10*np.log10(np.maximum(power, 1e-16)) + # About 40 ms neighborhood: only positive, short-lived excursions. + frames = max(3, int(round(.04*sample_rate/256)) | 1) + baseline = median_filter(level, size=(1,frames), mode='nearest') + excess = np.maximum(level-baseline-3, 0) + # Broad attack gate protects simultaneous rising energy (not guaranteed perfect). + energy = power.sum(axis=0) + attack = np.r_[False, energy[1:] > 1.4*np.maximum(energy[:-1],1e-16)] + gate = 1-maximum_filter1d(attack.astype(float), size=5) + band = np.clip((f-1200)/800,0,1)*np.clip((14000-f)/4000,0,1) + active = power > max(float(power.max())*1e-7,1e-15) + reduction = np.minimum(excess*.35,max_reduction_db)*band[:,None]*gate*active + reduction = gaussian_filter1d(gaussian_filter1d(reduction,.7,axis=0),.8,axis=1) + _, y = istft(z*10**(-reduction[None,:,:]/20), **kw) + y = y[:,1024:1024+x.shape[-1]].astype(np.float32) + return y, {'max_reduction_db':float(reduction.max()), + 'power_weighted_reduction_db':float((reduction*power).sum()/max(power.sum(),1e-30)), + 'attack_frame_fraction':float(np.mean(gate==0))} diff --git a/evaluation/full-enhancement-method/tests.log b/evaluation/full-enhancement-method/tests.log new file mode 100644 index 0000000..9121bd9 --- /dev/null +++ b/evaluation/full-enhancement-method/tests.log @@ -0,0 +1,5 @@ +................... +---------------------------------------------------------------------- +Ran 19 tests in 0.277s + +OK diff --git a/evaluation/full-enhancement-method/tests/test_apollo_enhanced.py b/evaluation/full-enhancement-method/tests/test_apollo_enhanced.py new file mode 100644 index 0000000..4572b1a --- /dev/null +++ b/evaluation/full-enhancement-method/tests/test_apollo_enhanced.py @@ -0,0 +1,26 @@ +import sys,unittest +from pathlib import Path +from unittest.mock import patch +import numpy as np +sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'source')) +import apollo_enhanced as e +class EnhancedTests(unittest.TestCase): + def test_chunk_boundary_and_file_edges(self): + x=np.tile((.1*np.sin(2*np.pi*220*np.arange(12*44100)/44100)).astype('float32'),(2,1));progress=[] + with patch.object(e,'fuse_preserving_phase',side_effect=lambda b,c,**k:b),patch.object(e,'stabilize_vocal_band',side_effect=lambda a,b,p,**k:a),patch.object(e,'stabilize_phase',side_effect=lambda a,b:b),patch.object(e,'soften_texture',side_effect=lambda a:(a,{})): + y=e.restore_region(x,0,x.shape[1],lambda v:v,progress.append) + np.testing.assert_allclose(y,x,atol=1e-7) + self.assertEqual(progress[-1],1) + self.assertTrue(all(a<=b for a,b in zip(progress,progress[1:]))) + def test_cancel_between_model_calls(self): + def cancel(p): + if p>0:raise RuntimeError('cancel') + with self.assertRaisesRegex(RuntimeError,'cancel'): + e.restore_region(np.zeros((2,44100),dtype='float32'),0,44100,lambda x:x,cancel) + def test_real_neighbor_context_and_gain(self): + x=np.random.default_rng(1).normal(0,.01,(2,5*44100)).astype('float32');calls=[] + def forward(v):calls.append(v.copy());return v + e.restore_region(x,2*44100,3*44100,forward) + self.assertEqual(len(calls),20) + np.testing.assert_array_equal(calls[0],x[:,44100:4*44100]) + np.testing.assert_allclose(calls[2],calls[0]*10**(.3)) diff --git a/evaluation/full-enhancement-method/tests/test_apollo_gain_fusion.py b/evaluation/full-enhancement-method/tests/test_apollo_gain_fusion.py new file mode 100644 index 0000000..f620109 --- /dev/null +++ b/evaluation/full-enhancement-method/tests/test_apollo_gain_fusion.py @@ -0,0 +1,90 @@ +import sys +from pathlib import Path +import unittest +import numpy as np + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "source")) +from apollo_gain_fusion import fuse_predictions, fuse_preserving_phase, stabilize_vocal_band + + +class GainFusionTests(unittest.TestCase): + def test_eight_pass_consensus_rejects_two_outliers(self): + t = np.arange(44100)/44100 + base = np.array([.1*np.sin(2*np.pi*300*t)]*2, dtype=np.float32) + approved = base+.03*np.sin(2*np.pi*8000*t) + grids = np.stack([base.copy() for _ in range(8)]) + grids[1, :, 10000:20000] += .8 + grids[4, :, 10000:20000] -= .6 + np.testing.assert_allclose(stabilize_vocal_band(approved, base, grids, cutoff_hz=3000), + approved, atol=1e-7) + + def test_extended_midband_keeps_upper_tone_and_rejects_bad_cutoff(self): + t = np.arange(44100)/44100 + mid = .1*np.sin(2*np.pi*2300*t) + high = .03*np.sin(2*np.pi*8000*t) + base = np.array([mid, mid], dtype=np.float32) + approved = base+high + grids = np.stack([base*.9 for _ in range(4)]) + z = stabilize_vocal_band(approved, base, grids, cutoff_hz=3000) + # The 8 kHz component is retained, while coherent 2.3 kHz correction + # is stronger than the previous 1.8 kHz setting. + section = slice(4410, -4410) + amplitude = 2*np.mean(z[0,section]*np.sin(2*np.pi*8000*t[section])) + self.assertAlmostEqual(amplitude, .03, places=5) + with self.assertRaises(ValueError): + stabilize_vocal_band(approved, base, grids, cutoff_hz=10000) + + def test_single_bad_grid_does_not_pollute_approved_output(self): + t = np.arange(44100)/44100 + base = np.array([.1*np.sin(2*np.pi*300*t)]*2, dtype=np.float32) + approved = base + .03*np.sin(2*np.pi*8000*t) + grids = np.stack([base.copy() for _ in range(4)]) + grids[1, :, 10000:20000] += .8 + np.testing.assert_allclose(stabilize_vocal_band(approved, base, grids), + approved, atol=1e-7) + + def test_consensus_low_band_correction_keeps_high_detail(self): + t = np.arange(44100)/44100 + base = np.array([.1*np.sin(2*np.pi*300*t)]*2, dtype=np.float32) + high = .03*np.sin(2*np.pi*8000*t) + approved = base+high + grids = np.stack([base*.8 for _ in range(4)]) + result = stabilize_vocal_band(approved, base, grids) + np.testing.assert_allclose(result[:,3000:-3000], (base*.8+high)[:,3000:-3000], atol=1e-6) + + def test_phase_fusion_keeps_stereo_relation_and_length(self): + t = np.arange(44101)/44100 + tone = np.sin(2*np.pi*8000*t) + base = np.array([tone, -.3*tone], dtype=np.float32) + alternate = np.array([.5*tone, .5*tone], dtype=np.float32) + result = fuse_preserving_phase(base, alternate) + self.assertEqual(result.shape, base.shape) + np.testing.assert_allclose(result[1], -.3*result[0], atol=2e-6) + self.assertTrue(np.isfinite(result).all()) + + def test_phase_fusion_silence_and_identity(self): + x = np.random.default_rng(2).normal(size=(2, 4097)).astype(np.float32) + np.testing.assert_allclose(fuse_preserving_phase(x, x), x, atol=1e-6) + z = np.zeros((2, 40), dtype=np.float32) + np.testing.assert_array_equal(fuse_preserving_phase(z, z), z) + + def test_band_routing_and_stereo(self): + t = np.arange(44100) / 44100 + low, high = np.sin(2*np.pi*200*t), np.sin(2*np.pi*12000*t) + base = np.array([low + high, .5*low - high], dtype=np.float32) + alt = np.array([.1*low + .7*high, .1*low - .7*high], dtype=np.float32) + result = fuse_predictions(base, alt)[:, 4410:-4410] + expected = np.array([low+.7*high, .5*low-.7*high])[:, 4410:-4410] + self.assertLess(np.max(abs(result-expected)), .001) + + def test_identity_and_validation(self): + x = np.random.default_rng(1).normal(size=(2, 1000)).astype(np.float32) + np.testing.assert_array_equal(fuse_predictions(x, x), x) + with self.assertRaises(ValueError): + fuse_predictions(x, x[:, :-1]) + with self.assertRaises(ValueError): + fuse_predictions(x, x * np.nan) + + +if __name__ == "__main__": + unittest.main() diff --git a/evaluation/full-enhancement-method/tests/test_apollo_phase_stability.py b/evaluation/full-enhancement-method/tests/test_apollo_phase_stability.py new file mode 100644 index 0000000..5f2cf45 --- /dev/null +++ b/evaluation/full-enhancement-method/tests/test_apollo_phase_stability.py @@ -0,0 +1,27 @@ +import sys,unittest +from pathlib import Path +import numpy as np +sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'source')) +from apollo_phase_stability import stabilize_phase + +class PhaseStabilityTests(unittest.TestCase): + def test_no_original_amplitude_blend(self): + t=np.arange(44101)/44100 + a=np.array([.1*np.sin(2*np.pi*440*t)]*2,dtype=np.float32) + b=a*2 + np.testing.assert_allclose(stabilize_phase(a,b),b,atol=2e-7) + def test_common_rotation_keeps_stereo_relation(self): + t=np.arange(44100)/44100 + a=np.array([.1*np.sin(2*np.pi*440*t)]*2,dtype=np.float32) + carrier=.1*np.sin(2*np.pi*440*t+.08*np.sin(2*np.pi*40*t)) + b=np.array([carrier,-.4*carrier],dtype=np.float32) + z=stabilize_phase(a,b) + self.assertEqual(z.shape,b.shape) + np.testing.assert_allclose(z[1],-.4*z[0],atol=2e-7) + def test_silent_reference_does_not_erase_output(self): + b=np.random.default_rng(1).normal(0,.01,(2,4097)).astype(np.float32) + np.testing.assert_allclose(stabilize_phase(b*0,b),b,atol=1e-7) + def test_bad_alignment_rejected(self): + with self.assertRaises(ValueError):stabilize_phase(np.zeros((2,100)),np.zeros((2,101))) + +if __name__=='__main__':unittest.main() diff --git a/evaluation/full-enhancement-method/tests/test_apollo_texture_softening.py b/evaluation/full-enhancement-method/tests/test_apollo_texture_softening.py new file mode 100644 index 0000000..fb47977 --- /dev/null +++ b/evaluation/full-enhancement-method/tests/test_apollo_texture_softening.py @@ -0,0 +1,20 @@ +import unittest +import numpy as np +from engine.apollo_texture_softening import soften_texture +class TextureTests(unittest.TestCase): + def test_bypass(self): + x=np.random.default_rng(4).normal(0,.1,(2,16000)).astype('float32') + y,d=soften_texture(x,max_reduction_db=0) + np.testing.assert_allclose(x,y,atol=1e-7) + def test_steady_tone_and_stereo(self): + a=.2*np.sin(2*np.pi*3000*np.arange(44100)/44100);x=np.array([a,-.3*a],dtype='float32') + y,d=soften_texture(x) + np.testing.assert_allclose(y[1],-.3*y[0],atol=1e-7) + self.assertLess(np.sqrt(np.mean((x[:,2048:-2048]-y[:,2048:-2048])**2)),1e-5) + def test_silence(self): + y,d=soften_texture(np.zeros((2,4410))) + self.assertEqual(float(abs(y).max()),0) + def test_bounded(self): + x=np.random.default_rng(7).normal(0,.1,(2,16000));y,d=soften_texture(x) + self.assertLessEqual(d['max_reduction_db'],2) + self.assertEqual(y.shape,x.shape) From 14b9e53ada493ba433f7f2e7f1ddbf973503721f Mon Sep 17 00:00:00 2001 From: hemanpapa-coder Date: Tue, 20 Oct 2026 17:18:28 +0900 Subject: [PATCH 6/6] Narrow PR 44 to optional texture processor and focused tests Keep full enhancement experiments and larger evaluation materials in the fork's prior history and Releases. Retain concise texture-only documentation and only the SciPy addition in the upstream CPU workflow. --- .github/workflows/cpu-unit-tests.yml | 3 +- TEXTURE_SOFTENING.md | 100 +-- evaluation/full-enhancement-method/LICENSE | 86 --- evaluation/full-enhancement-method/README.md | 28 - .../full-enhancement-method/SHA256.json | 14 - .../full-enhancement-method/reproduce.py | 20 - .../source/apollo_enhanced.py | 64 -- .../source/apollo_gain_fusion.py | 80 -- .../source/apollo_phase_stability.py | 27 - .../source/apollo_texture_softening.py | 35 - evaluation/full-enhancement-method/tests.log | 5 - .../tests/test_apollo_enhanced.py | 26 - .../tests/test_apollo_gain_fusion.py | 90 --- .../tests/test_apollo_phase_stability.py | 27 - .../tests/test_apollo_texture_softening.py | 20 - evaluation/texture-20261016/REPORT.md | 77 -- evaluation/texture-20261016/SHA256.json | 122 --- .../texture-20261016/audition-checks.json | 42 -- evaluation/texture-20261016/fork-ci.json | 7 - evaluation/texture-20261016/measurements.json | 692 ------------------ .../texture-20261016/memory-apollo-180.json | 21 - .../texture-20261016/memory-music-30.json | 21 - evaluation/texture-20261016/sources.json | 46 -- .../texture-20261016/system-unit-tests.log | 41 -- tests/benchmark_texture_softening.py | 51 -- tests/build_texture_music_comparison.py | 136 ---- tests/measure_texture_memory.py | 50 -- 27 files changed, 40 insertions(+), 1891 deletions(-) delete mode 100644 evaluation/full-enhancement-method/LICENSE delete mode 100644 evaluation/full-enhancement-method/README.md delete mode 100644 evaluation/full-enhancement-method/SHA256.json delete mode 100644 evaluation/full-enhancement-method/reproduce.py delete mode 100644 evaluation/full-enhancement-method/source/apollo_enhanced.py delete mode 100644 evaluation/full-enhancement-method/source/apollo_gain_fusion.py delete mode 100644 evaluation/full-enhancement-method/source/apollo_phase_stability.py delete mode 100644 evaluation/full-enhancement-method/source/apollo_texture_softening.py delete mode 100644 evaluation/full-enhancement-method/tests.log delete mode 100644 evaluation/full-enhancement-method/tests/test_apollo_enhanced.py delete mode 100644 evaluation/full-enhancement-method/tests/test_apollo_gain_fusion.py delete mode 100644 evaluation/full-enhancement-method/tests/test_apollo_phase_stability.py delete mode 100644 evaluation/full-enhancement-method/tests/test_apollo_texture_softening.py delete mode 100644 evaluation/texture-20261016/REPORT.md delete mode 100644 evaluation/texture-20261016/SHA256.json delete mode 100644 evaluation/texture-20261016/audition-checks.json delete mode 100644 evaluation/texture-20261016/fork-ci.json delete mode 100644 evaluation/texture-20261016/measurements.json delete mode 100644 evaluation/texture-20261016/memory-apollo-180.json delete mode 100644 evaluation/texture-20261016/memory-music-30.json delete mode 100644 evaluation/texture-20261016/sources.json delete mode 100644 evaluation/texture-20261016/system-unit-tests.log delete mode 100644 tests/benchmark_texture_softening.py delete mode 100644 tests/build_texture_music_comparison.py delete mode 100644 tests/measure_texture_memory.py diff --git a/.github/workflows/cpu-unit-tests.yml b/.github/workflows/cpu-unit-tests.yml index 5a62316..449af90 100644 --- a/.github/workflows/cpu-unit-tests.yml +++ b/.github/workflows/cpu-unit-tests.yml @@ -2,9 +2,8 @@ name: CPU unit tests on: pull_request: - workflow_dispatch: push: - branches: [main, experimental/optional-texture-softening] + branches: [main] jobs: unit-tests: diff --git a/TEXTURE_SOFTENING.md b/TEXTURE_SOFTENING.md index fffa303..78fdbe3 100644 --- a/TEXTURE_SOFTENING.md +++ b/TEXTURE_SOFTENING.md @@ -1,61 +1,39 @@ -# Experimental texture softening - -Contribution from Neuracoust, adapted from its Sound Build-up post-processing. -Apollo remains by Kai Li and Yi Luo / Look2Hear. Existing LICENSE (CC BY-SA 4.0) -applies. This does not modify model weights or claim a new restoration model. - -`python inference.py --in_wav=input.wav --out_wav=output.wav --texture-reduction-db=2` - -The default is 0: the existing inference path and output remain unchanged. -The optional CPU NumPy/SciPy post-process runs once after full-file or chunked -inference. Optional SciPy is imported before audio/model loading; missing SciPy -raises an actionable error before checkpoint download. Inputs shorter than -2048 frames are rejected before checkpoint download/model loading only when -the option is enabled. The disabled path retains short-clip behavior. It needs -at least 2048 output frames. It adds no model forward passes, -but holds a full-output STFT in memory, so chunked model inference no longer -implies bounded post-processing memory. SciPy is required only when enabled. - -The shared channel gain targets short positive spectral excursions relative to -a roughly 40 ms temporal median. It tapers between 1.2 and 14 kHz, limits gain -reduction to the requested 0–3 dB cap, and gates broad rising-energy frames. -Thresholds are engineering heuristics, not an artifact or vocal detector. -Normal musical modulation can trigger it; attacks can still be affected. -The cap describes STFT gain, not a bound on every time-domain sample difference. -It can reduce wanted detail and is deliberately opt-in. - -## Validation and limits - -Run `python -m unittest discover -s tests -p 'test_texture_softening.py' -v`. -Integration tests additionally verify that the disabled path never calls the -post-process, the enabled path writes its exact result, and the source WAV is -unchanged. Tests check exact zero-strength bypass, stationary tone preservation, common -stereo scaling including polarity, silence, exact odd output length, invalid -input, and a positive modulation case where processing actually engages. - -Run `python tests/benchmark_texture_softening.py --output-dir=texture-comparison` -to generate deterministic synthetic before/after FLOAT WAVs, equal-RMS audition -copies with common peak headroom, SHA-256 provenance and timing JSON. No Apollo -checkpoint, private song, or external download is needed. This tests the -post-process only, not an improvement in Apollo restoration quality. - -Prior local music experiments on a larger combined inference chain found local -maximum reductions of 1.38–1.80 dB and overall RMS reduction below 0.0011 dB. -High-band reference error increased 0.012–0.065 dB and waveform SNR was mixed. -Those private fixtures are not included, and those observations do not isolate -this proposal against current upstream Apollo. There is no controlled listening -study or evidence of universal vocal/piano improvement. Wider upstream-model, -music, transient, backend and listening evaluation remains open. This is a draft -for maintainer discussion, not a recommendation to enable by default. - -Local synthetic benchmark (macOS 26.5.1 arm64, Python 3.9.6, NumPy 2.0.2, -SciPy 1.13.1): 4 s stereo took 0.0301 / 0.0250 / 0.0250 s for the -post-process alone; max STFT gain reduction 1.9105 dB. Timing is hardware and -fixture dependent. Equal-RMS audition copies matched RMS within 0.000001 dB. - -## PR44 follow-up - -Early dependency/short-input checks, pinned CPU CI, real-music comparison -methodology and song-length memory evidence are documented in -[evaluation/texture-20261016/REPORT.md](evaluation/texture-20261016/REPORT.md). -The feature remains opt-in and the PR remains Draft pending listening. +# Optional experimental texture softening + +Apollo remains by Kai Li and Yi Luo / Look2Hear. This Neuracoust contribution +is adapted from Sound Build-up post-processing; the repository CC BY-SA 4.0 +license and upstream attribution apply. It changes neither weights nor training. + +```sh +python inference.py --in_wav=input.wav --out_wav=output.wav --texture-reduction-db=2 +``` + +The option defaults to **0 (off)** and accepts 0–3 dB. When enabled, a +stereo-linked NumPy/SciPy processor runs once on the existing Apollo prediction, +after either full-file or chunked inference; it adds no model forward passes. +Install SciPy in the inference environment (`python -m pip install scipy`). +Enabled processing checks SciPy before audio/checkpoint loading and rejects +inputs shorter than 2048 frames before checkpoint/model loading. With the +option disabled, SciPy is not required and the existing short-input path remains. + +The processor attenuates short positive spectral excursions against a roughly +40 ms temporal median, tapering between 1.2 and 14 kHz with a broad attack gate. +The cap bounds STFT gain reduction, not every time-domain sample difference. +This is a heuristic, not an artifact/vocal detector: wanted detail, modulation, +and transients can be suppressed. Keep this experimental and opt-in. + +A full-output STFT makes memory **O(duration)** even with chunked model +inference. Historical model-excluded probes measured 388.74 MiB tracked peak / +648.77 MiB process RSS for 30 s, and 2328.58 / 3042.25 MiB for a 180 s repeated +Apollo prediction. The latter is an allocation probe, not a full-song listening +test; these host-dependent figures are not new measurements of this review copy. + +Run `python -m unittest discover -s tests -v` for DSP and CLI regression tests. +The CLI tests mock model loading; they do not validate a checkpoint or listening +quality. [Existing texture-only audio bundle](https://github.com/hemanpapa-coder/Apollo/releases/tag/texture-evaluation-20261016) +and [fixed detailed evidence](https://github.com/hemanpapa-coder/Apollo/blob/f160a8a1e267626da2136fc20a487eef99f8cf28/evaluation/texture-20261016/REPORT.md) +provide eight aligned 15 s excerpts, raw 0/1/2/3 dB WAVs, loudness-matched blind +copies, commands and provenance. Sources are lossy proxies, not lossless clean +references; controlled listening and maintainer evaluation remain outstanding. +No universal quality improvement is claimed. The full enhancement chain is +outside this feature's scope and remains separate fork experiment material. diff --git a/evaluation/full-enhancement-method/LICENSE b/evaluation/full-enhancement-method/LICENSE deleted file mode 100644 index 8445ce6..0000000 --- a/evaluation/full-enhancement-method/LICENSE +++ /dev/null @@ -1,86 +0,0 @@ -Attribution-ShareAlike 4.0 International -By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. - -Section 1 – Definitions. -Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. -Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. -BY-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses , approved by Creative Commons as essentially the equivalent of this Public License. -Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. -Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. -Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. -License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution and ShareAlike. -Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. -Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. -Licensor means the individual(s) or entity(ies) granting rights under this Public License. -Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. -Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. -You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. -Section 2 – Scope. -License grant . -Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: -reproduce and Share the Licensed Material, in whole or in part; and -produce, reproduce, and Share Adapted Material. -Exceptions and Limitations . For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. -Term . The term of this Public License is specified in Section 6(a) . -Media and formats; technical modifications allowed . The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. -Downstream recipients . -Offer from the Licensor – Licensed Material . Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. -Additional offer from the Licensor – Adapted Material . Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter’s License You apply. -No downstream restrictions . You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. -No endorsement . Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i) . -Other rights . -Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. -Patent and trademark rights are not licensed under this Public License. -To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. -Section 3 – License Conditions. -Your exercise of the Licensed Rights is expressly made subject to the following conditions. - -Attribution . -If You Share the Licensed Material (including in modified form), You must: - -retain the following if it is supplied by the Licensor with the Licensed Material: -identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); -a copyright notice; -a notice that refers to this Public License; -a notice that refers to the disclaimer of warranties; -a URI or hyperlink to the Licensed Material to the extent reasonably practicable; -indicate if You modified the Licensed Material and retain an indication of any previous modifications; and -indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. -You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. -If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. -ShareAlike . -In addition to the conditions in Section 3(a) , if You Share Adapted Material You produce, the following conditions also apply. - -The Adapter’s License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-SA Compatible License. -You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. -You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. -Section 4 – Sui Generis Database Rights. -Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: - -for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; -if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b) ; and -You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. -For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. - -Section 5 – Disclaimer of Warranties and Limitation of Liability. -Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. -To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. -The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. -Section 6 – Term and Termination. -This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. -Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: - -automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or -upon express reinstatement by the Licensor. -For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. - -For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. -Sections 1 , 5 , 6 , 7 , and 8 survive termination of this Public License. -Section 7 – Other Terms and Conditions. -The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. -Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. -Section 8 – Interpretation. -For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. -To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. -No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. -Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. diff --git a/evaluation/full-enhancement-method/README.md b/evaluation/full-enhancement-method/README.md deleted file mode 100644 index 18f7f76..0000000 --- a/evaluation/full-enhancement-method/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Neuracoust full Apollo enhancement method - -This companion reproduces the application processing chain; PR #44 implements only the final optional texture-softening stage. The full application method is supplied for inspection and evaluation, not represented as already integrated into upstream inference.py. - -## Signal path - -1. Divide the selected audio into 10-second windows with 1-second output overlap. Real neighboring context is used; file edges use reflection padding. -2. Infer positive and negative polarity with 1-second context and combine (F(x)-F(-x))/2. -3. Repeat at +6 dB input gain, then compensate that gain. Fuse high-frequency magnitudes with the baseline phase and shared stereo gain, transitioning at 3–5 kHz and smoothing the gain envelope over 12 ms. -4. Infer eight shifted contexts (0, 110, 220, 330, 55, 165, 275, 385 samples), each with both polarities and 2 seconds of neighboring context. Median consensus corrects the band below 3 kHz. -5. Apply input-guided phase stabilization below 3 kHz, bounded to 0.15 rad and gated around attacks. The reference guides phase; dry waveform/magnitude is not mixed in by this step. -6. Apply the final texture-softening stage (2 dB cap), then overlap-add restored windows. - -This requires 20 neural forward passes per output window. It changes inference policy and pre/post-processing, not learned weights. It can alter wanted musical content and is not a validated artifact detector. The +6 dB branch is internally gain-compensated, not a simple playback-volume boost. - -## Reproduce - -Use an Apollo checkout containing the compatible look2hear model architecture and its dependencies, plus scipy, soundfile and numpy. The model used is the official checkpoint SHA-256 99d9af7f1ff20e63c393035513a655392818d66b4d7fc23d658175c1f15e8d76 (revision c68bd80fdd9c0d93d2f4a833cb154624f660a561). Use upstream e84bcacc59d5455f05d86a5c97dd4aeb3c14dbb6 for the model checkout. Checkpoint weights are not bundled. - -`python reproduce.py --apollo-root /path/to/Apollo --checkpoint /path/to/pytorch_model.bin --input /path/to/Untitled1017.wav --output enhanced.wav --device mps` - -`python -m unittest discover -s tests -p 'test_*.py'` - -The source snapshot is the current application DSP, preserved byte-for-byte. The wrapper calls restore_region directly with a fixed checkpoint and omits GUI, resource throttling, manual research settings and export processing. Device/thread performance can differ from the app. Runtime results are not physical listening evidence. - -## Attribution and scope - -Apollo model and original architecture: JusperLee/Apollo, https://github.com/JusperLee/Apollo. Additional inference/fusion/stabilization code: Neuracoust. Source distributed under CC BY-SA 4.0; see LICENSE. Audio has separate provenance and creator permission; no blanket audio license is asserted. The creator explicitly authorized public sharing of this Suno song and comparison outputs on 2026-10-17. diff --git a/evaluation/full-enhancement-method/SHA256.json b/evaluation/full-enhancement-method/SHA256.json deleted file mode 100644 index 83d73b9..0000000 --- a/evaluation/full-enhancement-method/SHA256.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "LICENSE": "eb8364a4474d5b6e207f3800c75b04070dde1f4ea9c210c38cd836f7a3f24203", - "README.md": "ede8be303771add17016ced7a5d5cf4c132c1cd467fe4684bfaa9dd92fe1093b", - "reproduce.py": "b5fe7a3d249851bbfacd154714d38611db6592d424f560845596755ce376ac34", - "tests.log": "d604981d971e6b563efd3d4c03a57a8dc15813336d519cc6d9d85061ac9fd60b", - "source/apollo_enhanced.py": "7a46b1bc8be70954641b87f3f974b891d9e99853ac9be362de086de807f73c1f", - "source/apollo_gain_fusion.py": "5d971695be03b7f31ef9eed6f62a941abe073a9a55587a80904c7720777dee32", - "source/apollo_phase_stability.py": "97f4687413542b0a1054629d9eeb73f666b0009ef089a4a6847982891764d22f", - "source/apollo_texture_softening.py": "dcbcda936416e2a04d6d00df5febbbfabe30382ee31fcba620a6aaf41ca8be4c", - "tests/test_apollo_enhanced.py": "5ed2876f08f14efe3fc0e8a8b21b87f265da122a8084443ed20814f14562a877", - "tests/test_apollo_gain_fusion.py": "a0c38aec851172b0647c6b78fc6699543b25015ee78e6a5329a6e2d3615b6d44", - "tests/test_apollo_phase_stability.py": "7bb819d0255e7bc37ca4cb95cb7e995719a50037f483c97c41a51dd255cfcc5a", - "tests/test_apollo_texture_softening.py": "d418e887e3d2c3a4f299acf8cc89ed1f5076149afdaf9896d16d8f5b002d4833" -} \ No newline at end of file diff --git a/evaluation/full-enhancement-method/reproduce.py b/evaluation/full-enhancement-method/reproduce.py deleted file mode 100644 index 02591fd..0000000 --- a/evaluation/full-enhancement-method/reproduce.py +++ /dev/null @@ -1,20 +0,0 @@ -import argparse,sys,math,hashlib,time,json -from pathlib import Path -p=argparse.ArgumentParser() -p.add_argument('--apollo-root',type=Path,required=True);p.add_argument('--checkpoint',type=Path,required=True);p.add_argument('--input',type=Path,required=True);p.add_argument('--output',type=Path,required=True);p.add_argument('--device',choices=['mps','cpu','cuda'],default='mps');a=p.parse_args() -assert hashlib.sha256(a.checkpoint.read_bytes()).hexdigest()=='99d9af7f1ff20e63c393035513a655392818d66b4d7fc23d658175c1f15e8d76','Unexpected checkpoint' -sys.path.insert(0,str(a.apollo_root));sys.path.insert(0,str(Path(__file__).parent/'source'));sys.modules['yaml._yaml']=None -import numpy as np,torch,soundfile as sf -from scipy.signal import resample_poly -import look2hear.models -from apollo_enhanced import restore_region -x,sr=sf.read(a.input,dtype='float32',always_2d=True) -if sr!=44100:x=resample_poly(x,44100//math.gcd(sr,44100),sr//math.gcd(sr,44100),axis=0).astype(np.float32) -torch.set_num_threads(4);device=torch.device(a.device) -m=look2hear.models.BaseModel.from_pretrain(str(a.checkpoint),sr=44100,win=20,feature_dim=256,layer=6).to(device).eval() -def forward(v): - with torch.no_grad():return m(torch.from_numpy(v).unsqueeze(0).to(device)).squeeze(0).cpu().numpy() -t=time.perf_counter();y=restore_region(x.T,0,len(x),forward) -assert y.shape==x.T.shape and np.isfinite(y).all() -a.output.parent.mkdir(parents=True,exist_ok=True);sf.write(a.output,y.T,44100,subtype='FLOAT') -print(json.dumps({'seconds':time.perf_counter()-t,'frames':len(x),'sample_rate':44100,'device':str(device),'torch':torch.__version__})) diff --git a/evaluation/full-enhancement-method/source/apollo_enhanced.py b/evaluation/full-enhancement-method/source/apollo_enhanced.py deleted file mode 100644 index 79dcefd..0000000 --- a/evaluation/full-enhancement-method/source/apollo_enhanced.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Approved comparison chain, opt-in independently of original Apollo modes.""" -import time -import numpy as np -from apollo_gain_fusion import fuse_preserving_phase, stabilize_vocal_band -from apollo_phase_stability import stabilize_phase -from apollo_texture_softening import soften_texture - - -def restore_region(audio, start, end, forward, progress=None, sample_rate=44100): - if sample_rate != 44100 or audio.ndim != 2 or not 0 <= start < end <= audio.shape[1]: - raise ValueError('Expected 44.1 kHz channels-by-frames region') - if not np.isfinite(audio).all(): - raise ValueError('Nonfinite audio') - # Bound model calls for long selections; overlap only restored predictions. - chunk, overlap = 10*sample_rate, sample_rate - positions = list(range(start, end, chunk-overlap)) - if len(positions)>1 and positions[-2]+chunk>=end: positions.pop() - out=np.zeros((audio.shape[0],end-start),dtype=np.float32) - weights=np.zeros(end-start,dtype=np.float32) - count=len(positions)*20; done=0 - def infer(segment): - nonlocal done - if progress: progress(done/count) - y=forward(np.ascontiguousarray(segment,dtype=np.float32)) - if y.shape != segment.shape or not np.isfinite(y).all(): - raise ValueError('Invalid Apollo prediction') - done+=1 - if progress: progress(done/count) - return y - def segment(lo,hi): - v=audio[:,max(0,lo):min(audio.shape[1],hi)] - return np.pad(v,((0,0),(max(0,-lo),max(0,hi-audio.shape[1]))),mode='reflect' if v.shape[1]>1 else 'edge') - for i,lo in enumerate(positions): - hi=min(lo+chunk,end); n=hi-lo - # Baseline and +6 dB prediction: exact one-second context used in trials. - a=segment(lo-sample_rate,hi+sample_rate) - base=.5*(infer(a)-infer(-a))[:,sample_rate:sample_rate+n] - gain=10**(6/20); boosted=.5*(infer(a*gain)-infer(-a*gain))[:,sample_rate:sample_rate+n]/gain - predictions=[] - for shift in (0,110,220,330,55,165,275,385): - v=segment(lo-2*sample_rate-shift,hi+2*sample_rate-shift) - y=.5*(infer(v)-infer(-v));offset=2*sample_rate+shift - predictions.append(y[:,offset:offset+n]) - y=fuse_preserving_phase(base,boosted,smoothing_ms=12) - y=stabilize_vocal_band(y,base,np.asarray(predictions),cutoff_hz=3000) - y=stabilize_phase(audio[:,lo:hi],y) - y,_=soften_texture(y) - w=np.ones(n,dtype=np.float32) - if i: w[:min(overlap,n)]=np.linspace(0,1,min(overlap,n)) - if hinp.maximum(np.sum(abs(B)**2,axis=0)*.04,1e-12) - # Protect strong rising attacks across all channels. - energy=np.sum(abs(A)**2,axis=(0,1));attack=np.r_[False,energy[1:]>1.5*np.maximum(energy[:-1],1e-15)] - gate=1-gaussian_filter1d(attack.astype(float),1,mode='nearest')/max(gaussian_filter1d(np.array([0.,0,1,0,0]),1).max(),1e-9);gate=np.clip(gate,0,1) - change=np.clip(turn,-.15,.15)*w[:,None]*support*gate[None,:] - _,z=istft(B*np.exp(1j*change),**kw);z=z[:,pad:pad+b.shape[1]] - return z.astype(np.float32) diff --git a/evaluation/full-enhancement-method/source/apollo_texture_softening.py b/evaluation/full-enhancement-method/source/apollo_texture_softening.py deleted file mode 100644 index f914121..0000000 --- a/evaluation/full-enhancement-method/source/apollo_texture_softening.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Offline candidate: bounded temporal spectral excursion reduction, no dry input. -Not a defect classifier. Musical fluctuations may also trigger the detector. -""" -import numpy as np -from scipy.signal import stft, istft -from scipy.ndimage import median_filter, gaussian_filter1d, maximum_filter1d - - -def soften_texture(audio, sample_rate=44100, max_reduction_db=2.0): - x = np.asarray(audio) - if x.ndim != 2 or x.shape[-1] < 2048 or not np.isfinite(x).all(): - raise ValueError('Expected finite channels-by-frames audio, at least 2048 frames') - if not np.isfinite(sample_rate) or sample_rate < 16000 or not 0 <= max_reduction_db <= 3: - raise ValueError('Unsupported rate or attenuation') - kw = dict(fs=sample_rate, nperseg=1024, noverlap=768) - f, _, z = stft(np.pad(x, ((0,0),(1024,1024)), mode='reflect'), **kw) - power = np.mean(abs(z)**2, axis=0) - level = 10*np.log10(np.maximum(power, 1e-16)) - # About 40 ms neighborhood: only positive, short-lived excursions. - frames = max(3, int(round(.04*sample_rate/256)) | 1) - baseline = median_filter(level, size=(1,frames), mode='nearest') - excess = np.maximum(level-baseline-3, 0) - # Broad attack gate protects simultaneous rising energy (not guaranteed perfect). - energy = power.sum(axis=0) - attack = np.r_[False, energy[1:] > 1.4*np.maximum(energy[:-1],1e-16)] - gate = 1-maximum_filter1d(attack.astype(float), size=5) - band = np.clip((f-1200)/800,0,1)*np.clip((14000-f)/4000,0,1) - active = power > max(float(power.max())*1e-7,1e-15) - reduction = np.minimum(excess*.35,max_reduction_db)*band[:,None]*gate*active - reduction = gaussian_filter1d(gaussian_filter1d(reduction,.7,axis=0),.8,axis=1) - _, y = istft(z*10**(-reduction[None,:,:]/20), **kw) - y = y[:,1024:1024+x.shape[-1]].astype(np.float32) - return y, {'max_reduction_db':float(reduction.max()), - 'power_weighted_reduction_db':float((reduction*power).sum()/max(power.sum(),1e-30)), - 'attack_frame_fraction':float(np.mean(gate==0))} diff --git a/evaluation/full-enhancement-method/tests.log b/evaluation/full-enhancement-method/tests.log deleted file mode 100644 index 9121bd9..0000000 --- a/evaluation/full-enhancement-method/tests.log +++ /dev/null @@ -1,5 +0,0 @@ -................... ----------------------------------------------------------------------- -Ran 19 tests in 0.277s - -OK diff --git a/evaluation/full-enhancement-method/tests/test_apollo_enhanced.py b/evaluation/full-enhancement-method/tests/test_apollo_enhanced.py deleted file mode 100644 index 4572b1a..0000000 --- a/evaluation/full-enhancement-method/tests/test_apollo_enhanced.py +++ /dev/null @@ -1,26 +0,0 @@ -import sys,unittest -from pathlib import Path -from unittest.mock import patch -import numpy as np -sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'source')) -import apollo_enhanced as e -class EnhancedTests(unittest.TestCase): - def test_chunk_boundary_and_file_edges(self): - x=np.tile((.1*np.sin(2*np.pi*220*np.arange(12*44100)/44100)).astype('float32'),(2,1));progress=[] - with patch.object(e,'fuse_preserving_phase',side_effect=lambda b,c,**k:b),patch.object(e,'stabilize_vocal_band',side_effect=lambda a,b,p,**k:a),patch.object(e,'stabilize_phase',side_effect=lambda a,b:b),patch.object(e,'soften_texture',side_effect=lambda a:(a,{})): - y=e.restore_region(x,0,x.shape[1],lambda v:v,progress.append) - np.testing.assert_allclose(y,x,atol=1e-7) - self.assertEqual(progress[-1],1) - self.assertTrue(all(a<=b for a,b in zip(progress,progress[1:]))) - def test_cancel_between_model_calls(self): - def cancel(p): - if p>0:raise RuntimeError('cancel') - with self.assertRaisesRegex(RuntimeError,'cancel'): - e.restore_region(np.zeros((2,44100),dtype='float32'),0,44100,lambda x:x,cancel) - def test_real_neighbor_context_and_gain(self): - x=np.random.default_rng(1).normal(0,.01,(2,5*44100)).astype('float32');calls=[] - def forward(v):calls.append(v.copy());return v - e.restore_region(x,2*44100,3*44100,forward) - self.assertEqual(len(calls),20) - np.testing.assert_array_equal(calls[0],x[:,44100:4*44100]) - np.testing.assert_allclose(calls[2],calls[0]*10**(.3)) diff --git a/evaluation/full-enhancement-method/tests/test_apollo_gain_fusion.py b/evaluation/full-enhancement-method/tests/test_apollo_gain_fusion.py deleted file mode 100644 index f620109..0000000 --- a/evaluation/full-enhancement-method/tests/test_apollo_gain_fusion.py +++ /dev/null @@ -1,90 +0,0 @@ -import sys -from pathlib import Path -import unittest -import numpy as np - -sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "source")) -from apollo_gain_fusion import fuse_predictions, fuse_preserving_phase, stabilize_vocal_band - - -class GainFusionTests(unittest.TestCase): - def test_eight_pass_consensus_rejects_two_outliers(self): - t = np.arange(44100)/44100 - base = np.array([.1*np.sin(2*np.pi*300*t)]*2, dtype=np.float32) - approved = base+.03*np.sin(2*np.pi*8000*t) - grids = np.stack([base.copy() for _ in range(8)]) - grids[1, :, 10000:20000] += .8 - grids[4, :, 10000:20000] -= .6 - np.testing.assert_allclose(stabilize_vocal_band(approved, base, grids, cutoff_hz=3000), - approved, atol=1e-7) - - def test_extended_midband_keeps_upper_tone_and_rejects_bad_cutoff(self): - t = np.arange(44100)/44100 - mid = .1*np.sin(2*np.pi*2300*t) - high = .03*np.sin(2*np.pi*8000*t) - base = np.array([mid, mid], dtype=np.float32) - approved = base+high - grids = np.stack([base*.9 for _ in range(4)]) - z = stabilize_vocal_band(approved, base, grids, cutoff_hz=3000) - # The 8 kHz component is retained, while coherent 2.3 kHz correction - # is stronger than the previous 1.8 kHz setting. - section = slice(4410, -4410) - amplitude = 2*np.mean(z[0,section]*np.sin(2*np.pi*8000*t[section])) - self.assertAlmostEqual(amplitude, .03, places=5) - with self.assertRaises(ValueError): - stabilize_vocal_band(approved, base, grids, cutoff_hz=10000) - - def test_single_bad_grid_does_not_pollute_approved_output(self): - t = np.arange(44100)/44100 - base = np.array([.1*np.sin(2*np.pi*300*t)]*2, dtype=np.float32) - approved = base + .03*np.sin(2*np.pi*8000*t) - grids = np.stack([base.copy() for _ in range(4)]) - grids[1, :, 10000:20000] += .8 - np.testing.assert_allclose(stabilize_vocal_band(approved, base, grids), - approved, atol=1e-7) - - def test_consensus_low_band_correction_keeps_high_detail(self): - t = np.arange(44100)/44100 - base = np.array([.1*np.sin(2*np.pi*300*t)]*2, dtype=np.float32) - high = .03*np.sin(2*np.pi*8000*t) - approved = base+high - grids = np.stack([base*.8 for _ in range(4)]) - result = stabilize_vocal_band(approved, base, grids) - np.testing.assert_allclose(result[:,3000:-3000], (base*.8+high)[:,3000:-3000], atol=1e-6) - - def test_phase_fusion_keeps_stereo_relation_and_length(self): - t = np.arange(44101)/44100 - tone = np.sin(2*np.pi*8000*t) - base = np.array([tone, -.3*tone], dtype=np.float32) - alternate = np.array([.5*tone, .5*tone], dtype=np.float32) - result = fuse_preserving_phase(base, alternate) - self.assertEqual(result.shape, base.shape) - np.testing.assert_allclose(result[1], -.3*result[0], atol=2e-6) - self.assertTrue(np.isfinite(result).all()) - - def test_phase_fusion_silence_and_identity(self): - x = np.random.default_rng(2).normal(size=(2, 4097)).astype(np.float32) - np.testing.assert_allclose(fuse_preserving_phase(x, x), x, atol=1e-6) - z = np.zeros((2, 40), dtype=np.float32) - np.testing.assert_array_equal(fuse_preserving_phase(z, z), z) - - def test_band_routing_and_stereo(self): - t = np.arange(44100) / 44100 - low, high = np.sin(2*np.pi*200*t), np.sin(2*np.pi*12000*t) - base = np.array([low + high, .5*low - high], dtype=np.float32) - alt = np.array([.1*low + .7*high, .1*low - .7*high], dtype=np.float32) - result = fuse_predictions(base, alt)[:, 4410:-4410] - expected = np.array([low+.7*high, .5*low-.7*high])[:, 4410:-4410] - self.assertLess(np.max(abs(result-expected)), .001) - - def test_identity_and_validation(self): - x = np.random.default_rng(1).normal(size=(2, 1000)).astype(np.float32) - np.testing.assert_array_equal(fuse_predictions(x, x), x) - with self.assertRaises(ValueError): - fuse_predictions(x, x[:, :-1]) - with self.assertRaises(ValueError): - fuse_predictions(x, x * np.nan) - - -if __name__ == "__main__": - unittest.main() diff --git a/evaluation/full-enhancement-method/tests/test_apollo_phase_stability.py b/evaluation/full-enhancement-method/tests/test_apollo_phase_stability.py deleted file mode 100644 index 5f2cf45..0000000 --- a/evaluation/full-enhancement-method/tests/test_apollo_phase_stability.py +++ /dev/null @@ -1,27 +0,0 @@ -import sys,unittest -from pathlib import Path -import numpy as np -sys.path.insert(0,str(Path(__file__).resolve().parents[1]/'source')) -from apollo_phase_stability import stabilize_phase - -class PhaseStabilityTests(unittest.TestCase): - def test_no_original_amplitude_blend(self): - t=np.arange(44101)/44100 - a=np.array([.1*np.sin(2*np.pi*440*t)]*2,dtype=np.float32) - b=a*2 - np.testing.assert_allclose(stabilize_phase(a,b),b,atol=2e-7) - def test_common_rotation_keeps_stereo_relation(self): - t=np.arange(44100)/44100 - a=np.array([.1*np.sin(2*np.pi*440*t)]*2,dtype=np.float32) - carrier=.1*np.sin(2*np.pi*440*t+.08*np.sin(2*np.pi*40*t)) - b=np.array([carrier,-.4*carrier],dtype=np.float32) - z=stabilize_phase(a,b) - self.assertEqual(z.shape,b.shape) - np.testing.assert_allclose(z[1],-.4*z[0],atol=2e-7) - def test_silent_reference_does_not_erase_output(self): - b=np.random.default_rng(1).normal(0,.01,(2,4097)).astype(np.float32) - np.testing.assert_allclose(stabilize_phase(b*0,b),b,atol=1e-7) - def test_bad_alignment_rejected(self): - with self.assertRaises(ValueError):stabilize_phase(np.zeros((2,100)),np.zeros((2,101))) - -if __name__=='__main__':unittest.main() diff --git a/evaluation/full-enhancement-method/tests/test_apollo_texture_softening.py b/evaluation/full-enhancement-method/tests/test_apollo_texture_softening.py deleted file mode 100644 index fb47977..0000000 --- a/evaluation/full-enhancement-method/tests/test_apollo_texture_softening.py +++ /dev/null @@ -1,20 +0,0 @@ -import unittest -import numpy as np -from engine.apollo_texture_softening import soften_texture -class TextureTests(unittest.TestCase): - def test_bypass(self): - x=np.random.default_rng(4).normal(0,.1,(2,16000)).astype('float32') - y,d=soften_texture(x,max_reduction_db=0) - np.testing.assert_allclose(x,y,atol=1e-7) - def test_steady_tone_and_stereo(self): - a=.2*np.sin(2*np.pi*3000*np.arange(44100)/44100);x=np.array([a,-.3*a],dtype='float32') - y,d=soften_texture(x) - np.testing.assert_allclose(y[1],-.3*y[0],atol=1e-7) - self.assertLess(np.sqrt(np.mean((x[:,2048:-2048]-y[:,2048:-2048])**2)),1e-5) - def test_silence(self): - y,d=soften_texture(np.zeros((2,4410))) - self.assertEqual(float(abs(y).max()),0) - def test_bounded(self): - x=np.random.default_rng(7).normal(0,.1,(2,16000));y,d=soften_texture(x) - self.assertLessEqual(d['max_reduction_db'],2) - self.assertEqual(y.shape,x.shape) diff --git a/evaluation/texture-20261016/REPORT.md b/evaluation/texture-20261016/REPORT.md deleted file mode 100644 index ff6e428..0000000 --- a/evaluation/texture-20261016/REPORT.md +++ /dev/null @@ -1,77 +0,0 @@ -# PR44 follow-up validation — 2026-10-16 - -## Early validation fixes - -SciPy is imported before audio/model loading only when texture softening is -enabled. A missing dependency produces an actionable RuntimeError before -checkpoint resolution, download or forward inference. Inputs shorter than -2048 frames are rejected before checkpoint resolution/model loading when -enabled. The disabled path retains short-clip behavior and does not import the -optional processor. New tests assert these boundaries and source-file retention. -Local tests and pinned Linux CPU CI each passed all 33 tests. - -Successful CI: https://github.com/hemanpapa-coder/Apollo/actions/runs/38916720169 -Commit tested: 9a4cd0875af86d5cc8b8334f44f1f2a8a2e3e16d -The upstream PR run is action_required (maintainer approval), not a completed -upstream CI pass. The fork uses the same workflow and contribution commit. Its log confirms -33 tests passed in 0.095 s under the pinned dependencies. A validation-only -companion PR is https://github.com/hemanpapa-coder/Apollo/pull/1; no merge is -requested there. - -## Post-process memory - -Fresh-process measurements, model excluded. Tracemalloc starts after input -allocation; process high-water RSS includes Python, imports, input and output. -These are different quantities and must not be compared as if identical. - -| Input | Traced peak | Process peak RSS | DSP time | -|---|---:|---:|---:| -| 30 s real music | 388.74 MiB | 648.77 MiB | 0.344 s | -| 180 s repeated Apollo prediction | 2328.58 MiB | 3042.25 MiB | 2.501 s | - -The 180 s input repeats a 19 s real Apollo prediction; this is a song-length -allocation probe, not a distinct three-minute listening test. Figures are -host/version dependent. Detailed JSON records NumPy/SciPy versions and process -baseline. The full-output STFT remains O(duration), even with chunked model -inference. Multi-minute material can require several GiB in addition to the -model; there is no bounded-memory post-process implementation in this PR. - -Reproduce in separate processes: - -```sh -python tests/measure_texture_memory.py --seconds 30 --input /path/to/music.wav --output /path/to/memory-30.json -python tests/measure_texture_memory.py --seconds 180 --input /path/to/context-apollo.wav --output /path/to/memory-180.json -``` - -## Real music comparison - -Eight 15-second cases from four publicly shareable CC BY recordings. The -comparison includes raw WAVs and six randomized, integrated-loudness-matched -conditions per excerpt; all inference settings and the checkpoint are fixed. -Sources are already lossy public files, so the decoded source proxy is not a -lossless clean master. Metrics against it cannot establish perceptual quality. -Listening validation and maintainer evaluation remain outstanding; keep Draft. - -The quiet case is a deliberate low-level stress test: -18 dB is applied to -source context before encoding and inference (about -33.50 LUFS). All other -cases use unity input gain. Two cases add no further codec degradation; these -are negative controls, not certified artifact-free recordings. - -At 3 dB, the no-added-degradation piano control has a proxy waveform SNR delta -of -0.02348 dB and spectrum RMSE delta of +0.00145 dB. The celesta control has --0.02973 dB / +0.00846 dB respectively. These are objective adverse/caution -examples, not evidence of audible harm. The other piano case also has a very -small SNR regression. All cases are retained and none is labeled a perceptual -success. A controlled listener must judge roughness versus lost wanted detail. - -The raw Apollo path is taken directly from the fixed upstream inference.py; -there is no app-specific polarity ensemble, gain fusion, dry/wet blend or -phase stabilization. Only this PR's optional texture processor varies. - -Public review bundle: https://github.com/hemanpapa-coder/Apollo/releases/tag/texture-evaluation-20261016 - -Bundle includes 96 raw/blind excerpt WAVs plus context and metadata. Source -credits and source hashes are in sources.json. Codec alignment peak lag is -recorded per excerpt; all are zero samples. Per-case blind loudness spread and -4x-oversampled peaks are in audition-checks.json. These checks do not replace -physical listening. diff --git a/evaluation/texture-20261016/SHA256.json b/evaluation/texture-20261016/SHA256.json deleted file mode 100644 index 5908635..0000000 --- a/evaluation/texture-20261016/SHA256.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "control-celesta/context-apollo.wav": "9ebe14061d67a71d87cf5f94a9d0cfdbcd31ad1d5e4e7dfb39dff618278984c6", - "control-celesta/context-input.wav": "3c080aabea76a53722f3161be69cb4ad0b83bdf095d073791e10ca60f72e6c43", - "control-celesta/context-source.wav": "c0cdf66b4ddb210b8600e220f36ab1f7977b8b8dbe1c59b0500b793903aacc8a", - "control-celesta/blind/A.wav": "93586bbf9a5837a96b3f1d8abd681834156e574e93464c84033d0fc31fb3a23e", - "control-celesta/blind/B.wav": "d4b424242e80490cabd93d722f776d8c83c25dc4984336fc15564475cacbc8e2", - "control-celesta/blind/C.wav": "4179da34b723097a80bef3a95093e94cb8a47b40c6155e96b369f87050abc545", - "control-celesta/blind/D.wav": "420b0917bd63d3251af9ee2c4c0410e8824efa0b240bcde137c89d624a9e6a8b", - "control-celesta/blind/E.wav": "480806e7fe79bcf3f02ceab6f5bd2baf6c9b4a9140ef025eb3e663976a0bab09", - "control-celesta/blind/F.wav": "21d09a584e9c84ae1c390c92f024c0f7fbd10d7cd0bd92e7234aefac0e071042", - "control-celesta/raw/apollo-0db.wav": "590be90be5260f132cc26640bb7c33924ffe8a1a8015dd7eb71b573c1ff5837a", - "control-celesta/raw/apollo-1db.wav": "031349ce386f161efa339a896d42d6808164fdd016ed378feba47cea33bb7b9f", - "control-celesta/raw/apollo-2db.wav": "959253e578139ca30110f3d658ab1b1d2af1aedc6225ac22d968b350254ee9b1", - "control-celesta/raw/apollo-3db.wav": "fe256b0143e522d0343bc8a27f7a89f913186f745ac9342ec3bb0e8b81e9b540", - "control-celesta/raw/degraded.wav": "108f37860767da7c92e551108fd529af21a8ee37a359f1164626087e9a7e2688", - "control-celesta/raw/source-proxy.wav": "4702b2871d82be2e3f3af912b7bb171d7829a4e936250d306d6c078d985b1570", - "control-piano/context-apollo.wav": "6f63c26e3e9956afb306b663c3a3145fdd41ac236c632a3c95b1acdcda54e57c", - "control-piano/context-input.wav": "24e023036296d8085ae618bc5bdff32b5d9cc6dcc654e410753220b0238cfc14", - "control-piano/context-source.wav": "61098d36a525ceffb0d7ec0ff7bb052728f468db22993250e8d377531026e064", - "control-piano/blind/A.wav": "6ec6cab695f5bd9ed4360073ea0973acf50088cdd658c5fd477fa4c188abe587", - "control-piano/blind/B.wav": "0e6e580105482021457657a8ad1a60bebab06deb2af0a3f8e185417335ee0f74", - "control-piano/blind/C.wav": "f50e5067ece214434ca0e355f8fb21546ae720f12b576f10f3fb7b369e021818", - "control-piano/blind/D.wav": "c28bd4388ab0aac394b6b6c941265cf85e0276e1f35811891b94a57a4274186b", - "control-piano/blind/E.wav": "dc5c9985bd12bb9789f2ea7fab6b8572f76af6040f959c0b500eadb0a249d110", - "control-piano/blind/F.wav": "f46222522a3f872f64118e5fe80dc32bea035fef5f1744d91623dc66b56d47ca", - "control-piano/raw/apollo-0db.wav": "167932b8e3c36eccc8a6df27e8c405e6e273c2701df7e78cf2092529bd260c11", - "control-piano/raw/apollo-1db.wav": "0ee97569a322a4629fafe5cd1374d2f1554aaa1bef8bd13d9218283c219ad9d5", - "control-piano/raw/apollo-2db.wav": "0c26a66af37692fe5edc1eb0185c1e125266fec4f990544cdffa15e03d830ab2", - "control-piano/raw/apollo-3db.wav": "c6be3fdae12d36fe85b9a452bf6e583f11388231a7421200eacb69f299155d3f", - "control-piano/raw/degraded.wav": "8c83ce459bad335a0061e4bdf3c1952d00c72dcd3b7f428ef8e7e8f8271e71e4", - "control-piano/raw/source-proxy.wav": "aac6ddf344c64ab58341674647e3ae031c139de4c909b28c6faa472633e0de87", - "dense/context-apollo.wav": "291e42e70d3a8ca52eb7339a99ba699f97df18e345d1a4e359b5b17d8bac58f6", - "dense/context-input.wav": "dc6c737cf820f1b594dcf2d5140b75160c1d877d233c8deeb1e1fed98a7e1020", - "dense/context-source.wav": "620d24c6cfb5e6746b6cba161996805730a333c31470f647c23e15d6ce000cc9", - "dense/blind/A.wav": "c1b2d528452a3e99fd2335cd3eea23d88c3c68612fa30c0f963c5f651a660b23", - "dense/blind/B.wav": "d5c67acf110d2356cb415f4669b12a8ec9cb3cd09fb33bef72fa386ff9db493a", - "dense/blind/C.wav": "a2e7d6e58c21e7854d8c1f74554d7e60f10b8c4286ed16680fd8c3ff604591c6", - "dense/blind/D.wav": "4e385d827da2cb04b208254232f6c9c1d6072ae648f180b460283c233c74f57e", - "dense/blind/E.wav": "722322b287d57684bfa5e8f7c3230f6a79e755be63bf5d49c316103b7c72760d", - "dense/blind/F.wav": "3806f40fc0577ff09b7a20c62d2f9d820ea852c79d49dcd87f0722464191f01e", - "dense/raw/apollo-0db.wav": "1b74fcfc74d2e30edadda7560de3e81e28cd67f689a1fa7361d27ebea6ebd102", - "dense/raw/apollo-1db.wav": "5653370f45951da21defe728bc08f4da6feb86976495add3218e6d37110efa8f", - "dense/raw/apollo-2db.wav": "03dd3766ad2d187e7591445be8b11b8f0ba284426cbdd99dde47e591616381a6", - "dense/raw/apollo-3db.wav": "cb4eeefb4d4c80c8c6d97d66642c42769905d84651cad7e0b749fa039ef07612", - "dense/raw/degraded.wav": "bd01491ff46b269e9c86aeedaba364eafaa17b896f5081d03453bcb191902d8a", - "dense/raw/source-proxy.wav": "b63b162d8dfce363f032e18e1735d34146a3bed5817f0a3673965c69f8a19d46", - "drums/context-apollo.wav": "2f2f9fd7392382268ecd909849ae80e235e26e7b782b9f7ab0ff6aeead2b5aad", - "drums/context-input.wav": "4725e8f5d02da6c7fd236725786f1477c4e6992409c4fa0b01763f96d5886ad3", - "drums/context-source.wav": "7524dc5c6807458cafd0f3897a3e7e02d5cc6a764eda410f79c4f4339ea389d8", - "drums/blind/A.wav": "791090336eb920843364fad47d27098f5b34a7f09fb31a5c99ead1ea23f65bea", - "drums/blind/B.wav": "a5ca4c541d6bcaf913b4e6df6e4cdb8e3f9096e7733da17586907b46be873f45", - "drums/blind/C.wav": "88e4cfd227f8e2405ac323222713360d6b0335fd6cf71a89a49b9dcdb9c8c2db", - "drums/blind/D.wav": "7c5767968c5ebc4a4604b3697f61391617dfad3976a7e359a12ec55bde7e7c0b", - "drums/blind/E.wav": "13a0fd6101291907881809eb3ef2851d5b627709a231682539c122934d760904", - "drums/blind/F.wav": "c4d9ee8dec348b9cd493d8837230c863325278e34b57be71ae15a03df6f47562", - "drums/raw/apollo-0db.wav": "6a15bdbf89838e2467b0d89276ebfbf6f5cf3a512fa9e101fe480addf825519d", - "drums/raw/apollo-1db.wav": "9c66d166834eab9e81d8aae31cdae4309b9421481073abf6799c9bb46c7851c3", - "drums/raw/apollo-2db.wav": "dd547346e1554fd1d3dd4ff09d78bd407090eebaefdfb7208c07b9deae83892c", - "drums/raw/apollo-3db.wav": "ff9a5babaa3acaded9a72aa4ee8198a0410182c30512f38535c0d1ce67d520a3", - "drums/raw/degraded.wav": "83eaced3891aa2eb35df9b66c81d33a99d374c4cf085b178dad56b350b7e021e", - "drums/raw/source-proxy.wav": "3482373e6750ce26f3ab45c591a0791f53741860e2519876118acc37477cd6c6", - "piano/context-apollo.wav": "db33132a2c310be7d0a536049727b1c3a7eb8b52eaec062001f93169bf6ce272", - "piano/context-input.wav": "4e27694f9064da72f0ecff3ffb21726661e3d56200cb8c55f7b9145fff2c150e", - "piano/context-source.wav": "76a98b56c8b33c9fe274475a304761e2cfd6fe32ca685aac644d4e553d989309", - "piano/blind/A.wav": "e7c98c2087842ce7a8be1adb754d03fedc0a22c23eb27d91032c8d94480506df", - "piano/blind/B.wav": "53d8ff76ede676fd7a6292be6c1e362031915fffeb4da4200a17507c15609c56", - "piano/blind/C.wav": "0b9db02c9503f54cfdd37bb72e8d5e84b44e893412ad52e05bc89670a894c05c", - "piano/blind/D.wav": "edefdf0e7b6b97a81519ab6c66c7c0d5030d202ac4bb9fda6f397523fc6889bc", - "piano/blind/E.wav": "47345c56397465941ef81a2de8413767a38f952a5e038b9a083870f39dc59d9b", - "piano/blind/F.wav": "585c127f1ddd846f60005855a56713f00c35c0eb234bc79053afabaa7453cf83", - "piano/raw/apollo-0db.wav": "94e784dd96830f444571e5abe24c6df9aa778dc9762386de8765f10e7e01754b", - "piano/raw/apollo-1db.wav": "e8e507f71c89049503c4eb0a7a5e6d20ae8ce049d8cdd3807427135fececf93b", - "piano/raw/apollo-2db.wav": "92d191f8cdfd394610409b700784c63d6ae0f5f8cbec67e1014b1f1fbe81b942", - "piano/raw/apollo-3db.wav": "1365d9b7b2e9ee1b028b6666fe3f0d340e9a3ddd8b74ee1cbf14a4a8e6e6fb7e", - "piano/raw/degraded.wav": "f8f33360cefcea248561571d85e812d737b57a4ea8fd5647a7a0cde2a6bb3c9b", - "piano/raw/source-proxy.wav": "eba7951959e5a3bf9de6197c461f7be9976227dc2631b20c2e2699726d168ed4", - "quiet/context-apollo.wav": "52b5300a41e99cf51c05927f8eda5b1f4c130ca8da6f023d5ec6d7f5c446c2db", - "quiet/context-input.wav": "e489863e1215bbd2b8af381ec8bfe96c0ad6db654fa05b8236e4871e9eccd9d5", - "quiet/context-source.wav": "a0fb7592d03c4a70f707aa496b0c0baebe8693f6a9732cd72daf0a60de39e04c", - "quiet/blind/A.wav": "9e460261288dd0e89722e4f6e7a9cad52eca8f4a89858ffe05cb103e161ced81", - "quiet/blind/B.wav": "f3bdff5e0c1a943bfe6b7096ebf78453c00d15ae8576db6e91681c1e41e30ccd", - "quiet/blind/C.wav": "32e97a8920c9796c76b0fcedc500230a1731ba0cb08dd9b029a78ae2ba65e666", - "quiet/blind/D.wav": "07c4efa159cdf84725930ad8c10323dcc160cef713bd4d358c03c1548de9e46e", - "quiet/blind/E.wav": "813e206cf1f3232a35e1c983c056aad2ed6a6076d6858bfcc83102455e002bfc", - "quiet/blind/F.wav": "179d7c2fbac8161513c359360f2ae3faf1baabc44b21cf225adeff0989b006a9", - "quiet/raw/apollo-0db.wav": "773a881f3cc6f2300ffe79f2357d9a485b9ac3b3b33636dbf99414d4a27f3920", - "quiet/raw/apollo-1db.wav": "509c3b9ea3127ccc2af10d1f7f2c797a8ad1b094664d701cafeb219c02d11383", - "quiet/raw/apollo-2db.wav": "9293833e41511e847e647a2c54606421ee1e3688fa669461652586ccaf6659ae", - "quiet/raw/apollo-3db.wav": "2e4f77d5a6189a98fa62f9f08809d697d4a050723216e164632c1f9ce398b509", - "quiet/raw/degraded.wav": "4026cb7d90415459f538c2e3b9bad228d054619377d053f3fa06b348ca7ca3fb", - "quiet/raw/source-proxy.wav": "43042d3af36d84bf3471960f5c9ed2eb2f8105151ae4309bcf2125966d1b450b", - "vocal/context-apollo.wav": "6b8731d5e1d5bc31835a7a1dbc9edad563c91c5848762b6929f45a84118e9268", - "vocal/context-input.wav": "bed6a94669a3313f561aaa9118e7d922c42c125bdf24f3bda64646059000cef7", - "vocal/context-source.wav": "418eb1a96ca57a1d31f61629f6754c5de92c7950d039c75cc29e5626d88dd5c4", - "vocal/blind/A.wav": "f4c8b5a59fec76e21ede2b4f82cb8641c70e337f77a679d93e4721bcb9063750", - "vocal/blind/B.wav": "9831b315de19c76111e254ed5cd3b367376e8211c357ea527af30dcc198a5025", - "vocal/blind/C.wav": "f44a2f8e78c7ca934e3a2c4d6024bc655955b9def0d0f0c7856ada4f9f868aa9", - "vocal/blind/D.wav": "2cbc6142c269c410180b935fddf140204ebb67eabf317056f01d3c66e976dc6a", - "vocal/blind/E.wav": "a128c56f3e3a18566cfb612e6d4b45ddabdd183672a42ac3ad1e6e16a2e8affd", - "vocal/blind/F.wav": "9e6b814287867219470d34684160f2fc7ea865af1e0d172c0cb034bc097aa864", - "vocal/raw/apollo-0db.wav": "ff36fe6b5d4df94fec766956a3acd8289524ae1b034f30d9fed1c603642406c2", - "vocal/raw/apollo-1db.wav": "b6787f75ff74126955fd961aa8eb6f3f15778cc4bc3b8d1da27673c1487e05b8", - "vocal/raw/apollo-2db.wav": "f6319b7ee14f62009eac02b8724d1bdd839530ce6a35340105020ee4ee4d67e3", - "vocal/raw/apollo-3db.wav": "f4b1dc90bd45f420ffd5fe44568f0f59e1c3f592a03cacdae53435e2e9c18016", - "vocal/raw/degraded.wav": "38a46d742276e1708527fa7a099b0adfee0a3a3671508f5f552ba5c52a25d43c", - "vocal/raw/source-proxy.wav": "e7746796056eb5f97d6073b229867344bf2214f633165cdf1390b77063538039", - "vocal-dense/context-apollo.wav": "596f49bc556d35328efc47be6b0d18de541c74d915bc7e03ca38f762e97d506d", - "vocal-dense/context-input.wav": "6fccc4ef98aa1be8731c04fcce1e97cd7cf8e10d1324e99312ba25a5c6afd04c", - "vocal-dense/context-source.wav": "0c09f73b0459fff082ac3756b057668e83ae767916fe0491c4019b85b5bc1484", - "vocal-dense/blind/A.wav": "615c5c8a110ad7a24682b3af650b345402e43466561f5aa3d5296abcc4359ea1", - "vocal-dense/blind/B.wav": "400c89c4fa1c7a652e12c0c4082b48234fb8ee50d8552e92e45dac0be7288833", - "vocal-dense/blind/C.wav": "578beecc107908e18c79effd5dd539c2f51f9d42f32bc0309ec232e25a1a292c", - "vocal-dense/blind/D.wav": "32858234be87d9cc25c3834e171888b5407d76f9ee20beba5c0020ff609b080c", - "vocal-dense/blind/E.wav": "932a6134703d4dfd5a3b3bd85260b869c7bba7e89dc094cd9b71560a044bd893", - "vocal-dense/blind/F.wav": "4fb7f4f6d635185f7d37966f76fa8e901bda558073ea1e7e54d496054a8f4f64", - "vocal-dense/raw/apollo-0db.wav": "50328e4230e725b77f2a8f7ecd431cd3a06fd552e4300105b145f3725d85ac9d", - "vocal-dense/raw/apollo-1db.wav": "c10983e471c45fd837948f16ada2dc27226305d0036a57b93a3f2e784e7ff579", - "vocal-dense/raw/apollo-2db.wav": "722deec0bccefba5761e452ff01ea906deda1fc86872ac10c45d775b25dee812", - "vocal-dense/raw/apollo-3db.wav": "efc99a3fa3a0c2eaa5b4d712d55dd842e4e288ced2e60326181bbe74f7442dc7", - "vocal-dense/raw/degraded.wav": "bf6588ac83e17debb7660c31a6c2ac30ca8d08a1582d8fdcbca38f503096687f", - "vocal-dense/raw/source-proxy.wav": "cb8a7bb2ab9476d36afb39db844406da2031cc95ba46fa4650e7407a3a577ed2" -} diff --git a/evaluation/texture-20261016/audition-checks.json b/evaluation/texture-20261016/audition-checks.json deleted file mode 100644 index 089be8b..0000000 --- a/evaluation/texture-20261016/audition-checks.json +++ /dev/null @@ -1,42 +0,0 @@ -[ - { - "case": "vocal", - "lufs_spread": 5.384742749470206e-07, - "oversampled_peak_dbfs": -9.971181296221424 - }, - { - "case": "vocal-dense", - "lufs_spread": 6.286101665864408e-07, - "oversampled_peak_dbfs": -14.033596587268264 - }, - { - "case": "drums", - "lufs_spread": 3.5918262142331514e-07, - "oversampled_peak_dbfs": -6.992506179182048 - }, - { - "case": "dense", - "lufs_spread": 4.743841195420373e-07, - "oversampled_peak_dbfs": -12.904741904925334 - }, - { - "case": "piano", - "lufs_spread": 5.04526965983132e-07, - "oversampled_peak_dbfs": -9.414745416715565 - }, - { - "case": "control-piano", - "lufs_spread": 6.514367996146575e-07, - "oversampled_peak_dbfs": -8.86666076546349 - }, - { - "case": "control-celesta", - "lufs_spread": 4.6879371495833766e-07, - "oversampled_peak_dbfs": -6.744472372091744 - }, - { - "case": "quiet", - "lufs_spread": 2.9967262449304144e-07, - "oversampled_peak_dbfs": -9.88741759144698 - } -] diff --git a/evaluation/texture-20261016/fork-ci.json b/evaluation/texture-20261016/fork-ci.json deleted file mode 100644 index 0e0f046..0000000 --- a/evaluation/texture-20261016/fork-ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "head_sha": "9a4cd0875af86d5cc8b8334f44f1f2a8a2e3e16d", - "status": "completed", - "conclusion": "success", - "html_url": "https://github.com/hemanpapa-coder/Apollo/actions/runs/38916720169", - "event": "push" -} diff --git a/evaluation/texture-20261016/measurements.json b/evaluation/texture-20261016/measurements.json deleted file mode 100644 index 638711c..0000000 --- a/evaluation/texture-20261016/measurements.json +++ /dev/null @@ -1,692 +0,0 @@ -{ - "upstream_commit": "e84bcacc59d5455f05d86a5c97dd4aeb3c14dbb6", - "upstream_inference_sha256": "6410cae56d31fe0e802f0995c14ce390b4256bf5207311cf67db4fbac3bab8c7", - "checkpoint_sha256": "99d9af7f1ff20e63c393035513a655392818d66b4d7fc23d658175c1f15e8d76", - "checkpoint_revision": "c68bd80fdd9c0d93d2f4a833cb154624f660a561", - "pure_python_yaml": true, - "device": "mps", - "torch": "2.8.0", - "numpy": "2.0.2", - "scipy": "1.13.1", - "soundfile": "0.13.1", - "libsndfile": "1.2.2", - "platform": "macOS-26.5.1-arm64-arm-64bit", - "chunk_seconds": 6, - "overlap_seconds": 1, - "chunk_pad_seconds": 1, - "chunk_batch_size": 1, - "sources": [ - { - "id": "voice", - "file": "where-now.mp3", - "title": "Where You Are Now", - "attribution": "Benjamin Orth featuring Wolf Fly (source vocal: Ascension / Rhiannon)", - "license": "CC-BY-3.0", - "license_url": "https://creativecommons.org/licenses/by/3.0/", - "source_page": "https://ccmixter.org/files/Benjamin_Orth/33036", - "download": "https://ccmixter.org/content/Benjamin_Orth/Benjamin_Orth_-_Where_You_Are_Now.mp3", - "sha256": "56e989e49e0ff91b74cfd92da19b64a4b82aa477a7b39822803aa538c617d046" - }, - { - "id": "vibe", - "file": "Kevin_MacLeod_-_Vibe_Ace.hq.ogg", - "title": "Vibe Ace", - "attribution": "Kevin MacLeod", - "license": "CC-BY-4.0", - "license_url": "https://creativecommons.org/licenses/by/4.0/", - "source_page": "https://librosa.org/data/audio/Kevin_MacLeod_-_Vibe_Ace.toml", - "download": "https://librosa.org/data/audio/Kevin_MacLeod_-_Vibe_Ace.hq.ogg", - "sha256": "73d6443ef90a7c022f164e5aa90e56c2291585930b39b1656d0765abbc1f1779" - }, - { - "id": "piano", - "file": "Study2.ogg", - "title": "Studies for Piano", - "attribution": "Richard Kearns", - "license": "CC-BY-3.0", - "license_url": "https://creativecommons.org/licenses/by/3.0/", - "source_page": "https://commons.wikimedia.org/wiki/File:Study2.ogg", - "download": "https://upload.wikimedia.org/wikipedia/commons/d/de/Study2.ogg", - "sha256": "4a70c3a9c699cd174b81b048a70ec69b7d5c171a0e469c05fe1d70484ba5389a" - }, - { - "id": "celesta", - "file": "Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.hq.ogg", - "title": "Dance of the Sugar Plum Fairy", - "attribution": "P.I. Tchaikovsky (composer), Kevin MacLeod (arranger)", - "license": "CC-BY-4.0", - "license_url": "https://creativecommons.org/licenses/by/4.0/", - "source_page": "https://librosa.org/data/audio/Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.toml", - "download": "https://librosa.org/data/audio/Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.hq.ogg", - "sha256": "f062221a56a227cdb7c067cf2e6ac0e250a50012f7693ca0c8e31f05f83e49b1" - } - ], - "cases": [ - { - "id": "vocal", - "source": "voice", - "start_seconds": 30, - "duration": 15, - "no_added_degradation": false, - "focus": "female vocal/sibilants; review 2\u201312 s", - "model_seconds": 9.506508208, - "postprocessing": { - "1": { - "max_reduction_db": 0.9277780193038324, - "power_weighted_reduction_db": 0.006405656677210542, - "attack_frame_fraction": 0.23606457508376485, - "seconds": 0.18501620900000404 - }, - "2": { - "max_reduction_db": 1.7665355749321057, - "power_weighted_reduction_db": 0.007618372300704901, - "attack_frame_fraction": 0.23606457508376485, - "seconds": 0.15007120799999996 - }, - "3": { - "max_reduction_db": 2.3250870674913915, - "power_weighted_reduction_db": 0.007930222658002499, - "attack_frame_fraction": 0.23606457508376485, - "seconds": 0.14990266600000268 - } - }, - "metrics": { - "source-proxy": { - "proxy_snr_db": 345.3335764340887, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -13.146152969119903 - }, - "degraded": { - "proxy_snr_db": 17.84819909156824, - "proxy_log_spectrum_rmse_db": 6.264313244834549, - "integrated_lufs": -13.52753996686618 - }, - "apollo-0db": { - "proxy_snr_db": 17.795616680900224, - "proxy_log_spectrum_rmse_db": 5.29298021890908, - "integrated_lufs": -13.199174859138699 - }, - "apollo-1db": { - "proxy_snr_db": 17.805633251725332, - "proxy_log_spectrum_rmse_db": 5.296966228380251, - "integrated_lufs": -13.211267095718757 - }, - "apollo-2db": { - "proxy_snr_db": 17.80449815598604, - "proxy_log_spectrum_rmse_db": 5.29839798994255, - "integrated_lufs": -13.21351593860634 - }, - "apollo-3db": { - "proxy_snr_db": 17.803378530321503, - "proxy_log_spectrum_rmse_db": 5.298760352251937, - "integrated_lufs": -13.214075860523861 - } - }, - "blind_key": { - "A": "degraded", - "B": "apollo-3db", - "C": "source-proxy", - "D": "apollo-0db", - "E": "apollo-2db", - "F": "apollo-1db" - }, - "blind_target_lufs": -23.0, - "common_gain_db": 0.0, - "codec_alignment_peak_lag_samples": 0, - "largest_change_window_seconds": [ - 13, - 14 - ], - "proxy_snr_delta_3db": 0.007761849421278555, - "proxy_spectral_delta_3db": 0.0057801333428564305 - }, - { - "id": "vocal-dense", - "source": "voice", - "start_seconds": 70, - "duration": 15, - "no_added_degradation": false, - "focus": "vocal with dense pop mixture; review 0\u201315 s", - "model_seconds": 8.839069875000007, - "postprocessing": { - "1": { - "max_reduction_db": 0.9381951735232099, - "power_weighted_reduction_db": 0.004960963929212733, - "attack_frame_fraction": 0.36003655193420653, - "seconds": 0.2037515410000026 - }, - "2": { - "max_reduction_db": 1.7956498706561956, - "power_weighted_reduction_db": 0.005941718101797443, - "attack_frame_fraction": 0.36003655193420653, - "seconds": 0.1563755829999991 - }, - "3": { - "max_reduction_db": 2.4268124378433713, - "power_weighted_reduction_db": 0.00614522027951007, - "attack_frame_fraction": 0.36003655193420653, - "seconds": 0.15161662500000261 - } - }, - "metrics": { - "source-proxy": { - "proxy_snr_db": 351.5158244900671, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -7.670278454925665 - }, - "degraded": { - "proxy_snr_db": 18.16938604175261, - "proxy_log_spectrum_rmse_db": 8.13214966462172, - "integrated_lufs": -8.070041709257842 - }, - "apollo-0db": { - "proxy_snr_db": 17.94836529927291, - "proxy_log_spectrum_rmse_db": 6.96038304457811, - "integrated_lufs": -7.79526875496955 - }, - "apollo-1db": { - "proxy_snr_db": 17.958142610163698, - "proxy_log_spectrum_rmse_db": 6.96473037484198, - "integrated_lufs": -7.80740722841217 - }, - "apollo-2db": { - "proxy_snr_db": 17.957255971095712, - "proxy_log_spectrum_rmse_db": 6.965914567959103, - "integrated_lufs": -7.809689099263081 - }, - "apollo-3db": { - "proxy_snr_db": 17.956408039578804, - "proxy_log_spectrum_rmse_db": 6.966201236106422, - "integrated_lufs": -7.810137634976561 - } - }, - "blind_key": { - "A": "source-proxy", - "B": "apollo-1db", - "C": "apollo-3db", - "D": "apollo-2db", - "E": "degraded", - "F": "apollo-0db" - }, - "blind_target_lufs": -23.0, - "common_gain_db": 0.0, - "codec_alignment_peak_lag_samples": 0, - "largest_change_window_seconds": [ - 9, - 10 - ], - "proxy_snr_delta_3db": 0.008042740305892693, - "proxy_spectral_delta_3db": 0.005818191528311978 - }, - { - "id": "drums", - "source": "vibe", - "start_seconds": 20, - "duration": 15, - "no_added_degradation": false, - "focus": "kit/cymbals in jazz mixture; review 0\u201315 s", - "model_seconds": 8.302095249999994, - "postprocessing": { - "1": { - "max_reduction_db": 0.9233757737189019, - "power_weighted_reduction_db": 0.00012472032349085393, - "attack_frame_fraction": 0.43374961925068534, - "seconds": 0.15764849999999342 - }, - "2": { - "max_reduction_db": 1.7802924918901082, - "power_weighted_reduction_db": 0.00015786187026980153, - "attack_frame_fraction": 0.43374961925068534, - "seconds": 0.15351825000000474 - }, - "3": { - "max_reduction_db": 2.498484735242565, - "power_weighted_reduction_db": 0.00016551712022401562, - "attack_frame_fraction": 0.43374961925068534, - "seconds": 0.15145716600000014 - } - }, - "metrics": { - "source-proxy": { - "proxy_snr_db": 344.1179240078923, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -16.420689771864364 - }, - "degraded": { - "proxy_snr_db": 28.483370656736696, - "proxy_log_spectrum_rmse_db": 5.030928634273675, - "integrated_lufs": -16.85941741193594 - }, - "apollo-0db": { - "proxy_snr_db": 28.00615449160807, - "proxy_log_spectrum_rmse_db": 4.023621021478898, - "integrated_lufs": -16.523815672079053 - }, - "apollo-1db": { - "proxy_snr_db": 28.008189835176616, - "proxy_log_spectrum_rmse_db": 4.0269196981262345, - "integrated_lufs": -16.524208896396086 - }, - "apollo-2db": { - "proxy_snr_db": 28.00822534480985, - "proxy_log_spectrum_rmse_db": 4.028354078076484, - "integrated_lufs": -16.524309022564946 - }, - "apollo-3db": { - "proxy_snr_db": 28.008010948150556, - "proxy_log_spectrum_rmse_db": 4.028889846237043, - "integrated_lufs": -16.524331499164685 - } - }, - "blind_key": { - "A": "source-proxy", - "B": "apollo-1db", - "C": "apollo-0db", - "D": "degraded", - "E": "apollo-2db", - "F": "apollo-3db" - }, - "blind_target_lufs": -23.0, - "common_gain_db": 0.0, - "codec_alignment_peak_lag_samples": 0, - "largest_change_window_seconds": [ - 14, - 15 - ], - "proxy_snr_delta_3db": 0.0018564565424874502, - "proxy_spectral_delta_3db": 0.005268824758144497 - }, - { - "id": "dense", - "source": "voice", - "start_seconds": 110, - "duration": 15, - "no_added_degradation": false, - "focus": "dense synth/guitar/drum mixture; review 0\u201315 s", - "model_seconds": 8.237370834000018, - "postprocessing": { - "1": { - "max_reduction_db": 0.9120900451871284, - "power_weighted_reduction_db": 0.004309473577788946, - "attack_frame_fraction": 0.2598233323180018, - "seconds": 0.15123416599999473 - }, - "2": { - "max_reduction_db": 1.7621566271340454, - "power_weighted_reduction_db": 0.005109912040847642, - "attack_frame_fraction": 0.2598233323180018, - "seconds": 0.22263233300000707 - }, - "3": { - "max_reduction_db": 2.565897819557046, - "power_weighted_reduction_db": 0.0052868646615776595, - "attack_frame_fraction": 0.2598233323180018, - "seconds": 0.16460370899997656 - } - }, - "metrics": { - "source-proxy": { - "proxy_snr_db": 350.39079978812, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -8.32650770833035 - }, - "degraded": { - "proxy_snr_db": 18.650989516675775, - "proxy_log_spectrum_rmse_db": 7.243176641043024, - "integrated_lufs": -8.728852053925175 - }, - "apollo-0db": { - "proxy_snr_db": 18.430476557209875, - "proxy_log_spectrum_rmse_db": 6.55321420324369, - "integrated_lufs": -8.401759731910804 - }, - "apollo-1db": { - "proxy_snr_db": 18.441415491719127, - "proxy_log_spectrum_rmse_db": 6.558932200205994, - "integrated_lufs": -8.411200885319044 - }, - "apollo-2db": { - "proxy_snr_db": 18.44144120578801, - "proxy_log_spectrum_rmse_db": 6.560664703567572, - "integrated_lufs": -8.412877325469987 - }, - "apollo-3db": { - "proxy_snr_db": 18.441063239838577, - "proxy_log_spectrum_rmse_db": 6.561086789245587, - "integrated_lufs": -8.413223169617641 - } - }, - "blind_key": { - "A": "apollo-2db", - "B": "source-proxy", - "C": "apollo-3db", - "D": "apollo-0db", - "E": "apollo-1db", - "F": "degraded" - }, - "blind_target_lufs": -23.0, - "common_gain_db": 0.0, - "codec_alignment_peak_lag_samples": 0, - "largest_change_window_seconds": [ - 3, - 4 - ], - "proxy_snr_delta_3db": 0.010586682628701993, - "proxy_spectral_delta_3db": 0.007872586001896664 - }, - { - "id": "piano", - "source": "piano", - "start_seconds": 15, - "duration": 15, - "no_added_degradation": false, - "focus": "piano transients/sustains; review 0\u201315 s", - "model_seconds": 8.493648957999994, - "postprocessing": { - "1": { - "max_reduction_db": 0.9508322441721178, - "power_weighted_reduction_db": 0.00045141379300632663, - "attack_frame_fraction": 0.1836734693877551, - "seconds": 0.19210229199998707 - }, - "2": { - "max_reduction_db": 1.8980818305936575, - "power_weighted_reduction_db": 0.0004986413529220128, - "attack_frame_fraction": 0.1836734693877551, - "seconds": 0.1550508750000006 - }, - "3": { - "max_reduction_db": 2.7052187879223273, - "power_weighted_reduction_db": 0.0005021784835520333, - "attack_frame_fraction": 0.1836734693877551, - "seconds": 0.16535299999998188 - } - }, - "metrics": { - "source-proxy": { - "proxy_snr_db": 343.2317032986203, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -15.152773119187982 - }, - "degraded": { - "proxy_snr_db": 22.119948343243422, - "proxy_log_spectrum_rmse_db": 4.488636491493283, - "integrated_lufs": -15.590826010004978 - }, - "apollo-0db": { - "proxy_snr_db": 22.45829972331471, - "proxy_log_spectrum_rmse_db": 4.270752712136737, - "integrated_lufs": -15.158116675664862 - }, - "apollo-1db": { - "proxy_snr_db": 22.458025271541764, - "proxy_log_spectrum_rmse_db": 4.271372475557171, - "integrated_lufs": -15.158933378482187 - }, - "apollo-2db": { - "proxy_snr_db": 22.45794316452489, - "proxy_log_spectrum_rmse_db": 4.271515249737962, - "integrated_lufs": -15.15900740164424 - }, - "apollo-3db": { - "proxy_snr_db": 22.457933200560735, - "proxy_log_spectrum_rmse_db": 4.271584967013272, - "integrated_lufs": -15.15901145108523 - } - }, - "blind_key": { - "A": "apollo-3db", - "B": "degraded", - "C": "source-proxy", - "D": "apollo-0db", - "E": "apollo-1db", - "F": "apollo-2db" - }, - "blind_target_lufs": -23.0, - "common_gain_db": 0.0, - "codec_alignment_peak_lag_samples": 0, - "largest_change_window_seconds": [ - 1, - 2 - ], - "proxy_snr_delta_3db": -0.0003665227539748628, - "proxy_spectral_delta_3db": 0.0008322548765349325 - }, - { - "id": "control-piano", - "source": "piano", - "start_seconds": 40, - "duration": 15, - "no_added_degradation": true, - "focus": "no added codec degradation negative control; review wanted detail", - "model_seconds": 8.386500916000045, - "postprocessing": { - "1": { - "max_reduction_db": 0.9514021415209956, - "power_weighted_reduction_db": 0.00054733751255932, - "attack_frame_fraction": 0.16905269570514772, - "seconds": 0.1788028330000202 - }, - "2": { - "max_reduction_db": 1.8381541803499792, - "power_weighted_reduction_db": 0.0005987326947648451, - "attack_frame_fraction": 0.16905269570514772, - "seconds": 0.15105508400000645 - }, - "3": { - "max_reduction_db": 2.627608112536266, - "power_weighted_reduction_db": 0.0006023057328315994, - "attack_frame_fraction": 0.16905269570514772, - "seconds": 0.15169883399994433 - } - }, - "metrics": { - "source-proxy": { - "proxy_snr_db": 343.63278628936285, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -14.794504118250858 - }, - "degraded": { - "proxy_snr_db": 343.63278628936285, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -14.794504118250858 - }, - "apollo-0db": { - "proxy_snr_db": 32.67701580650404, - "proxy_log_spectrum_rmse_db": 2.5706256258761084, - "integrated_lufs": -14.348045783442986 - }, - "apollo-1db": { - "proxy_snr_db": 32.65810836188682, - "proxy_log_spectrum_rmse_db": 2.57155384015423, - "integrated_lufs": -14.349035324858942 - }, - "apollo-2db": { - "proxy_snr_db": 32.654000138846556, - "proxy_log_spectrum_rmse_db": 2.5718817634047624, - "integrated_lufs": -14.349139732325051 - }, - "apollo-3db": { - "proxy_snr_db": 32.6535331204102, - "proxy_log_spectrum_rmse_db": 2.572071769486433, - "integrated_lufs": -14.349147027379429 - } - }, - "blind_key": { - "A": "degraded", - "B": "apollo-1db", - "C": "apollo-2db", - "D": "source-proxy", - "E": "apollo-3db", - "F": "apollo-0db" - }, - "blind_target_lufs": -23.0, - "common_gain_db": 0.0, - "codec_alignment_peak_lag_samples": 0, - "largest_change_window_seconds": [ - 1, - 2 - ], - "proxy_snr_delta_3db": -0.023482686093835525, - "proxy_spectral_delta_3db": 0.0014461436103245795 - }, - { - "id": "control-celesta", - "source": "celesta", - "start_seconds": 10, - "duration": 15, - "no_added_degradation": true, - "focus": "no added codec degradation negative control; review attacks", - "model_seconds": 8.30687341700002, - "postprocessing": { - "1": { - "max_reduction_db": 0.9523644463693307, - "power_weighted_reduction_db": 0.0004061499640822737, - "attack_frame_fraction": 0.20499543100822418, - "seconds": 0.1756770000000074 - }, - "2": { - "max_reduction_db": 1.7935269672681515, - "power_weighted_reduction_db": 0.0004671529406520139, - "attack_frame_fraction": 0.20499543100822418, - "seconds": 0.1520695830000136 - }, - "3": { - "max_reduction_db": 2.618655255984388, - "power_weighted_reduction_db": 0.0004782941493130434, - "attack_frame_fraction": 0.20499543100822418, - "seconds": 0.15150983299997733 - } - }, - "metrics": { - "source-proxy": { - "proxy_snr_db": 336.12060760267525, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -22.635389032198916 - }, - "degraded": { - "proxy_snr_db": 336.12060760267525, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -22.635389032198916 - }, - "apollo-0db": { - "proxy_snr_db": 33.61315576889569, - "proxy_log_spectrum_rmse_db": 1.3162946130946984, - "integrated_lufs": -22.22164050696478 - }, - "apollo-1db": { - "proxy_snr_db": 33.590349744624085, - "proxy_log_spectrum_rmse_db": 1.3225470463593738, - "integrated_lufs": -22.222407965498636 - }, - "apollo-2db": { - "proxy_snr_db": 33.58461991740678, - "proxy_log_spectrum_rmse_db": 1.3243090331203096, - "integrated_lufs": -22.222521140427652 - }, - "apollo-3db": { - "proxy_snr_db": 33.58342788859913, - "proxy_log_spectrum_rmse_db": 1.3247537746249867, - "integrated_lufs": -22.222541913796967 - } - }, - "blind_key": { - "A": "apollo-3db", - "B": "degraded", - "C": "apollo-2db", - "D": "apollo-1db", - "E": "apollo-0db", - "F": "source-proxy" - }, - "blind_target_lufs": -23.0, - "common_gain_db": 0.0, - "codec_alignment_peak_lag_samples": 0, - "largest_change_window_seconds": [ - 13, - 14 - ], - "proxy_snr_delta_3db": -0.029727880296555043, - "proxy_spectral_delta_3db": 0.00845916153028825 - }, - { - "id": "quiet", - "source": "piano", - "input_gain_db": -18.0, - "start_seconds": 65, - "duration": 15, - "no_added_degradation": false, - "focus": "quiet piano candidate; review decays 0\u201315 s", - "model_seconds": 8.358211500000003, - "postprocessing": { - "1": { - "max_reduction_db": 0.9499271894995048, - "power_weighted_reduction_db": 0.0004136056155710113, - "attack_frame_fraction": 0.1839780688394761, - "seconds": 0.19872379199999557 - }, - "2": { - "max_reduction_db": 1.7785922008176083, - "power_weighted_reduction_db": 0.0004456497121363847, - "attack_frame_fraction": 0.1839780688394761, - "seconds": 0.1582491660000045 - }, - "3": { - "max_reduction_db": 2.5186530260478026, - "power_weighted_reduction_db": 0.00044896900937477176, - "attack_frame_fraction": 0.1839780688394761, - "seconds": 0.15844245799999612 - } - }, - "metrics": { - "source-proxy": { - "proxy_snr_db": 325.01836077936616, - "proxy_log_spectrum_rmse_db": 0.0, - "integrated_lufs": -33.49932920875261 - }, - "degraded": { - "proxy_snr_db": 22.03086961638961, - "proxy_log_spectrum_rmse_db": 2.0993352248392325, - "integrated_lufs": -33.93843779897025 - }, - "apollo-0db": { - "proxy_snr_db": 21.869373892818334, - "proxy_log_spectrum_rmse_db": 2.0394451912450218, - "integrated_lufs": -33.518635404188466 - }, - "apollo-1db": { - "proxy_snr_db": 21.870460735482816, - "proxy_log_spectrum_rmse_db": 2.0396993261373693, - "integrated_lufs": -33.51933350552817 - }, - "apollo-2db": { - "proxy_snr_db": 21.87055010005373, - "proxy_log_spectrum_rmse_db": 2.0398698611095307, - "integrated_lufs": -33.51938342323602 - }, - "apollo-3db": { - "proxy_snr_db": 21.87053925175779, - "proxy_log_spectrum_rmse_db": 2.0399275184970955, - "integrated_lufs": -33.51938866459359 - } - }, - "blind_key": { - "A": "source-proxy", - "B": "apollo-3db", - "C": "degraded", - "D": "apollo-0db", - "E": "apollo-2db", - "F": "apollo-1db" - }, - "blind_target_lufs": -23.0, - "common_gain_db": 0.0, - "codec_alignment_peak_lag_samples": 0, - "largest_change_window_seconds": [ - 12, - 13 - ], - "proxy_snr_delta_3db": 0.0011653589394562403, - "proxy_spectral_delta_3db": 0.0004823272520737554 - } - ], - "notes": "Sources are already lossy public recordings; source-proxy is NOT a lossless clean reference. No listener verdict. Controls are no-added-degradation, not certified clean recordings." -} diff --git a/evaluation/texture-20261016/memory-apollo-180.json b/evaluation/texture-20261016/memory-apollo-180.json deleted file mode 100644 index ae54f50..0000000 --- a/evaluation/texture-20261016/memory-apollo-180.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "duration": 180.0, - "frames": 7938000, - "channels": 2, - "sample_rate": 44100, - "numpy": "2.0.2", - "scipy": "1.13.1", - "platform": "macOS-26.5.1-arm64-arm-64bit", - "input_file": "context-apollo.wav", - "input_repeated": true, - "seconds": 2.50147275, - "traced_peak_bytes": 2441695549, - "process_peak_rss_bytes": 3190030336, - "process_peak_before_bytes": 226721792, - "statistics": { - "max_reduction_db": 1.803205901063708, - "power_weighted_reduction_db": 0.007614077796485415, - "attack_frame_fraction": 0.23599961311538833 - }, - "finite": true -} diff --git a/evaluation/texture-20261016/memory-music-30.json b/evaluation/texture-20261016/memory-music-30.json deleted file mode 100644 index c36a039..0000000 --- a/evaluation/texture-20261016/memory-music-30.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "duration": 30.0, - "frames": 1323000, - "channels": 2, - "sample_rate": 44100, - "numpy": "2.0.2", - "scipy": "1.13.1", - "platform": "macOS-26.5.1-arm64-arm-64bit", - "input_file": "SDTEST1.WAV", - "input_repeated": false, - "seconds": 0.3436969579999998, - "traced_peak_bytes": 407622657, - "process_peak_rss_bytes": 680280064, - "process_peak_before_bytes": 160071680, - "statistics": { - "max_reduction_db": 1.8357674293826705, - "power_weighted_reduction_db": 0.006417486371190556, - "attack_frame_fraction": 0.2149893760865366 - }, - "finite": true -} diff --git a/evaluation/texture-20261016/sources.json b/evaluation/texture-20261016/sources.json deleted file mode 100644 index 0c6a51b..0000000 --- a/evaluation/texture-20261016/sources.json +++ /dev/null @@ -1,46 +0,0 @@ -[ - { - "id": "voice", - "file": "where-now.mp3", - "title": "Where You Are Now", - "attribution": "Benjamin Orth featuring Wolf Fly (source vocal: Ascension / Rhiannon)", - "license": "CC-BY-3.0", - "license_url": "https://creativecommons.org/licenses/by/3.0/", - "source_page": "https://ccmixter.org/files/Benjamin_Orth/33036", - "download": "https://ccmixter.org/content/Benjamin_Orth/Benjamin_Orth_-_Where_You_Are_Now.mp3", - "sha256": "56e989e49e0ff91b74cfd92da19b64a4b82aa477a7b39822803aa538c617d046" - }, - { - "id": "vibe", - "file": "Kevin_MacLeod_-_Vibe_Ace.hq.ogg", - "title": "Vibe Ace", - "attribution": "Kevin MacLeod", - "license": "CC-BY-4.0", - "license_url": "https://creativecommons.org/licenses/by/4.0/", - "source_page": "https://librosa.org/data/audio/Kevin_MacLeod_-_Vibe_Ace.toml", - "download": "https://librosa.org/data/audio/Kevin_MacLeod_-_Vibe_Ace.hq.ogg", - "sha256": "73d6443ef90a7c022f164e5aa90e56c2291585930b39b1656d0765abbc1f1779" - }, - { - "id": "piano", - "file": "Study2.ogg", - "title": "Studies for Piano", - "attribution": "Richard Kearns", - "license": "CC-BY-3.0", - "license_url": "https://creativecommons.org/licenses/by/3.0/", - "source_page": "https://commons.wikimedia.org/wiki/File:Study2.ogg", - "download": "https://upload.wikimedia.org/wikipedia/commons/d/de/Study2.ogg", - "sha256": "4a70c3a9c699cd174b81b048a70ec69b7d5c171a0e469c05fe1d70484ba5389a" - }, - { - "id": "celesta", - "file": "Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.hq.ogg", - "title": "Dance of the Sugar Plum Fairy", - "attribution": "P.I. Tchaikovsky (composer), Kevin MacLeod (arranger)", - "license": "CC-BY-4.0", - "license_url": "https://creativecommons.org/licenses/by/4.0/", - "source_page": "https://librosa.org/data/audio/Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.toml", - "download": "https://librosa.org/data/audio/Kevin_MacLeod_-_P_I_Tchaikovsky_Dance_of_the_Sugar_Plum_Fairy.hq.ogg", - "sha256": "f062221a56a227cdb7c067cf2e6ac0e250a50012f7693ca0c8e31f05f83e49b1" - } -] diff --git a/evaluation/texture-20261016/system-unit-tests.log b/evaluation/texture-20261016/system-unit-tests.log deleted file mode 100644 index b0198f5..0000000 --- a/evaluation/texture-20261016/system-unit-tests.log +++ /dev/null @@ -1,41 +0,0 @@ -/Volumes/Program Dev/Neuracoust Suno Sound Build-up/contributions/apollo-texture/test-dependencies/urllib3/__init__.py:35: NotOpenSSLWarning: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'LibreSSL 2.8.3'. See: https://github.com/urllib3/urllib3/issues/3020 - warnings.warn( -test_auto_falls_back_to_cpu (test_inference.DeviceSelectionTests) ... ok -test_auto_prefers_cuda_over_mps (test_inference.DeviceSelectionTests) ... ok -test_auto_uses_mps_when_cuda_is_unavailable (test_inference.DeviceSelectionTests) ... ok -test_explicit_unavailable_cuda_fails (test_inference.DeviceSelectionTests) ... ok -test_explicit_unavailable_mps_fails (test_inference.DeviceSelectionTests) ... ok -test_arbitrary_hugging_face_repository_is_rejected (test_inference.InferenceTests) ... ok -test_checkpoint_directory_is_rejected (test_inference.InferenceTests) ... ok -test_chunk_padding_discards_degraded_input_edges (test_inference.InferenceTests) ... ok -test_chunk_padding_wider_than_the_hop_reproduces_the_input (test_inference.InferenceTests) -Regression test ... ok -test_chunk_settings_reject_invalid_durations (test_inference.InferenceTests) ... ok -test_chunked_cpu_inference_preserves_samples_and_limits_chunk_size (test_inference.InferenceTests) ... ok -test_chunking_rejects_invalid_batch_size (test_inference.InferenceTests) ... ok -test_chunking_rejects_short_model_output (test_inference.InferenceTests) ... ok -test_cpu_inference_preserves_input_and_audio_shape (test_inference.InferenceTests) ... ok -test_existing_local_checkpoint_is_accepted (test_inference.InferenceTests) ... ok -test_invalid_batch_size_fails_before_checkpoint_download (test_inference.InferenceTests) ... ok -test_missing_local_checkpoint_has_clear_error (test_inference.InferenceTests) ... ok -test_official_checkpoint_downloads_from_trusted_repository (test_inference.InferenceTests) ... ok -test_refuses_hard_link_output_alias (test_inference.InferenceTests) ... ok -test_refuses_to_overwrite_input (test_inference.InferenceTests) ... ok -test_short_input_uses_original_full_file_path (test_inference.InferenceTests) ... ok -test_unpadded_chunks_leak_degraded_edges (test_inference.InferenceTests) ... ok -test_wrong_sample_rate_fails_before_download_or_device_transfer (test_inference.InferenceTests) ... ok -test_invalid_cap_rejected_before_model_load (test_texture_inference.IntegrationTests) ... ok -test_missing_scipy_fails_before_audio_checkpoint_or_model (test_texture_inference.IntegrationTests) ... ok -test_optional_postprocessing_and_default_bypass (test_texture_inference.IntegrationTests) ... ok -test_short_clip_rejected_before_checkpoint_but_disabled_still_works (test_texture_inference.IntegrationTests) ... ok -test_unrelated_import_errors_are_not_misreported (test_texture_inference.IntegrationTests) ... ok -test_actual_modulation_is_bounded (test_texture_softening.TextureTests) ... ok -test_exact_bypass (test_texture_softening.TextureTests) ... ok -test_invalid (test_texture_softening.TextureTests) ... ok -test_silence (test_texture_softening.TextureTests) ... ok -test_steady_tone_stereo_and_length (test_texture_softening.TextureTests) ... ok - ----------------------------------------------------------------------- -Ran 33 tests in 16.020s - -OK diff --git a/tests/benchmark_texture_softening.py b/tests/benchmark_texture_softening.py deleted file mode 100644 index 64b19b0..0000000 --- a/tests/benchmark_texture_softening.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Reproducible synthetic DSP comparison, no model or private assets.""" -import argparse -import hashlib -import json -import platform -from pathlib import Path -import sys -import time - -import numpy as np -import scipy -import soundfile as sf - -sys.path.insert(0, str(Path(__file__).resolve().parents[1])) -from texture_softening import soften_texture - - -def main(): - parser = argparse.ArgumentParser(__doc__) - parser.add_argument('--output-dir', type=Path, required=True) - args = parser.parse_args() - args.output_dir.mkdir(parents=True, exist_ok=True) - sr = 44100 - t = np.arange(4*sr)/sr - low = .4*np.sin(2*np.pi*220*t) - high = .02*(.1+(np.mod(t, .08)<.008))*np.sin(2*np.pi*4000*t) - x = np.asarray([low+high, .7*low-.3*high], dtype=np.float32) - times = [] - for _ in range(3): - start = time.perf_counter() - y, stats = soften_texture(x, max_reduction_db=2) - times.append(time.perf_counter()-start) - report = {'python': platform.python_version(), 'platform': platform.platform(), - 'numpy': np.__version__, 'scipy': scipy.__version__, - 'sample_rate': sr, 'frames': len(t), 'cap_db': 2, - 'postprocess_seconds': times, 'statistics': stats, 'files': {}} - rms = lambda v: float(np.sqrt(np.mean(v.astype(np.float64)**2))) - matched = [v*(rms(x)/max(rms(v), 1e-30)) for v in (x,y)] - common = min(1., 10**(-1/20)/max(float(abs(v).max()) for v in matched)) - for name,v in [('raw-before',x),('raw-after',y), - ('audition-before',matched[0]*common),('audition-after',matched[1]*common)]: - path = args.output_dir / (name+'.wav') - sf.write(path, v.T, sr, subtype='FLOAT') - report['files'][path.name] = {'sha256': hashlib.sha256(path.read_bytes()).hexdigest(), - 'rms': rms(v), 'peak': float(abs(v).max())} - (args.output_dir/'measurements.json').write_text(json.dumps(report, indent=2)+'\n') - print(json.dumps(report, indent=2)) - - -if __name__ == '__main__': - main() diff --git a/tests/build_texture_music_comparison.py b/tests/build_texture_music_comparison.py deleted file mode 100644 index 20fa10b..0000000 --- a/tests/build_texture_music_comparison.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Build eight aligned real-music comparisons with a fixed upstream Apollo. -Run from repo root after fetching the exact sources recorded in sources.json. -This is objective/AB preparation, not an automated perceptual quality verdict. -""" -import argparse -import hashlib -import importlib.util -import json -import platform -import random -import subprocess -import sys -import time -from pathlib import Path - -import numpy as np -import soundfile as sf -import scipy -from scipy.signal import resample_poly, stft -import torch -import pyloudnorm as pyln -sys.path.insert(0, str(Path(__file__).resolve().parents[1])) -from texture_softening import soften_texture - -BASE = 'e84bcacc59d5455f05d86a5c97dd4aeb3c14dbb6' -SR = 44100 - -def digest(p): - return hashlib.sha256(Path(p).read_bytes()).hexdigest() - - -def main(): - p = argparse.ArgumentParser(__doc__) - p.add_argument('--sources-dir', type=Path, required=True) - p.add_argument('--checkpoint', type=Path, required=True) - p.add_argument('--output-dir', type=Path, required=True) - p.add_argument('--device', choices=['mps','cpu','cuda'], default='mps') - p.add_argument('--only-case', help='Rebuild one case, preserving completed cases in the existing manifest') - p.add_argument('--pure-python-yaml', action='store_true', help='Use the supported pure-Python PyYAML parser instead of its optional C extension') - a = p.parse_args() - if a.pure_python_yaml: - sys.modules['yaml._yaml'] = None - a.output_dir.mkdir(parents=True, exist_ok=True) - upstream = a.output_dir/'upstream_inference.py' - upstream.write_bytes(subprocess.check_output(['git','show',BASE+':inference.py'])) - spec = importlib.util.spec_from_file_location('apollo_upstream',upstream) - api = importlib.util.module_from_spec(spec);spec.loader.exec_module(api) - torch.set_num_threads(4) - device = api.select_device(a.device) - model = api.look2hear.models.BaseModel.from_pretrain(str(a.checkpoint),sr=SR,win=20,feature_dim=256,layer=6).to(device).eval() - sources = json.loads((a.sources_dir/'sources.json').read_text()) - for row in sources: - row['sha256'] = digest(a.sources_dir/row['file']) - lookup = {r['id']:r for r in sources} - cases = [('vocal', 'voice',30,False,'female vocal/sibilants; review 2–12 s'), - ('vocal-dense','voice',70,False,'vocal with dense pop mixture; review 0–15 s'), - ('drums','vibe',20,False,'kit/cymbals in jazz mixture; review 0–15 s'), - ('dense','voice',110,False,'dense synth/guitar/drum mixture; review 0–15 s'), - ('piano','piano',15,False,'piano transients/sustains; review 0–15 s'), - ('quiet','piano',65,False,'quiet piano candidate; review decays 0–15 s'), - ('control-piano','piano',40,True,'no added codec degradation negative control; review wanted detail'), - ('control-celesta','celesta',10,True,'no added codec degradation negative control; review attacks')] - report = dict(upstream_commit=BASE,upstream_inference_sha256=digest(upstream), - checkpoint_sha256=digest(a.checkpoint),checkpoint_revision='c68bd80fdd9c0d93d2f4a833cb154624f660a561', - pure_python_yaml=a.pure_python_yaml,device=str(device),torch=torch.__version__,numpy=np.__version__,scipy=scipy.__version__, - soundfile=sf.__version__,libsndfile=sf.__libsndfile_version__,platform=platform.platform(), - chunk_seconds=6,overlap_seconds=1,chunk_pad_seconds=1,chunk_batch_size=1, - sources=sources,cases=[],notes='Sources are already lossy public recordings; source-proxy is NOT a lossless clean reference. No listener verdict. Controls are no-added-degradation, not certified clean recordings.') - if a.only_case: - previous=json.loads((a.output_dir/'measurements.json').read_text()) - report['cases']=[r for r in previous['cases'] if r['id']!=a.only_case] - meter = pyln.Meter(SR) - rng = random.Random(441044) - for cid,sid,start,control,focus in cases: - if a.only_case and cid!=a.only_case: - skipped=list(range(6));rng.shuffle(skipped) - continue - out = a.output_dir/cid;out.mkdir(exist_ok=True) - raw = out/'raw';raw.mkdir(exist_ok=True) - audition = out/'blind';audition.mkdir(exist_ok=True) - source, rate = sf.read(a.sources_dir/lookup[sid]['file'],dtype='float32',always_2d=True) - assert rate==SR and source.shape[1]==2 - input_gain_db=-18.0 if cid=='quiet' else 0.0 - context = source[(start-2)*SR:(start+17)*SR]*10**(input_gain_db/20) - assert context.shape==(19*SR,2) - sf.write(out/'context-source.wav',context,SR,subtype='FLOAT') - if control: degraded = context.copy() - else: - # libsndfile/LAME constant-bitrate encoding, fixed compression setting. - sf.write(out/'context-degraded.mp3',context,SR,format='MP3',bitrate_mode='CONSTANT',compression_level=.8) - degraded, rate = sf.read(out/'context-degraded.mp3',dtype='float32',always_2d=True) - assert degraded.shape==context.shape and rate==SR - sf.write(out/'context-input.wav',degraded,SR,subtype='FLOAT') - x = torch.from_numpy(np.ascontiguousarray(degraded.T)).unsqueeze(0) - then = time.perf_counter() - with torch.inference_mode(): - prediction = api.run_model(model,x,device,chunk_samples=6*SR,overlap_samples=SR,chunk_pad_samples=SR,chunk_batch_size=1).squeeze(0).numpy() - elapsed = time.perf_counter()-then - sf.write(out/'context-apollo.wav',prediction.T,SR,subtype='FLOAT') - variants = {'source-proxy':context[2*SR:17*SR].T,'degraded':degraded[2*SR:17*SR].T, - 'apollo-0db':prediction[:,2*SR:17*SR]} - stats = {} - for cap in (1,2,3): - then=time.perf_counter();soft,st=soften_texture(prediction,max_reduction_db=cap) - stats[str(cap)]={**st,'seconds':time.perf_counter()-then} - variants[f'apollo-{cap}db']=soft[:,2*SR:17*SR] - metrics={} - for name,v in variants.items(): - sf.write(raw/(name+'.wav'),v.T,SR,subtype='FLOAT') - ref=variants['source-proxy'].astype(np.float64);z=v.astype(np.float64) - gain=float((ref*z).sum()/max((z*z).sum(),1e-30)) - snr=10*np.log10(max((ref*ref).sum(),1e-30)/max(((ref-gain*z)**2).sum(),1e-30)) - _,_,rz=stft(ref,fs=SR,nperseg=2048,noverlap=1024,boundary=None,padded=False) - _,_,vz=stft(z*gain,fs=SR,nperseg=2048,noverlap=1024,boundary=None,padded=False) - spectral=float(np.sqrt(np.mean((20*np.log10(np.maximum(abs(rz),1e-5))-20*np.log10(np.maximum(abs(vz),1e-5)))**2))) - metrics[name]={'proxy_snr_db':float(snr),'proxy_log_spectrum_rmse_db':spectral,'integrated_lufs':float(meter.integrated_loudness(v.T))} - matched={name:v*10**((-23-metrics[name]['integrated_lufs'])/20) for name,v in variants.items()} - truepeak=max(float(abs(resample_poly(v,4,1,axis=-1)).max()) for v in matched.values()) - common=min(1.,10**(-1/20)/truepeak) - names=list(matched);rng.shuffle(names);key={} - for i,name in enumerate(names): - letter=chr(65+i);v=matched[name]*common - sf.write(audition/(letter+'.wav'),v.T,SR,subtype='FLOAT') - key[letter]=name - row=dict(id=cid,source=sid,input_gain_db=input_gain_db,start_seconds=start,duration=15,no_added_degradation=control, - focus=focus,model_seconds=elapsed,postprocessing=stats,metrics=metrics, - blind_key=key,blind_target_lufs=-23+20*np.log10(common),common_gain_db=20*np.log10(common)) - report['cases'].append(row) - (a.output_dir/'measurements.json').write_text(json.dumps(report,indent=2)+'\n') - print(cid,'model seconds',round(elapsed,2),flush=True) - hashes={str(f.relative_to(a.output_dir)):digest(f) for f in a.output_dir.rglob('*.wav')} - (a.output_dir/'SHA256.json').write_text(json.dumps(hashes,indent=2)+'\n') - (a.output_dir/'sources.json').write_text(json.dumps(sources,indent=2)+'\n') - - -if __name__=='__main__': main() diff --git a/tests/measure_texture_memory.py b/tests/measure_texture_memory.py deleted file mode 100644 index 3940423..0000000 --- a/tests/measure_texture_memory.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Measure post-process allocations and process high-water RSS in a fresh process. -RSS includes interpreter/dependencies and input; tracemalloc starts after input. -No model is loaded. Supply a song WAV with --input or a deterministic probe. -""" -import argparse -import json -import platform -import resource -import sys -import time -import tracemalloc -from pathlib import Path -import numpy as np -import scipy -import soundfile as sf -sys.path.insert(0, str(Path(__file__).resolve().parents[1])) -from texture_softening import soften_texture - -p = argparse.ArgumentParser(__doc__) -p.add_argument('--seconds', type=float, required=True) -p.add_argument('--input', type=Path) -p.add_argument('--output', type=Path, required=True) -a = p.parse_args() -sr = 44100 -n = int(a.seconds*sr) -if a.input: - source, sr = sf.read(a.input, dtype='float32', always_2d=True) - if sr != 44100 or source.shape[1] != 2: - raise ValueError('Expected 44.1 kHz stereo source') - x = np.tile(source.T, (1, (n+len(source)-1)//len(source)))[:, :n].copy() -else: - t = np.arange(n)/sr - x = np.array([.2*np.sin(2*np.pi*220*t), .15*np.sin(2*np.pi*331*t)], dtype='float32') - del t -unit = 1 if sys.platform == 'darwin' else 1024 -before = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss*unit -tracemalloc.start() -began = time.perf_counter() -y, stats = soften_texture(x) -seconds = time.perf_counter()-began -current, peak = tracemalloc.get_traced_memory() -tracemalloc.stop() -report = dict(duration=a.seconds, frames=n, channels=2, sample_rate=sr, - numpy=np.__version__, scipy=scipy.__version__, platform=platform.platform(), - input_file=a.input.name if a.input else None, input_repeated=bool(a.input and n>len(source)), - seconds=seconds, traced_peak_bytes=peak, process_peak_rss_bytes=resource.getrusage(resource.RUSAGE_SELF).ru_maxrss*unit, - process_peak_before_bytes=before, statistics=stats, finite=bool(np.isfinite(y).all())) -a.output.parent.mkdir(parents=True, exist_ok=True) -a.output.write_text(json.dumps(report, indent=2)+'\n') -print(json.dumps(report), flush=True)