Technology

Androidで波形再生

iPhone/iPadに比べて、Androidで音楽を作るソフトというのはあまり多くありません。自分もAndroidのプログラムを書いたりしていますが、Androidで事前に用意したMP3を鳴らすとかそういうのは出来ても、リアルタイムに波形を作って、それを出力する方法というのはあまり聞きません。ということでちょっと調べてみたところ、AudioTrackというオブジェクトを使うと、PCMを出力出来そうなことが分かりました。

PCMとは、アナログの波形をデジタルに変化する手法の一つで、おそらく最もナイーブな手法です。PCMは時間を細かく区切って、ある時間における波の高低を数値化することで、アナログの波形を数値の列に変換しています。CDもこの方式でアナログデータをデジタル化していて、CDの場合は1秒間を44100等分して、波の高低を216段階、すなわち65536段階のどれにあたるかを記録しています。

AndroidのAudioTrackは、byte型もしくはshort型の配列に数値を代入し、それを再生するというAPIです。例えば、配列を44100個用意して、正弦関数を使用して計算した値を代入し、その配列を1秒間鳴らせば、それは正弦波として聞くことができます。

AudioTrackのAPIについては、次のサイトが詳しいです。

とりあえずは、何かしら音声を数値化したものを用意すれば良いということです。音の波形の中で一番簡単な波は正弦波でしょう。正弦波の例を下に示してみます。

青い波が「ド」の音、赤い波が「ミ」の音、黄色い波が「ソ」の音の波形をそれぞれ示しています。そして、緑の波が3つの波の平均を取ったものです。理論的には緑の波を聞けば、人間はその音がドとミとソの音を合わせた音、つまりCメジャーコードを聞くことになります。

緑の波だけを抽出したものを下に示します。

なんだか複雑な形をしていますが、これがCメジャーコードの波形です。これは3つの正弦関数を純粋に平均しているだけなので、振幅が-1から1になっていますが、Androidでこの波形を鳴らす場合には、振幅をbyte型もしくはshort型が取り得る値の範囲にまで拡大する必要があります。実際にこれをAndroidで鳴らすとちゃんとCメジャーコードとして聞くことが出来ました。理論的には当たり前の結果なのですが、でもこういう複雑な波形をみてそれを聞くと、調和のとれた音として聞こえるのは不思議なものです。

今回作成したサンプルコードを下に掲載します。ほとんどにュウさいと様のサンプルなのですが、onResume関数やonPause関数を含め、ひと通りAndroidのプログラムの格好をしているものです。実際にはボタンを1つ配置し、それをクリックするとCメジャーコードを再生するプログラムとなっています。

package com.kuronekoya.test.at01;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class At01Activity extends Activity implements OnClickListener {
  Button btnPlay;
  AudioTrack track=null;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    btnPlay = (Button)findViewById(R.id.btnPlay);
    btnPlay.setOnClickListener(this);
  }

  @Override
  public void onResume(){
    super.onResume();
    if(track==null){
      track = new AudioTrack(
        AudioManager.STREAM_MUSIC,
        44100,
        AudioFormat.CHANNEL_CONFIGURATION_DEFAULT,
        AudioFormat.ENCODING_DEFAULT,
        44100,
        AudioTrack.MODE_STATIC);
    }

    // sine wave
    byte[] sinWave = new byte[44100];

    double freq_c3 = 261.6256;
    double freq_e3 = 329.6276;
    double freq_g3 = 391.9954;
    double t = 0.0;
    double dt = 1.0 / 44100;

    for (int i = 0; i < sinWave.length; i++, t += dt) {
      double sum = Math.sin(2.0 * Math.PI * t * freq_c3)
        + Math.sin(2.0 * Math.PI * t * freq_e3)
        + Math.sin(2.0 * Math.PI * t * freq_g3);
      sinWave[i] = (byte) (Byte.MAX_VALUE * (sum/3));
    }
    track.write(sinWave, 0, sinWave.length);
  }

  @Override
  public void onPause(){
    super.onPause();
    track.stop();
    track.release();
  }

  public void onClick(View v) {
    if(v==btnPlay){
      if(track.getPlayState()==android.media.AudioTrack.PLAYSTATE_PLAYING){
        track.stop();
        track.reloadStaticData();
      }
    track.play();
    }
  }
}

ちなみにWindowsなどで使われているWaveファイルはPCMによって変換した数値をそのままファイルにしているというシンプルな構造なので、AudioTrackで再生するのことは、特に難しいことをしなくても出来ます。


Topic