gpt4 book ai didi

audio - 分析 “whiSTLe”声音的音高/音符

转载 作者:行者123 更新时间:2023-12-04 13:12:34 27 4
gpt4 key购买 nike

我正在尝试构建一个系统,该系统将能够处理某人吹口哨的记录并输出注释。

谁能推荐一个开源平台,我可以将其用作音符/音高识别和波形文件分析的基础?

提前致谢

最佳答案

正如其他许多人已经说过的那样,FFT是解决问题的方法。我已经使用http://www.cs.princeton.edu/introcs/97data/中的FFT代码在Java中编写了一个小示例。为了运行它,您还将需要该页面上的Complex类(请参阅源以获取确切的URL)。

代码读取文件,在文件上逐个窗口浏览,并在每个窗口上执行FFT。对于每个FFT,它都会寻找最大系数并输出相应的频率。这对于正弦波之类的干净信号确实非常有效,但是对于实际的啸叫声,您可能必须添加更多声音。我已经用自己创建的一些口哨测试了一些文件(使用便携式计算机的集成麦克风),代码确实了解正在发生的事情,但是为了获得实际记录,还需要做更多的工作。

1)您可能需要一些更智能的窗口技术。我的代码现在使用的是一个简单的矩形窗口。由于FFT假定输入信号可以周期性地继续,因此当窗口中的第一个和最后一个采样不匹配时,会检测到其他频率。这就是所谓的频谱泄漏(http://en.wikipedia.org/wiki/Spectral_leakage),通常会使用一个在窗口的开始和结束处降低样本权重的窗口(http://en.wikipedia.org/wiki/Window_function)。尽管泄漏不应导致将错误的频率检测为最大频率,但使用窗口将提高检测质量。

2)要将频率与实际音符相匹配,可以使用包含频率的数组(例如440 Hz的a'),然后查找最接近已识别频率的频率。但是,如果吹口哨不符合标准调音,则将不再起作用。既然哨声仍然是正确的,只是调音有所不同(就像吉他或其他乐器可以进行不同的调音,并且听起来仍然不错,只要对所有琴弦进行一致的调音),您仍然可以通过以下方式找到音符:以所识别频率的比率。您可以阅读http://en.wikipedia.org/wiki/Pitch_%28music%29作为起点。这也很有趣:http://en.wikipedia.org/wiki/Piano_key_frequencies

3)此外,检测每个单独音调开始和停止的时间点可能很有趣。这可以作为预处理步骤添加。然后,您可以对每个单独的音符进行FFT。但是,如果吹口哨不停,而只是在音符之间弯曲,这将不是那么容易。

绝对可以看看其他人建议的库。我什么都不知道,但是也许它们已经包含了执行上述功能的功能。

现在到代码了。请让我知道对您有用的东西,我觉得这个话题很有趣。

编辑:我更新了代码,以包括重叠和从频率到音符的简单映射器。如上所述,它仅适用于“调谐”的吹口哨。

package de.ahans.playground;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

