在现代计算机技术的帮助下,可以通过连接到声卡的麦克风捕获实时声音/音乐。声卡能够捕获数字信号,这些信号是一组在均匀间隔时间内取得的量化声音值。然而,数字信号本身并不提供声音中存在的频率信息。为了确定这些信息,需要对数据进行分析。
短时傅里叶变换(STFT)能够表示信号的相位和幅度。STFT的结果可以用来生成信号的频谱图:幅度平方随时间和频率的变化。将使用快速傅里叶变换(FFT)来生成短时间周期内信号的频谱图。计算出频谱图后,可以通过找到幅度平方最大值的索引来确定基本频率。改进的算法会找到几个这样的候选频率bin,其幅度平方位于最大值的顶部,然后进一步分析它们,以使用信号数据验证候选的基本频率。
当乐器演奏音符时,声音波是由弦、空气或扬声器产生的——乐器产生音乐音符。音乐音符的一个特点是音高(基本频率)。传统上,音乐字母表频率被分为八度,然后是半音。一个八度有12个命名的音调:C(主音),C#,D,D#,E,F,F#,G,G#,A,A#和B。八度也有名称:大调,小调,一线,二线等。“标准音高”(一线A或A4)的声波基本频率等于440 Hz。相邻两个音符的频率相差2的1/12次方,而两个相邻八度中同名音符的频率相差2的次方。
吉他通常演奏大调到二线八度的音高。空弦(E2,A2,D3,G3,B3和E4)的音高在表中用粗体字标出。
解决方案包含三个项目:主窗口应用程序(FftGuitarTuner),声音分析库(SoundAnalysis)和声音捕获库(SoundCapture)。解决方案的核心和SoundAnalysis项目是FFT算法(见SoundAnalysis.FftAlgorithm类的Calculate方法):
bit reversal
ComplexNumber[] data = new ComplexNumber[length];
for (int i = 0; i < x.Length; i++) {
int j = ReverseBits(i, bitsInLength);
data[j] = new ComplexNumber(x[i]);
}
// Cooley-Tukey
for (int i = 0; i < bitsInLength; i++) {
int m = 1 << i;
int n = m * 2;
double alpha = -(2 * Math.PI / n);
for (int k = 0; k < m; k++) {
// e^(-2*pi/N*k)
ComplexNumber oddPartMultiplier = new ComplexNumber(0, alpha * k).PoweredE();
for (int j = k; j < length; j += n) {
ComplexNumber evenPart = data[j];
ComplexNumber oddPart = oddPartMultiplier * data[j + m];
data[j] = evenPart + oddPart;
data[j + m] = evenPart - oddPart;
}
}
}
// calculate spectrogram
double[] spectrogram = new double[length];
for (int i = 0; i < spectrogram.Length; i++) {
spectrogram[i] = data[i].AbsPower2();
}
算法的数据由声卡捕获缓冲区提供。抽象的SoundCapture.SoundCaptureBase实用程序类是DirectSound的Capture和CaptureBuffer类的适配器,有助于封装缓冲和设置音频格式参数。应用程序需要Microsoft DirectX 9运行时组件,以便从麦克风实时捕获声音。
启动应用程序后,选择声音设备并演奏音符。应用程序将捕获实时声音,并计算信号的当前基本频率。这些信息可以用来调音吉他。
计算快速傅里叶变换时,使用了Cooley-Tukey算法。它为所需任务提供了良好的性能。为了挑战算法,应用程序实时分析了大约22,000个样本块:声音以44,100 Hz的速率和16位样本大小捕获,分析每秒进行两次。
声音分析库可以用于音调、背景噪声、声音或语音检测。连续声音的频谱图系列可以显示为2D(或3D)图像以进行视觉呈现。