forProfile static method
Derives the profile-specific recipe for base:
mechanicalis a little crisper — quicker attack (never under 2ms, so still click-free), shorter decay and a faint 2nd harmonic — but its brightness (harmonics <= 0.15) and gain (<= 0.4) are capped so it never turns harsh.subtleis softer still — slower attack, no harmonics, lower gain — keepingattack + decayunchanged so the fade-out stays smooth.
Implementation
static SoundRecipe forProfile(SoundRecipe base, FeedbackProfile profile) {
final isNoise = base.waveform == SoundWaveform.noise;
switch (profile) {
case FeedbackProfile.mechanical:
final attack = (base.envelope.attackMs * 0.6).clamp(2.0, 1000.0);
return base.copyWith(
envelope: base.envelope.copyWith(
attackMs: attack,
decayMs: base.envelope.decayMs * 0.8,
),
harmonics: isNoise
? base.harmonics
: [
for (final h in base.harmonics) h.clamp(0.0, 0.15),
if (base.harmonics.isEmpty) 0.12,
],
gain: (base.gain * 1.1).clamp(0.0, 0.4),
);
case FeedbackProfile.subtle:
final attack = base.envelope.attackMs * 1.6;
final decay = (base.envelope.decayMs - (attack - base.envelope.attackMs))
.clamp(1.0, 10000.0);
return base.copyWith(
envelope: base.envelope.copyWith(attackMs: attack, decayMs: decay),
harmonics: const [],
gain: base.gain * 0.75,
);
case FeedbackProfile.tactile:
case FeedbackProfile.silent:
return base;
}
}