
絶賛Androidの勉強中。
AudioTrackを使って動的に音を生成してます。
クリックするとランダムに音を作って鳴らしてくれます。
以前AS3で作ったものをAndroidに移植した感じです。
ちなみにIS06だとちゃんと動いたけど、HT-03Aだと音がプチプチでした。
Androidでも動的サウンド生成って出来るんですね。
知りませんでした。
以下、今回のソースです。
Main.java。
package jp.sakef;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class Main extends Activity
{
private static int SAMPLING_RATE = 44100;
private static int BUFFER = 1024;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
setContentView(new MainView(getApplicationContext()));
}
// 描画&音の生成を行うクラス
public class MainView extends SurfaceView implements SurfaceHolder.Callback, Runnable
{
private AudioTrack track;
private Paint paint;
private SurfaceHolder sholder;
private Thread thread;
private short samples[];
private ArrayList<SoundData> container;
public MainView(Context context)
{
super(context);
sholder = null;
thread = null;
paint = new Paint();
paint.setAntiAlias(true);
samples = new short[BUFFER];
container = new ArrayList<SoundData>();
int minBufferSize = AudioTrack.getMinBufferSize(SAMPLING_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
track = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLING_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
track.play();
getHolder().addCallback(this);
}
public void surfaceCreated(SurfaceHolder holder)
{
sholder = holder;
thread = new Thread(this);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
if(thread != null ) thread.start();
}
public void surfaceDestroyed(SurfaceHolder holder)
{
thread = null;
track.stop();
track.release();
}
public void run()
{
SoundData sData;
int sSize;
while (thread != null)
{
// 白く塗りつぶす
Canvas canvas = sholder.lockCanvas();
canvas.drawColor(Color.argb(255, 255, 255, 255));
// 音を生成&書き込み
for(int i = 0; i < BUFFER; i++ )
{
float totalWav = 0.0f;
sSize = container.size();
while(sSize > 0)
{
sData = container.get(sSize-1);
sData.position = (float) ((sData.position + sData.w)%(2*Math.PI));
float eachWav;
if(sData.position < Math.PI) eachWav = (float) (1 - 2*sData.position/Math.PI);
else eachWav = (float) (-1 + 2 * (sData.position - Math.PI)/Math.PI);
totalWav += eachWav * 0.4 * sData.volume;
sSize--;
}
// データを範囲内に丸める
totalWav = (totalWav > -0.9)?(totalWav):(-0.9f);
totalWav = (totalWav < 0.9)?(totalWav):(0.9f);
samples[i] = (short) (Short.MAX_VALUE * totalWav);
}
track.write(samples, 0, samples.length);
// 音データの更新&円の描画
sSize = container.size();
while(sSize > 0)
{
sData = container.get(sSize-1);
paint.setColor(Color.argb((int)(255*sData.volume), sData.cr, 255, sData.cb));
canvas.drawCircle(sData.x, sData.y, sData.radius, paint);
sData.render();
if(sData.state == 3) container.remove(sData);
sSize--;
}
sholder.unlockCanvasAndPost(canvas);
}
}
// クリック時のイベント
public boolean onTouchEvent(MotionEvent event)
{
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
float frequency = (float) (440 * Math.pow(2, ((1.0 / 12.0) * 20 * Math.random())));
container.add(new SoundData(frequency,0.4f, 0.01f, 2, 0.8f, event.getX(), event.getY()));
}
return true;
}
}
// 音データを保持するクラス
public class SoundData
{
public float frequency;
public float volume;
public float position;
public float x;
public float y;
public float w;
public int state;
public int cr;
public int cb;
public int radius;
private int count;
private float fadeIn;
private float fadeOut;
private float show;
private float volMax;
// コンストラクタ
public SoundData(float frequency, float fadeIn, float fadeOut, int show, float volMax, float x, float y)
{
this.frequency = frequency;
this.fadeIn = fadeIn;
this.fadeOut = fadeOut;
this.show = show;
this.volMax = volMax;
this.x = x;
this.y = y;
position = 0;
volume = state = count = radius = 0;
w = (float) ((2*Math.PI * frequency) / SAMPLING_RATE);
cr = (int)(255 * Math.random());
cb = (int)(255 * Math.random());
}
// 更新用
public void render()
{
radius +=2;
switch(state)
{
// 音をフェードインさせる
case 0:
volume += fadeIn;
if(volume>=volMax)
{
volume = volMax;
state = 1;
}
break;
// 音を流す
case 1:
count ++;
if(count == show) state = 2;
break;
// 音をフェードアウトさせる
case 2:
volume -= fadeOut;
if(volume <= 0)
{
state = 3;
volume = 0;
}
break;
}
}
}
}