You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			200 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
		
		
			
		
	
	
			200 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
| 
											9 years ago
										 | (function(window) { | ||
|  |   // internal: same as jQuery.extend(true, args...)
 | ||
|  |   var extend = function() { | ||
|  |     var target = arguments[0], | ||
|  |         sources = [].slice.call(arguments, 1); | ||
|  |     for (var i = 0; i < sources.length; ++i) { | ||
|  |       var src = sources[i]; | ||
|  |       for (key in src) { | ||
|  |         var val = src[key]; | ||
|  |         target[key] = typeof val === "object" | ||
|  |           ? extend(typeof target[key] === "object" ? target[key] : {}, val) | ||
|  |           : val; | ||
|  |       } | ||
|  |     } | ||
|  |     return target; | ||
|  |   }; | ||
|  | 
 | ||
|  |   var WORKER_FILE = { | ||
|  |     wav: "WebAudioRecorderWav.js", | ||
|  |     ogg: "WebAudioRecorderOgg.js", | ||
|  |     mp3: "WebAudioRecorderMp3.js" | ||
|  |   }; | ||
|  | 
 | ||
|  |   // default configs
 | ||
|  |   var CONFIGS = { | ||
|  |     workerDir: "/",     // worker scripts dir (end with /)
 | ||
|  |     numChannels: 2,     // number of channels
 | ||
|  |     encoding: "wav",    // encoding (can be changed at runtime)
 | ||
|  | 
 | ||
|  |     // runtime options
 | ||
|  |     options: { | ||
|  |       timeLimit: 300,           // recording time limit (sec)
 | ||
|  |       encodeAfterRecord: false, // process encoding after recording
 | ||
|  |       progressInterval: 1000,   // encoding progress report interval (millisec)
 | ||
|  |       bufferSize: undefined,    // buffer size (use browser default)
 | ||
|  | 
 | ||
|  |       // encoding-specific options
 | ||
|  |       wav: { | ||
|  |         mimeType: "audio/wav" | ||
|  |       }, | ||
|  |       ogg: { | ||
|  |         mimeType: "audio/ogg", | ||
|  |         quality: 0.5            // (VBR only): quality = [-0.1 .. 1]
 | ||
|  |       }, | ||
|  |       mp3: { | ||
|  |         mimeType: "audio/mpeg", | ||
|  |         bitRate: 160            // (CBR only): bit rate = [64 .. 320]
 | ||
|  |       } | ||
|  |     } | ||
|  |   }; | ||
|  | 
 | ||
|  |   // constructor
 | ||
|  |   var WebAudioRecorder = function(sourceNode, configs) { | ||
|  |     extend(this, CONFIGS, configs || {}); | ||
|  |     this.context = sourceNode.context; | ||
|  |     if (this.context.createScriptProcessor == null) | ||
|  |       this.context.createScriptProcessor = this.context.createJavaScriptNode; | ||
|  |     this.input = this.context.createGain(); | ||
|  |     sourceNode.connect(this.input); | ||
|  |     this.buffer = []; | ||
|  |     this.initWorker(); | ||
|  |   }; | ||
|  | 
 | ||
|  |   // instance methods
 | ||
|  |   extend(WebAudioRecorder.prototype, { | ||
|  |     isRecording: function() { return this.processor != null; }, | ||
|  | 
 | ||
|  |     setEncoding: function(encoding) { | ||
|  |       if (this.isRecording()) | ||
|  |         this.error("setEncoding: cannot set encoding during recording"); | ||
|  |       else if (this.encoding !== encoding) { | ||
|  |         this.encoding = encoding; | ||
|  |         this.initWorker(); | ||
|  |       } | ||
|  |     }, | ||
|  | 
 | ||
|  |     setOptions: function(options) { | ||
|  |       if (this.isRecording()) | ||
|  |         this.error("setOptions: cannot set options during recording"); | ||
|  |       else { | ||
|  |         extend(this.options, options); | ||
|  |         this.worker.postMessage({ command: "options", options: this.options }); | ||
|  |       } | ||
|  |     }, | ||
|  | 
 | ||
|  |     startRecording: function() { | ||
|  |       if (this.isRecording()) | ||
|  |         this.error("startRecording: previous recording is running"); | ||
|  |       else { | ||
|  |         var numChannels = this.numChannels, | ||
|  |             buffer = this.buffer, | ||
|  |             worker = this.worker; | ||
|  |         this.processor = this.context.createScriptProcessor( | ||
|  |                                 this.options.bufferSize, | ||
|  |                                 this.numChannels, this.numChannels); | ||
|  |         this.input.connect(this.processor); | ||
|  |         this.processor.connect(this.context.destination); | ||
|  |         this.processor.onaudioprocess = function(event) { | ||
|  |           for (var ch = 0; ch < numChannels; ++ch) | ||
|  |             buffer[ch] = event.inputBuffer.getChannelData(ch); | ||
|  |           worker.postMessage({ command: "record", buffer: buffer }); | ||
|  |         }; | ||
|  |         this.worker.postMessage({ | ||
|  |           command: "start", | ||
|  |           bufferSize: this.processor.bufferSize | ||
|  |         }); | ||
|  |         this.startTime = Date.now(); | ||
|  |       } | ||
|  |     }, | ||
|  | 
 | ||
|  |     recordingTime: function() { | ||
|  |       return this.isRecording() ? (Date.now() - this.startTime) * 0.001 : null; | ||
|  |     }, | ||
|  | 
 | ||
|  |     cancelRecording: function() { | ||
|  |       if (this.isRecording()) { | ||
|  |         this.input.disconnect(); | ||
|  |         this.processor.disconnect(); | ||
|  |         delete this.processor; | ||
|  |         this.worker.postMessage({ command: "cancel" }); | ||
|  |       } else | ||
|  |         this.error("cancelRecording: no recording is running"); | ||
|  |     }, | ||
|  | 
 | ||
|  |     finishRecording: function() { | ||
|  |       if (this.isRecording()) { | ||
|  |         this.input.disconnect(); | ||
|  |         this.processor.disconnect(); | ||
|  |         delete this.processor; | ||
|  |         this.worker.postMessage({ command: "finish" }); | ||
|  |       } else | ||
|  |         this.error("finishRecording: no recording is running"); | ||
|  |     }, | ||
|  | 
 | ||
|  |     cancelEncoding: function() { | ||
|  |       if (this.options.encodeAfterRecord) | ||
|  |         if (this.isRecording()) | ||
|  |           this.error("cancelEncoding: recording is not finished"); | ||
|  |         else { | ||
|  |           this.onEncodingCanceled(this); | ||
|  |           this.initWorker(); | ||
|  |         } | ||
|  |       else | ||
|  |         this.error("cancelEncoding: invalid method call"); | ||
|  |     }, | ||
|  | 
 | ||
|  |     initWorker: function() { | ||
|  |       if (this.worker != null) | ||
|  |         this.worker.terminate(); | ||
|  |       this.onEncoderLoading(this, this.encoding); | ||
|  |       this.worker = new Worker(this.workerDir + WORKER_FILE[this.encoding]); | ||
|  |       var _this = this; | ||
|  |       this.worker.onmessage = function(event) { | ||
|  |         var data = event.data; | ||
|  |         switch (data.command) { | ||
|  |           case "loaded": | ||
|  |             _this.onEncoderLoaded(_this, _this.encoding); | ||
|  |             break; | ||
|  |           case "timeout": | ||
|  |             _this.onTimeout(_this); | ||
|  |             break; | ||
|  |           case "progress": | ||
|  |             _this.onEncodingProgress(_this, data.progress); | ||
|  |             break; | ||
|  |           case "complete": | ||
|  |             _this.onComplete(_this, data.blob); | ||
|  |             break; | ||
|  |           case "error": | ||
|  |             _this.error(data.message); | ||
|  |         } | ||
|  |       }; | ||
|  |       this.worker.postMessage({ | ||
|  |         command: "init", | ||
|  |         config: { | ||
|  |           sampleRate: this.context.sampleRate, | ||
|  |           numChannels: this.numChannels | ||
|  |         }, | ||
|  |         options: this.options | ||
|  |       }); | ||
|  |     }, | ||
|  | 
 | ||
|  |     error: function(message) { | ||
|  |       this.onError(this, "WebAudioRecorder.js:" + message); | ||
|  |     }, | ||
|  | 
 | ||
|  |     // event handlers
 | ||
|  |     onEncoderLoading: function(recorder, encoding) {}, | ||
|  |     onEncoderLoaded: function(recorder, encoding) {}, | ||
|  |     onTimeout: function(recorder) { recorder.finishRecording(); }, | ||
|  |     onEncodingProgress: function (recorder, progress) {}, | ||
|  |     onEncodingCanceled: function(recorder) {}, | ||
|  |     onComplete: function(recorder, blob) { | ||
|  |       recorder.onError(recorder, "WebAudioRecorder.js: You must override .onComplete event"); | ||
|  |     }, | ||
|  |     onError: function(recorder, message) { console.log(message); } | ||
|  |   }); | ||
|  | 
 | ||
|  |   window.WebAudioRecorder = WebAudioRecorder; | ||
|  | })(window); |