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

Revision 2151, 19.6 kB (checked in by tekisuke, 5 years ago)

lang/actionscript/flmml/:
前回は早まりました。マクロ問題、今度こそ修正。

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