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     this.processor = worker;///////////TODO MOD
 69     
 70     var recording = false,
 71       currCallback;
 72     
 73     var dataListener;//TODO MOD
 74 
 75     var doRecordAudio = function(e){
 76       if (!recording) return;
 77       worker.postMessage({
 78         command: 'record',
 79         buffer: [
 80           e.inputBuffer.getChannelData(0),
 81           e.inputBuffer.getChannelData(1)
 82         ]
 83       });
 84     }
 85     
 86     this._initSource = function(inputSource){
 87 
 88         this.context = inputSource.context;
 89         
 90         //if we already had another input-source before, we need to clean up first:
 91         if(this.node){
 92         	this.node.disconnect();
 93         }
 94         
 95     	if(!this.context.createScriptProcessor){
 96         	this.node = this.context.createJavaScriptNode(bufferLen, 2, 2);
 97         }
 98         else {
 99         	this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
100         }
101 
102     	this.node.onaudioprocess = doRecordAudio;
103     	
104     	worker.postMessage({
105         	command: 'init',
106         	config: {
107         		sampleRate: this.context.sampleRate
108         	}
109         });
110     	
111     	inputSource.connect(this.node);
112         this.node.connect(this.context.destination); 
113     }
114 
115     this.configure = function(cfg){
116       for (var prop in cfg){
117         if (cfg.hasOwnProperty(prop)){
118           config[prop] = cfg[prop];
119         }
120       }
121     }
122 
123     this.record = function(){
124       recording = true;
125     }
126 
127     this.stop = function(){
128       recording = false;
129     }
130 
131     this.clear = function(){
132       worker.postMessage({ command: 'clear' });
133     }
134 
135     this.getBuffers = function(cb, isListener) {//TODO MOD additional parameter
136     	//TODO MOD start
137       if(isListener === true){
138     	  dataListener = cb;
139           worker.postMessage({ command: 'getBuffers', id: 'listener' });
140       }
141       else {//TODO MOD end
142     	  currCallback = cb || config.callback;
143           worker.postMessage({ command: 'getBuffers' })
144       }
145     }
146 
147     this.exportWAV = function(cb, type){
148     	currCallback = cb || config.callback;
149     	type = type || config.type || 'audio/wav';
150     	if (!currCallback) throw new Error('Callback not set');
151     	worker.postMessage({
152     		command: 'exportWAV',
153     		type: type
154     	});
155     }
156 
157     this.exportMonoWAV = function(cb, type){
158     	currCallback = cb || config.callback;
159     	type = type || config.type || 'audio/wav';
160     	if (!currCallback) throw new Error('Callback not set');
161     	worker.postMessage({
162     		command: 'exportMonoWAV',
163     		type: type
164     	});
165     }
166     
167     var selfRef = this;///////////TODO MOD
168     worker.onmessage = function(e){
169 
170     	var blob = e.data;
171     	///////////TODO MOD start
172     	if(blob.buffers){
173     		var id = blob.id;
174     		blob = blob.buffers;
175     		if(id && id === 'listener' && dataListener){
176     			dataListener(blob);
177     		}
178     		return;
179     	}
180     	///////////TODO MOD end
181     	
182     	///////////TODO MOD start
183     	var result = true;
184     	if(selfRef.beforeonmessage){
185     		result = selfRef.beforeonmessage(e);
186     	}
187     	if(typeof result !== 'undefined' && !result){
188     		return;
189     	}
190     	///////////TODO MOD end
191     	
192     	currCallback(blob);
193     	
194     }
195 
196 //    source.connect(this.node);
197 //    this.node.connect(this.context.destination);    // if the script node is not connected to an output the "onaudioprocess" event is not triggered in chrome.
198     
199     this._initSource(source);
200   };
201 
202   Recorder.forceDownload = function(blob, filename){
203     var url = (window.URL || window.webkitURL).createObjectURL(blob);
204     var link = window.document.createElement('a');
205     link.href = url;
206     link.download = filename || 'output.wav';
207 //  var click = document.createEvent("Event");click.initEvent("click", true, true);
208 	//MOD NOTE: FireFox requires a MouseEvent (in Chrome a simple Event would do the trick)
209     var click = document.createEvent("MouseEvent");
210 	click.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
211     link.dispatchEvent(click);
212   }
213 
214   window.Recorder = Recorder;
215 })(window);
216