constructor (player, canvas, options = {}) {
    this.canvas = canvas
    const { width, height } = this.canvas.getBoundingClientRect()
    this.canvas.width = width * 2
    this.canvas.height = height * 2
    this.player = player
    this.options = Analyze.defaultConfig
    Object.keys(options).map(key => {
      this.options[key] = options[key]
    })

    if (!Analyze.AudioCtx) {
      return
    }
    if (player.audioCtx) {
      this._audioCtx = player.audioCtx
    } else {
      this._audioCtx = new Analyze.AudioCtx()
    }
    this.analyser = this._audioCtx.createAnalyser()
    const gainNode = this._audioCtx.createGain()
    gainNode.gain.setValueAtTime(player.volume, player.currentTime)
    this.gainNode = gainNode
    this.source = this._audioCtx.createMediaElementSource(player.video)
    this.ctx = canvas.getContext('2d')
    this.source.connect(this.analyser)
    this.analyser.connect(gainNode)
    this.fftSize = this.options.fftSize
    this.frameCount = 0

    if (this.options.isGradient) {
      this.options.linear = getGradient(this.ctx, canvas.width, this.options.colors)
    }
    gainNode.connect(this._audioCtx.destination)
    this._initPlayerEvents()
  }
@constructor
@param { any } player
@param { HTMLElement } canvas
@param { IAnalyzeOptions } options
@returns