Skip to content

Commit

Permalink
Merge pull request #58 from Nytra/testing2
Browse files Browse the repository at this point in the history
Add BandPassFilter, FrequencyModulator, update ButterworthFilter defaults, improve filter sound
  • Loading branch information
Xlinka authored Nov 26, 2024
2 parents e68f0b0 + 4569a26 commit be7bc87
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 11 deletions.
86 changes: 86 additions & 0 deletions ProjectObsidian/Components/Audio/BandPassFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using FrooxEngine;
using Elements.Assets;
using System.Net;
using ProtoFlux.Runtimes.Execution;

namespace Obsidian.Components.Audio
{
[Category(new string[] { "Obsidian/Audio" })]
public class BandPassFilter : Component, IAudioSource, IWorldElement
{
[Range(0.1f, 1.41f, "0.00")]
public readonly Sync<float> Resonance;

[Range(20f, 20000f, "0.00")]
public readonly Sync<float> LowFrequency;

[Range(20f, 20000f, "0.00")]
public readonly Sync<float> HighFrequency;

public readonly SyncRef<IAudioSource> Source;

private double lastTime;

private object lowFilter = null;
private object highFilter = null;

public bool IsActive
{
get => Source.Target != null && Source.Target.IsActive;
}

public int ChannelCount => Source.Target?.ChannelCount ?? 0;

public void Read<S>(Span<S> buffer) where S : unmanaged, IAudioSample<S>
{
if (!IsActive)
{
return;
}

Span<S> tempBuffer = stackalloc S[buffer.Length];
tempBuffer = buffer;
Source.Target.Read(tempBuffer);

if (lowFilter == null)
{
lowFilter = new ButterworthFilter.FilterButterworth<S>();
}
if (highFilter == null)
{
highFilter = new ButterworthFilter.FilterButterworth<S>();
}

((ButterworthFilter.FilterButterworth<S>)lowFilter).UpdateCoefficients(HighFrequency, (int)(tempBuffer.Length / (Engine.Current.AudioSystem.DSPTime - lastTime)), ButterworthFilter.FilterButterworth<S>.PassType.Lowpass, Resonance);
((ButterworthFilter.FilterButterworth<S>)highFilter).UpdateCoefficients(LowFrequency, (int)(tempBuffer.Length / (Engine.Current.AudioSystem.DSPTime - lastTime)), ButterworthFilter.FilterButterworth<S>.PassType.Highpass, Resonance);

for (int i = 0; i < tempBuffer.Length; i++)
{
((ButterworthFilter.FilterButterworth<S>)lowFilter).Update(ref tempBuffer[i]);
((ButterworthFilter.FilterButterworth<S>)highFilter).Update(ref tempBuffer[i]);
}

lastTime = Engine.Current.AudioSystem.DSPTime;
}

protected override void OnAwake()
{
base.OnAwake();
Resonance.Value = 1.41f;
LowFrequency.Value = 20f;
HighFrequency.Value = 20000f;
lastTime = Engine.Current.AudioSystem.DSPTime;
}

protected override void OnChanges()
{
base.OnChanges();
if (Source.GetWasChangedAndClear())
{
lowFilter = null;
highFilter = null;
}
}
}
}
35 changes: 24 additions & 11 deletions ProjectObsidian/Components/Audio/ButterworthFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Obsidian.Components.Audio;
[Category(new string[] { "Obsidian/Audio" })]
public class ButterworthFilter : Component, IAudioSource, IWorldElement
{
[Range(0f, 10000f, "0.00")]
[Range(20f, 20000f, "0.00")]
public readonly Sync<float> Frequency;

[Range(0.1f, 1.41f, "0.00")]
Expand All @@ -19,6 +19,8 @@ public class ButterworthFilter : Component, IAudioSource, IWorldElement

private double lastTime;

private object filter = null;

public bool IsActive
{
get
Expand All @@ -30,7 +32,18 @@ public bool IsActive
protected override void OnAwake()
{
base.OnAwake();
lastTime = -1;
lastTime = Engine.Current.AudioSystem.DSPTime;
Frequency.Value = 20f;
Resonance.Value = 1.41f;
}

protected override void OnChanges()
{
base.OnChanges();
if (Source.GetWasChangedAndClear())
{
filter = null;
}
}

public int ChannelCount => Source.Target?.ChannelCount ?? 0;
Expand All @@ -48,11 +61,16 @@ public void Read<S>(Span<S> buffer) where S : unmanaged, IAudioSample<S>

Source.Target.Read(span);

var filter = new FilterButterworth<S>(Frequency, (int)(span.Length / (Engine.Current.AudioSystem.DSPTime - lastTime)), LowPass ? FilterButterworth<S>.PassType.Lowpass : FilterButterworth<S>.PassType.Highpass, Resonance);
if (filter == null)
{
filter = new FilterButterworth<S>();
}

((FilterButterworth<S>)filter).UpdateCoefficients(Frequency, (int)(span.Length / (Engine.Current.AudioSystem.DSPTime - lastTime)), LowPass ? FilterButterworth<S>.PassType.Lowpass : FilterButterworth<S>.PassType.Highpass, Resonance);

for (int i = 0; i < span.Length; i++)
{
filter.Update(ref span[i]);
((FilterButterworth<S>)filter).Update(ref span[i]);
}

lastTime = Engine.Current.AudioSystem.DSPTime;
Expand All @@ -69,7 +87,7 @@ public class FilterButterworth<S> where S: unmanaged, IAudioSample<S>
private readonly int sampleRate;
private readonly PassType passType;

private readonly float c, a1, a2, a3, b1, b2;
private float c, a1, a2, a3, b1, b2;

/// <summary>
/// Array of input values, latest are in front
Expand All @@ -81,13 +99,8 @@ public class FilterButterworth<S> where S: unmanaged, IAudioSample<S>
/// </summary>
private S[] outputHistory = new S[3];

public FilterButterworth(float frequency, int sampleRate, PassType passType, float resonance)
public void UpdateCoefficients(float frequency, int sampleRate, PassType passType, float resonance)
{
this.resonance = resonance;
this.frequency = frequency;
this.sampleRate = sampleRate;
this.passType = passType;

switch (passType)
{
case PassType.Lowpass:
Expand Down
79 changes: 79 additions & 0 deletions ProjectObsidian/Components/Audio/FrequencyModulator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using FrooxEngine;
using Elements.Assets;

namespace Obsidian.Components.Audio
{
[Category(new string[] { "Obsidian/Audio" })]
public class FrequencyModulator : Component, IAudioSource, IWorldElement
{
[Range(0f, 1000f, "0.00")]
public readonly Sync<float> ModulationIndex;

public readonly SyncRef<IAudioSource> CarrierSource;
public readonly SyncRef<IAudioSource> ModulatorSource;

public bool IsActive
{
get
{
return CarrierSource.Target != null &&
ModulatorSource.Target != null &&
CarrierSource.Target.IsActive &&
ModulatorSource.Target.IsActive;
}
}

public int ChannelCount
{
get
{
return CarrierSource.Target?.ChannelCount ?? 0;
}
}

protected override void OnAwake()
{
base.OnAwake();
ModulationIndex.Value = 100f; // Default modulation index
}

public void Read<S>(Span<S> buffer) where S : unmanaged, IAudioSample<S>
{
if (!IsActive)
{
return;
}

int channelCount = ChannelCount;
if (channelCount == 0)
{
return;
}

// Temporary buffers for carrier and modulator sources
Span<S> carrierBuffer = stackalloc S[buffer.Length];
Span<S> modulatorBuffer = stackalloc S[buffer.Length];

// Read data from sources
CarrierSource.Target.Read(carrierBuffer);
ModulatorSource.Target.Read(modulatorBuffer);

float modulationIndex = ModulationIndex.Value;

// Apply FM synthesis
for (int i = 0; i < buffer.Length; i++)
{
// Get carrier and modulator values
float carrierValue = carrierBuffer[i].AbsoluteAmplitude;
float modulatorValue = modulatorBuffer[i].AbsoluteAmplitude;

// Compute frequency modulation
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);
}
}
}
}

0 comments on commit be7bc87

Please sign in to comment.