AndroidでAudioTrackで矩形波を鳴らす

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

```java
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;
    }
}
```

```java
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);
    }
}
```