Map DX7 detune and output compensation
This commit is contained in:
parent
4c10705320
commit
e745231ed2
8 changed files with 144 additions and 44 deletions
|
|
@ -45,17 +45,30 @@ math is still fake in the places that matter perceptually.
|
|||
- Render one self-feedback operator across DX7 feedback values.
|
||||
- Fit feedback amount separately from route/index scaling.
|
||||
|
||||
4. **Envelope level curve**
|
||||
4. **Ratio detune**
|
||||
- Lower DX7 ratio-mode detune into Aquarium operator ratios.
|
||||
- Keep detune as frequency behavior, not a modulation or chorus feature.
|
||||
|
||||
5. **Algorithm output compensation**
|
||||
- Use ROM `COM` values to balance carriers before any PRC-specific branch
|
||||
boosting.
|
||||
- `COM` stores the output count minus one. Because Aquarium's carrier
|
||||
amplitude curve was fitted against algorithm 32's six-output baseline,
|
||||
carriers scale by `6 / (COM + 1)` during DX7 lowering.
|
||||
- Replace manual carrier scale constants with algorithm-derived lowering.
|
||||
|
||||
6. **Envelope level curve**
|
||||
- Render fixed-output operators with varied DX7 EG levels and rates.
|
||||
- Fit the rate/level envelope amplitude curve instead of assuming
|
||||
`level / 99`.
|
||||
|
||||
5. **Algorithm output compensation**
|
||||
- Use algorithm topology/ROM compensation to balance carriers before any
|
||||
PRC-specific branch boosting.
|
||||
- Replace manual carrier scale constants with algorithm-derived lowering.
|
||||
7. **Summed/cascaded modulation**
|
||||
- Render project-authored algorithm-8 stacks that isolate `6 -> 5 -> 3`
|
||||
and `4 + 5 -> 3`.
|
||||
- Replace the hard `PRC SYNTH1` cascaded-route probe constant with a fitted
|
||||
summed-modulator route rule.
|
||||
|
||||
6. **Hard target replay**
|
||||
8. **Hard target replay**
|
||||
- Re-run `PRC SYNTH1`.
|
||||
- Keep the log-mel gate and listening artifacts.
|
||||
- Raise thresholds only when the calibrated lowering improves for the right
|
||||
|
|
|
|||
|
|
@ -218,6 +218,28 @@ public static class Dx7SysEx
|
|||
public static float OperatorFeedbackAmount(int feedback) =>
|
||||
FeedbackAmounts[Math.Clamp(feedback, 0, FeedbackAmounts.Length - 1)];
|
||||
|
||||
public static float OperatorOutputCompensation(Dx7AlgorithmTopology topology, int operatorNumber)
|
||||
{
|
||||
var outputStep = topology.RomSteps.FirstOrDefault(step =>
|
||||
step.TargetOperator == operatorNumber &&
|
||||
step.OutputCompensation > 0);
|
||||
return outputStep is null
|
||||
? 1
|
||||
: 6f / (outputStep.OutputCompensation + 1);
|
||||
}
|
||||
|
||||
public static float OperatorFrequencyRatio(Dx7Operator op, int midiNote = 60)
|
||||
{
|
||||
if (op.FrequencyMode == Dx7FrequencyMode.Fixed)
|
||||
{
|
||||
return Math.Max(0.5f, op.FrequencyCoarse);
|
||||
}
|
||||
|
||||
var coarse = op.FrequencyCoarse == 0 ? 0.5f : op.FrequencyCoarse;
|
||||
var fine = 1 + op.FrequencyFine / 100f;
|
||||
return coarse * fine * RatioModeDetuneFactor(op.Detune, midiNote);
|
||||
}
|
||||
|
||||
public static Dx7RateLevelEnvelopeApproximation ApproximateRateLevelEnvelope(Dx7Envelope envelope)
|
||||
{
|
||||
var l1 = Level(envelope.Level1);
|
||||
|
|
@ -451,6 +473,13 @@ public static class Dx7SysEx
|
|||
|
||||
private static float Level(int value) => Math.Clamp(value, 0, 99) / 99f;
|
||||
|
||||
private static float RatioModeDetuneFactor(int detune, int midiNote)
|
||||
{
|
||||
var logFrequency = MathF.Log2(440f) + (midiNote - 69) / 12f;
|
||||
var detuneRatio = 0.0209f * MathF.Exp(-0.396f * logFrequency) / 7f;
|
||||
return MathF.Pow(2, detuneRatio * logFrequency * (Math.Clamp(detune, 0, 14) - 7));
|
||||
}
|
||||
|
||||
private static int ScaleVelocity(int velocity, int sensitivity)
|
||||
{
|
||||
var clampedVelocity = Math.Clamp(velocity, 0, 127);
|
||||
|
|
|
|||
|
|
@ -32,3 +32,4 @@
|
|||
{"kind":"dx7_output_level_curve","lesson":"A project-authored Dexed single-carrier sweep shows DX7 carrier amplitude follows 2^((outputLevel - 99) / 8). The previous internal-level proxy made mid-level operators far too loud. `Dx7SysEx.OperatorOutputAmplitude` now owns the measured curve; next calibration rung is two-operator modulation index.","source":"DX7 single-carrier calibration rung on 2026-05-14"}
|
||||
{"kind":"dx7_modulation_index","lesson":"Isolated two-operator Dexed FM fits full-level modulation at about 12.55 radians. Under Aquarium's current Faust phase formula this maps to route index 6.275 for a full-level modulator. Apply this to isolated two-op branches, but do not blindly apply it to cascaded algorithm stacks before algorithm output compensation is modeled.","source":"DX7 two-operator modulation calibration rung on 2026-05-14"}
|
||||
{"kind":"dx7_feedback_scale","lesson":"Isolated Dexed feedback sweeps fit a nonlinear Aquarium feedback table: 0, .01, .02, .05, .10, .19, .38, .66 for DX7 feedback values 0-7. PRC feedback value 5 was already accidentally close via feedback*0.04; the table moves that knowledge into Dx7SysEx.OperatorFeedbackAmount.","source":"DX7 feedback calibration rung on 2026-05-14"}
|
||||
{"kind":"dx7_detune_and_com","lesson":"PRC exposed two separate DX7 facts: ratio-mode detune must lower into operator frequency ratios, and ROM COM output compensation must be applied relative to the algorithm-32 six-output baseline used by the carrier-amplitude calibration. This cut the PRC carrier scale table; the remaining cascaded-route constant is still a fenced hard-target scar needing summed-modulator calibration.","source":"DX7 PRC parity rung on 2026-05-14"}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
"DX7 measured carrier output amplitude curve",
|
||||
"DX7 isolated modulation-index route scale",
|
||||
"DX7 calibrated feedback amount table",
|
||||
"DX7 ratio detune lowering",
|
||||
"DX7 ROM COM output compensation",
|
||||
"staged operator envelope DSL",
|
||||
"DX7 envelope-to-ADSR approximation",
|
||||
"standard ADSR envelope model",
|
||||
|
|
@ -64,9 +66,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, staged operator envelopes, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference 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, 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; applying that blindly to cascaded PRC stacks is wrong until algorithm output compensation exists. Feedback now lowers through a fitted table instead of PRC-local constants. Staged operator envelopes now let Aquarium express four-rate/four-level contours in readable DSL.",
|
||||
"next": "Follow docs/dx7-calibration-plan.md: calibrate envelope level curve and algorithm output compensation before adding new DSL surface or tuning PRC constants."
|
||||
"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, staged operator envelopes, two Aquarium DSL rebuild attempts, first-class OperatorGraph support, public-domain SysEx fixture coverage, dexed-py reference 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, 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; applying that blindly to cascaded PRC stacks is wrong. 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). Staged operator envelopes now let Aquarium express four-rate/four-level contours in readable DSL.",
|
||||
"next": "Follow docs/dx7-calibration-plan.md: calibrate envelope level curve and summed/cascaded modulation before adding new DSL surface or tuning PRC constants."
|
||||
},
|
||||
"note_and_envelope_model": {
|
||||
"rule": "Notes own frequency, gate duration, and host/MIDI source. Envelopes own ADSR level shape.",
|
||||
|
|
@ -80,8 +82,8 @@
|
|||
"binding_rule": "Parameter binding uses field-site references such as `lpf=@/macro/brightness` and `/opgraphs/0/operators/2/level`; the field owns meaning and the parameter owns the runtime value. Avoid target-routing semantics unless a new graph authority is deliberately mapped."
|
||||
},
|
||||
"last_verified": {
|
||||
"command": "dotnet test AquariumSynthCSharp.slnx --no-restore",
|
||||
"result": "57 passed with AQUARIUM_DX7_PYTHON set to bundled Python",
|
||||
"commit": "pending DX7 feedback calibration"
|
||||
"command": "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore",
|
||||
"result": "59 passed; latest PRC log-mel 0.24476814, score 0.5708394",
|
||||
"commit": "pending DX7 detune and COM compensation"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,21 @@ Doctrine update:
|
|||
|
||||
Completed this slice:
|
||||
|
||||
- Added the ratio-detune and algorithm-output-compensation rungs to the DX7
|
||||
lowering. `Dx7SysEx.OperatorFrequencyRatio` now applies ratio-mode detune
|
||||
from the Dexed/DX7 note formula, and `Dx7SysEx.OperatorOutputCompensation`
|
||||
maps ROM `COM` values relative to the algorithm-32 six-output baseline.
|
||||
- The PRC probe no longer has a carrier-level scale table. It still has a
|
||||
fenced cascaded-route constant (`1.6`) for the `op6/op5/op4 -> op3` stack;
|
||||
that is now named as the next calibration target, not promoted into core
|
||||
lowering.
|
||||
- Latest PRC run with detune, COM compensation, graph gain `.39`, and the
|
||||
fenced cascaded-route probe: log-mel `0.24476814`, score `0.5708394`, RMS
|
||||
ratio `1.0108943`, zero-crossing ratio `0.7931764`, centroid ratio
|
||||
`1.1131053`.
|
||||
|
||||
Previous slice:
|
||||
|
||||
- Completed the feedback calibration rung. Isolated Dexed feedback sweeps fit a
|
||||
nonlinear Aquarium feedback table: `0, .01, .02, .05, .10, .19, .38, .66`
|
||||
for DX7 feedback values `0..7`.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
project: AquariumSynthCSharp
|
||||
spine_version: 1
|
||||
updated: 2026-05-14T04:13:00+01:00
|
||||
updated: 2026-05-14T04:26:00+01:00
|
||||
|
||||
prime_directive: >
|
||||
Build coherent synth machinery. References exist to reveal missing structure,
|
||||
|
|
@ -47,6 +47,9 @@ live_invariants:
|
|||
- DX7 carrier output amplitude follows the measured `2^((outputLevel - 99) / 8)` curve; do not restore the old internal-level proxy.
|
||||
- DX7 isolated two-operator modulation uses Aquarium route index `6.275` for full-level modulators; do not apply that blindly to cascaded stacks before algorithm output compensation is modeled.
|
||||
- DX7 feedback lowers through `Dx7SysEx.OperatorFeedbackAmount`; do not restore PRC-local `feedback * constant` folklore.
|
||||
- DX7 ratio-mode detune lowers through `Dx7SysEx.OperatorFrequencyRatio`; do not silently drop detune from operator graph scripts.
|
||||
- DX7 algorithm output compensation lowers through ROM `COM` values relative to the algorithm-32 six-output baseline; do not restore PRC-local carrier scale tables.
|
||||
- The PRC cascaded-route value is a fenced hard-target probe scar, not a core DX7 fact. Replace it with a summed-modulator calibration rung before broad DX7 import.
|
||||
- DX7 calibration must proceed through isolated rungs in `docs/dx7-calibration-plan.md`; do not keep tuning hard PRC-specific constants as a substitute for a level/index model.
|
||||
- Staged operator envelopes own multi-target operator level contours through `env=rl rates=... levels=...`; do not force DX7-style contours back into the general ADSR voice envelope model.
|
||||
- DX7 rate/level envelopes may lower to staged operator envelopes or labeled ADSR approximations, but exact DX7 EG timing/gain calibration remains missing pressure.
|
||||
|
|
@ -64,7 +67,7 @@ reference_targets:
|
|||
priority: 1
|
||||
purpose: "Structured FM/operator graph pressure test."
|
||||
source: "https://github.com/asb2m10/dexed"
|
||||
status: "DX7 SysEx parser, public-domain SysEx fixture, dexed-py reference rendering, first thresholded rendered parity test for analog1.syx voice 13, hard PRC SYNTH1 probe with listening WAV artifacts and log-mel <= 0.255, renderable delayed operator feedback, DX7 effective operator-level approximation, staged operator envelopes, algorithm topology extraction, envelope-to-ADSR approximation, two Aquarium DSL rebuild attempts, first-class operator graph support, and Aquarium candidate rendering through Faust-generated C# exist."
|
||||
status: "DX7 SysEx parser, public-domain SysEx fixture, dexed-py reference rendering, first thresholded rendered parity test for analog1.syx voice 13, hard PRC SYNTH1 probe with listening WAV artifacts and log-mel <= 0.255, renderable delayed operator feedback, DX7 effective operator-level approximation, measured carrier output amplitude, isolated route index, feedback table, ratio detune lowering, ROM COM output compensation, staged operator envelopes, algorithm topology extraction, envelope-to-ADSR approximation, two Aquarium DSL rebuild attempts, first-class operator graph support, and Aquarium candidate rendering through Faust-generated C# exist."
|
||||
first_slice:
|
||||
- "Add a harder public-domain DX7 parity target that audibly needs operator routing."
|
||||
zynaddsubfx:
|
||||
|
|
@ -101,10 +104,12 @@ reference_targets:
|
|||
next_agent_start_here:
|
||||
recommended_first_task: >
|
||||
Listen to the latest generated PRC SYNTH1 WAVs under
|
||||
Continue the ladder in `docs/dx7-calibration-plan.md` with envelope level
|
||||
curve and algorithm output compensation. Single-carrier amplitude, isolated
|
||||
two-op modulation index, and feedback are now fitted; cascaded PRC stacks
|
||||
still need a real compensation model.
|
||||
`artifacts/parity/dx7-prc-synth1`. Continue the ladder in
|
||||
`docs/dx7-calibration-plan.md` with envelope level curve and
|
||||
summed/cascaded modulation calibration. Single-carrier amplitude, isolated
|
||||
two-op modulation index, feedback, ratio detune, and ROM COM output
|
||||
compensation are now fitted or mapped; the PRC cascaded-route constant
|
||||
still needs a real summed-modulator model.
|
||||
must_read:
|
||||
- AGENTS.md
|
||||
- docs/audio-doctrine.md
|
||||
|
|
@ -117,6 +122,15 @@ next_agent_start_here:
|
|||
- "dotnet test AquariumSynthCSharp.slnx --no-restore"
|
||||
|
||||
completed_slices:
|
||||
- date: 2026-05-14
|
||||
summary: >
|
||||
Added DX7 ratio-mode detune and algorithm output compensation to central
|
||||
lowering. `Dx7SysEx.OperatorFrequencyRatio` preserves detune in operator
|
||||
graph ratios, and `Dx7SysEx.OperatorOutputCompensation` maps ROM `COM`
|
||||
output counts relative to the algorithm-32 six-output amplitude baseline.
|
||||
The PRC probe no longer carries a manual carrier-level scale table; its
|
||||
remaining cascaded-route value is fenced as a hard-target probe scar.
|
||||
verification: "AQUARIUM_DX7_PYTHON=<bundled python> dotnet test AquariumSynthCSharp.slnx --no-restore --filter \"Dx7SysExTests|PublicDomainDx7PrcSynth1WritesListeningWavsWhenInstalled\": passed"
|
||||
- date: 2026-05-14
|
||||
summary: >
|
||||
Completed the DX7 feedback calibration rung. Isolated feedback sweeps fit
|
||||
|
|
|
|||
|
|
@ -152,15 +152,16 @@ public sealed class Dx7ReferenceParityTests
|
|||
builder.AppendLine("opgraph");
|
||||
builder.AppendLine(" name=dx7_prc_synth1_probe");
|
||||
builder.AppendLine(" freq=261.6256");
|
||||
builder.AppendLine(" gain=0.55");
|
||||
builder.AppendLine(" gain=0.39");
|
||||
builder.AppendLine();
|
||||
|
||||
foreach (var op in voice.Operators.OrderByDescending(op => op.Number))
|
||||
{
|
||||
var level = Dx7SysEx.ApproximateOperatorLevel(op).LinearLevel * CarrierLevelScale(op.Number);
|
||||
var level = Dx7SysEx.ApproximateOperatorLevel(op).LinearLevel *
|
||||
Dx7SysEx.OperatorOutputCompensation(topology, op.Number);
|
||||
var envelope = ScaledEnvelope(Dx7SysEx.ApproximateRateLevelEnvelope(op.Envelope), 0.62f);
|
||||
builder.AppendLine($"operator name=op{op.Number}");
|
||||
builder.AppendLine($" ratio={F(Dx7Ratio(op))}");
|
||||
builder.AppendLine($" ratio={F(Dx7SysEx.OperatorFrequencyRatio(op))}");
|
||||
builder.AppendLine($" level={F(level)}");
|
||||
if (topology.SelfFeedbackOperators.Contains(op.Number))
|
||||
{
|
||||
|
|
@ -186,32 +187,11 @@ public sealed class Dx7ReferenceParityTests
|
|||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static float Dx7Ratio(Dx7Operator op)
|
||||
{
|
||||
if (op.FrequencyMode == Dx7FrequencyMode.Fixed)
|
||||
{
|
||||
return Math.Max(0.5f, op.FrequencyCoarse);
|
||||
}
|
||||
|
||||
var coarse = op.FrequencyCoarse == 0 ? 0.5f : op.FrequencyCoarse;
|
||||
return coarse * (1 + op.FrequencyFine / 100f);
|
||||
}
|
||||
|
||||
private static float CarrierLevelScale(int operatorNumber) => operatorNumber switch
|
||||
{
|
||||
6 => 0.3f,
|
||||
5 => 0.55f,
|
||||
4 => 0.6f,
|
||||
3 => 2.5f,
|
||||
2 => 0.8f,
|
||||
_ => 1
|
||||
};
|
||||
|
||||
private static float PrcSynth1RouteIndex(int sourceOperator, int targetOperator) => (sourceOperator, targetOperator) switch
|
||||
{
|
||||
(2, 1) => Dx7SysEx.OperatorModulationRouteIndex,
|
||||
// Cascaded stacks still need algorithm output compensation; do not apply the isolated two-op scale blindly.
|
||||
_ => 0.7f
|
||||
// Hard-target probe scar: cascaded stacks need dedicated summed-modulator calibration.
|
||||
_ => 1.6f
|
||||
};
|
||||
|
||||
private static Dx7RateLevelEnvelopeApproximation ScaledEnvelope(Dx7RateLevelEnvelopeApproximation approximation, float scale)
|
||||
|
|
|
|||
|
|
@ -150,6 +150,18 @@ public sealed class Dx7SysExTests
|
|||
Assert.Equal([6], topology.SelfFeedbackOperators);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapsDx7AlgorithmOutputCompensationRelativeToAlgorithmThirtyTwo()
|
||||
{
|
||||
var algorithm8 = Dx7SysEx.AlgorithmTopology(8);
|
||||
var algorithm32 = Dx7SysEx.AlgorithmTopology(32);
|
||||
|
||||
Assert.Equal(3, Dx7SysEx.OperatorOutputCompensation(algorithm8, 1));
|
||||
Assert.Equal(3, Dx7SysEx.OperatorOutputCompensation(algorithm8, 3));
|
||||
Assert.Equal(1, Dx7SysEx.OperatorOutputCompensation(algorithm8, 2));
|
||||
Assert.Equal(1, Dx7SysEx.OperatorOutputCompensation(algorithm32, 1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApproximatesDx7RateLevelEnvelopeAsAdsr()
|
||||
{
|
||||
|
|
@ -224,6 +236,18 @@ public sealed class Dx7SysExTests
|
|||
Assert.Equal(0.66f, Dx7SysEx.OperatorFeedbackAmount(7));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppliesDx7RatioModeDetuneToOperatorFrequency()
|
||||
{
|
||||
var centered = InitOperator(frequencyCoarse: 1, detune: 7);
|
||||
var sharp = InitOperator(frequencyCoarse: 1, detune: 14);
|
||||
var flat = InitOperator(frequencyCoarse: 1, detune: 0);
|
||||
|
||||
Assert.Equal(1, Dx7SysEx.OperatorFrequencyRatio(centered, midiNote: 60));
|
||||
Assert.InRange(Dx7SysEx.OperatorFrequencyRatio(sharp, midiNote: 60), 1.0048f, 1.0049f);
|
||||
Assert.InRange(Dx7SysEx.OperatorFrequencyRatio(flat, midiNote: 60), 0.9951f, 0.9952f);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApproximatesDx7RateLevelEnvelopeAsStagedEnvelope()
|
||||
{
|
||||
|
|
@ -287,6 +311,28 @@ public sealed class Dx7SysExTests
|
|||
return data;
|
||||
}
|
||||
|
||||
private static Dx7Operator InitOperator(
|
||||
int frequencyCoarse,
|
||||
int frequencyFine = 0,
|
||||
int detune = 7,
|
||||
Dx7FrequencyMode frequencyMode = Dx7FrequencyMode.Ratio) =>
|
||||
new(
|
||||
Number: 1,
|
||||
Envelope: new Dx7Envelope(99, 99, 99, 99, 99, 99, 99, 0),
|
||||
BreakPoint: 39,
|
||||
LeftDepth: 0,
|
||||
RightDepth: 0,
|
||||
LeftCurve: 0,
|
||||
RightCurve: 0,
|
||||
RateScaling: 0,
|
||||
AmplitudeModulationSensitivity: 0,
|
||||
KeyVelocitySensitivity: 0,
|
||||
OutputLevel: 99,
|
||||
FrequencyMode: frequencyMode,
|
||||
FrequencyCoarse: frequencyCoarse,
|
||||
FrequencyFine: frequencyFine,
|
||||
Detune: detune);
|
||||
|
||||
private static byte[] PackVoice(byte[] edit)
|
||||
{
|
||||
var packed = new byte[Dx7SysEx.PackedVoiceLength];
|
||||
|
|
|
|||
Loading…
Reference in a new issue