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

Revision 2157, 19.8 kB (checked in by tekisuke, 5 years ago)

lang/actionscript/flmml/:
繰り返しのバグも修正。
http://d.hatena.ne.jp/tekisuke/20071119

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                        var l:int = newstr.length;
344                        newstr += m_string.substring(m_letter);
345                        m_string = newstr;
346                        m_letter = l;
347                        nest--;
348                    }
349                    break;
350                default:
351                    break;
352                }
353            }
354            if (nest >= 0) warning(MWarning.UNCLOSED_REPEAT, "");
355        }
356
357        protected function getIndex(idArr:Array, id:String):int {
358            for(var i:int = 0; i < idArr.length; i++)
359                if (((String)(idArr[i])) == id) return i;
360            return -1;
361        }
362
363        protected function insertLenOrder(idArr:Array, valArr:Array, id:String, val:String):void {
364            var len:int = id.length;
365            var i:int;
366            for(i = 0; i < idArr.length; i++) {
367                if (len >= ((String)(idArr[i])).length) break;
368            }
369            idArr.splice(i, 0, id);
370            valArr.splice(i, 0, val);
371        }
372
373        protected function replaceMacro(idArr:Array, valArr:Array):void {
374            var i:int;
375            for(i = 0; i < idArr.length; i++) {
376                var id:String = idArr[i];
377                if (m_string.substr(m_letter, id.length) == idArr[i]) {
378                    //trace("["+m_string.substr(m_letter, id.length)+"]");
379                    m_string = m_string.substring(0, m_letter-1) + valArr[i] + m_string.substring(m_letter + id.length);
380                    m_letter += id.length;
381                    break;
382                }
383            }
384        }
385
386        protected function macroInMacro(str:String, idArr:Array, valArr:Array):String {
387            for(var i:int = 0; i < idArr.length; i++) {
388                var id:String = "$"+idArr[i];
389                //trace("id:"+id);
390                var idx:int = str.indexOf(id);
391                while(idx >= 0) {
392                    str = str.substring(0, idx) + valArr[i] + str.substring(idx + id.length);
393                    idx = str.indexOf(id, idx);
394                }
395            }
396            return str;
397        }
398
399        protected function processMacro(str:String):void {
400            m_string = str;
401            begin();
402            var top:int = 1;
403            var idArr:Array = new Array();
404            var valArr:Array = new Array();
405            var i:int;
406            // [a-zA-Z][a-zA-Z0-9#\+\(\)_]*
407            // ex.) $Am    =/:4a<ce>a:/;
408            //      $EonG# =/:4g#b<e>b:/;
409            //      $Gm    =/:4gb-<d>b-:/;
410            //      $DonF# =/:4f#a<d>a:/;
411            //      $Am $EonG# $Gm $DonF#
412            while(m_letter < m_string.length) {
413                var c:String = getCharNext();
414                switch(c) {
415                case '$':
416                    // macro definition?
417                    if (top) {
418                        var last:int = m_string.indexOf(";", m_letter);
419                        if (last > m_letter) {
420                            var macro:String = m_string.substring(m_letter, last);
421                            var token:Array = macro.split("=");
422                            if (token.length >= 2 &&
423                                token[0].length >= 1) {
424                                var id:Array = token[0].match("[a-zA-Z_][a-zA-Z_0-9#\+\(\)]*");
425                                //trace(token[0], id);
426                                if (id != null) {
427                                    //trace("macro:$"+id[0]+"="+token[1]);
428                                    if (id[0].length > 0) {
429                                        m_string = remove(m_string, --m_letter, last);
430                                        var idx:int = getIndex(idArr, id[0]);
431                                        // first definition
432                                        if (idx < 0) {
433                                            token[1] = macroInMacro(token[1], idArr, valArr);
434                                            //trace("define $"+id[i]+"="+token[1]);
435                                            insertLenOrder(idArr, valArr, id[i], token[1]);
436                                        }
437                                        // macro redefinition
438                                        else {
439                                            token[1] = macroInMacro(token[1], idArr, valArr);
440                                            //trace("redefine $"+id[i]+"="+token[1]);
441                                            valArr[idx] = token[1];
442                                        }
443                                    }
444                                }
445                            }
446                            // macro use
447                            else {
448                                replaceMacro(idArr, valArr);
449                            }
450                        }
451                        // macro use
452                        else {
453                            replaceMacro(idArr, valArr);
454                        }
455                    }
456                    // macro use
457                    else {
458                        replaceMacro(idArr, valArr);
459                    }
460                    break;
461                case ';':
462                    top = 1;
463                    break;
464                default:
465                    if (!StringUtil.isWhitespace(c) && c != ' ') top = 0;
466                    break;
467                }
468            }
469        }
470
471        static public function removeWhitespace(str:String):String {
472            return str.replace(new RegExp("[  \n\r\t\f]+","g"),"");
473        }
474
475        static public function remove(str:String, start:int, end:int):String {
476            return str.substring(0, start) + str.substring(end+1);
477        }
478
479        public function play(str:String):void {
480            m_sequencer.disconnectAll();
481            m_tracks = new Array();
482            m_tracks[0] = createTrack();
483            m_sequencer.connect(m_tracks[MTrack.TEMPO_TRACK]);
484            m_tracks[1] = createTrack();
485            m_sequencer.connect(m_tracks[MTrack.FIRST_TRACK]);
486            m_warning = new String();
487
488            m_trackNo = MTrack.FIRST_TRACK;
489            m_octave = 4;
490            m_velocity = 100;
491            m_length = 4;
492            m_tempo  = 120;
493            m_keyoff = 1;
494            m_gate = 15;
495            m_maxGate = 16;
496            m_form = MOscillator.PULSE;
497
498            processMacro(str);
499            //trace(m_string);
500            m_string = removeWhitespace(m_string);
501            processRepeat();
502            //trace(m_string);
503            process();
504
505            // omit
506            if (m_tracks[m_tracks.length-1].getNumEvents() == 0) m_tracks.pop();
507
508            // conduct
509            m_tracks[MTrack.TEMPO_TRACK].conduct(m_tracks);
510
511            // post process
512            for(var i:int = MTrack.TEMPO_TRACK; i < m_tracks.length; i++) {
513                m_tracks[i].recRest(384);
514                m_tracks[i].recEOT();
515            }
516
517            // dispatch event
518            dispatchEvent(new MMLEvent(MMLEvent.COMPILE_COMPLETE, false, false, 0, 0));
519
520            // play start
521            m_sequencer.play();
522        }
523
524        public function stop():void {
525            m_sequencer.stop();
526        }
527
528        public function setMasterVolume(vol:int):void {
529            m_sequencer.setMasterVolume(vol);
530        }
531
532        public function getGlobalTick():uint {
533            return m_sequencer.getGlobalTick();
534        }
535
536        public function isPlaying():Boolean {
537            return m_sequencer.isPlaying();
538        }
539    }
540}
Note: See TracBrowser for help on using the browser.