2015年8月3日月曜日

[コラム]AndroidのAudioTrackを基礎から理解する



MediaCodecクラスの登場などにより、AudioTrackクラスを使う機会が増えてきました。そこでAudioTrackクラスの使い方について、メディア用語の解説も交えて解説していきます。

■AudioTrackクラス

リニアPCMの音声データを再生するためのクラスです。MediaPlayerクラスなどで再生するよりも使用方法は複雑ですが、バッファの扱いや音声の再生位置の確認などについて細かく制御することができます。

■AudioTrackによる再生の流れ

AudioTrackクラスの使い方の前に、AudioTrackクラスを使用するとどのような流れで音声が再生されるかを説明します。

 

・ AudioTrack

音声データの制御(書き込み、再生や停止など)を行います。

・AudioManager

再生ボリュームなどをコントロールします。

・AudioSystem

AudioFlingerをコントロールします。

・AudioFlinger

音声再生の処理の中枢部分です。ミキサーやトラックの再生状況の管理、音量の管理、音声の出力先の制御などを行います。

・AudioMixer

現在再生中の複数のトラックの音を1つにミックスします。最大32トラックまで扱えます。

・再生処理の流れ

アプリケーションからAudioTrackを生成すると、AudioFlinger側にもTrackが生成されます。そして、そのTrackはAudioMixerに管理されており、複数のTrackを同時に再生する場合はAudioMixerによって1つの音声にミックスされます。音声データの書き込み、再生や停止などの制御はAudioTrackを通じて行います。音量の管理などはAudioManagerとAudioSystemを通じて行われます。

■AudioTrackのコンストラクタ

AudioTrackクラスのコンストラクタは下記のようになっています。

AudioTrack(
        int streamType,
        int sampleRateInHz,
        int channelConfig,
        int audioFormat,
        int bufferSizeInBytes,
        int mode,
        int sessionId)

それぞれの引数について解説していきます。

■第1引数StreamType

Androidの音声再生では、通話音声やアラーム音などで個別に音量やミュート設定が管理されています。今から再生に使用するAudioTrackはどのストリームに属するかを設定します。

  • STREAM_ALARM:アラーム音
  • STREAM_DTMF:電話のピポパポ音
  • STREAM_MUSIC:音楽
  • STREAM_NOTIFICATION:通知音
  • STREAM_RING:着信音
  • STREAM_SYSTEM:システム音
  • STREAM_VOICE_CALL:通話音
※AudioManagerに定義されています。

たとえば、STREAM_MUSICにするとで音楽再生で再生する音として分類されます。それぞれストリームごとのボリューム値とMute設定はAudioManagerで設定でき、その値はAudioFlingerで管理されます。

■第2引数sampleRateInHz

サンプリングレートは、1秒間あたりの音声データの記録回数のことです。たとえば、44.1kHzの場合は1秒間に44100回音声データを記録しています。この引数では、これから再生する音声データのサンプリングレートを設定します。


22.05kHzの場合は1秒間に22050回データを記録しています。44.1kHzのほうが22.05kHzに比べて精度は良いですが、記録回数が2倍になるため、データサイズも2倍になります。


記録した1回のデータを「1サンプル」や「1フレーム」と呼びます。

■第3引数channelConfig

音声データがモノラルかステレオであるかを設定します。モノラルは1チャンネル(Center)、ステレオを2チャンネル(LeftとRight)です。

・モノラル


・ステレオ

 

  • CHANNEL_OUT_MONO 1チャンネル(モノラル)
  • CHANNEL_OUT_STEREO 2チャンネル(ステレオ)
ステレオはモノラルに比べてチャンネル数が2倍であるため、データサイズも2倍になります。

■第4引数audioFormat

音声データの1サンプルあたりのデータサイズを設定します。連続的なアナログデータを不連続なデジタルデータに置き換えることを量子化といい、そのデータサイズのことを量子化ビット数といいます。

 

  • ENCODING_PCM_8BIT 8ビット
  • ENCODING_PCM_16BIT 16ビット
  • ENCODING_PCM_FLOAT 32ビット浮動小数点(※Lollipop(API Level21)以降)
 ENCODING_PCM_FLOATはLollipop(API Level21)から設定可能で、ハイレゾ音源再生時に使用されます。

16bitの場合は65536段階で、8bitの場合は256段階で音量を記録します。16bitのほうが8bitに比べて精度は良いですが、データサイズは2倍になります。

■第5引数bufferSizeInBytes / 第6引数mode

第5引数では音声再生用のバッファサイズを設定、第6引数ではStreamingモードとStaticモードの2モードを設定します。この2つのモードはバッファの扱い方がまったく違います。第5引数で設定するバッファサイズはモードによって変わります。

・Staticモード

音声データ全体をメモリに確保して再生します。メモリの使用量は大きいですが、再生までのオーバーヘッドが小さいのがメリットです。効果音の再生に向いています。


第5引数に設定するbufferSizeInByteの値は、音声のデータサイズです。

・Streamingモード

リングバッファを用いて再生します。少ないメモリで長時間の再生が可能です。通常の音楽再生やストリーミング再生などに向いています。


