1 /*
  2  * License (MIT)
  3  *
  4  * modifications:
  5  * 
  6  * 	Copyright (C) 2013 DFKI GmbH
  7  * 	Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
  8  * 	German Research Center for Artificial Intelligence
  9  * 	http://www.dfki.de
 10  *  
 11  * based on
 12  * 	Copyright (C) 2013 Matt Diamond (MIT License)
 13  *
 14  * 	Permission is hereby granted, free of charge, to any person obtaining a 
 15  * 	copy of this software and associated documentation files (the 
 16  * 	"Software"), to deal in the Software without restriction, including 
 17  * 	without limitation the rights to use, copy, modify, merge, publish, 
 18  * 	distribute, sublicense, and/or sell copies of the Software, and to 
 19  * 	permit persons to whom the Software is furnished to do so, subject to 
 20  * 	the following conditions:
 21  * 
 22  * 	The above copyright notice and this permission notice shall be included 
 23  * 	in all copies or substantial portions of the Software.
 24  * 
 25  * 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 26  * 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 27  * 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 28  * 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 29  * 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 30  * 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 31  * 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 32  */
 33 
 34 
 35 (function(window){
 36 
 37   var WORKER_PATH = 'mmirf/workers/recorderWorker.js';
 38 
 39   var Recorder = function(source, cfg){
 40     var config = cfg || {};
 41     var bufferLen = config.bufferLen || 4096;
 42     this.context = source.context;
 43 //    this.createScriptProcessor = function(contextObj, bufferSize, numberOfInputChannels, numberOfOutputChannels){
 44 //    	if(contextObj.createJavaScriptNode){
 45 //    		return contextObj.createJavaScriptNode(bufferSize, numberOfInputChannels, numberOfOutputChannels);
 46 //    	}
 47 //    	else if(contextObj.createScriptProcessor){
 48 //    		return contextObj.createScriptProcessor(bufferSize, numberOfInputChannels, numberOfOutputChannels);
 49 //    	}
 50 //    	else {
 51 //    		throw Error('Could not create script-processor for AudioContext: context provides no function for generating processor!');
 52 //    	}
 53 //    };
 54 //    this.node = this.createScriptProcessor(this.context, bufferLen, 2, 2);
 55     if(!this.context.createScriptProcessor){
 56     	this.node = this.context.createJavaScriptNode(bufferLen, 2, 2);
 57     }
 58     else {
 59     	this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
 60     }
 61     var worker = new Worker(config.workerPath || WORKER_PATH);
 62     worker.postMessage({
 63     	command: 'init',
 64     	config: {
 65     		sampleRate: this.context.sampleRate
 66     	}
 67     });
 68     var recording = false,
 69       currCallback;
 70 
 71     this.node.onaudioprocess = function(e){
 72       if (!recording) return;
 73       worker.postMessage({
 74         command: 'record',
 75         buffer: [
 76           e.inputBuffer.getChannelData(0),
 77           e.inputBuffer.getChannelData(1)
 78         ]
 79       });
 80     }
 81 
 82     this.configure = function(cfg){
 83       for (var prop in cfg){
 84         if (cfg.hasOwnProperty(prop)){
 85           config[prop] = cfg[prop];
 86         }
 87       }
 88     }
 89 
 90     this.record = function(){
 91       recording = true;
 92     }
 93 
 94     this.stop = function(){
 95       recording = false;
 96     }
 97 
 98     this.clear = function(){
 99       worker.postMessage({ command: 'clear' });
100     }
101 
102     this.getBuffers = function(cb) {
103       currCallback = cb || config.callback;
104       worker.postMessage({ command: 'getBuffers' })
105     }
106 
107     this.exportWAV = function(cb, type){
108     	currCallback = cb || config.callback;
109     	type = type || config.type || 'audio/wav';
110     	if (!currCallback) throw new Error('Callback not set');
111     	worker.postMessage({
112     		command: 'exportWAV',
113     		type: type
114     	});
115     }
116 
117     this.exportMonoWAV = function(cb, type){
118     	currCallback = cb || config.callback;
119     	type = type || config.type || 'audio/wav';
120     	if (!currCallback) throw new Error('Callback not set');
121     	worker.postMessage({
122     		command: 'exportMonoWAV',
123     		type: type
124     	});
125     }
126 
127     worker.onmessage = function(e){
128     	var blob = e.data;
129     	currCallback(blob);
130     }
131 
132     source.connect(this.node);
133     this.node.connect(this.context.destination);    // if the script node is not connected to an output the "onaudioprocess" event is not triggered in chrome.
134   };
135 
136   Recorder.forceDownload = function(blob, filename){
137     var url = (window.URL || window.webkitURL).createObjectURL(blob);
138     var link = window.document.createElement('a');
139     link.href = url;
140     link.download = filename || 'output.wav';
141     var click = document.createEvent("Event");
142     click.initEvent("click", true, true);
143     link.dispatchEvent(click);
144   }
145 
146   window.Recorder = Recorder;
147 })(window);
148