이전 글에서 이어지는 내용이므로 이전 글을 보고 와주세요.
2024.03.26 - [Unity/Study] - 유니티 Audio Spectrum에서 얻은 데이터들을 대역 별로 나누기 #기초
Buffer를 사용하면 위 파란색 큐브처럼 값이 급격하게 변화하는 것을 막을 수 있습니다.
값이 갑자기 튀는 현상을 '약간 해결'해줄 수 있다고 생각하시면 됩니다.
Buffer란?
'Buffer’는 일반적으로 데이터를 일시적으로 저장하는 임시 저장소를 의미합니다. 데이터를 처리하는 두 개의 프로세스 사이에서 임시 데이터로 사용된다고 생각하시면 됩니다.
아래 코드에서 사용되는 'Buffer’는 FrequencyBands[i]의 값들을 일시적으로 저장하고, 이전의 값들을 기반으로 새로운 값을 계산하는 데 사용됩니다. 버퍼를 사용하여 데이터의 변화를 부드럽게 만들거나, 빠른 변화에 대응하는 등의 처리를 할 수 있습니다.
AudioPeer 수정
using UnityEngine;
public class AudioPeer : MonoBehaviour
{
public static AudioPeer Instance;
public enum AudioChannel
{
Left = 0,
Right = 1,
Center = 2,
LFE = 3,
RearLeft = 4,
RearRight = 5
}
[Header("[Ref]")]
[SerializeField] private AudioSource audioSource;
[Space(3)]
[Header("[Value]")]
[SerializeField, Tooltip("첫 번째 (왼쪽) 스테레오 채널 또는 모노 채널 \n두 번째 (오른쪽) 스테레오 채널 \n센터 채널 \nLFE (저주파 효과) 채널 \n후방 왼쪽 채널 \n후방 오른쪽 채널")] private AudioChannel channel = AudioChannel.Left;
[Range(3, 12)] public int Pow = 8;
public int FreqBandPower = 1;
[Space(3)]
[Header("[OutPut]")]
public float[] Samples;
public float[] FrequencyBands;
public float[] FrequencyBuffer;
private void Awake()
{
Instance = this;
Samples = new float[(int)Mathf.Pow(2,Pow)];
FrequencyBands = new float[Pow - 1];
FrequencyBuffer = new float[Pow - 1];
}
private void Update()
{
GetSpectrumAudioSource();
MakeFrequencyBand();
SmoothFrequencyBands();
}
private void GetSpectrumAudioSource()
{
audioSource.GetSpectrumData(samples: Samples, channel: (int)channel, FFTWindow.Blackman);
}
private void MakeFrequencyBand()
{
int count = 0;
for (int i = 0; i < Pow - 1; i++)
{
float average = 0;
int sampleCount = (int)Mathf.Pow(2, i) * 2;
if (i == Pow - 2)
{
sampleCount += 2;
}
for (int j = 0; j < sampleCount; j++)
{
average += Samples[count];
count++;
}
average /= sampleCount;
FrequencyBands[i] = average * FreqBandPower;
}
}
private void SmoothFrequencyBands()
{
for (int i = 0; i < Pow - 1; ++i)
{
FrequencyBuffer[i] = 0.875f * FrequencyBuffer[i] + 0.125f * FrequencyBands[i];
}
}
}
FrequencyBuffer 배열을 선언해줍니다.
추가된 함수는 아래와 같습니다.
private void SmoothFrequencyBands()
{
for (int i = 0; i < Pow - 1; ++i)
{
FrequencyBuffer[i] = 0.875f * FrequencyBuffer[i] + 0.125f * FrequencyBands[i];
}
}
FrequencyBuffer[i]를 이전 값의 87.5%와 FrequencyBands[i]의 12.5%의 합으로 업데이트하는 방식으로(일종의 가중 평균) FrequencyBuffer[i]를 부드럽게 업데이트하는 방식입니다.
ParamCube 스크립트 작성
이전 포스팅까지는 SpawnCube 스크립트에서 큐브를 컨트롤 했는데, 이젠 따로 ParamCube 스크립트를 만들어서 따로 큐브를 컨트롤해주고자 합니다.
using UnityEngine;
public class ParamCube : MonoBehaviour
{
public int index;
public bool useBuffer;
[SerializeField] private float sizePower = 10f;
[SerializeField] private float minYScale = 1f;
private void Update()
{
if (!useBuffer)
{
transform.localScale = new Vector3(1f, (AudioPeer.Instance.FrequencyBands[index] * sizePower) + minYScale, 1f);
}
else
{
transform.localScale = new Vector3(1f, (AudioPeer.Instance.FrequencyBuffer[index] * sizePower) + minYScale, 1f);
}
}
}
Buffer를 사용하고자 하는 객체에는 인스펙터에서 체크해주면 됩니다.
SpawnCubes 스크립트 수정
using UnityEngine;
public class SpawnCubes : MonoBehaviour
{
[SerializeField] private GameObject radiusObject;
private GameObject[] spawnedRadiusObjects;
public float Radius = 3f;
[SerializeField] private float sizePower = 10f;
[SerializeField] private float minYScale = 1f;
private float x = 0.3f;
private float z = 0.3f;
private int SampleLength;
private bool isStart;
private void Start()
{
SampleLength = AudioPeer.Instance.Samples.Length;
}
private void Spawn()
{
spawnedRadiusObjects = new GameObject[SampleLength];
for (int i = 0; i < SampleLength; i++)
{
float angle = i * 360f / SampleLength;
transform.rotation = Quaternion.Euler(0, angle, 0);
GameObject go = Instantiate(radiusObject, transform.forward * Radius, Quaternion.identity);
go.name = "Cube" + i;
spawnedRadiusObjects[i] = go;
}
isStart = true;
}
private void Update()
{
if (!isStart) return;
for (int i = 0; i < SampleLength; i++)
{
spawnedRadiusObjects[i].transform.localScale = new Vector3(x, (AudioPeer.Instance.Samples[i] * sizePower) + minYScale, z);
}
}
}
Update함수를 수정해주었습니다.