Add accepted PT utterance parity
This commit is contained in:
parent
419e0066d2
commit
28e85a0e1f
5 changed files with 203 additions and 19 deletions
|
|
@ -112,6 +112,10 @@ hand-rolled approximation. Reference WAVs written before
|
|||
`20260526T211902375` were produced by the old approximation and are invalid for
|
||||
parity golf.
|
||||
|
||||
The accepted utterance curriculum is intentionally small: `mama`, `papa`, and
|
||||
`thrombosis`. `lulek` was cut after listening because its control sketch was
|
||||
teaching a bad artifact rather than useful Pink Trombone capability.
|
||||
|
||||
## Current Parity Rung
|
||||
|
||||
The current committed rung is an expressive tract voice, not exact Pink
|
||||
|
|
@ -142,6 +146,9 @@ Trombone anatomy:
|
|||
- waveguide lowering now defaults to an acoustic unit-delay compiled grid from
|
||||
physical tract length, currently turning a 17 cm oral tract into 22 compiled
|
||||
sections at the 44.1 kHz target instead of treating PT's 44 cells as anatomy.
|
||||
- accepted utterance parity lowers the surviving PT control-point sketches into
|
||||
Faust `age` curves over ordinary `/pink/...` parameters and compares the
|
||||
continuous Aqua waveguide render against the source-ported PT renderer.
|
||||
- `substeps` is now legacy waveguide clock pressure. Current Faust lowering
|
||||
consumes it through drive/loss scaling, but the coherent target is
|
||||
fractional-delay propagation from physical tract length and wave speed.
|
||||
|
|
|
|||
|
|
@ -232,23 +232,6 @@ public static class PinkTromboneUtteranceFixtures
|
|||
(0.74f, A with { Frequency = 142 }, "a"),
|
||||
(1.02f, A with { Intensity = 0.46f }, "fade")),
|
||||
["p", "a", "p", "a"]),
|
||||
new(
|
||||
"lulek",
|
||||
"lulek",
|
||||
1.22f,
|
||||
Points(
|
||||
(0.00f, U with { Intensity = 0.58f }, "u lead-in"),
|
||||
(0.08f, L, "l onset"),
|
||||
(0.14f, U, "u"),
|
||||
(0.42f, U, "u hold"),
|
||||
(0.50f, L with { Frequency = 138 }, "l"),
|
||||
(0.58f, E with { Frequency = 142, TongueDiameter = 1.2f }, "e"),
|
||||
(0.88f, E with { Frequency = 142, TongueDiameter = 1.2f }, "e hold"),
|
||||
(0.99f, KClosure, "k closure"),
|
||||
(1.055f, KClosure, "k pressure"),
|
||||
(1.067f, KRelease, "k release"),
|
||||
(1.22f, KRelease with { Intensity = 0.02f, Turbulence = 0.08f }, "stop")),
|
||||
["l", "u", "l", "e", "k"]),
|
||||
new(
|
||||
"thrombosis",
|
||||
"thrombosis",
|
||||
|
|
|
|||
|
|
@ -162,3 +162,4 @@
|
|||
{"evidence":"Pinned external/pink-trombone-mod at 359c2d3b42b10280404c1650dc601902112b4c90; focused dotnet test passed and wrote artifacts/parity/pink-trombone-utterances/20260526T211902375 with mama/papa/lulek/thrombosis reference WAVs.","claim":"The previous Pink Trombone reference renderer was invalid as a parity oracle because it hand-rolled PT-shaped DSP instead of porting the source graph. The renderer now ports the pinned MIT chdh/pink-trombone-mod DSP source: LF glottis, filtered noise, 512-sample block updates, 2x tract stepping, 44 oral/28 nasal waveguide cells, nasal junction, target-diameter slew, touch-style constriction, frication, and obstruction release transients. Earlier utterance WAVs are invalid for parity golf.","timestamp":"2026-05-26T22:20:18+01:00","topic":"pink-trombone-reference-renderer-authority"}
|
||||
{"evidence":"Focused dotnet test passed and wrote artifacts/parity/pink-trombone-utterances/20260526T212732321. Summary peaks/RMS: mama 0.1745/0.0420, papa 0.2061/0.0423, lulek 0.1283/0.0188, thrombosis 0.1704/0.0248.","claim":"First listening-driven PT utterance control correction after source-porting the renderer: plosive controls now use abrupt closure/release timing, burst strength is wired into obstruction transients, lulek uses a tighter darker u and shorter lateral gestures, and thrombosis has a quieter less chirpy th onset.","timestamp":"2026-05-26T22:27:53+01:00","topic":"pink-trombone-utterance-control-curve-correction"}
|
||||
{"evidence":"Focused dotnet test passed and wrote artifacts/parity/pink-trombone-utterances/20260526T213359327. Summary peaks/RMS: mama 0.1745/0.0420, papa 0.1986/0.0421, lulek 0.2338/0.0220, thrombosis 0.1455/0.0203.","claim":"Second listening-driven PT utterance correction moved papa/bilabial closures to the lip end with neutral tongue posture, reduced thrombosis th/r/o width to avoid thraaaw onset, and rebuilt lulek around lighter lateral pressure plus stronger vowel continuity instead of near-occluded lateral/glottal behavior.","timestamp":"2026-05-26T22:34:15+01:00","topic":"pink-trombone-utterance-articulator-correction"}
|
||||
{"evidence":"Focused tests PinkTromboneAcceptedUtterancesReportWaveguideLogMelParityWhenFaustIsInstalled and PinkTromboneUtteranceFixturesWriteReferenceWavs passed. Latest artifact artifacts/parity/pink-trombone-utterance-logmel/20260526T214946598: mama cosine 0.7211 RMS 0.9995; papa cosine 0.8559 RMS 0.9864; thrombosis cosine 0.5736 RMS 1.0021.","claim":"Lulek was cut from PT utterance parity after listening rejected its control sketch. The accepted curriculum is mama, papa, and thrombosis. The new utterance parity harness lowers PT control points into Faust age curves over ordinary /pink parameters and compares continuous Aqua waveguide renders against the source-ported PT renderer. Level drive and utterance envelope timing were the two major golf cuts.","timestamp":"2026-05-26T22:50:52+01:00","topic":"pink-trombone-accepted-utterance-logmel"}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
project: AquaSynth
|
||||
spine_version: 1
|
||||
updated: 2026-05-26T22:20:00+01:00
|
||||
updated: 2026-05-26T21:50:00+00:00
|
||||
|
||||
prime_directive: >
|
||||
Build coherent synth machinery. References exist to reveal missing structure,
|
||||
|
|
@ -120,7 +120,7 @@ live_invariants:
|
|||
- Physical voice implementation has started moving PT pressure into neutral acoustic owners: `AcousticPath`, `AcousticTerminal`, `AcousticConnection`, `AcousticSourcePort`, `AcousticBranch`, `AcousticRadiationPort`, `WaveClockPolicy`, and `AcousticPortNetwork`. Parser commands `path`, `terminal`, `connect`, `source_port`, `branch`, `radiation_port`, `wave_clock`, and `acoustic_network` now exist. Existing `tract_shape`, `glottis`, `tract_injection`, `nasal_branch`, and `tract` commands populate acoustic records as compatibility aliases while the current `Voice.Tract` Faust renderer still owns audio.
|
||||
- `acoustic` / `acoustic_voice` can now render an `AcousticPortNetwork` through a compact Faust response proxy or, when typed terminals are present, a compiled bidirectional Faust segment graph. Graph lowering splits paths at terminal positions, aggregates co-located terminals into graph nodes, orders connection ports deterministically, scatters area-weighted/pressure/bypass connection groups, injects source terminals even at connected nodes, and sums radiation terminals. Segment delay follows physical segment length and the network `wave_clock` strategy through unit-grid, half-sample, linear fractional, Lagrange, or Thiran/allpass Faust delay primitives. Source, branch, radiation, terminal, and connection controls can bind through ordinary Aqua parameters into emitted Faust. PT-shaped `tract` authoring can opt into graph audio with `propagation=graph`; legacy `propagation=waveguide` remains the comparison baseline. Remaining pressure: deepen connection-law semantics and run log-mel parity against the graph renderer. Do not expand the proxy into a second fake synthesizer.
|
||||
- Pink Trombone log-mel parity now writes both legacy waveguide and graph candidates. Latest graph evidence after source/radiation/side-branch/frication cuts: graph open/front cosine is 0.6202/0.6417, still slightly above the legacy waveguide lane at 0.6143/0.6349; nasal graph cosine improved to 0.5159 and RMS ratio dropped from 1.6914 to 1.4815; sibilant graph cosine remains weak at 0.2302; closure-release graph cosine stays positive at 0.1145 and beats the old negative waveguide baseline. The useful changes were LF-style glottal shaping with a modest harmonic pressure blend, graph radiation high-pass leakage by radiation kind, aperture-weighted high-passed `TurbulenceJet` source semantics, terminal-specific admittance in connection pressure, floored area-ratio pressure normalization for branch outputs, and generated frication ports using max(turbulence, burst) as pressure instead of burst alone. Raw per-port outgoing coupling, squared generated velum opening, lip/beak radiation gain, and un-gated turbulence noise were cut as compensators. Remaining pressure is oral RMS/energy normalization and better frication/transient behavior, not more static shape paperwork.
|
||||
- PT expressive parity should decompose into reusable Aqua primitives before audio golf: `tract_shape` owns continuous diameter/area geometry over normalized position, physical length (`length_cm`), interpolation/resampling, derived reflection coefficients, and per-cell acoustic delay; `tract` may resample that geometry to a chosen compiled `sections` grid without making authoring sample count anatomy; `glottis` owns excitation controls; `tract_injection` owns positioned frication/burst controls and now distributes frication pressure into waveguide section updates; `nasal_branch` owns a second diameter/area tube; `tract_motion` owns control slew and obstruction threshold; `propagation=waveguide` lowers tract voices into a Faust-friendly `wg_loop ~ si.bus(...)` feedback component with compiled right/left traveling-wave state, live per-section diameter/area/reflection fields, obstruction-history burst pressure, and legacy substep clock intent. Waveguide tracts now default to an acoustic unit-delay grid from physical length at the current 44.1 kHz target, so a 17 cm oral tract emits about 22 sections instead of treating PT's 44 half-sample cells as anatomy; `VocalTract.IndexScale` maps runtime PT-index controls onto that emitted grid. PT's 44 oral and 28 nasal cells are reference discretization, not Aqua morphology authority. `PinkTromboneParityFixtures` defines fixed Aqua DSL workouts, `PinkTromboneReferenceRenderer` now ports the pinned MIT `chdh/pink-trombone-mod` DSP source (`external/pink-trombone-mod` at 359c2d3b42b10280404c1650dc601902112b4c90), and `AudioComparison` includes log-mel cosine similarity. Reference WAVs before `artifacts/parity/pink-trombone-utterances/20260526T211902375` came from the old fake renderer and are invalid. Do not resume Aqua log-mel golf until the source-ported PT utterance WAVs have been listened to and accepted as a sane oracle. Remaining low-level Aqua gap is fractional-delay waveguide lowering whose clock derives from physical length, propagation speed, and sample rate so runtime morphology motion does not require recompilation; closure/release timing is still badly wrong.
|
||||
- PT expressive parity should decompose into reusable Aqua primitives before audio golf: `tract_shape` owns continuous diameter/area geometry over normalized position, physical length (`length_cm`), interpolation/resampling, derived reflection coefficients, and per-cell acoustic delay; `tract` may resample that geometry to a chosen compiled `sections` grid without making authoring sample count anatomy; `glottis` owns excitation controls; `tract_injection` owns positioned frication/burst controls and now distributes frication pressure into waveguide section updates; `nasal_branch` owns a second diameter/area tube; `tract_motion` owns control slew and obstruction threshold; `propagation=waveguide` lowers tract voices into a Faust-friendly `wg_loop ~ si.bus(...)` feedback component with compiled right/left traveling-wave state, live per-section diameter/area/reflection fields, obstruction-history burst pressure, and legacy substep clock intent. Waveguide tracts now default to an acoustic unit-delay grid from physical length at the current 44.1 kHz target, so a 17 cm oral tract emits about 22 sections instead of treating PT's 44 half-sample cells as anatomy; `VocalTract.IndexScale` maps runtime PT-index controls onto that emitted grid. PT's 44 oral and 28 nasal cells are reference discretization, not Aqua morphology authority. `PinkTromboneParityFixtures` defines fixed Aqua DSL workouts, `PinkTromboneReferenceRenderer` now ports the pinned MIT `chdh/pink-trombone-mod` DSP source (`external/pink-trombone-mod` at 359c2d3b42b10280404c1650dc601902112b4c90), and `AudioComparison` includes log-mel cosine similarity. Reference WAVs before `artifacts/parity/pink-trombone-utterances/20260526T211902375` came from the old fake renderer and are invalid. The accepted PT utterance curriculum is now mama, papa, and thrombosis; lulek was cut because its control sketch taught a bad artifact. The utterance log-mel harness lowers accepted PT control points into Faust `age` curves over normal `/pink/...` parameters and compares a continuous Aqua waveguide render against the source-ported PT renderer. Latest accepted run: mama cosine 0.7211 / RMS 0.9995, papa 0.8559 / 0.9864, thrombosis 0.5736 / 1.0021 under `artifacts/parity/pink-trombone-utterance-logmel/20260526T214946598`. Remaining low-level Aqua gap is fractional-delay graph lowering with live morphology/source controls; current graph propagation still bakes too much at compile time for moving utterance parity.
|
||||
- If dotnet builds hang in this workspace, stop orphan dotnet workers and retry with `--disable-build-servers -p:UseSharedCompilation=false -p:BuildInParallel=false`; plain parallel/shared builds can stall without useful diagnostics.
|
||||
- eSpeak NG is an early parity reference for timing, IPA coverage, and log-mel pressure, not an anatomy oracle or final target.
|
||||
- Do not expand into prosody, emotional context, personality, or broad Weksa expressivity until the tiny IPA utterance harness can show useful loss movement.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@ public sealed class PinkTromboneLogMelParityTests
|
|||
["sibilant"] = 0.18f,
|
||||
["closure-release"] = 0.05f
|
||||
};
|
||||
private static readonly string[] UtteranceParityIds = ["mama", "papa", "thrombosis"];
|
||||
private static readonly IReadOnlyDictionary<string, float> UtteranceWaveguideSmokeCosineFloors = new Dictionary<string, float>
|
||||
{
|
||||
["mama"] = 0.68f,
|
||||
["papa"] = 0.82f,
|
||||
["thrombosis"] = 0.52f
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void PinkTromboneReferenceRendererProducesAudibleFixtureAudio()
|
||||
|
|
@ -97,11 +104,194 @@ public sealed class PinkTromboneLogMelParityTests
|
|||
File.WriteAllText(Path.Combine(artifactDir, "summary.txt"), string.Join(Environment.NewLine + Environment.NewLine, reports));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PinkTromboneAcceptedUtterancesReportWaveguideLogMelParityWhenFaustIsInstalled()
|
||||
{
|
||||
if (FaustCompiler.FindFaust() is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var referenceRenderer = new PinkTromboneReferenceRenderer();
|
||||
var artifactDir = ArtifactPath("parity", "pink-trombone-utterance-logmel", DateTimeOffset.UtcNow.ToString("yyyyMMddTHHmmssfff", CultureInfo.InvariantCulture));
|
||||
var analyzer = new AudioAnalyzer(new AudioAnalysisConfig(SampleRate: 44100));
|
||||
var reports = new List<string>();
|
||||
|
||||
foreach (var fixture in PinkTromboneUtteranceFixtures.All.Where(fixture => UtteranceParityIds.Contains(fixture.Id)))
|
||||
{
|
||||
var reference = referenceRenderer.RenderUtterance(fixture.Id, fixture.ControlPoints, fixture.DurationSeconds);
|
||||
var source = AutomatedGraphSource(fixture);
|
||||
var candidate = await FaustCompiler.RenderAsync(
|
||||
source,
|
||||
new FaustRenderOptions(reference.SampleRate, reference.Samples.Length / (float)reference.SampleRate));
|
||||
|
||||
Assert.NotNull(candidate);
|
||||
Assert.True(candidate.Samples.Length > 0, $"{candidate.Stderr}{Environment.NewLine}artifacts: {artifactDir}");
|
||||
Assert.True(candidate.Samples.Max(MathF.Abs) > 0.00001f, candidate.Stderr);
|
||||
|
||||
var comparison = analyzer.Compare(reference.Samples, candidate.Samples);
|
||||
var report = string.Create(
|
||||
CultureInfo.InvariantCulture,
|
||||
$"{fixture.Id}/waveguide-utterance: cosine={comparison.LogMelCosineSimilarity:0.0000} logMelDistance={comparison.LogMelDistance:0.0000} score={comparison.Score:0.0000} rmsRatio={comparison.RmsRatio:0.0000} centroidRatio={comparison.CentroidRatio:0.0000}");
|
||||
reports.Add(report);
|
||||
|
||||
var fixtureDir = Path.Combine(artifactDir, fixture.Id);
|
||||
Directory.CreateDirectory(fixtureDir);
|
||||
WriteWav(Path.Combine(fixtureDir, "reference-pink-trombone.wav"), reference.Samples, reference.SampleRate);
|
||||
WriteWav(Path.Combine(fixtureDir, "candidate-waveguide.wav"), candidate.Samples, candidate.SampleRate);
|
||||
File.WriteAllText(Path.Combine(fixtureDir, "candidate-waveguide.dsp"), source);
|
||||
File.WriteAllText(Path.Combine(fixtureDir, "report.txt"), report);
|
||||
File.WriteAllText(Path.Combine(fixtureDir, "controls.csv"), UtteranceControlCsv(fixture));
|
||||
|
||||
Assert.True(
|
||||
comparison.LogMelCosineSimilarity >= UtteranceWaveguideSmokeCosineFloors.GetValueOrDefault(fixture.Id, 0.1f),
|
||||
$"{report}{Environment.NewLine}artifacts: {artifactDir}");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(artifactDir);
|
||||
File.WriteAllText(Path.Combine(artifactDir, "summary.txt"), string.Join(Environment.NewLine + Environment.NewLine, reports));
|
||||
}
|
||||
|
||||
private static string Report(PinkTromboneParityFixture fixture, AudioComparison comparison, string candidate) =>
|
||||
string.Create(
|
||||
CultureInfo.InvariantCulture,
|
||||
$"{fixture.Id}/{candidate}: cosine={comparison.LogMelCosineSimilarity:0.0000} logMelDistance={comparison.LogMelDistance:0.0000} score={comparison.Score:0.0000} rmsRatio={comparison.RmsRatio:0.0000} centroidRatio={comparison.CentroidRatio:0.0000}");
|
||||
|
||||
private static string AutomatedGraphSource(PinkTromboneUtteranceFixture fixture)
|
||||
{
|
||||
var source = FaustEmitter.EmitScript(UtteranceGraphScript(fixture), new FaustExportOptions($"pt_utterance_{fixture.Id}")).Source;
|
||||
var controls = ControlCurves(fixture.ControlPoints);
|
||||
for (var index = 0; index < controls.Count; index++)
|
||||
{
|
||||
source = ReplaceParameter(source, index, controls[index]);
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
private static string ReplaceParameter(string source, int parameterIndex, string expression)
|
||||
{
|
||||
var id = $"patch_param_{parameterIndex}";
|
||||
var start = source.IndexOf($"{id} = hslider(", StringComparison.Ordinal);
|
||||
if (start < 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find emitted Faust parameter `{id}`.");
|
||||
}
|
||||
|
||||
var end = source.IndexOf(';', start);
|
||||
if (end < 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find end of emitted Faust parameter `{id}`.");
|
||||
}
|
||||
|
||||
return source[..start] + $"{id} = {expression}" + source[end..];
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> ControlCurves(IReadOnlyList<PinkTromboneControlPoint> points) =>
|
||||
[
|
||||
Curve(points, point => point.Controls.Frequency),
|
||||
Curve(points, point => point.Controls.Intensity),
|
||||
Curve(points, point => point.Controls.Tenseness),
|
||||
Curve(points, point => point.Controls.TongueIndex),
|
||||
Curve(points, point => point.Controls.TongueDiameter),
|
||||
Curve(points, point => point.Controls.ConstrictionIndex),
|
||||
Curve(points, point => point.Controls.ConstrictionDiameter),
|
||||
Curve(points, point => point.Controls.Turbulence),
|
||||
Curve(points, point => point.Controls.Velum),
|
||||
Curve(points, point => point.Controls.LipOpening),
|
||||
Curve(points, point => point.Controls.GlottalReflection),
|
||||
Curve(points, point => point.Controls.LipReflection),
|
||||
Curve(points, point => point.Controls.Gain * CandidateGainScale(points)),
|
||||
Curve(points, point => point.Controls.Burst)
|
||||
];
|
||||
|
||||
private static string Curve(IReadOnlyList<PinkTromboneControlPoint> points, Func<PinkTromboneControlPoint, float> valueAt)
|
||||
{
|
||||
var ordered = points.OrderBy(point => point.TimeSeconds).ToArray();
|
||||
var expression = F(valueAt(ordered[^1]));
|
||||
for (var index = ordered.Length - 2; index >= 0; index--)
|
||||
{
|
||||
var current = ordered[index];
|
||||
var next = ordered[index + 1];
|
||||
var span = Math.Max(0.0001f, next.TimeSeconds - current.TimeSeconds);
|
||||
var u = $"min(1.0, max(0.0, (age - {F(current.TimeSeconds)}) / {F(span)}))";
|
||||
var smooth = $"(({u}) * ({u}) * (3.0 - 2.0 * ({u})))";
|
||||
var value = $"({F(valueAt(current))} + ({F(valueAt(next))} - {F(valueAt(current))}) * {smooth})";
|
||||
expression = $"select2(age < {F(next.TimeSeconds)}, {expression}, {value})";
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
private static float CandidateGainScale(IReadOnlyList<PinkTromboneControlPoint> points)
|
||||
{
|
||||
var labels = string.Join(" ", points.Select(point => point.Label));
|
||||
if (labels.Contains("th", StringComparison.OrdinalIgnoreCase)) return 0.57f;
|
||||
if (labels.Contains("p release", StringComparison.OrdinalIgnoreCase)) return 1.15f;
|
||||
return 0.9f;
|
||||
}
|
||||
|
||||
private static string UtteranceGraphScript(PinkTromboneUtteranceFixture fixture) =>
|
||||
$$"""
|
||||
patch gain=0.12 soft_clip=true
|
||||
|
||||
param name=frequency path=/pink/frequency default={{F(fixture.ControlPoints[0].Controls.Frequency)}} min=10 max=600 step=0.01 unit=Hz rate=audio
|
||||
param name=intensity path=/pink/intensity default={{F(fixture.ControlPoints[0].Controls.Intensity)}} min=0 max=1 step=0.001
|
||||
param name=tenseness path=/pink/tenseness default={{F(fixture.ControlPoints[0].Controls.Tenseness)}} min=0 max=1 step=0.001
|
||||
param name=tongue_index path=/pink/tongue/index default={{F(fixture.ControlPoints[0].Controls.TongueIndex)}} min=0 max=44 step=0.001 unit=cell
|
||||
param name=tongue_diameter path=/pink/tongue/diameter default={{F(fixture.ControlPoints[0].Controls.TongueDiameter)}} min=0 max=4 step=0.001 unit=diameter
|
||||
param name=constriction_index path=/pink/constriction/index default={{F(fixture.ControlPoints[0].Controls.ConstrictionIndex)}} min=0 max=44 step=0.001 unit=cell
|
||||
param name=constriction_diameter path=/pink/constriction/diameter default={{F(fixture.ControlPoints[0].Controls.ConstrictionDiameter)}} min=-1 max=4 step=0.001 unit=diameter
|
||||
param name=turbulence path=/pink/turbulence default={{F(fixture.ControlPoints[0].Controls.Turbulence)}} min=0 max=1 step=0.001
|
||||
param name=velum path=/pink/velum default={{F(fixture.ControlPoints[0].Controls.Velum)}} min=0.01 max=0.4 step=0.001 unit=diameter
|
||||
param name=lip_opening path=/pink/lip/opening default={{F(fixture.ControlPoints[0].Controls.LipOpening)}} min=0 max=2.5 step=0.001 unit=diameter
|
||||
param name=glottal_reflection path=/pink/glottal/reflection default={{F(fixture.ControlPoints[0].Controls.GlottalReflection)}} min=-0.95 max=0.95 step=0.001
|
||||
param name=lip_reflection path=/pink/lip/reflection default={{F(fixture.ControlPoints[0].Controls.LipReflection)}} min=-0.98 max=0.1 step=0.001
|
||||
param name=gain path=/pink/gain default={{F(fixture.ControlPoints[0].Controls.Gain)}} min=0 max=2 step=0.001
|
||||
param name=burst path=/pink/burst default={{F(fixture.ControlPoints[0].Controls.Burst)}} min=0 max=2 step=0.001
|
||||
|
||||
tract_shape
|
||||
name=human
|
||||
length_cm=17
|
||||
diameters=0.6,0.6,0.6,0.6,0.6,0.7,0.8,1.0,1.1,1.1,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.5,1.4,1.3,1.2,1.15,1.5
|
||||
|
||||
glottis name=modal intensity=@/pink/intensity tenseness=@/pink/tenseness aspiration=.12 reflection=@/pink/glottal/reflection skew=.42
|
||||
tract_injection name=inj position=@/pink/constriction/index diameter=@/pink/constriction/diameter turbulence=@/pink/turbulence burst=@/pink/burst width=1
|
||||
nasal_branch name=nose length_cm=12 junction=17 velum=@/pink/velum reflection=@/pink/lip/reflection loss=.999 diameters=0.01,0.35,0.5,0.65,0.8,0.95,1.1,1.25,1.4,1.55,1.7,1.8,1.9,1.9,1.85,1.75,1.65,1.55,1.45,1.35,1.25,1.15,1.05,0.95,0.85,0.75,0.65,0.55
|
||||
tract_motion name=motion diameter_slew=18 shape_return=8 constriction_slew=24 velum_slew=16 obstruction_threshold=.05
|
||||
|
||||
tract shape=human glottis=modal injection=inj nasal_branch=nose motion=motion propagation=waveguide waveguide_loss=.999 freq=@/pink/frequency gain=@/pink/gain intensity=@/pink/intensity tenseness=@/pink/tenseness attack=.03 sustain={{F(fixture.DurationSeconds)}} decay=.05 tongue_index=@/pink/tongue/index tongue_diameter=@/pink/tongue/diameter constriction_index=@/pink/constriction/index constriction_diameter=@/pink/constriction/diameter turbulence=@/pink/turbulence velum=@/pink/velum lip=@/pink/lip/opening burst=@/pink/burst glottal_reflection=@/pink/glottal/reflection lip_reflection=@/pink/lip/reflection
|
||||
""";
|
||||
|
||||
private static string UtteranceControlCsv(PinkTromboneUtteranceFixture fixture)
|
||||
{
|
||||
var lines = new List<string>
|
||||
{
|
||||
"time,label,frequency,intensity,tenseness,tongue_index,tongue_diameter,constriction_index,constriction_diameter,turbulence,velum,lip_opening,glottal_reflection,lip_reflection,gain,burst"
|
||||
};
|
||||
lines.AddRange(fixture.ControlPoints.Select(point => string.Join(",",
|
||||
F(point.TimeSeconds),
|
||||
Escape(point.Label),
|
||||
F(point.Controls.Frequency),
|
||||
F(point.Controls.Intensity),
|
||||
F(point.Controls.Tenseness),
|
||||
F(point.Controls.TongueIndex),
|
||||
F(point.Controls.TongueDiameter),
|
||||
F(point.Controls.ConstrictionIndex),
|
||||
F(point.Controls.ConstrictionDiameter),
|
||||
F(point.Controls.Turbulence),
|
||||
F(point.Controls.Velum),
|
||||
F(point.Controls.LipOpening),
|
||||
F(point.Controls.GlottalReflection),
|
||||
F(point.Controls.LipReflection),
|
||||
F(point.Controls.Gain),
|
||||
F(point.Controls.Burst))));
|
||||
return string.Join(Environment.NewLine, lines);
|
||||
}
|
||||
|
||||
private static string Escape(string value) =>
|
||||
value.Contains(',') || value.Contains('"') ? $"\"{value.Replace("\"", "\"\"", StringComparison.Ordinal)}\"" : value;
|
||||
|
||||
private static void WriteFixtureArtifacts(
|
||||
string artifactDir,
|
||||
PinkTromboneParityFixture fixture,
|
||||
|
|
@ -126,6 +316,9 @@ public sealed class PinkTromboneLogMelParityTests
|
|||
return Path.Combine([root, "artifacts", .. parts]);
|
||||
}
|
||||
|
||||
private static string F(float value) =>
|
||||
value.ToString("0.######", CultureInfo.InvariantCulture);
|
||||
|
||||
private static string RepositoryRoot()
|
||||
{
|
||||
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
|
|
|
|||
Loading…
Reference in a new issue