[Unity] [사운드] AMDF를 통해 사운드 파형에서 실제 기와까지 음계 판정

10291 단어 소리Unity

[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'로 변해 이들의 차이가 뚜렷하지 않았다.

낮은 계산량으로 일정한 정밀도를 얻을 수 있다


실시간 응답을 요구하고 자원이 제한된 환경에서 효과적인 수단 중 하나라고 할 수 있다.

좋은 웹페이지 즐겨찾기