From 12cb151d78ce379e87354b356674988bdf651be1 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:41:33 +0000 Subject: [PATCH] Add ADSR Envelope node and adjust frequency modulator's modulation range --- .../Components/Audio/FrequencyModulator.cs | 6 +- .../ProtoFlux/Math/ADSR_Envelope.cs | 75 +++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 ProjectObsidian/ProtoFlux/Math/ADSR_Envelope.cs diff --git a/ProjectObsidian/Components/Audio/FrequencyModulator.cs b/ProjectObsidian/Components/Audio/FrequencyModulator.cs index 3cece20..48cdbe0 100644 --- a/ProjectObsidian/Components/Audio/FrequencyModulator.cs +++ b/ProjectObsidian/Components/Audio/FrequencyModulator.cs @@ -7,7 +7,7 @@ namespace Obsidian.Components.Audio [Category(new string[] { "Obsidian/Audio" })] public class FrequencyModulator : Component, IAudioSource, IWorldElement { - [Range(0f, 1000f, "0.00")] + [Range(0f, 5f, "0.00")] public readonly Sync ModulationIndex; public readonly SyncRef CarrierSource; @@ -35,7 +35,7 @@ public int ChannelCount protected override void OnAwake() { base.OnAwake(); - ModulationIndex.Value = 100f; // Default modulation index + ModulationIndex.Value = 1f; // Default modulation index } public void Read(Span buffer) where S : unmanaged, IAudioSample @@ -74,7 +74,7 @@ public void Read(Span buffer) where S : unmanaged, IAudioSample float modulatedValue = (float)(carrierValue * Math.Sin(2 * Math.PI * modulationIndex * modulatorValue)); // Write modulated value to the buffer - buffer[i] = buffer[i].Bias(buffer[i].AbsoluteAmplitude - modulatedValue); + buffer[i] = buffer[i].Bias(buffer[i].AbsoluteAmplitude - modulatedValue); } } } diff --git a/ProjectObsidian/ProtoFlux/Math/ADSR_Envelope.cs b/ProjectObsidian/ProtoFlux/Math/ADSR_Envelope.cs new file mode 100644 index 0000000..2783913 --- /dev/null +++ b/ProjectObsidian/ProtoFlux/Math/ADSR_Envelope.cs @@ -0,0 +1,75 @@ +using System; +using ProtoFlux.Core; +using ProtoFlux.Runtimes.Execution; +using FrooxEngine.ProtoFlux; +using FrooxEngine; +using System.Threading.Tasks; +using Elements.Core; + +namespace ProtoFlux.Runtimes.Execution.Nodes.Obsidian.Math +{ + [NodeCategory("Obsidian/Math")] + public class ADSR_Envelope : AsyncActionNode + { + public readonly ValueInput AttackTime; + public readonly ValueInput DecayTime; + public readonly ValueInput SustainTime; + public readonly ValueInput SustainValue; + public readonly ValueInput ReleaseTime; + + [DefaultValueAttribute(CurvePreset.Smooth)] + public ValueInput Curve; + + public ObjectInput> Target; + + public AsyncCall OnStarted; + + public Continuation OnDone; + + protected override async Task RunAsync(FrooxEngineContext context) + { + IField field = Target.Evaluate(context); + if (field == null) + { + return null; + } + + CurvePreset curve = Curve.Evaluate(context, CurvePreset.Smooth); + float attackTime = AttackTime.Evaluate(context); + float decayTime = DecayTime.Evaluate(context); + float sustainValue = SustainValue.Evaluate(context); + float sustainTime = SustainTime.Evaluate(context); + float releaseTime = ReleaseTime.Evaluate(context); + + // ATTACK: tween from 0 to 1 over attackTime seconds + TaskCompletionSource completion = new TaskCompletionSource(); + field.TweenFromTo(0f, 1f, attackTime, curve, null, delegate + { + completion.SetResult(result: true); + }); + await OnStarted.ExecuteAsync(context); + await completion.Task; + + // DECAY: tween from 1 to min(sustainValue, 1) over decayTime seconds + TaskCompletionSource completion2 = new TaskCompletionSource(); + field.TweenFromTo(1f, MathX.Min(sustainValue, 1f), decayTime, curve, null, delegate + { + completion2.SetResult(result: true); + }); + await completion2.Task; + + // SUSTAIN: stay at min(sustainValue, 1) for sustainTime seconds + await Task.Delay(TimeSpan.FromSeconds(sustainTime)); + + // RELEASE: tween from min(sustainValue, 1) to 0 over releaseTime seconds + TaskCompletionSource completion3 = new TaskCompletionSource(); + field.TweenFromTo(MathX.Min(sustainValue, 1f), 0f, releaseTime, curve, null, delegate + { + completion3.SetResult(result: true); + }); + await completion3.Task; + + return OnDone.Target; + } + } +} \ No newline at end of file