ほりすのぶろぶろぶろぐ

ほりすのぶろぶろぶろぐ

非情報系から最高のエンジニアを目指す旧帝大学生

音を検知する【Android Studio】

はじめに

 初めて開発中のオリジナルアプリで端末のマイクから音を検知し、さらに音量を取得する必要があったので、そのやり方を調べました。
自分の記憶を定着させるためのメモ程度に書きますのであしからず。
 

パーミッション

音量の感知にはRECORD_AUDIOのパーミッションが必要。
AndroidManifest.xml

<uses-permission android:name="android.permission.RECORD_AUDIO"/>

を追加。
API16以降ではpermissionを記述しただけでは適用されず、端末の設定からマイクのpermissionをONにしなければなりません。
その他にもアプリ起動時にpermissionを求める記述もありますが、簡単のため今回はこの方法でやりたいと思います。
f:id:kurutabrog:20190401171823p:plain

音の検知

AudioRecordクラスを使用することで、マイクからの入力を波形データとして取得可能とのこと。
リアルタイムで音声を扱うには、UIスレッドで行うのではなく、別のスレッドを立てるらしいです。
Androidの公式リファレンスより、AudioRecordクラスのコンストラクタは

public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

とされていますが、何が何だかわからない。これはこういうものだ、とおまじないのように記述するのでも良いがせっかくなのでなんとなくでも意味を理解したい。
1つ目の引数は録音元。MediaRecorder#AudioSourceを充てる。今回はMicrophone audio surceであるAudioSource#MICを使用する。
2つ目の引数はサンプルレート。定数SAMPLE_RATEに予め8000(80kHz)を代入しておく。
3つ目の引数はオーディオチャンネルの設定。AudioFormat#CHANNEL_IN_MONOと、AudioFormat#CHANNEL_IN_STEREOがあるが、前者だとすべてのデバイスで動作することが保証されているため、ここではおとなしく前者を適用。
4つ目の引数はオーディオデータが返されるフォーマット。AudioFormat#ENCORDING_PCM_8BIT、AudioFormat#ENCORDING_PCM_16BIT、AudioFormat#ENCORDING_PCM_FLOATがある。16BITを選択。
5つ目の引数は録音中にオーディオデータが書き込まれるバッファの合計サイズ。AudioRecord#getBufferSizeで、AudioRecordのインスタンスを作成するための必要最低限のバッファサイズを決定する。

流れ

①AudioRecordのインスタンス化。
②AudioRecord#startRecordingで録音の開始。
③AudioRecord#readで端末からオーディオデータを読み込んでshort or float or byteの配列に記録する。
④AudioRecord#stopで録音停止。
⑤AudioRecord#releaseでAudioRecordリソースを開放。これ重要。

MainActivity.javaでの実装

アプリ内で複数のアクティビティがある場合は、アクティビティの切り替えに応じて録音を開始したり止めたりを考えます。
これについては、Androidのライフサイクルにおいて、他のアクティビティから戻ってきた際に呼ばれるonResumeメソッドと、他のアクティビティに移る際に呼ばれるonPauseメソッドを活用します。


onResume()内では、音を感知するクラスをインスタンス化し、Handler#post()で処理内容を書きます。
そして、Threadを立てて録音開始。
音を感知するためのSoundDetectionクラスと、OnVolumeListenerは自分で作成します。ここでは省略。

    //他のアクティビティから帰ってきたときに呼ばれる
    @Override
    protected void onResume() {
        super.onResume();
        soundDetection = new SoundDetection();
        soundDetection.setOnVolumeListener(new SoundDetection.OnVolumeListener(){
            //音を感知したら呼び出される
            public void onReachedVolume(short volume){
                //別スレッドからUIスレッドに処理を投げる
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                      //なにかしらの処理
                    }
                });
            }
        });
        //別のスレッドとして録音開始
        new Thread(soundDetection).start();
    }

onPause()内では、録音を止めるために自作クラスのSoundDetectionクラスで作成したstop()メソッドを呼び出します。

    //他のアクティビティに移ったときに呼ばれる
    @Override
    protected void onPause() {
        super.onPause();
        //録音停止
        soundDetection.stop();
    }

今日はここまで。