-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #72 from Nytra/audioWork
Add SawtoothGenerator and fix a problem with Frequency Quantize when the input is zero
- Loading branch information
Showing
2 changed files
with
200 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
using System; | ||
using ProtoFlux.Core; | ||
using ProtoFlux.Runtimes.Execution; | ||
using FrooxEngine.ProtoFlux; | ||
using FrooxEngine; | ||
using Elements.Assets; | ||
using Elements.Core; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace ProtoFlux.Runtimes.Execution.Nodes.Obsidian.Audio | ||
{ | ||
public class SawtoothGeneratorProxy : ProtoFluxEngineProxy, IAudioSource | ||
{ | ||
public float Frequency; | ||
|
||
public float Amplitude; | ||
|
||
public float Phase; | ||
|
||
public double time; | ||
|
||
private float[] tempBuffer; | ||
|
||
public bool Active; | ||
|
||
public bool IsActive => Active; | ||
|
||
public int ChannelCount => 1; | ||
|
||
private bool updateTime; | ||
|
||
public void Read<S>(Span<S> buffer) where S : unmanaged, IAudioSample<S> | ||
{ | ||
if (!IsActive) | ||
{ | ||
buffer.Fill(default(S)); | ||
return; | ||
} | ||
|
||
tempBuffer = tempBuffer.EnsureSize(buffer.Length); | ||
var temptime = time; | ||
temptime %= (1f / Frequency); | ||
var clampedAmplitude = MathX.Clamp01(Amplitude); | ||
float advance = (1f / (float)base.Engine.AudioSystem.SampleRate); | ||
for (int i = 0; i < buffer.Length; i++) | ||
{ | ||
tempBuffer[i] = (2.0f * ((((float)temptime / (1f / Frequency)) + Phase) % 1.0f) - 1.0f) * clampedAmplitude; | ||
if (tempBuffer[i] > 1f) tempBuffer[i] = 1f; | ||
else if (tempBuffer[i] < -1f) tempBuffer[i] = -1f; | ||
temptime += advance; | ||
} | ||
if (updateTime) | ||
{ | ||
time = temptime; | ||
updateTime = false; | ||
} | ||
double position = 0.0; | ||
MonoSample lastSample = default(MonoSample); | ||
MemoryMarshal.Cast<float, MonoSample>(MemoryExtensions.AsSpan(tempBuffer)).CopySamples(buffer, ref position, ref lastSample); | ||
} | ||
|
||
protected override void OnStart() | ||
{ | ||
Engine.AudioSystem.AudioUpdate += () => | ||
{ | ||
updateTime = true; | ||
}; | ||
} | ||
} | ||
[NodeCategory("Obsidian/Audio")] | ||
public class SawtoothGenerator : ProxyVoidNode<FrooxEngineContext, SawtoothGeneratorProxy>, IExecutionChangeListener<FrooxEngineContext> | ||
{ | ||
[ChangeListener] | ||
[DefaultValueAttribute(440f)] | ||
public readonly ValueInput<float> Frequency; | ||
|
||
[ChangeListener] | ||
[DefaultValueAttribute(1f)] | ||
public readonly ValueInput<float> Amplitude; | ||
|
||
[ChangeListener] | ||
[DefaultValueAttribute(0f)] | ||
public readonly ValueInput<float> Phase; | ||
|
||
[PossibleContinuations(new string[] { "OnReset" })] | ||
public readonly Operation Reset; | ||
|
||
public Continuation OnReset; | ||
|
||
public readonly ObjectOutput<IAudioSource> AudioOutput; | ||
|
||
private ObjectStore<Action<IChangeable>> _enabledChangedHandler; | ||
|
||
private ObjectStore<SlotEvent> _activeChangedHandler; | ||
|
||
public bool ValueListensToChanges { get; private set; } | ||
|
||
private bool ShouldListen(SawtoothGeneratorProxy proxy) | ||
{ | ||
if (proxy.Enabled) | ||
{ | ||
return proxy.Slot.IsActive; | ||
} | ||
return false; | ||
} | ||
|
||
protected override void ProxyAdded(SawtoothGeneratorProxy proxy, FrooxEngineContext context) | ||
{ | ||
base.ProxyAdded(proxy, context); | ||
NodeContextPath path = context.CaptureContextPath(); | ||
ProtoFluxNodeGroup group = context.Group; | ||
context.GetEventDispatcher(out var dispatcher); | ||
Action<IChangeable> enabledHandler = delegate | ||
{ | ||
dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) | ||
{ | ||
UpdateListenerState(c); | ||
}); | ||
}; | ||
SlotEvent activeHandler = delegate | ||
{ | ||
dispatcher.ScheduleEvent(path, delegate (FrooxEngineContext c) | ||
{ | ||
UpdateListenerState(c); | ||
}); | ||
}; | ||
proxy.EnabledField.Changed += enabledHandler; | ||
proxy.Slot.ActiveChanged += activeHandler; | ||
_enabledChangedHandler.Write(enabledHandler, context); | ||
_activeChangedHandler.Write(activeHandler, context); | ||
ValueListensToChanges = ShouldListen(proxy); | ||
proxy.Active = ValueListensToChanges; | ||
} | ||
|
||
protected override void ProxyRemoved(SawtoothGeneratorProxy proxy, FrooxEngineContext context, bool inUseByAnotherInstance) | ||
{ | ||
if (!inUseByAnotherInstance) | ||
{ | ||
proxy.EnabledField.Changed -= _enabledChangedHandler.Read(context); | ||
proxy.Slot.ActiveChanged -= _activeChangedHandler.Read(context); | ||
_enabledChangedHandler.Clear(context); | ||
_activeChangedHandler.Clear(context); | ||
proxy.Active = false; | ||
} | ||
} | ||
|
||
protected void UpdateListenerState(FrooxEngineContext context) | ||
{ | ||
SawtoothGeneratorProxy proxy = GetProxy(context); | ||
if (proxy != null) | ||
{ | ||
bool shouldListen = ShouldListen(proxy); | ||
if (shouldListen != ValueListensToChanges) | ||
{ | ||
ValueListensToChanges = shouldListen; | ||
context.Group.MarkChangeTrackingDirty(); | ||
proxy.Active = shouldListen; | ||
} | ||
} | ||
} | ||
|
||
public void Changed(FrooxEngineContext context) | ||
{ | ||
SawtoothGeneratorProxy proxy = GetProxy(context); | ||
if (proxy == null) | ||
{ | ||
return; | ||
} | ||
proxy.Amplitude = Amplitude.Evaluate(context, 1f); | ||
proxy.Phase = Phase.Evaluate(context, 0f); | ||
proxy.Frequency = Frequency.Evaluate(context, 440f); | ||
} | ||
|
||
protected override void ComputeOutputs(FrooxEngineContext context) | ||
{ | ||
SawtoothGeneratorProxy proxy = GetProxy(context); | ||
AudioOutput.Write(proxy, context); | ||
} | ||
|
||
private IOperation DoReset(FrooxEngineContext context) | ||
{ | ||
SawtoothGeneratorProxy proxy = GetProxy(context); | ||
if (proxy == null) | ||
{ | ||
return null; | ||
} | ||
proxy.time = 0f; | ||
return OnReset.Target; | ||
} | ||
|
||
public SawtoothGenerator() | ||
{ | ||
AudioOutput = new ObjectOutput<IAudioSource>(this); | ||
Reset = new Operation(this, 0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters