Calculate Frequency from sound input using FFT - android

My app. is displaying the peak frequency of input sound in RPM .. i have array of doubles contains the samples in time domain.
audioRecord.read(buffer, 0, 1024);
Then i did FFT on it .
transformer.ft(toTransform);
using this class Here
then i got the max magnitude of complex values which are the results of FFT
// block size = 1024
double magnitude[] = new double[blockSize / 2];
for (int i = 0; i < magnitude.length; i++) {
double R = toTransform[2 * i] * toTransform[2 * i];
double I = toTransform[2 * i + 1] * toTransform[2 * i * 1];
magnitude[i] = Math.sqrt(I + 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;
}
}
Now i got the index of the max magnitude ...
1 - How can i get the Peak Frequency in details pls ?
2 - Is there any ready function called ComputeFrequency() or getFrequency() ?
Thanks in advance :)

The frequency corresponding to a given FFT bin index is given by:
f = i * Fs / N;
where:
Fs = sample rate (Hz)
N = FFT size
i = bin index
So for your peak index maxIndex and FFT size blockSize the frequency of the peak will be:
f = maxIndex * Fs / blockSize;
See this answer for more details.

Related

How to change audio pitch?

I'm using the Oboe C++ library for playing sounds in my android application.
I want to change the pitch of my audio samples.
So, I started creating "mPos" float value to hold the current played frame and adding the "mPitch" value every step.
It seems like the audio played correctly with the new Pitch but it's double it's self when the pitch is high(e.g 1.2) and make a weird noise and when the pitch is low (e.g 0.212).
This is my first-time audio programming,
I did a lot of research before I post this question. I even send messages directly to "Oboe" supports but no response.
Does anyone have any idea how to implement the Pitch correctly?
streamLength always 192
channelCount always 2
Code:
void Player::renderAudio(float *stream, int32_t streamLength){
const int32_t channelCount = mSound->getChannelCount();
if (mIsPlaying){
float framesToRenderFromData = streamLength ;
float totalSourceFrames = mSound->getTotalFrames()/mPitch;
const float *data = mSound->getData();
// Check whether we're about to reach the end of the recording
if (mPos + streamLength >= totalSourceFrames ){
framesToRenderFromData = (totalSourceFrames - mPos);
mIsPlaying = false;
}
for (int i = 0; i < framesToRenderFromData; ++i) {
for (int j = 0; j < channelCount; ++j) {
if(j % 2 == 0){
stream[(i*channelCount)+j] = (data[((size_t)mPos * channelCount)) + j] * mLeftVol) * mVol;
}else{
stream[(i*channelCount)+j] = (data[((size_t)mPos * channelCount)) + j] * mRightVol) * mVol;
}
}
mPos += mPitch;
if(mPos >= totalSourceFrames){
mPos = 0;
}
}
if (framesToRenderFromData < streamLength){
renderSilence(&stream[(size_t)framesToRenderFromData], streamLength * channelCount);
}
} else {
renderSilence(stream, streamLength * channelCount);
}
}
void Player::renderSilence(float *start, int32_t numSamples){
for (int i = 0; i < numSamples; ++i) {
start[i] = 0;
}
}
void Player::setPitch(float pitchData){
mPitch = pitchData;
};
When you multiply a float variable (mPos) by an integer-type variable (channelCount), the result is a float. You are, at the least, messing up your channel interleaving. Instead of
(size_t)(mPos * channelCount)
try
((size_t)mPos) * channelCount
EDIT:
You are intentionally looping the source when reaching the end, with the if statement that results in mPos = 0;. Instead of doing this, you could calculate the number of source samples independently of the pitch, but break out of the loop when your source samples are exhausted. Also, your comparison of the source and destination samples isn't useful because of the pitch adjustment:
float framesToRenderFromData = streamLength ;
float totalSourceFrames = mSound->getTotalFrames(); // Note change here
const float *data = mSound->getData();
// Note: Only check here is whether mPos has reached the end from
// a previous call
if ( mPos >= totalSourceFrames ) {
framesToRenderFromData = 0.0f;
}
for (int i = 0; i < framesToRenderFromData; ++i) {
for (int j = 0; j < channelCount; ++j) {
if(j % 2 == 0){
stream[(i*channelCount)+j] = (data[((size_t)mPos * channelCount)) + j] * mLeftVol) * mVol;
}else{
stream[(i*channelCount)+j] = (data[((size_t)mPos * channelCount)) + j] * mRightVol) * mVol;
}
}
mPos += mPitch;
if ( ((size_t)mPos) >= totalSourceFrames ) { // Replace this 'if' and its contents
framesToRenderFromData = (size_t)mPos;
mPos = 0.0f;
break;
}
}
A note, however, for completeness: You really shouldn't be accomplishing pitch change in this way for any serious application -- the sound quality will be terrible. There are free libraries for audio resampling to an arbitrary target rate; these will convert your source sample to a higher or lower number of samples, and provide quality pitch changes when replayed at the same rate as the source.

Get amplitude from MediaPlayer using Visualizer

i've been reading another posts about calculate the amplitude in real time from a Mediaplayer, but i have no clear how to get a value useful for me. What i need is a linear amplitude value normalize between 0-100, but as i've watched in another posts they are performing a db calculation which has not much sense, cause they are not normalized to max 0dB value (from How to calculate the audio amplitude in real time (android)):
double amplitude = 0;
for (int i = 0; i < audioData.length/2; i++) {
double y = (audioData[i*2] | audioData[i*2+1] << 8) / 32768.0
// depending on your endianness:
// double y = (audioData[i*2]<<8 | audioData[i*2+1]) / 32768.0
amplitude += Math.abs(y);
}
amplitude = amplitude / audioData.length / 2;
I've watched that for calculate de dB, i should do as below (from How to compute decibel (dB) of Amplitude from Media Player?
)
double sum=0;
for (int i = 0; i < audioData.length/2; i++) {
double y = (audioData[i*2] | audioData[i*2+1] << 8) / 32768.0;
sum += y * y;
}
double rms = Math.sqrt(sum / audioData.length/2);
dbAmp = 20.0*Math.log10(rms);
I've tried for that solution but the real time values are near to 0 but sometimes are over than 0, i mean, something between -Inifinit (no sound) to 1.2 (if i avoid 20.0* multiply) or anything else from than order. Anyway, i'd like to obtain a normalized value [0-100], not a dB value.

FFT implementation produces a glitch

I'm getting a strange glitch in a FFT graph for white noise:
I've checked with reference program and while noise file seems to be fine.
Is it a bug in implementation?
void four1(float data[], int nn, int isign) {
int n, mmax, m, j, istep, i;
float wtemp, wr, wpr, wpi, wi, theta;
float tempr, tempi;
n = nn << 1;
j = 1;
for (int i = 1; i < n; i += 2) {
if (j > i) {
tempr = data[j];
data[j] = data[i];
data[i] = tempr;
tempr = data[j + 1];
data[j + 1] = data[i + 1];
data[i + 1] = tempr;
}
m = n >> 1;
while (m >= 2 && j > m) {
j -= m;
m >>= 1;
}
j += m;
}
mmax = 2;
while (n > mmax) {
istep = 2 * mmax;
theta = TWOPI / (isign * mmax);
wtemp = sin(0.5 * theta);
wpr = -2.0 * wtemp * wtemp;
wpi = sin(theta);
wr = 1.0;
wi = 0.0;
for (m = 1; m < mmax; m += 2) {
for (i = m; i <= n; i += istep) {
j = i + mmax;
tempr = wr * data[j] - wi * data[j + 1];
tempi = wr * data[j + 1] + wi * data[j];
data[j] = data[i] - tempr;
data[j + 1] = data[i + 1] - tempi;
data[i] += tempr;
data[i + 1] += tempi;
}
wr = (wtemp = wr) * wpr - wi * wpi + wr;
wi = wi * wpr + wtemp * wpi + wi;
}
mmax = istep;
}
}
Apart from a few minor changes, this code appears to be taken out of the 2nd edition of Numerical Recipes in C. The documentation for this function (taken from the book) states:
Replaces data[1..2*nn] by its discrete Fourier transform, if isign is input as 1; or replaces data[1..2*nn] by nn times its inverse discrete Fourier transform, if isign is input as −1.
data is a complex array of length nn or, equivalently, a real array of length 2*nn. nn MUST be an integer power of 2 (this is not checked for!).
This implementation yields correct results, given an input array with 1-based indexing. You can choose to use the same indexing convention by allocating a C array of size 2*nn+1 and filling your array starting at index 1. Alternatively you could pass an array of size 2*nn which has been fill starting at index 0, but calling four1(data-1, nn, isign) (notice the -1 offset on the data array).

Implementation of FFT array

