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);
							 |