์ค๋์ Unity์์ Microphone API๋ฅผ ์ฌ์ฉํด์ ๋ง์ดํฌ ์ ๋ ฅ์ ๋ฐ์ UI๋ก ์๊ฐํํ๋ ์์คํ ์ ๋ง๋ค์ด๋ดค๋ค.
๋ง์ดํฌ๋ก ์ ๋ ฅ๋ ์๋ฆฌ์ ํฌ๊ธฐ๋ฅผ ๊ฐ์งํ๋ค.
๊ฐ์ง๋ ๋ณผ๋ฅจ์ Image.fillAmount๋ก ์๊ฐํํ๋ค.
์กฐ์ฉํ ๋ ๋ฐ๊ฐ 0์ ๊ฐ๊น๊ณ , ์๋ฆฌ๋ฅผ ๋ด๋ฉด ์์ฐ์ค๋ฝ๊ฒ ์ฌ๋ผ๊ฐ๋๋ก ํ๋ค.
Unity์๋ Microphone ํด๋์ค๋ฅผ ํตํด ์ค๋์ค ์
๋ ฅ์ ๋ฐ์ ์ ์๋ค.
AudioSource์ ๋ง์ดํฌ ์
๋ ฅ์ ์ฐ๊ฒฐํ๊ณ , GetOutputData()๋ก ์ค์๊ฐ ์ค๋์ค ์ํ์ ๊ฐ์ ธ์ ๋ถ์ํ ์ ์๋ค.
audioSource.clip = Microphone.Start(mic, true, 10, 44100);
audioSource.loop = true;
audioSource.Play();
GetOutputData()๋ก ๋ฐ์ ์ํ์ ํตํด RMS(Root Mean Square)๋ฅผ ๊ตฌํ๊ณ , ์ด๋ฅผ ๋ฐ์๋ฒจ(dB)๋ก ๋ณํํ๋ค.
float sum = 0f;
for (int i = 0; i < samples.Length; i++)
{
sum += samples[i] * samples[i];
}
float rms = Mathf.Sqrt(sum / samples.Length);
float db = 20 * Mathf.Log10(rms / 0.1f);
์ด๊ธฐ์๋ ์๋ฌด ๋ง๋ ์ ํด๋ fillAmount๊ฐ 0.3 ๊ทผ์ฒ์์ ๊ณ์ ์์ง์ฌ์ ๋๋ฌด ๋ฏผ๊ฐํ๊ฒ ๋๊ปด์ก๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์๊ณ๊ฐ(threshold dB)๋ฅผ ์ค์ ํด์ ์ผ์ ๋ณผ๋ฅจ ์ดํ์ ์
๋ ฅ์ ๋ฌด์ํ๋๋ก ์ฒ๋ฆฌํ๋ค.
float thresholdDb = -20f;
db = Mathf.Max(db, thresholdDb);
๊ทธ๋ฆฌ๊ณ Mathf.InverseLerp()๋ฅผ ํตํด -20dB ~ 0dB ๋ฒ์๋ฅผ 0~1๋ก ์ ๊ทํํด์ Image.fillAmount์ ๋ฐ์ํ๋ค.
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
// ์ด ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ ์ค๋ธ์ ํธ์๋ AudioSource๊ฐ ๋ฐ๋์ ํ์ํ๋ค๋ ์๋ฏธ
[RequireComponent(typeof(AudioSource))]
public class MicrophoneTest : MonoBehaviour
{
private AudioSource audioSource; // ๋ง์ดํฌ ์
๋ ฅ์ ๋ฐ์ AudioSource ์ปดํฌ๋ํธ
public Image soundImage; // ์๋ฆฌ ํฌ๊ธฐ๋ฅผ ์๊ฐํํ UI Image (fillAmount๋ก ์ฌ์ฉ)
void Start()
{
audioSource = GetComponent<AudioSource>(); // AudioSource ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์ด
// ๋ง์ดํฌ ์ฅ์น๊ฐ ์๋์ง ํ์ธ
if (Microphone.devices.Length > 0)
{
string mic = Microphone.devices[0]; // ์ฒซ ๋ฒ์งธ ๋ง์ดํฌ ์ฅ์น๋ฅผ ์ฌ์ฉ
Debug.Log("์ฌ์ฉ์ค์ธ ๋ง์ดํฌ : " + mic);
// ๋ง์ดํฌ ์
๋ ฅ์ AudioSource์ ์ฐ๊ฒฐ (10์ด์ง๋ฆฌ ๋ฃจํ ๋
น์, ์ํ๋ ์ดํธ 44100Hz)
audioSource.clip = Microphone.Start(mic, true, 10, 44100);
audioSource.loop = true; // ๋ฐ๋ณต ์ฌ์ ์ค์
// ๋ง์ดํฌ๊ฐ ์์๋ ๋๊น์ง ๋๊ธฐ
while (!(Microphone.GetPosition(mic) > 0)) {}
// ์ค๋์ค ์ฌ์ ์์ (์ค์ ๋ก๋ ๋ง์ดํฌ ์
๋ ฅ ์๋ฆฌ ์ฌ์)
audioSource.Play();
}
else
{
Debug.LogWarning("๋ง์ดํฌ ์ฅ์น๋ฅผ ์ฐพ์์ ์์ต๋๋ค");
}
}
void Update()
{
float[] samples = new float[256]; // ์ค๋์ค ์ํ ๋ฐ์ดํฐ๋ฅผ ๋ด์ ๋ฐฐ์ด
audioSource.GetOutputData(samples, 0); // ํ์ฌ ์ฌ์ ์ค์ธ ์ค๋์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ด (์ฑ๋ 0)
float sum = 0f;
// RMS(Root Mean Square)๋ฅผ ๊ณ์ฐํ๊ธฐ ์ํ ์ ๊ณฑํฉ ๊ณ์ฐ
for (int i = 0; i < samples.Length; i++)
{
sum += samples[i] * samples[i];
}
float rms = Mathf.Sqrt(sum / samples.Length); // RMS ๊ณ์ฐ (์๋ฆฌ์ ์๋์ง)
float db = 20 * Mathf.Log10(rms / 0.1f); // RMS ๊ฐ์ ๋ฐ์๋ฒจ(dB)๋ก ๋ณํ
// 1๋จ๊ณ: ๋๋ฌด ์์ ์๋ฆฌ๋ ๋ฌด์ํ๊ธฐ ์ํ ์๊ณ๊ฐ ์ค์
float thresholdDb = -20f;
db = Mathf.Max(db, thresholdDb); // ๋ฐ์๋ฒจ์ด -20๋ณด๋ค ์์ผ๋ฉด -20์ผ๋ก ๊ณ ์ (๋
ธ์ด์ฆ ๋ฐฉ์ง)
// 2๋จ๊ณ: ๋ฐ์๋ฒจ์ 0~1 ๋ฒ์๋ก ์ ๊ทํํ๊ณ ๊ฐ๋ ์ ์ฉ
float sensitivity = 1.0f; // ๊ฐ๋ ์ค์ (๊ฐ์ด ํด์๋ก ๋ฏผ๊ฐ)
float normalizedVolume = Mathf.Clamp01(sensitivity * Mathf.InverseLerp(-20f, 0f, db));
// Mathf.InverseLerp: -20dB ~ 0dB ๋ฒ์๋ฅผ 0~1๋ก ๋งคํ
// Clamp01: ๊ฐ์ด 0๋ณด๋ค ์๊ฑฐ๋ 1๋ณด๋ค ํฌ์ง ์๋๋ก ์ ํ
// 3๋จ๊ณ: UI ์ด๋ฏธ์ง์ ๋ถ๋๋ฝ๊ฒ ๋ฐ์ (๋ณผ๋ฅจ ๋ฐ์ฒ๋ผ ๋ณด์ด๊ฒ ํ๊ธฐ ์ํด ๋ณด๊ฐ ์ ์ฉ)
float currentFill = soundImage.fillAmount;
soundImage.fillAmount = Mathf.Lerp(currentFill, normalizedVolume, Time.deltaTime * 10f);
// Mathf.Lerp๋ฅผ ์ฌ์ฉํด ๊ฐ์์ค๋ฌ์ด ๋ณํ ์์ด ์์ฐ์ค๋ฝ๊ฒ fillAmount๋ฅผ ๋ณ๊ฒฝ
}
}
๋งํ์ง ์์ ๋๋ ๋ฐ๊ฐ 0์ ๋จธ๋ฌด๋ฅธ๋ค.
๋ง์ ํ๋ฉด ์์ฐ์ค๋ฝ๊ฒ ๋ฐ๊ฐ ์ฌ๋ผ๊ฐ๋ค.
fillAmount์ Mathf.Lerp()๋ก ๋ถ๋๋ฌ์ด ์ ํ๋ ์ ์ฉํ๋ค.
Unity์์ ๋ง์ดํฌ ์
๋ ฅ์ ๋ค๋ค๋ณธ ๊ฑด ์ฒ์์ด์๋๋ฐ, ์๊ฐ๋ณด๋ค ๊ฐ๋จํ๊ฒ ๊ตฌํ์ด ๊ฐ๋ฅํ๋ค.
๋จ์ํ Image.fillAmount์ RMS ๊ณ์ฐ๋ง์ผ๋ก๋ ์์ฑ ์๊ฐํ๋ฅผ ํ ์ ์๋ค๋ ์ ์ด ํฅ๋ฏธ๋ก์ ๊ณ ,
์ถํ์๋ ์ด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ฑ ์ธ์ ํธ๋ฆฌ๊ฑฐ๋ ์์ฑ ๊ธฐ๋ฐ ์ธํฐ๋์
๋ ๋ง๋ค์ด๋ณผ ์ ์์ ๊ฒ ๊ฐ๋ค.