I have a problem when implementing a FFT algorithm in Android.
Let´s say that I have a wav file of 8.000 bytes length.
I am aware that you have to select a size of the FFT algorithm (and also has to be a power of 2). My problem is that I am not really sure about how to further proceed from now on.
Lets say that I have chosen a size of the FFT of N=1024.
I have basically to options on my mind:
1) Apply the FFT algorithm directly to the whole array of 8.000 bytes
2) Divide the 8000 byte array wav file in chunks of 1024 bytes (and fill with 0´s the last chunk untill having 8 exact chunks),
then apply the fft to each of this chunks and finally collate all the different chunks again to have one single byte array to represent.
8000*2*1 sec = 8192
I think it´s the option 2 but I am not completely sure.
Here is the fft array thaT I am using:
package com.example.acoustics;
public class FFT {
int n, m;
// Lookup tables. Only need to recompute when size of FFT changes.
double[] cos;
double[] sin;
public FFT(int n) {
this.n = n;
this.m = (int) (Math.log(n) / Math.log(2));
// Make sure n is a power of 2
if (n != (1 << m))
throw new RuntimeException("FFT length must be power of 2");
// precompute tables
cos = new double[n / 2];
sin = new double[n / 2];
for (int i = 0; i < n / 2; i++) {
cos[i] = Math.cos(-2 * Math.PI * i / n);
sin[i] = Math.sin(-2 * Math.PI * i / n);
}
}
/***************************************************************
* fft.c
* Douglas L. Jones
* University of Illinois at Urbana-Champaign
* January 19, 1992
* http://cnx.rice.edu/content/m12016/latest/
*
* fft: in-place radix-2 DIT DFT of a complex input
*
* input:
* n: length of FFT: must be a power of two
* m: n = 2**m
* input/output
* x: double array of length n with real part of data
* y: double array of length n with imag part of data
*
* Permission to copy and use this program is granted
* as long as this header is included.
****************************************************************/
public void fft(double[] x, double[] y) {
int i, j, k, n1, n2, a;
double c, s, t1, t2;
// Bit-reverse
j = 0;
n2 = n / 2;
for (i = 1; i < n - 1; i++) {
n1 = n2;
while (j >= n1) {
j = j - n1;
n1 = n1 / 2;
}
j = j + n1;
if (i < j) {
t1 = x[i];
x[i] = x[j];
x[j] = t1;
t1 = y[i];
y[i] = y[j];
y[j] = t1;
}
}
// FFT
n1 = 0;
n2 = 1;
for (i = 0; i < m; i++) {
n1 = n2;
n2 = n2 + n2;
a = 0;
for (j = 0; j < n1; j++) {
c = cos[a];
s = sin[a];
a += 1 << (m - i - 1);
for (k = j; k < n; k = k + n2) {
t1 = c * x[k + n1] - s * y[k + n1];
t2 = s * x[k + n1] + c * y[k + n1];
x[k + n1] = x[k] - t1;
y[k + n1] = y[k] - t2;
x[k] = x[k] + t1;
y[k] = y[k] + t2;
}
}
}
}
}
I think that you can use the entire array with the FFT. There is not problem with that, you can use 2^13 = 8192 and complete the array with zeros, this processing is also called zero padding and is used in more than one implementation of the FFT. If your procedure works well there is not problem with run the entire array, but if you use section of size 1024 for compute the FFT, then you will have a segmented Fourier transform that not describe well the entire spectrum of the signal, because the FFT use all the positions in the array to compute each value in the new transformed array, then you not get the correct answer in the position one for example if you don't use the entire array of the signal.
This is my analysis of your question I am not hundred percent sure but my knowledge about Fourier series tell me that this is almost that is going to do if you compute a segmented form of the Fourier Transform instead the entire serie.

Audio Recorder get noise level in decibal

I am trying to fetch noise level of recorded audio in decibals. I am using following code but it is not giving the correct output
byte[] audioData = new byte[bufferSize];
recorder.read(audioData, 0, bufferSize);
ByteBuffer bb = ByteBuffer.wrap(audioData);
int sampleSize = bb.getInt();
now if I log sampleSize then it gives very huge value like 956318464
Can anybody tell how to get correct noise level in decibals.
First off- decibels is a ratio. You can't just get decibels, you need to compare the volume to a baseline measurement. So the real equation in terms of amplitude is
db= 10* log10(amplitude/baseline_amplitude);
If you're recording the audio now, to get the amplitude use MediaRecorder.getMaxAmplitude. For a baseline amplitude, measure the expected background noise.
public int calculatePowerDb(short[] sdata, int off, int samples)
{
double sum = 0;
double sqsum = 0;
for (int i = 0; i < samples; i++)
{
final long v = sdata[off + i];
sum += v;
sqsum += v * v;
}
double power = (sqsum - sum * sum / samples) / samples;
power /= MAX_16_BIT * MAX_16_BIT;
double result = Math.log10(power) * 10f + FUDGE;
return (int)result;
}
private static final float MAX_16_BIT = 32768;
private static final float FUDGE = 0.6f;
its works fine this methode

Categories

Resources