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