日記プログラミング勉強

Androidアプリ向けに、傾きとか加速度センサーを使ってテルミンっぽいの作ろっかなー、と思ってまして。
色々調べてたんだけどなかなか出来なくて、やっとなんとなく動くサンプルが作れたので嬉しくて晒すことに。

参考にしたサイトは以下。

八角研究所 : Javaでピコピコシンセを作ってみよう!(2) - 音程と音長の計算
http://metatoys.org/propella/tips/RectWave.html
http://sites.google.com/site/niusounds/programming/android/AudioTrack


八角研究所さんのサイトで、サンプルコードをもらって動かしてみたけど、なんでこれで音がなるのか、というとこの理屈が全然わからなかった。
なんでbyteの配列が矩形波になるのよ???と言うところがまるっきりわからなかった。

が、初心に帰って、分からない言葉をググる、ということをやって、やっとわかった。気がする。
サンプリングレートとビットレート、どう違う? - 日経トレンディネット
パルス符号変調 - Wikipedia
byteの配列で表現してるのは、波形を形作ってる値なのね。
矩形波の場合100,100,100・・・,-100,-100,-100・・・って形でパルスを表現する、と。
最初から矩形波ばっかり見てたからわからなかったんだ。
サンプリングレートとパルス符号変調の事から調べてたら、あぁなるほど波形を表現するのにbyte配列使うのね、って理解できてたかもしれない。


で、できたのがこのアプレット

今更アプレットかよ!!って話ですが、「矩形波を作ろう」さんのサイトにあったサンプルコードを頂いて改造してたら、eclipseだとアプレットって動作確認しやすくて都合がいいのでこれで良いや、と思ってこれにしちまいました。。。かつ、記事に貼りつけるの面倒なのでソースをそのままさらします。
OscillatorTheremin.java って名前つけて保存して、パッケージ名は適当に変えて動かしてみてください。

あ、コードの中に出てくる Oscillator は、「にュウさいと:AudioTrackを使う」にあったサンプルコードそのままです。
OscillatorTheremin.java と同じパッケージに入れておけば動くはず。全部のメソッドがstaticだから。


OscillatorTheremin.java

package org.bangucs.softs;

/**
 * OscillatorTheremin.java
 * JAVAテルミンテスト版
 *
 * $Id: Theremin0.java 880 2004-11-24 06:43:26Z propella $
 *
 * @author YAMAMIYA Takasi
 * @version 1.0
 */

import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

public class OscillatorTheremin extends Applet {

	int SAMPLE_RATE = 11250;
	int FREQUENCY_MAX = 880;
	Dimension size = new Dimension();
	String status;
	SourceDataLine line = null;
	DataLine.Info info = null;

	public void init() {
		try {
			AudioFormat audio_format = new AudioFormat(SAMPLE_RATE, 8, 1, true,
					true);
			info = new DataLine.Info(SourceDataLine.class, audio_format);

			line = (SourceDataLine) AudioSystem.getLine(info);
			line.open();
			line.start();
			int frequency = 440; // ---------------------- (*1)
			size = this.getSize();

			this.addMouseListener(new MouseAdapter() {
				public void mouseEntered(MouseEvent e) {
					// 再生を開始
					line.start();
				}

				public void mouseExited(MouseEvent e) {
					line.stop();
				}
			});

			this.addMouseMotionListener(new MouseMotionAdapter() {
				public void mouseMoved(MouseEvent e) {
					int x = e.getX();
					int y = e.getY();
					if (size.width <= 0)
						return;
					double frequency = FREQUENCY_MAX * x / size.width;
					byte[] b = Oscillator.sinWave(frequency, SAMPLE_RATE, y);
					line.write(b, 0, b.length);
					status = "freq:" + frequency;
					repaint();
				}
			});
		} catch (LineUnavailableException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

	}

	public void destroy() {
		line.flush();
	}

	public void paint(Graphics g) {
		showStatus(status);
	}
}

ちなみに、

byte[] b = Oscillator.sinWave(frequency, SAMPLE_RATE, y);
                      ^^^^^^^ 

のとこで、使うメソッドを変えると音が結構変わります。
また、このサンプルでは、音の長さを決めるときにy座標をそのまま使ってるんで(根拠レス)、これも変えると印象が変わります。

あと

	int SAMPLE_RATE = 11250;
                          ^^^^^^
	int FREQUENCY_MAX = 880;
                            ^^^^

とかの値も、変えると結構印象変わります。

このサンプルのまま動かすと、トーキングモジュレーターみたいなノイジーな音がしてたまんねーっす。
全然テルミンじゃない音です。すいません。
俺の中ではこれでも十分使える音なんだけど・・・まあ副産物ということで。


もうちょっと調整して、androidのセンサーで音程を決めるようにしたらさらに面白そう。
俺実機持ってませんが。。。