School of Musical Notes
音符の群れ。
クリックで音符を追加。
音符が線を通り過ぎたとき、その線に対応する高さの音が鳴る。
音符は全音符、二分音符、四分音符、八分音符の四種類。
/**
* Copyright naraba ( http://wonderfl.net/user/naraba )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/gTZZ
*/
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import org.si.sion.*;
import org.si.sion.utils.SiONPresetVoice;
public class SoundBoid extends MovieClip
{
private const DIST_OF_VIEW:int = 50;
private const DIST_OF_AT:int = 30;
private const MAX_VELOCITY:int = 4;
private const TADPOLE_HEAD:int = 5;
private const TADPOLE_TAIL:int = 30;
private const TADPOLE_FLG1:int = 5;
private const TADPOLE_ARG1:Number = -Math.PI / 4;
private const TADPOLE_FLG2:int = 2;
private const TADPOLE_ARG2:Number = TADPOLE_ARG1 - Math.PI / 30;
private const TADPOLE_VALUES:Array = [1, 2, 4, 8];
private const UNIT_VALUE:int = 8;
private const BASE_PITCH:int = 48;
private const LINES_PITCH:Array = [10, 7, 5, 3, 0];
private var school:Array;
private var lines:Array;
private var driver:SiONDriver;
private var voice:SiONVoice;
public function SoundBoid()
{
driver = new SiONDriver();
var preset:SiONPresetVoice = new SiONPresetVoice();
voice = preset["valsound.guitar1"];
driver.play();
init();
addEventListener(Event.ENTER_FRAME, update);
stage.addEventListener(MouseEvent.MOUSE_UP, add);
}
private function init():void
{
school = new Array();
lines = new Array();
for (var j:int = 1; j <= LINES_PITCH.length; j++) {
lines.push(stage.stageHeight / LINES_PITCH.length * (j - 0.5));
}
}
private function add(e:MouseEvent):void
{
var t:Tadpole = new Tadpole();
t.px = t.x = e.stageX;
t.py = t.y = e.stageY;
t.vx = Math.sqrt(MAX_VELOCITY) * (Math.random() * 2 - 1);
t.vy = Math.sqrt(MAX_VELOCITY) * (Math.random() * 2 - 1);
t.nv = TADPOLE_VALUES[Math.floor(Math.random() * TADPOLE_VALUES.length)];
t.hit = 0x000000;
school.push(t);
}
private function update(e:Event):void
{
var tmpn:Number;
var tmpx:Number;
var tmpy:Number;
var o:Tadpole;
for each (var t:Tadpole in school) {
// separation
var svx:Number = 0;
var svy:Number = 0;
for each (o in school) {
if (t != o && distance(t.x, t.y, o.x, o.y) < DIST_OF_AT) {
svx -= (o.x - t.x);
svy -= (o.y - t.y);
}
}
// alignment
var avx:Number = 0;
var avy:Number = 0;
tmpx = 0;
tmpy = 0;
tmpn = 0;
for each (o in school) {
if (t != o && distance(t.x, t.y, o.x, o.y) < DIST_OF_VIEW) {
tmpx += o.vx;
tmpy += o.vy;
tmpn++;
}
}
if (tmpn > 0) {
cvx = tmpx / tmpn - t.x;
cvy = tmpy / tmpn - t.y;
}
// cohesion
var cvx:Number = 0;
var cvy:Number = 0;
tmpx = 0;
tmpy = 0;
tmpn = 0;
for each (o in school) {
if (t != o && distance(t.x, t.y, o.x, o.y) < DIST_OF_VIEW) {
tmpx += o.x;
tmpy += o.y;
tmpn++;
}
}
if (tmpn > 0) {
cvx = tmpx / tmpn - t.x;
cvy = tmpy / tmpn - t.y;
}
// update and play
tmpx = t.vx + svx / 100 + avx + cvx / 100;
tmpy = t.vy + svy / 100 + avy + cvy / 100;
var v:Number = distance(tmpx, tmpy, 0, 0);
if (v > MAX_VELOCITY) {
tmpx = (tmpx / v) * MAX_VELOCITY;
tmpy = (tmpy / v) * MAX_VELOCITY;
}
t.vx = tmpx;
t.vy = tmpy;
t.px = t.x;
t.py = t.y;
t.x += t.vx;
t.y += t.vy;
if (t.x < TADPOLE_HEAD && t.vx < 0) t.vx = -t.vx;
if (t.x > stage.stageWidth - TADPOLE_HEAD && t.vx > 0) t.vx = -t.vx;
if (t.y < TADPOLE_HEAD && t.vy < 0) t.vy = -t.vy;
if (t.y > stage.stageHeight - TADPOLE_HEAD && t.vy > 0) t.vy = -t.vy;
playNote(t);
}
draw();
}
private function distance(x1:Number, y1:Number, x2:Number, y2:Number):Number
{
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}
private function doNoteOn(py:Number, cy:Number, ly:Number):Boolean {
return (py > ly && ly > cy) || (py < ly && ly < cy);
}
private function playNote(t:Tadpole):void
{
for (var i:int = 0; i < lines.length; i++) {
if (doNoteOn(t.py, t.y, lines[i])) {
driver.noteOn(BASE_PITCH + LINES_PITCH[i], voice, UNIT_VALUE / t.nv);
t.hit = 0xFFFF00;
}
}
}
private function draw():void
{
var arg:Number;
var fillcol:uint;
graphics.clear();
graphics.beginFill(0xFFFFFF);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
graphics.endFill();
graphics.lineStyle(2, 0x008888);
for each (var ly:int in lines) {
graphics.moveTo(0, ly);
graphics.lineTo(stage.stageWidth, ly);
}
graphics.lineStyle(2, 0x000000);
for each (var t:Tadpole in school) {
if (t.hit > 0x222200) {
graphics.beginFill(t.hit, 0.5);
t.hit -= 0x222200
} else if (t.nv == 1 || t.nv == 2) {
graphics.beginFill(0xFFFFFF, 0);
} else {
graphics.beginFill(0x000000, 1);
}
graphics.drawCircle(t.x, t.y, TADPOLE_HEAD);
graphics.endFill();
if (t.nv != 1) {
arg = Math.atan2(t.vy, t.vx);
graphics.moveTo(t.x + TADPOLE_HEAD * Math.sin(arg), t.y - TADPOLE_HEAD * Math.cos(arg));
graphics.lineTo(t.x + TADPOLE_HEAD * Math.sin(arg) - TADPOLE_TAIL * Math.cos(arg),
t.y - TADPOLE_HEAD * Math.cos(arg) - TADPOLE_TAIL * Math.sin(arg));
if (t.nv == 8) {
var f:Number = Math.sin(arg) < 0 ? 2 : 1
graphics.curveTo(t.x + TADPOLE_HEAD * TADPOLE_FLG1 * Math.sin(arg + f * TADPOLE_ARG1),
t.y - TADPOLE_HEAD * TADPOLE_FLG1 * Math.cos(arg + f * TADPOLE_ARG1),
t.x + TADPOLE_HEAD * TADPOLE_FLG2 * Math.sin(arg + f * TADPOLE_ARG2),
t.y - TADPOLE_HEAD * TADPOLE_FLG2 * Math.cos(arg + f * TADPOLE_ARG2));
}
}
}
}
}
}
class Tadpole
{
public var x:Number;
public var y:Number;
public var px:Number;
public var py:Number;
public var vx:Number;
public var vy:Number;
public var nv:Number;
public var hit:uint;
}