入力音声のデシベルを取得する方法【Android】
マイクから入力した音声を高速フーリエ変換して音声部分のデシベルを取得する方法を紹介します。
FFT変換にはJTransformsを使用しています。
https://github.com/wendykierp/JTransforms
import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.os.Bundle; import android.app.Activity; import android.os.Handler; import android.util.Log; import android.view.Menu; import android.widget.TextView; import org.jtransforms.fft.DoubleFFT_1D; public class MainActivity extends Activity { final static int SAMPLING_RATE = 44100; final static int FFT_SIZE = 4096; AudioRecord audioRec = null; boolean bIsRecording = false; int bufSize; TextView textInfo = null; Handler mHandler; String msg = ""; double dB_baseline = Math.pow(2, 15)*FFT_SIZE*Math.sqrt(2); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textInfo = findViewById(R.id.view); mHandler = new Handler(); bufSize=AudioRecord.getMinBufferSize( SAMPLING_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); if (FFT_SIZE > bufSize) bufSize = FFT_SIZE; } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. // getMenuInflater().inflate(R.menu.main, menu); return true; } int nosound_count = 0; boolean record_mode = false; @Override protected void onResume() { super.onResume(); //AudioRecordの作成 audioRec = new AudioRecord( MediaRecorder.AudioSource.MIC, SAMPLING_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufSize *2); audioRec.startRecording(); bIsRecording = true; new Thread(new Runnable() { @Override public void run() { short buf[] = new short[bufSize]; while (bIsRecording) { audioRec.read(buf, 0, buf.length); // Log.d("audiorecord", String.valueOf(buf.length)); DoubleFFT_1D fft = new DoubleFFT_1D(FFT_SIZE) ; double[] FFTdata = new double[FFT_SIZE]; for(int i=0;i<FFT_SIZE;i++){ FFTdata[i] = (double) buf[i]; } fft.realForward(FFTdata); //dBFS計算 double[] dbfs = new double[FFT_SIZE/2]; double max_db = -120d; int max_i = 0; for(int i=0;i<FFT_SIZE;i+=2){ dbfs[i/2]=(int) (20*Math.log10( Math.sqrt(Math.pow(FFTdata[i], 2) +Math.pow(FFTdata[i+1], 2))/dB_baseline)); if(max_db<dbfs[i/2]){ max_db = dbfs[i/2]; max_i = i/2; } } msg = ""; boolean on_voice = false; int hz = (int)((SAMPLING_RATE/ (double) FFT_SIZE) * max_i); int db = (int)max_db; if (hz > 100) { if (db > -50) { if (record_mode == false) { record_mode = true; nosound_count = 0; } on_voice = true; Log.i("音声", hz + "[Hz]" + max_db + "[db]"); } } if (record_mode) { if (on_voice == false) { nosound_count++; } if (nosound_count > 10) { record_mode = false; nosound_count = 0; } } if (record_mode) { msg = "記録中..." ; } else { msg = ""; } //メインスレッドのメッセージキューにメッセージを登録します。 mHandler.post(new Runnable() { //run()の中の処理はメインスレッドで動作されます。 public void run() { textInfo.setText(msg); } }); } // 録音停止 audioRec.stop(); audioRec.release(); } }).start(); } @Override protected void onPause() { super.onPause(); if (bIsRecording) { bIsRecording = false; } } }
dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }