using System;
using ManagedBass;
using T3.Core.Logging;
namespace T3.Core.Audio;
///
/// Analyze audio input from internal soundtrack or from selected Wasapi device.
/// Transforms the FFT buffer generated by BASS and generates frequency bands and peaks.
///
/// Note: This static class delegates to .
/// For multithreaded analysis, create separate instances.
///
public static class AudioAnalysis
{
private static AudioAnalysisContext Context => AudioAnalysisContext.Default;
///
/// Processes FFT data to compute frequency bands, peaks, and attacks.
/// Delegates to .
///
internal static void ProcessUpdate(float gainFactor = 1f, float decayFactor = 0.9f)
{
Context.ProcessFftUpdate(gainFactor, decayFactor);
}
#region Buffer Accessors (delegate to default context)
///
/// Result of the FFT analysis in gain. Delegates to .
///
public static float[] FftGainBuffer => Context.FftGainBuffer;
///
/// Result of the FFT analysis converted to dB and mapped to normalized range.
/// Delegates to .
///
public static float[] FftNormalizedBuffer => Context.FftNormalizedBuffer;
///
/// Current frequency band levels (0-1 normalized).
/// Delegates to .
///
public static float[] FrequencyBands => Context.FrequencyBands;
///
/// Peak-hold values for frequency bands with decay.
/// Delegates to .
///
public static float[] FrequencyBandPeaks => Context.FrequencyBandPeaks;
///
/// Attack values for frequency bands (rate of increase).
/// Delegates to .
///
public static float[] FrequencyBandAttacks => Context.FrequencyBandAttacks;
///
/// Peak attack values with slower decay.
/// Delegates to .
///
public static float[] FrequencyBandAttackPeaks => Context.FrequencyBandAttackPeaks;
///
/// Onset detection values for beat synchronization.
/// Delegates to .
///
internal static float[] FrequencyBandOnSets => Context.FrequencyBandOnSets;
#endregion
internal const DataFlags BassFlagForFftBufferSize = DataFlags.FFT2048;
internal const int FftBufferSize = 1024; // For Bass DataFlags.FFT2048
///
/// 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.
///
/// Interleaved stereo float buffer
public static void ComputeFftFromBuffer(float[] pcmBuffer)
{
ComputeFftFromBuffer(pcmBuffer, Context);
}
///
/// 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.
///
/// Interleaved stereo float buffer
/// The analysis context to store results in
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);
}
}
}