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); } } }