2026-11-04 13:38:49 +00:00
|
|
|
using System.Text.Json;
|
Add aqua-feel: headless feel meter and gate; fix PRC SYNTH1 clipping and thruster low-rpm hiss
tools/aqua-feel renders every playground patch (thruster at four rpm) and every Ding
event on every instrument offline through faustwasm, measures onset latency, attack,
crest, true peak and clipping, BS.1770 loudness, centroid/flux over time, noise-to-tone
ratio (flat and A-weighted) and a low-motion flag, writes report.json plus a static
gallery, and gates against thresholds.json (waivers must cite a follow-up and go stale).
Nothing is heard; metrics are evidence, not verdicts.
Uses it: PRC SYNTH1 calibrated peaked at 2.10 (18,633 overs); shipped patch gain 1.4 to
0.45, peak 0.67. Thruster low-rpm A-weighted noise-to-tone 20.7/23.0 dB at rpm 300/600
down to 13.1/5.0 by cutting the idle rasp and rattle floors and opening the rumble
low-pass at low load. Trial copy mirrored; its README notes the narrower sweep.
AquaLower gains --dings to lower DingCatalog instruments and events.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 15:31:35 +00:00
|
|
|
using AquaSynth.Dings;
|
2026-11-04 13:38:49 +00:00
|
|
|
using AquaSynth.Dsl;
|
|
|
|
|
|
|
|
|
|
// Lowers .aqua patches to Faust source for the static playground (tools/aqua-playground).
|
|
|
|
|
// usage: AquaLower <outDir> <patchesRoot> <name>=<relative .aqua or .dsp path> ...
|
|
|
|
|
// A .aqua entry goes through FaustEmitter.EmitScript, the same lowering the daemon uses; a .dsp entry
|
|
|
|
|
// (the thruster) is already Faust and is copied verbatim. Writes <outDir>/<name>.dsp, and
|
|
|
|
|
// catalog.json for the browser (the one non-CultNet xeno at this boundary).
|
Add aqua-feel: headless feel meter and gate; fix PRC SYNTH1 clipping and thruster low-rpm hiss
tools/aqua-feel renders every playground patch (thruster at four rpm) and every Ding
event on every instrument offline through faustwasm, measures onset latency, attack,
crest, true peak and clipping, BS.1770 loudness, centroid/flux over time, noise-to-tone
ratio (flat and A-weighted) and a low-motion flag, writes report.json plus a static
gallery, and gates against thresholds.json (waivers must cite a follow-up and go stale).
Nothing is heard; metrics are evidence, not verdicts.
Uses it: PRC SYNTH1 calibrated peaked at 2.10 (18,633 overs); shipped patch gain 1.4 to
0.45, peak 0.67. Thruster low-rpm A-weighted noise-to-tone 20.7/23.0 dB at rpm 300/600
down to 13.1/5.0 by cutting the idle rasp and rattle floors and opening the rumble
low-pass at low load. Trial copy mirrored; its README notes the narrower sweep.
AquaLower gains --dings to lower DingCatalog instruments and events.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 15:31:35 +00:00
|
|
|
// usage: AquaLower --dings <outDir>: lowers each DingCatalog instrument to <outDir>/<id>.dsp and writes dings.json
|
|
|
|
|
// (instruments with their root frequency, events with their notes) so aqua-feel can mix an event the way DingsPlaybackHost does.
|
|
|
|
|
if (args.Length == 2 && args[0] == "--dings")
|
|
|
|
|
{
|
|
|
|
|
var dingDir = Path.GetFullPath(args[1]);
|
|
|
|
|
Directory.CreateDirectory(dingDir);
|
|
|
|
|
var instruments = new List<object>();
|
|
|
|
|
foreach (var ins in DingCatalog.Instruments.Values.OrderBy(i => i.Id, StringComparer.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
var export = FaustEmitter.EmitScript(ins.Script, new FaustExportOptions("dings_" + ins.Id.Replace('-', '_')));
|
|
|
|
|
File.WriteAllText(Path.Combine(dingDir, ins.Id + ".dsp"), export.Source);
|
|
|
|
|
instruments.Add(new { id = ins.Id, rootFrequency = ins.RootFrequency, dsp = ins.Id + ".dsp", controlPath = "/ding/frequency", warnings = export.Warnings });
|
|
|
|
|
Console.WriteLine($"{ins.Id}: {export.Source.Length} chars, {export.Warnings.Count} warnings");
|
|
|
|
|
}
|
|
|
|
|
var events = DingCatalog.Events.Values.OrderBy(e => e.Id, StringComparer.Ordinal)
|
|
|
|
|
.Select(e => new { id = e.Id, meaning = e.Meaning, notes = e.Notes.Select(n => new { semitones = n.Semitones, delayMilliseconds = n.DelayMilliseconds, gain = n.Gain }) });
|
|
|
|
|
File.WriteAllText(Path.Combine(dingDir, "dings.json"), JsonSerializer.Serialize(new { instruments, events, noteSeconds = 1.5 }, new JsonSerializerOptions { WriteIndented = true }));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2026-11-04 13:38:49 +00:00
|
|
|
if (args.Length < 3) { Console.Error.WriteLine("usage: AquaLower <outDir> <patchesRoot> <name>=<path> ..."); return 2; }
|
|
|
|
|
var outDir = Path.GetFullPath(args[0]);
|
|
|
|
|
var root = Path.GetFullPath(args[1]);
|
|
|
|
|
Directory.CreateDirectory(outDir);
|
|
|
|
|
var entries = new List<object>();
|
|
|
|
|
foreach (var spec in args.Skip(2))
|
|
|
|
|
{
|
|
|
|
|
var eq = spec.IndexOf('=');
|
|
|
|
|
var name = spec[..eq];
|
|
|
|
|
var rel = spec[(eq + 1)..];
|
|
|
|
|
var path = Path.Combine(root, rel);
|
|
|
|
|
var text = File.ReadAllText(path);
|
|
|
|
|
string source;
|
|
|
|
|
var warnings = new List<string>();
|
|
|
|
|
if (path.EndsWith(".aqua", StringComparison.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
var export = FaustEmitter.EmitScript(text, new FaustExportOptions(name));
|
|
|
|
|
source = export.Source;
|
|
|
|
|
warnings.AddRange(export.Warnings);
|
|
|
|
|
}
|
|
|
|
|
else source = text;
|
|
|
|
|
File.WriteAllText(Path.Combine(outDir, name + ".dsp"), source);
|
|
|
|
|
entries.Add(new { name, origin = rel.Replace('\\', '/'), kind = path.EndsWith(".aqua", StringComparison.Ordinal) ? "aqua" : "faust", dsp = name + ".dsp", warnings });
|
|
|
|
|
Console.WriteLine($"{name}: {source.Length} chars from {rel}, {warnings.Count} warnings");
|
|
|
|
|
}
|
|
|
|
|
File.WriteAllText(Path.Combine(outDir, "catalog.json"), JsonSerializer.Serialize(entries, new JsonSerializerOptions { WriteIndented = true }));
|
|
|
|
|
return 0;
|