別の方法でテルミンを作る
2週間ぐらい開きましたが続けます。ただ今後は徐々にサウンド処理はAndroid NDKを使ってC++で行おうと思っています。最終的にはAudioTrackを叩くのですがやはり早いですね。路線変更したのはCausticの作者がサウンド処理はC++でUIはOpenGLだと言ってたからで、あのレスポンスの良さはそういうわけかと、ゲームのプログラマはつええなと。
さて簡単なテルミンもどきを作ります。Canvasに赤い丸があるので指で動かすと音が変化するというイメージです。
今までやってきたコールバックを検知するという方法ですが、これが遅い!。結局Threadを使います。こちらはいい感じです。2つを比較してみて相当な遅れがありましたので、今後はThreadのほうを採用します。いろいろ調べたのにThreadの方が早いとか怒り心頭ですよ。ただ誤解していたら教えてくださいコメントとかで。
package com.modoki.ThereminModoki; import android.app.Activity; import android.os.Bundle; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; import android.view.View; import android.content.Context; import android.graphics.*; import android.view.MotionEvent; import android.view.WindowManager; import android.view.Display; public class ThereminModoki extends Activity { public MyCanvas myview; SinOsc sinosc; short[] buf; int bf; public int pwidth; public int pheight; public Snd snd; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //ここから4行は画面のサイズを取得 WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); pwidth = display.getWidth(); pheight = display.getHeight(); //画面 myview = new MyCanvas(getApplication()); setContentView(myview); //サイン波 sinosc = new SinOsc(440, 1); snd = new Snd(); snd.setPriority(10); //スレッドの優先順位で10が1番高い snd.start(); } @Override public void onResume() { //電話を切って再び遊ぶために音を出す。 super.onResume(); sinosc.amp = 1; } @Override public void onPause() { //電話がかかってきたりした時のために音を絞る super.onPause(); sinosc.amp = 0; } @Override public void onDestroy() { //if( snd.track != null ){ snd.isRunning = false; snd.track.setStereoVolume(0, 0); if(snd.track.getPlayState()==AudioTrack.PLAYSTATE_PLAYING){ snd.track.stop(); } snd.track.flush(); snd.track.release(); //} super.onDestroy(); } class MyCanvas extends View{ public int displayWidth; public int displayHeight; public int px; public int py; public int prepy; public int size; public MyCanvas(Context context){ super(context); setFocusable(true); displayWidth = pwidth; displayHeight = pheight; px = displayWidth/2; py = displayHeight/2; size = 25; prepy = 0; } public void onDraw(Canvas canvas){ canvas.drawColor(Color.WHITE); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); paint.setColor(Color.RED); canvas.drawCircle(px, py, size, paint); } public boolean onTouchEvent(MotionEvent event){ int tempx = (int)event.getX(); int tempy = (int)event.getY(); //赤丸の内側に指があるときだけ反応する。 if(!(px-size>tempx || tempx>px+size || py-size>tempy || tempy>py+size)){ px = (int)event.getX(); py = (int)event.getY(); switch(event.getAction()){ case MotionEvent.ACTION_DOWN: size = 35; invalidate(); break; case MotionEvent.ACTION_MOVE: sinosc.freq=(int)((px/(double)displayWidth)*3000.0); sinosc.amp=(float)((py/(double)displayHeight)*1.0); size = Math.round(10)+35; invalidate(); break; case MotionEvent.ACTION_UP: size = 30; invalidate(); break; } } return true; } } class SinOsc{ public double sampleRate = 44100; public double freq; public double amp; public double phase = 0; public SinOsc(double f, double a){ freq = f; amp = a; } public double updata(){ phase += freq/sampleRate; phase = (phase > 1) ? 0 : phase; return Math.sin(2*Math.PI*phase)*amp; } } void sndOut(short data[],SinOsc input) { for (int i = 0; i < data.length; i++) { data[i] = (short)(Short.MAX_VALUE * input.updata()); } } class Snd extends Thread { public AudioTrack track ; public int buffer_size; public int chunk_size; public short[] buf; public boolean isRunning = true; public Snd() { buffer_size = android.media.AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); track = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, buffer_size, AudioTrack.MODE_STREAM); chunk_size = buffer_size/4; buf = new short[chunk_size]; } public void run() { track.play(); while (isRunning) { sndOut(buf,sinosc); track.write(buf,0,buf.length); //try { //sleep(1); //} catch (Exception localException) {} } } } }