// Generated by CoffeeScript 1.11.1
(function() {
  var AudioContext, Webcast;

  Webcast = {
    Encoder: {}
  };

  if (typeof window !== "undefined") {
    window.Webcast = Webcast;
  }

  if (typeof self !== "undefined") {
    self.Webcast = Webcast;
  }

  Webcast.Encoder.Asynchronous = (function() {
    function Asynchronous(arg) {
      var blob, j, len1, script, scripts;
      this.encoder = arg.encoder, scripts = arg.scripts;
      this.mime = this.encoder.mime;
      this.info = this.encoder.info;
      this.channels = this.encoder.channels;
      this.pending = [];
      this.scripts = [];
      for (j = 0, len1 = scripts.length; j < len1; j++) {
        script = scripts[j];
        this.scripts.push("'" + script + "'");
      }
      script = "var window;\nimportScripts(" + (this.scripts.join()) + ");\nvar encoder = " + (this.encoder.toString()) + ";\nself.onmessage = function (e) {\n  var type = e.data.type;\n  var data = e.data.data;\n  if (type === \"buffer\") {\n    encoder.encode(data, function (encoded) {\n      postMessage(encoded);\n    });\n    return;\n  }\n  if (type === \"close\") {\n    encoder.close(function (buffer) {\n      postMessage({close:true, buffer:buffer});\n      self.close();\n    });\n    return;\n  }\n};";
      blob = new Blob([script], {
        type: "text/javascript"
      });
      this.worker = new Worker(URL.createObjectURL(blob));
      this.worker.onmessage = (function(_this) {
        return function(arg1) {
          var data;
          data = arg1.data;
          return _this.pending.push(data);
        };
      })(this);
    }

    Asynchronous.prototype.toString = function() {
      return "(new Webcast.Encoder.Asynchronous({\n  encoder: " + (this.encoder.toString()) + ",\n  scripts: [" + (this.scripts.join()) + "]\n}))";
    };

    Asynchronous.prototype.close = function(fn) {
      this.worker.onmessage = (function(_this) {
        return function(arg) {
          var chunk, data, j, k, len, len1, len2, offset, ref, ref1, ret;
          data = arg.data;
          if (!data.close) {
            _this.pending.push(data);
            return;
          }
          _this.pending.push(data.buffer);
          len = 0;
          ref = _this.pending;
          for (j = 0, len1 = ref.length; j < len1; j++) {
            chunk = ref[j];
            len += chunk.length;
          }
          ret = new Uint8Array(len);
          offset = 0;
          ref1 = _this.pending;
          for (k = 0, len2 = ref1.length; k < len2; k++) {
            chunk = ref1[k];
            ret.set(chunk, offset);
            offset += chunk.length;
          }
          return fn(ret);
        };
      })(this);
      return this.worker.postMessage({
        type: "close"
      });
    };

    Asynchronous.prototype.encode = function(buffer, fn) {
      this.worker.postMessage({
        type: "buffer",
        data: buffer
      });
      return fn(this.pending.shift());
    };

    return Asynchronous;

  })();

  if (typeof window !== "undefined") {
    AudioContext = window.AudioContext || window.webkitAudioContext;
    AudioContext.prototype.createWebcastSource = function(bufferSize, channels, passThrough) {
      var context, node, options;
      context = this;
      node = context.createScriptProcessor(bufferSize, channels, channels);
      passThrough || (passThrough = false);
      options = {
        recorderSource: null,
        encoder: null,
        socket: null,
        passThrough: passThrough || false
      };
      node.onaudioprocess = function(buf) {
        var audio, channel, channelData, j, ref, ref1;
        audio = [];
        for (channel = j = 0, ref = buf.inputBuffer.numberOfChannels - 1; 0 <= ref ? j <= ref : j >= ref; channel = 0 <= ref ? ++j : --j) {
          channelData = buf.inputBuffer.getChannelData(channel);
          audio[channel] = channelData;
          if (options.passThrough) {
            buf.outputBuffer.getChannelData(channel).set(channelData);
          } else {
            buf.outputBuffer.getChannelData(channel).set(new Float32Array(channelData.length));
          }
        }
        return (ref1 = options.encoder) != null ? typeof ref1.encode === "function" ? ref1.encode(audio, function(data) {
          var ref2;
          if (data != null) {
            return (ref2 = options.socket) != null ? ref2.sendData(data) : void 0;
          }
        }) : void 0 : void 0;
      };
      node.setPassThrough = function(b) {
        return options.passThrough = b;
      };
      node.connectSocket = function(encoder, url) {
        if (encoder instanceof Webcast.Recorder) {
          options.recorderSource = context.createMediaStreamDestination();
          node.connect(options.recorderSource);
          encoder.start(options.recoderSource.stream, function(data) {
            var ref;
            if (data != null) {
              return (ref = options.socket) != null ? ref.sendData(data) : void 0;
            }
          });
        }
        options.encoder = encoder;
        return options.socket = new Webcast.Socket({
          url: url,
          mime: options.encoder.mime,
          info: options.encoder.info
        });
      };
      node.close = function(cb) {
        var fn, ref, ref1;
        if ((ref = options.recorderSource) != null) {
          ref.disconnect();
        }
        options.recorderSource = null;
        fn = function() {
          var ref1;
          if ((ref1 = options.socket) != null) {
            ref1.close();
          }
          options.socket = options.encoder = null;
          return typeof cb === "function" ? cb() : void 0;
        };
        if (((ref1 = options.encoder) != null ? ref1.close : void 0) == null) {
          return fn();
        }
        return options.encoder.close(function(data) {
          var ref2;
          if ((ref2 = options.socket) != null) {
            ref2.sendData(data);
          }
          return fn();
        });
      };
      node.getSocket = function() {
        return options.socket;
      };
      node.sendMetadata = (function(_this) {
        return function(metadata) {
          var ref;
          return (ref = options.socket) != null ? ref.sendMetadata(metadata) : void 0;
        };
      })(this);
      node.isOpen = function() {
        return options != null ? options.socket.isOpen() : void 0;
      };
      return node;
    };
  }

  Webcast.Encoder.Mp3 = (function() {
    Mp3.prototype.mime = "audio/mpeg";

    function Mp3(arg) {
      this.samplerate = arg.samplerate, this.bitrate = arg.bitrate, this.channels = arg.channels;
      this.shine = new Shine({
        samplerate: this.samplerate,
        bitrate: this.bitrate,
        channels: this.channels,
        mode: this.channels === 1 ? Shine.MONO : Shine.JOINT_STEREO
      });
      this.info = {
        audio: {
          channels: this.channels,
          samplerate: this.samplerate,
          bitrate: this.bitrate,
          encoder: "libshine"
        }
      };
      this;
    }

    Mp3.prototype.toString = function() {
      return "(new Webcast.Encoder.Mp3({\n  bitrate: " + this.bitrate + ",\n  channels: " + this.channels + ",\n  samplerate: " + this.samplerate + "\n }))";
    };

    Mp3.prototype.close = function(data, fn) {
      var flushed, rem;
      rem = new Uint8Array;
      if (fn != null) {
        if ((data != null ? data.length : void 0) > 0) {
          rem = this.shine.encode(data);
        }
      } else {
        fn = data;
      }
      flushed = this.shine.close();
      data = new Uint8Array(rem.length + flushed.length);
      data.set(rem);
      data.set(flushed, rem.length);
      return fn(data);
    };

    Mp3.prototype.encode = function(data, fn) {
      data = data.slice(0, this.channels);
      return fn(this.shine.encode(data));
    };

    return Mp3;

  })();

  Webcast.Encoder.Raw = (function() {
    function Raw(arg) {
      this.channels = arg.channels, this.samplerate = arg.samplerate;
      this.mime = "audio/x-raw,format=S8,channels=" + this.channels + ",layout=interleaved,rate=" + this.samplerate;
      this.info = {
        audio: {
          channels: this.channels,
          samplerate: this.samplerate,
          encoder: "RAW u8 encoder"
        }
      };
    }

    Raw.prototype.toString = function() {
      return "(new Webcast.Encoder.Raw({\n  channels: " + this.channels + ",\n  samplerate: " + this.samplerate + "\n }))";
    };

    Raw.prototype.doEncode = function(data) {
      var buf, chan, channels, i, j, k, ref, ref1, samples;
      channels = data.length;
      samples = data[0].length;
      buf = new Int8Array(channels * samples);
      for (chan = j = 0, ref = channels - 1; 0 <= ref ? j <= ref : j >= ref; chan = 0 <= ref ? ++j : --j) {
        for (i = k = 0, ref1 = samples - 1; 0 <= ref1 ? k <= ref1 : k >= ref1; i = 0 <= ref1 ? ++k : --k) {
          buf[channels * i + chan] = data[chan][i] * 127;
        }
      }
      return buf;
    };

    Raw.prototype.close = function(data, fn) {
      var ret;
      ret = new Uint8Array;
      if (fn != null) {
        if ((data != null ? data.count : void 0) > 0) {
          ret = this.doEncode(data);
        }
      } else {
        fn = data;
      }
      return fn(ret);
    };

    Raw.prototype.encode = function(data, fn) {
      return fn(this.doEncode(data));
    };

    return Raw;

  })();

  Webcast.Recorder = (function() {
    Recorder.prototype.mime = "audio/ogg";

    function Recorder(arg) {
      this.samplerate = arg.samplerate, this.bitrate = arg.bitrate, this.channels = arg.channels;
      this.info = {
        audio: {
          channels: this.channels,
          samplerate: this.samplerate,
          bitrate: this.bitrate,
          encoder: "MediaRecorder"
        }
      };
    }

    Recorder.prototype.start = function(stream, cb) {
      var recorder;
      recorder = new MediaRecorder(stream);
      return recorder.ondataavailable = (function(_this) {
        return function(e) {
          var blob;
          if (recorder.state === "recording") {
            blob = new Blob([e.data], _this.mime);
            return cb(blob);
          }
        };
      })(this);
    };

    return Recorder;

  })();

  Webcast.Encoder.Resample = (function() {
    function Resample(arg) {
      var i, j, ref;
      this.encoder = arg.encoder, this.samplerate = arg.samplerate, this.type = arg.type;
      this.mime = this.encoder.mime;
      this.info = this.encoder.info;
      this.channels = this.encoder.channels;
      this.ratio = parseFloat(this.encoder.samplerate) / parseFloat(this.samplerate);
      this.type = this.type || Samplerate.FASTEST;
      this.resamplers = [];
      this.remaining = [];
      for (i = j = 0, ref = this.channels - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
        this.resamplers[i] = new Samplerate({
          type: this.type
        });
        this.remaining[i] = new Float32Array;
      }
    }

    Resample.prototype.toString = function() {
      return "(new Webcast.Encoder.Resample({\n  encoder: " + (this.encoder.toString()) + ",\n  samplerate: " + this.samplerate + ",\n  type: " + this.type + "\n }))";
    };

    Resample.prototype.close = function(fn) {
      var data, i, j, ref;
      for (i = j = 0, ref = this.remaining.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
        data = this.resamplers[i].process({
          data: this.remaining[i],
          ratio: this.ratio,
          last: true
        }).data;
      }
      return this.encoder.close(data, fn);
    };

    Resample.prototype.concat = function(a, b) {
      var ret;
      if (typeof b === "undefined") {
        return a;
      }
      ret = new Float32Array(a.length + b.length);
      ret.set(a);
      ret.subarray(a.length).set(b);
      return ret;
    };

    Resample.prototype.encode = function(buffer, fn) {
      var data, i, j, ref, ref1, used;
      for (i = j = 0, ref = this.channels - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
        buffer[i] = this.concat(this.remaining[i], buffer[i]);
        ref1 = this.resamplers[i].process({
          data: buffer[i],
          ratio: this.ratio
        }), data = ref1.data, used = ref1.used;
        this.remaining[i] = buffer[i].subarray(used);
        buffer[i] = data;
      }
      return this.encoder.encode(buffer, fn);
    };

    return Resample;

  })();

  Webcast.Socket = function(arg) {
    var hello, info, key, mime, parser, password, send, socket, url, user, value;
    url = arg.url, mime = arg.mime, info = arg.info;
    parser = document.createElement("a");
    parser.href = url;
    user = parser.username;
    password = parser.password;
    parser.username = parser.password = "";
    url = parser.href;
    socket = new WebSocket(url, "webcast");
    socket.mime = mime;
    socket.info = info;
    hello = {
      mime: mime
    };
    if ((user != null) && user !== "") {
      hello.user = socket.user = user;
    }
    if ((password != null) && password !== "") {
      hello.password = socket.password = password;
    }
    for (key in info) {
      value = info[key];
      hello[key] = value;
    }
    send = socket.send;
    socket.send = null;
    socket.addEventListener("open", function() {
      return send.call(socket, JSON.stringify({
        type: "hello",
        data: hello
      }));
    });
    socket.sendData = function(data) {
      if (!socket.isOpen()) {
        return;
      }
      if (!((data != null ? data.length : void 0) > 0)) {
        return;
      }
      if (!(data instanceof ArrayBuffer)) {
        data = data.buffer.slice(data.byteOffset, data.length * data.BYTES_PER_ELEMENT);
      }
      return send.call(socket, data);
    };
    socket.sendMetadata = function(metadata) {
      if (!socket.isOpen()) {
        return;
      }
      return send.call(socket, JSON.stringify({
        type: "metadata",
        data: metadata
      }));
    };
    socket.isOpen = function() {
      return socket.readyState === WebSocket.OPEN;
    };
    return socket;
  };

}).call(this);
