[Unity] [사운드] AMDF를 통해 사운드 파형에서 실제 기와까지 음계 판정
[Unity] [사운드] AMDF를 통해 사운드 파형에서 실제 기와까지 음계 판정
파형에 포함된 주파수 성분을 추출할 때 부립엽 변환을 하지 않아도 주파수 성분을 산출할 수 있다.
AMDF라는 자가 연관성을 통해 파형 데이터에서 주파수 성분을 구하는 방법이 있다
https://pdfs.semanticscholar.org/7e00/c103c0197a05f9d20511ef03fd8bb0ba81a5.pdf
그래서 이거 해볼게요.
운영 환경: Unity 5.6.0f3
이른바 AMDF
AMDF는 Average Magnitude Difference Function의 약칭이라고 합니다.
요컨대
"100Hz 주파수 웨이브 데이터"및
1/50초 틀린 거랑 비교하면
물론 전혀 일치하지 않습니다.
하지만 1/100초를 엇갈린 것보다
완전히 일치하다.
이것을 응용하여 오차의 양을 조금씩 바꾸고 관련 함수를 계속 가하여'오위의 양과 일치율'의 관계에서 파형 데이터에 포함된 주파수 성분을 추출한다.
※ 도표 생성에 사용https://www.desmos.com/calculator.감사합니다.
조금씩 오차량을 바꾸어 자기 관련 함수를 자꾸 가하다
실시 시 아래 자료를 참고하였다.
https://www.utdallas.edu/~hynek/citing_papers/Mamun_A%20High%20Resolution%20Pitch%20Detection%20Algorithm.pdf
유니티에서는 오디오소스의 클립에 임의의 사운드 데이터를 할당하면 간단하게 파형 데이터를 얻을 수 있다.
웨이브 데이터는 AudioSource입니다.GetOutputData에서 가져올 수 있습니다.
https://docs.unity3d.com/jp/current/ScriptReference/AudioSource.GetOutputData.html void Start()
{
audioSource = GetComponent<AudioSource>();
}
void Update()
{
audioSource.GetOutputData(outputData, 0);
}
※ 마이크에서 주웠을 때, 이렇게 초기화하면 OK var audioSource = this.GetComponent<AudioSource>();
audioSource.loop = true;
audioSource.clip = Microphone.Start(deviceName, true, 10, 44100);
while (!(Microphone.GetPosition(deviceName) > 0)) { }
audioSource.Play();
440Hz의 경우를 가정하면
AudioSettings.하면, 만약, 만약...
440Hz의 한 주기는 440분의 1초이기 때문에 4800/440≈109로 색인을 엇갈린 파형 데이터를 비교한다.
아웃풋데이터[0]와 아웃풋데이터[19], 아웃풋데이터[11]와 아웃풋데이터[110], 아웃풋데이터[2]와 아웃풋데이터[11]를 접촉과 비교하면 된다는 것이다. // 誤差の総和を求めて、
int offset = (AudioSettings.outputSampleRate / 440);
int N = outputData.Length - offset;
for (int n = 0; n < N; ++n) {
diff += Mathf.Abs(outputData[n] - outputData[n + offset]);
}
// 比較した回数で割る
diff *= 1f / N;
또한, 오디오 세트링스.아웃풋 Sample Rate가 48000이면 Get Output Data가 참조하는 인덱스가 1/48000초 동안 이동할 때마다 시간이 엇갈립니다. 이것이 바로 해상도입니다.그러므로
440Hz의 한 주기 소요 시간은 약 109.09/48000초
439Hz의 한 주기 소요 시간은 약 109.34/48000초
따라서 엇갈려야 할 양은 같고 440Hz와 439Hz는 단순히 비교할 수 없다.
포함된 주파수 성분을 구하다
우선 이렇게 0~200의 범위 내에서 인덱스를 엇갈려 오차량의 오차를 산출한다. float[] diff = new float[max - min + 1];
for (int m = 0; m <= 200; ++m) {
int N = outputData.Length - m;
for (int n = 0; n < N; ++n) {
diff[m - min] += Mathf.Abs(outputData[n] - outputData[n + m]);
}
diff[m - min] *= 1f / N;
}
La(A4)를 들어보고 diff의 내용을 시각화해 봅시다.
이렇게 돼서
오차가 작을수록'골짜기'가 된다.
중앙, 오른쪽, 109가 엇갈린 곳에 작은 골짜기가 하나 있다(오차가 적다).
※ 그나저나 라(A4)의 스펙트럼은 다음과 같은 형태입니다. 440Hz 이후 880Hz, 1320Hz... 두 배의 진동을 보이는 스파이크를 볼 수 있습니다.
이걸로 음계를 재볼게요.
설치의 개요를 표시합니다. List<float> keyHzList = new List<float> {
27.500f, // 最低音(A0)
29.135f,
・・・
440.000f, // ラ(A4)
・・・
4186.009f, // 最高音(C8)
};
audioSource.GetOutputData(outputData, 0);
float[] keys = new float[88];
for(int key = 0; key < 88; ++key) {
float hz = keyHzList[key];
int m = (int)(AudioSettings.outputSampleRate / hz);
int N = outputData.Length - m;
float diff = 0f;
for (int n = 0; n < N; ++n) {
diff += Mathf.Abs(outputData[n] - outputData[n + m]);
}
diff *= 1f / N;
keys[key] = diff;
}
이로써 계산된'88개 키의 오차'에 근거하여'오차가 상대적으로 적은 키보드가 튕겼다'고 판단하여 키보드에 비추어 보았다
La(A4)를 테스트할 수 있습니다.
이와 함께 8도 이하의 라(A3) 1개도 검출됐다.
La(A4), 즉 440Hz를 조사할 때 색인을'109'로 엇갈려 비교했다.
'109'를 놓쳤을 때 일치율이 높은 파형 데이터는 물론 그 정수배인'218'과'327'을 놓쳤을 때도 일치율이 높다.
그리고 8개 8도 이하의 라(A3), 220Hz를 조사했을 때 220Hz/4000≈'218'로 변해 이들의 차이가 뚜렷하지 않았다.
낮은 계산량으로 일정한 정밀도를 얻을 수 있다
실시간 응답을 요구하고 자원이 제한된 환경에서 효과적인 수단 중 하나라고 할 수 있다.
Reference
이 문제에 관하여([Unity] [사운드] AMDF를 통해 사운드 파형에서 실제 기와까지 음계 판정), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://qiita.com/tosaki/items/293306fc428c81ea90e3
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
void Start()
{
audioSource = GetComponent<AudioSource>();
}
void Update()
{
audioSource.GetOutputData(outputData, 0);
}
var audioSource = this.GetComponent<AudioSource>();
audioSource.loop = true;
audioSource.clip = Microphone.Start(deviceName, true, 10, 44100);
while (!(Microphone.GetPosition(deviceName) > 0)) { }
audioSource.Play();
// 誤差の総和を求めて、
int offset = (AudioSettings.outputSampleRate / 440);
int N = outputData.Length - offset;
for (int n = 0; n < N; ++n) {
diff += Mathf.Abs(outputData[n] - outputData[n + offset]);
}
// 比較した回数で割る
diff *= 1f / N;
float[] diff = new float[max - min + 1];
for (int m = 0; m <= 200; ++m) {
int N = outputData.Length - m;
for (int n = 0; n < N; ++n) {
diff[m - min] += Mathf.Abs(outputData[n] - outputData[n + m]);
}
diff[m - min] *= 1f / N;
}
List<float> keyHzList = new List<float> {
27.500f, // 最低音(A0)
29.135f,
・・・
440.000f, // ラ(A4)
・・・
4186.009f, // 最高音(C8)
};
audioSource.GetOutputData(outputData, 0);
float[] keys = new float[88];
for(int key = 0; key < 88; ++key) {
float hz = keyHzList[key];
int m = (int)(AudioSettings.outputSampleRate / hz);
int N = outputData.Length - m;
float diff = 0f;
for (int n = 0; n < N; ++n) {
diff += Mathf.Abs(outputData[n] - outputData[n + m]);
}
diff *= 1f / N;
keys[key] = diff;
}
Reference
이 문제에 관하여([Unity] [사운드] AMDF를 통해 사운드 파형에서 실제 기와까지 음계 판정), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/tosaki/items/293306fc428c81ea90e3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)