UNPKG

aves.js

Version:

Audio spectrum analyzer written in type script. Developed using web audio api.

130 lines (115 loc) 6.75 kB
import AvesAnalyser from '../aves/AvesAnalyser' import * as util from '../util' export default class { private _canvasWidth: number private _canvasHeight: number private _canvasElm: HTMLCanvasElement private _ctx: CanvasRenderingContext2D private _animationFrameId: number private _bgColor: string private gridStyle: string = util.createColor(230, 230, 230, 0.5) private scaleStyle: string = util.createColor(250, 250, 250, 1) private _dispHz: number[] = [30, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 15000] private _dispDecibel: number[] = [0, -10, -20, -30, -40, -50, -60, -70, -80, -90] constructor(elm: HTMLCanvasElement, canvasWidth: number, canvasHeight: number) { this._canvasElm = elm this._canvasElm.width = this._canvasWidth = canvasWidth this._canvasElm.height = this._canvasHeight = canvasHeight this._ctx = this._canvasElm.getContext('2d') this._bgColor = util.createColor(46, 40, 48) this._ctx.clearRect(0, 0, this._canvasWidth, this._canvasHeight) this._ctx.fillStyle = this._bgColor this._ctx.fillRect(0, 0, this._canvasWidth, this._canvasHeight) } /** * 特定のHzをX軸のどの部分に対数表示をすればいいかを * 計算する。 * @param {number} hz * @param {number} minHz * @param {number} maxHz * @returns {number} 横軸の画面位置 */ pointX(hz: number, minHz: number, maxHz: number): number { return ( ((util.seekDigit(hz) - util.seekDigit(minHz)) / (util.seekDigit(maxHz) - util.seekDigit(minHz))) * this._canvasWidth ) } /** * 描画メソッド * @param {AvesAnalyser} avesAnalyser */ draw(avesAnalyser: AvesAnalyser) { // ───────────────────────────────────────────────────────────────── // 初期化処理 // ───────────────────────────────────────────────────────────────── const arrayLength: number = avesAnalyser.byteTimeDomainArray.length this._ctx.beginPath() const fontSize = 11 this._ctx.font = util.createFont(String(fontSize) + 'px') this._ctx.fillStyle = this._bgColor this._ctx.fillRect(0, 0, this._canvasWidth, this._canvasHeight) // ───────────────────────────────────────────────────────────────── // デフォルトでは2048までループ // ───────────────────────────────────────────────────────────────── for (let i = 0; i <= arrayLength; i++) { // ───────────────────────────────────────────────────────────────── // プロットする点を得る処理 // ───────────────────────────────────────────────────────────────── const pointX = (i / arrayLength) * this._canvasWidth // ───────────────────────────────────────────────────────────────── // avesAnalyser.byteTimeDomainArrayの中身の数値は0~255 // ───────────────────────────────────────────────────────────────── const pointY = (1 - avesAnalyser.byteTimeDomainArray[i] / 255) * this._canvasHeight if (i === 0) { this._ctx.moveTo(0, pointY) } else { this._ctx.lineTo(pointX, pointY) } // ───────────────────────────────────────────────────────────────── // X軸に目盛りを描画 // ───────────────────────────────────────────────────────────────── const sec = i * avesAnalyser.samplingInterval() const msec = sec * Math.pow(10, 3) if (msec % 5 === 0) { var text = Math.round(msec) + ' msec' this._ctx.fillStyle = this.gridStyle this._ctx.fillRect(pointX, 0, 1, this._canvasHeight) this._ctx.fillStyle = this.scaleStyle this._ctx.fillText(text, pointX + 4, this._canvasHeight - fontSize) } } // ───────────────────────────────────────────────────────────────── // グラフを描画 // ───────────────────────────────────────────────────────────────── this._ctx.strokeStyle = util.createColor(250, 250, 250, 1) this._ctx.lineWidth = 1 this._ctx.stroke() // ───────────────────────────────────────────────────────────────── // Y軸に目盛りを描画 // ───────────────────────────────────────────────────────────────── var textYs = ['1.00', '0.00', '-1.00'] for (var i = 0, len = textYs.length; i < len; i++) { var text = textYs[i] var gy = ((1 - parseFloat(text)) / 2) * this._canvasHeight this._ctx.fillStyle = this.gridStyle this._ctx.fillRect(0, gy, this._canvasWidth, 1) this._ctx.fillStyle = this.scaleStyle this._ctx.fillText(text, 5, gy + 12) } } /** * * * @param {AvesAnalyser} avesAnalyser * requestAnimationFrameで自分自身を呼ぶ */ animationStart(avesAnalyser: AvesAnalyser) { avesAnalyser.getByteTimeDomainData() this.draw(avesAnalyser) this._animationFrameId = requestAnimationFrame(() => this.animationStart(avesAnalyser)) } animationStop() { cancelAnimationFrame(this._animationFrameId) } }