PC-98のロマンをあえてぶち壊しにしてみるテスト
FM音源風?
実際やってみてあの刻みの意味がよくわかった。
耳がもげそう
// forked from nemu90kWw's PC-98のBEEP音のアレ forked from: TinySiOPM MML workbench
// forked from keim_at_Si's TinySiOPM MML workbench
// MML Editor
// - shift+Enter で play/stop
// - 最下段で再生開始ポジションを指定
//------------------------------------------------------------
// BGMLIBを移植してみた。でもなんか違う・・・。
// 本物はBio_100%のサイトで聴けるよ。
package {
import flash.media.*;
import flash.display.*;
import flash.events.*;
import flash.text.*;
[SWF(width="465", height="465", backgroundColor="0x000000", frameRate="30")]
public class MMLEditor extends Sprite
{
private var _sound:Sound;
private var _channel:SoundChannel;
private var _module:Beep;
private var _sequencer:Sequencer;
private var _textField:TextField;
private var _statusField:TextField;
private var _position:TextField;
private var _track:TextField;
function MMLEditor() {
var format:TextFormat = new TextFormat("MS Gothic", 12, 0x80f0c0);
_textField = new TextField();
_textField.width = 465;
_textField.height = 449;
_textField.multiline = true;
_textField.type = "input";
_textField.defaultTextFormat = format;
addChild(_textField);
format.color = 0;
_statusField= new TextField();
_statusField.x = 0;
_statusField.y = 449;
_statusField.width = 465;
_statusField.height = 16;
_statusField.background = true;
_statusField.backgroundColor = 0x80f0c0;
_statusField.defaultTextFormat = format;
_statusField.text = "position:";
addChild(_statusField);
_position = new TextField();
_position.x = 60;
_position.y = 449;
_position.type = "input";
_position.defaultTextFormat = format;
_position.text = "0";
addChild(_position);
stage.addEventListener("keyDown", _onKeyDown);
var mml:String = "";
mml += ";=======================================\n";
mml += ";= 爆笑超深どんとこい =\n";
mml += ";= -------------------- =\n";
mml += ";= 1991 (C) T.Ichikawa =\n";
mml += ";= ALL RIGHTS RESERVED. =\n";
mml += ";=======================================\n";
mml += "T150O2L16N0 ,O2L16N0 ,O3L16N0\n";
mml += "ERERERERERERERERERERERERERERERER ,R2GRGRGRGRGRGRGRGRBRBRBRBR ,R1BRBRBRBR>ERERERER<\n";
mml += "ERERERERERERERERERERERERERERERERERERERERERERERER ,BRBRBRBR>ERERERERGRGRGRGRBRBRBRBRBRBRBRBRBRBRBRBR< ,>GRGRGRGRBRBRBRBR>ERERERERERERERERERERERERERERERER<<\n";
mml += "L8O2E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,L8N1O5E2.EFF2G.F.D ,L8N1O4B2.B>CC2D.C.<A\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,E2.EDD1 ,B2.BAA2>>C<B>C16D.<<\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,>E2.EFF2G.F.D< ,B2.B>CC2D.C.<A\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,>E2.EDD1< ,B2.BAA1\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,L16GAGF+L8E2REF.A.>CC<B>CD< ,R1R1\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,>E1E2D4.<A ,R1R1\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,B2.B>CC1< ,O4L16GAGF+L8E2REF.A.>CC<B>CD<\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,B1B2A2 ,>E1E2D2<\n";
mml += "E>E<E>E<G>G<G>G<F+>F+<F+>F+<F>F<F>F< ,E2G2F+4.F4.G4 ,R1R1\n";
mml += "E>E<E>E<G>G<G>G<F+>F+<F+>F+<F>F<F>F< ,E2G2F+4.A4.D4 ,R1R1\n";
mml += "E>E<E>E<G>G<G>G<F+>F+<F+>F+<F>F<F>F< ,E2G2F+4.F4.G4 ,B2>E2D4.C4.D4<\n";
mml += "E>E<E>E<G>G<G>G<A>A<A>A<<B>B<B>B ,E2G2A4.B4RB4 ,B2>D2E4.E-4R<B4\n";
mml += "C>C<C>C<C>C<C>C<C>C<C>C<C>C<C>C< ,>C.<B.E2.>EREREDC< ,>C.<B.E2.>E2EDC<\n";
mml += "G>G<G>G<G>G<G>G<G>G<G>G<G>G<G>G< ,B.A.GG1GRG4 ,B.A.GG1GRG4\n";
mml += "F>F<F>F<F>F<F>F<F>F<F>F<F>F<F>F< ,A.G.F2.>CRCRC<BA ,A.G.F2.>C2C<BA\n";
mml += "B>B<B>B<B>B<B>B<B>B<B>B<B>B<B>B< ,B1B2BRB4 ,B1B2BRB4\n";
mml += "C>C<C>C<C>C<C>C<C>C<C>C<C>C<C>C< ,>C.<B.E2.>EREREDC< ,>C.<B.E2.>E2EDC<\n";
mml += "G>G<G>G<G>G<G>G<G>G<G>G<G>G<G>G< ,B.A.GG1GRG4 ,B.A.GG1GRG4\n";
mml += "F+>F+<F+>F+<F+>F+<F+>F+<F+>F+<F+>F+<F+>F+<F+>F+< ,A.G.F+2.>CRCRCDE< ,A.G.F+2.>C2CDE<\n";
mml += "B>B<B>B<B>B<B>B<B>B<B>B<B>B<B>B< ,>E-2E-<B>E-F+F+1< ,>E-2E-<B>E-F+F+1<\n";
mml += "L8O2E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,L8N1O5E2.EFF2G.F.D ,L8N1O4B2.B>CC2D.C.<A\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,E2.EDD1 ,B2.BAA2>>C<B>C16D.<<\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,>E2.EFF2G.F.D< ,B2.B>CC2D.C.<A\n";
mml += "E>E<E>E<E>E<EF4>F<F>F<F>F<F>F< ,>E2.EDD1< ,B2.BAA1\n";
mml += ";<END FLAG>--------------------------------------------------------------------\n";
_textField.text = mml;
_module = new Beep(2048, _onSoundFrame);
_sequencer = new Sequencer(_textField.text);
_sound = new Sound();
_sound.addEventListener("sampleData", _onStream);
_channel = _sound.play();
}
private function _onKeyDown(e:KeyboardEvent) : void {
if (e.shiftKey && e.keyCode == 13) {
if (_channel) {
_channel.stop();
_channel = null;
} else {
_module.reset();
_sequencer.mml = _textField.text;
_sequencer.pos = int(_position.text);
_channel = _sound.play();
}
}
}
private function _onSoundFrame() : void {
_sequencer.onSoundFrame();
}
private function _onStream(e:SampleDataEvent) : void {
var moduleOut:Vector.<Number> = _module.render();
for (var i:int = 0; i<4096; i++) {
e.data.writeFloat(moduleOut[i]);
}
}
}
}
import flash.events.*;
import flash.utils.*;
import flash.media.*;
class Sequencer
{
public static var tracks:Array;
public static var freq:int = 44100;
public static var tempo:Number = 120;
public static function get tick():Number {return 7.5 / tempo * freq;}
public static var tck:Number = 1;
function Sequencer(mml:String = null)
{
this.mml = mml;
}
public function onSoundFrame() : void {
for each (var tr:Track in tracks) tr.execute();
return;
}
public function set pos(p:int) : void {
for (var i:int=0; i<p; i++) {
for each (var tr:Track in tracks) tr.execute();
}
}
public function set mml(seq:String) : void
{
seq = seq.replace(/[ \t]|;.*$/gm, "");
var list:Array = seq.split("\r");
var res:Array = new Array("", "", "");
for(var i:int = 0; i < list.length; i++)
{
var temp:Array = list[i].split(",");
for(var t:int = 0; t < temp.length; t++)
{
if(t == 3) {break;}
res[t] += temp[t];
}
}
tracks = new Array();
tracks.push(new Track(res[0]));
tracks.push(new Track(res[1]));
tracks.push(new Track(res[2]));
tck = 1;
for(var i:int = 0; i < tracks.length; i++)
{
if(tracks[i].currentNote.oct == 0xFF) {break;}
var pitch:Number = freq / (Math.pow(2, tracks[i].currentNote.key / 12) * Math.pow(2, tracks[i].currentNote.oct+5));
tck *= pitch;
}
}
}
class Track
{
private var codeA:int="A".charCodeAt();
private var nt:Array=[9,11,0,2,4,5,7];
private var lentable:Array = new Array();
private var notelist:Array;
private var oct:int;
private var len:int;
private var tenuto:int;
private var pos:int;
private var cnt:Number;
public var currentNote:Note;
private var _rex:RegExp=/([A-GRTLON<>])([-+])?(-?\d+)?(\.*\.)?/gi;
function Track(seq:String)
{
lentable[32] = 1;
lentable[16] = 2;
lentable[8] = 4;
lentable[4] = 8;
lentable[2] = 16;
lentable[1] = 32;
lentable[24] = 1.5;
lentable[12] = 3;
lentable[6] = 6;
lentable[3] = 12;
var note:Note;
notelist = new Array();
cnt = 0;
pos = 0;
oct = 5;
len = 4;
tenuto = 1;
for (var res:* = _rex.exec(seq); res; res = _rex.exec(seq))
{
res[1] = res[1].toUpperCase();
var cmd:int = res[1].charCodeAt();
if (cmd>=codeA && cmd<=codeA+6)
{
note = new Note();
note.key = nt[cmd-codeA];
if(res[2] == "-") {note.key--;}
if(res[2] == "+") {note.key++;}
note.oct = oct;
note.len = lentable[(res[3]) ? int(res[3]) : len] * Sequencer.tick;
if(res[4]) {
var l:Number = note.len;
for(var i:int = 0; i < res[4].length; i++)
{
l /= 2;
note.len += l;
}
}
notelist.push(note);
}
else if (res[1] == 'R') {
note = new Note();
note.key = 0xFF;
note.oct = 0xFF;
note.len = lentable[(res[3]) ? int(res[3]) : len] * Sequencer.tick;
if(res[4]) {
var l:Number = note.len;
for(var i:int = 0; i < res[4].length; i++)
{
l /= 2;
note.len += l;
}
}
notelist.push(note);
}
else {
switch(res[1]){
case 'T': Sequencer.tempo = int(res[3]); break;
case 'L': len = int(res[3]); break;
case 'O': oct = int(res[3]); break;
case 'N': tenuto = int(res[3]); break;
case '<': oct--; break;
case '>': oct++; break;
}
}
}
currentNote = notelist[0];
}
public function execute() : void
{
cnt++;
if (cnt >= currentNote.len)
{
cnt -= currentNote.len;
pos++;
if(pos == notelist.length) {
pos = 0;
}
currentNote = notelist[pos];
}
}
}
class Note
{
public var key:int;
public var oct:int;
public var len:int;
}
class Beep
{
private var _output:Vector.<Number>;
private var _bufferSize:int;
private var _onSoundFrame:Function;
private var phase:Number = 0;
public var pitch:Number = 0;
function Beep(bufferSize:int=2048, onSoundFrame:Function=null)
{
_bufferSize = bufferSize;
_onSoundFrame = onSoundFrame;
_output = new Vector.<Number>(bufferSize*2, true);
}
public function reset() : void {
phase = 0;
}
public function render():Vector.<Number>
{
for (var i:int=0; i < _bufferSize; i++)
{
if(_onSoundFrame != null) _onSoundFrame();
var track:Track;
var num:int;
if(phase % Sequencer.tck*3 < Sequencer.tck) {
num = 0;
}
else if(phase % Sequencer.tck*3 < Sequencer.tck*2){
num = 1;
}
else {
num = 2;
}
for (var j:int = 0; j < 3; j++)
{
track = Sequencer.tracks[num];
if(track.currentNote.oct != 0xFF) {break;}
num--;
if(num == -1) {num = 2;}
}
if(j==3)
{
_output[i*2] = 0;
_output[i*2+1] = 0;
phase++;
continue;
}
pitch = Math.pow(2, track.currentNote.key / 12) * Math.pow(2, track.currentNote.oct+5) / Sequencer.freq;
_output[i*2] = Math.sin(Math.PI * pitch * phase) < 0 ? 0.1 : -0.1;
_output[i*2+1] = Math.sin(Math.PI * pitch * phase) < 0 ? 0.1 : -0.1;
phase++;
}
return _output;
}
}