Get ultrasound from android using frequency
I want to receive ultrasound from any android device from the ultrasound example at 18kHz to 19kHz.
I am using the below code to calculate the frequency but it doesn't seem to give me the correct frequency. The frequency I remains between 11 kHz and 13 kHz.
private void calculateFrequency()
{
// 1 - Initialize audio
int channel_config = AudioFormat.CHANNEL_CONFIGURATION_MONO;
int format = AudioFormat.ENCODING_PCM_16BIT;
int sampleRate = 8000;
int bufferSize = 2048;
if (bufferSize < AudioRecord.getMinBufferSize(sampleRate, channel_config, format))
bufferSize = AudioRecord.getMinBufferSize(sampleRate, channel_config, format);
AudioRecord audioInput = new AudioRecord(AudioSource.MIC, sampleRate, channel_config, format, bufferSize);
// 2 - Get sound
byte[] audioBuffer = new byte[bufferSize];
audioInput.startRecording();
int nbRead = audioInput.read(audioBuffer, 0, bufferSize);
audioInput.stop();
audioInput.release();
// 3 - Transform to double array
double[] micBufferData = new double[bufferSize];
final int bytesPerSample = 2; // As it is 16bit PCM
final double amplification = 100.0; // choose a number as you like
for (int index = 0, floatIndex = 0; index < nbRead - bytesPerSample + 1; index += bytesPerSample, floatIndex++) {
double sample = 0;
for (int b = 0; b < bytesPerSample; b++) {
int v = audioBuffer[index + b];
if (b < bytesPerSample - 1 || bytesPerSample == 1) {
v &= 0xFF;
}
sample += v << (b * 8);
}
double sample32 = amplification * (sample / 32768.0);
micBufferData[floatIndex] = sample32;
}
// 4 - Create complex array
Complex[] fftTempArray = new Complex[bufferSize];
for (int i=0; i<bufferSize; i++)
{
fftTempArray[i] = new Complex(micBufferData[i], 0);
}
// 5 - Calculate FFT
Complex[] fftArray = FFT.fft(fftTempArray);
// 6 - Calculate magnitude
double[] magnitude = new double[bufferSize / 2];
for (int i = 0; i < (bufferSize / 2); i++)
{
magnitude[i] = Math.sqrt(fftArray[i*2].re() * fftArray[i*2].re() + fftArray[i*2].im() * fftArray[i*2].im());
}
// 7 - Get maximum magnitude
double max_magnitude = -1;
for (int i = 0; i < bufferSize / 2; i++)
{
if (magnitude[i] > max_magnitude)
{
max_magnitude = magnitude[i];
}
}
// 8 - Calculate frequency
int freq = (int)(max_magnitude * sampleRate / bufferSize);
((TextView) findViewById(R.id.textView1)).setText("FREQUENCY = " + freq + "Hz");
}
I am using two phones: one is sending ultrasound with this app , and the other for receiving this ultrasound. I used this question as a starting point where I took the FFT and Complex classes .
What's wrong with my code?
source to share
To get a correct estimate of the un-smoothed frequency, you need to use a sampling rate higher (10-20% higher to avoid filter dropping) than twice the highest frequency in the audio input, thus more than twice as much as you want to find.
This is due to the required Nyquist norm in the sampling theorem.
So, if you want to find a 19 kHz signal, you need a sampling rate close to 48000.
source to share
Steps 7 and 8 are not entirely correct - you need to use the highest FFT bin index to determine the frequency:
// 7 - Get maximum magnitude
double max_magnitude = -1;
int max_magnitude_index = -1;
for (int i = 0; i < bufferSize / 2; i++)
{
if (magnitude[i] > max_magnitude)
{
max_magnitude = magnitude[i];
max_magnitude_index = i;
}
}
// 8 - Calculate frequency
int freq = (int)(max_magnitude_imdex * sampleRate / bufferSize);
And as @ hotpaw2 pointed out, your sample rate is too low at 8 kHz - it should be at least 44.1 kHz, preferably 48 kHz.
source to share