I have been scratching my head about this all day and have run out of ideas now, so I am posting this here
I am trying to implement a feedback suppressor in Android using the NLMS algorithm
I use an AudioRecord to obtain audio samples from the MIC and AudioTrack to play it back. I read in 1 sample at a time as a short variable, convert it to double, pass it through my algorithm, convert it back to short and send it to the speaker. However, I am getting weird values in the intermediate variable and canot understand why it is happening. Here is the code:
public class MainActivity extends Activity {
AudioManager am = null;
AudioRecord record =null;
AudioTrack track =null;
final int SAMPLE_FREQUENCY = 16000; // ORIGINAL 44100
final int SIZE_OF_RECORD_ARRAY = 1; // 1024 ORIGINAL; 1000 / 40 = 25
// final int WAV_SAMPLE_MULTIPLICATION_FACTOR = 1;
final double WAV_SAMPLE_MULTIPLICATION_FACTOR = 0.5;
final int N = 4; // ORIGINAL 40
final int FEEDBACK_DELAY_IN_MSEC = 1; // use small integer values here to keep the calculation of NO_OF_DELAY_SAMPLES from becoming non-whole number
// final int NO_OF_DELAY_SAMPLES = SAMPLE_FREQUENCY / (FEEDBACK_DELAY_IN_MSEC * 1000); // NO_OF_DELAY_SAMPLES = 16
final int NO_OF_DELAY_SAMPLES = 0;
final int TOTAL_SIZE_OF_X = N + NO_OF_DELAY_SAMPLES;
int i = 0, n = 0; // n represents nth sample
boolean isPlaying = false; // represents if the Pass Through button is pressed
boolean applyDsp = false; // represents if the Apply Filter button is pressed
boolean bufferFull = false;
private volatile boolean keepThreadRunning;
double[] w = new double[N]; // w represents filter coefficients
double[] x = new double[TOTAL_SIZE_OF_X];
double e;
double d;
double send_out;
double mu;
double y = 0;
// /*
private RandomAccessFile stateFile;
String stateFileLoc = Environment.getExternalStorageDirectory().getPath();
FileDescriptor fd;
// */
class MyThread extends Thread{
private volatile boolean needsToPassThrough;
// /*
MyThread(){
super();
}
MyThread(boolean newPTV){
this.needsToPassThrough = newPTV;
}
// */
// /*
@Override
public void run(){
short[] lin = new short[SIZE_OF_RECORD_ARRAY];
short[] speaker = new short[SIZE_OF_RECORD_ARRAY];
double speaker_double;
int num = 0;
Log.d("MYLOG", "ENTERED RUN");
if(needsToPassThrough){
record.startRecording();
track.play();
Log.d("MYLOG", "COMES HERE BEFORE BTN PRESS?");
}
n = TOTAL_SIZE_OF_X -1;
while (keepThreadRunning) { // thread runs until this loop stops; this loop runs as long as the program is running
num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
for(i=0;i<lin.length;i++)
d = (double)lin[i]; // this line requires that lin[] has to be a single element array
if(isPlaying){
if(applyDsp){
y=0.0; // initialize every time
for(i=0; i<N; i++){
y += w[N-1-i] * x[n-i - NO_OF_DELAY_SAMPLES];
}
// Implementing step 2
e = d - y;
// /*
try {
stateFile.writeDouble(e);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Implementing step 3
mu = 0.5 / (x[n] * x[n] + 0.01);
// Implementing step 4
for(i=0; i<N; i++){
w[N-1-i] = w[N-1-i] + 2.0*mu*e*x[n-i - NO_OF_DELAY_SAMPLES];
}
} // closing of if(applyDsp) block
// Implementing step 5
for(i=0;i<TOTAL_SIZE_OF_X-1;i++){
x[i] = x[i+1];
}
send_out = e;
speaker_double = send_out * WAV_SAMPLE_MULTIPLICATION_FACTOR;
// implementing step 6
x[TOTAL_SIZE_OF_X -1] = speaker_double;
for(i=0;i<speaker.length; i++)
speaker[i] = (short)speaker_double;
track.write(speaker, 0, num);
} // if(isPlaying) block closed; this represents if the "Pass Through" button has been clicked
} // while (keepThreadRunning) closes here; this infinite loop runs as long as the program is running
record.stop();
track.stop();
record.release();
track.release();
}
public void stopThread(){
keepThreadRunning = false;
}
} // End of MyThread class
MyThread newThread;
I have just included that part of the code that contains the Thread which performs the NLMS to keep it short. Since SIZE_OF_RECORD_ARRAY
is 1, the lin
variable in num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
will always be a short
array with just one element. (in fact, anu double or short array that may be encountered in the above code will just have a single element). the line e = d - y
is key here. d is the value I get from the mic (d is short typecasted to double). During every run of the while (keepThreadRunning) {
infinite loop, a new value for e should be calculated, which is later typecasted to short and passed to the AudioTrack. The problem is that e
is not getting proper values in my case, and there is no output from the speakers. If I send the variable d
to the AudioTrack, whatever input is spoken in to the microphone appears at the output (with a slight delay, but that is expected). If I try to write the e
variable values to file, I am getting strange values. Since using String.valueOf(e)
did not work out for me (each string character is considered as a 16-bit character and so 0
appears as <space>0
, -1
appears as <space>-<space>1
), I directly wrote the double value to file using RandomAccessFile.write Double and viewed the file in a hex viewer. It seems that there is some random value at the beginning of the file, after which a pattern emerges, which I am not sure why it is there. A screenshot of the hex file is shown below:
The pattern shown continues till the end of file. Why is this happening? Since there was no output I assumed at least everything should have been 0, but as can be seen from this figure it's not 0 either, instead it is 7F F8 00 00 00 00 00 00
repeated again and again. Please help me determine why this is happening.
PS: The boolean values isPlaying
and applyDsp
represent the state of two buttons that I have on the interface. When I press the applyDsp
button on the interface, the speaker makes a very short and relatively loud "pop" sound, which I assume may be the reason for the random values in the beginning of the file containing the values of e
shown above, but I am not sure about this, and I am not sure abput why that popping noise is there in the first place.