text: /**
   * @file AMR 录音、转换、播放器
   * @author BenzLeung(https://github.com/BenzLeung)
   * @date 2017/11/12
   * Created by JetBrains PhpStorm.
   *
   * 每位工程师都有保持代码优雅的义务
   * each engineer has a duty to keep the code elegant
   */
  /* eslint-disable no-underscore-dangle */
  import {
    decodeAudioArrayBufferByContext,
    getCtxSampleRate,
    playPcm,
    stopPcm,
  } from './audioContext';
  
  import AMR from '../library/amrnb';
  
  export default class BenzAMRRecorder {
  
    _isInit = false;
  
    _samples = new Float32Array(0);
  
    _rawData = new Uint8Array(0);
  
    _blob = null;
  
    _onEnded = null;
  
    _onPlay = null;
  
    _onStop = null;
  
    _onStartRecord = null;
  
    _onCancelRecord = null;
  
    _onFinishRecord = null;
  
    _isPlaying = false;
  
    /**
     * 是否已经初始化
     * @return {boolean}
     */
    isInit() {
      return this._isInit;
    }
  
    /**
     * 使用浮点数据初始化
     * @param {Float32Array} array
     * @return {Promise}
     */
    initWithArrayBuffer(array) {
      if (this._isInit) {
        throw new Error('AMR has been initialized. For a new AMR, please generate a new BenzAMRRecorder().');
      }
      return new Promise((resolve, reject) => {
        const u8Array = new Uint8Array(array);
        this._samples = AMR.decode(u8Array);
        this._isInit = true;
  
        if (!this._samples) {
          decodeAudioArrayBufferByContext(array).then((data) => {
            this._isInit = true;
            this._rawData = BenzAMRRecorder.encodeAMR(new Float32Array(data), getCtxSampleRate());
            this._samples = AMR.decode(this._rawData);
            this._blob = BenzAMRRecorder.rawAMRData2Blob(this._rawData);
            resolve();
          }).catch(() => {
            reject(new Error('Failed to decode.'));
          });
        } else {
          this._rawData = u8Array;
          resolve();
        }
      });
    }
  
    /**
     * 使用 Blob 对象初始化（ <input type="file">）
     * @param {Blob} blob
     * @return {Promise}
     */
    initWithBlob(blob) {
      if (this._isInit) {
        throw new Error('AMR has been initialized. For a new AMR, please generate a new BenzAMRRecorder().');
      }
      this._blob = blob;
      const p = new Promise((resolve) => {
        const reader = new window.FileReader();
        reader.onload = (e) => {
          resolve(e.target.result);
        };
        reader.readAsArrayBuffer(blob);
      });
      return p.then(data => this.initWithArrayBuffer(data));
    }
  
    /**
     * 使用 url 初始化
     * @param {string} url
     * @return {Promise}
     */
    initWithUrl(url) {
      if (this._isInit) {
        throw new Error('AMR has been initialized. For a new AMR, please generate a new BenzAMRRecorder().');
      }
      // 先播放一个空音频，
      // 因为有些环境（如iOS）播放首个音频时禁止自动、异步播放，
      // 播放空音频防止加载后立即播放的功能失效。
      // 但即使如此，initWithUrl 仍然须放入一个用户事件中
      playPcm(new Float32Array(10), 24000);
  
      const p = new Promise((resolve, reject) => {
        const xhr = new window.XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = () => {
          resolve(xhr.response);
        };
        xhr.onerror = () => {
          reject(new Error(`Failed to fetch ${url}`));
        };
        xhr.send();
      });
      return p.then(array => this.initWithArrayBuffer(array));
    }
  
    on(action, fn) {
      if (typeof fn === 'function') {
        switch (action) {
          case 'play':
            this._onPlay = fn;
            break;
          case 'stop':
            this._onStop = fn;
            break;
          case 'ended':
            this._onEnded = fn;
            break;
          default:
        }
      }
    }
  
    /**
     * 播放事件
     * @param {Function} fn
     */
    onPlay(fn) {
      this.on('play', fn);
    }
  
    /**
     * 停止事件（包括播放结束）
     * @param {Function} fn
     */
    onStop(fn) {
      this.on('stop', fn);
    }
  
    /**
     * 播放结束事件
     * @param {Function} fn
     */
    onEnded(fn) {
      this.on('ended', fn);
    }
  
    _onEndCallback() {
      this._isPlaying = false;
      if (this._onStop) {
        this._onStop();
      }
      if (this._onEnded) {
        this._onEnded();
      }
    }
  
    /**
     * 播放
     */
    play() {
      if (!this._isInit) {
        throw new Error('Please init AMR first.');
      }
      if (this._onPlay) {
        this._onPlay();
      }
      this._isPlaying = true;
      playPcm(
        this._samples,
        8000,
        this._onEndCallback.bind(this)
      );
    }
  
    /**
     * 停止
     */
    stop() {
      stopPcm();
      this._isPlaying = false;
      if (this._onStop) {
        this._onStop();
      }
    }
  
    /**
     * 是否正在播放
     * @return {boolean}
     */
    isPlaying() {
      return this._isPlaying;
    }
  
    /**
     * 获取音频的时间长度（单位：秒）
     * @return {Number}
     */
    getDuration() {
      const rate = 8000;
      return this._samples.length / rate;
    }
  
    getBlob() {
      return this._blob;
    }
  
    static encodeAMR(samples, sampleRate) {
      sampleRate = sampleRate || 8000;
      return AMR.encode(samples, sampleRate, 7);
    }
  
    static rawAMRData2Blob(data) {
      return new window.Blob([data.buffer], {
        type: 'audio/amr',
      });
    }
  }
  