Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save sagarpatel/1f3a645e60d54b7c3e1f9143d12edd14 to your computer and use it in GitHub Desktop.
Save sagarpatel/1f3a645e60d54b7c3e1f9143d12edd14 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System.Collections;
public class FD_FrequencyDataManager : MonoBehaviour
{
const int m_rawFFTDataSize = 1024; //8192;
float[] m_currentRawFFTDataArray;
const int m_processedFFTDataSize = 128; // separated into 8 sections
float[] m_processedFFTDataArray;
float[] m_previousProcessedFFTDataArray;
// set first to 0 as a quick hack to keep bass ragion clean so it doens't clip through player
int[] m_samplesAccumulationPerSectionArray = { 1, 1, 2, 3, 6, 8, 12, 30 }; // for FFT 2048//{ 1, 2, 4, 8, 12, 24, 55, 175 }; //for FFT 8192 // // // for FFT 1024 { 1, 1, 2, 3, 6, 8, 12, 30 };
int m_samplesAccumulationStartIndexOffset = 0;
float[] m_sectionsScalerArray = { 1, 1, 1, 1, 1, 1, 1, 1 };
public float m_globalFFTDataScaler = 1.0f;
public AnimationCurve m_rValueCurve;
public AnimationCurve m_gValueCurve;
public AnimationCurve m_bValueCurve;
public float d_r;
public float d_g;
public float d_b;
public Color m_currentFFTColor;
public float m_currentVolume;
float m_rScaler = 1.0f;
float m_gScaler = 1.0f;
float m_bScaler = 1.0f;
public float m_colorScaler = 25.0f;
float m_localPeakValueThreashold = 0.15f;
float m_localPeakRedistributionRatio = 0.25f;
int m_peakValuesSpreadItterations = 3;
const int m_outputSamplesCount = 1024;
float[] m_outputSamplesArray;
int k_volumeRollingAverageCount = 7;
int m_volumeRollingAverageIndex = 0;
float[] m_volumeRollingAverageArray;
float m_volumeRollingAverage = 0;
public float m_volumeTemporalMaxima = 0;
public float m_volumeTemporalMaximaDecay = 0.421f;
void Start()
{
m_currentRawFFTDataArray = new float[m_rawFFTDataSize];
m_processedFFTDataArray = new float[m_processedFFTDataSize];
m_previousProcessedFFTDataArray = new float[m_processedFFTDataSize];
m_outputSamplesArray = new float[m_outputSamplesCount];
m_volumeRollingAverageArray = new float[k_volumeRollingAverageCount];
for (int i = 0; i < m_volumeRollingAverageArray.Length; i++)
m_volumeRollingAverageArray[i] = 0;
}
void Update()
{
CalculateFreshFFTData();
CalculateFreshRGB();
CalculateFreshVolume();
CalculateVolumeTemporalMaxima();
}
void CalculateVolumeTemporalMaxima()
{
if (m_volumeRollingAverage > m_volumeTemporalMaxima)
m_volumeTemporalMaxima = m_volumeRollingAverage;
else
m_volumeTemporalMaxima = Mathf.Clamp(m_volumeTemporalMaxima - m_volumeTemporalMaximaDecay * Time.deltaTime, 0, 1);
}
void ProcessRawFFTData()
{
int accumulationIndex = 0;
int accumulationInterval = m_processedFFTDataArray.Length / 8;
float tempSum = 0;
int rawFFTIndex = 0;
int currentRawSamplesPerProcessedPoint = 0;
for (int i = 0; i < m_processedFFTDataArray.Length; i++)
{
if (i % accumulationInterval == 0 && i != 0)
accumulationIndex += 1;
tempSum = 0;
currentRawSamplesPerProcessedPoint = m_samplesAccumulationPerSectionArray[accumulationIndex];
for (int j = 0; j < currentRawSamplesPerProcessedPoint; j++)
{
tempSum += m_sectionsScalerArray[accumulationIndex] * m_currentRawFFTDataArray[rawFFTIndex + m_samplesAccumulationStartIndexOffset];
rawFFTIndex += 1;
}
m_processedFFTDataArray[i] = Mathf.Clamp(m_globalFFTDataScaler * tempSum, 0, 1); ///(float)currentRawSamplesPerProcessedPoint;
//Debug.Log("Sum for acuumuator: " + currentRawSamplesPerProcessedPoint + " , " + tempSum);
// averageing with previous to smooth out depth axis and hide repeat data
m_processedFFTDataArray[i] = (0.75f * m_processedFFTDataArray[i] + 0.25f * m_previousProcessedFFTDataArray[i]);
m_previousProcessedFFTDataArray[i] = m_processedFFTDataArray[i];
}
SpreadOutPeakValues();
}
void SpreadOutPeakValues()
{
int spreadCounter = 0;
float prevDelta = 0;
float postDelta = 0;
// Do multiple passes
for (int j = 0; j < m_peakValuesSpreadItterations; j++)
{
// TODO: handle cases for high values at begining and end of array
for (int i = 1; i < m_processedFFTDataArray.Length - 1; i++)
{
float amountToRedistributePerSide = 0.5f * m_localPeakRedistributionRatio * m_processedFFTDataArray[i];
prevDelta = m_processedFFTDataArray[i] - m_processedFFTDataArray[i - 1];
postDelta = m_processedFFTDataArray[i] - m_processedFFTDataArray[i + 1];
if (prevDelta > m_localPeakValueThreashold)
{
spreadCounter++;
m_processedFFTDataArray[i] -= amountToRedistributePerSide;
m_processedFFTDataArray[i - 1] += 0.5f * amountToRedistributePerSide;
}
if (postDelta > m_localPeakValueThreashold)
{
spreadCounter++;
m_processedFFTDataArray[i] -= amountToRedistributePerSide;
m_processedFFTDataArray[i + 1] += 0.5f * amountToRedistributePerSide;
}
}
}
//if(spreadCounter > 0)
// Debug.LogError("spread: " + spreadCounter );
}
Color CalculateFreshRGB()
{
int subdivisionIndex = 0;
int subdivisionInterval = m_processedFFTDataArray.Length / 8;
// goes through subdivisions 0 - 3
float r = 0;
int r_SubdivisionStart = 0;
int r_SubdivisionEnd = 4;
float r_weight = 1.0f / ((r_SubdivisionEnd - r_SubdivisionStart + 1) * (float)subdivisionInterval);
// goes through subdivisions 3 - 6
float g = 0;
int g_SubdivisionStart = 3;
int g_SubdivisionEnd = 6;
float g_weight = 1.0f / ((g_SubdivisionEnd - g_SubdivisionStart + 1) * (float)subdivisionInterval);
// goes through subdivisions 4 - 7
float b = 0;
int b_SubdivisionStart = 4;
int b_SubdivisionEnd = 7;
float b_weight = 1.0f / ((b_SubdivisionEnd - b_SubdivisionStart + 1) * (float)subdivisionInterval);
float progressOnCurve = 0;
for (int i = 0; i < m_processedFFTDataArray.Length; i++)
{
if (i % subdivisionInterval == 0 && i != 0)
subdivisionIndex += 1;
if (subdivisionIndex <= r_SubdivisionEnd)
{
progressOnCurve = Mathf.InverseLerp(subdivisionInterval * r_SubdivisionStart, subdivisionInterval * r_SubdivisionEnd, i);
r += m_rValueCurve.Evaluate(progressOnCurve) * m_processedFFTDataArray[i] * r_weight;
}
if (subdivisionIndex >= g_SubdivisionStart && subdivisionIndex <= g_SubdivisionEnd)
{
progressOnCurve = Mathf.InverseLerp(subdivisionInterval * g_SubdivisionStart, subdivisionInterval * g_SubdivisionEnd, i);
g += m_gValueCurve.Evaluate(progressOnCurve) * m_processedFFTDataArray[i] * g_weight;
}
if (subdivisionIndex >= b_SubdivisionStart)
{
progressOnCurve = Mathf.InverseLerp(subdivisionInterval * b_SubdivisionStart, subdivisionInterval * b_SubdivisionEnd, i);
b += m_bValueCurve.Evaluate(progressOnCurve) * m_processedFFTDataArray[i] * b_weight;
}
}
Color calculatedColor = m_colorScaler * new Color(r * m_rScaler, g * m_gScaler, b * m_bScaler);
m_currentFFTColor = calculatedColor;
d_r = calculatedColor.r;
d_g = calculatedColor.g;
d_b = calculatedColor.b;
return calculatedColor;
}
float[] CalculateFreshFFTData()
{
//m_liveAudioDataManager.m_liveAudioSource.GetSpectrumData(m_currentRawFFTDataArray, 0, FFTWindow.BlackmanHarris);
AudioListener.GetSpectrumData(m_currentRawFFTDataArray, 0, FFTWindow.BlackmanHarris);
ProcessRawFFTData();
return m_processedFFTDataArray;
}
// ported over from Sagar's VR_VF project
// http://answers.unity3d.com/questions/165729/editing-height-relative-to-audio-levels.html
float CalculateFreshVolume()
{
float vol = 0;
AudioListener.GetOutputData(m_outputSamplesArray, 0);
float sum = 0;
for (int i = 0; i < m_outputSamplesArray.Length; i++)
{
sum += m_outputSamplesArray[i] * m_outputSamplesArray[i];
}
// vol in RMS
vol = Mathf.Sqrt(sum / (float)m_outputSamplesArray.Length);
//vol = (sum / (float)m_outputSamplesArray.Length);
vol = Mathf.Sqrt(vol);
m_currentVolume = vol;
m_volumeRollingAverageIndex = (m_volumeRollingAverageIndex + 1) % k_volumeRollingAverageCount;
m_volumeRollingAverageArray[m_volumeRollingAverageIndex] = m_currentVolume;
sum = 0;
for (int i = 0; i < k_volumeRollingAverageCount; i++)
{
sum += m_volumeRollingAverageArray[i];
}
m_volumeRollingAverage = sum / (float)k_volumeRollingAverageCount;
return vol;
}
int CalculateMaxExtraSamplesCountForSection(int targetSectionIndex)
{
int totalSamplesSum = 0 + m_samplesAccumulationStartIndexOffset;
int sectionIndexCounter = 0;
int sectionInterval = m_processedFFTDataArray.Length / 8;
for (int i = 0; i < m_processedFFTDataArray.Length; i++)
{
if (i % sectionInterval == 0 && i != 0)
sectionIndexCounter += 1;
totalSamplesSum += m_samplesAccumulationPerSectionArray[sectionIndexCounter];
}
int samplesRemaining = m_currentRawFFTDataArray.Length - totalSamplesSum;
int maxSamplesCountForIndex = samplesRemaining / m_samplesAccumulationPerSectionArray[targetSectionIndex];
return maxSamplesCountForIndex;
}
public void IncrementFrequencyRangeSamplesCount(int sectionIndex, int samplesChangeCount)
{
int maxSamplesCount = m_samplesAccumulationPerSectionArray[sectionIndex] + CalculateMaxExtraSamplesCountForSection(sectionIndex);
int changedSamplesCountRaw = m_samplesAccumulationPerSectionArray[sectionIndex] + samplesChangeCount;
m_samplesAccumulationPerSectionArray[sectionIndex] = (int)Mathf.Clamp(changedSamplesCountRaw, 1, maxSamplesCount);
}
// public data getters
// array is of length m_processedFFTDataSize
public float[] GetFreshFFTData()
{
return m_processedFFTDataArray;
}
public Color GetFreshRGB()
{
return m_currentFFTColor;
}
// range is 0 to 1, float
public float GetVolumeRollingAverage()
{
return m_volumeRollingAverage;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment