This commit is contained in:
unknown committed 2022-01-18 17:10:16 +11:00
commit e58a138bea
176 files changed
+6194

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8b3ea3309c3fe7d47943846083c9bf86
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,81 @@
using System.Linq;
using Assets.WasapiAudio.Scripts.Unity;
using UnityEditor;
using UnityEngine;
namespace Assets.WasapiAudio.Editor
{
[CustomPropertyDrawer(typeof(SpectrumDataPreviewAttribute))]
public class SpectrumDataPreviewDrawer : PropertyDrawer
{
private const float DrawerHeight = 150.0f;
private const float AverageBarHeight = 5.0f;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return DrawerHeight;
}
// Start is called before the first frame update
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
if (property.serializedObject == null || property.serializedObject.targetObject == null)
{
return;
}
var preview = fieldInfo.GetValue(property.serializedObject.targetObject) as SpectrumData;
if (preview == null || preview.Values == null)
{
return;
}
try
{
// Draw black background
EditorGUI.DrawRect(position, Color.black);
var x = 0.0f;
var barWidth = position.width / preview.Values.Length;
var average = preview.Values.Average() * DrawerHeight;
for (var i = 0; i < preview.Values.Length; i++)
{
// Draw spectrum bar
var barHeight = preview.Values[i] * DrawerHeight;
var spectrumBarColor = Color.green;
if (barHeight >= average)
{
spectrumBarColor = Color.yellow;
}
var spectrumBar = new Rect(position.x + x,
position.y + DrawerHeight - barHeight,
barWidth,
barHeight);
EditorGUI.DrawRect(spectrumBar, spectrumBarColor);
// Draw average line
var averageLine = new Rect(position.x,
position.y + DrawerHeight - average,
position.width,
AverageBarHeight);
EditorGUI.DrawRect(averageLine, Color.blue);
x += barWidth;
}
EditorGUI.PrefixLabel(position, label);
}
catch
{
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46e201e71577a90439e16b71c254f90f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b03f19e30c6f90d459210f661348a562
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
Binary file not shown.
@@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 39c1ace5108df3a448c1045a9ccefa6c
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2176dc913cfed63409c26999975d0ace
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 499e69e03e0bbf94eadfd1407e650d06
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using CSCore.DSP;
namespace Assets.WasapiAudio.Scripts.Core
{
/// <summary>
/// BasicSpectrumProvider
/// </summary>
public class BasicSpectrumProvider : FftProvider, ISpectrumProvider
{
private readonly int _sampleRate;
private readonly List<object> _contexts = new List<object>();
public BasicSpectrumProvider(int channels, int sampleRate, FftSize fftSize)
: base(channels, fftSize)
{
if (sampleRate <= 0)
{
throw new ArgumentOutOfRangeException(nameof(sampleRate));
}
_sampleRate = sampleRate;
}
public int GetFftBandIndex(float frequency)
{
int fftSize = (int)FftSize;
double f = _sampleRate / 2.0;
// ReSharper disable once PossibleLossOfFraction
return (int)((frequency / f) * (fftSize / 2));
}
public bool GetFftData(float[] fftResultBuffer, object context)
{
if (_contexts.Contains(context))
return false;
_contexts.Add(context);
GetFftData(fftResultBuffer);
return true;
}
public override void Add(float[] samples, int count)
{
base.Add(samples, count);
if (count > 0)
{
_contexts.Clear();
}
}
public override void Add(float left, float right)
{
base.Add(left, right);
_contexts.Clear();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ac978f06ccd8f943ad7c7d03b6229bc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,45 @@
using CSCore;
using CSCore.DSP;
namespace Assets.WasapiAudio.Scripts.Core
{
public class BiQuadFilterSource : SampleAggregatorBase
{
private readonly object _lockObject = new object();
private BiQuad _biQuad;
public BiQuad Filter
{
get => _biQuad;
set
{
lock (_lockObject)
{
_biQuad = value;
}
}
}
public BiQuadFilterSource(ISampleSource source) : base(source)
{
}
public override int Read(float[] buffer, int offset, int count)
{
var read = base.Read(buffer, offset, count);
lock (_lockObject)
{
if (Filter != null)
{
for (int i = 0; i < read; i++)
{
buffer[i + offset] = Filter.Process(buffer[i + offset]);
}
}
}
return read;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce8b1509cfd7d26459241de2dc788b5d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
namespace Assets.WasapiAudio.Scripts.Core
{
internal interface ISpectrumProvider
{
bool GetFftData(float[] fftBuffer, object context);
int GetFftBandIndex(float frequency);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3b3d8384de325694c8a3a05f5c22b115
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Linq;
using CSCore.DSP;
namespace Assets.WasapiAudio.Scripts.Core
{
internal class LineSpectrum : SpectrumBase
{
public int BarCount
{
get => SpectrumResolution;
set => SpectrumResolution = value;
}
public LineSpectrum(FftSize fftSize, int minFrequency, int maxFrequency)
: base(minFrequency, maxFrequency)
{
FftSize = fftSize;
}
public float[] GetSpectrumData(double maxValue)
{
// Get spectrum data internal
var fftBuffer = new float[(int)FftSize];
UpdateFrequencyMapping();
if (SpectrumProvider.GetFftData(fftBuffer, this))
{
SpectrumPointData[] spectrumPoints = CalculateSpectrumPoints(maxValue, fftBuffer);
// Convert to float[]
List<float> spectrumData = new List<float>();
spectrumPoints.ToList().ForEach(point => spectrumData.Add((float)point.Value));
return spectrumData.ToArray();
}
return null;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2567baf275265a478107376fdd44c99
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,9 @@
namespace Assets.WasapiAudio.Scripts.Core
{
public enum ScalingStrategy
{
Decibel,
Linear,
Sqrt
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a5498d82d260dc48a788da725b7d4c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using CSCore;
using CSCore.DSP;
namespace Assets.WasapiAudio.Scripts.Core
{
internal abstract class SpectrumBase
{
private const int ScaleFactorLinear = 9;
protected const int ScaleFactorSqr = 2;
protected const double MinDbValue = -90;
protected const double MaxDbValue = 0;
protected const double DbScale = (MaxDbValue - MinDbValue);
private int _fftSize;
private bool _isXLogScale;
private int _maxFftIndex;
private int _minFrequency;
private int _minimumFrequencyIndex;
private int _maxFrequency;
private int _maximumFrequencyIndex;
private int[] _spectrumIndexMax;
private int[] _spectrumLogScaleIndexMax;
private ISpectrumProvider _spectrumProvider;
protected int SpectrumResolution;
private bool _useAverage;
[Browsable(false)]
public ISpectrumProvider SpectrumProvider
{
get => _spectrumProvider;
set => _spectrumProvider = value ?? throw new ArgumentNullException(nameof(value));
}
public bool IsXLogScale
{
get => _isXLogScale;
set
{
_isXLogScale = value;
UpdateFrequencyMapping();
}
}
public ScalingStrategy ScalingStrategy { get; set; }
public bool UseAverage
{
get => _useAverage;
set => _useAverage = value;
}
[Browsable(false)]
public FftSize FftSize
{
get => (FftSize) _fftSize;
protected set
{
if ((int) Math.Log((int) value, 2) % 1 != 0)
throw new ArgumentOutOfRangeException(nameof(value));
_fftSize = (int) value;
_maxFftIndex = _fftSize / 2 - 1;
}
}
public SpectrumBase(int minFrequency, int maxFrequency)
{
_minFrequency = minFrequency;
_maxFrequency = maxFrequency;
}
protected virtual void UpdateFrequencyMapping()
{
_minimumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(_minFrequency), _maxFftIndex);
_maximumFrequencyIndex = Math.Min(_spectrumProvider.GetFftBandIndex(_maxFrequency) + 1, _maxFftIndex);
var actualResolution = SpectrumResolution;
var indexCount = _maximumFrequencyIndex - _minimumFrequencyIndex;
var linearIndexBucketSize = Math.Round(indexCount / (double) actualResolution, 3);
_spectrumIndexMax = _spectrumIndexMax.CheckBuffer(actualResolution, true);
_spectrumLogScaleIndexMax = _spectrumLogScaleIndexMax.CheckBuffer(actualResolution, true);
var maxLog = Math.Log(actualResolution, actualResolution);
for (int i = 1; i < actualResolution; i++)
{
var logIndex =
(int) ((maxLog - Math.Log((actualResolution + 1) - i, (actualResolution + 1))) * indexCount) +
_minimumFrequencyIndex;
_spectrumIndexMax[i - 1] = _minimumFrequencyIndex + (int) (i * linearIndexBucketSize);
_spectrumLogScaleIndexMax[i - 1] = logIndex;
}
if (actualResolution > 0)
{
_spectrumIndexMax[_spectrumIndexMax.Length - 1] =
_spectrumLogScaleIndexMax[_spectrumLogScaleIndexMax.Length - 1] = _maximumFrequencyIndex;
}
}
protected virtual SpectrumPointData[] CalculateSpectrumPoints(double maxValue, float[] fftBuffer)
{
var dataPoints = new List<SpectrumPointData>();
double value0 = 0, value = 0;
double lastValue = 0;
double actualMaxValue = maxValue;
var spectrumPointIndex = 0;
for (int i = _minimumFrequencyIndex; i <= _maximumFrequencyIndex; i++)
{
switch (ScalingStrategy)
{
case ScalingStrategy.Decibel:
value0 = (((20 * Math.Log10(fftBuffer[i])) - MinDbValue) / DbScale) * actualMaxValue;
break;
case ScalingStrategy.Linear:
value0 = (fftBuffer[i] * ScaleFactorLinear) * actualMaxValue;
break;
case ScalingStrategy.Sqrt:
value0 = ((Math.Sqrt(fftBuffer[i])) * ScaleFactorSqr) * actualMaxValue;
break;
}
var recalc = true;
value = Math.Max(0, Math.Max(value0, value));
while (spectrumPointIndex <= _spectrumIndexMax.Length - 1 &&
i ==
(IsXLogScale
? _spectrumLogScaleIndexMax[spectrumPointIndex]
: _spectrumIndexMax[spectrumPointIndex]))
{
if (!recalc)
{
value = lastValue;
}
if (value > maxValue)
{
value = maxValue;
}
if (_useAverage && spectrumPointIndex > 0)
{
value = (lastValue + value) / 2.0;
}
dataPoints.Add(new SpectrumPointData {SpectrumPointIndex = spectrumPointIndex, Value = value});
lastValue = value;
value = 0.0;
spectrumPointIndex++;
recalc = false;
}
}
return dataPoints.ToArray();
}
[DebuggerDisplay("{" + nameof(Value) + "}")]
protected struct SpectrumPointData
{
public int SpectrumPointIndex;
public double Value;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8fbdbf5c48dee7b4da3f946503b29f80
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.Linq;
namespace Assets.WasapiAudio.Scripts.Core
{
public class SpectrumSmoother
{
private long _iteration;
private readonly int _spectrumSize;
private readonly float[] _smoothedSpectrum;
private readonly List<float[]> _spectrumHistory = new List<float[]>();
public byte SmoothingIterations { get; }
public SpectrumSmoother(int spectrumSize, byte smoothingIterations)
{
_spectrumSize = spectrumSize;
SmoothingIterations = smoothingIterations;
_smoothedSpectrum = new float[_spectrumSize];
for (int i = 0; i < _spectrumSize; i++)
{
_spectrumHistory.Add(new float[SmoothingIterations]);
}
}
public float[] GetSpectrumData(float[] spectrum)
{
_iteration++;
// Record and average last N frames
for (var i = 0; i < _spectrumSize; i++)
{
var historyIndex = _iteration % SmoothingIterations;
var audioData = spectrum[i];
_spectrumHistory[i][historyIndex] = audioData;
_smoothedSpectrum[i] = _spectrumHistory[i].Average();
}
return _smoothedSpectrum;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6dd5954917d385d458671783cb3579ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,140 @@
using System;
using Assets.WasapiAudio.Scripts.Unity;
using CSCore;
using CSCore.CoreAudioAPI;
using CSCore.DSP;
using CSCore.SoundIn;
using CSCore.Streams;
namespace Assets.WasapiAudio.Scripts.Core
{
public class WasapiAudio
{
private const FftSize CFftSize = FftSize.Fft4096;
private const float MaxAudioValue = 1.0f;
private readonly WasapiCaptureType _captureType;
private readonly int _spectrumSize;
private readonly ScalingStrategy _scalingStrategy;
private readonly int _minFrequency;
private readonly int _maxFrequency;
private WasapiCapture _wasapiCapture;
private SoundInSource _soundInSource;
private IWaveSource _realtimeSource;
private BasicSpectrumProvider _basicSpectrumProvider;
private LineSpectrum _lineSpectrum;
private SingleBlockNotificationStream _singleBlockNotificationStream;
private WasapiAudioFilter[] _filters;
private Action<float[]> _receiveAudio;
public WasapiAudio(WasapiCaptureType captureType, int spectrumSize, ScalingStrategy scalingStrategy, int minFrequency, int maxFrequency, WasapiAudioFilter[] filters, Action<float[]> receiveAudio)
{
_captureType = captureType;
_spectrumSize = spectrumSize;
_scalingStrategy = scalingStrategy;
_minFrequency = minFrequency;
_maxFrequency = maxFrequency;
_filters = filters;
_receiveAudio = receiveAudio;
}
public void StartListen()
{
switch (_captureType)
{
case WasapiCaptureType.Loopback:
_wasapiCapture = new WasapiLoopbackCapture();
break;
case WasapiCaptureType.Microphone:
MMDevice defaultMicrophone;
using (var deviceEnumerator = new MMDeviceEnumerator())
{
defaultMicrophone = deviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Communications);
}
_wasapiCapture = new WasapiCapture();
_wasapiCapture.Device = defaultMicrophone;
break;
default:
throw new InvalidOperationException("Unhandled WasapiCaptureType");
}
_wasapiCapture.Initialize();
_soundInSource = new SoundInSource(_wasapiCapture);
_basicSpectrumProvider = new BasicSpectrumProvider(_soundInSource.WaveFormat.Channels, _soundInSource.WaveFormat.SampleRate, CFftSize);
_lineSpectrum = new LineSpectrum(CFftSize, _minFrequency, _maxFrequency)
{
SpectrumProvider = _basicSpectrumProvider,
BarCount = _spectrumSize,
UseAverage = true,
IsXLogScale = true,
ScalingStrategy = _scalingStrategy
};
_wasapiCapture.Start();
var sampleSource = _soundInSource.ToSampleSource();
if (_filters != null && _filters.Length > 0)
{
foreach (var filter in _filters)
{
sampleSource = sampleSource.AppendSource(x => new BiQuadFilterSource(x));
var biQuadSource = (BiQuadFilterSource) sampleSource;
switch (filter.Type)
{
case WasapiAudioFilterType.LowPass:
biQuadSource.Filter = new LowpassFilter(_soundInSource.WaveFormat.SampleRate, filter.Frequency);
break;
case WasapiAudioFilterType.HighPass:
biQuadSource.Filter = new HighpassFilter(_soundInSource.WaveFormat.SampleRate, filter.Frequency);
break;
case WasapiAudioFilterType.BandPass:
biQuadSource.Filter = new BandpassFilter(_soundInSource.WaveFormat.SampleRate, filter.Frequency);
break;
}
}
}
_singleBlockNotificationStream = new SingleBlockNotificationStream(sampleSource);
_realtimeSource = _singleBlockNotificationStream.ToWaveSource();
var buffer = new byte[_realtimeSource.WaveFormat.BytesPerSecond / 2];
_soundInSource.DataAvailable += (s, ea) =>
{
while (_realtimeSource.Read(buffer, 0, buffer.Length) > 0)
{
var spectrumData = _lineSpectrum.GetSpectrumData(MaxAudioValue);
if (spectrumData != null)
{
_receiveAudio?.Invoke(spectrumData);
}
}
};
_singleBlockNotificationStream.SingleBlockRead += SingleBlockNotificationStream_SingleBlockRead;
}
public void StopListen()
{
_singleBlockNotificationStream.SingleBlockRead -= SingleBlockNotificationStream_SingleBlockRead;
_soundInSource.Dispose();
_realtimeSource.Dispose();
_receiveAudio = null;
_wasapiCapture.Stop();
_wasapiCapture.Dispose();
}
private void SingleBlockNotificationStream_SingleBlockRead(object sender, SingleBlockReadEventArgs e)
{
_basicSpectrumProvider.Add(e.Left, e.Right);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a3c269a428d97534b89ae32d45f9658a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,18 @@
using System;
namespace Assets.WasapiAudio.Scripts.Core
{
public enum WasapiAudioFilterType
{
LowPass,
HighPass,
BandPass,
}
[Serializable]
public class WasapiAudioFilter
{
public WasapiAudioFilterType Type;
public int Frequency;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 512a31747c43f524b9317c43013b7f98
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
namespace Assets.WasapiAudio.Scripts.Core
{
public enum WasapiCaptureType
{
Loopback,
Microphone
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 503e87bd4ebdf5b498b2683bb0bba127
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c7d31d84c01ffe24eb0e36653e03832a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,57 @@
using System.Collections.Generic;
using UnityEngine;
namespace Assets.WasapiAudio.Scripts.Unity
{
public abstract class AudioVisualizationEffect : MonoBehaviour
{
// Inspector Properties
public WasapiAudioSource WasapiAudioSource;
[SerializeReference]
[SerializeReferenceButton]
public List<SpectrumTransformer> Transformers = new List<SpectrumTransformer>();
[SpectrumDataPreview]
public SpectrumData Preview;
protected int SpectrumSize { get; private set; }
public virtual void Awake()
{
if (WasapiAudioSource == null)
{
Debug.Log("You must set a WasapiAudioSource");
return;
}
SpectrumSize = WasapiAudioSource.SpectrumSize;
Preview = new SpectrumData();
}
public float[] GetSpectrumData()
{
if (WasapiAudioSource == null)
{
Debug.Log("You must set a WasapiAudioSource");
return null;
}
// Get raw / unmodified spectrum data
var spectrumData = WasapiAudioSource.GetSpectrumData();
// Run spectrum data through all configured transformers
if (Transformers != null && Transformers.Count > 0)
{
foreach (var transformer in Transformers)
{
spectrumData = transformer.Transform(spectrumData);
}
}
Preview.Values = spectrumData;
return spectrumData;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ba1b44ffd6d8856448e4ae29b67ca69d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,17 @@
//using UnityEngine;
//namespace Assets.WasapiAudio.Scripts.Unity
//{
// [CreateAssetMenu(fileName = "Audio Visualization Profile", menuName = "Audio/Audio Visualization Profile", order = 1)]
// public class AudioVisualizationProfile : ScriptableObject
// {
// // Inspector Fields
// public AnimationCurve ScaleCurve;
// [Range(1, 50)]
// public int AudioSmoothingIterations = 5;
// [Range(1, 2)]
// public float MinMaxThreshold = 1.2f;
// public float MinScale = 0.0f;
// public float MaxScale = 1.0f;
// }
//}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ceabdd331bee1a8459e8b85d942a772f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,13 @@
namespace Assets.WasapiAudio.Scripts.Unity
{
public abstract class AudioVisualizationSpectrumBandEffect : AudioVisualizationEffect
{
// Inspector properties
public int SpectrumBand;
protected float GetSpectrumBandData()
{
return GetSpectrumData()[SpectrumBand];
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b411fd36109dfe746be0478532cc7bc4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
using System;
namespace Assets.WasapiAudio.Scripts.Unity
{
[Serializable]
public class SpectrumData
{
public float[] Values;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4ddc80a206711b4b8c119fec4626751
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,10 @@
using System;
using UnityEngine;
namespace Assets.WasapiAudio.Scripts.Unity
{
[AttributeUsage(AttributeTargets.Field)]
public class SpectrumDataPreviewAttribute : PropertyAttribute
{
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87b43e36cb5d1a64bb463e84f5e104a8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,43 @@
using System;
using System.Linq;
namespace Assets.WasapiAudio.Scripts.Unity
{
[Serializable]
public abstract class SpectrumTransformer
{
private float[] _input;
private float[] _output;
public bool Enabled = true;
protected float InputMin { get; private set; }
protected float InputMax { get; private set; }
protected float InputAverage { get; private set; }
protected float InputRange { get; private set; }
public float[] Transform(float[] input)
{
_input = input;
if (_output == null || _output.Length != _input.Length)
{
_output = new float[_input.Length];
}
InputMin = _input.Min();
InputMax = _input.Max();
InputAverage = _input.Average();
InputRange = InputMax - InputMin;
if (Enabled)
{
PerformTransform(_input, ref _output);
}
return Enabled ? _output : _input;
}
protected abstract void PerformTransform(float[] input, ref float[] output);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1bf2ffb4b98288345ac7353d5ca83108
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 723fc4f6d47c6234fbe8232c91accc22
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
using System;
using UnityEngine;
namespace Assets.WasapiAudio.Scripts.Unity.Transformers
{
[Serializable]
public class CurveScaledSpectrumTransformer : SpectrumTransformer
{
public AnimationCurve ScaleCurve;
protected override void PerformTransform(float[] input, ref float[] output)
{
if (ScaleCurve == null)
{
return;
}
var scaleStep = 1.0f / input.Length;
for (int i = 0; i < input.Length; i++)
{
var scaledValue = ScaleCurve.Evaluate(scaleStep * i) * input[i];
output[i] = scaledValue;
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 565fed8317271174792674bb2bc0e5af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,64 @@
using System;
using UnityEngine;
namespace Assets.WasapiAudio.Scripts.Unity.Transformers
{
[Serializable]
public class PercentileBoostSpectrumTransformer : SpectrumTransformer
{
private float[] _sortedPercentileArray;
[Range(0.0f, 1.0f)]
public float Percentile = 0.5f;
public float BelowPercentileMultiplier = 0.5f;
public float AbovePercentileMultiplier = 2.0f;
protected override void PerformTransform(float[] input, ref float[] output)
{
if (_sortedPercentileArray == null || _sortedPercentileArray.Length != input.Length)
{
_sortedPercentileArray = new float[input.Length];
}
input.CopyTo(_sortedPercentileArray, 0);
var percentile = GetPercentile(_sortedPercentileArray, Percentile);
for (int i = 0; i < input.Length; i++)
{
if (input[i] < percentile)
{
output[i] = input[i] * BelowPercentileMultiplier;
}
else
{
output[i] = input[i] * AbovePercentileMultiplier;
}
}
}
public float GetPercentile(float[] sequence, float percentile)
{
Array.Sort(sequence);
var length = sequence.Length;
var n = (length - 1) * percentile + 1;
// Another method: double n = (N + 1) * excelPercentile;
if (n == 1.0f)
{
return sequence[0];
}
if (n == length)
{
return sequence[length - 1];
}
int k = (int)n;
float d = n - k;
return sequence[k - 1] + d * (sequence[k] - sequence[k - 1]);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ac3cd62c87d1ef54f90e71e71fe2000b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,25 @@
using System;
using Assets.WasapiAudio.Scripts.Core;
using UnityEngine;
namespace Assets.WasapiAudio.Scripts.Unity.Transformers
{
[Serializable]
public class SmoothSpectrumTransformer : SpectrumTransformer
{
private SpectrumSmoother _smoother;
[Range(1, 50)]
public byte Iterations = 1;
protected override void PerformTransform(float[] input, ref float[] output)
{
if (_smoother == null || _smoother.SmoothingIterations != Iterations)
{
_smoother = new SpectrumSmoother(input.Length, Iterations);
}
output = _smoother.GetSpectrumData(input);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a86e967af4ca4f4ab907c1a6d5a77aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,53 @@
using UnityEngine;
using UnityEngine.VFX;
using UnityEngine.VFX.Utility;
namespace Assets.WasapiAudio.Scripts.Unity
{
[AddComponentMenu("VFX/Property Binders/VFX Audio Spectrum Data Binder")]
[VFXBinder("Audio/Audio Spectrum Data to AttributeMap")]
public class VfxAudioSpectrumDataBinder : VfxAudioVisualizationEffect
{
[VFXPropertyBinding("System.UInt32"), SerializeField]
protected ExposedProperty SpectrumSizeParameter = "SpectrumSize";
[VFXPropertyBinding("UnityEngine.Texture2D"), SerializeField]
protected ExposedProperty SpectrumDataTextureParameter = "SpectrumDataTexture";
private Texture2D _texture;
private Color[] _colorCache;
private void UpdateTexture()
{
if (_texture == null)
{
_texture = new Texture2D(SpectrumSize, 1, TextureFormat.RFloat, false);
_texture.name = $"AudioSpectrumData{SpectrumSize}";
_colorCache = new Color[SpectrumSize];
}
var spectrumData = GetSpectrumData();
for (int i = 0; i < SpectrumSize; i++)
{
_colorCache[i] = new Color(spectrumData[i], 0, 0, 0);
}
_texture.SetPixels(_colorCache);
_texture.Apply();
}
public override void UpdateBinding(VisualEffect component)
{
base.UpdateBinding(component);
UpdateTexture();
component.SetUInt(SpectrumSizeParameter, (uint) SpectrumSize);
component.SetTexture(SpectrumDataTextureParameter, _texture);
}
public override string ToString()
{
return "Audio Spectrum Data";
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d899048990fdc6246a5f17ff39f840bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,52 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.VFX;
using UnityEngine.VFX.Utility;
namespace Assets.WasapiAudio.Scripts.Unity
{
public abstract class VfxAudioVisualizationEffect : VFXBinderBase
{
// Inspector Properties
public WasapiAudioSource WasapiAudioSource;
[SerializeReference]
[SerializeReferenceButton]
public List<SpectrumTransformer> Transformers = new List<SpectrumTransformer>();
[SpectrumDataPreview]
public SpectrumData Preview;
protected int SpectrumSize { get; private set; }
public override bool IsValid(VisualEffect component)
{
return WasapiAudioSource != null;
}
public override void UpdateBinding(VisualEffect component)
{
SpectrumSize = WasapiAudioSource.SpectrumSize;
Preview = new SpectrumData();
}
protected float[] GetSpectrumData()
{
// Get raw / unmodified spectrum data
var spectrumData = WasapiAudioSource.GetSpectrumData();
// Run spectrum data through all configured transformers
if (Transformers != null && Transformers.Count > 0)
{
foreach (var transformer in Transformers)
{
spectrumData = transformer.Transform(spectrumData);
}
}
Preview.Values = spectrumData;
return spectrumData;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4dc0fbf002e63e148b601b689dcc5ca7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,46 @@
using Assets.WasapiAudio.Scripts.Core;
using UnityEngine;
namespace Assets.WasapiAudio.Scripts.Unity
{
[ExecuteInEditMode]
public class WasapiAudioSource : MonoBehaviour
{
private Core.WasapiAudio _wasapiAudio;
private float[] _spectrumData;
// Inspector Properties
public WasapiCaptureType CaptureType = WasapiCaptureType.Loopback;
public int SpectrumSize = 32;
public ScalingStrategy ScalingStrategy = ScalingStrategy.Sqrt;
public int MinFrequency = 100;
public int MaxFrequency = 20000;
public WasapiAudioFilter[] Filters;
public void Awake()
{
// Setup loopback audio and start listening
_wasapiAudio = new Core.WasapiAudio(CaptureType, SpectrumSize, ScalingStrategy, MinFrequency, MaxFrequency, Filters, spectrumData =>
{
_spectrumData = spectrumData;
});
_wasapiAudio.StartListen();
}
public void Update()
{
}
public float[] GetSpectrumData()
{
return _spectrumData;
}
public void OnApplicationQuit()
{
_wasapiAudio?.StopListen();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 120ca4c4f9442e14db8c72acac5da136
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: