HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: //home/arjun/projects/buyercall/node_modules/vue-audio-visual/src/components/AvMedia.js
import * as vue from 'vue'
/**
 * Component props
 */
const props = {
  /**
   * prop: 'media'
   * MediaStream object for visualisation. Can be delivered by
   * Web Audio API functions like getUserMedia or RTCPeerConnection
   */
  media: {
    type: MediaStream,
    required: false,
    default: null
  },

  /**
   * prop: 'canv-width'
   * Canvas element width. Default 300
   */
  canvWidth: {
    type: Number,
    default: 300
  },

  /**
   * prop: 'canv-class'
   * Canvas element css class name.
   */
  canvClass: {
    type: String,
    default: null
  },

  /**
   * prop: 'canv-height'
   * Canvas element height. Default 80
   */
  canvHeight: {
    type: Number,
    default: 80
  },

  /**
   * prop: 'canv-fill-color'
   * Canvas fill background RGB color.
   * Default is transperent.
   */
  canvFillColor: {
    type: String,
    default: null
  },

  /**
   * prop: 'fft-size'
   * Represents the window size in samples that is used when performing
   * a Fast Fourier Transform (FFT) to get frequency domain data.
   * Must be power of 2 between 2^5 and 2^15
   * Default: 8192 for 'wform' 1024 for 'freq'
   */
  fftSize: {
    type: Number,
    default: null // 1024 // 8192
  },

  /**
   * prop: 'type'
   * Type of visualisation.
   * wform - using byte time domaine data
   * frequ - using byte frequency data
   * wform when not recognized.
   * Default: wform
   */
  type: {
    type: String,
    default: 'wform'
  },

  /**
   * prop: 'frequ-lnum'
   * Vertical lines number for frequ type.
   * Default: 60
   */
  frequLnum: {
    type: Number,
    default: 60
  },

  /**
   * prop: 'frequ-line-cap'
   * Draw line with rounded end caps.
   * Default: false
   */
  frequLineCap: {
    type: Boolean,
    default: false
  },

  /**
   * prop: 'frequ-direction'
   * Direction for frequency visualization.
   * lr - from left to right
   * mo - from middle out
   * lr when not recognized.
   * Default: wform
   */
  frequDirection: {
    type: String,
    default: 'lr'
  },

  /**
   * prop: 'line-color'
   * Line color.
   * Default: lime
   */
  lineColor: {
    type: String,
    default: 'lime'
  },

  /**
   * prop: 'line-width'
   * Line width.
   * Default: 0.5 for wform and 3 for frequ
   */
  lineWidth: {
    type: Number,
    default: null
  },

  /**
   * prop: 'radius'
   * Circle radius.
   * Default: 4 for circle
   */
  radius: {
    type: Number,
    default: 4
  },

  /**
   * prop: 'connect-destination'
   * Analyser to connect to audio context's destination
   * Default: false
   */
  connectDestination: {
    type: Boolean,
    default: false
  }
}

// A fix for webpack warnings across vue2 & vue3
const h = vue['h'[0]]

/**
 * Component AvMedia
 */
