root/lang/actionscript/flmml/trunk/src/com/txt_nifty/sketch/flmml/MML.as @ 14830

Revision 14830, 23.3 kB (checked in by tekisuke, 5 years ago)

lang/actionscript/flmml/: フィルタコマンド実装。

MChannel.as    フィルタ実装。
MEvent.as 終了イベントを追加。
MLPFilter.as MFilterに名前変更。
MML.as         フィルタと終了イベントを追加。
MOscillator.as  高速化。
MSequencer.as 終了処理を追加。
MStatus.as 終了処理を追加。
MTrack.as 終了処理を追加。
MFilter.as LPFとHPFを2種類ずつ追加。

Line 
1package com.txt_nifty.sketch.flmml {
2    import de.popforge.audio.output.*;
3    import flash.events.EventDispatcher;
4    import mx.utils.StringUtil;
5
6    public class MML extends EventDispatcher {
7        protected var m_sequencer:MSequencer;
8        protected var m_tracks:Array;
9        protected var m_string:String;
10        protected var m_trackNo:int;
11        protected var m_octave:int;
12        protected var m_relativeDir:Boolean; //
13        protected var m_velocity:int;        // default velocity
14        protected var m_length:int;          // default length
15        protected var m_tempo:int;
16        protected var m_letter:int;
17        protected var m_keyoff:int;
18        protected var m_gate:int;
19        protected var m_maxGate:int;
20        protected var m_form:int;
21        protected var m_warning:String;
22
23        public function MML() {
24            m_sequencer = new MSequencer();
25            var self:MML = this;
26            m_sequencer.addEventListener(MMLEvent.COMPLETE, function(e:MMLEvent):void {
27                    self.dispatchEvent(new MMLEvent(MMLEvent.COMPLETE));
28                });
29        }
30
31        public function set onSignal(func:Function):void {
32            // ex) function func(globalTick:uint, event:int):void {}
33            m_sequencer.onSignal = func;
34        }
35
36        public function setSignalInterval(interval:int):void {
37            m_sequencer.setSignalInterval(interval);
38        }
39
40        public function getWarnings():String {
41            return m_warning;
42        }
43
44        protected function warning(warnId:int, str:String):void {
45            m_warning += MWarning.getString(warnId, str) +"\n";
46        }
47
48        protected function len2tick(len:int):int {
49            if (len == 0) return m_length;
50            return 384/len;
51        }
52
53        protected function note(noteNo:int):void {
54            //trace("note"+noteNo);
55            noteNo += getKeySig();
56            var len:int;
57            len = getUInt(0);
58            var tick:int = len2tick(len);
59            tick = getDot(tick);
60            var keyon:int = (m_keyoff == 0) ? 0 : 1;
61            m_keyoff = 1;
62            if (getChar() == '&') { // tie
63                next();
64                m_keyoff = 0;
65            }
66            m_tracks[m_trackNo].recNote(noteNo + m_octave*12, tick, m_velocity, keyon, m_keyoff);
67        }
68
69        protected function rest():void {
70            //trace("rest");
71            var len:int;
72            len = getUInt(0);
73            var tick:int = len2tick(len);
74            tick = getDot(tick);
75            m_tracks[m_trackNo].recRest(tick);
76        }
77
78        protected function atmark():void {
79            var c:String = getChar();
80            var o:int = 1, a:int = 0, d:int = 64, s:int = 32, r:int = 0;
81            switch(c) {
82            case 'v': // Volume
83                next();
84                m_velocity = getUInt(m_velocity);
85                if (m_velocity > 127) m_velocity = 127;
86                break;
87            case 'e': // Envelope
88                next();
89                o = getUInt(o);
90                if (getChar() == ',') next();
91                a = getUInt(a);
92                if (getChar() == ',') next();
93                d = getUInt(d);
94                if (getChar() == ',') next();
95                s = getUInt(s);
96                if (getChar() == ',') next();
97                r = getUInt(r);
98                //trace("A"+a+",D"+d+",S"+s+",R"+r);
99                m_tracks[m_trackNo].recEnvelope(o, a, d, s, r);
100                break;
101            case 'n': // Noise frequency
102                next();
103                o = getUInt(0);
104                if (o < 0 || o > 127) o = 0;
105                m_tracks[m_trackNo].recNoiseFreq(o);
106                break;
107            case 'w': // pulse Width modulation
108                next();
109                o = getUInt(50);
110                if (o < 1) o = 1;
111                if (o > 99) o = 99;
112                m_tracks[m_trackNo].recPWM(o);
113                break;
114            case 'p': // Pan
115                next();
116                o = getUInt(64);
117                if (o < 1) o = 1;
118                if (o > 127) o = 127;
119                m_tracks[m_trackNo].recPan(o);
120                break;
121            case '\'': // formant filter
122                next();
123                o = m_string.indexOf('\'', m_letter);
124                if (o >= 0) {
125                    var vstr:String = m_string.substring(m_letter, o);
126                    var vowel:int = 0;
127                    switch(vstr) {
128                    case 'a': vowel = MFormant.VOWEL_A; break;
129                    case 'e': vowel = MFormant.VOWEL_E; break;
130                    case 'i': vowel = MFormant.VOWEL_I; break;
131                    case 'o': vowel = MFormant.VOWEL_O; break;
132                    case 'u': vowel = MFormant.VOWEL_U; break;
133                    default: vowel = -1; break;
134                    }
135                    m_tracks[m_trackNo].recFormant(vowel);
136                    m_letter = o + 1;
137                }
138                break;
139            case 'd': // Detune
140                next();
141                o = getSInt(0);
142                m_tracks[m_trackNo].recDetune(o);
143                break;
144            case 'l': // Low frequency oscillator (LFO)
145                {
146                    var dp:int = 0, wd:int = 0, fm:int = 1, rv:int = 1, dl:int = 0, tm:int = 0, cn:int = 0;
147                    next();
148                    dp = getUInt(dp);
149                    if (getChar() == ',') next();
150                    wd = getUInt(wd);
151                    if (getChar() == ',') {
152                        next();
153                        if (getChar() == '-') { rv = -1; next(); }
154                        fm = (getUInt(fm) + 1) * rv;
155                        if (getChar() == ',') {
156                            next();
157                            dl = getUInt(dl);
158                            if (getChar() == ',') {
159                                next();
160                                tm = getUInt(tm);
161                            }
162                        }
163                    }
164                    //trace("DePth"+dp+",WiDth"+wd+",ForM"+fm+",DeLay"+dl+",TiMe"+tm);
165                    m_tracks[m_trackNo].recLFO(dp, wd, fm, dl, tm);
166                }
167                break;
168            case 'f': // Filter
169                {
170                    var swt:int = 0, amt:int = 0, frq:int = 0, res:int = 0;
171                    next();
172                    swt = getSInt(swt);
173                    if (getChar() == ',') {
174                        next();
175                        amt = getSInt(amt);
176                        if (getChar() == ',') {
177                            next();
178                            frq = getUInt(frq);
179                            if (getChar() == ',') {
180                                next();
181                                res = getUInt(res);
182                            }
183                        }
184                    }
185                    m_tracks[m_trackNo].recLPF(swt, amt, frq, res);
186                }
187                break;
188            default:
189                m_form = getUInt(m_form);
190                m_tracks[m_trackNo].recForm(m_form);
191                break;
192            }
193        }
194
195        protected function firstLetter():void {
196            var c:String = getCharNext();
197            var i:int;
198            switch(c) {
199            case "c": note(0);  break;
200            case "d": note(2);  break;
201            case "e": note(4);  break;
202            case "f": note(5);  break;
203            case "g": note(7);  break;
204            case "a": note(9);  break;
205            case "b": note(11); break;
206            case "r": rest(); break;
207            case "o":
208                m_octave = getUInt(m_octave);
209                if (m_octave < -2) m_octave = -2;
210                if (m_octave >  8) m_octave =  8;
211                break;
212            case "v":
213                m_velocity = getUInt((m_velocity-7)/8) * 8 + 7;
214                if (m_velocity < 0)   m_velocity = 0;
215                if (m_velocity > 127) m_velocity = 127;
216                break;
217            case "l":
218                m_length = len2tick(getUInt(0));
219                m_length = getDot(m_length);
220                break;
221            case "t":
222                m_tempo = getUInt(m_tempo);
223                if (m_tempo == 0) m_tempo = 1;
224                m_tracks[MTrack.TEMPO_TRACK].recTempo(m_tracks[m_trackNo].getRecGlobalTick(), m_tempo);
225                break;
226            case "q":
227                m_gate = getUInt(m_gate);
228                m_tracks[m_trackNo].recGate(m_gate / m_maxGate);
229                break;
230            case "<" :
231                if (m_relativeDir) m_octave++; else m_octave--;
232                break;
233            case ">":
234                if (m_relativeDir) m_octave--; else m_octave++;
235                break;
236            case ';':
237                //trace("nexttrack");
238                if (m_tracks[m_trackNo].getNumEvents() > 0) {
239                    m_tracks[++m_trackNo] = createTrack();
240                }
241                break;
242            case '@':
243                atmark();
244                break;
245            default:
246                {
247                    var cc:int = c.charCodeAt(0);
248                    if (cc < 128)
249                        warning(MWarning.UNKNOWN_COMMAND, c);
250                }
251                break;
252            }
253        }
254
255        protected function getCharNext():String {
256            return (m_letter < m_string.length) ? m_string.charAt(m_letter++) : '';
257        }
258
259        protected function getChar():String {
260            return (m_letter < m_string.length) ? m_string.charAt(m_letter) : '';
261        }
262
263        protected function next(i:int = 1):void {
264            m_letter += 1;
265        }
266
267        protected function getKeySig():int {
268            var k:int = 0;
269            var f:int = 1;
270            while(f) {
271                var c:String = getChar();
272                switch(c) {
273                case "+": case "#": k++; next(); break;
274                case "-":           k--; next(); break;
275                default: f = 0; break;
276                }
277            }
278            return k;
279        }
280
281        protected function getUInt(def:int):int {
282            var ret:int = 0;
283            var l:int = m_letter;
284            var f:int = 1;
285            while(f) {
286                var c:String = getChar();
287                switch(c) {
288                case '0': ret = ret * 10 + 0; next(); break;
289                case '1': ret = ret * 10 + 1; next(); break;
290                case '2': ret = ret * 10 + 2; next(); break;
291                case '3': ret = ret * 10 + 3; next(); break;
292                case '4': ret = ret * 10 + 4; next(); break;
293                case '5': ret = ret * 10 + 5; next(); break;
294                case '6': ret = ret * 10 + 6; next(); break;
295                case '7': ret = ret * 10 + 7; next(); break;
296                case '8': ret = ret * 10 + 8; next(); break;
297                case '9': ret = ret * 10 + 9; next(); break;
298                default: f = 0; break;
299                }
300            }
301            return (m_letter == l) ? def : ret;
302        }
303
304        protected function getSInt(def:int):int {
305            var c:String = getChar();
306            var s:int = 1;
307            if      (c == '-') { s = -1; next(); }
308            else if (c == '+') next();
309            return getUInt(def) * s;
310        }
311
312        protected function getDot(tick:int):int {
313            var c:String = getChar();
314            var intick:int = tick;
315            while(c == '.') {
316                next();
317                intick /= 2;
318                tick += intick;
319                c = getChar();
320            }
321            return tick;
322        }
323
324        public function createTrack():MTrack {
325            m_octave = 4;
326            m_velocity = 100;
327            return new MTrack();
328        }
329
330        protected function begin():void {
331            m_letter = 0;
332        }
333
334        protected function process():void {
335            begin();
336            while(m_letter < m_string.length) {
337                firstLetter();
338            }
339        }
340
341        protected function processRepeat():void {
342            m_string = m_string.toLowerCase();
343            begin();
344            var repeat:Array = new Array();
345            var origin:Array = new Array();
346            var start:Array = new Array();
347            var last:Array = new Array();
348            var nest:int = -1;
349            while(m_letter < m_string.length) {
350                var c:String = getCharNext();
351                switch(c) {
352                case '/':
353                    if (getChar() == ':') {
354                        next();
355                        origin[++nest] = m_letter - 2;
356                        repeat[nest] = getUInt(2);
357                        start[nest] = m_letter;
358                        last[nest] = -1;
359                    }
360                    else if (nest >= 0) {
361                        last[nest] = m_letter - 1;
362                        m_string = m_string.substring(0, m_letter-1) + m_string.substring(m_letter);
363                        m_letter--;
364                    }
365                    else {
366                    }
367                    break;
368                case ':':
369                    if (getChar() == '/' && nest >= 0) {
370                        next();
371                        var contents:String = m_string.substring(start[nest], m_letter - 2);
372                        var newstr:String = m_string.substring(0, origin[nest]);
373                        for (var i:int = 0; i < repeat[nest]; i++) {
374                            if (i < repeat[nest]-1 || last[nest] < 0) newstr += contents;
375                            else newstr += m_string.substring(start[nest], last[nest]);
376                        }
377                        var l:int = newstr.length;
378                        newstr += m_string.substring(m_letter);
379                        m_string = newstr;
380                        m_letter = l;
381                        nest--;
382                    }
383                    break;
384                default:
385                    break;
386                }
387            }
388            if (nest >= 0) warning(MWarning.UNCLOSED_REPEAT, "");
389        }
390
391        protected function getIndex(idArr:Array, id:String):int {
392            for(var i:int = 0; i < idArr.length; i++)
393                if (((String)(idArr[i])) == id) return i;
394            return -1;
395        }
396
397        protected function insertLenOrder(idArr:Array, valArr:Array, id:String, val:String):void {
398            var len:int = id.length;
399            var i:int;
400            for(i = 0; i < idArr.length; i++) {
401                if (len >= ((String)(idArr[i])).length) break;
402            }
403            idArr.splice(i, 0, id);
404            valArr.splice(i, 0, val);
405        }
406
407        protected function replaceMacro(idArr:Array, valArr:Array):void {
408            var i:int;
409            for(i = 0; i < idArr.length; i++) {
410                var id:String = idArr[i];
411                if (m_string.substr(m_letter, id.length) == idArr[i]) {
412                    //trace("["+m_string.substr(m_letter, id.length)+"]");
413                    m_string = m_string.substring(0, m_letter-1) + valArr[i] + m_string.substring(m_letter + id.length);
414                    m_letter += id.length;
415                    break;
416                }
417            }
418        }
419
420        protected function macroInMacro(str:String, idArr:Array, valArr:Array):String {
421            for(var i:int = 0; i < idArr.length; i++) {
422                var id:String = "$"+idArr[i];
423                //trace("id:"+id);
424                var idx:int = str.indexOf(id);
425                while(idx >= 0) {
426                    str = str.substring(0, idx) + valArr[i] + str.substring(idx + id.length);
427                    idx = str.indexOf(id, idx);
428                }
429            }
430            return str;
431        }
432
433        protected function processMacro():void {
434            begin();
435            var top:int = 1;
436            var idArr:Array = new Array();
437            var valArr:Array = new Array();
438            var i:int;
439            var last:int;
440            // [a-zA-Z][a-zA-Z0-9#\+\(\)_]*
441            // ex.) $Am    =/:4a<ce>a:/;
442            //      $EonG# =/:4g#b<e>b:/;
443            //      $Gm    =/:4gb-<d>b-:/;
444            //      $DonF# =/:4f#a<d>a:/;
445            //      $Am $EonG# $Gm $DonF#
446            while(m_letter < m_string.length) {
447                var c:String = getCharNext();
448                switch(c) {
449                case '$':
450                    // macro definition?
451                    if (top) {
452                        last = m_string.indexOf(";", m_letter);
453                        if (last > m_letter) {
454                            var macro:String = m_string.substring(m_letter, last);
455                            var token:Array = macro.split("=");
456                            if (token.length >= 2 &&
457                                token[0].length >= 1) {
458                                var id:Array = token[0].match("[a-zA-Z_][a-zA-Z_0-9#\+\(\)]*");
459                                //trace("token:"+token[0]+"/"+id);
460                                if (id != null) {
461                                    //trace("macro:$"+id[0]+"="+token[1]);
462                                    if (id[0].length > 0) {
463                                        m_string = remove(m_string, --m_letter, last);
464                                        var idx:int = getIndex(idArr, id[0]);
465                                        // first definition
466                                        if (idx < 0) {
467                                            token[1] = macroInMacro(token[1], idArr, valArr);
468                                            //trace("define $"+id[i]+"="+token[1]);
469                                            insertLenOrder(idArr, valArr, id[i], token[1]);
470                                        }
471                                        // macro redefinition
472                                        else {
473                                            token[1] = macroInMacro(token[1], idArr, valArr);
474                                            //trace("redefine $"+id[i]+"="+token[1]);
475                                            valArr[idx] = token[1];
476                                        }
477                                    }
478                                }
479                            }
480                            // macro use
481                            else {
482                                replaceMacro(idArr, valArr);
483                                top = 0;
484                            }
485                        }
486                        // macro use
487                        else {
488                            replaceMacro(idArr, valArr);
489                            top = 0;
490                        }
491                    }
492                    // macro use
493                    else {
494                        replaceMacro(idArr, valArr);
495                        top = 0;
496                    }
497                    break;
498                case ';':
499                    top = 1;
500                    break;
501                default:
502                    if (!StringUtil.isWhitespace(c) && c != ' ') top = 0;
503                    break;
504                }
505            }
506            //
507            var exp:RegExp = /[\n\r\f]*#OCTAVE\s+REVERSE\s*[\n\r\f]/;
508            if (m_string.match(exp)) {
509                m_string = m_string.replace(exp, "");
510                m_relativeDir = false;
511            }
512        }
513
514        protected function processComment(str:String):void {
515            m_string = str;
516            begin();
517            var commentStart:int = -1;
518            while(m_letter < m_string.length) {
519                var c:String = getCharNext();
520                switch(c) {
521                case '/':
522                    if (getChar() == '*') {
523                        if (commentStart < 0) commentStart = m_letter - 1;
524                        next();
525                    }
526                    break;
527                case '*':
528                    if (getChar() == '/') {
529                        if (commentStart >= 0) {
530                            m_string = remove(m_string, commentStart, m_letter);
531                            m_letter = commentStart;
532                            commentStart = -1;
533                        }
534                        else {
535                            warning(MWarning.UNOPENED_COMMENT, "");
536                        }
537                    }
538                    break;
539                default:
540                    break;
541                }
542            }
543            if (commentStart >= 0) warning(MWarning.UNCLOSED_COMMENT, "");
544        }
545
546        static public function removeWhitespace(str:String):String {
547            return str.replace(new RegExp("[  \n\r\t\f]+","g"),"");
548        }
549
550        static public function remove(str:String, start:int, end:int):String {
551            return str.substring(0, start) + str.substring(end+1);
552        }
553
554        public function play(str:String):void {
555            if (m_sequencer.isPaused()) {
556                m_sequencer.play();
557                return;
558            }
559            m_sequencer.disconnectAll();
560            m_tracks = new Array();
561            m_tracks[0] = createTrack();
562            m_tracks[1] = createTrack();
563            m_warning = new String();
564
565            m_trackNo = MTrack.FIRST_TRACK;
566            m_octave = 4;
567            m_relativeDir = true;
568            m_velocity = 100;
569            m_length = len2tick(4);
570            m_tempo  = 120;
571            m_keyoff = 1;
572            m_gate = 15;
573            m_maxGate = 16;
574            m_form = MOscillator.PULSE;
575
576            processComment(str);
577            //trace(m_string+"\n\n");
578            processMacro();
579            //trace(m_string);
580            m_string = removeWhitespace(m_string);
581            processRepeat();
582            //trace(m_string);
583            process();
584
585            // omit
586            if (m_tracks[m_tracks.length-1].getNumEvents() == 0) m_tracks.pop();
587
588            // conduct
589            m_tracks[MTrack.TEMPO_TRACK].conduct(m_tracks);
590
591            // post process
592            for(var i:int = MTrack.TEMPO_TRACK; i < m_tracks.length; i++) {
593                m_tracks[i].recRest(384);
594                m_tracks[i].recClose();
595                m_tracks[i].recRest(96);
596                m_tracks[i].recEOT();
597                m_sequencer.connect(m_tracks[i]);
598            }
599
600            // dispatch event
601            dispatchEvent(new MMLEvent(MMLEvent.COMPILE_COMPLETE, false, false, 0, 0));
602
603            // play start
604            m_sequencer.play();
605        }
606
607        public function stop():void {
608            m_sequencer.stop();
609        }
610
611        public function pause():void {
612            m_sequencer.pause();
613        }
614
615        public function resume():void {
616            m_sequencer.play();
617        }
618
619        public function setMasterVolume(vol:int):void {
620            m_sequencer.setMasterVolume(vol);
621        }
622
623        public function getGlobalTick():uint {
624            return m_sequencer.getGlobalTick();
625        }
626
627        public function isPlaying():Boolean {
628            return m_sequencer.isPlaying();
629        }
630
631        public function isPaused():Boolean {
632            return m_sequencer.isPaused();
633        }
634    }
635}
Note: See TracBrowser for help on using the browser.