# 用Unity的GetSpectrumData方法识别钢琴曲中的钢琴琴键

2 篇文章 2 订阅

## 音律

### 88键钢琴各键键位与音高

(图1：88键钢琴键位与音高，原图地址

## Unity GetSpectrumData获取的音频数据与88键钢琴各键的映射

    float[] Herz_PianoKeys = new float[88];
void InitAllPianoKeysHerz()
{
for(int i = 0; i < Herz_PianoKeys.Length; i++)
{
Herz_PianoKeys[i] = Mathf.Pow(Mathf.Pow(2, ET12), ((i + 1) - Pos_A4))*Herz_A4;
}
}


    private float[] spectrumData = new float[8192];
private Dictionary<int, int> KeysDataMap = new Dictionary<int, int>();

void BindKeysAndSpectrumData()
{
//由于官网上缺乏说明，其实这里对GetSpectrumData的返回数组中的每个成员的所代表的频率只是猜测，不过从测试结果来看应该是猜对了
float interval = MaxHerzOfSpectrumData / spectrumData.Length;
//尝试找到精确的映射。与88和8192两个参数有关。
//try to find the precise mapping.the algorithm depending on the parameters of 88 and 8192
for (int i = 0; i < spectrumData.Length; i++)
{
for (int j = 0; j < Herz_PianoKeys.Length; j++) {
if (Mathf.Abs((i + 1) * interval - Herz_PianoKeys[j]) <= 0.05f)
{
KeysDataMap[j] = i;
}
else if (KeysDataMap.ContainsKey(j) == false && Mathf.Abs((i + 1) * interval - Herz_PianoKeys[j]) <= 1f)
{
KeysDataMap[j] = i;
}
else if (KeysDataMap.ContainsKey(j) == false && Mathf.Abs((i + 1) * interval - Herz_PianoKeys[j]) <= 2f)
{
KeysDataMap[j] = i;
}
}
}
}


    void AnalyzeMusic()
{
float maxValue = 0;
int maxKey = 0;
foreach (var key in KeysDataMap.Keys)
{
//find max
if (spectrumData[KeysDataMap[key]] > maxValue && spectrumData[KeysDataMap[key]] > threadshold)
{
maxValue = spectrumData[KeysDataMap[key]];
maxKey = key;
}
}

if (maxValue>0){
Debug.Log(maxKey + 1);
Debug.Log(spectrumData[KeysDataMap[maxKey]]);
//test
TestResult(maxKey+1);
}
}


## 全部代码与测试结果

C4(键位40)->C#4(键位41)->D4(键位42)->D#4(键位43)->E4(键位44)->F4(键位45)->F#4(键位46)->G4(键位47)->G#4(键位48)->A4(键位49)->A#4(键位50)->B4(键位51)->A5(键位52)->B4(键位51)->A#4(键位50)->A4(键位49)->G#4(键位48)->G4(键位47)->F#4(键位46)->F4(键位45)->E4(键位44)->D#4(键位43)->D4(键位42)->C#4(键位41)->C4(键位40)

(图2：爬音音频测试结果正确)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PianoKeysDetector : MonoBehaviour
{
//88键钢琴
//88 keys piano
float[] Herz_PianoKeys = new float[88];

//A4键440标准赫兹，键位49
//A4 at key 49 with 440Hz
float Herz_A4 = 440f;
int Pos_A4 = 49;

//12平均律
//twelve-tone equal temperament
float ET12 = 1f / 12f;

//过滤当前音乐杂音阈值
//filter threadshold of current music cliip

private AudioSource thisAudioSource;
private float[] spectrumData = new float[8192];
//the value denpended on pc, sould be updated in runtime
private float MaxHerzOfSpectrumData = 22050;

//钢琴键位<-->spectrumData位置
//piano key position<-->spectrumData position
private Dictionary<int, int> KeysDataMap = new Dictionary<int, int>();

void InitAllPianoKeysHerz()
{
for(int i = 0; i < Herz_PianoKeys.Length; i++)
{
Herz_PianoKeys[i] = Mathf.Pow(Mathf.Pow(2, ET12), ((i + 1) - Pos_A4))*Herz_A4;
}
}

void BindKeysAndSpectrumData()
{
float interval = MaxHerzOfSpectrumData / spectrumData.Length;
//尝试找到精确的映射。与88和8192两个参数有关。
//try to find the precise mapping.the algorithm depending on the parameters of 88 and 8192
for (int i = 0; i < spectrumData.Length; i++)
{
for (int j = 0; j < Herz_PianoKeys.Length; j++) {
if (Mathf.Abs((i + 1) * interval - Herz_PianoKeys[j]) <= 0.05f)
{
KeysDataMap[j] = i;
}
else if (KeysDataMap.ContainsKey(j) == false && Mathf.Abs((i + 1) * interval - Herz_PianoKeys[j]) <= 1f)
{
KeysDataMap[j] = i;
}
else if (KeysDataMap.ContainsKey(j) == false && Mathf.Abs((i + 1) * interval - Herz_PianoKeys[j]) <= 2f)
{
KeysDataMap[j] = i;
}
}
}
}

void AnalyzeMusic()
{
float maxValue = 0;
int maxKey = 0;
foreach (var key in KeysDataMap.Keys)
{
//find max
if (spectrumData[KeysDataMap[key]] > maxValue && spectrumData[KeysDataMap[key]] > threadshold)
{
maxValue = spectrumData[KeysDataMap[key]];
maxKey = key;
}
}

if (maxValue>0){
Debug.Log(maxKey + 1);
Debug.Log(spectrumData[KeysDataMap[maxKey]]);
//test
TestResult(maxKey+1);
}
}

//should be:C4 C#4 D4 D#4 E4 F4 F#4 G4 G#4 A4 A#4 B4 C5 B4 A#4 A4 G#4 G4 F#4 F4 E4 D#4 D4 C#4 C4
//          40 41  42 43  44 45 46  47 48  49 50  51 52 51 50  49 48  47 46  45 44 43  42 41  40
List<int> testResults = new List<int>();
void TestResult(int val)
{
if (testResults.Count > 0 && val != testResults[testResults.Count - 1])
{
}
else if (testResults.Count == 0)
{
}
}

// Start is called before the first frame update
void Start()
{
//例如：44100/2=22050
//eg:44100/2=22050
MaxHerzOfSpectrumData = AudioSettings.outputSampleRate / 2;
thisAudioSource = gameObject.GetComponent<AudioSource>();
InitAllPianoKeysHerz();
BindKeysAndSpectrumData();
thisAudioSource.Play();
Invoke("DebugTestResults", thisAudioSource.clip.length);
}

// Update is called once per frame
void Update()
{
thisAudioSource.GetSpectrumData(spectrumData, 0, FFTWindow.BlackmanHarris);
AnalyzeMusic();
}

//debug function

void DebugAllPianoKeysHerz()
{
for (int i = 0; i < Herz_PianoKeys.Length; i++)
{
Debug.Log(i);
Debug.Log("Herz:" + Herz_PianoKeys[i]);
}
}
void DebugKeysDataMap()
{
foreach (var key in KeysDataMap.Keys)
{
Debug.Log(key);
Debug.Log(KeysDataMap[key]);
}
}
void DebugTestResults()
{
string result = "";
for(int i = 0; i < testResults.Count; i++)
{
result += testResults[i].ToString() + " ";
}
Debug.Log(result);
}
}



## 未解决的问题

• 5
点赞
• 14
收藏
觉得还不错? 一键收藏
• 4
评论
10-01 369
09-18 657
04-24 2万+
01-23
08-09
08-19
03-30 2594
01-15 1724

### “相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、付费专栏及课程。