forked from: SiON MML Edtor 2
SiON MML Editor
[USAGE]
Write MML in the text field and Shift+Enter to play.
All MMLs in all pages are concatenated and play.
Ctrl+Z to undo, Ctrl+Y to redo (codes from psyark's Psycode)
------------------------------------------------------------
/**
* Copyright yonatan ( http://wonderfl.net/user/yonatan )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/hJAB
*/
// forked from keim_at_Si's SiON MML Edtor 2
// SiON MML Editor
// [USAGE]
// Write MML in the text field and Shift+Enter to play.
// All MMLs in all pages are concatenated and play.
// Ctrl+Z to undo, Ctrl+Y to redo (codes from psyark's Psycode)
//------------------------------------------------------------
package {
import flash.display.*;
import flash.events.*;
import flash.ui.Keyboard;
[SWF(backgroundColor="#ffffff", frameRate="20")]
public class main extends Sprite {
function main() {
stage.scaleMode = "noScale";
stage.align = "TL";
stage.stageFocusRect = false;
addEventListener("addedToStage", _onAddedToStage);
stage.addEventListener("resize", _onResize);
stage.addEventListener("keyDown", _onKeyDown);
driver.addEventListener("error", _onError);
control = new ControlPanel(this);
mmlEditor = new MMLEditor(this, 0, 0, _onTextChange);
pageSelector = new PageSelector(this);
message = new MessageBox(this);
searchWindow = new SearchWindow(this);
}
private function _onAddedToStage(event:Event) : void {
_updateSize();
loadCookie();
}
private function _onResize(event:Event) : void {
_updateSize();
}
private function _onKeyDown(event:KeyboardEvent) : void {
switch (event.keyCode) {
case Keyboard.ENTER:
if (event.shiftKey) togglePlay();
break;
}
}
private function _onTextChange(length:int) : void {
textLength = length;
control.updateTrackLabel(false);
}
private function _onError(e:ErrorEvent) : void {
message.show(e.text);
}
private function _updateSize() : void {
control.setSize(stage.stageWidth, 24);
mmlEditor.y = 24;
mmlEditor.setSize(stage.stageWidth, stage.stageHeight - 48);
pageSelector.y = stage.stageHeight - 24;
pageSelector.setSize(stage.stageWidth, 24);
}
}
}
import flash.display.*;
import flash.geom.Rectangle;
import flash.text.*;
import flash.events.*;
import flash.ui.Keyboard;
import flash.net.*;
import flash.utils.*;
import com.bit101.components.*;
import org.si.sion.*;
import org.si.sion.events.*;
import org.si.sion.utils.Translator;
import org.si.sion.sequencer.base.*;
// Global variables
//--------------------------------------------------
var cookieName:String = "savedData";
var driver:SiONDriver = new SiONDriver();
var message:MessageBox;
var mmlEditor:MMLEditor;
var control:ControlPanel;
var pageSelector:PageSelector;
var searchWindow:SearchWindow;
var pageIndex:int = 0;
var soloIndex:int = -1;
var textLength:int = 0;
// Global functions
//--------------------------------------------------
function loadCookie() : void {
var so:SharedObject = SharedObject.getLocal(cookieName);
mmlEditor.xml = so.data.mmlxml;
}
function saveCookie() : void {
var so:SharedObject = SharedObject.getLocal(cookieName);
so.data.mmlxml = mmlEditor.xml;
so.flush();
}
function togglePlay() : void {
if (driver.isPlaying) {
driver.stop();
control.cbPlay(false);
} else {
saveCookie();
driver.position = control.position * 1000;
driver.play(mmlEditor.mml);
control.cbPlay(true);
}
}
function selectPage(index:int) : void {
if (pageIndex != index) {
control.cbPageChanged(index);
mmlEditor.cbPageChanged(index);
pageSelector.cbPageChanged(index);
pageIndex = index;
}
}
function setMute(index:int, bool:Boolean) : void {
mmlEditor.cbMuteChanged(index, bool);
pageSelector.cbMuteChanged(index, bool);
control.cbPageChanged(pageIndex);
soloIndex = -1;
}
function setSolo(index:int, bool:Boolean) : void {
var i:int, mute:Boolean, imax:int = MMLEditor.FIELD_COUNT;
for (i=0; i<imax; i++) {
mute = bool && (i != index);
mmlEditor.cbMuteChanged(i, mute);
pageSelector.cbMuteChanged(i, mute);
}
soloIndex = (bool) ? index : -1;
control.cbPageChanged(pageIndex);
}
// Message box
//--------------------------------------------------
class MessageBox extends Window {
private var _text:Text, _button:PushButton;
function MessageBox(parent:DisplayObjectContainer=null, title:String="Error") {
super(parent, 100, 100, title);
setSize(200, 160);
_text = new Text(content, 0, 0, "");
_text.setSize(200, 120);
_button = new PushButton(content, 76, 122, "OK", function(e:Event):void{visible = false;});
_button.setSize(48,16);
visible = false;
}
public function show(message:String="") : void {
_text.text = message;
visible = true;
}
}
// Menu window
//--------------------------------------------------
class MenuPopup extends Panel {
function MenuPopup(parent:DisplayObjectContainer=null) {
super(parent, 263, 0);
var y:int = 4;
newButton("clear", clear);
newButton("load", load);
newButton("save as xml", saveXML);
newButton("save as mml", saveMML);
newButton("translate from TSSCP MML", transTSSCP);
newButton("show search window", showSearch);
setSize(200, y+2);
this.y = -y-4;
visible = false;
function newButton(label:String, func:Function) : PushButton {
var newButton:PushButton = new PushButton(content, 4, y, label, func);
newButton.setSize(193, 16);
y += 18;
return newButton;
}
}
public function clear(e:Event) : void {
mmlEditor.mml = "";
visible = false;
}
public function load(e:Event) : void {
var fr:FileReference = new FileReference();
fr.addEventListener("select", function(e:Event) : void { fr.load(); });
fr.addEventListener("complete", _onLoaded);
fr.browse();
visible = false;
}
private function _onLoaded(e:Event) : void {
var text:String = String(e.target.data);
if (/^<MMLList/.test(text)) mmlEditor.xml = new XML(text);
else mmlEditor.mml = text;
}
public function saveXML(e:Event) : void {
new FileReference().save(mmlEditor.xml, "sionmml.xml");
visible = false;
}
public function saveMML(e:Event) : void {
new FileReference().save(mmlEditor.mml, "sionmml.txt");
visible = false;
}
public function transTSSCP(e:Event) : void {
mmlEditor.mml = Translator.tsscp(mmlEditor.mml, false);
visible = false;
}
public function showSearch(e:Event) : void {
searchWindow.visible = true;
visible = false;
}
}
// Search window
//--------------------------------------------------
class SearchWindow extends Window {
public var input:InputText, execute:PushButton, regexp:CheckBox;
function SearchWindow(parent:DisplayObjectContainer=null) {
super(parent, 160, 160, "Search Window");
input = new InputText(content, 4, 4, "");
input.setSize(192, 16);
execute = new PushButton(content, 148, 24, "sreach", _onSearch);
execute.setSize(48, 16);
regexp = new CheckBox(content, 30, 26, "use regular expression");
new PushButton(this, 182, 3, "X", _onClose).setSize(15, 15);
setSize(200, 60);
visible = false;
}
private function _onClose(e:Event) : void {
visible = false;
}
private function _onSearch(e:Event) : void {
mmlEditor.colorSearchResult(input.text, regexp.selected);
}
}
// Control panel
//--------------------------------------------------
class ControlPanel extends Panel {
public var playButton:PushButton;
public var posLabel:Label, posInput:InputText;
public var muteCheck:CheckBox, soloCheck:CheckBox;
public var loadLabel:Label, trackLabel:Label;
function ControlPanel(parent:DisplayObjectContainer=null, xpos:Number=0, ypos:Number=0) {
super(parent, xpos, ypos);
playButton = new PushButton(content, 2, 5, "play", function(e:Event):void { togglePlay(); });
playButton.setSize(40, 16);
posLabel = new Label(content, 50, 2, "position");
posInput = new InputText(content, 90, 5, "0");
trackLabel = new Label(content, 250, 2, "");
loadLabel = new Label(content, 300, 2, "");
posInput.setSize(40, 16);
muteCheck = new CheckBox(content, 150, 8, "mute", _onMuteClick);
soloCheck = new CheckBox(content, 200, 8, "solo", _onSoloClick);
driver.addEventListener(SiONEvent.STREAM, _onStream);
}
public function get position() : int {
return int(posInput.text);
}
public function updateTrackLabel(isPlaying:Boolean) : void {
if (isPlaying) trackLabel.text = "track:" + String(driver.trackCount);
else trackLabel.text = "letters:" + String(textLength);
}
public function cbPlay(bool:Boolean) : void {
playButton.label = (bool) ? "stop" : "play";
loadLabel.visible = bool;
updateTrackLabel(bool);
}
public function cbPageChanged(index:int) : void {
muteCheck.selected = mmlEditor.getMute(index);
soloCheck.selected = (index == soloIndex);
}
private function _onStream(e:SiONEvent) : void { loadLabel.text = "load:" + String(driver.processTime) + "[ms]"; }
private function _onMuteClick(e:Event) : void { setMute(pageIndex, muteCheck.selected); }
private function _onSoloClick(e:Event) : void { setSolo(pageIndex, soloCheck.selected); }
}
// Pager
//--------------------------------------------------
class PageSelector extends Panel {
public var pageSelector:Vector.<PushButton> = new Vector.<PushButton>(MMLEditor.FIELD_COUNT);
public var pageMute:Vector.<Shape> = new Vector.<Shape>(MMLEditor.FIELD_COUNT);
public var mergedButton:PushButton, menuButton:PushButton, menuPopup:MenuPopup;
function PageSelector(parent:DisplayObjectContainer=null, xpos:Number=0, ypos:Number=0) {
super(parent, xpos, ypos);
for (var i:int=0; i<MMLEditor.FIELD_COUNT; i++) {
pageSelector[i] = new PushButton(content, i*50+64, 5, "page"+String(i+1), _onPageSelected);
pageSelector[i].toggle = true;
pageSelector[i].setSize(48, 16);
pageSelector[i].addChild(pageMute[i] = new Shape());
pageSelector[i].doubleClickEnabled = true;
pageSelector[i].addEventListener("doubleClick", _onDoubleClick);
pageMute[i].graphics.beginFill(0x000000, 0.25);
pageMute[i].graphics.drawRect(0, 0, 48, 16);
pageMute[i].graphics.endFill();
pageMute[i].visible = false;
}
pageSelector[0].selected = true;
mergedButton = new PushButton(content, 2, 5, "merged", _onMergedSelected);
mergedButton.setSize(48, 16);
menuButton = new PushButton(content, 415, 5, "menu", _onMenu);
menuButton.setSize(48, 16);
menuPopup = new MenuPopup(this);
}
public function cbPageChanged(index:int) : void {
for (var i:int=0; i<MMLEditor.FIELD_COUNT; i++) pageSelector[i].selected = (index == i);
}
public function cbMuteChanged(index:int, mute:Boolean) : void {
if (index < MMLEditor.FIELD_COUNT) pageMute[index].visible = mute;
}
private function _onMergedSelected(e:Event) : void {
mmlEditor.updateMergedMML();
selectPage(MMLEditor.FIELD_COUNT);
}
private function _onMenu(e:Event) : void {
menuPopup.visible = !menuPopup.visible;
}
private function _onPageSelected(e:Event) : void {
selectPage(int((e.target.x-39)/50));
}
private function _onDoubleClick(e:Event) : void {
var index:int = int((e.target.x-39)/50);
setMute(index, !mmlEditor.getMute(index));
}
override public function setSize(w:Number, h:Number) : void {
super.setSize(w, h);
if (menuButton) menuButton.x = width - 50;
if (menuPopup) menuPopup.x = width - 202;
}
}
// Editor
//--------------------------------------------------
class MMLEditor extends Sprite {
static public const FIELD_COUNT:int = 5;
static public const HELP_HEIGHT:int = 80;
public var sion140:Boolean = true;
private var mmlFields:Vector.<MMLField> = new Vector.<MMLField>(FIELD_COUNT+1);
private var helpText:TextArea;
private var activeIndex:int = 0;
function MMLEditor(parent:DisplayObjectContainer=null, xpos:Number=0, ypos:Number=0, func:Function=null) {
if (parent) parent.addChild(this);
this.x = xpos;
this.y = ypos;
MMLTextField.defaultFormat.size = 12;
MMLTextField.defaultFormat.font = "MS Gothic, Courier, _typewriter, Courier New"; // it's the only monospace font that works for me
MMLTextField.defaultFormat.color = 0x606060;
MMLTextField.searchFormat.color = 0xc06060;
MMLTextField.searchFormat.underline = true;
MMLTextField.highlightFormat.color = 0x800000;
for (var i:int=0; i<=FIELD_COUNT; i++) mmlFields[i] = new MMLField(this, (i==FIELD_COUNT), func);
helpText = new TextArea(this);
helpText.editable = false;
mmlFields[0].visible = true;
setInterval(_updateHelp, 500);
}
public function cbPageChanged(index:int) : void {
for (var i:int=0; i<=FIELD_COUNT; i++) mmlFields[i].visible = false;
mmlFields[index].visible = true;
mmlFields[index].setFocus();
activeIndex = index;
}
public function cbMuteChanged(index:int, mute:Boolean) : void {
mmlFields[index].mute = mute;
}
public function getMute(index:int) : Boolean {
return mmlFields[index].mute;
}
public function setSize(width:Number, height:Number) : void {
for (var i:int=0; i<=FIELD_COUNT; i++) mmlFields[i].setSize(width, height - HELP_HEIGHT);
helpText.y = mmlFields[0].y + mmlFields[0].height;
helpText.setSize(width, HELP_HEIGHT);
}
public function set xml(list:XML) : void {
if (list) {
for (var i:int=0; i<FIELD_COUNT; i++) mmlFields[i].text = list.MML[i];
}
}
public function get xml() : XML {
var xml:XML = <MMLList version='1'/>, cont:String;
for (var i:int=0; i<FIELD_COUNT; i++) {
cont = "<![CDATA["+ mmlFields[i].text + "]]>";
xml.appendChild(new XML("<MML field='" + String(i) + "'>" + cont + "</MML>"));
}
return xml;
}
public function set mml(text:String) : void {
mmlFields[0].text = text;
for (var i:int=1; i<FIELD_COUNT; i++) mmlFields[i].text = "";
}
public function get mml() : String {
var i:int, result:String = "", text:String, rex:RegExp = /;\s*$/;
for (i=0; i<FIELD_COUNT; i++) {
text = mmlFields[i].text;
if (!mmlFields[i].mute && text != "") {
if (sion140) text = text.replace(/([A-Z]+(=|@?[0-9]*{))/g, "#$1").replace(/#+/g, "#"); //}
if (rex.test(text)) result += text;
else result += text + ";\n";
}
}
return result;
}
public function updateMergedMML() : void {
mmlFields[FIELD_COUNT].text = mml;
}
public function colorSearchResult(pattern:String, byRegExp:Boolean=false) : void {
mmlFields[pageIndex].colorSearchResult(pattern, byRegExp);
}
private var _eventGlobalFlags:Vector.<Boolean> = new Vector.<Boolean> (MMLEvent.COMMAND_MAX, true); // global event flag
private var _userDefinedEventID:Object = {}; // id map of user-defined event letter set by newMMLEventListener().
private function _updateHelp():void {
Parser._setUserDefinedEventID(_userDefinedEventID);
Parser._setGlobalEventFlags(_eventGlobalFlags);
var setting:MMLParserSetting = new MMLParserSetting();
try {
Parser.prepareParse(setting, mmlFields[activeIndex].text);
var event:MMLEvent = Parser.parse(0, mmlFields[activeIndex].tf.caretIndex);
} catch(e:*) {
helpText.text = "error: " + e.message;
return;
}
helpText.text = "caretIndex " + mmlFields[activeIndex].tf.caretIndex + "\n";
helpText.text += Parser.matchStart + '\n';
helpText.text += Parser.matchEnd + '\n';
helpText.text += 'Event: ' + Parser.eventAtCaret + '\n';
mmlFields[activeIndex].tf.setTextFormat(MMLTextField.defaultFormat, 0, mmlFields[activeIndex].tf.length);
if(Parser.eventAtCaret) {
mmlFields[activeIndex].tf.setTextFormat(MMLTextField.highlightFormat, Parser.matchStart, Parser.matchEnd);
}
}
}
class MMLField extends Panel {
private var textField:MMLTextField;
private var scrollBar:CustomSlider;
private var funcTextChange:Function;
function MMLField(parent:DisplayObjectContainer, constant:Boolean=false, func:Function=null) {
super(parent);
funcTextChange = func;
scrollBar = new CustomSlider("vertical", content, 0, 0, _onSliderChanged);
scrollBar.backClick = true;
textField = new MMLTextField(content, constant);
textField.addEventListener("change", _onTextChanged);
textField.addEventListener("scroll", _onTextScroll);
visible = false;
}
public function get text() : String { return textField.text; }
public function set text(mml:String) : void { textField.text = mml || ""; }
public function get mute() : Boolean { return (textField.type == 'dynamic'); }
public function set mute(bool:Boolean) : void {
if (textField.constant || bool) {
textField.type = 'dynamic';
textField.backgroundColor = 0xc0c0c0;
} else {
textField.type = 'input';
textField.backgroundColor = 0xffffff;
}
}
override public function setSize(width:Number, height:Number) : void {
super.setSize(width, height);
if (textField) {
textField.setSize(width - 15, height);
scrollBar.setSize(15, height);
scrollBar.x = width - 15;
scrollBar.setRange(1, textField.maxScrollV, textField.scrollV, textField.numLines - textField.maxScrollV);
}
}
public function setFocus() : void {
stage.focus = textField;
}
private function _onSliderChanged(e:Event) : void {
textField.scrollV = scrollBar.value;
}
private function _onTextChanged(e:Event) : void {
scrollBar.setRange(1, textField.maxScrollV, textField.scrollV, textField.numLines - textField.maxScrollV);
funcTextChange(textField.text.length);
}
private function _onTextScroll(e:Event) : void {
scrollBar.setRange(1, textField.maxScrollV, textField.scrollV, textField.numLines - textField.maxScrollV);
}
public function colorSearchResult(pattern:String, byRegExp:Boolean=false) : void {
textField.setTextFormat(MMLTextField.defaultFormat);
var mml:String = textField.text;
if (pattern != "") {
if (byRegExp) {
var res:*, rex:RegExp = new RegExp(pattern, "g");
while (res = rex.exec(mml)) textField.setTextFormat(MMLTextField.searchFormat, res.index, res.index+res[0].length);
} else {
for (var i:int=0; i!=-1;) {
i = mml.indexOf(pattern, i);
if (i == -1) break;
textField.setTextFormat(MMLTextField.searchFormat, i, i+pattern.length);
i += pattern.length+1;
}
}
}
}
public function get tf():MMLTextField { return textField; }
}
// Slider with stretching handle
//--------------------------------------------------
class CustomSlider extends Slider {
protected var _handleLength:Number = 64, _isH:Boolean;
function CustomSlider(orientation:String = "horizontal", parent:DisplayObjectContainer = null, xpos:Number = 0, ypos:Number = 0, defaultHandler:Function = null) {
super(orientation, parent, xpos, ypos, defaultHandler);
_isH = (_orientation == "horizontal");
}
public function setRange(min:Number, max:Number, value:Number, range:Number) : void {
setSliderParams(min, max, value);
if (visible = (_min < _max)) {
_handleLength = range / (range + max - min) * ((_isH) ? _width : _height);
invalidate();
}
}
override protected function drawHandle() : void {
_handle.graphics.clear();
_handle.graphics.beginFill(Style.BUTTON_FACE);
if (_isH) _handle.graphics.drawRect(1, 1, _handleLength - 2, _height - 2);
else _handle.graphics.drawRect(1, 1, _width - 2, _handleLength - 2);
_handle.graphics.endFill();
positionHandle();
}
override protected function onDrag(event:MouseEvent) : void {
stage.addEventListener(MouseEvent.MOUSE_UP, onDrop);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onSlide);
if (_isH) _handle.startDrag(false, new Rectangle(0, 0, width - _handleLength, 0));
else _handle.startDrag(false, new Rectangle(0, 0, 0, height - _handleLength));
}
override protected function positionHandle() : void {
if (_isH) _handle.x = (_value - _min) / (_max - _min) * (_width - _handleLength);
else _handle.y = (_value - _min) / (_max - _min) * (_height - _handleLength);
}
override protected function onBackClick(event:MouseEvent) : void {
if (_isH) {
_handle.x = mouseX - _height / 2;
_handle.x = Math.max(_handle.x, 0);
_handle.x = Math.min(_handle.x, _width - _handleLength);
_value = _handle.x / (_width - _handleLength) * (_max - _min) + _min;
} else {
_handle.y = mouseY - _width / 2;
_handle.y = Math.max(_handle.y, 0);
_handle.y = Math.min(_handle.y, _height - _handleLength);
_value = _handle.y / (_height - _handleLength) * (_max - _min) + _min;
}
dispatchEvent(new Event(Event.CHANGE));
}
override protected function onSlide(event:MouseEvent) : void {
var oldValue:Number = _value;
if (_isH) _value = _handle.x / (_width - _handleLength) * (_max - _min + 1) + _min;
else _value = _handle.y / (_height - _handleLength) * (_max - _min + 1) + _min;
if (_value != oldValue) dispatchEvent(new Event(Event.CHANGE));
}
}
// undo and redo are from psyark's Psycode (modified)
//--------------------------------------------------
class MMLTextField extends TextField {
public var constant:Boolean;
private var _preventFollowingTextInput:Boolean = false;
private var _prevText:String = "";
private var _prevSBI:int;
private var _prevSEI:int;
private var _history:Vector.<HistoryItem> = new Vector.<HistoryItem>();
private var _historyIndex:int;
private var _ignoreChange:Boolean = false;
static public var defaultFormat:TextFormat = new TextFormat();
static public var searchFormat:TextFormat = new TextFormat();
static public var highlightFormat:TextFormat = new TextFormat();
function MMLTextField(parent:DisplayObjectContainer, constant:Boolean) {
this.constant = constant;
selectable = true;
alwaysShowSelection = true;
mouseEnabled = true;
type = (constant) ? 'dynamic' : 'input';
multiline = true;
wordWrap = false;
background = true;
backgroundColor = (constant) ? 0xc0c0c0 : 0xffffff;
defaultTextFormat = defaultFormat;
if (!constant) {
addEventListener("change", _onChange);
addEventListener("textInput", _onTextInput);
addEventListener("keyDown", _onKeyDown);
}
parent.addChild(this);
clearHistory();
}
public function setSize(width:Number, height:Number) : void {
this.width = width;
this.height = height;
}
public function clearHistory() : void {
_history.length = 0;
_historyIndex = 0;
_prevText = text;
}
private function _onChange(event:Event) : void {
var res:*;
if (_prevText != text) {
if (_preventFollowingTextInput) {
res = StringComparator.compare(_prevText, text);
replaceText(res.l, length - res.r, _prevText.substring(res.l, _prevText.length - res.r).replace(/\r/g, "\n"));
setSelection(_prevSBI, _prevSEI);
} else {
if (!_ignoreChange) {
res = StringComparator.compare(_prevText, text);
var item:HistoryItem = new HistoryItem(res.l);
item.oldText = _prevText.substring(res.l, _prevText.length - res.r);
item.newText = text.substring(res.l, text.length - res.r);
_history.length = _historyIndex;
_history.push(item);
_historyIndex = _history.length;
}
_prevText = text;
_prevSBI = selectionBeginIndex;
_prevSEI = selectionEndIndex;
}
}
}
private function _onTextInput(event:Event) : void {
if (_preventFollowingTextInput) event.preventDefault();
}
private function _onKeyDown(event:KeyboardEvent) : void {
_preventFollowingTextInput = false;
switch (event.keyCode) {
case Keyboard.BACKSPACE:
break;
case 89: // Y
if (event.ctrlKey) doCtrlY();
break;
case 90: // Z
if (event.ctrlKey) doCtrlZ();
break;
}
function doCtrlZ() : void {
if (_history.length && _historyIndex > 0) {
var item:HistoryItem = _history[_historyIndex - 1];
replaceText(item.index, item.index + item.newText.length, item.oldText.replace(/\r/g, "\n"));
setSelection(item.index + item.oldText.length, item.index + item.oldText.length);
_historyIndex--;
_dispatchIgnorableChangeEvent();
}
event.preventDefault();
_preventFollowingTextInput = true;
}
function doCtrlY() : void {
if (_history.length && _historyIndex < _history.length) {
var item:HistoryItem = _history[_historyIndex];
replaceText(item.index, item.index + item.oldText.length, item.newText.replace(/\r/g, "\n"));
setSelection(item.index + item.newText.length, item.index + item.newText.length);
_historyIndex++;
_dispatchIgnorableChangeEvent();
}
event.preventDefault();
_preventFollowingTextInput = true;
}
}
private function _dispatchIgnorableChangeEvent():void {
_ignoreChange = true;
dispatchEvent(new Event("change"));
_ignoreChange = false;
}
}
class HistoryItem {
public var index:int;
public var oldText:String;
public var newText:String;
public function HistoryItem(index:int=0, oldText:String="", newText:String="") {
this.index = index;
this.oldText = oldText;
this.newText = newText;
}
}
class StringComparator {
static public function compare(str1:String, str2:String) : * {
var minLength:int = Math.min(str1.length, str2.length);
var step:int, l:int, r:int;
for (step=0x1000000; step>minLength;) step>>=1;
for (l=0; l<minLength;) {
if (str1.substr(0, l+step) != str2.substr(0, l+step)) {
if (step == 1) { break; }
step >>= 1;
} else {
l += step;
}
}
l = Math.min(l, minLength);
minLength -= l;
for (step=0x1000000; step>minLength;) step>>=1;
for (r=0; r<minLength;) {
if (str1.substr(-r - step) != str2.substr(-r - step)) {
if (step == 1) { break; }
step >>= 1;
} else {
r += step;
}
}
r = Math.min(r, minLength);
return {"l":l, "r":r};
}
}
//----------------------------------------------------------------------------------------------------
// MML parser class
// Copyright (c) 2008 keim All rights reserved.
// Distributed under BSD-style license (see org.si.license.txt).
//----------------------------------------------------------------------------------------------------
import flash.utils.getTimer;
/** MML parser class. */
class Parser
{
// tables
//--------------------------------------------------
static private var _keySignitureTable:Array = [
Vector.<int>([ 0, 0, 0, 0, 0, 0, 0]),
Vector.<int>([ 0, 0, 0, 1, 0, 0, 0]),
Vector.<int>([ 1, 0, 0, 1, 0, 0, 0]),
Vector.<int>([ 1, 0, 0, 1, 1, 0, 0]),
Vector.<int>([ 1, 1, 0, 1, 1, 0, 0]),
Vector.<int>([ 1, 1, 0, 1, 1, 1, 0]),
Vector.<int>([ 1, 1, 1, 1, 1, 1, 0]),
Vector.<int>([ 1, 1, 1, 1, 1, 1, 1]),
Vector.<int>([ 0, 0, 0, 0, 0, 0,-1]),
Vector.<int>([ 0, 0,-1, 0, 0, 0,-1]),
Vector.<int>([ 0, 0,-1, 0, 0,-1,-1]),
Vector.<int>([ 0,-1,-1, 0, 0,-1,-1]),
Vector.<int>([ 0,-1,-1, 0,-1,-1,-1]),
Vector.<int>([-1,-1,-1, 0,-1,-1,-1]),
Vector.<int>([-1,-1,-1,-1,-1,-1,-1])
];
// valiables
//--------------------------------------------------
// settting
static private var _setting:MMLParserSetting = null;
// MML string
static private var _mmlString:String = null;
// user defined event map.
static private var _userDefinedEventID:Object = null;
// system event strings
static private var _systemEventStrings:Vector.<String> = new Vector.<String>(32);
static private var _sequenceMMLStrings:Vector.<String> = new Vector.<String>(32);
// flag list of global event
static private var _globalEventFlags:Vector.<Boolean> = null;
// temporaries
static private var _freeEventChain:MMLEvent = null;
static private var _interruptInterval:int = 0;
static private var _startTime:int = 0;
static private var _parsingTime:int = 0;
static private var _staticLength:int = 0;
static private var _staticOctave:int = 0;
static private var _staticNoteShift:int = 0;
static private var _isLastEventLength:Boolean = false;
static private var _systemEventIndex:int = 0;
static private var _sequenceMMLIndex:int = 0;
static private var _headMMLIndex:int = 0;
static private var _cacheMMLString:Boolean = false;
static private var _keyScale :Vector.<int> = Vector.<int>([0,2,4,5,7,9,11]);
static private var _keySigniture:Vector.<int> = _keySignitureTable[0];
static private var _keySignitureCustom:Vector.<int> = new Vector.<int>(7, true);
static private var _terminator :MMLEvent = new MMLEvent();
static private var _lastEvent :MMLEvent = null;
static private var _lastSequenceHead:MMLEvent = null;
static private var _repeatStac:Array = [];
// properties
//--------------------------------------------------
/** Key signiture for all notes. The letter for key signiture is expressed as /[A-G][+\-#b]?m?/. */
static public function set keySign(sign:String) : void
{
var note:int, i:int, list:Array, shift:String, noteLetters:String = "cdefgab";
switch(sign) {
case '':
case 'C': case 'Am': _keySigniture = _keySignitureTable[0]; break;
case 'G': case 'Em': _keySigniture = _keySignitureTable[1]; break;
case 'D': case 'Bm': _keySigniture = _keySignitureTable[2]; break;
case 'A': case 'F+m': case 'F#m': _keySigniture = _keySignitureTable[3]; break;
case 'E': case 'C+m': case 'C#m': _keySigniture = _keySignitureTable[4]; break;
case 'B': case 'G+m': case 'G#m': _keySigniture = _keySignitureTable[5]; break;
case 'F+': case 'F#': case 'D+m': case 'D#m': _keySigniture = _keySignitureTable[6]; break;
case 'C+': case 'C#': case 'A+m': case 'A#m': _keySigniture = _keySignitureTable[7]; break;
case 'F': case 'Dm': _keySigniture = _keySignitureTable[8]; break;
case 'B-': case 'Bb': case 'Gm': _keySigniture = _keySignitureTable[9]; break;
case 'E-': case 'Eb': case 'Cm': _keySigniture = _keySignitureTable[10]; break;
case 'A-': case 'Ab': case 'Fm': _keySigniture = _keySignitureTable[11]; break;
case 'D-': case 'Db': case 'B-m': case 'Bbm': _keySigniture = _keySignitureTable[12]; break;
case 'G-': case 'Gb': case 'E-m': case 'Ebm': _keySigniture = _keySignitureTable[13]; break;
case 'C-': case 'Cb': case 'A-m': case 'Abm': _keySigniture = _keySignitureTable[14]; break;
default:
for (i=0; i<7; i++) { _keySignitureCustom[i] = 0; }
list = sign.split(/[\s,]/);
for (i=0; i<list.length; i++) {
note = noteLetters.indexOf(list.charAt(0).toLowerCase());
if (note == -1) throw errorKeySign(sign);
if (list.length > 1) {
shift = list.charAt(1);
_keySignitureCustom[note] = (shift=='+' || shift=='#') ? 1 : (shift=='-' || shift=='b') ? -1 : 0;
} else {
_keySignitureCustom[note] = 0;
}
}
_keySigniture = _keySignitureCustom;
}
}
/** Parsing progression (0-1). */
static public function get parseProgress() : Number
{
if (_mmlString != null) {
return _mmlRegExp.lastIndex / (_mmlString.length+1);
}
return 0;
}
// constructor
//--------------------------------------------------
/** constructer do nothing. */
function Parser()
{
}
// allocator
//--------------------------------------------------
/** @private [internal] Free all events in the sequence. */
static internal function _freeAllEvents(seq:MMLSequence) : void
{
if (seq.headEvent == null) return;
// connect to free list
seq.tailEvent.next = _freeEventChain;
// update head of free list
_freeEventChain = seq.headEvent;
// clear
seq.headEvent = null;
seq.tailEvent = null;
}
/** @private [internal] Free event. */
static internal function _freeEvent(e:MMLEvent) : MMLEvent
{
var next:MMLEvent = e.next;
e.next = _freeEventChain;
_freeEventChain = e;
return next;
}
/** @private [internal] allocate event */
static internal function _allocEvent(id:int, data:int, length:int=0) : MMLEvent
{
if (_freeEventChain) {
var e:MMLEvent = _freeEventChain;
_freeEventChain = _freeEventChain.next;
return e.initialize(id, data, length);
}
return (new MMLEvent()).initialize(id, data, length);
}
// settting
//--------------------------------------------------
/** @private [internal] Set map of user defined ids. */
static internal function _setUserDefinedEventID(map:Object) : void
{
if (_userDefinedEventID !== map) {
_userDefinedEventID = map;
_mmlRegExp = null;
}
}
/** @private [internal] Set array of global event flags. */
static internal function _setGlobalEventFlags(flags:Vector.<Boolean>) : void
{
_globalEventFlags = flags;
}
// public operation
//--------------------------------------------------
/* Add new event. */
static public function addMMLEvent(id:int, data:int=0, length:int=0, noteOption:Boolean=false) : MMLEvent
{
if (!noteOption) {
// Make channel data chain
if (id == MMLEvent.SEQUENCE_HEAD) {
_lastSequenceHead.jump = _lastEvent;
_lastSequenceHead = _pushMMLEvent(id, data, length);
_initialize_track();
} else
// Concatinate REST event
if (id == MMLEvent.REST && _lastEvent.id == MMLEvent.REST) {
_lastEvent.length += length;
} else {
_pushMMLEvent(id, data, length);
// seqHead.data is the count of global events
if (_globalEventFlags[id]) _lastSequenceHead.data++;
}
} else {
// note option event is inserted after NOTE .
if (_lastEvent.id == MMLEvent.NOTE) {
length = _lastEvent.length;
_lastEvent.length = 0;
_pushMMLEvent(id, data, length);
} else {
// Error when there is no NOTE before SLUR event.
throw errorSyntax("* or &");
}
}
_isLastEventLength = false;
return _lastEvent;
}
/** Get MMLEvent id by mml command letter.
* @param mmlCommand letter of MML command.
* @return Event id. Returns 0 if not found.
*/
static public function getEventID(mmlCommand:String) : int
{
switch (mmlCommand) {
case 'c': case 'd': case 'e': case 'f': case 'g': case 'a': case 'b': return MMLEvent.NOTE;
case 'r': return MMLEvent.REST;
case 'q': return MMLEvent.QUANT_RATIO;
case '@q': return MMLEvent.QUANT_COUNT;
case 'v': return MMLEvent.VOLUME;
case '@v': return MMLEvent.FINE_VOLUME;
case '%': return MMLEvent.MOD_TYPE;
case '@': return MMLEvent.MOD_PARAM;
case '@i': return MMLEvent.INPUT_PIPE;
case '@o': return MMLEvent.OUTPUT_PIPE;
case '(': case ')': return MMLEvent.VOLUME_SHIFT;
case '&': return MMLEvent.SLUR;
case '&&': return MMLEvent.SLUR_WEAK;
case '*': return MMLEvent.PITCHBEND;
case ',': return MMLEvent.PARAMETER;
case '$': return MMLEvent.REPEAT_ALL;
case '[': return MMLEvent.REPEAT_BEGIN;
case ']': return MMLEvent.REPEAT_END;
case '|': return MMLEvent.REPEAT_BREAK;
case 't': return MMLEvent.TEMPO;
}
return 0;
}
/** @private [internal] get command letters. */
static internal function _getCommandLetters(list:Array) : void
{
list[MMLEvent.NOTE] = 'c'
list[MMLEvent.REST] = 'r';
list[MMLEvent.QUANT_RATIO] = 'q';
list[MMLEvent.QUANT_COUNT] = '@q';
list[MMLEvent.VOLUME] = 'v';
list[MMLEvent.FINE_VOLUME] = '@v';
list[MMLEvent.MOD_TYPE] = '%';
list[MMLEvent.MOD_PARAM] = '@';
list[MMLEvent.INPUT_PIPE] = '@i';
list[MMLEvent.OUTPUT_PIPE] = '@o';
list[MMLEvent.VOLUME_SHIFT] = '(';
list[MMLEvent.SLUR] = '&';
list[MMLEvent.SLUR_WEAK] = '&&';
list[MMLEvent.PITCHBEND] = '*';
list[MMLEvent.PARAMETER] = ',';
list[MMLEvent.REPEAT_ALL] = '$';
list[MMLEvent.REPEAT_BEGIN] = '[';
list[MMLEvent.REPEAT_END] = ']';
list[MMLEvent.REPEAT_BREAK] = '|';
list[MMLEvent.TEMPO] = 't';
}
/** @private [internal] get system event string */
static internal function _getSystemEventString(e:MMLEvent) : String
{
return _systemEventStrings[e.data];
}
/** @private [internal] get sequence mml string */
static internal function _getSequenceMML(e:MMLEvent) : String
{
return (e.length == -1) ? "" : _sequenceMMLStrings[e.length];
}
// push event
static private function _pushMMLEvent(id:int, data:int, length:int) : MMLEvent
{
_lastEvent.next = _allocEvent(id, data, length);
_lastEvent = _lastEvent.next;
return _lastEvent;
}
// register system event string
static private function _regSystemEventString(str:String) : int
{
if (_systemEventStrings.length <= _systemEventIndex) _systemEventStrings.length = _systemEventStrings.length * 2;
_systemEventStrings[_systemEventIndex++] = str;
return _systemEventIndex - 1;
}
// register sequence MML string
static private function _regSequenceMMLStrings(str:String) : int
{
if (_sequenceMMLStrings.length <= _sequenceMMLIndex) _sequenceMMLStrings.length = _sequenceMMLStrings.length * 2;
_sequenceMMLStrings[_sequenceMMLIndex++] = str;
return _sequenceMMLIndex - 1;
}
// regular expression
//--------------------------------------------------
static private const REX_WHITESPACE:int = 1;
static private const REX_SYSTEM :int = 2;
static private const REX_COMMAND :int = 3;
static private const REX_NOTE :int = 4;
static private const REX_SHIFT_NOTE:int = 5;
static private const REX_USER_EVENT:int = 6;
static private const REX_EVENT :int = 7;
static private const REX_TABLE :int = 8;
static private const REX_PARAM :int = 9;
static private const REX_PERIOD :int = 10;
static private var _mmlRegExp:RegExp = null;
static private function createRegExp(reset:Boolean) : RegExp
{
if (_mmlRegExp == null) {
// user defined event letters
var ude:Array = [];
for (var letter:String in _userDefinedEventID) { ude.push(letter); }
var uderex:String = (ude.length > 0) ? (ude.sort(Array.DESCENDING).join('|')) : 'a'; // ('A`) I know its an ad-hok solution...
var rex:String;
rex = "(\\s+)"; // whitespace (res[1])
rex += "|(#[^;]*)"; // system (res[2])
rex += "|("; // --all-- (res[3])
rex += "([a-g])([\\-+#]?)"; // note (res[4],[5])
rex += "|(" + uderex + ")"; // module events (res[6])
rex += "|(@[qvio]?|&&|!@ns|[rlqovt^<>()\\[\\]/|$%&*,;])"; // default events (res[7])
rex += "|(\\{.*?\\}[0-9]*\\*?[\\-0-9.]*\\+?[\\-0-9.]*)"; // table event (res[8])
rex += ")\\s*(-?[0-9]*)" // parameter (res[9])
rex += "\\s*(\\.*)" // periods (res[10])
_mmlRegExp = new RegExp(rex, 'gms');
}
// reset last index
if (reset) _mmlRegExp.lastIndex = 0;
return _mmlRegExp;
}
// parser
//--------------------------------------------------
/** Prepare to parse.
* @param mml MML String.
* @return Returns head MMLEvent. The return value of null means no head event.
*/
static public function prepareParse(setting:MMLParserSetting, mml:String) : void
{
// set internal parameters
_setting = setting;
_mmlString = mml;
_parsingTime = getTimer();
// create RegExp
createRegExp(true);
// initialize
_initialize();
}
/** Parse mml string.
* @param interrupt Interrupting interval [ms]. 0 means no interruption. The interrupt appears between each sequence.
* @return Returns head MMLEvent. The return value of null means no head event.
*/
static public var eventAtCaret:MMLEvent;
static public var matchStart:int;
static public var matchEnd:int;
static public function parse(interrupt:int=0, caretIndex:int = -1) : MMLEvent
{
var shift:int, note:int, halt:Boolean, rex:RegExp, res:*,
mml2nn:int = _setting.mml2nn,
codeC:int = "c".charCodeAt();
// set interrupting interval
_interruptInterval = interrupt;
_startTime = getTimer();
// regular expression
rex = createRegExp(false);
eventAtCaret = null;
// parse
halt = false;
res = rex.exec(_mmlString);
while (res && String(res[0]).length>0) {
// skip comments
if (res[REX_WHITESPACE] == undefined) {
if (res[REX_NOTE]) {
// note events.
note = String(res[REX_NOTE]).charCodeAt() - codeC;
if (note < 0) note += 7;
shift = _keySigniture[note];
switch(String(res[REX_SHIFT_NOTE])) {
case '+': case '#': shift++; break;
case '-': shift--; break;
}
_note(_keyScale[note] + shift + mml2nn, __calcLength(), __period());
} else
if (res[REX_USER_EVENT]) {
// user defined events.
if (!String(res[REX_USER_EVENT]) in _userDefinedEventID) throw errorUnknown("REX_USER_EVENT");
addMMLEvent(_userDefinedEventID[String(res[REX_USER_EVENT])], __param());
} else
if (res[REX_EVENT]) {
// default events.
switch(String(res[REX_EVENT])) {
case 'r': _rest (__calcLength(), __period()); break;
case 'l': _length (__calcLength(), __period()); break;
case '^': _tie (__calcLength(), __period()); break;
case 'o': _octave (__param(_setting.defaultOctave)); break;
case 'q': _quant (__param(_setting.defaultQuantRatio)); break;
case '@q': _at_quant (__param(_setting.defaultQuantCount)); break;
case 'v': _volume (__param(_setting.defaultVolume)); break;
case '@v': _at_volume(__param(_setting.defaultFineVolume)); break;
case '%': _mod_type (__param()); break;
case '@': _mod_param(__param()); break;
case '@i': _input (__param(0)); break;
case '@o': _output(__param(0)); break;
case '(': _volumeShift( __param(1)); break;
case ')': _volumeShift(-__param(1)); break;
case '<': _octaveShift( __param(1)); break;
case '>': _octaveShift(-__param(1)); break;
case '&': _slur(); break;
case '&&': _slurweak(); break;
case '*': _portament(); break;
case ',': _parameter(__param()); break;
case ';': halt = _end_sequence(); break;
case '$': _repeatPoint(); break;
case '[': _repeatBegin(__param(2)); break;
case ']': _repeatEnd(__param()); break;
case '|': _repeatBreak(); break;
case '!@ns': _noteShift( __param(0)); break;
case 't': _tempo(__param(_setting.defaultBPM)); break;
default:
throw errorUnknown("REX_EVENT;"+res[REX_EVENT]);
break;
}
} else
if (res[REX_SYSTEM]) {
// system command is only available at the top of the channel sequence.
if (_lastEvent.id != MMLEvent.SEQUENCE_HEAD) throw errorSyntax(res[0]);
// add system event
addMMLEvent(MMLEvent.SYSTEM_EVENT, _regSystemEventString(res[REX_SYSTEM]));
} else
if (res[REX_TABLE]) {
// add table event
addMMLEvent(MMLEvent.TABLE_EVENT, _regSystemEventString(res[REX_TABLE]));
} else {
// syntax error
throw errorSyntax(res[0]);
}
}
if(res && res.index <= caretIndex && res.index + res[0].length > caretIndex) {
eventAtCaret = _lastEvent;
matchStart = res.index;
matchEnd = matchStart + res[0].length;
}
// halt
if (halt) return null;
// parse next
res = rex.exec(_mmlString);
}
// parsing complete
// check repeating stac
if (_repeatStac.length != 0) throw errorStacOverflow("[");
// set last channel's last event.
if (_lastEvent.id != MMLEvent.SEQUENCE_HEAD) _lastSequenceHead.jump = _lastEvent;
// calculate parsing time
_parsingTime = getTimer() - _parsingTime;
// clear terminator
var headEvent:MMLEvent = _terminator.next;
_terminator.next = null;
return headEvent;
// internal functions
//----------------------------------------
// parse length. The return value of int.MIN_VALUE means abbreviation.
function __calcLength() : int {
if (String(res[REX_PARAM]).length == 0) return int.MIN_VALUE;
var len:int = int(res[REX_PARAM]);
if (len == 0) return 0;
var iLength:int = _setting.resolution/len;
if (iLength<1 || iLength>_setting.resolution) throw errorRangeOver("length", 1, _setting.resolution);
return iLength;
}
// parse param.
function __param(defaultValue:int = int.MIN_VALUE) : int {
return (String(res[REX_PARAM]).length > 0) ? int(res[REX_PARAM]) : defaultValue;
}
// parse periods.
function __period() : int {
return String(res[REX_PERIOD]).length;
}
}
// initialize before parse
static private function _initialize() : void
{
// free all remains
var e:MMLEvent = _terminator.next;
while (e) { e = _freeEvent(e); }
// initialize tempraries
_systemEventIndex = 0; // system event index
_sequenceMMLIndex = 0; // sequence mml index
_lastEvent = _terminator; // clear event chain
_lastSequenceHead = _pushMMLEvent(MMLEvent.SEQUENCE_HEAD, 0, 0); // add first event (SEQUENCE_HEAD).
if (_cacheMMLString) addMMLEvent(MMLEvent.DEBUG_INFO, -1);
_initialize_track();
}
// initialize before starting new track.
static private function _initialize_track() : void
{
_staticLength = _setting.defaultLength; // initialize l command value
_staticOctave = _setting.defaultOctave; // initialize o command value
_staticNoteShift = 0; // initialize note shift
_isLastEventLength = false; // initialize l command flag
_repeatStac.length = 0; // clear repeating pointer stac
_headMMLIndex = _mmlRegExp.lastIndex; // mml index of sequence head
}
// note
//------------------------------
// note
static private function _note(note:int, iLength:int, period:int) : void
{
note += _staticOctave*12 + _staticNoteShift;
if (note < 0) {
//throw errorNoteOutofRange(note);
note = 0;
} else
if (note > 127) {
//throw errorNoteOutofRange(note);
note = 127;
}
addMMLEvent(MMLEvent.NOTE, note, __calcLength(iLength, period));
}
// rest
static private function _rest(iLength:int, period:int) : void
{
addMMLEvent(MMLEvent.REST, 0, __calcLength(iLength, period));
}
// length operation
//------------------------------
// length
static private function _length(iLength:int, period:int) : void
{
_staticLength = __calcLength(iLength, period);
_isLastEventLength = true;
}
// tie
static private function _tie(iLength:int, period:int) : void
{
if (_isLastEventLength) {
_staticLength += __calcLength(iLength, period);
} else
if (_lastEvent.id == MMLEvent.REST || _lastEvent.id == MMLEvent.NOTE) {
_lastEvent.length += __calcLength(iLength, period);
} else {
throw errorSyntax("tie command");
}
}
// slur
static private function _slur() : void
{
addMMLEvent(MMLEvent.SLUR, 0, 0, true);
}
// weak slur
static private function _slurweak() : void
{
addMMLEvent(MMLEvent.SLUR_WEAK, 0, 0, true);
}
// portament
static private function _portament() : void
{
addMMLEvent(MMLEvent.PITCHBEND, 0, 0, true);
}
// gate time
static private function _quant(param:int) : void
{
if (param<_setting.minQuantRatio || param>_setting.maxQuantRatio) {
throw errorRangeOver("q", _setting.minQuantRatio, _setting.maxQuantRatio);
}
addMMLEvent(MMLEvent.QUANT_RATIO, param);
}
// absolute gate time
static private function _at_quant(param:int) : void
{
if (param<_setting.minQuantCount || param>_setting.maxQuantCount) {
throw errorRangeOver("@q", _setting.minQuantCount, _setting.maxQuantCount);
}
addMMLEvent(MMLEvent.QUANT_COUNT, param);
}
// calculate length
static private function __calcLength(iLength:int, period:int) : int
{
// set default value
if (iLength == int.MIN_VALUE) iLength = _staticLength;
// extension by period
var len:int = iLength;
while (period>0) { iLength += len>>(period--); }
return iLength;
}
// pitch operation
//------------------------------
// octave
static private function _octave(param:int) : void
{
if (param<_setting.minOctave || param>_setting.maxOctave) {
throw errorRangeOver("o", _setting.minOctave, _setting.maxOctave);
}
_staticOctave = param;
}
// octave shift
static private function _octaveShift(param:int) : void
{
param *= _setting.octavePolarization;
_staticOctave += param;
}
// note shift
static private function _noteShift(param:int) : void
{
_staticNoteShift += param;
}
// volume
static private function _volume(param:int) : void
{
if (param<0 || param>_setting.maxVolume) {
throw errorRangeOver("v", 0, _setting.maxVolume);
}
addMMLEvent(MMLEvent.VOLUME, param);
}
// fine volume
static private function _at_volume(param:int) : void
{
if (param<0 || param>_setting.maxFineVolume) {
throw errorRangeOver("@v", 0, _setting.maxFineVolume);
}
addMMLEvent(MMLEvent.FINE_VOLUME, param);
}
// volume shift
static private function _volumeShift(param:int) : void
{
param *= _setting.volumePolarization;
if (_lastEvent.id == MMLEvent.VOLUME_SHIFT || _lastEvent.id == MMLEvent.VOLUME) {
_lastEvent.data += param;
} else {
addMMLEvent(MMLEvent.VOLUME_SHIFT, param);
}
}
// repeating
//------------------------------
// repeat point
static private function _repeatPoint() : void
{
addMMLEvent(MMLEvent.REPEAT_ALL, 0);
}
// begin repeating
static private function _repeatBegin(rep:int) : void
{
if (rep < 1 || rep > 65535) throw errorRangeOver("[", 1, 65535);
addMMLEvent(MMLEvent.REPEAT_BEGIN, rep, 0);
_repeatStac.unshift(_lastEvent);
}
// break repeating
static private function _repeatBreak() : void
{
if (_repeatStac.length == 0) throw errorStacUnderflow("|");
addMMLEvent(MMLEvent.REPEAT_BREAK);
_lastEvent.jump = MMLEvent(_repeatStac[0]);
}
// end repeating
static private function _repeatEnd(rep:int) : void
{
if (_repeatStac.length == 0) throw errorStacUnderflow("]");
addMMLEvent(MMLEvent.REPEAT_END);
var beginEvent:MMLEvent = MMLEvent(_repeatStac.shift());
_lastEvent.jump = beginEvent; // rep_end.jump = rep_start
beginEvent.jump = _lastEvent; // rep_start.jump = rep_end
// update repeat count
if (rep != int.MIN_VALUE) {
if (rep < 1 || rep > 65535) throw errorRangeOver("]", 1, 65535);
beginEvent.data = rep;
}
}
// others
//------------------------------
// module type
static private function _mod_type(param:int) : void
{
addMMLEvent(MMLEvent.MOD_TYPE, param);
}
// module parameters
static private function _mod_param(param:int) : void
{
addMMLEvent(MMLEvent.MOD_PARAM, param);
}
// set input pipe
static private function _input(param:int) : void
{
addMMLEvent(MMLEvent.INPUT_PIPE, param);
}
// set output pipe
static private function _output(param:int) : void
{
addMMLEvent(MMLEvent.OUTPUT_PIPE, param);
}
// pural parameters
static private function _parameter(param:int) : void
{
addMMLEvent(MMLEvent.PARAMETER, param);
}
// sequence change
static private function _end_sequence() : Boolean
{
if (_lastEvent.id != MMLEvent.SEQUENCE_HEAD) {
if (_lastSequenceHead.next && _lastSequenceHead.next.id == MMLEvent.DEBUG_INFO) {
// memory sequence MMLs id in _lastSequenceHead.next.data
_lastSequenceHead.next.data = _regSequenceMMLStrings(_mmlString.substring(_headMMLIndex, _mmlRegExp.lastIndex));
}
addMMLEvent(MMLEvent.SEQUENCE_HEAD, 0);
if (_cacheMMLString) addMMLEvent(MMLEvent.DEBUG_INFO, -1);
// Returns true when it has to interrupt.
if (_interruptInterval == 0) return false;
return (_interruptInterval < (getTimer() - _startTime));
}
return false;
}
// tempo
static private function _tempo(t:int) : void
{
addMMLEvent(MMLEvent.TEMPO, t);
}
// errors
//--------------------------------------------------
static public function errorUnknown(n:String) : Error
{
return new Error("MMLParser Error : Unknown error #" + n + ".");
}
static public function errorNoteOutofRange(note:int) : Error
{
return new Error("MMLParser Error : Note #" + note + " is out of range.");
}
static public function errorSyntax(syn:String) : Error
{
return new Error("MMLParser Error : Syntax error '" + syn + "'.");
}
static public function errorRangeOver(cmd:String, min:int, max:int) : Error
{
return new Error("MMLParser Error : The parameter of '" + cmd + "' command must ragne from " + min + " to " + max + ".");
}
static public function errorStacUnderflow(cmd:String) : Error
{
return new Error("MMLParser Error : The stac of '" + cmd + "' command is underflow.");
}
static public function errorStacOverflow(cmd:String) : Error
{
return new Error("MMLParser Error : The stac of '" + cmd + "' command is overflow.");
}
static public function errorKeySign(ksign:String) : Error
{
return new Error("MMLParser Error : Cannot recognize '" + ksign + "' as a key signiture.");
}
}