Changeset 29152

Show
Ignore:
Timestamp:
01/28/09 05:36:50 (5 years ago)
Author:
moriyoshi
Message:

* Add MML parser.

Location:
lang/javascript/SoundGenerator
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • lang/javascript/SoundGenerator/sound.js

    r29142 r29152  
    4040        var n = document.createElement("iframe"); 
    4141        document.body.appendChild(n); 
    42         n.src = d; 
     42        n.setAttribute('src', d); 
    4343        n.style.visibility = "hidden"; 
    4444    } 
     
    5656    }, 
    5757 
    58     generatePulseWave: function(freq, duration, vol) { 
     58    generateSquareWave: function(freq, duration, vol) { 
    5959        var t = 0, d = this.data, res = this.resolution, 
    6060            eg = this.envelopeGenerator; 
     
    6363            v = 0; 
    6464            for (var i = pi, e = pi * 40; i <= e; i += pi + pi) { 
    65                 var m = t * freq / res, z = i / 40; 
     65                var m = t * freq / res, z = i / 50; 
    6666                v += sin(i * 2 * m) * sin(z) / z / i; 
    6767            } 
     
    240240var MusicBuilder = function() { this.initialize.apply(this, arguments); }; 
    241241MusicBuilder.prototype = { 
    242     initialize: function(sg, tone) { 
     242    initialize: function(sg) { 
    243243        this.sg = sg; 
    244         this.tempo = 120; 
    245         this.volume = 120; 
    246         this.tone = 'square'; 
     244        this.tempo_ = 120; 
     245        this.volume_ = 120; 
     246        this.tone_ = 'square'; 
    247247        this.initializeTone(); 
    248248    }, 
    249249 
    250250    initializeTone: function() { 
    251         var volume = this.volume; 
    252         switch (this.tone) { 
     251        var self = this; 
     252        switch (this.tone_) { 
    253253        case 'saw': 
    254254            this.sound = function(freq, duration) { 
    255                 this.sg.generateSawWave(freq, duration, volume); 
     255                this.sg.generateSawWave(freq, duration, self.volume_); 
    256256            }; 
    257257            break; 
    258258        case 'square': 
    259259            this.sound = function(freq, duration) { 
    260                 this.sg.generatePulseWave(freq, duration, volume); 
     260                this.sg.generateSquareWave(freq, duration, self.volume_); 
    261261            }; 
    262262            break; 
     
    265265 
    266266    calculateDuration: function(len) { 
    267         return (this.sg.resolution * 240 / this.tempo) / len; 
     267        return (this.sg.resolution * 240 / this.tempo_) / len; 
     268    }, 
     269 
     270    tone: function(v) { 
     271        this.tone_ = v; 
     272        this.initializeTone(); 
     273    }, 
     274 
     275    tempo: function(v) { 
     276        this.tempo_ = v; 
    268277    }, 
    269278 
     
    276285    rest: function(len) { 
    277286        this.sg.generateSilence(this.calculateDuration(len)); 
     287    }, 
     288 
     289    envelope: function(eg) { 
     290        this.sg.envelopeGenerator = eg; 
     291    }, 
     292 
     293    parseMML: function(mml) { 
     294        var regex = /([><])|([LMOSTVS@])\s*(\d*)|([ABCDEFG]([#+-]?)|R)\s*(?:(\d+)\s*|(\.+)\s*){0,2}|\s+/gi; 
     295        var m; 
     296        var pitch_base = 36, default_len = 4; 
     297        var syntax_error = { message: "Syntax error" }; 
     298        var invalid_parameter = { message: "Invalid parameter" }; 
     299        var pitch_table = { 
     300            "a":  0, "A":  0, 
     301            "b":  2, "B":  2, 
     302            "c": -9, "C": -9, 
     303            "d": -7, "D": -7, 
     304            "e": -5, "E": -5, 
     305            "f": -4, "F": -4, 
     306            "g": -2, "G": -2 
     307        }; 
     308        var envelope_cycle = 1000; 
     309        var envelopes = [ 
     310            function(t) { return 1; }, 
     311            function(t) { return Math.max(0, 1 - t / envelope_cycle); }, 
     312            function(t) { return Math.min(1, t / envelope_cycle); }, 
     313            function(t) { return 1 - (t % envelope_cycle) / envelope_cycle; }, 
     314            function(t) { return (t % envelope_cycle) / envelope_cycle; }, 
     315            function(t) { return Math.abs((t % (envelope_cycle * 2)) - envelope_cycle) / envelope_cycle; }, 
     316            function(t) { return 1 - Math.abs((t % (envelope_cycle * 2)) - envelope_cycle) / envelope_cycle; }, 
     317            function(t) { return t < envelope_cycle ? 1 - t / envelope_cycle: 1; }, 
     318            function(t) { return t < envelope_cycle ? t / envelope_cycle: 0; } 
     319        ]; 
     320 
     321        var envelope_table = [ 
     322            envelopes[0], 
     323            envelopes[1], 
     324            envelopes[1], 
     325            envelopes[1], 
     326            envelopes[8], 
     327            envelopes[8], 
     328            envelopes[8], 
     329            envelopes[8], 
     330            envelopes[3], 
     331            envelopes[1], 
     332            envelopes[5], 
     333            envelopes[7], 
     334            envelopes[4], 
     335            envelopes[2], 
     336            envelopes[6], 
     337            envelopes[8] 
     338        ]; 
     339 
     340        tone_table = [ 'square', 'saw' ]; 
     341        while (m = regex.exec(mml)) { 
     342            switch (m[1]) { 
     343            case "<": 
     344                pitch_base += 12; 
     345                continue; 
     346            case ">": 
     347                pitch_base -= 12; 
     348                continue; 
     349            } 
     350            if (m[2]) { 
     351                var param = m[3] == '' ? -1: parseInt(m[3]); 
     352                switch (m[2]) { 
     353                case "l": case "L": 
     354                    if (param < 0) 
     355                        throw syntax_error; 
     356                    default_len = param; 
     357                    break; 
     358                case "o": case "O": 
     359                    if (param < 0) 
     360                        throw syntax_error; 
     361                    pitch_base = (param - 1) * 12; 
     362                    break; 
     363                case "t": case "T": 
     364                    if (param < 0) 
     365                        throw syntax_error; 
     366                    this.tempo(param); 
     367                    break; 
     368                case "v": case "V": 
     369                    if (param < 0) 
     370                        throw syntax_error; 
     371                    this.volume(param); 
     372                    break; 
     373                case 's': case 'S': 
     374                    if (param < 0) 
     375                        throw syntax_error; 
     376                    if (param >= envelope_table.length) 
     377                        throw invalid_parameter;  
     378                    this.envelope(envelope_table[param]); 
     379                    break; 
     380                case 'm': case 'M': 
     381                    if (param < 0) 
     382                        throw syntax_error; 
     383                    if (param < 1) 
     384                        throw invalid_parameter; 
     385                    envelope_cycle = this.sg.resolution * param / 8000; 
     386                    break; 
     387                case '@': 
     388                    if (param < 0) 
     389                        throw syntax_error; 
     390                    if (param >= tone_table.length) 
     391                        throw invalid_parameter;  
     392                    this.tone(tone_table[param]); 
     393                } 
     394                continue; 
     395            } 
     396            var l = typeof(m[6]) == 'undefined' ? default_len: parseInt(m[6]), 
     397                a = typeof(m[7]) == 'undefined' ? 0: m[7].length, 
     398                d = l * (1 << a) / ((1 << (a + 1)) - 1); 
     399            if (m[4]) { 
     400                this.note(pitch_base + pitch_table[m[4].charAt(0)] 
     401                    + (m[5] ? ("#+".indexOf(m[5]) < 0 ? -1: 1): 0), d); 
     402            } else { 
     403                this.rest(d); 
     404            } 
     405        } 
    278406    } 
    279407}; 
  • lang/javascript/SoundGenerator/test.html

    r29142 r29152  
    77 
    88var m1 = new MusicBuilder(new SoundGenerator(16000)); 
    9 m1.note(43, 16); 
    10 m1.note(43, 16); 
    11 m1.rest(16); 
    12 m1.note(43, 16); 
    13 m1.rest(16); 
    14 m1.note(39, 16); 
    15 m1.note(43, 8); 
    16 m1.note(46, 8); 
    17 m1.rest(8); 
    18 m1.note(34, 8); 
    19 m1.rest(8); 
    20  
     9m1.parseMML("O5@0T120L16E E RE RC E8 G8R8>G8R8"); 
    2110var m2 = new MusicBuilder(new SoundGenerator(16000)); 
    22 m2.note(33, 16); 
    23 m2.note(33, 16); 
    24 m2.rest(16); 
    25 m2.note(33, 16); 
    26 m2.rest(16); 
    27 m2.note(33, 16); 
    28 m2.note(33, 8); 
    29 m2.note(34, 8); 
    30 m2.rest(4); 
    31 m2.rest(8); 
    32  
     11m2.parseMML("O4@0T120L16F+F+RF+RF+F+8G8R4."); 
    3312var m3 = new MusicBuilder(new SoundGenerator(16000)); 
    34 m3.note(17, 16); 
    35 m3.note(17, 16); 
    36 m3.rest(16); 
    37 m3.note(17, 16); 
    38 m3.rest(16); 
    39 m3.note(17, 16); 
    40 m3.note(17, 8); 
    41 m3.note(22, 8); 
    42 m3.rest(8); 
    43 m3.note(22, 8); 
    44 m3.rest(8); 
    45  
     13m3.parseMML("O3@0T120L16D D RD RD D8 G8R8 G8R8"); 
    4614new SoundPlayer(16000).play(m1.sg.data, m2.sg.data, m3.sg.data); 
    47 //new SoundPlayer(16000).play(m1.sg.data); 
    4815}; 
    4916  </script>