
第2回 超音波を出してみよう
超音波通信を行うために、超音波の送出は必要不可欠な要素です。
そこで今回は、Androidアプリで動的に音の波形を生成し、超音波の送出に挑戦してみました!
早速、実際に音の鳴っている様子をどうぞ!
18,000Hzは可聴領域外ですので動画では聞こえませんが、本当に鳴っているかどうかは次回以降の記事で解析していきますので、お楽しみに!
では、アプリの実装について詳しくみていきましょう。
アプリでの音の鳴らし方
Android SDKでは、音を鳴らすための様々なクラスが用意されています。代表的な3つを挙げてみます。
- MediaPlayer
再生時間の長い音楽ファイルを再生するのに適しています。
ストリーミング再生も可能です。
http://developer.android.com/intl/ja/reference/android/media/MediaPlayer.html
- SoundPool
効果音などの短いサイズの音を再生するのに適しています。
事前にデータの展開がなされるので、再生開始までのタイムラグが非常に短いのが特徴です。
http://developer.android.com/intl/ja/reference/android/media/SoundPool.html
- AudioTrack
配列に格納された波形データを再生するクラスです。
プログラム内で音の波形を生成して鳴らすことが可能です。
http://developer.android.com/intl/ja/reference/android/media/AudioTrack.html
今回はアプリ内で動的に音の波形を生成するため、AudioTrackを使用します。
アプリ作成のポイント
いよいよアプリを作っていきます! アプリ作成におけるポイントは以下のとおりです。
- アプリの入力欄から「音の周波数」「再生時間」を受け取り、音の波形生成に必要なパラメータを計算
- 音の波形データをPCM 8bit エンコーディング形式の配列として生成
- AudioTrackというクラスに波形データの配列を渡し、再生
それでは、各項目を詳しく説明していきます。
音の波形生成に必要なパラメータを計算
音の波形を生成するにあたり、必要はパラメータは以下の3つです。
- 鳴らしたい音の周波数
- 再生時間
- サンプリングレート
このうち、周波数と再生時間を引数とし、サンプリングレートと、音の波形を格納する配列のサイズを決定する関数を作ります。
今回は以下の図のように、1周期の波形を4つのデータで表現するようにします。
周波数 f (Hz) の音は1秒間に f 個の波があるので、この場合1秒間に 4f 個のデータが必要となります。すなわち、この 4f がサンプリングレートとなります。
1秒間で 4f 個のデータが必要なので、 t 秒間で必要なデータの個数は以下のように求めることができます。
s = 4ft
以下は実装例の一部となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// サンプリングレートの下限値、上限値の設定 private static final int SAMPLE_RATE_MIN = 4000; private static final int SAMPLE_RATE_MAX = 96000; // サンプリングレートの計算 private int calcSampleRate(int freq) { int sampleRate = freq * 4; if(sampleRate < SAMPLE_RATE_MIN) { sampleRate = SAMPLE_RATE_MIN; } else if(sampleRate > SAMPLE_RATE_MAX) { sampleRate = SAMPLE_RATE_MAX; } return sampleRate; } // 波形データ生成 private byte[] createWaves(int sampleRate, int time) { int dataNum = (int)((double)sampleRate * ((double)time / 1000.0)); byte[] data = new byte[dataNum]; // ここに波形データ格納の処理も入る return data; } |
音の波形を配列に格納
音の波形を格納するデータ配列も作り終えたので、矩形波になるように値を入れていくだけです!
今回、音のデータフォーマットをPCM 8bit エンコーディングとしたので、データ配列はbyte型を用います。
このとき、音の波形は128を中央値として、最大値が255、最小値が0として扱われますので、値の代入には注意が必要です!
以下は実装例の一部となります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// 波形データ生成 private byte[] createWaves(int sampleRate, int time) { int dataNum = (int)((double)sampleRate * ((double)time / 1000.0)); byte[] data = new byte[dataNum]; int flag = 0; for(int i = 0; i < dataNum; i = i + 2) { if(flag == 0) { data[i] = (byte)0xff; data[i+1] = (byte)0xff; flag++; } else { data[i] = (byte)0x00; data[i+1] = (byte)0x00; flag--; } } return data; } } |
AudioTrackを使って再生
いよいよ大詰めです! AudioTrackのコンストラクタに必要なパラメータを渡しましょう。今回は以下のような設定値にしています。
鳴らすストリームタイプ | AudoManager.STREAM_MUSIC |
サンプリングレート | 事前に計算した値 |
チャンネル設定 | AudioFormat.CHANNEL_OUT_MONO |
波形データのデータフォーマット | AudioFormat.ENCODING_PCM_8BIT |
AudioTrackの動作モード | AudioTrack.MODE_STATIC |
あとは、AudioTrack.write()で波形データを書き込み、AudioTrack.play()で再生です!
AudioTrackは再生満了時にきちんとAudioTrack.stop()をコールしてあげる必要がありますので、お忘れないよう! この実装は様々でしょうが、リスナーを付けて再生終了を監視するのがスマートかなと思います。
以下は実装例の一部です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
// play waves public void play(byte[] data, int sampleRate) { try { // AudioTrackコンストラクタ mTrack = new AudioTrack( AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_8BIT, data.length, AudioTrack.MODE_STATIC ); // 再生完了のリスナー設定 mTrack.setNotificationMarkerPosition(data.length); mTrack.setPlaybackPositionUpdateListener( new AudioTrack.OnPlaybackPositionUpdateListener() { public void onPeriodicNotification(AudioTrack track) { } public void onMarkerReached(AudioTrack track) { if (track.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { track.stop(); track.release(); track = null; } } } ); // 波形データの書き込みと再生 mTrack.reloadStaticData(); mTrack.write(data, 0, data.length); mTrack.play(); } catch (Exception e) { e.printStackTrace(); if(mTrack != null) { mTrack.stop(); mTrack.release(); } mTrack = null; } } |
まとめ
ちょっと開発者向けっぽい内容になりましたが、いかがだったでしょうか?
今回作成したアプリでは、波形は固定パターンですが、任意の周波数を任意の時間で再生することができるようになり、超音波通信の実現に一歩近づきました!
次回は超音波の受信に挑戦します!
お楽しみに!!
9bat45