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