// forked from keim_at_Si's SiON Tenorion // SiON TENORION package { import com.bit101.components.Label; import com.bit101.components.Panel; import com.bit101.components.PushButton; import com.bit101.components.Window; import flash.display.Sprite; import flash.events.*; import flash.net.navigateToURL; import flash.net.URLRequest; import flash.text.TextField; import flash.utils.escapeMultiByte; import org.si.sion.*; import org.si.sion.events.*; import org.si.sion.sequencer.SiMMLTrack; import org.si.sion.utils.SiONPresetVoice; /** * いい音ができたら友達に Twitter で知らせる機能つけた。 * デバッグプレイヤーのみ対応。 */ [SWF(width = "465", height = "465", backgroundColor = "#ffffff", frameRate = 30)] public class Tenorion extends Sprite { // driver public var driver:SiONDriver = new SiONDriver(); // preset voice public var presetVoice:SiONPresetVoice = new SiONPresetVoice(); // voices, notes and tracks public var tracks:Vector.<SiMMLTrack> = new Vector.<SiMMLTrack>(16); public var voices:Vector.<int> = Vector.<int>([ 0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]); public var notes:Vector.<int> = Vector.<int>([36,48,60,72, 43,48,55,60, 65,67,70,72, 77,79,82,84]); // beat counter public var beatCounter:int; // control pad public var matrixPad:MatrixPad; private var bit:Bit; private var label:Label; private var panel:Panel; private var cnt: uint; private var waitCnt: uint; private var sendBtn:PushButton; // constructor function Tenorion() { driver.setVoice(0, presetVoice["valsound.percus1"]); // bass drum driver.setVoice(1, presetVoice["valsound.percus28"]); // snare drum driver.setVoice(2, presetVoice["valsound.percus17"]); // close hihat driver.setVoice(3, presetVoice["valsound.percus23"]); // open hihat driver.setVoice(4, presetVoice["valsound.bass18"]); // listen click driver.setTimerInterruption(1, _onTimerInterruption); driver.setBeatCallbackInterval(1); driver.addEventListener(SiONTrackEvent.BEAT, _onBeat); driver.addEventListener(SiONEvent.STREAM_START, _onStreamStart); // control pad matrixPad = new MatrixPad(stage); matrixPad.x = 72; matrixPad.y = 72; addChild(matrixPad); // start streaming driver.play(); var btn: PushButton = new PushButton(this); btn.label = "Tweet"; btn.x = (465 - btn.width) / 2; btn.y = 465 - btn.height; btn.buttonMode = true; btn.addEventListener(MouseEvent.CLICK, onClick); bit = new Bit("http://wonderfl.net/c/hEMO"); // めんどくさくて手打ち bit.addEventListener(Event.COMPLETE, onBitComp); bit.addEventListener(IOErrorEvent.IO_ERROR, onBitIOError); panel = new Panel(this); panel.width = panel.height = 465; panel.visible = false; panel.alpha = 0.5; label = new Label(this); label.scaleX = label.scaleY = 3; label.visible = false; sendBtn = new PushButton(this); sendBtn.label = "send"; sendBtn.buttonMode = true; sendBtn.x = (465 - btn.width) / 2; sendBtn.y = (465 - btn.height) / 2; sendBtn.visible = false; sendBtn.addEventListener(MouseEvent.CLICK, onClickSend); if (WQuery.available) { var q: String = WQuery.getQuery("seq"); if (q == "") return; var qs: Array = q.split("_"); for (var i:int = 0; i < qs.length; i++) { matrixPad.add(i, parseInt(qs[i], 36)); } } } private function onBitIOError(e: IOErrorEvent ): void { label.text = "ERROR..."; waitCnt = cnt + 90; } private function onBitComp(e: Event): void { label.text = "COMPLETE!!"; waitCnt = cnt + 90; } private function onClick(e: MouseEvent ): void { var seq: Vector.<int> = matrixPad.sequences; var n: int = seq.length; var query: String = "?seq="; for (var i:int = 0; i < n; i++) { query += seq[i].toString(36); if (i < n - 1) query += "_"; } waitCnt = uint(-1); label.text = "NOW COMPRESSING..."; panel.visible = label.visible = true; matrixPad.enabled = false; bit.getURL(query); addEventListener(Event.ENTER_FRAME, loop); } private function loop(e: Event ): void { label.x = (465 - label.width * label.scaleX) / 2; label.y = (465 - label.height * label.scaleY) / 2; label.alpha = (cnt % 8); cnt ++; if (waitCnt < cnt) { removeEventListener(Event.ENTER_FRAME, loop); label.visible = false; cnt = 0; if (bit.result == "") { panel.visible = false; matrixPad.enabled = true; } else { sendBtn.visible = true; } } } private function onClickSend(e: MouseEvent ): void { sendBtn.visible = panel.visible = false; matrixPad.enabled = true; var url: String = "http://twitter.com/home/?status=" + escapeMultiByte("いい音できた。") + bit.result; navigateToURL(new URLRequest(url), "_blank"); } // _onStreamStart (SiONEvent.STREAM_START) is called back first of all after SiONDriver.play(). private function _onStreamStart(e:SiONEvent) : void { // create new controlable tracks and set voice for (var i:int=0; i<16; i++) { tracks[i] = driver.sequencer.newControlableTrack(); tracks[i].setChannelModuleType(6, 0, voices[i]); tracks[i].velocity = 64; } beatCounter = 0; } // _onBeat (SiONTrackEvent.BEAT) is called back in each beat at the sound timing. private function _onBeat(e:SiONTrackEvent) : void { matrixPad.beat(e.eventTriggerID & 15); } // _onTimerInterruption (SiONDriver.setTimerInterruption) is called back in each beat at the buffering timing. private function _onTimerInterruption() : void { var beatIndex:int = beatCounter & 15; for (var i:int=0; i<16; i++) { if (matrixPad.sequences[i] & (1<<beatIndex)) tracks[i].keyOn(notes[i]); } beatCounter++; } } } import flash.display.*; import flash.events.*; import flash.geom.*; class MatrixPad extends Bitmap { public var sequences:Vector.<int> = new Vector.<int>(16); private var canvas:Shape = new Shape(); private var buffer:BitmapData = new BitmapData(320, 320, true, 0); private var padOn:BitmapData = _pad(0x303050, 0x6060a0); private var padOff:BitmapData = _pad(0x303050, 0x202040); private var pt:Point = new Point(); private var colt:ColorTransform = new ColorTransform(1,1,1,0.1) private var _enabled: Boolean = true; function MatrixPad(stage:Stage) { super(new BitmapData(320, 320, false, 0)); var i:int; for (i=0; i<256; i++) { pt.x = (i&15)*20; pt.y = (i&240)*1.25; buffer.copyPixels(padOff, padOff.rect, pt); bitmapData.copyPixels(padOff, padOff.rect, pt); } for (i=0; i<16; i++) sequences[i] = 0; addEventListener("enterFrame", _onEnterFrame); stage.addEventListener("click", _onClick); } public function add(track: int, value: uint): void { for (var i:int = 0; i < 16; i++) { if (value >> i & 1) { pt.x = i * 20; pt.y = (15 - track) * 20; buffer.copyPixels(padOn, padOn.rect, pt); } } trace(value); sequences[track] = value; } private function _pad(border:int, face:int) : BitmapData { var pix:BitmapData = new BitmapData(20, 20, false, 0); canvas.graphics.clear(); canvas.graphics.lineStyle(1, border); canvas.graphics.beginFill(face); canvas.graphics.drawRect(1, 1, 17, 17); canvas.graphics.endFill(); pix.draw(canvas); return pix; } private function _onEnterFrame(e:Event) : void { bitmapData.draw(buffer, null, colt); } private function _onClick(e:Event) : void { if (!_enabled) return; if (mouseX>=0 && mouseX<320 && mouseY>=0 && mouseY<320) { var track:int = 15-int(mouseY*0.05), beat:int = int(mouseX*0.05); sequences[track] ^= 1<<beat; pt.x = beat*20; pt.y = (15-track)*20; if (sequences[track] & (1<<beat)) buffer.copyPixels(padOn, padOn.rect, pt); else buffer.copyPixels(padOff, padOff.rect, pt); } } public function set enabled(value: Boolean): void { _enabled = value; } public function beat(beat16th:int) : void { for (pt.x=beat16th*20, pt.y=0; pt.y<320; pt.y+=20) bitmapData.copyPixels(padOn, padOn.rect, pt); } } /** * URL 圧縮するやつ。 */ class Bit extends EventDispatcher { import flash.events.EventDispatcher; import flash.events.IOErrorEvent; import flash.events.Event; import flash.net.URLLoader; import flash.net.URLRequest; private const BASE: String = "http://api.bit.ly/shorten?version=2.0.1&apiKey=R_608e24fd300e458016ef6ea805c6ecd6&login=bkwon&format=xml&longUrl="; private var loader:URLLoader; private var _result: String = ""; private var wonderflUrl: String; public function Bit(wonderflURL: String) { wonderflUrl = wonderflURL; loader = new URLLoader(); loader.addEventListener(Event.COMPLETE, comp); loader.addEventListener(IOErrorEvent.IO_ERROR, ioerror); } public function getURL(query: String): void { var url: String = BASE + wonderflUrl + query; _result = ""; loader.load(new URLRequest(url)); } private function ioerror(e: IOErrorEvent ): void { dispatchEvent(e.clone()); } private function comp(e: Event ): void { var res: XML = new XML(loader.data); _result = res.results.nodeKeyVal.shortUrl; dispatchEvent(e.clone()); } public function get result(): String { return _result; } } /** * 無理やり URL を取得する。(デバッグプレイヤーオンリー) * http://wonderfl.net/code/854d4b5fed8c905e98aee6da935a496d79ea6213 */ class WQuery { import flash.external.ExternalInterface; import flash.system.Capabilities; private static var _qString: String = "///"; public static function get available(): Boolean { return ExternalInterface.available && Capabilities.isDebugger; } public static function getQuery(key: String): String { if (qString == "" ) return ""; var s: int = _qString.indexOf(key + "="); if (s < 0) return ""; var e: int = _qString.indexOf("&", s); if (e < 0) e = _qString.length; return _qString.substring(s + key.length + 1, e); } public static function get qString():String { if (!available) return ""; try { if (_qString == "///") _qString = ExternalInterface.call("function() { return window.location.search; }"); } catch (error: Error) { if (error.errorID == 2060) { var msg: String = error.message; msg = msg.split(" は ").pop().split(" ").shift(); if (msg.indexOf("?") < 0) _qString = ""; else _qString = msg.substr(msg.indexOf("?")); } } return _qString; } } Tweet + forked from: SiON Tenorion