const AvMedia = {
  name: 'av-media',
  data () {
    return {
      ctx: null,
      audioCtx: null,
      analyser: null
    }
  },
  props,
  render (hv2) {
    if (h) {
      // Vue3 render
      return h('div')
    }
    // Vue2 render
    return hv2('div')
  },
  mounted () {
    this.createCanvas()
  },
  watch: {
    media: function (newVal, oldVal) {
      try {
        if (newVal) this.setAnalyser()
        this.draw()
      } catch (err) {
        console.log('Failed create analyser', err)
      }
    }
  },
  methods: {
    /**
     * Create Canvas inside div
     */
    createCanvas: function () {
      const canv = document.createElement('canvas')
      canv.width = this.canvWidth
      canv.height = this.canvHeight
      if (this.canvClass) canv.setAttribute('class', this.canvClass)
      this.ctx = canv.getContext('2d')
      this.$el.appendChild(canv)
    },

    /**
     * Set analyser
     */
    setAnalyser: function () {
      this.audioCtx = this.audioCtx || new AudioContext()
      this.analyser = this.analyser || this.audioCtx.createAnalyser()
      const src = this.audioCtx.createMediaStreamSource(this.media)

      src.connect(this.analyser)
      if (this.fftSize) {
        this.analyser.fftSize = this.fftSize
      } else {
        this.analyser.fftSize = this.type === 'frequ' ? 1024 : 8192
      }
      if (this.connectDestination) {
        this.analyser.connect(this.audioCtx.destination)
      }
    },

    draw: function () {
      const data = new Uint8Array(this.analyser.fftSize)

      if (this.canvFillColor) this.ctx.fillStyle = this.canvFillColor
      this.ctx.clearRect(0, 0, this.canvWidth, this.canvHeight)
      this.ctx.beginPath()
      this.ctx.strokeStyle = this.lineColor

      if (this.type === 'frequ') {
        this.analyser.getByteFrequencyData(data)
        this.frequ(data)
      } else if (this.type === 'circle') {
        this.analyser.getByteFrequencyData(data)
        this.circle(data)
      } else {
        this.analyser.getByteTimeDomainData(data)
        this.wform(data)
      }

      requestAnimationFrame(this.draw)
    },

    wform: function (data) {
      const h = this.canvHeight
      const step = this.canvWidth / this.analyser.fftSize
      let x = 0
      this.ctx.lineWidth = this.lineWidth || 0.5
      data.forEach(v => {
        const y = (v / 255.0) * h
        this.ctx.lineTo(x, y)
        x += step
      })
      this.ctx.stroke()
    },

    frequ: function (data) {
      const middleOut = this.frequDirection === 'mo'
      const start = middleOut ? this.canvWidth / 2 : 0
      const c = middleOut ? this.frequLnum / 2 : this.frequLnum
      const step = middleOut ? this.canvWidth / c / 2 : this.canvWidth / c
      const h = this.canvHeight
      const lw = this.lineWidth || 2
      for (let i = 0; i < c; i++) {
        const x = middleOut ? i * step : i * step + lw
        const v = data.slice(x, x + step).reduce((sum, v) => sum + (v / 255.0 * h), 0) / step
        const space = (h - v) / 2 + 2 // + 2 is space for caps
        this.ctx.lineWidth = lw
        this.ctx.lineCap = this.frequLineCap ? 'round' : 'butt'
        this.ctx.moveTo(start + x, space)
        this.ctx.lineTo(start + x, h - space)
        this.ctx.stroke()

        if (middleOut && i > 0) {
          this.ctx.moveTo(start - x, space)
          this.ctx.lineTo(start - x, h - space)
          this.ctx.stroke()
        }
      }
    },

    circle: function (data) {
      const cx = this.canvWidth / 2 // center X
      const cy = this.canvHeight / 2 // center Y
      const r = this.radius || 4
      const lineWidth = this.lineWidth
      const lineSpace = 10
      const arcStep = Math.ceil(lineWidth + lineSpace)
      const step = ((lineWidth + lineSpace) / data.length) * (2 * Math.PI)
      const barLen = this.canvWidth / 1.2 - r
      let angle = Math.PI

      this.ctx.lineWidth = this.lineWidth || 0.5

      data.forEach((_, index) => {
        angle += step
        if (index % arcStep) {
          return
        }

        const bits = Math.round(
          data.slice(index, index + arcStep).reduce((v, t) => t + v, 0) /
            arcStep
        )

        const blen = r + (bits / 255.0) * barLen
        this.ctx.beginPath()
        this.ctx.lineCap = 'round'
        this.ctx.moveTo(r * Math.cos(angle) + cx, r * Math.sin(angle) + cy)
        this.ctx.lineTo(
          blen * Math.cos(angle) + cx,
          blen * Math.sin(angle) + cy
        )
        this.ctx.stroke()
      })
    }
  }
}

export default AvMedia