Initial commit
This commit is contained in:
		
						commit
						b7defdf5d8
					
				
					 35 changed files with 2343 additions and 0 deletions
				
			
		
							
								
								
									
										706
									
								
								Home/LTI/src/main.dev.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										706
									
								
								Home/LTI/src/main.dev.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,706 @@
 | 
			
		|||
/* global
 | 
			
		||||
  Audio,
 | 
			
		||||
  AudioContext,
 | 
			
		||||
  FileReader,
 | 
			
		||||
  Plotly,
 | 
			
		||||
  Vue,
 | 
			
		||||
  cancelAnimationFrame,
 | 
			
		||||
  fetch,
 | 
			
		||||
  math,
 | 
			
		||||
  requestAnimationFrame
 | 
			
		||||
*/
 | 
			
		||||
Vue.config.devtools = true
 | 
			
		||||
 | 
			
		||||
const figureIn = {
 | 
			
		||||
  data: [
 | 
			
		||||
    {
 | 
			
		||||
      line: {
 | 
			
		||||
        dash: 'solid',
 | 
			
		||||
        color: 'rgb(0,113.985,188.955)',
 | 
			
		||||
        width: 1
 | 
			
		||||
      },
 | 
			
		||||
      mode: 'lines+markers',
 | 
			
		||||
      name: '',
 | 
			
		||||
      type: 'scatter',
 | 
			
		||||
      x: [],
 | 
			
		||||
      y: [],
 | 
			
		||||
      xaxis: 'x',
 | 
			
		||||
      yaxis: 'y',
 | 
			
		||||
      marker: {
 | 
			
		||||
        line: {
 | 
			
		||||
          width: 1,
 | 
			
		||||
          color: []
 | 
			
		||||
        },
 | 
			
		||||
        size: 8,
 | 
			
		||||
        symbol: 'circle',
 | 
			
		||||
        color: []
 | 
			
		||||
      },
 | 
			
		||||
      visible: true,
 | 
			
		||||
      showlegend: true
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  layout: {
 | 
			
		||||
    title: '',
 | 
			
		||||
    width: 840,
 | 
			
		||||
    xaxis: {
 | 
			
		||||
      side: 'bottom',
 | 
			
		||||
      type: 'linear',
 | 
			
		||||
      range: [],
 | 
			
		||||
      ticks: 'inside',
 | 
			
		||||
      anchor: 'y',
 | 
			
		||||
      domain: [0.13, 0.905],
 | 
			
		||||
      mirror: 'ticks',
 | 
			
		||||
      nticks: 9,
 | 
			
		||||
      ticklen: 6.51,
 | 
			
		||||
      showgrid: false,
 | 
			
		||||
      showline: true,
 | 
			
		||||
      tickfont: {
 | 
			
		||||
        size: 10,
 | 
			
		||||
        color: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
        family: 'Arial, sans-serif'
 | 
			
		||||
      },
 | 
			
		||||
      tickmode: 'auto',
 | 
			
		||||
      zeroline: false,
 | 
			
		||||
      autorange: true,
 | 
			
		||||
      gridcolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      gridwidth: 1,
 | 
			
		||||
      linecolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      linewidth: 1,
 | 
			
		||||
      tickcolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      tickwidth: 1,
 | 
			
		||||
      titlefont: {
 | 
			
		||||
        size: 11,
 | 
			
		||||
        color: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
        family: 'Arial, sans-serif'
 | 
			
		||||
      },
 | 
			
		||||
      exponentformat: 'none'
 | 
			
		||||
    },
 | 
			
		||||
    yaxis: {
 | 
			
		||||
      side: 'left',
 | 
			
		||||
      type: 'linear',
 | 
			
		||||
      range: [],
 | 
			
		||||
      ticks: 'inside',
 | 
			
		||||
      anchor: 'x',
 | 
			
		||||
      domain: [0.11, 0.925],
 | 
			
		||||
      mirror: 'ticks',
 | 
			
		||||
      nticks: 12,
 | 
			
		||||
      ticklen: 6.51,
 | 
			
		||||
      showgrid: false,
 | 
			
		||||
      showline: true,
 | 
			
		||||
      tickfont: {
 | 
			
		||||
        size: 10,
 | 
			
		||||
        color: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
        family: 'Arial, sans-serif'
 | 
			
		||||
      },
 | 
			
		||||
      tickmode: 'auto',
 | 
			
		||||
      zeroline: true,
 | 
			
		||||
      autorange: false,
 | 
			
		||||
      gridcolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      gridwidth: 1,
 | 
			
		||||
      linecolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      linewidth: 1,
 | 
			
		||||
      tickcolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      tickwidth: 1,
 | 
			
		||||
      titlefont: {
 | 
			
		||||
        size: 11,
 | 
			
		||||
        color: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
        family: 'Arial, sans-serif'
 | 
			
		||||
      },
 | 
			
		||||
      exponentformat: 'none',
 | 
			
		||||
      showticklabels: true
 | 
			
		||||
    },
 | 
			
		||||
    height: 480,
 | 
			
		||||
    margin: {
 | 
			
		||||
      b: 0,
 | 
			
		||||
      l: 0,
 | 
			
		||||
      r: 0,
 | 
			
		||||
      t: 0,
 | 
			
		||||
      pad: 0
 | 
			
		||||
    },
 | 
			
		||||
    autosize: false,
 | 
			
		||||
    hovermode: 'closest',
 | 
			
		||||
    titlefont: {
 | 
			
		||||
      color: 'rgba(0,0,0,0)'
 | 
			
		||||
    },
 | 
			
		||||
    showlegend: false,
 | 
			
		||||
    annotations: [
 | 
			
		||||
      {
 | 
			
		||||
        x: 0.5175,
 | 
			
		||||
        y: 0.935,
 | 
			
		||||
        font: {
 | 
			
		||||
          size: 11,
 | 
			
		||||
          color: 'rgb(0,0,0)',
 | 
			
		||||
          family: 'Arial, sans-serif'
 | 
			
		||||
        },
 | 
			
		||||
        text: '',
 | 
			
		||||
        xref: 'paper',
 | 
			
		||||
        yref: 'paper',
 | 
			
		||||
        align: 'center',
 | 
			
		||||
        xanchor: 'center',
 | 
			
		||||
        yanchor: 'bottom',
 | 
			
		||||
        borderpad: 3,
 | 
			
		||||
        showarrow: false,
 | 
			
		||||
        textangle: 0,
 | 
			
		||||
        bordercolor: 'rgba(0,0,0,0)',
 | 
			
		||||
        borderwidth: 0.5
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    plot_bgcolor: 'rgba(0,0,0,0)',
 | 
			
		||||
    paper_bgcolor: 'rgb(255,255,255)'
 | 
			
		||||
  },
 | 
			
		||||
  frames: []
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const figureMag = {
 | 
			
		||||
  data: [
 | 
			
		||||
    {
 | 
			
		||||
      line: {
 | 
			
		||||
        dash: 'solid',
 | 
			
		||||
        color: 'rgb(0,113.985,188.955)',
 | 
			
		||||
        width: 1
 | 
			
		||||
      },
 | 
			
		||||
      mode: 'lines',
 | 
			
		||||
      name: '',
 | 
			
		||||
      type: 'scatter',
 | 
			
		||||
      x: [],
 | 
			
		||||
      y: [],
 | 
			
		||||
      xaxis: 'x',
 | 
			
		||||
      yaxis: 'y',
 | 
			
		||||
      marker: {
 | 
			
		||||
        line: {
 | 
			
		||||
          width: 1,
 | 
			
		||||
          color: []
 | 
			
		||||
        },
 | 
			
		||||
        size: 8,
 | 
			
		||||
        symbol: 'circle',
 | 
			
		||||
        color: []
 | 
			
		||||
      },
 | 
			
		||||
      visible: true,
 | 
			
		||||
      showlegend: true
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  layout: {
 | 
			
		||||
    title: '',
 | 
			
		||||
    width: 840,
 | 
			
		||||
    xaxis: {
 | 
			
		||||
      title: 'Normalized Frequency (× π rad/sample)',
 | 
			
		||||
      side: 'bottom',
 | 
			
		||||
      type: 'linear',
 | 
			
		||||
      range: [0, 2],
 | 
			
		||||
      ticks: 'inside',
 | 
			
		||||
      anchor: 'y',
 | 
			
		||||
      domain: [0.13, 0.905],
 | 
			
		||||
      mirror: 'ticks',
 | 
			
		||||
      nticks: 9,
 | 
			
		||||
      ticklen: 6.51,
 | 
			
		||||
      showgrid: true,
 | 
			
		||||
      showline: true,
 | 
			
		||||
      tickfont: {
 | 
			
		||||
        size: 10,
 | 
			
		||||
        color: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
        family: 'Arial, sans-serif'
 | 
			
		||||
      },
 | 
			
		||||
      tickmode: 'array',
 | 
			
		||||
      tickvals: [0, 0.2, 0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2],
 | 
			
		||||
      zeroline: false,
 | 
			
		||||
      autorange: true,
 | 
			
		||||
      gridcolor: 'rgba(38.25,38.25,38.25,0.25)',
 | 
			
		||||
      gridwidth: 1,
 | 
			
		||||
      linecolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      linewidth: 1,
 | 
			
		||||
      tickcolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      tickwidth: 1,
 | 
			
		||||
      titlefont: {
 | 
			
		||||
        size: 18,
 | 
			
		||||
        color: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
        family: 'serif'
 | 
			
		||||
      },
 | 
			
		||||
      exponentformat: 'none'
 | 
			
		||||
    },
 | 
			
		||||
    yaxis: {
 | 
			
		||||
      title: 'Magnitude (dB)',
 | 
			
		||||
      side: 'left',
 | 
			
		||||
      type: 'linear',
 | 
			
		||||
      range: [],
 | 
			
		||||
      ticks: 'inside',
 | 
			
		||||
      anchor: 'x',
 | 
			
		||||
      domain: [0.11, 0.925],
 | 
			
		||||
      mirror: 'ticks',
 | 
			
		||||
      nticks: 12,
 | 
			
		||||
      ticklen: 6.51,
 | 
			
		||||
      showgrid: true,
 | 
			
		||||
      showline: true,
 | 
			
		||||
      tickfont: {
 | 
			
		||||
        size: 10,
 | 
			
		||||
        color: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
        family: 'Arial, sans-serif'
 | 
			
		||||
      },
 | 
			
		||||
      tickmode: 'auto',
 | 
			
		||||
      zeroline: false,
 | 
			
		||||
      autorange: true,
 | 
			
		||||
      gridcolor: 'rgba(38.25,38.25,38.25,0.25)',
 | 
			
		||||
      gridwidth: 1,
 | 
			
		||||
      linecolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      linewidth: 1,
 | 
			
		||||
      tickcolor: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
      tickwidth: 1,
 | 
			
		||||
      titlefont: {
 | 
			
		||||
        size: 18,
 | 
			
		||||
        color: 'rgb(38.25,38.25,38.25)',
 | 
			
		||||
        family: 'serif'
 | 
			
		||||
      },
 | 
			
		||||
      exponentformat: 'none',
 | 
			
		||||
      showticklabels: true
 | 
			
		||||
    },
 | 
			
		||||
    height: 480,
 | 
			
		||||
    margin: {
 | 
			
		||||
      b: 0,
 | 
			
		||||
      l: 0,
 | 
			
		||||
      r: 0,
 | 
			
		||||
      t: 0,
 | 
			
		||||
      pad: 0
 | 
			
		||||
    },
 | 
			
		||||
    autosize: false,
 | 
			
		||||
    hovermode: 'closest',
 | 
			
		||||
    titlefont: {
 | 
			
		||||
      color: 'rgba(38.25,38.25,38.25,1)'
 | 
			
		||||
    },
 | 
			
		||||
    showlegend: false,
 | 
			
		||||
    annotations: [
 | 
			
		||||
      {
 | 
			
		||||
        x: 0.5175,
 | 
			
		||||
        y: 0.935,
 | 
			
		||||
        font: {
 | 
			
		||||
          size: 11,
 | 
			
		||||
          color: 'rgb(0,0,0)',
 | 
			
		||||
          family: 'Arial, sans-serif'
 | 
			
		||||
        },
 | 
			
		||||
        text: '',
 | 
			
		||||
        xref: 'paper',
 | 
			
		||||
        yref: 'paper',
 | 
			
		||||
        align: 'center',
 | 
			
		||||
        xanchor: 'center',
 | 
			
		||||
        yanchor: 'bottom',
 | 
			
		||||
        borderpad: 3,
 | 
			
		||||
        showarrow: false,
 | 
			
		||||
        textangle: 0,
 | 
			
		||||
        bordercolor: 'rgba(0,0,0,0)',
 | 
			
		||||
        borderwidth: 0.5
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    plot_bgcolor: 'rgba(0,0,0,0)',
 | 
			
		||||
    paper_bgcolor: 'rgb(255,255,255)'
 | 
			
		||||
  },
 | 
			
		||||
  frames: []
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const figureOut = JSON.parse(JSON.stringify(figureIn))
 | 
			
		||||
const figurePhase = JSON.parse(JSON.stringify(figureMag))
 | 
			
		||||
 | 
			
		||||
window.app = new Vue({
 | 
			
		||||
  el: '#root',
 | 
			
		||||
  data: {
 | 
			
		||||
    figureIn: figureIn,
 | 
			
		||||
    figureOut: figureOut,
 | 
			
		||||
    figureMag: figureMag,
 | 
			
		||||
    figurePhase: figurePhase,
 | 
			
		||||
    p: 0,
 | 
			
		||||
    q: 0,
 | 
			
		||||
    a: [1],
 | 
			
		||||
    b: [1],
 | 
			
		||||
    w: [],
 | 
			
		||||
    x: [1].concat(Array(19).fill(0)),
 | 
			
		||||
    y: [1].concat(Array(19).fill(0)),
 | 
			
		||||
    z: [],
 | 
			
		||||
    preset: 'd',
 | 
			
		||||
    length: 20,
 | 
			
		||||
    isTrig: false,
 | 
			
		||||
    A: 1,
 | 
			
		||||
    T: 19,
 | 
			
		||||
    tao: 0,
 | 
			
		||||
    scaler: 1,
 | 
			
		||||
    reverse: false,
 | 
			
		||||
    toPower: false,
 | 
			
		||||
    readonly: true,
 | 
			
		||||
    stem: true,
 | 
			
		||||
    systemFile: '',
 | 
			
		||||
    fileReader: new FileReader()
 | 
			
		||||
  },
 | 
			
		||||
  methods: {
 | 
			
		||||
    systemFileHandler () {
 | 
			
		||||
      const file = document.querySelector('#systemFile').files[0]
 | 
			
		||||
      if (file) {
 | 
			
		||||
        this.systemFile = file
 | 
			
		||||
        this.fileReader.readAsText(this.systemFile)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    getDegreeKey (s) {
 | 
			
		||||
      // convert 'a' -> 'p', 'b' -> 'q'
 | 
			
		||||
      // 'p' - 'a' == 15
 | 
			
		||||
      return String.fromCharCode(s.charCodeAt(0) + 15)
 | 
			
		||||
    },
 | 
			
		||||
    system (s) {
 | 
			
		||||
      this.truncate()
 | 
			
		||||
      const old = this[s].slice(0)
 | 
			
		||||
      const len = this[this.getDegreeKey(s)] + 1
 | 
			
		||||
      if (len > old.length) {
 | 
			
		||||
        this[s] = Array(len).fill(1)
 | 
			
		||||
        Array.prototype.splice.apply(this[s], [0, old.length].concat(old))
 | 
			
		||||
      } else {
 | 
			
		||||
        this[s] = old.slice(0, len)
 | 
			
		||||
      }
 | 
			
		||||
      this.output()
 | 
			
		||||
    },
 | 
			
		||||
    input (name, length) {
 | 
			
		||||
      const omega = (2 * Math.PI) / this.T
 | 
			
		||||
      const fn = {
 | 
			
		||||
        d: [1].concat(Array(length - 1).fill(0)),
 | 
			
		||||
        u: Array(length).fill(1),
 | 
			
		||||
        sin: [...Array(length).keys()].map(
 | 
			
		||||
          x => this.A * Math.sin(omega * (x + this.tao))
 | 
			
		||||
        ),
 | 
			
		||||
        cos: [...Array(length).keys()].map(
 | 
			
		||||
          x => this.A * Math.cos(omega * (x + this.tao))
 | 
			
		||||
        ),
 | 
			
		||||
        exp: [...Array(length).keys()].map(x => Math.exp(x)),
 | 
			
		||||
        log: [...Array(length).keys()].map(x => Math.log(x + 1)),
 | 
			
		||||
        n: [...Array(length).keys()],
 | 
			
		||||
        c: this.x
 | 
			
		||||
      }
 | 
			
		||||
      return fn[name]
 | 
			
		||||
    },
 | 
			
		||||
    output () {
 | 
			
		||||
      const d = this.input('d', this.length)
 | 
			
		||||
      const h = this.filter(this.b, this.a, d)
 | 
			
		||||
      const x = this.input(this.preset, this.length)
 | 
			
		||||
      this.x = x.map((x, n) => this.scale(x, n))
 | 
			
		||||
      if (this.reverse) this.x = this.x.reverse()
 | 
			
		||||
      this.y = this.conv(this.x, h).slice(0, this.length)
 | 
			
		||||
      this.redrawIO(this.x, this.figureIn)
 | 
			
		||||
      this.redrawIO(this.y, this.figureOut)
 | 
			
		||||
    },
 | 
			
		||||
    scale (x, n) {
 | 
			
		||||
      const s = Math.pow(this.scaler, this.toPower ? n : 1)
 | 
			
		||||
      return s * x
 | 
			
		||||
    },
 | 
			
		||||
    presetHandler () {
 | 
			
		||||
      const s = this.preset
 | 
			
		||||
      if (s === 'c') {
 | 
			
		||||
        this.scaler = 1
 | 
			
		||||
        this.toPower = false
 | 
			
		||||
        this.readonly = false
 | 
			
		||||
      } else {
 | 
			
		||||
        this.length = 20
 | 
			
		||||
        this.x = this.input(s, this.length)
 | 
			
		||||
        this.readonly = true
 | 
			
		||||
        if (s === 'sin' || s === 'cos') {
 | 
			
		||||
          this.isTrig = true
 | 
			
		||||
        } else {
 | 
			
		||||
          this.isTrig = false
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      this.output()
 | 
			
		||||
    },
 | 
			
		||||
    lengthHandler () {
 | 
			
		||||
      this.truncate()
 | 
			
		||||
      if (this.length > this.x.length) {
 | 
			
		||||
        const s = this.preset
 | 
			
		||||
        if (s === 'c') {
 | 
			
		||||
          const diff = this.length - this.x.length
 | 
			
		||||
          this.x = this.x.concat(Array(diff).fill(0))
 | 
			
		||||
        } else {
 | 
			
		||||
          this.x = this.input(s, this.length)
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        this.x.length = this.length
 | 
			
		||||
      }
 | 
			
		||||
      this.output()
 | 
			
		||||
    },
 | 
			
		||||
    inputHandler () {
 | 
			
		||||
      this.x = this.x.split(',')
 | 
			
		||||
      this.length = this.x.length
 | 
			
		||||
      this.output()
 | 
			
		||||
    },
 | 
			
		||||
    trigHandler () {
 | 
			
		||||
      this.truncate()
 | 
			
		||||
      this.output()
 | 
			
		||||
    },
 | 
			
		||||
    truncate () {
 | 
			
		||||
      const keys = ['p', 'q', 'length', 'T', 'tao']
 | 
			
		||||
      keys.forEach(key => {
 | 
			
		||||
        this[key] = Math.trunc(this[key])
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    // view helper function
 | 
			
		||||
    firstTerm (a, i) {
 | 
			
		||||
      for (let j = 0; j < i; j += 1) {
 | 
			
		||||
        if (a[j] !== 0) return false
 | 
			
		||||
      }
 | 
			
		||||
      return true
 | 
			
		||||
    },
 | 
			
		||||
    // view helper function
 | 
			
		||||
    allZero (a) {
 | 
			
		||||
      for (const i of a) {
 | 
			
		||||
        if (i !== 0) return false
 | 
			
		||||
      }
 | 
			
		||||
      return true
 | 
			
		||||
    },
 | 
			
		||||
    filter (b, a, x) {
 | 
			
		||||
      if (a[0] !== 0) {
 | 
			
		||||
        const n = a.length - 1
 | 
			
		||||
        const m = b.length - 1
 | 
			
		||||
        const y = []
 | 
			
		||||
        for (let i = 0; i < x.length; i += 1) {
 | 
			
		||||
          let lhs = 0
 | 
			
		||||
          let rhs = 0
 | 
			
		||||
          for (let j = 1; j <= n && j <= i; j += 1) {
 | 
			
		||||
            lhs = lhs + a[j] * y[i - j]
 | 
			
		||||
          }
 | 
			
		||||
          for (let j = 0; j <= m && j <= i; j += 1) {
 | 
			
		||||
            rhs = rhs + b[j] * x[i - j]
 | 
			
		||||
          }
 | 
			
		||||
          y[i] = (rhs - lhs) / a[0]
 | 
			
		||||
        }
 | 
			
		||||
        return y
 | 
			
		||||
      }
 | 
			
		||||
      return x
 | 
			
		||||
    },
 | 
			
		||||
    conv (x, h) {
 | 
			
		||||
      let disp = 0
 | 
			
		||||
      const y = []
 | 
			
		||||
      for (let k = 0; k < h.length; k += 1) {
 | 
			
		||||
        y.push(x[0] * h[k])
 | 
			
		||||
      }
 | 
			
		||||
      disp = disp + 1
 | 
			
		||||
      for (let j = 1; j < x.length; j += 1) {
 | 
			
		||||
        for (let k = 0; k < h.length; k += 1) {
 | 
			
		||||
          if (disp + k !== y.length) {
 | 
			
		||||
            y[disp + k] = y[disp + k] + x[j] * h[k]
 | 
			
		||||
          } else {
 | 
			
		||||
            y.push(x[j] * h[k])
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        disp = disp + 1
 | 
			
		||||
      }
 | 
			
		||||
      return y
 | 
			
		||||
    },
 | 
			
		||||
    redrawIO (data, figure) {
 | 
			
		||||
      const x = figure.data[0].x
 | 
			
		||||
      const y = figure.data[0].y
 | 
			
		||||
      const lineColor = figure.data[0].marker.line.color
 | 
			
		||||
      const color = figure.data[0].marker.color
 | 
			
		||||
      x.length = 0
 | 
			
		||||
      y.length = 0
 | 
			
		||||
      lineColor.length = 0
 | 
			
		||||
      color.length = 0
 | 
			
		||||
      for (let i = 0; i < this.length; i += 1) {
 | 
			
		||||
        if (this.stem) {
 | 
			
		||||
          figure.data[0].mode = 'lines+markers'
 | 
			
		||||
          x.push(i, i, null)
 | 
			
		||||
          y.push(0, data[i], null)
 | 
			
		||||
          lineColor.push(
 | 
			
		||||
            'rgba(0,0,0,0)',
 | 
			
		||||
            'rgb(0,113.985,188.955)',
 | 
			
		||||
            'rgba(0,0,0,0)'
 | 
			
		||||
          )
 | 
			
		||||
          color.push('rgba(0,0,0,0)', 'rgba(0,0,0,0)', 'rgba(0,0,0,0)')
 | 
			
		||||
        } else {
 | 
			
		||||
          figure.data[0].mode = 'lines'
 | 
			
		||||
          x.push(i)
 | 
			
		||||
          y.push(data[i])
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      Plotly.redraw(document.getElementById('input'))
 | 
			
		||||
      Plotly.redraw(document.getElementById('output'))
 | 
			
		||||
    },
 | 
			
		||||
    freqz (ob, oa, oz) {
 | 
			
		||||
      let b = ob.slice()
 | 
			
		||||
      let a = oa.slice()
 | 
			
		||||
      if (b.length > a.length) {
 | 
			
		||||
        a = a.slice().concat(Array(b.length - a.length).fill(0))
 | 
			
		||||
      } else if (b.length < a.length) {
 | 
			
		||||
        b = b.slice().concat(Array(a.length - b.length).fill(0))
 | 
			
		||||
      }
 | 
			
		||||
      const H = []
 | 
			
		||||
      for (let i = 0; i < oz.length; i += 1) {
 | 
			
		||||
        const z = math.conj(oz[i])
 | 
			
		||||
        let B = math.complex(0, 0)
 | 
			
		||||
        let A = math.complex(0, 0)
 | 
			
		||||
        for (let i = 0; i < b.length; i += 1) {
 | 
			
		||||
          B = math.add(B, math.multiply(b[i], math.pow(z, i)))
 | 
			
		||||
          A = math.add(A, math.multiply(a[i], math.pow(z, i)))
 | 
			
		||||
        }
 | 
			
		||||
        H.push(math.round(math.divide(B, A), 5))
 | 
			
		||||
      }
 | 
			
		||||
      return H
 | 
			
		||||
    },
 | 
			
		||||
    unwrap (op) {
 | 
			
		||||
      const p = op.slice()
 | 
			
		||||
      const dp = []
 | 
			
		||||
      const dps = []
 | 
			
		||||
      const dpcorr = []
 | 
			
		||||
      const cumsum = []
 | 
			
		||||
      const cutoff = Math.PI
 | 
			
		||||
      for (let i = 0; i < p.length - 1; i += 1) {
 | 
			
		||||
        dp[i] = p[i + 1] - p[i]
 | 
			
		||||
      }
 | 
			
		||||
      for (let i = 0; i < p.length - 1; i += 1) {
 | 
			
		||||
        dps[i] =
 | 
			
		||||
          dp[i] +
 | 
			
		||||
          Math.PI -
 | 
			
		||||
          Math.floor((dp[i] + Math.PI) / (2 * Math.PI)) * (2 * Math.PI) -
 | 
			
		||||
          Math.PI
 | 
			
		||||
      }
 | 
			
		||||
      for (let i = 0; i < p.length - 1; i += 1) {
 | 
			
		||||
        if (dps[i] === -Math.PI && dp[i] > 0) {
 | 
			
		||||
          dps[i] = Math.PI
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      for (let i = 0; i < p.length - 1; i += 1) {
 | 
			
		||||
        dpcorr[i] = dps[i] - dp[i]
 | 
			
		||||
      }
 | 
			
		||||
      for (let i = 0; i < p.length - 1; i += 1) {
 | 
			
		||||
        if (Math.abs(dp[i]) < cutoff) {
 | 
			
		||||
          dpcorr[i] = 0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      cumsum[0] = dpcorr[0]
 | 
			
		||||
      for (let i = 1; i < p.length - 1; i += 1) {
 | 
			
		||||
        cumsum[i] = cumsum[i - 1] + dpcorr[i]
 | 
			
		||||
      }
 | 
			
		||||
      for (let i = 1; i < p.length; i += 1) {
 | 
			
		||||
        p[i] += cumsum[i - 1]
 | 
			
		||||
      }
 | 
			
		||||
      return p
 | 
			
		||||
    },
 | 
			
		||||
    redrawFR () {
 | 
			
		||||
      const mags1 = []
 | 
			
		||||
      const mags2 = []
 | 
			
		||||
      const phases1 = []
 | 
			
		||||
      const phases2 = []
 | 
			
		||||
      const H = this.freqz(this.b, this.a, this.z)
 | 
			
		||||
      for (let i = 0; i < H.length; i += 1) {
 | 
			
		||||
        const mag = Math.sqrt(Math.pow(H[i].re, 2) + Math.pow(H[i].im, 2))
 | 
			
		||||
        const phase = atan2(H[i].im, H[i].re)
 | 
			
		||||
        if (i < 512) {
 | 
			
		||||
          mags1.push(20 * Math.log10(mag))
 | 
			
		||||
          phases1.push(phase)
 | 
			
		||||
        } else {
 | 
			
		||||
          mags2.push(20 * Math.log10(mag))
 | 
			
		||||
          phases2.push(phase)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      const unwrap1 = this.unwrap(phases1).map(x => (x * 180) / Math.PI)
 | 
			
		||||
      const unwrap2 = this.unwrap(phases2).map(x => (x * 180) / Math.PI)
 | 
			
		||||
      mags1[511] = null
 | 
			
		||||
      unwrap1[511] = null
 | 
			
		||||
      mags2[0] = null
 | 
			
		||||
      unwrap2[0] = null
 | 
			
		||||
      figureMag.data[0].y = mags1.concat(mags2)
 | 
			
		||||
      figurePhase.data[0].y = unwrap1.concat(unwrap2)
 | 
			
		||||
      Plotly.redraw(document.getElementById('magnitude'))
 | 
			
		||||
      Plotly.redraw(document.getElementById('phase'))
 | 
			
		||||
      function atan2 (im, re) {
 | 
			
		||||
        if (im === 0 && re === 0) {
 | 
			
		||||
          return 0
 | 
			
		||||
        }
 | 
			
		||||
        return Math.atan2(im, re)
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    drawBuffer (width, height, context, buffer) {
 | 
			
		||||
      context.clearRect(0, 0, width, height)
 | 
			
		||||
      const data = buffer
 | 
			
		||||
      const step = Math.ceil(data.length / width)
 | 
			
		||||
      const amp = height / 2
 | 
			
		||||
      for (let i = 0; i < width; i++) {
 | 
			
		||||
        let min = 1.0
 | 
			
		||||
        let max = -1.0
 | 
			
		||||
        for (let j = 0; j < step; j++) {
 | 
			
		||||
          const datum = data[i * step + j]
 | 
			
		||||
          if (datum < min) min = datum
 | 
			
		||||
          if (datum > max) max = datum
 | 
			
		||||
        }
 | 
			
		||||
        context.fillStyle = '#0072BD'
 | 
			
		||||
        context.fillRect(i, (1 + min) * amp, 1, Math.max(1, (max - min) * amp))
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    reverb () {
 | 
			
		||||
      const canvas = document.getElementById('waveform3')
 | 
			
		||||
      const audioSource = new Audio('asset/sample.mp3')
 | 
			
		||||
      const audioCtx = new AudioContext()
 | 
			
		||||
      const audioInput = audioCtx.createMediaElementSource(audioSource)
 | 
			
		||||
      const convolver = audioCtx.createConvolver()
 | 
			
		||||
      const gain = audioCtx.createGain()
 | 
			
		||||
      const analyser = audioCtx.createAnalyser()
 | 
			
		||||
      const waveform = new Float32Array(analyser.frequencyBinCount)
 | 
			
		||||
      let request
 | 
			
		||||
      let chunks = new Float32Array(0)
 | 
			
		||||
      gain.gain.value = 3.5
 | 
			
		||||
      audioSource.onended = e => {
 | 
			
		||||
        cancelAnimationFrame(request)
 | 
			
		||||
        audioCtx.close()
 | 
			
		||||
        this.drawBuffer(
 | 
			
		||||
          canvas.width,
 | 
			
		||||
          canvas.height,
 | 
			
		||||
          canvas.getContext('2d'),
 | 
			
		||||
          chunks
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      fetch('asset/clap.mp3')
 | 
			
		||||
        .then(response => response.arrayBuffer())
 | 
			
		||||
        .then(arrayBuffer => audioCtx.decodeAudioData(arrayBuffer))
 | 
			
		||||
        .then(buffer => {
 | 
			
		||||
          convolver.buffer = buffer
 | 
			
		||||
          audioInput
 | 
			
		||||
            .connect(convolver)
 | 
			
		||||
            .connect(gain)
 | 
			
		||||
            .connect(analyser)
 | 
			
		||||
            .connect(audioCtx.destination)
 | 
			
		||||
          audioSource.play()
 | 
			
		||||
          updateWaveform()
 | 
			
		||||
        })
 | 
			
		||||
      function updateWaveform () {
 | 
			
		||||
        request = requestAnimationFrame(updateWaveform)
 | 
			
		||||
        analyser.getFloatTimeDomainData(waveform)
 | 
			
		||||
        chunks = Float32Concat(chunks, waveform)
 | 
			
		||||
      }
 | 
			
		||||
      function Float32Concat (first, second) {
 | 
			
		||||
        const firstLength = first.length
 | 
			
		||||
        const result = new Float32Array(firstLength + second.length)
 | 
			
		||||
        result.set(first)
 | 
			
		||||
        result.set(second, firstLength)
 | 
			
		||||
        return result
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  mounted () {
 | 
			
		||||
    const input = document.getElementById('input')
 | 
			
		||||
    const output = document.getElementById('output')
 | 
			
		||||
    const magnitude = document.getElementById('magnitude')
 | 
			
		||||
    const phase = document.getElementById('phase')
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i <= 1023; i += 1) {
 | 
			
		||||
      this.w.push((i * (2 * Math.PI)) / 1023)
 | 
			
		||||
      this.z.push(math.exp(math.complex(0, this.w[i])))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    figurePhase.layout.yaxis.title = 'Phase (degrees)'
 | 
			
		||||
    figureMag.data[0].x = this.w.map(x => x / Math.PI)
 | 
			
		||||
    figurePhase.data[0].x = this.w.map(x => x / Math.PI)
 | 
			
		||||
    Plotly.plot(input, { data: figureIn.data, layout: figureIn.layout })
 | 
			
		||||
    Plotly.plot(output, { data: figureOut.data, layout: figureOut.layout })
 | 
			
		||||
    Plotly.plot(magnitude, { data: figureMag.data, layout: figureMag.layout })
 | 
			
		||||
    Plotly.plot(phase, { data: figurePhase.data, layout: figurePhase.layout })
 | 
			
		||||
    this.redrawIO(this.x, this.figureIn)
 | 
			
		||||
    this.redrawIO(this.y, this.figureOut)
 | 
			
		||||
    this.redrawFR()
 | 
			
		||||
    this.fileReader.onload = e => {
 | 
			
		||||
      const json = JSON.parse(this.fileReader.result)
 | 
			
		||||
      this.a = json.a
 | 
			
		||||
      this.b = json.b
 | 
			
		||||
      this.p = this.a.length - 1
 | 
			
		||||
      this.q = this.b.length - 1
 | 
			
		||||
      this.output()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue