LoginSignup
11

More than 5 years have passed since last update.

AndroidでAudioTrackで矩形波を鳴らす

Last updated at Posted at 2014-12-02

ActivityにはUI要素は特に用意していません。
画面をタッチしている間、440hzの矩形波を鳴らします。

public class Oscillator {
    private double frequency = 440;
    private double[] buffer;
    private double t = 0;
    private double sampleRate;

    public Oscillator(int bufferSize, int sampleRate) {
        buffer = new double[bufferSize];
        this.sampleRate = sampleRate;
    }

    /**
     * 呼び出す度に波形の一部を生成する。
     */
    public double[] nextBuffer() {
        for (int i = 0; i < buffer.length; i++) {
            // まずサイン波を生成して値が正なら1、負なら-1とすることで矩形波を生成する。
            double sin = Math.sin(2 * Math.PI * t * frequency);
            buffer[i] = sin > 0 ? 1 : -1;

            t += 1 / sampleRate;
        }
        return buffer;
    }

    /**
     * 生成波形の位相を初期状態に戻す。
     */
    public void reset() {
        t = 0;
    }
}
public class MainActivity extends Activity {
    private static final int STREAM_TYPE = AudioManager.STREAM_MUSIC;
    private static final int SAMPLE_RATE = 44100;
    private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_MONO;
    private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
    private static final int BUFFER_SIZE = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT);
    private static final int MODE = AudioTrack.MODE_STREAM;
    private Oscillator osc = new Oscillator(BUFFER_SIZE, SAMPLE_RATE);
    private AudioTrack track;
    private Thread backgroundThread;
    private boolean running;
    private boolean touching;

    @Override
    protected void onResume() {
        super.onResume();
        track = new AudioTrack(STREAM_TYPE, SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE, MODE);
        startBackgroundThread();
    }

    @Override
    protected void onPause() {
        stopBackgroundThread();
        track.release();
        track = null;
        super.onPause();
    }

    private void startBackgroundThread() {
        running = true;
        track.play();
        osc.reset();
        backgroundThread = new Thread() {
            @Override
            public void run() {
                short[] sBuffer = new short[BUFFER_SIZE];
                while (running) {
                    // 画面を触っている間のみ音を鳴らす
                    if (touching) {
                        double[] dBuffer = osc.nextBuffer();

                        // bufferには -1 〜 +1 のデータが入るので、shortの値域に変換する
                        for (int i = 0; i < BUFFER_SIZE; i++) {
                            sBuffer[i] = (short) (dBuffer[i] * Short.MAX_VALUE);
                        }
                    } else {
                        // 無音
                        for (int i = 0; i < BUFFER_SIZE; i++) {
                            sBuffer[i] = 0;
                        }
                    }
                    track.write(sBuffer, 0, BUFFER_SIZE);
                }
            }
        };
        backgroundThread.start();
    }


    private void stopBackgroundThread() {
        running = false;
        track.stop();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touching = true;
                break;
            case MotionEvent.ACTION_UP:
                touching = false;
                osc.reset();
                break;
        }
        return super.onTouchEvent(event);
    }
}

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11