1 /** 2 * Sound generator worker 3 * @param {Number} sample 4 * @constructor 5 */ 6 function AP_Service(sample) { 7 this.keys = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 8 this.sample = sample; 9 this.instrument = [ 10 new AP_Instrument(sample, 1, {sqr:-.2,sin:.5}), 11 new AP_Instrument(sample, .3, {sqr:.4,sin:.4}), 12 new AP_Instrument(sample, .7, {sin:2}), 13 new AP_Instrument(sample, .1, {noi:.2}), 14 new AP_Instrument(sample, .1, {sin:.6}) 15 ]; 16 } 17 18 /** 19 * Base64 encoding 20 * @param {Number} value 21 */ 22 AP_Service.prototype.add = function(value) { 23 var buffer = this.buffer, 24 c1, c2, c3, e1, e2, e3, e4; 25 if (value !== undefined) { 26 buffer.push(value); 27 } 28 if (buffer.length > 2 || value === undefined) { 29 c1 = buffer.shift(); 30 c2 = buffer.shift(); 31 c3 = buffer.shift(); 32 e1 = c1 >> 2; 33 e2 = ((c1 & 3) << 4) | (c2 >> 4); 34 e3 = ((c2 & 15) << 2) | (c3 >> 6); 35 e4 = c3 & 63; 36 if (isNaN(c2)) e3 = e4 = 64; 37 else if (isNaN(c3)) e4 = 64; 38 this.data = this.data + 39 this.keys.charAt(e1) + 40 this.keys.charAt(e2) + 41 this.keys.charAt(e3) + 42 this.keys.charAt(e4); 43 } 44 }; 45 46 /** 47 * String encoding 48 * @param value 49 * @returns {AP_Service} 50 */ 51 AP_Service.prototype.str = function(value) { 52 for (var i=0; i<value.length; i++) { 53 this.add(value.charCodeAt(i)); 54 } 55 return this; 56 }; 57 58 /** 59 * Number encoding 60 * @param value 61 * @param size 62 * @returns {AP_Service} 63 */ 64 AP_Service.prototype.num = function(value, size) { 65 for (var i=0; i<size; i++) { 66 this.add(value % 256); 67 value = Math.floor(value / 256); 68 } 69 return this; 70 }; 71 72 /** 73 * Generate Base64 encoded WAV file from data array 74 * @param {Array} data 75 * @returns {String} 76 */ 77 AP_Service.prototype.write = function(data) { 78 this.str('RIFF') 79 .num(data.length + 36, 4) 80 .str('WAVEfmt ') 81 .num(16, 4) 82 .num(1, 2) 83 .num(1, 2) 84 .num(this.sample, 4) 85 .num(this.sample, 4) 86 .num(1, 2) 87 .num(8, 2) 88 .str('data') 89 .num(data.length, 4); 90 for (var i=0; i<data.length; i++) { 91 this.add(data[i]); 92 } 93 this.add(); 94 delete data; 95 return this.data; 96 }; 97 98 /** 99 * Worker service 100 * @param {String} notes 101 * @returns {String} 102 */ 103 AP_Service.prototype.handle = function(notes) { 104 this.buffer = []; 105 this.data = 'data:audio/wav;base64,'; 106 var tempo = 1.25, 107 note = notes.split('|', this.instrument.length); 108 channel = [], 109 data = []; 110 for (var i=0; i<note.length; i++) { 111 channel.push(new AP_Channel(this.instrument[i], note[i], tempo)); 112 } 113 do { 114 var num = 0, 115 sum = 0, 116 val = 0; 117 for (var j=0; j<channel.length; j++) { 118 val = channel[j].get(); 119 if (val !== false) { 120 sum += val; 121 num++; 122 } 123 } 124 if (num > 0) { 125 data.push(Math.round(sum / num * 120 + 128)); 126 } 127 } 128 while (num > 0); 129 return this.write(data); 130 }; 131 132 var service = new AP_Service(22050); 133 self.addEventListener('message', function(e) { 134 var result = service.handle(e.data); 135 self.postMessage(result); 136 }); 137