第5引数に設定するbufferSizeInByteの値は、getMinBufferSize()で取得できる値を設定するのが基本です。getMinBufferSize()が返す値はAudioFlingerの情報を元に決められたリングバッファの妥当な最小サイズです。

ただし、ストリーミング再生などではAndroidの負荷によって音飛び(バッファ・アンダーラン)が発生する場合があります。そのようなときはある程度バッファを拡張することで軽減できる可能性があります。

■第7引数SessionID

SessionIDはAudioTrackごとのユニークなIDで、AudioEffect(Equalizer、PresetReverbなど)という音声に特殊効果をかけるクラスとの紐付けるためなどに使います。特にSessionIDを他クラスに使うことがないのであれば、SessionIDの指定が不要なコンストラクタがありますのでそちらを使用してください。Lollipop(API Level21)からはAudioManagerのgenerateAudioSessionId()で独自に取得することができます。

AudioManager audioManager =   (AudioManager)getSystemService(Context.AUDIO_SERVICE);
int sessionID = audioManager.generateAudioSessionId(); 

■音声データのヘッダサイズを確認する

AudioTrackで音声データを再生するときに必要なのは、音声データ部分のみです。ヘッダ情報は不要です。wav形式の音声データでは、先頭にヘッダ情報が44バイト(リニアPCMの場合46バイト)あり、それを再生してしまうとノイズになります。対策としては、AudioTrackのwrite()時にヘッダサイズ分をオフセットするのが簡単です。

 mAudioTrack.write(mByteArray, 46, mByteArray.length - 46);

■音声を再生する

AudioTrackで音声を再生するサンプルコードです。
※分かりやすくするため、ハードコーディングやエラー処理の省略をしています。

private byte[] mByteArray = null;
private AudioTrack mAudioTrack = null;
private Thread mThread = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    AssetManager assetManager = getAssets();
    AssetFileDescriptor assetFileDescriptor = null;

    try {
        // assetの音声データを開く
        assetFileDescriptor = assetManager.openFd("Test.wav");
    } catch (IOException e) {
        e.printStackTrace();
        return;
    }

    // byte配列を生成し、音声データを読み込む
    mByteArray = new byte[(int)assetFileDescriptor.getLength()];
    InputStream inputStream = null;

    try {
        inputStream = assetFileDescriptor.createInputStream();
        inputStream.read(mByteArray);
        inputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
        return;
    }

    // Streamモードで再生を行うので、リングバッファサイズを取得
    int minBufferSizeInBytes = AudioTrack.getMinBufferSize(
        44100,
        AudioFormat.CHANNEL_OUT_STEREO,
        AudioFormat.ENCODING_PCM_16BIT);

    // AudioTrackを生成する
    // (44.1kHz、ステレオ、16bitの音声データ)
    mAudioTrack = new AudioTrack(
        AudioManager.STREAM_MUSIC,
        44100,
        AudioFormat.CHANNEL_OUT_STEREO,
        AudioFormat.ENCODING_PCM_16BIT,
        minBufferSizeInBytes,
        AudioTrack.MODE_STREAM);

    mThread = new Thread(this);
    mThread.start();
}

@Override
public void run() {
    // AudioTrackを再生開始状態にする 
    mAudioTrack.play();

    // 音声データを書き込む
    // ※wav形式(リニアPCM)のヘッダ情報46バイトを調整する
    mAudioTrack.write(mByteArray, 46, mByteArray.length - 46);

    // AudioTrackを停止する 
    mAudioTrack.stop();

    // AudioTrackをフラッシュする 
    mAudioTrack.flush();
}

■現在の再生位置を取得する

現在の再生位置を取得するにはgetPlaybackHeadPosition()を使用します。このAPIが返す値はフレーム数ですので、時間(ミリ秒など)が必要な場合はサンプリングレートを使って計算します。たとえば、44.1kHzの場合、1フレームは1/44100秒ですので、下記の計算で時間を求めることができます。

再生時間(ミリ秒) = フレーム数 × 1000 / 44100
※×1000は単位を秒からミリ秒に変換するため

int headPositionFrame = mAudioTrack.getPlaybackHeadPosition();
int msec = headPositionFrame * 1000 / 44100;

なお、KitKat(API Level19)からはgetTimestamp()を使うことができます。AudioTrackのgetPlaybackHeadPosition()で取得できるフレーム数はint型ですが、getTimestamp()(AudioTimestamp.framePosition)はlong型です。よって、長時間再生に対応されています。

AudioTimestamp audioTimestamp = new AudioTimestamp();
mAudioTrack.getTimestamp(audioTimestamp);
long msec = audioTimestamp.framePosition * 1000 / 44100;


140 180 Android

記載されている会社名、および商品名等は、各社の商標または登録商標です。

2 コメント:

  1. このコメントは投稿者によって削除されました。

    返信削除
  2. パラメタ解説助かりました。コンストラクタが、deprecated になっているのだけが残念でした。

    返信削除

Related Posts Plugin for WordPress, Blogger...