Częstotliwość dźwięku android

0

Witam, Próbuję uzyskać częstotliwość dźwięku z mikrofonu w czasie rzeczywistym w androidzie, próbowałem skorzystać z tego rozwiązania https://stackoverflow.com/a/42265870/6699541 niestety wyniki nie dokładne
5kHz - 49957
8kHZ - 80103
10kHz - 99914
12kHz - 120155
14kHz - 139965
16kHz - 160207
18kHz - 180017
nie do końca wiem co może być przyczyną tej niedokładności znalazłem bibliotekę używającą rozwiązania w c++ (kissFFT a tu biblioteka https://android-arsenal.com/details/1/6086#!description ) lecz nie do końca wiem jak jej użyć. Moje pytanie czy to będzie dobra droga użycie tej biblioteki (jeśli tak to jak jej użyć) bądź jakie inne rozwiązania były by dobre? (nie mówię to jakimś gotowcu a jedynie wskazaniu odpowiedniej drogi)
Z góry dzięki :)

1
  1. skąd wynik tych testów, skąd wzorzec częstotliwości?
  2. Ta podlinkowana odpowiedź z SO jest beznadziejna (nie dziwne, że nie ma głosów)
  3. całkowanie tylko 1024 próbek musi odbić się negatywnie na precyzji wyników
  4. Coś podejrzanie wygląda pobieranie tych próbek z mikrofonu, wartość bufferReadResult jest w zasadzie ignorowana.

Lepiej poczytaj dokumentację i wprowadź właściwe poprawki, bo to na pewno jest źle.

1

Poprawka na szybkiego:

final int bufferSizeInBytes = 1024; // to bym powiększył znacznie
class Recording extends Thread {

    @Override
    public void run() {
        while (keepRunning()) {
            int readSoFar = 0;
            short[] buffer = new short[bufferSizeInBytes];

            int bufferReadResult = audioInput.read(buffer, 
                                                   readSoFar,
                                                   bufferSizeInBytes - readSoFar);

            if (bufferReadResult < 0) {
                  break;
            }
            readSoFar += bufferReadResult;
            if (readSoFar == bufferSizeInBytes) {
                calculate(buffer);
                readSoFar = 0;
            }
        }
    }
}
0

Do tego co napisał @MarekR22 dodałbym kilka faktów:

  • przy częstotliwości próbkowania 44kHz i długości bufora 1024 dla FFT dostajesz widmo z krokiem prawie 86Hz, więc przy tej metodzie detekcji częstotliwości będziesz miał błąd około 1,7% dla 5kHz i będzie malał dla coraz wyższych częstotliwości
  • przy zwiększeniu rozmiaru bufora do 4096 ten krok będzie mniejszy 86/4 = 21,5 Hz, więc i tak o pomiarze "precyzyjnym" mowy nie ma, a zwiększysz obciążenie obliczeniami.
  • żeby wykrywać częstotliwości pomiędzy dyskretnymi częstotliwościami trzeba zastosować aproksymację widma np. w podlinkownym wątku SO ktoś podał link do GitHuba z projektem aplikacji demo, w którym zrobił to tak:
/*
Wykrycie największego piku w widmie w decybelach
*/
for (int i = 1; i < spectrumAmpOutDB.length; i++) {
            if (spectrumAmpOutDB[i] > maxAmpDB) {
                maxAmpDB = spectrumAmpOutDB[i];
                maxAmpFreq = i;
            }
        }
        int sampleRate = 44100;
        maxAmpFreq = maxAmpFreq * sampleRate / fftLen;
        /*
          Aproksymacja
        */
        if (sampleRate / fftLen < maxAmpFreq && maxAmpFreq < sampleRate / 2 - sampleRate / fftLen) {
            int id = (int) (Math.round(maxAmpFreq / sampleRate * fftLen));
            double x1 = spectrumAmpOutDB[id - 1];
            double x2 = spectrumAmpOutDB[id];
            double x3 = spectrumAmpOutDB[id + 1];
            double a = (x3 + x1) / 2 - x2;
            double b = (x3 - x1) / 2;
            if (a < 0) {
                double xPeak = -b / (2 * a);
                if (Math.abs(xPeak) < 1) {
                    maxAmpFreq += xPeak * sampleRate / fftLen;
                }
            }
        }
        return maxAmpFreq;

0

moje metoda obecnie wygląd tak, pytanie co muszę teraz policzyć by otrzymać wartość finalną i czy do tej pory jest dobrze?
private void calculate(short[] buffer) {

        Complex[] complexData = new Complex[buffer.length];

        for (int i = 0; i < buffer.length; i++) {
            complexData[i] = new Complex(buffer[i], 0.0);
        }

        Complex[] fft = FFT.fft(complexData);
        spectrumData = new double[fft.length];

        for (int i = 0; i < fft.length; i++) {
            spectrumData[i] = fft[i].abs() / buffer.length;
            Log.i("freq", "" + spectrumData[i]);
        }

    }
0

Pytanie zasadnicze: chcesz sobie to fft narysować, wykrywać konkretną częstotliwość czy może mierzyć częstotliwość wejściową? Z postu wynika to ostatnie, ale chcę się upewnić co w zasadzie robisz. Bo ogólnie FFT niekoniecznie musi tu być optymalne, w ostatnim zastosowaniu prostym (prymitywnym wręcz), acz często skutecznym rozwiązaniem jest LPF+detektor szczytowy przerabiający Ci sygnał na prostokąt; często zadziała równie dobrze a imho będzie prostsze; pytanie tylko jaki masz zakres częstotliwości wejściowych, jaka jest częstotliwość próbkowania... no ogólnie - co to ma robić.

1 użytkowników online, w tym zalogowanych: 0, gości: 1