I am really struggling to figure this out. Essentially I am trying to find what frequency is being played via the mic. To my understand, I need to bruteforce the Goertzel algorithm. So essentially I just try every frequency using the Goertzel algorithm until I find the correct one. However, I do not understand how I actually know when the Goertzel algorithm has found the correct algorithm. Could someone please help me.
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button recordButton;
private TextView result;
private AudioRecord recording;
private static final int RECORDER_SAMPLERATE = 10000;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING);
double[] dbSample = new double[bufferSize];
short[] sample = new short[bufferSize];
private int frequency = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recordButton = findViewById(R.id.recordButton);
result = findViewById(R.id.resultTextView);
recordButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
recording = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, RECORDER_SAMPLERATE,
RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSize);
recording.startRecording();
int bufferReadResult = recording.read(sample, 0, bufferSize);
for (int j = 0; j < bufferSize && j < bufferReadResult; j++) {
dbSample[j] = (double) sample[j];
goertzel.processSample(dbSample[j]);
}
// Is this correct?
magnitude = Math.sqrt(goertzel.getMagnitudeSquared());
if(magnitude > maxMagnitude){
maxMagnitude = magnitude;
System.out.println("Freq is: " + Integer.toString(frequency));
}
goertzel.resetGoertzel();
frequency += 1;
}
});
}
}
Goertzel.java
public class Goertzel {
private float samplingRate;
private float targetFrequency;
private long n;
private double coeff, Q1, Q2;
private double sine, cosine;
public Goertzel(float samplingRate, float targetFrequency, long inN) {
this.samplingRate = samplingRate;
this.targetFrequency = targetFrequency;
n = inN;
}
public void resetGoertzel() {
Q1 = 0;
Q2 = 0;
}
public void initGoertzel() {
int k;
float floatN;
double omega;
floatN = (float) n;
k = (int) (0.5 + ((floatN * targetFrequency) / samplingRate));
omega = (2.0 * Math.PI * k) / floatN;
sine = Math.sin(omega);
cosine = Math.cos(omega);
coeff = 2.0 * cosine;
resetGoertzel();
}
public void processSample(double sample) {
double Q0;
Q0 = coeff * Q1 - Q2 + sample;
Q2 = Q1;
Q1 = Q0;
}
public double[] getRealImag(double[] parts) {
parts[0] = (Q1 - Q2 * cosine);
parts[1] = (Q2 * sine);
return parts;
}
public double getMagnitudeSquared() {
return (Q1 * Q1 + Q2 * Q2 - Q1 * Q2 * coeff);
}
}
You've asked about brute-forcing Goertzel specifically, so here is an annotated JUnit test that illustrates a reasonable approach:
Basically, for every 256 samples of audio you read in, you take that array, and run it past an array of Goertzels which cover the frequencies you are interested in (each Goertzel only measures one frequency). That gives you an output spectrum. You may interpret that spectrum how you choose; I took your question to mean, "how do you find the frequency of the LOUDEST component of the input audio?". In that case, you would search the return value of
detectFrequencies()for the largest magnitude. The corresponding member offreqsis your answer.The fact is, you probably don't want Goertzel, you want an FFT, due to FFT's superior "computational efficiency". Because Goertzel is somewhat slower (to cover a spectrum as fully as an FFT), you may have trouble getting this answer to run in real time.
As an aside, I don't think a samplerate of 10000 is supported, on Android.