Source: incomingMessage.js

const Emitter = require('./emitter');
const Constants = require('./constants');
const Utils = require('./utils');

/**
 * @hideconstructor
 * @classdesc Incoming audio message class. Instances are returned as arguments for corresponding <code>ZCC.Session</code> events
 **/
class IncomingMessage extends Emitter {

  static get PersistentPlayer() {
    return IncomingMessage.persistentPlayer;
  }

  static set PersistentPlayer(player) {
    IncomingMessage.persistentPlayer = player;
  }

  constructor(messageData, session) {
    super();
    this.streamId = messageData.stream_id;
    this.codecDetails = Utils.parseCodedHeader(messageData.codec_header);
    this.messageDidStart = false;
    let library = Utils.getLoadedLibrary();
    this.options =
      Object.assign({
        encoding: '32bitFloat',
        channels: 1,
        sampleRate: IncomingMessage.detectSampleRate(this.codecDetails.rate),
        flushingTime: 240,
      },
      session.options,
      {messageData: messageData}
    );

    if (this.options.decoder && !Utils.isFunction(this.options.decoder)) {
      this.options.decoder = library.Decoder;
    }

    if (this.options.player && !Utils.isFunction(this.options.player)) {
      this.options.player = library.Player;
    }

    this.initPlayer();
    this.initDecoder();
    this.initSessionHandlers();

    this.session = session;
    this.instanceId = messageData.stream_id.toString();
    this.session.on([Constants.EVENT_INCOMING_VOICE_DATA, this.instanceId], this.incomingVoiceHandler);
    this.session.on([Constants.EVENT_INCOMING_VOICE_DID_STOP, this.instanceId], this.incomingVoiceDidStopHandler);
    this.on([Constants.EVENT_INCOMING_VOICE_DATA_DECODED, this.instanceId], this.decodedAudioHandler);
  }


  // default decoder supports 8k, 12k, 16k, 24k and 48k
  // safari supports 24k and 48k only
  static detectSampleRate(codecSampleRate) {
    if (codecSampleRate <= 24000) {
      return 24000;
    }
    return 48000;
  }

  initSessionHandlers() {
    this.decodedAudioHandler = (pcmData) => {
      if (this.player && Utils.isFunction(this.player.feed)) {
        this.player.feed(pcmData);
      }
    };

    this.incomingVoiceDidStopHandler = () => {
      /**
       * Incoming voice message stopped
       * @event IncomingMessage#incoming_voice_did_stop
       * @param {ZCC.IncomingMessage} incomingMessage incoming message instance (self)
       */
      this.emit(Constants.EVENT_INCOMING_VOICE_DID_STOP, this);
      if (this.player && Utils.isFunction(this.player.destroy) && !IncomingMessage.PersistentPlayer) {
        this.player.destroy();
      }
      if (this.decoder && Utils.isFunction(this.decoder.destroy)) {
        this.decoder.destroy();
      }
      this.session.off([Constants.EVENT_INCOMING_VOICE_DATA, this.instanceId], this.incomingVoiceHandler);
      this.session.off([Constants.EVENT_INCOMING_VOICE_DID_STOP, this.instanceId], this.incomingVoiceDidStopHandler);
    };

    this.incomingVoiceHandler = (parsedAudioPacket) => {
      if (!this.messageDidStart) {
        this.messageDidStart = true;
        /**
         * Incoming voice message started
         * @event IncomingMessage#incoming_voice_did_start
         * @param {ZCC.IncomingMessage} incomingMessage incoming message instance (self)
         */
        this.emit(Constants.EVENT_INCOMING_VOICE_DID_START, this);
        this.session.onIncomingVoiceDidStart(this);
      }

      /**
       * Incoming voice message packet (with encoded audio)
       * @event IncomingMessage#incoming_voice_data
       * @param {Object} incomingVoicePacket voice message packet object
       * @property {Uint8Array} messageData encoded (opus) data
       * @property {Number} messageId incoming message id
       * @property {Number} packetId incoming packet id
       */
      this.emit(Constants.EVENT_INCOMING_VOICE_DATA, parsedAudioPacket);
      if (this.decoder) {
        this.decode(parsedAudioPacket);
      }
    }
  }

  decode(parsedAudioPacket) {
    this.decoder.decode(parsedAudioPacket.messageData);
  }

  initDecoder() {
    if (!this.options.decoder) {
      return;
    }
    this.decoder = new this.options.decoder(this.options);
    this.decoder.ondata = (pcmData) => {
      /**
       * Incoming voice message packet decoded
       * @event IncomingMessage#incoming_voice_data_decoded
       * @param {Float32Array} pcmData decoded pcm packet
       */
      this.emit(Constants.EVENT_INCOMING_VOICE_DATA_DECODED, pcmData);
      this.session.onIncomingVoiceDecoded(pcmData, this);
    }
  }

  initPlayer() {
    if (IncomingMessage.PersistentPlayer && !this.options.noPersistentPlayer) {
      this.player = IncomingMessage.PersistentPlayer;
      this.player.setSampleRate(this.options.sampleRate);
      return;
    }
    if (!this.options.player) {
      return;
    }
    this.player = new this.options.player(this.options);
    if (this.options.noPersistentPlayer) {
      return;
    }
    IncomingMessage.PersistentPlayer = this.player;
  }
}


module.exports = IncomingMessage;