public class FftMaxFrequency {

// taken from http://www.cs.princeton.edu/introcs/97data/FFT.java.html
// (first hit in Google for "java fft"
// needs Complex class from http://www.cs.princeton.edu/introcs/97data/Complex.java
public static Complex[] fft(Complex[] x) {
int N = x.length;

// base case
if (N == 1) return new Complex[] { x[0] };

// radix 2 Cooley-Tukey FFT
if (N % 2 != 0) { throw new RuntimeException("N is not a power of 2"); }

// fft of even terms
Complex[] even = new Complex[N/2];
for (int k = 0; k < N/2; k++) {
even[k] = x[2*k];
}
Complex[] q = fft(even);

// fft of odd terms
Complex[] odd = even; // reuse the array
for (int k = 0; k < N/2; k++) {
odd[k] = x[2*k + 1];
}
Complex[] r = fft(odd);

// combine
Complex[] y = new Complex[N];
for (int k = 0; k < N/2; k++) {
double kth = -2 * k * Math.PI / N;
Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
y[k] = q[k].plus(wk.times(r[k]));
y[k + N/2] = q[k].minus(wk.times(r[k]));
}
return y;
}

static class AudioReader {
private AudioFormat audioFormat;

public AudioReader() {}

public double[] readAudioData(File file) throws UnsupportedAudioFileException, IOException {
AudioInputStream in = AudioSystem.getAudioInputStream(file);
audioFormat = in.getFormat();
int depth = audioFormat.getSampleSizeInBits();
long length = in.getFrameLength();
if (audioFormat.isBigEndian()) {
throw new UnsupportedAudioFileException("big endian not supported");
}
if (audioFormat.getChannels() != 1) {
throw new UnsupportedAudioFileException("only 1 channel supported");
}

byte[] tmp = new byte[(int) length];
byte[] samples = null;
int bytesPerSample = depth/8;
int bytesRead;
while (-1 != (bytesRead = in.read(tmp))) {
if (samples == null) {
samples = Arrays.copyOf(tmp, bytesRead);
} else {
int oldLen = samples.length;
samples = Arrays.copyOf(samples, oldLen + bytesRead);
for (int i = 0; i < bytesRead; i++) samples[oldLen+i] = tmp[i];
}
}

double[] data = new double[samples.length/bytesPerSample];

for (int i = 0; i < samples.length-bytesPerSample; i += bytesPerSample) {
int sample = 0;
for (int j = 0; j < bytesPerSample; j++) sample += samples[i+j] << j*8;
data[i/bytesPerSample] = (double) sample / Math.pow(2, depth);
}

return data;
}

public AudioFormat getAudioFormat() {
return audioFormat;
}
}

public class FrequencyNoteMapper {
private final String[] NOTE_NAMES = new String[] {
"A", "Bb", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"
};
private final double[] FREQUENCIES;
private final double a = 440;
private final int TOTAL_OCTAVES = 6;
private final int START_OCTAVE = -1; // relative to A

public FrequencyNoteMapper() {
FREQUENCIES = new double[TOTAL_OCTAVES*12];
int j = 0;
for (int octave = START_OCTAVE; octave < START_OCTAVE+TOTAL_OCTAVES; octave++) {
for (int note = 0; note < 12; note++) {
int i = octave*12+note;
FREQUENCIES[j++] = a * Math.pow(2, (double)i / 12.0);
}
}
}

public String findMatch(double frequency) {
if (frequency == 0)
return "none";

double minDistance = Double.MAX_VALUE;
int bestIdx = -1;

for (int i = 0; i < FREQUENCIES.length; i++) {
if (Math.abs(FREQUENCIES[i] - frequency) < minDistance) {
minDistance = Math.abs(FREQUENCIES[i] - frequency);
bestIdx = i;
}
}

int octave = bestIdx / 12;
int note = bestIdx % 12;

return NOTE_NAMES[note] + octave;
}
}

public void run (File file) throws UnsupportedAudioFileException, IOException {
FrequencyNoteMapper mapper = new FrequencyNoteMapper();

// size of window for FFT
int N = 4096;
int overlap = 1024;
AudioReader reader = new AudioReader();
double[] data = reader.readAudioData(file);

// sample rate is needed to calculate actual frequencies
float rate = reader.getAudioFormat().getSampleRate();

// go over the samples window-wise
for (int offset = 0; offset < data.length-N; offset += (N-overlap)) {
// for each window calculate the FFT
Complex[] x = new Complex[N];
for (int i = 0; i < N; i++) x[i] = new Complex(data[offset+i], 0);
Complex[] result = fft(x);

// find index of maximum coefficient
double max = -1;
int maxIdx = 0;
for (int i = result.length/2; i >= 0; i--) {
if (result[i].abs() > max) {
max = result[i].abs();
maxIdx = i;
}
}
// calculate the frequency of that coefficient
double peakFrequency = (double)maxIdx*rate/(double)N;
// and get the time of the start and end position of the current window
double windowBegin = offset/rate;
double windowEnd = (offset+(N-overlap))/rate;
System.out.printf("%f s to %f s:\t%f Hz -- %s\n", windowBegin, windowEnd, peakFrequency, mapper.findMatch(peakFrequency));
}
}

public static void main(String[] args) throws UnsupportedAudioFileException, IOException {
new FftMaxFrequency().run(new File("/home/axr/tmp/entchen.wav"));
}
}

关于audio - 分析 “whiSTLe”声音的音高/音符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2076857/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com