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

Revision 38797, 47.5 kB (checked in by tekisuke, 2 years ago)

@aloerinさんの代理コミット。
#USING POLYによるポリフォニック(複数発音)指定が追加されました。
その他、#TITLE, #COMMENT, #ARTIST, #CODINGなど各種情報を埋め込めるようになりました。

Line 
1package com.txt_nifty.sketch.flmml {
2    import flash.events.EventDispatcher;
3    import flash.utils.*;
4   
5    import mx.utils.StringUtil;
6
7    public class MML extends EventDispatcher {
8        protected var m_sequencer:MSequencer;
9        protected var m_tracks:Array;
10        protected var m_string:String;
11        protected var m_trackNo:int;
12        protected var m_octave:int;
13        protected var m_relativeDir:Boolean; //
14        protected var m_velocity:int;        // default velocity
15        protected var m_velDetail:Boolean;
16        protected var m_velDir:Boolean;
17        protected var m_length:int;          // default length
18        protected var m_tempo:Number;
19        protected var m_letter:int;
20        protected var m_keyoff:int;
21        protected var m_gate:int;
22        protected var m_maxGate:int;
23        protected var m_form:int;
24        protected var m_noteShift:int;
25        protected var m_warning:String;
26        protected var m_maxPipe:int;
27        protected var m_maxSyncSource:int;
28                protected var m_beforeNote:int;
29                protected var m_portamento:int;
30                protected var m_usingPoly:Boolean;
31                protected var m_polyVoice:int;
32                protected var m_polyForce:Boolean;
33                protected var m_metaTitle:String;
34                protected var m_metaArtist:String;
35                protected var m_metaCoding:String;
36                protected var m_metaComment:String;
37        protected static var MAX_PIPE:int = 3;
38        protected static var MAX_SYNCSOURCE:int = 3;
39                protected static var MAX_POLYVOICE:int = 64;
40
41        public function MML() {
42            m_sequencer = new MSequencer();
43            var self:MML = this;
44            m_sequencer.addEventListener(MMLEvent.COMPLETE, function(e:MMLEvent):void {
45                    m_sequencer.stop();
46                    self.dispatchEvent(new MMLEvent(MMLEvent.COMPLETE));
47                });
48            m_sequencer.addEventListener(MMLEvent.BUFFERING, function(e:MMLEvent):void {
49                    self.dispatchEvent(new MMLEvent(MMLEvent.BUFFERING, false, false, 0, 0, e.progress));
50                });
51        }
52
53        public function set onSignal(func:Function):void {
54            // ex) function func(globalTick:uint, event:int):void {}
55            m_sequencer.onSignal = func;
56        }
57
58        public function setSignalInterval(interval:int):void {
59            m_sequencer.setSignalInterval(interval);
60        }
61
62        public function getWarnings():String {
63            return m_warning;
64        }
65
66        protected function warning(warnId:int, str:String):void {
67            m_warning += MWarning.getString(warnId, str) +"\n";
68        }
69
70        protected function len2tick(len:int):int {
71            if (len == 0) return m_length;
72            return 384/len;
73        }
74
75        protected function note(noteNo:int):void {
76            //trace("note"+noteNo);
77            noteNo += m_noteShift + getKeySig();
78                        if (getChar() == '*') { // ポルタメント記号
79                                m_beforeNote = noteNo + m_octave * 12;
80                                m_portamento = 1;
81                                next();
82                        }
83                        else {
84                                var lenMode:int;
85                                var len:int;
86                                var tick:int = 0;
87                                var tickTemp:int;
88                                var tie:int = 0;
89                                var keyon:int = (m_keyoff == 0) ? 0 : 1;
90                                m_keyoff = 1;
91                                while (1) {
92                                        if (getChar() != '%') {
93                                                lenMode = 0;
94                                        }
95                                        else {
96                                                lenMode = 1;
97                                                next();
98                                        }
99                                        len = getUInt(0);
100                                        if (tie == 1 && len == 0) {
101                                                m_keyoff = 0;
102                                                break;
103                                        }
104                                        tickTemp = (lenMode ? len : len2tick(len));
105                                        tick += getDot(tickTemp);
106                                        tie = 0;
107                                        if (getChar() == '&') { // tie
108                                                tie = 1;
109                                                next();
110                                        }
111                                        else {
112                                                break;
113                                        }
114                                }
115                                if (m_portamento == 1) { // ポルタメントなら
116                                        m_tracks[m_trackNo].recPortamento(m_beforeNote - (noteNo + m_octave * 12), tick);
117                                }
118                                m_tracks[m_trackNo].recNote(noteNo + m_octave * 12, tick, m_velocity, keyon, m_keyoff);
119                                if (m_portamento == 1) { // ポルタメントなら
120                                        m_tracks[m_trackNo].recPortamento(0, 0);
121                                        m_portamento = 0;
122                                }
123                        }
124        }
125
126        protected function rest():void {
127            //trace("rest");
128                        var lenMode:int = 0;
129                        if (getChar() == '%') {
130                                lenMode = 1;
131                                next();
132                        }
133            var len:int;
134            len = getUInt(0);
135            var tick:int = lenMode ? len : len2tick(len);
136            tick = getDot(tick);
137            m_tracks[m_trackNo].recRest(tick);
138        }
139
140        protected function atmark():void {
141            var c:String = getChar();
142            var o:int = 1, a:int = 0, d:int = 64, s:int = 32, r:int = 0, sens:int = 0, mode:int = 0;
143            switch(c) {
144            case 'v': // Volume
145                m_velDetail = true;
146                next();
147                m_velocity = getUInt(m_velocity);
148                if (m_velocity > 127) m_velocity = 127;
149                break;
150            case 'x': // Expression
151                next();
152                o = getUInt(127);
153                if (o > 127) o = 127;
154                m_tracks[m_trackNo].recExpression(o);
155                break;
156            case 'e': // Envelope
157                {
158                        var releasePos:int;
159                        var t:Vector.<int> = new Vector.<int>(), l:Vector.<int> = new Vector.<int>();
160                        next();
161                        o = getUInt(o);
162                        if (getChar() == ',') next();
163                        a = getUInt(a);
164                        releasePos = m_letter;
165                        while(true){
166                                if (getChar() == ',') {
167                                        next();
168                                }else{
169                                        break;
170                                }
171                                releasePos = m_letter - 1;
172                        d = getUInt(d);
173                                if (getChar() == ',') {
174                                        next();
175                                }else{
176                                        m_letter = releasePos;
177                                        break;
178                                }
179                        s = getUInt(s);
180                        t.push(d);
181                        l.push(s);
182                    }
183                    if(t.length == 0){
184                        t.push(d);
185                        l.push(s);
186                    }
187                        if (getChar() == ',') next();
188                        r = getUInt(r);
189                        //trace("A"+a+",D"+d+",S"+s+",R"+r);
190                        m_tracks[m_trackNo].recEnvelope(o, a, t, l, r);
191                }
192                break;
193            case 'n': // Noise frequency
194                next();
195                if (getChar() == 's') { // Note Shift (relative)
196                    next();
197                    m_noteShift += getSInt(0);
198                }
199                else {
200                    o = getUInt(0);
201                    if (o < 0 || o > 127) o = 0;
202                    m_tracks[m_trackNo].recNoiseFreq(o);
203                }
204                break;
205            case 'w': // pulse Width modulation
206                next();
207                o = getSInt(50);
208                                if (o < 0) {
209                                        if (o > -1) o = -1;
210                                        if (o < -99) o = -99;
211                                }
212                                else {
213                                        if (o < 1) o = 1;
214                                        if (o > 99) o = 99;
215                                }
216                m_tracks[m_trackNo].recPWM(o);
217                break;
218            case 'p': // Pan
219                next();
220                                if (getChar() == 'l') { // poly mode
221                                        next();
222                                        o = getUInt(m_polyVoice);
223                                        o = Math.max(0, Math.min(m_polyVoice, o));
224                                        m_tracks[m_trackNo].recPoly(o);
225                                }
226                                else {
227                                        o = getUInt(64);
228                                        if (o < 1) o = 1;
229                                        if (o > 127) o = 127;
230                                        m_tracks[m_trackNo].recPan(o);
231                                }
232                break;
233            case '\'': // formant filter
234                next();
235                o = m_string.indexOf('\'', m_letter);
236                if (o >= 0) {
237                    var vstr:String = m_string.substring(m_letter, o);
238                    var vowel:int = 0;
239                    switch(vstr) {
240                    case 'a': vowel = MFormant.VOWEL_A; break;
241                    case 'e': vowel = MFormant.VOWEL_E; break;
242                    case 'i': vowel = MFormant.VOWEL_I; break;
243                    case 'o': vowel = MFormant.VOWEL_O; break;
244                    case 'u': vowel = MFormant.VOWEL_U; break;
245                    default: vowel = -1; break;
246                    }
247                    m_tracks[m_trackNo].recFormant(vowel);
248                    m_letter = o + 1;
249                }
250                break;
251            case 'd': // Detune
252                next();
253                o = getSInt(0);
254                m_tracks[m_trackNo].recDetune(o);
255                break;
256            case 'l': // Low frequency oscillator (LFO)
257                {
258                    var dp:int = 0, wd:int = 0, fm:int = 1, sf:int = 0, rv:int = 1, dl:int = 0, tm:int = 0, cn:int = 0, sw:int = 0;
259                    next();
260                    dp = getUInt(dp);
261                    if (getChar() == ',') next();
262                    wd = getUInt(wd);
263                    if (getChar() == ',') {
264                        next();
265                        if (getChar() == '-') { rv = -1; next(); }
266                        fm = (getUInt(fm) + 1) * rv;
267                    if (getChar() == '-') {
268                        next();
269                        sf = getUInt(0);
270                    }
271                        if (getChar() == ',') {
272                            next();
273                            dl = getUInt(dl);
274                            if (getChar() == ',') {
275                                next();
276                                tm = getUInt(tm);
277                                if (getChar() == ',') {
278                                        next();
279                                        sw = getUInt(sw);
280                                }
281                            }
282                        }
283                    }
284                    //trace("DePth"+dp+",WiDth"+wd+",ForM"+fm+",DeLay"+dl+",TiMe"+tm);
285                    m_tracks[m_trackNo].recLFO(dp, wd, fm, sf, dl, tm, sw);
286                }
287                break;
288            case 'f': // Filter
289                {
290                    var swt:int = 0, amt:int = 0, frq:int = 0, res:int = 0;
291                    next();
292                    swt = getSInt(swt);
293                    if (getChar() == ',') {
294                        next();
295                        amt = getSInt(amt);
296                        if (getChar() == ',') {
297                            next();
298                            frq = getUInt(frq);
299                            if (getChar() == ',') {
300                                next();
301                                res = getUInt(res);
302                            }
303                        }
304                    }
305                    m_tracks[m_trackNo].recLPF(swt, amt, frq, res);
306                }
307                break;
308            case 'q': // gate time 2
309                next();
310                m_tracks[m_trackNo].recGate2(getUInt(2) * 2); // '*2' according to TSSCP
311                break;
312                case 'i': // Input
313                {
314                        sens = 0;
315                    next();
316                    sens = getUInt(sens);
317                    if (getChar() == ',') {
318                        next();
319                        a = getUInt(a);
320                        if (a > m_maxPipe) a = m_maxPipe;
321                    }
322                    m_tracks[m_trackNo].recInput(sens, a);
323                }
324                // @i[n],[m]   m:pipe no
325                // if (n == 0) off
326                // else sensitivity = n (max:8)
327                break;
328            case 'o': // Output
329                {
330                    mode = 0;
331                    next();
332                    mode = getUInt(mode);
333                    if (getChar() == ',') {
334                        next();
335                        a = getUInt(a);
336                        if (a > m_maxPipe) {
337                            m_maxPipe = a;
338                            if (m_maxPipe >= MAX_PIPE) m_maxPipe = a = MAX_PIPE;
339                        }
340                    }
341                    m_tracks[m_trackNo].recOutput(mode, a);
342                }
343                // @o[n],[m]   m:pipe no
344                // if (n == 0) off
345                // if (n == 1) overwrite
346                // if (n == 2) add
347                break;
348            case 'r': // Ring
349                {
350                        sens = 0;
351                        next();
352                        sens = getUInt(sens);
353                        if (getChar() == ',') {
354                                next();
355                                a = getUInt(a);
356                                if (a > m_maxPipe) a = m_maxPipe;
357                        }
358                        m_tracks[m_trackNo].recRing(sens, a);
359                }
360                break;
361            case 's': // Sync
362                {
363                        mode = 0;
364                        next();
365                        mode = getUInt(mode);
366                        if (getChar() == ',') {
367                                next();
368                                a = getUInt(a);
369                                if (mode == 1) {
370                                // Sync out
371                                if (a > m_maxSyncSource) {
372                                        m_maxSyncSource = a;
373                                        if (m_maxSyncSource >= MAX_SYNCSOURCE) m_maxSyncSource = a = MAX_SYNCSOURCE;
374                                }
375                                } else if (mode == 2) {
376                                // Sync in
377                                if (a > m_maxSyncSource) a = m_maxSyncSource;
378                                }
379                        }
380                        m_tracks[m_trackNo].recSync(mode, a);
381                }
382                break;
383                        case 'u':       // midi風なポルタメント
384                                next();
385                                var rate:int;
386                                mode = getUInt(0);
387                                switch (mode) {
388                                        case 0:
389                                        case 1:
390                                                m_tracks[m_trackNo].recMidiPort(mode);
391                                                break;
392                                        case 2:
393                                                rate = 0;
394                                                if (getChar() == ',') {
395                                                        next();
396                                                        rate = getUInt(0);
397                                                        if (rate < 0) rate = 0;
398                                                        if (rate > 127) rate = 127;
399                                                }
400                                                m_tracks[m_trackNo].recMidiPortRate(rate * 1);
401                                                break;
402                                        case 3:
403                                                if (getChar() == ',') {
404                                                        next();
405                                                        var oct:int;
406                                                        var baseNote:int = -1;
407                                                        if (getChar() != 'o') {
408                                                                oct = m_octave;
409                                                        }
410                                                        else {
411                                                                next();
412                                                                oct = getUInt(0);
413                                                        }
414                                                        c = getChar();
415                                                        switch(c) {
416                                                                case 'c': baseNote = 0; break;
417                                                                case 'd': baseNote = 2; break;
418                                                                case 'e': baseNote = 4; break;
419                                                                case 'f': baseNote = 5; break;
420                                                                case 'g': baseNote = 7; break;
421                                                                case 'a': baseNote = 9; break;
422                                                                case 'b': baseNote = 11; break;
423                                                        }
424                                                        if (baseNote >= 0) {
425                                                                next();
426                                                                baseNote += m_noteShift + getKeySig();
427                                                                baseNote += oct * 12;
428                                                        }
429                                                        else {
430                                                                baseNote = getUInt(60);
431                                                        }
432                                                        if (baseNote < 0) baseNote = 0;
433                                                                if (baseNote > 127) baseNote = 127;
434                                                        m_tracks[m_trackNo].recPortBase(baseNote);
435                                                }
436                                                break;
437                                }
438                                break;
439            default:
440                m_form = getUInt(m_form);
441                a = 0;
442                if (getChar() == '-') {
443                    next();
444                    a = getUInt(0);
445                }
446                m_tracks[m_trackNo].recForm(m_form, a);
447                break;
448            }
449        }
450
451        protected function firstLetter():void {
452            var c:String = getCharNext();
453            var c0:String;
454            var i:int;
455            switch(c) {
456            case "c": note(0);  break;
457            case "d": note(2);  break;
458            case "e": note(4);  break;
459            case "f": note(5);  break;
460            case "g": note(7);  break;
461            case "a": note(9);  break;
462            case "b": note(11); break;
463            case "r": rest(); break;
464            case "o": // Octave
465                m_octave = getUInt(m_octave);
466                if (m_octave < -2) m_octave = -2;
467                if (m_octave >  8) m_octave =  8;
468                break;
469            case "v": // Volume
470                m_velDetail = false;
471                m_velocity = getUInt((m_velocity-7)/8) * 8 + 7;
472                if (m_velocity < 0)   m_velocity = 0;
473                if (m_velocity > 127) m_velocity = 127;
474                break;
475            case "(": // vol up/down
476            case ")":
477                if (c == "(" && m_velDir ||
478                    c == ")" && !m_velDir) { // up
479                    m_velocity += (m_velDetail) ? 1 : 8;
480                    if (m_velocity > 127) m_velocity = 127;
481                }
482                else { // down
483                    m_velocity -= (m_velDetail) ? 1 : 8;
484                    if (m_velocity < 0) m_velocity = 0;
485                }
486                break;
487            case "l": // Length
488                m_length = len2tick(getUInt(0));
489                m_length = getDot(m_length);
490                break;
491            case "t": // Tempo
492                m_tempo = getUNumber(m_tempo);
493                if (m_tempo < 1) m_tempo = 1;
494                m_tracks[MTrack.TEMPO_TRACK].recTempo(m_tracks[m_trackNo].getRecGlobalTick(), m_tempo);
495                break;
496            case "q": // gate time (rate)
497                m_gate = getUInt(m_gate);
498                m_tracks[m_trackNo].recGate(Number(m_gate) / Number(m_maxGate));
499                break;
500            case "<" : // octave shift
501                if (m_relativeDir) m_octave++; else m_octave--;
502                break;
503            case ">": // octave shift
504                if (m_relativeDir) m_octave--; else m_octave++;
505                break;
506            case ";": // end of track
507                m_keyoff = 1;
508                if (m_tracks[m_trackNo].getNumEvents() > 0) {
509                        m_trackNo++;
510                }
511                m_tracks[m_trackNo] = createTrack();
512                break;
513            case "@":
514                atmark();
515                break;
516            case "x":
517                m_tracks[m_trackNo].recVolMode(getUInt(1));
518                break;
519            case "n":
520                c0 = getChar();
521                if (c0 == "s") { // Note Shift (absolute)
522                    next();
523                    m_noteShift = getSInt(m_noteShift);
524                }
525                else
526                    warning(MWarning.UNKNOWN_COMMAND, c + c0);
527                break;
528                        case '[':
529                                m_tracks[m_trackNo].recChordStart();
530                                break;
531                        case ']':
532                                m_tracks[m_trackNo].recChordEnd();
533                                break;
534            default:
535                {
536                    var cc:int = c.charCodeAt(0);
537                    if (cc < 128)
538                        warning(MWarning.UNKNOWN_COMMAND, c);
539                }
540                break;
541            }
542        }
543
544        protected function getCharNext():String {
545            return (m_letter < m_string.length) ? m_string.charAt(m_letter++) : '';
546        }
547
548        protected function getChar():String {
549            return (m_letter < m_string.length) ? m_string.charAt(m_letter) : '';
550        }
551
552        protected function next(i:int = 1):void {
553            m_letter += 1;
554        }
555
556        protected function getKeySig():int {
557            var k:int = 0;
558            var f:int = 1;
559            while(f) {
560                var c:String = getChar();
561                switch(c) {
562                case "+": case "#": k++; next(); break;
563                case "-":           k--; next(); break;
564                default: f = 0; break;
565                }
566            }
567            return k;
568        }
569
570        protected function getUInt(def:int):int {
571            var ret:int = 0;
572            var l:int = m_letter;
573            var f:int = 1;
574            while(f) {
575                var c:String = getChar();
576                switch(c) {
577                case '0': ret = ret * 10 + 0; next(); break;
578                case '1': ret = ret * 10 + 1; next(); break;
579                case '2': ret = ret * 10 + 2; next(); break;
580                case '3': ret = ret * 10 + 3; next(); break;
581                case '4': ret = ret * 10 + 4; next(); break;
582                case '5': ret = ret * 10 + 5; next(); break;
583                case '6': ret = ret * 10 + 6; next(); break;
584                case '7': ret = ret * 10 + 7; next(); break;
585                case '8': ret = ret * 10 + 8; next(); break;
586                case '9': ret = ret * 10 + 9; next(); break;
587                default: f = 0; break;
588                }
589            }
590            return (m_letter == l) ? def : ret;
591        }
592
593        protected function getUNumber(def:Number):Number {
594            var ret:Number = getUInt(int(def));
595            var l:Number = 1;
596            if (getChar() == '.') {
597                next();
598                var f:Boolean = true;
599                while(f) {
600                    var c:String = getChar();
601                    l *= 0.1;
602                    switch(c) {
603                        case '0': ret = ret + 0 * l; next(); break;
604                        case '1': ret = ret + 1 * l; next(); break;
605                        case '2': ret = ret + 2 * l; next(); break;
606                        case '3': ret = ret + 3 * l; next(); break;
607                        case '4': ret = ret + 4 * l; next(); break;
608                        case '5': ret = ret + 5 * l; next(); break;
609                        case '6': ret = ret + 6 * l; next(); break;
610                        case '7': ret = ret + 7 * l; next(); break;
611                        case '8': ret = ret + 8 * l; next(); break;
612                        case '9': ret = ret + 9 * l; next(); break;
613                        default: f = false; break;
614                    }
615                }
616            }
617            return ret;
618        }
619
620        protected function getSInt(def:int):int {
621            var c:String = getChar();
622            var s:int = 1;
623            if      (c == '-') { s = -1; next(); }
624            else if (c == '+') next();
625            return getUInt(def) * s;
626        }
627
628        protected function getDot(tick:int):int {
629            var c:String = getChar();
630            var intick:int = tick;
631            while(c == '.') {
632                next();
633                intick /= 2;
634                tick += intick;
635                c = getChar();
636            }
637            return tick;
638        }
639
640        public function createTrack():MTrack {
641            m_octave = 4;
642            m_velocity = 100;
643            m_noteShift = 0;
644            return new MTrack();
645        }
646
647        protected function begin():void {
648            m_letter = 0;
649        }
650
651        protected function process():void {
652            begin();
653            while(m_letter < m_string.length) {
654                firstLetter();
655            }
656        }
657
658        protected function processRepeat():void {
659            m_string = m_string.toLowerCase();
660            begin();
661            var repeat:Array = new Array();
662            var origin:Array = new Array();
663            var start:Array = new Array();
664            var last:Array = new Array();
665            var nest:int = -1;
666            while(m_letter < m_string.length) {
667                var c:String = getCharNext();
668                switch(c) {
669                case '/':
670                    if (getChar() == ':') {
671                        next();
672                        origin[++nest] = m_letter - 2;
673                        repeat[nest] = getUInt(2);
674                        start[nest] = m_letter;
675                        last[nest] = -1;
676                    }
677                    else if (nest >= 0) {
678                        last[nest] = m_letter - 1;
679                        m_string = m_string.substring(0, m_letter-1) + m_string.substring(m_letter);
680                        m_letter--;
681                    }
682                    else {
683                    }
684                    break;
685                case ':':
686                    if (getChar() == '/' && nest >= 0) {
687                        next();
688                        var contents:String = m_string.substring(start[nest], m_letter - 2);
689                        var newstr:String = m_string.substring(0, origin[nest]);
690                        for (var i:int = 0; i < repeat[nest]; i++) {
691                            if (i < repeat[nest]-1 || last[nest] < 0) newstr += contents;
692                            else newstr += m_string.substring(start[nest], last[nest]);
693                        }
694                        var l:int = newstr.length;
695                        newstr += m_string.substring(m_letter);
696                        m_string = newstr;
697                        m_letter = l;
698                        nest--;
699                    }
700                    break;
701                default:
702                    break;
703                }
704            }
705            if (nest >= 0) warning(MWarning.UNCLOSED_REPEAT, "");
706        }
707
708        protected function getIndex(idArr:Array, id:String):int {
709            for(var i:int = 0; i < idArr.length; i++)
710                if (((String)(idArr[i])) == id) return i;
711            return -1;
712        }
713
714        protected function replaceMacro(macroTable:Array):Boolean {
715            for each(var macro:Object in macroTable){
716                if(m_string.substr(m_letter, macro.id.length) == macro.id){
717                    var start:int = m_letter, last:int = m_letter + macro.id.length, code:String = macro.code;
718                    m_letter += macro.id.length;
719                    var c:String = getCharNext();
720                    while(StringUtil.isWhitespace(c) || c == ' '){
721                        c = getCharNext();
722                    }
723                    var args:Array = new Array();
724                                        var q:int = 0;
725                    if(c == "{"){
726                        c = getCharNext();
727                        while (q == 1 || (c != "}" && c != "")) {
728                                                        if (c == '"') q = 1 - q;
729                            if(c == "$"){
730                                replaceMacro(macroTable);
731                            }
732                            c = getCharNext();
733                        }
734                        last = m_letter;
735                        var argstr:String = m_string.substring(start + macro.id.length + 1, last - 1);
736                        var curarg:String = "", quoted:Boolean = false;
737                        for(var pos:int = 0; pos < argstr.length; pos++){
738                                if(!quoted && argstr.charAt(pos) == '"'){
739                                        quoted = true;
740                                }else if(quoted && (pos + 1) < argstr.length && argstr.charAt(pos) == '\\' && argstr.charAt(pos + 1) == '"'){
741                                        curarg += '"';
742                                        pos++;
743                                }else if(quoted && argstr.charAt(pos) == '"'){
744                                        quoted = false;
745                                }else if(!quoted && argstr.charAt(pos) == ','){
746                                        args.push(curarg);
747                                        curarg = "";
748                                }else{
749                                        curarg += argstr.charAt(pos);
750                                }
751                        }
752                        args.push(curarg);
753                        if(quoted){
754                                warning(MWarning.UNCLOSED_ARGQUOTE, "");
755                        }
756                    }
757                    // 引数への置換
758                    for(var i:int = 0; i < code.length; i++){
759                        for(var j:int = 0; j < args.length; j++){
760                                if(j >= macro.args.length){
761                                break;
762                                }
763                                if(code.substr(i, macro.args[j].id.length + 1) == ("%" + macro.args[j].id)){
764                                code = code.substring(0, i) + code.substring(i).replace("%" + macro.args[j].id, args[macro.args[j].index]);
765                                i += args[macro.args[j].index].length - 1;
766                                break;
767                                }
768                        }
769                    }
770                    m_string = m_string.substring(0, start - 1) + code + m_string.substring(last);
771                    m_letter = start - 1;
772                    //trace(m_string.substring(m_letter));
773                    return true;
774                }
775            }
776            return false;
777        }
778
779        protected function processMacro():void {
780            var i:int;
781                        var matched:Array;
782                        // OCTAVE REVERSE
783            var exp:RegExp = /^#OCTAVE\s+REVERSE\s*$/m;
784            if (m_string.match(exp)) {
785                m_string = m_string.replace(exp, "");
786                m_relativeDir = false;
787            }
788            // VELOCITY REVERSE
789            exp = /^#VELOCITY\s+REVERSE\s*$/m;
790            if (m_string.match(exp)) {
791                m_string = m_string.replace(exp, "");
792                m_velDir = false;
793            }
794                        // meta informations
795                        {
796                                m_metaTitle   = findMetaDesc("TITLE"  );        // #TITLE
797                                m_metaArtist  = findMetaDesc("ARTIST" );        // #ARTIST
798                                m_metaComment = findMetaDesc("COMMENT");        // #COMMENT
799                                m_metaCoding  = findMetaDesc("CODING" );        // #CODING
800                                findMetaDesc("PRAGMA"); // #PRAGMA
801                        }
802                        // POLY MODE
803                        {
804                                var usePoly:String = findMetaDesc("USING\\s+POLY");
805                                usePoly = usePoly.replace("\r",  "");
806                                usePoly = usePoly.replace("\n", " ");
807                                usePoly = usePoly.toLowerCase();
808                                if (usePoly.length > 0) {
809                                        var ss:Array = usePoly.split(" ");
810                                        if (ss.length < 1) {
811                                                m_usingPoly = false;
812                                        }
813                                        else {
814                                                m_usingPoly = true;
815                                                m_polyVoice = Math.min(Math.max(1, parseInt(ss[0])), MAX_POLYVOICE); // 1~MAX_POLYVOICE
816                                        }
817                                        for (i = 1; i < ss.length; i++) {
818                                                if (ss[i] == "force") {
819                                                        m_polyForce = true;
820                                                }
821                                        }
822                                        if (m_polyVoice <= 1) {
823                                                m_usingPoly = false;
824                                                m_polyForce = false;
825                                        }
826                                        // trace("using poly = " + m_usingPoly + ", max voice = " + m_polyVoice + ", force = " + m_polyForce);
827                                }
828                        }
829            // GB WAVE (ex. "#WAV10 0,0123456789abcdeffedcba9876543210")
830            {
831                exp = /^#WAV10\s.*$/gm;
832                matched = m_string.match(exp);
833                if (matched) {
834                    for(i = 0; i < matched.length; i++) {
835                        m_string = m_string.replace(exp, "");
836                        //trace(matched[i]);
837                        var wav:Array = matched[i].split(" ");
838                        var wavs:String = "";
839                        for(var j:int = 1; j < wav.length; j++) wavs += wav[j];
840                        var arg:Array = wavs.split(",");
841                        var waveNo:int = parseInt(arg[0]);
842                        if (waveNo < 0) waveNo = 0;
843                        if (waveNo >= MOscGbWave.MAX_WAVE) waveNo = MOscGbWave.MAX_WAVE-1;
844                        //trace(waveNo+":",arg[1].toLowerCase());
845                        MOscGbWave.setWave(waveNo,
846                                           (arg[1].toLowerCase()+"00000000000000000000000000000000").substr(0, 32));
847                    }
848                }
849                exp = /^#WAV13\s.*$/gm;
850                matched = m_string.match(exp);
851                if (matched) {
852                    for(i = 0; i < matched.length; i++) {
853                        m_string = m_string.replace(exp, "");
854                        //trace(matched[i]);
855                        wav = matched[i].split(" ");
856                        wavs = "";
857                        for(j = 1; j < wav.length; j++) wavs += wav[j];
858                        arg = wavs.split(",");
859                        waveNo = parseInt(arg[0]);
860                        if (waveNo < 0) waveNo = 0;
861                        if (waveNo >= MOscWave.MAX_WAVE) waveNo = MOscWave.MAX_WAVE-1;
862                        //trace(waveNo+":",arg[1].toLowerCase());
863                        MOscWave.setWave(waveNo,arg[1].toLowerCase());
864                    }
865                }
866                        //2009.05.10 OffGao ADD START addDPCM
867            // DPCM WAVE (ex. "#WAV9 0,0123456789abcdeffedcba9876543210")
868                exp = /^#WAV9\s.*$/gm;
869                matched = m_string.match(exp);
870                if (matched) {
871                    for(i = 0; i < matched.length; i++) {
872                        m_string = m_string.replace(exp, "");
873                        //trace(matched[i]);
874                        wav = matched[i].split(" ");
875                        wavs = "";
876                        for(j = 1; j < wav.length; j++) wavs += wav[j];
877                        arg = wavs.split(",");
878                        waveNo = parseInt(arg[0]);
879                        if (waveNo < 0) waveNo = 0;
880                        if (waveNo >= MOscFcDpcm.MAX_WAVE) waveNo = MOscFcDpcm.MAX_WAVE-1;
881                        var intVol:int = parseInt(arg[1]);
882                        if (intVol < 0) intVol = 0;
883                        if (intVol > 127)intVol = 127;
884                        var loopFg:int = parseInt(arg[2]);
885                        if (loopFg < 0) loopFg = 0;
886                        if (loopFg > 1) loopFg = 1;
887                        /*
888                        var length:int = -1;
889                        if (arg.length >= 5){
890                            length = parseInt(arg[4]);
891                            if (length < 1) length = 1;
892                            if (length > 0xff) length = 0xff;
893                        }
894                        MOscFcDpcm.setWave(waveNo,intVol,loopFg,arg[3],length);
895                        */
896                        MOscFcDpcm.setWave(waveNo,intVol,loopFg,arg[3]);
897                    }
898                }
899            }
900                        //2009.05.10 OffGao ADD END addDPCM
901            // macro
902            begin();
903            var top:Boolean = true;
904            var macroTable:Array = new Array();
905            while(m_letter < m_string.length) {
906                var c:String = getCharNext();
907                switch(c) {
908                    case '$':
909                        if(top){
910                            var last:int = m_string.indexOf(";", m_letter);
911                            if(last > m_letter){
912                                var nameEnd:int = m_string.indexOf("=", m_letter);
913                                if(nameEnd > m_letter && nameEnd < last){
914                                    var start:int = m_letter;
915                                    var argspos:int = m_string.indexOf("{");
916                                    if(argspos < 0 || argspos >= nameEnd){
917                                        argspos = nameEnd;
918                                    }
919                                    var regexResult:Array = m_string.substring(start, argspos).match("[a-zA-Z_][a-zA-Z_0-9#\+\(\)]*");
920                                        if(regexResult != null){
921                                            var id:String = regexResult[0];
922                                            if(id.length > 0){
923                                                var args:Array = new Array();
924                                                if(argspos < nameEnd){
925                                                    var argstr:String = m_string.substring(argspos + 1, m_string.indexOf("}", argspos));
926                                                    args = argstr.split(",");
927                                                    for(i = 0; i < args.length; i++){
928                                                        var argid:Array = args[i].match("[a-zA-Z_][a-zA-Z_0-9#\+\(\)]*");
929                                                        args[i] = { id: (argid != null ? argid[0] : ""), index: i };
930                                                    }
931                                                    args.sort(function (a:Object, b:Object):int {
932                                                        if(a.id.length > b.id.length)  return -1;
933                                                        if(a.id.length == b.id.length) return  0;
934                                                        return 1;
935                                                    });
936                                                }
937                                                m_letter = nameEnd + 1;
938                                                c = getCharNext();
939                                                while(m_letter < last){
940                                                    if(c == "$"){
941                                                        if(!replaceMacro(macroTable)){
942                                                        if(m_string.substr(m_letter, id.length) == id){
943                                                            m_letter--;
944                                                            m_string = remove(m_string, m_letter, m_letter + id.length);
945                                                            warning(MWarning.RECURSIVE_MACRO, id);
946                                                        }
947                                                    }
948                                                        last = m_string.indexOf(";", m_letter);
949                                                    }
950                                                    c = getCharNext();
951                                                }
952                                                var pos:int = 0;
953                                                for(; pos < macroTable.length; pos++){
954                                                 if(macroTable[pos].id == id){
955                                                     macroTable.splice(pos, 1);
956                                                     pos--;
957                                                     continue;
958                                                 }
959                                                        if(macroTable[pos].id.length < id.length){
960                                                                break;
961                                                        }
962                                                }
963                                                macroTable.splice(pos, 0, { id: id, code: m_string.substring(nameEnd + 1, last), args: args });
964                                                m_string = remove(m_string, start - 1, last);
965                                                m_letter = start - 1;
966                                            }
967                                    }
968                                }else{
969                                    // macro use
970                                    replaceMacro(macroTable);
971                                    top = false;
972                                 }
973                            }else{
974                                // macro use
975                                replaceMacro(macroTable);
976                                top = false;
977                            }
978                        }else{
979                            // macro use
980                            replaceMacro(macroTable);
981                            top = false;
982                        }
983                        break;
984                    case ';':
985                        top = true;
986                        break;
987                    default:
988                        if(!StringUtil.isWhitespace(c) && c != ' '){
989                            top = false;
990                        }
991                        break;
992                }
993            }
994        }
995               
996                // 指定されたメタ記述を引き抜いてくる
997                protected function findMetaDesc(sectionName:String):String {
998            var i:int;
999                        var matched:Array;
1000                        var mm:Array;
1001                        var e1:RegExp;
1002                        var e2:RegExp;
1003                        var tt:String = "";
1004                       
1005                        e1 = new RegExp("^#" + sectionName + "(\\s*|\\s+(.*))$", "gm"); // global multi-line
1006                        e2 = new RegExp("^#" + sectionName + "(\\s*|\\s+(.*))$",  "m"); //        multi-line
1007                       
1008                        matched = m_string.match(e1);
1009                        if (matched) {
1010                                m_string = m_string.replace(e1, "");
1011                                for(i = 0; i < matched.length; i++) {
1012                                        mm = matched[i].match(e2);
1013                                        if (mm.length >= 3) {
1014                                                tt += mm[2];
1015                                                if (i + 1 < matched.length) {
1016                                                        tt += "\r\n";
1017                                                }
1018                                        }
1019                                }
1020                                // trace(sectionName + " = " + tt);
1021                        }                       
1022                        return tt;
1023                }       
1024               
1025        protected function processComment(str:String):void {
1026            m_string = str;
1027            begin();
1028            var commentStart:int = -1;
1029            while(m_letter < m_string.length) {
1030                var c:String = getCharNext();
1031                switch(c) {
1032                case '/':
1033                    if (getChar() == '*') {
1034                        if (commentStart < 0) commentStart = m_letter - 1;
1035                        next();
1036                    }
1037                    break;
1038                case '*':
1039                    if (getChar() == '/') {
1040                        if (commentStart >= 0) {
1041                            m_string = remove(m_string, commentStart, m_letter);
1042                            m_letter = commentStart;
1043                            commentStart = -1;
1044                        }
1045                        else {
1046                            warning(MWarning.UNOPENED_COMMENT, "");
1047                        }
1048                    }
1049                    break;
1050                default:
1051                    break;
1052                }
1053            }
1054            if (commentStart >= 0) warning(MWarning.UNCLOSED_COMMENT, "");
1055        }
1056
1057                protected function processGroupNotes():void {
1058                        var GroupNotesStart:int = -1;
1059                        var GroupNotesEnd:int;
1060                        var noteCount:int = 0;
1061                        var repend:int, len:int, tick:int, tick2:int, tickdiv:Number, noteTick:int, noteOn:int;
1062                        var lenMode:int;
1063                        var defLen:int = 96;
1064                        var newstr:String;
1065                        begin();
1066                        while (m_letter < m_string.length) {
1067                                var c:String = getCharNext();
1068                                switch(c) {
1069                                        case 'l':
1070                                                defLen = len2tick(getUInt(0));
1071                                                defLen = getDot(defLen);
1072                                                break;
1073                                        case '{':
1074                                                GroupNotesStart = m_letter - 1;
1075                                                noteCount = 0;
1076                                                break;
1077                                        case '}':
1078                                                repend = m_letter;
1079                                                if (GroupNotesStart < 0) {
1080                                                        warning(MWarning.UNOPENED_GROUPNOTES, "");
1081                                                }
1082                                                tick = 0;
1083                                                while (1) {
1084                                                        if (getChar() != '%') {
1085                                                                lenMode = 0;
1086                                                        }
1087                                                        else {
1088                                                                lenMode = 1;
1089                                                                next();
1090                                                        }
1091                                                        len = getUInt(0);
1092                                                        if (len == 0) {
1093                                                                if (tick == 0) tick = defLen;
1094                                                                break;
1095                                                        }
1096                                                        tick2 = (lenMode ? len : len2tick(len));
1097                                                        tick2 = getDot(tick2);
1098                                                        tick += tick2;
1099                                                        if (getChar() != '&') {
1100                                                                break;
1101                                                        }
1102                                                        next();
1103                                                }
1104                                                GroupNotesEnd = m_letter;
1105                                                m_letter = GroupNotesStart + 1;
1106                                                newstr = m_string.substring(0, GroupNotesStart);
1107                                                tick2 = 0;
1108                                                tickdiv = Number(tick) / Number(noteCount);
1109                                                noteCount = 1;
1110                                                noteOn = 0;
1111                                                while (m_letter < repend) {
1112                                                        c = getCharNext();
1113                                                        switch (c) {
1114                                                                case '+':
1115                                                                case '#':
1116                                                                case '-':
1117                                                                        break;
1118
1119                                                                default:
1120                                                                        if ((c >= 'a' && c <= 'g') || c == 'r') {
1121                                                                                if (noteOn == 0) {
1122                                                                                        noteOn = 1;
1123                                                                                        break;
1124                                                                                }
1125                                                                        }
1126                                                                        if (noteOn == 1) {
1127                                                                                noteTick = Math.round(Number(noteCount) * tickdiv - Number(tick2));
1128                                                                                noteCount++;
1129                                                                                tick2 += noteTick;
1130                                                                                if (tick2 > tick) {
1131                                                                                        noteTick -= (tick2 - tick);
1132                                                                                        tick2 = tick;
1133                                                                                }
1134                                                                                newstr += "%";
1135                                                                                newstr += String(noteTick);
1136                                                                        }
1137                                                                        noteOn = 0;
1138                                                                        if ((c >= 'a' && c <= 'g') || c == 'r') {
1139                                                                                noteOn = 1;
1140                                                                        }
1141                                                                        break;
1142                                                        }
1143                                                        if (c != '}') {
1144                                                                newstr += c;
1145                                                        }
1146                                                }
1147                                                m_letter = newstr.length;
1148                                                newstr += m_string.substring(GroupNotesEnd);
1149                                                m_string = newstr;
1150                                                GroupNotesStart = -1;
1151                                                break;
1152                                        default:
1153                                                if ((c >= 'a' && c <= 'g') || c == 'r') {
1154                                                        noteCount++;
1155                                                }
1156                                                break;
1157                                }
1158                        }
1159                        if (GroupNotesStart >= 0) warning(MWarning.UNCLOSED_GROUPNOTES, "");
1160                }
1161
1162        static public function removeWhitespace(str:String):String {
1163            return str.replace(new RegExp("[  \n\r\t\f]+","g"),"");
1164        }
1165
1166        static public function remove(str:String, start:int, end:int):String {
1167            return str.substring(0, start) + str.substring(end+1);
1168        }
1169
1170        public function play(str:String):void {
1171            if (m_sequencer.isPaused()) {
1172                m_sequencer.play();
1173                return;
1174            }
1175            m_sequencer.disconnectAll();
1176            m_tracks = new Array();
1177            m_tracks[0] = createTrack();
1178            m_tracks[1] = createTrack();
1179            m_warning = new String();
1180
1181            m_trackNo = MTrack.FIRST_TRACK;
1182            m_octave = 4;
1183            m_relativeDir = true;
1184            m_velocity = 100;
1185            m_velDetail = true;
1186            m_velDir = true;
1187            m_length = len2tick(4);
1188            m_tempo  = 120;
1189            m_keyoff = 1;
1190            m_gate = 15;
1191            m_maxGate = 16;
1192            m_form = MOscillator.PULSE;
1193            m_noteShift = 0;
1194            m_maxPipe = 0;
1195            m_maxSyncSource = 0;
1196                        m_beforeNote = 0;
1197                        m_portamento = 0;
1198                        m_usingPoly = false;
1199                        m_polyVoice = 1;
1200                        m_polyForce = false;
1201                       
1202                        m_metaTitle   = "";
1203                        m_metaArtist  = "";
1204                        m_metaCoding  = "";
1205            m_metaComment = "";
1206                       
1207            processComment(str);
1208            //trace(m_string+"\n\n");
1209            processMacro();
1210            //trace(m_string);
1211            m_string = removeWhitespace(m_string);
1212            processRepeat();
1213            //trace(m_string);
1214                        processGroupNotes();
1215                        // trace(m_string);
1216            process();
1217
1218            // omit
1219            if (m_tracks[m_tracks.length-1].getNumEvents() == 0) m_tracks.pop();
1220
1221            // conduct
1222            m_tracks[MTrack.TEMPO_TRACK].conduct(m_tracks);
1223
1224            // post process
1225            for(var i:int = MTrack.TEMPO_TRACK; i < m_tracks.length; i++) {
1226                if (i > MTrack.TEMPO_TRACK) {
1227                                        if (m_usingPoly && (m_polyForce || m_tracks[i].findPoly())) {
1228                                                m_tracks[i].usingPoly(m_polyVoice);
1229                                        }
1230                    m_tracks[i].recRestMSec(2000);
1231                    m_tracks[i].recClose();
1232                }
1233                m_sequencer.connect(m_tracks[i]);
1234            }
1235
1236            // initialize modules
1237            m_sequencer.createPipes(m_maxPipe+1);
1238            m_sequencer.createSyncSources(m_maxSyncSource + 1);
1239
1240            // dispatch event
1241            dispatchEvent(new MMLEvent(MMLEvent.COMPILE_COMPLETE, false, false, 0, 0));
1242
1243            // play start
1244            m_sequencer.play();
1245        }
1246
1247        public function stop():void {
1248            m_sequencer.stop();
1249        }
1250
1251        public function pause():void {
1252            m_sequencer.pause();
1253        }
1254
1255        public function resume():void {
1256            m_sequencer.play();
1257        }
1258
1259        public function setMasterVolume(vol:int):void {
1260            m_sequencer.setMasterVolume(vol);
1261        }
1262
1263        public function getGlobalTick():uint {
1264            return m_sequencer.getGlobalTick();
1265        }
1266
1267        public function isPlaying():Boolean {
1268            return m_sequencer.isPlaying();
1269        }
1270
1271        public function isPaused():Boolean {
1272            return m_sequencer.isPaused();
1273        }
1274
1275        public function getTotalMSec():uint {
1276            return m_tracks[MTrack.TEMPO_TRACK].getTotalMSec();
1277        }
1278        public function getTotalTimeStr():String {
1279            return m_tracks[MTrack.TEMPO_TRACK].getTotalTimeStr();
1280        }
1281        public function getNowMSec():uint {
1282            return m_sequencer.getNowMSec();
1283        }
1284        public function getNowTimeStr():String {
1285            return m_sequencer.getNowTimeStr();
1286        }
1287        public function getVoiceCount():int {
1288                        var i:int;
1289                        var c:int = 0;
1290            for (i = 0; i < m_tracks.length; i++) {
1291                                c += m_tracks[i].getVoiceCount();
1292                        }
1293            return c;
1294        }               
1295        public function getMetaTitle():String {
1296            return m_metaTitle;
1297        }
1298        public function getMetaComment():String {
1299            return m_metaComment;
1300        }
1301        public function getMetaArtist():String {
1302            return m_metaArtist;
1303        }
1304        public function getMetaCoding():String {
1305            return m_metaCoding;
1306        }
1307    }
1308}
Note: See TracBrowser for help on using the browser.