Apply DX7 envelope lowering
This commit is contained in:
parent
ae152fb490
commit
65f5f84c02
9 changed files with 136 additions and 25 deletions
|
|
@ -75,6 +75,12 @@ math is still fake in the places that matter perceptually.
|
|||
- Aquarium staged operator envelopes now support per-segment curves, e.g.
|
||||
`env=rl rates=.00145,.0508,.5268,.35 levels=2,.297,.0156,0 curves=lin,exp,exp,exp`.
|
||||
This is a general staged-contour feature, not a DX7-specific syntax escape.
|
||||
- `Dx7SysEx.ApproximateAppliedRateLevelEnvelope` lowers a DX7 EG into a
|
||||
curved staged contour by tracing Dexed-style block-interpolated applied
|
||||
gain and normalizing it against Aquarium's separate operator-output level.
|
||||
On `PRC SYNTH1`, this improves log-mel from `0.24476814` to `0.21931955`
|
||||
and envelope distance from `0.43700194` to `0.10271241`; zero-crossing
|
||||
remains bad, so oscillator/phase behavior is still open pressure.
|
||||
|
||||
7. **Summed/cascaded modulation**
|
||||
- Render project-authored algorithm-8 stacks that isolate `6 -> 5 -> 3`
|
||||
|
|
@ -90,6 +96,8 @@ math is still fake in the places that matter perceptually.
|
|||
- Keep the log-mel gate and listening artifacts.
|
||||
- Raise thresholds only when the calibrated lowering improves for the right
|
||||
reason.
|
||||
- Current curved-envelope replay gates log-mel at `<= 0.225`, envelope
|
||||
distance at `<= 0.12`, RMS ratio near unity, and score at `>= 0.50`.
|
||||
|
||||
## Rejected Path
|
||||
|
||||
|
|
|
|||
|
|
@ -79,15 +79,15 @@ First deliverable:
|
|||
rendering exist, and `analog1.syx` voice 13 has the first thresholded parity
|
||||
rebuild. The next pressure is a harder voice that actually needs audible
|
||||
operator routing rather than a sine-like behavioral match. Staged operator
|
||||
envelopes now exist as readable `env=rl rates=... levels=...` declarations;
|
||||
direct DX7 rate/level lowering on `PRC SYNTH1` still fails hard enough to
|
||||
make EG timing/gain calibration the live pressure. The hard PRC probe now
|
||||
writes ignored listening artifacts under `artifacts/parity/dx7-prc-synth1`
|
||||
so metrics and ears can steer the next calibration pass together. Listening
|
||||
showed aggregate score was a weak judge for this target, so the hard PRC
|
||||
probe now gates primarily on log-mel distance; remaining mismatch points at
|
||||
DX7 output/index scaling and algorithm output compensation rather than a
|
||||
missing authoring syntax. The calibration ladder lives in
|
||||
envelopes now exist as readable `env=rl rates=... levels=...` declarations
|
||||
with per-segment curves. Direct DX7 rate/level lowering on `PRC SYNTH1`
|
||||
forced EG timing/gain calibration; the current applied-envelope lowering
|
||||
improves log-mel and envelope distance but exposes worse zero-crossing/phase
|
||||
behavior. The hard PRC probe writes ignored listening artifacts under
|
||||
`artifacts/parity/dx7-prc-synth1` so metrics and ears can steer the next
|
||||
calibration pass together. Listening showed aggregate score was a weak judge
|
||||
for this target, so the hard PRC probe gates primarily on log-mel distance.
|
||||
The calibration ladder lives in
|
||||
[dx7-calibration-plan.md](dx7-calibration-plan.md) and should be followed
|
||||
before more PRC-specific tuning.
|
||||
|
||||
|
|
|
|||
|
|
@ -275,6 +275,48 @@ public static class Dx7SysEx
|
|||
"Approximation: DX7 EG uses four rate/level segments; Aquarium staged envelopes preserve the intermediate target levels.");
|
||||
}
|
||||
|
||||
public static Dx7RateLevelEnvelopeApproximation ApproximateAppliedRateLevelEnvelope(
|
||||
Dx7Envelope envelope,
|
||||
float gateSeconds,
|
||||
float durationSeconds = 1.4f,
|
||||
int sampleRate = 44100)
|
||||
{
|
||||
var trace = TraceInterpolatedEnvelope(
|
||||
envelope,
|
||||
gateSeconds,
|
||||
Math.Max(durationSeconds, gateSeconds + 0.55f),
|
||||
sampleRate: sampleRate);
|
||||
var firstStage2 = FirstStageIndex(trace, 2);
|
||||
var firstStage3 = FirstStageIndex(trace, 3);
|
||||
|
||||
var peakSample = Math.Min(63, trace.Count - 1);
|
||||
var level2Sample = firstStage2 >= 0 ? firstStage2 : Math.Min(peakSample + 64, trace.Count - 1);
|
||||
var sustainSample = Math.Clamp(firstStage3 >= 0 ? firstStage3 : (int)(gateSeconds * sampleRate), 0, trace.Count - 1);
|
||||
|
||||
var r1 = Math.Max(64f / sampleRate, trace[peakSample].TimeSeconds);
|
||||
var r2 = Math.Max(64f / sampleRate, trace[level2Sample].TimeSeconds - r1);
|
||||
var r3 = Math.Max(64f / sampleRate, trace[sustainSample].TimeSeconds - r1 - r2);
|
||||
var release = Math.Max(0.02f, Math.Min(0.6f, trace[^1].TimeSeconds - gateSeconds));
|
||||
|
||||
const float fullLevelGainNormalization = 0.5f;
|
||||
return new Dx7RateLevelEnvelopeApproximation(
|
||||
new RateLevelEnvelope(
|
||||
r1,
|
||||
trace[peakSample].Gain * fullLevelGainNormalization,
|
||||
r2,
|
||||
trace[level2Sample].Gain * fullLevelGainNormalization,
|
||||
r3,
|
||||
trace[sustainSample].Gain * fullLevelGainNormalization,
|
||||
release,
|
||||
0f,
|
||||
RateLevelCurve.Linear,
|
||||
RateLevelCurve.Exponential,
|
||||
RateLevelCurve.Exponential,
|
||||
RateLevelCurve.Exponential),
|
||||
gateSeconds,
|
||||
"Approximation: DX7 applied EG gain sampled per 64-sample block and normalized for Aquarium operator-level scaling.");
|
||||
}
|
||||
|
||||
public static IReadOnlyList<Dx7EnvelopeTracePoint> TraceEnvelope(
|
||||
Dx7Envelope envelope,
|
||||
float gateSeconds,
|
||||
|
|
@ -334,6 +376,16 @@ public static class Dx7SysEx
|
|||
return points;
|
||||
}
|
||||
|
||||
private static int FirstStageIndex(IReadOnlyList<Dx7EnvelopeTracePoint> trace, int stage)
|
||||
{
|
||||
for (var i = 0; i < trace.Count; i++)
|
||||
{
|
||||
if (trace[i].Stage == stage) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static Dx7VoiceBank ParseBank(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
var data = PackedBankData(bytes);
|
||||
|
|
|
|||
|
|
@ -39,3 +39,4 @@
|
|||
{"kind":"dx7_envelope_trace","lesson":"Direct DX7 EG tracing shows the current `env=rl` abstraction is the wrong shape for some DX7 envelopes: the traced EG can spike above unit gain and drop below .02 within about 9 ms while `env=rl` is still near 1. A runtime `env=dx7` surface was attempted and cut because matching the local trace did not yet match Dexed plugin audio.","source":"DX7 envelope trace artifact on 2026-05-14"}
|
||||
{"kind":"dx7_envelope_applied_gain","lesson":"Dexed-style operator audio does not use the raw DX7 EG state directly. It samples the EG once per 64-sample block and linearly interpolates applied gain across the block. A DX7-capable Aquarium envelope must model that applied-gain timebase; matching raw EG trace alone can make a candidate far too quiet or otherwise wrong.","source":"DX7 interpolated envelope trace artifact on 2026-05-14"}
|
||||
{"kind":"curved_rate_level_envelope","lesson":"Aquarium staged operator envelopes now support per-segment curves and deliberate levels above unity. Keep this authority in `RateLevelEnvelope`: it protects the invariant that multi-target operator contours can model exponential synth envelopes without inventing one-off DX7 syntax.","source":"Curved rate/level envelope implementation on 2026-05-14"}
|
||||
{"kind":"dx7_applied_envelope_lowering","lesson":"`Dx7SysEx.ApproximateAppliedRateLevelEnvelope` improves the hard PRC target by tracing Dexed-style block-interpolated EG gain, normalizing it against Aquarium operator output level, and emitting curved `env=rl`. Latest PRC metrics: log-mel 0.21931955, envelope distance 0.10271241, RMS ratio 1.0003664, score 0.5026183. Zero-crossing worsened to 0.28642866, so contour parity exposed a remaining oscillator/phase mismatch rather than solving DX7 parity outright.","source":"DX7 applied-envelope PRC probe on 2026-05-14"}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
"DX7 topology-aware route lowering",
|
||||
"DX7 envelope trace helper",
|
||||
"DX7 block-interpolated envelope applied-gain trace helper",
|
||||
"DX7 applied-envelope curved lowering",
|
||||
"staged operator envelope DSL",
|
||||
"curved staged operator envelope segments",
|
||||
"DX7 envelope-to-ADSR approximation",
|
||||
|
|
@ -71,9 +72,9 @@
|
|||
"rule": "Add DSL abstractions only when reference rebuilds prove the missing invariant. Do not consume references that cannot be backed by parity tests; parsing alone is inventory, not evidence of expressivity."
|
||||
},
|
||||
"dx7_reference_path": {
|
||||
"status": "DX7 SysEx parser extracts voice edit buffers, packed voices, packed 32-voice banks, six operators, envelopes, algorithm, feedback, oscillator sync, LFO, transpose, voice name, checksum validation, ReferencePatch features, algorithm topology from the reverse-engineered OPS algorithm ROM table, envelope-to-ADSR approximations, measured carrier output amplitude, isolated two-op modulation route scale, calibrated feedback amount table, ratio detune lowering, ROM COM output compensation, topology-aware route lowering, staged operator envelopes with per-segment curves, raw and block-interpolated DX7 envelope tracing, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference rendering, project-authored Dexed patch rendering, Aquarium candidate rendering through Faust-generated C#, one thresholded rendered parity test for analog1.syx voice 13, a hard PRC SYNTH1 parity probe with listening WAV artifacts and log-mel <= 0.255, project-authored algorithm-8 summed-stack parity, and renderable delayed operator feedback.",
|
||||
"lesson": "Algorithm 32's additive six-carrier shape maps cleanly to six Aquarium voices. Algorithm 8 justified OperatorGraph: operators, modulation edges, carriers, and feedback belong to a graph authority, not Voice fields. The first rendered parity rung is behavioral and sine-like, not exact DX7 operator execution. A harder algorithm-8 target exposed that feedback must be delayed recursion in Faust, not a direct cyclic self-reference. Static DX7 output-level approximation helped import/rebuild math but was mathematically wrong for carrier amplitude. A single-carrier Dexed sweep shows carrier amplitude follows 2^((outputLevel - 99) / 8). Isolated two-op DX7 FM maps full-level modulation to Aquarium route index 6.275, while summed/cascaded algorithm-8 branches need a lower topology-aware route scale. Feedback now lowers through a fitted table instead of PRC-local constants. Ratio-mode detune belongs in operator frequency lowering. ROM COM values carry output-count compensation; because the amplitude curve was fitted against algorithm 32, DX7 carriers scale by 6/(COM+1). Project-authored Dexed patch rendering now gives lawful calibration targets. Reusing the operator output curve for EG levels was tested and cut because it failed hard-target log-mel. Direct envelope tracing shows current linear `env=rl` cannot express DX7 EG spike/drop behavior. Dexed-style audio samples EG once per 64-sample block and interpolates applied gain, so raw EG parity alone is not audio parity. Staged operator envelopes now support linear/exponential segments and supranormal levels, which lets the DSL approximate that contour without a DX7-only syntax escape.",
|
||||
"next": "Use the curved staged-envelope surface plus `Dx7SysEx.TraceEnvelope`, `Dx7SysEx.TraceInterpolatedEnvelope`, and `artifacts/parity/dx7-envelope-trace/egstep.csv` to calibrate a DX7 lowering that improves rendered PRC log-mel before adding any more public envelope syntax."
|
||||
"status": "DX7 SysEx parser extracts voice edit buffers, packed voices, packed 32-voice banks, six operators, envelopes, algorithm, feedback, oscillator sync, LFO, transpose, voice name, checksum validation, ReferencePatch features, algorithm topology from the reverse-engineered OPS algorithm ROM table, envelope-to-ADSR approximations, measured carrier output amplitude, isolated two-op modulation route scale, calibrated feedback amount table, ratio detune lowering, ROM COM output compensation, topology-aware route lowering, staged operator envelopes with per-segment curves, raw and block-interpolated DX7 envelope tracing, applied-envelope curved lowering, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference rendering, project-authored Dexed patch rendering, Aquarium candidate rendering through Faust-generated C#, one thresholded rendered parity test for analog1.syx voice 13, a hard PRC SYNTH1 parity probe with listening WAV artifacts and log-mel <= 0.225, project-authored algorithm-8 summed-stack parity, and renderable delayed operator feedback.",
|
||||
"lesson": "Algorithm 32's additive six-carrier shape maps cleanly to six Aquarium voices. Algorithm 8 justified OperatorGraph: operators, modulation edges, carriers, and feedback belong to a graph authority, not Voice fields. The first rendered parity rung is behavioral and sine-like, not exact DX7 operator execution. A harder algorithm-8 target exposed that feedback must be delayed recursion in Faust, not a direct cyclic self-reference. Static DX7 output-level approximation helped import/rebuild math but was mathematically wrong for carrier amplitude. A single-carrier Dexed sweep shows carrier amplitude follows 2^((outputLevel - 99) / 8). Isolated two-op DX7 FM maps full-level modulation to Aquarium route index 6.275, while summed/cascaded algorithm-8 branches need a lower topology-aware route scale. Feedback now lowers through a fitted table instead of PRC-local constants. Ratio-mode detune belongs in operator frequency lowering. ROM COM values carry output-count compensation; because the amplitude curve was fitted against algorithm 32, DX7 carriers scale by 6/(COM+1). Project-authored Dexed patch rendering now gives lawful calibration targets. Reusing the operator output curve for EG levels was tested and cut because it failed hard-target log-mel. Direct envelope tracing shows current linear `env=rl` cannot express DX7 EG spike/drop behavior. Dexed-style audio samples EG once per 64-sample block and interpolates applied gain, so raw EG parity alone is not audio parity. Staged operator envelopes now support linear/exponential segments and supranormal levels. `ApproximateAppliedRateLevelEnvelope` uses that surface and improves PRC log-mel/envelope metrics, but worsens zero-crossing, so oscillator/phase behavior remains unsolved.",
|
||||
"next": "Use the PRC zero-crossing/phase mismatch and project-authored envelope sweeps to separate contour wins from timbre damage before broad DX7 import."
|
||||
},
|
||||
"note_and_envelope_model": {
|
||||
"rule": "Notes own frequency, gate duration, and host/MIDI source. Envelopes own ADSR level shape.",
|
||||
|
|
@ -88,7 +89,7 @@
|
|||
},
|
||||
"last_verified": {
|
||||
"command": "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore",
|
||||
"result": "65 passed; staged envelope curves parse/render and DX7 trace artifact includes raw/applied/curved gain",
|
||||
"commit": "pending DX7 interpolated envelope trace"
|
||||
"result": "66 passed; PRC log-mel 0.21931955 and envelope distance 0.10271241 with applied-envelope lowering",
|
||||
"commit": "pending DX7 applied-envelope lowering"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,15 @@ Doctrine update:
|
|||
|
||||
Completed this slice:
|
||||
|
||||
- Added `Dx7SysEx.ApproximateAppliedRateLevelEnvelope`, which traces the
|
||||
Dexed-style block-interpolated applied EG gain and lowers it into Aquarium's
|
||||
curved staged envelope surface, normalized so operator output level remains a
|
||||
separate authority.
|
||||
- Retried hard `PRC SYNTH1` with the applied-envelope lowering. Latest focused
|
||||
run: log-mel `0.21931955` (was `0.24476814`), envelope distance
|
||||
`0.10271241` (was `0.43700194`), RMS ratio `1.0003664`, score `0.5026183`.
|
||||
Zero-crossing ratio got worse at `0.28642866`, so oscillator/phase behavior
|
||||
is the next exposed mismatch.
|
||||
- Extended staged operator envelopes with per-segment curves:
|
||||
`curves=lin,exp,exp,lin`. Linear remains the default, and levels can now
|
||||
express deliberate transient overshoot above `1` instead of clipping away
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
project: AquariumSynthCSharp
|
||||
spine_version: 1
|
||||
updated: 2026-05-14T05:42:00+01:00
|
||||
updated: 2026-05-14T12:39:00+01:00
|
||||
|
||||
prime_directive: >
|
||||
Build coherent synth machinery. References exist to reveal missing structure,
|
||||
|
|
@ -57,6 +57,7 @@ live_invariants:
|
|||
- DX7 rate/level envelopes may lower to staged operator envelopes or labeled ADSR approximations, but exact DX7 EG timing/gain calibration remains missing pressure.
|
||||
- DX7 envelope trace evidence now lives in `Dx7SysEx.TraceEnvelope`, `Dx7SysEx.TraceInterpolatedEnvelope`, and `artifacts/parity/dx7-envelope-trace/egstep.csv`; Dexed-style audio uses block-interpolated applied gain, not the raw EG state alone.
|
||||
- Do not add an `env=dx7` runtime surface until it matches Dexed plugin audio with the block-interpolated gain model, not only the raw trace helper.
|
||||
- DX7 applied envelope lowering now lives in `Dx7SysEx.ApproximateAppliedRateLevelEnvelope`; it normalizes traced applied gain so operator output level remains a separate authority.
|
||||
- Recompile only for graph shape changes; vary declared parameters through the hosted DSP control API.
|
||||
- Reference fixtures must preserve provenance, license status, source hash, and feature pressure.
|
||||
- Public-domain reference fixtures and dexed-py helpers are test/development material only; the NuGet package must ship only AquariumSynth.Dsl.
|
||||
|
|
@ -115,10 +116,10 @@ next_agent_start_here:
|
|||
two-op modulation index, feedback, ratio detune, ROM COM output
|
||||
compensation, and topology-aware route lowering are now fitted or mapped;
|
||||
a project-authored algorithm-8 stack test now backs the summed route scale
|
||||
outside PRC. The attempted exponential EG level curve was cut because it
|
||||
still failed PRC log-mel. Use `Dx7SysEx.TraceEnvelope`,
|
||||
`Dx7SysEx.TraceInterpolatedEnvelope`, and the `dx7-envelope-trace` artifact
|
||||
to design the next envelope model.
|
||||
outside PRC. Curved applied-envelope lowering now improves the hard PRC
|
||||
log-mel and envelope metrics, but zero-crossing/phase behavior is worse.
|
||||
Next pressure is oscillator/phase behavior or a project-authored envelope
|
||||
sweep that can separate contour wins from timbre damage.
|
||||
must_read:
|
||||
- AGENTS.md
|
||||
- docs/audio-doctrine.md
|
||||
|
|
@ -131,6 +132,16 @@ next_agent_start_here:
|
|||
- "dotnet test AquariumSynthCSharp.slnx --no-restore"
|
||||
|
||||
completed_slices:
|
||||
- date: 2026-05-14
|
||||
summary: >
|
||||
Added `Dx7SysEx.ApproximateAppliedRateLevelEnvelope` and used it in the
|
||||
hard PRC SYNTH1 probe. The helper traces Dexed-style block-interpolated
|
||||
applied EG gain, normalizes it against Aquarium operator-output scaling,
|
||||
and emits curved `env=rl` syntax. PRC log-mel improved from 0.24476814 to
|
||||
0.21931955 and envelope distance from 0.43700194 to 0.10271241, with RMS
|
||||
ratio near unity. Zero-crossing ratio worsened to 0.28642866, so parity
|
||||
is still not solved.
|
||||
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled|ProjectAuthoredDx7AlgorithmEightSummedStackMeetsParityWhenInstalled|ApproximatesDx7AppliedEnvelopeAsCurvedRateLevelEnvelope|TracesDx7EnvelopeAppliedBlockInterpolatedGain\": passed"
|
||||
- date: 2026-05-14
|
||||
summary: >
|
||||
Added per-segment curves to staged operator envelopes. `env=rl` remains
|
||||
|
|
|
|||
|
|
@ -101,9 +101,10 @@ public sealed class Dx7ReferenceParityTests
|
|||
var script = Dx7VoiceProbeScript(
|
||||
voice,
|
||||
graphName: "dx7_prc_synth1_probe",
|
||||
graphGain: 0.39f,
|
||||
graphGain: 0.235f,
|
||||
envelopeScale: 0.62f,
|
||||
gateSeconds: null);
|
||||
gateSeconds: null,
|
||||
useAppliedEnvelope: true);
|
||||
var candidateSource = FaustEmitter.EmitScript(script);
|
||||
var candidate = await FaustCompiler.RenderAsync(
|
||||
candidateSource.Source,
|
||||
|
|
@ -128,8 +129,10 @@ public sealed class Dx7ReferenceParityTests
|
|||
script,
|
||||
comparison);
|
||||
|
||||
Assert.True(comparison.LogMelDistance <= 0.255f, $"{ParityReport(comparison)}{Environment.NewLine}artifacts: {artifactDir}");
|
||||
Assert.True(comparison.Score >= 0.40f, $"{ParityReport(comparison)}{Environment.NewLine}artifacts: {artifactDir}");
|
||||
Assert.True(comparison.LogMelDistance <= 0.225f, $"{ParityReport(comparison)}{Environment.NewLine}artifacts: {artifactDir}");
|
||||
Assert.True(comparison.EnvelopeDistance <= 0.12f, $"{ParityReport(comparison)}{Environment.NewLine}artifacts: {artifactDir}");
|
||||
Assert.InRange(comparison.RmsRatio, 0.95f, 1.05f);
|
||||
Assert.True(comparison.Score >= 0.50f, $"{ParityReport(comparison)}{Environment.NewLine}artifacts: {artifactDir}");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -261,7 +264,8 @@ public sealed class Dx7ReferenceParityTests
|
|||
string graphName,
|
||||
float graphGain,
|
||||
float envelopeScale,
|
||||
float? gateSeconds)
|
||||
float? gateSeconds,
|
||||
bool useAppliedEnvelope = false)
|
||||
{
|
||||
var topology = Dx7SysEx.AlgorithmTopology(voice.Algorithm);
|
||||
var builder = new StringBuilder();
|
||||
|
|
@ -286,7 +290,10 @@ public sealed class Dx7ReferenceParityTests
|
|||
{
|
||||
builder.AppendLine($" feedback={F(Dx7SysEx.OperatorFeedbackAmount(voice.Feedback))}");
|
||||
}
|
||||
builder.AppendLine($" {ScaledEnvelope(Dx7SysEx.ApproximateRateLevelEnvelope(op.Envelope), envelopeScale, gateSeconds).ToScriptSpec()}");
|
||||
var envelope = useAppliedEnvelope
|
||||
? Dx7SysEx.ApproximateAppliedRateLevelEnvelope(op.Envelope, gateSeconds ?? 0.85f)
|
||||
: ScaledEnvelope(Dx7SysEx.ApproximateRateLevelEnvelope(op.Envelope), envelopeScale, gateSeconds);
|
||||
builder.AppendLine($" {envelope.ToScriptSpec()}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -339,6 +339,28 @@ public sealed class Dx7SysExTests
|
|||
Assert.InRange(trace[(int)(0.2f * 44100)].Gain, 0.12f, 0.14f);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApproximatesDx7AppliedEnvelopeAsCurvedRateLevelEnvelope()
|
||||
{
|
||||
var approximation = Dx7SysEx.ApproximateAppliedRateLevelEnvelope(
|
||||
new Dx7Envelope(
|
||||
Rate1: 98,
|
||||
Rate2: 64,
|
||||
Rate3: 48,
|
||||
Rate4: 55,
|
||||
Level1: 99,
|
||||
Level2: 76,
|
||||
Level3: 43,
|
||||
Level4: 0),
|
||||
gateSeconds: 0.75f);
|
||||
|
||||
Assert.InRange(approximation.Envelope.Rate1Seconds, 0.0014f, 0.0015f);
|
||||
Assert.InRange(approximation.Envelope.Level1, 0.99f, 1.01f);
|
||||
Assert.Equal(RateLevelCurve.Exponential, approximation.Envelope.Curve2);
|
||||
Assert.Contains("curves=lin,exp,exp,exp", approximation.ToScriptSpec());
|
||||
Assert.Contains("64-sample block", approximation.Notes);
|
||||
}
|
||||
|
||||
private static byte[] InitVoice(string name, int algorithm, int feedback)
|
||||
{
|
||||
var data = new byte[Dx7SysEx.VoiceEditBufferLength];
|
||||
|
|
|
|||
Loading…
Reference in a new issue