私はこれについて一日中頭を悩ませていて、今はアイデアがなくなったので、ここに投稿しています
NLMS アルゴリズムを使用して Android にフィードバック サプレッサーを実装しようとしています
。AudioRecord を使用して MIC からオーディオ サンプルを取得し、AudioTrack を使用して再生します。一度に 1 つのサンプルを short 変数として読み込み、それを double に変換し、それをアルゴリズムに渡し、再び short に変換してスピーカーに送信します。ただし、中間変数で奇妙な値が得られ、なぜそれが起こっているのか理解できません。コードは次のとおりです。
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;
コードを短くするために、NLMS を実行するスレッドを含むコードの部分を含めました。SIZE_OF_RECORD_ARRAY
is は 1 であるため、変数lin
innum = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
は常にshort
1 つの要素だけを持つ配列になります。(実際、上記のコードで遭遇する可能性のある anu double または short 配列は、単一の要素を持つだけです)。ここではラインe = d - y
が重要です。d は、マイクから取得した値です (d は double に型キャストされた短い値です)。無限ループを実行するたびにwhile (keepThreadRunning) {
、e の新しい値を計算する必要があります。これは後で short に型キャストされ、AudioTrack に渡されます。問題はe
、私の場合、適切な値が得られず、スピーカーからの出力がないことです。変数を送信するとd
AudioTrack に入力すると、マイクに入力されたものが出力に表示されます (わずかな遅延がありますが、これは想定内です)。e
変数値をファイルに書き込もうとすると、奇妙な値が得られます。を使用してString.valueOf(e)
もうまくいかなかったので (各文字列文字は 16 ビット文字と見なされるため、 のよう0
に表示され<space>0
、 のように-1
表示されます<space>-<space>1
)、RandomAccessFile.write Double を使用して double 値をファイルに直接書き込み、16 進ビューアーでファイルを表示しました。 . ファイルの先頭にランダムな値があるようで、その後パターンが出現しますが、なぜそこにあるのかわかりません。hex ファイルのスクリーンショットを以下に示します。
示されているパターンは、ファイルの終わりまで続きます。なぜこうなった?出力がなかったので、少なくともすべてが 0 になるはずだと思っていましたが、この図からわかるように、0 ではなく、7F F8 00 00 00 00 00 00
何度も繰り返されています。なぜこれが起こっているのかを判断するのを手伝ってください。
PS: ブール値isPlaying
とapplyDsp
は、インターフェイスにある 2 つのボタンの状態を表します。インターフェイスのボタンを押すと、スピーカーは非常に短く比較的大きな「ポップ」音を出します。これが、上記applyDsp
の値を含むファイルの先頭にあるランダムな値の理由であると思いますが、e
これについてはよくわかりませんが、そもそもなぜそのポップノイズが発生するのかわかりません。