Извлечение аудио частоты из инструмента, чтобы найти музыкальную ноту
Я пытаюсь разработать приложение для Android, которое извлекает звуковую частоту из инструмента. Я использую метод быстрого преобразования Фурье с Jtransforms. Вот что у меня так далеко:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new readFrequencies().execute();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private class readFrequencies extends AsyncTask<Void,Integer,Integer> {
@Override
protected Integer doInBackground(Void... arg0) {
AudioRecord recorder = null;
int bufferSize = 0;
boolean recording = true;
int rate = 8000;
short audioFormat = AudioFormat.ENCODING_PCM_16BIT;
short channelConfig = AudioFormat.CHANNEL_IN_MONO;
try {
bufferSize = AudioRecord.getMinBufferSize(rate,channelConfig, audioFormat);
recorder = new AudioRecord(AudioSource.DEFAULT, rate,
channelConfig, audioFormat, bufferSize);
if (recorder.getState() == AudioRecord.STATE_INITIALIZED) {
/*
* Android 4.1.2
*
int recorder_id = recorder.getAudioSessionId();
if (NoiseSuppressor.isAvailable()) NoiseSuppressor.create(recorder_id);
*/
}
else {
Toast.makeText(getApplicationContext(),
"Error en la inicialización", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {}
short[] audioData = new short[bufferSize];
if (recorder != null) {
while (recording) {
if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
recorder.startRecording();
}
else {
int numshorts = recorder.read(audioData,0,audioData.length);
if ((numshorts != AudioRecord.ERROR_INVALID_OPERATION) &&
(numshorts != AudioRecord.ERROR_BAD_VALUE)) {
// Hann
double[] preRealData = new double[bufferSize];
double PI = 3.14159265359;
for (int i = 0; i < bufferSize; i++) {
double multiplier = 0.5 * (1 - Math.cos(2*PI*i/(bufferSize-1)));
preRealData[i] = multiplier * audioData[i];
}
DoubleFFT_1D fft = new DoubleFFT_1D(bufferSize);
double[] realData = new double[bufferSize * 2];
for (int i=0;i<bufferSize;i++) {
realData[2*i] = preRealData[i];
realData[2*i+1] = 0;
}
fft.complexForward(realData);
double magnitude[] = new double[bufferSize / 2];
for (int i = 0; i < magnitude.length; i++) {
double R = realData[2 * i];
double I = realData[2 * i + 1];
magnitude[i] = Math.sqrt(I*I + R*R);
}
int maxIndex = 0;
double max = magnitude[0];
for(int i = 1; i < magnitude.length; i++) {
if (magnitude[i] > max) {
max = magnitude[i];
maxIndex = i;
}
}
int frequency = rate * maxIndex / bufferSize;
publishProgress(frequency);
}
else {
if (numshorts == AudioRecord.ERROR_BAD_VALUE) {
Toast.makeText(getApplicationContext(),
"ERROR_BAD_VALUE", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getApplicationContext(),
"ERROR_INVALID_OPERATION", Toast.LENGTH_SHORT).show();
}
return -1;
}
}
}
if (recorder.getState() == AudioRecord.RECORDSTATE_RECORDING)
recorder.stop(); //stop the recorder before ending the thread
recorder.release();
recorder=null;
}
return 0;
}
protected void onProgressUpdate(Integer... f) {
TextView texto = (TextView) findViewById(R.id.texto);
texto.setText(String.valueOf(f[0]));
}
protected void onPostExecute(Integer f) {
TextView texto = (TextView) findViewById(R.id.texto);
int frecuencias = f.intValue();
texto.setText(String.valueOf(frecuencias));
}
}
}
С помощью этого кода я могу получить точную частоту от генератора частоты, который выдает чистые сигналы. Однако, когда я пытаюсь сделать то же самое с инструментом, я получаю случайные частоты. Я знаю, что когда речь идет о реальных инструментах, полученные сигналы содержат гармоники, которые могут влиять на конечный результат, но я не знаю, чтобы получить реальную частоту в этом случае. Кто-нибудь может мне помочь?
Я использовал TarsosDSP и также попробовал метод автокорреляции, но я не смог получить то, что хотел.
Заранее спасибо.
2 ответа
Существует огромная разница между самой мощной частотной составляющей в спектре и воспринимаемой высотой тона человеком-слушателем.
Эта академическая статья, вероятно, является окончательным обзором подходов к решению некоторых проблем обнаружения основного тона, но не рассматривает проблемы восприятия, с которыми вам придется иметь дело при использовании реальных сигналов. По крайней мере, вам нужно будет учесть струнные и духовые колонны, которые прыгают на октаву в начале и конце нот, а также недостающие основы. Это будет особенная лотерея на Android, где микрофоны на устройствах не всегда совместимы.
Поиск пиковой частоты в спектре не лучше, чем использование автокорреляции, и часто это неправильно с реальными сигналами.
Подход FFT (точнее, с использованием STFT и оценки частоты по разностям фаз) может работать, но только с большой последующей обработкой спектра. Некоторые коммерческие приложения хроматического тюнера используют подход на основе FFT (я могу засвидетельствовать это, написав один).
Возможно, вы захотите посмотреть, как некоторые из плагинов для извлечения функций для Sonic Visualiser решают эту проблему.
Я также рекомендую ознакомиться с огромным количеством предыдущих вопросов, которые здесь задают примерно одно и то же - обычно это те, кто задает вопрос о настройке гитарных тюнеров.
Что бы это ни стоило - я считаю, что электронные тюнеры для музыкальных инструментов (например, гитарные тюнеры и т. Д.) Так не делают. Вместо того, чтобы делать БПФ, как вы делаете, они просто измеряют период волн (то есть время между переходами через ноль), а затем вычисляют частоту по периоду.