aves.js
Version:
Audio spectrum analyzer written in type script. Developed using web audio api.
130 lines (115 loc) • 6.75 kB
text/typescript
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)
}
}