Skip to content

Instantly share code, notes, and snippets.

@logue
Last active January 6, 2018 00:00
Show Gist options
  • Save logue/71233d660cd7bfc6710a9a2ebfa43ab1 to your computer and use it in GitHub Desktop.
Save logue/71233d660cd7bfc6710a9a2ebfa43ab1 to your computer and use it in GitHub Desktop.
AudioContextの勉強のために作った、音にリバーブエフェクトをかけるためのライブラリ。まだ未完成。
/**
* @file Add reverb effect.
* @copyright © 2017,2018 By Logue <http://logue.be/>.
* @license MIT
* Adapted from https://github.com/web-audio-components/simple-reverb
*/
export default class Reverb {
private ctx: AudioContext
private outputNode: GainNode
private wetGainNode: GainNode
private dryGainNode: GainNode
private convolverNode: ConvolverNode
private filterNode: BiquadFilterNode
private _cutOff: number
private _decay: number
private _delay: number
private _filterType: BiquadFilterType
private _mix: number
private _reverse: boolean
private _time: number
private defaults = {
cutOff: 350,
decay: 2,
delay: 0,
filterType: 'lowpass',
mix: 0.5,
reverse: false,
time: 3
};
/**
* Add reverb effect.
*/
constructor(ctx: AudioContext, options: {
cutOff: (number | undefined),
decay: (number | undefined),
delay: (number | undefined),
filterType: (BiquadFilterType | undefined),
mix: (number | undefined),
reverse: (boolean | undefined),
time: (number | undefined)
}) {
this.ctx = ctx;
this.outputNode = this.ctx.createGain();
this.wetGainNode = this.ctx.createGain();
this.dryGainNode = this.ctx.createGain();
this.convolverNode = this.ctx.createConvolver();
this.filterNode = this.ctx.createBiquadFilter();
// エフェクトのかかり方の接続
this.outputNode.connect(this.dryGainNode);
this.outputNode.connect(this.wetGainNode);
// エフェクトを接続
this.convolverNode.connect(this.filterNode);
this.dryGainNode.connect(this.outputNode);
this.wetGainNode.connect(this.outputNode);
// フィルタを接続
this.filterNode.connect(this.outputNode);
// 入力値と初期値をマージする
for (let key in this.defaults) {
this['_' + key] = (options[key] === undefined) ?
this.defaults[key] : options[key];
}
// エフェクタに反映
this.dryGainNode.gain.value = this.getDryLevel(this._mix);
this.wetGainNode.gain.value = this.getWetLevel(this._mix);
this.filterNode.type = this._filterType;
this.filterNode.frequency.value = this._cutOff;
// インパルス応答を生成
this.BuildImpulse();
}
/**
* Utility function for building an impulse response
* from the module parameters.
*/
private BuildImpulse() {
const rate: number = this.ctx.sampleRate;
const length: number = Math.max(rate * this._time, 1);
const delayDuration: number = rate * this._delay;
let impulse: AudioBuffer = this.ctx.createBuffer(2, length, rate);
let impulseL: Float32Array = new Float32Array(length);
let impulseR: Float32Array = new Float32Array(length);
for (let i: number = 0; i < length; i++) {
let n: number, pow: number;
if (i < delayDuration) {
// Delay Effect
impulseL[i] = 0;
impulseR[i] = 0;
} else {
n = this._reverse ? length - (i - delayDuration) : i - delayDuration;
n = this._reverse ? length - i : i;
pow = Math.pow(1 - n / length, this._decay);
impulseL[i] = (Math.random() * 2 - 1) * pow;
impulseR[i] = (Math.random() * 2 - 1) * pow;
}
n = this._reverse ? length - (i - delayDuration) : i - delayDuration;
pow = Math.pow(1 - n / length, this._decay);
impulseL[i] = (Math.random() * 2 - 1) * pow;
impulseR[i] = (Math.random() * 2 - 1) * pow;
}
impulse.getChannelData(0).set(impulseL);
impulse.getChannelData(1).set(impulseR);
console.info('Update impulse responce.');
this.convolverNode.buffer = impulse;
}
public connect(destinationNode: AudioNode) {
console.info('Connect Reverb.');
this.outputNode.connect(destinationNode);
}
public disconnect(no: number) {
console.info('Disconnect Reverb.');
this.outputNode.disconnect(no);
}
public mix(mix: number) {
this._mix = mix;
this.dryGainNode.gain.value = this.getDryLevel(mix);
this.wetGainNode.gain.value = this.getWetLevel(mix);
}
public time(time: number) {
this._time = time;
this.BuildImpulse();
}
/**
* Impulse response decay rate.
*/
public decay(decay: number) {
this._decay = decay;
this.BuildImpulse();
}
/**
* Impulse response decay rate.
*/
public delay(delay: number) {
this._delay = delay;
this.BuildImpulse();
}
/**
* Reverse the impulse response.
*/
public reverse(reverse: boolean) {
this._reverse = reverse;
this.BuildImpulse();
}
/**
* Cut off frequency.
*/
public cutOff(freq: number) {
this.filterNode.frequency.value = this._cutOff = freq;
}
/**
* Filter Type.
*/
public filterType(type: BiquadFilterType) {
this.filterNode.type = this._filterType = type;
};
private getDryLevel(value: number): number {
if (value > 1 || value < 0) {
return 0;
}
if (value <= 0.5)
return 1;
return 1 - ((value - 0.5) * 2);
};
private getWetLevel(value: number): number {
if (value > 1 || value < 0) {
return 0;
}
if (value >= 0.5)
return 1;
return 1 - ((value - 0.5) * 2);
};
}
@logue
Copy link
Author

logue commented Apr 2, 2017

インパルス応答(impulse response)のWAVファイルってライセンスがあやふやなのって多いじゃん。
だったら、自分で作っちゃえばいいっしょ。

@logue
Copy link
Author

logue commented Jan 6, 2018

TypeScriptで書き直した。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment