128 lines
4.8 KiB
C#
128 lines
4.8 KiB
C#
using System;
|
|
using ManagedBass;
|
|
using T3.Core.Logging;
|
|
|
|
namespace T3.Core.Audio;
|
|
|
|
/// <summary>
|
|
/// Analyze audio input from internal soundtrack or from selected Wasapi device.
|
|
/// Transforms the FFT buffer generated by BASS and generates frequency bands and peaks.
|
|
///
|
|
/// <para><b>Note:</b> This static class delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// For multithreaded analysis, create separate <see cref="AudioAnalysisContext"/> instances.</para>
|
|
/// </summary>
|
|
public static class AudioAnalysis
|
|
{
|
|
private static AudioAnalysisContext Context => AudioAnalysisContext.Default;
|
|
|
|
/// <summary>
|
|
/// Processes FFT data to compute frequency bands, peaks, and attacks.
|
|
/// Delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// </summary>
|
|
internal static void ProcessUpdate(float gainFactor = 1f, float decayFactor = 0.9f)
|
|
{
|
|
Context.ProcessFftUpdate(gainFactor, decayFactor);
|
|
}
|
|
|
|
#region Buffer Accessors (delegate to default context)
|
|
|
|
/// <summary>
|
|
/// Result of the FFT analysis in gain. Delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// </summary>
|
|
public static float[] FftGainBuffer => Context.FftGainBuffer;
|
|
|
|
/// <summary>
|
|
/// Result of the FFT analysis converted to dB and mapped to normalized range.
|
|
/// Delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// </summary>
|
|
public static float[] FftNormalizedBuffer => Context.FftNormalizedBuffer;
|
|
|
|
/// <summary>
|
|
/// Current frequency band levels (0-1 normalized).
|
|
/// Delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// </summary>
|
|
public static float[] FrequencyBands => Context.FrequencyBands;
|
|
|
|
/// <summary>
|
|
/// Peak-hold values for frequency bands with decay.
|
|
/// Delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// </summary>
|
|
public static float[] FrequencyBandPeaks => Context.FrequencyBandPeaks;
|
|
|
|
/// <summary>
|
|
/// Attack values for frequency bands (rate of increase).
|
|
/// Delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// </summary>
|
|
public static float[] FrequencyBandAttacks => Context.FrequencyBandAttacks;
|
|
|
|
/// <summary>
|
|
/// Peak attack values with slower decay.
|
|
/// Delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// </summary>
|
|
public static float[] FrequencyBandAttackPeaks => Context.FrequencyBandAttackPeaks;
|
|
|
|
/// <summary>
|
|
/// Onset detection values for beat synchronization.
|
|
/// Delegates to <see cref="AudioAnalysisContext.Default"/>.
|
|
/// </summary>
|
|
internal static float[] FrequencyBandOnSets => Context.FrequencyBandOnSets;
|
|
|
|
#endregion
|
|
|
|
internal const DataFlags BassFlagForFftBufferSize = DataFlags.FFT2048;
|
|
internal const int FftBufferSize = 1024; // For Bass DataFlags.FFT2048
|
|
|
|
/// <summary>
|
|
/// Computes FFT from a PCM buffer during export.
|
|
/// Creates a temporary stream, populates it with the buffer data, and extracts FFT.
|
|
/// Uses the default context's FftGainBuffer.
|
|
/// </summary>
|
|
/// <param name="pcmBuffer">Interleaved stereo float buffer</param>
|
|
public static void ComputeFftFromBuffer(float[] pcmBuffer)
|
|
{
|
|
ComputeFftFromBuffer(pcmBuffer, Context);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Computes FFT from a PCM buffer during export into a specific context.
|
|
/// Creates a temporary stream, populates it with the buffer data, and extracts FFT.
|
|
/// </summary>
|
|
/// <param name="pcmBuffer">Interleaved stereo float buffer</param>
|
|
/// <param name="context">The analysis context to store results in</param>
|
|
internal static void ComputeFftFromBuffer(float[] pcmBuffer, AudioAnalysisContext context)
|
|
{
|
|
if (pcmBuffer == null || pcmBuffer.Length < 4)
|
|
{
|
|
Array.Clear(context.FftGainBuffer, 0, context.FftGainBuffer.Length);
|
|
return;
|
|
}
|
|
|
|
// Create a temporary push stream
|
|
int tempStream = Bass.CreateStream(
|
|
AudioConfig.MixerFrequency,
|
|
2, // stereo
|
|
BassFlags.Float | BassFlags.Decode,
|
|
StreamProcedureType.Push);
|
|
|
|
if (tempStream == 0)
|
|
{
|
|
Log.Warning($"[AudioAnalysis] Failed to create temp stream for FFT: {Bass.LastError}");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Push the PCM data into the stream
|
|
int bytesToPush = pcmBuffer.Length * sizeof(float);
|
|
Bass.StreamPutData(tempStream, pcmBuffer, bytesToPush);
|
|
|
|
// Get FFT data from the stream
|
|
Bass.ChannelGetData(tempStream, context.FftGainBuffer, (int)DataFlags.FFT2048);
|
|
}
|
|
finally
|
|
{
|
|
// Clean up the temporary stream
|
|
Bass.StreamFree(tempStream);
|
|
}
|
|
}
|
|
} |