@logue/reverb 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Reverb.ts DELETED
@@ -1,360 +0,0 @@
1
- import Meta from './Meta';
2
- import OptionInterface from './interfaces/OptionInterface';
3
- import {NoiseType} from './NoiseType';
4
-
5
- /**
6
- * JS reverb effect class
7
- *
8
- * @author Logue <logue@hotmail.co.jp>
9
- * @copyright 2019-2021 Masashi Yoshikawa <https://logue.dev/> All rights reserved.
10
- * @license MIT
11
- * @see {@link https://github.com/logue/Reverb.js}
12
- * {@link https://github.com/web-audio-components/simple-reverb}
13
- */
14
- export default class Reverb {
15
- /** バージョン */
16
- public readonly version: string;
17
- /** ビルド日時 */
18
- public readonly build: string;
19
- /** AudioContext */
20
- private readonly ctx: AudioContext;
21
- /** ウェットレベル(エフェクターをかけたレベル) */
22
- private readonly wetGainNode: GainNode;
23
- /** ドライレベル(原音レベル) */
24
- private readonly dryGainNode: GainNode;
25
- /** インパルス応答用フィルタ */
26
- private readonly filterNode: BiquadFilterNode;
27
- /** 畳み込みノード */
28
- private readonly convolverNode: ConvolverNode;
29
- /** 出力ノード */
30
- private readonly outputNode: GainNode;
31
- /** 変数 */
32
- private readonly _options: OptionInterface;
33
- /** 接続済みフラグ */
34
- private isConnected: boolean;
35
-
36
- /**
37
- * constructor
38
- * @param ctx Root AudioContext
39
- * @param options Configure
40
- */
41
- constructor(ctx: AudioContext, options: OptionInterface | undefined) {
42
- // バージョン情報など
43
- this.version = Meta.version;
44
- this.build = Meta.date;
45
- // マスターのAudioContextを取得
46
- this.ctx = ctx;
47
- // デフォルト値をマージ
48
- this._options = {...optionDefaults, ...options} as const;
49
- // 初期化
50
- this.wetGainNode = this.ctx.createGain();
51
- this.dryGainNode = this.ctx.createGain();
52
- this.filterNode = this.ctx.createBiquadFilter();
53
- this.convolverNode = this.ctx.createConvolver();
54
- this.outputNode = this.ctx.createGain();
55
- // 接続済みフラグを落とす
56
- this.isConnected = false;
57
- // インパルス応答を生成
58
- this.buildImpulse();
59
- }
60
-
61
- /**
62
- * connect
63
- * @param sourceNode 原音ノード
64
- */
65
- public connect(sourceNode: AudioNode): AudioNode {
66
- // 畳み込みノードをウェットレベルに接続
67
- this.convolverNode.connect(this.filterNode);
68
- // フィルタノードをウェットレベルに接続
69
- this.filterNode.connect(this.wetGainNode);
70
- // 入力ノードを畳み込みノードに接続
71
- sourceNode.connect(this.convolverNode);
72
- // ドライレベルを出力ノードに接続
73
- sourceNode.connect(this.dryGainNode).connect(this.outputNode);
74
- // ウェットレベルを出力ノードに接続
75
- sourceNode.connect(this.wetGainNode).connect(this.outputNode);
76
- // 接続済みフラグを立てる
77
- this.isConnected = true;
78
-
79
- return this.outputNode;
80
- }
81
-
82
- /**
83
- * disconnect
84
- * @param sourceNode 原音のノード
85
- */
86
- public disconnect(sourceNode: AudioNode | undefined): AudioNode | undefined {
87
- // 初期状態ではノードがつながっていないためエラーになる
88
- if (this.isConnected) {
89
- // 畳み込みノードをウェットレベルから切断
90
- this.convolverNode.disconnect(this.filterNode);
91
- // フィルタノードをウェットレベルから切断
92
- this.filterNode.disconnect(this.wetGainNode);
93
- }
94
- // 接続済みフラグを解除
95
- this.isConnected = false;
96
-
97
- // そのままノードを返す(他のAPIに似せるため)
98
- return sourceNode;
99
- }
100
-
101
- /**
102
- * Dry/Wet ratio
103
- * @param mix
104
- */
105
- public mix(mix: number): void {
106
- if (!this.inRange(mix, 0, 1)) {
107
- throw new RangeError('Reverb.js: Dry/Wet ratio must be between 0 to 1.');
108
- }
109
- this._options.mix = mix;
110
- this.dryGainNode.gain.value = 1 - this._options.mix;
111
- this.wetGainNode.gain.value = this._options.mix;
112
- console.debug(`Reverb.js: Set dry/wet ratio to ${mix * 100}%`);
113
- }
114
-
115
- /**
116
- * Set Impulse Response time length (second)
117
- * @param value
118
- */
119
- public time(value: number): void {
120
- if (!this.inRange(value, 1, 50)) {
121
- throw new RangeError(
122
- 'Reverb.js: Time length of inpulse response must be less than 50sec.'
123
- );
124
- }
125
- this._options.time = value;
126
- this.buildImpulse();
127
- console.info(`Reverb.js: Set inpulse response time length to ${value}sec.`);
128
- }
129
-
130
- /**
131
- * Impulse response decay rate.
132
- * @param value
133
- */
134
- public decay(value: number): void {
135
- if (!this.inRange(value, 0, 100)) {
136
- throw new RangeError(
137
- 'Reverb.js: Inpulse Response decay level must be less than 100.'
138
- );
139
- }
140
- this._options.decay = value;
141
- this.buildImpulse();
142
- console.debug(`Reverb.js: Set inpulse response decay level to ${value}.`);
143
- }
144
-
145
- /**
146
- * Impulse response delay time. (NOT deley effect)
147
- * @param value
148
- */
149
- public delay(value: number): void {
150
- if (!this.inRange(value, 0, 100)) {
151
- throw new RangeError(
152
- 'Reverb.js: Inpulse Response delay time must be less than 100.'
153
- );
154
- }
155
- this._options.delay = value;
156
- this.buildImpulse();
157
- console.debug(`Reverb.js: Set inpulse response delay time to ${value}sec.`);
158
- }
159
-
160
- /**
161
- * Reverse the impulse response.
162
- * @param reverse
163
- */
164
- public reverse(reverse: boolean): void {
165
- this._options.reverse = reverse;
166
- this.buildImpulse();
167
- console.debug(
168
- `Reverb.js: Inpulse response is ${reverse ? '' : 'not '}reversed.`
169
- );
170
- }
171
-
172
- /**
173
- * Filter type.
174
- * @param type
175
- */
176
- public filterType(type: BiquadFilterType): void {
177
- this.filterNode.type = this._options.filterType = type;
178
- console.debug(`Set filter type to ${type}`);
179
- }
180
-
181
- /**
182
- * Filter frequency.
183
- * @param freq
184
- */
185
- public filterFreq(freq: number): void {
186
- if (!this.inRange(freq, 20, 5000)) {
187
- throw new RangeError(
188
- 'Reverb.js: Filter frequrncy must be between 20 and 5000.'
189
- );
190
- }
191
- this._options.filterFreq = freq;
192
- this.filterNode.frequency.value = this._options.filterFreq;
193
- console.debug(`Set filter frequency to ${freq}Hz.`);
194
- }
195
-
196
- /**
197
- * Filter quality.
198
- * @param q
199
- */
200
- public filterQ(q: number): void {
201
- if (!this.inRange(q, 0, 10)) {
202
- throw new RangeError(
203
- 'Reverb.js: Filter quality value must be between 0 and 10.'
204
- );
205
- }
206
- this._options.filterQ = q;
207
- this.filterNode.Q.value = this._options.filterQ;
208
- console.debug(`Set filter quality to ${q}.`);
209
- }
210
-
211
- /**
212
- * Inpulse Response Noise algorithm.
213
- * @param type
214
- */
215
- public setNoise(type: NoiseType) {
216
- this._options.noise = type;
217
- this.buildImpulse();
218
- console.debug(`Set Noise type to ${type}.`);
219
- }
220
-
221
- /**
222
- * return true if in range, otherwise false
223
- * @private
224
- * @param x Target value
225
- * @param min Minimum value
226
- * @param max Maximum value
227
- * @return
228
- */
229
- private inRange(x: number, min: number, max: number): boolean {
230
- return (x - min) * (x - max) <= 0;
231
- }
232
-
233
- /**
234
- * Utility function for building an impulse response
235
- * from the module parameters.
236
- * @private
237
- */
238
- private buildImpulse(): void {
239
- // インパルス応答生成ロジック
240
-
241
- /** サンプリングレート */
242
- const rate: number = this.ctx.sampleRate;
243
- /** インパルス応答の演奏時間 */
244
- const duration: number = Math.max(rate * this._options.time, 1);
245
- /** インパルス応答が始まるまでの遅延時間 */
246
- const delayDuration: number = rate * this._options.delay;
247
- /** インパルス応答バッファ(今の所ステレオのみ) */
248
- const impulse: AudioBuffer = this.ctx.createBuffer(2, duration, rate);
249
- /** 左チャンネル */
250
- const impulseL: Float32Array = new Float32Array(duration);
251
- /** 右チャンネル*/
252
- const impulseR: Float32Array = new Float32Array(duration);
253
-
254
- /** 一時計算用 */
255
- const b = [0, 0, 0, 0, 0, 0, 0];
256
-
257
- for (let i = 0; i < duration; i++) {
258
- /** @type {number} 減衰率 */
259
- let n = 0;
260
-
261
- if (i < delayDuration) {
262
- // Delay Effect
263
- impulseL[i] = 0;
264
- impulseR[i] = 0;
265
- n = this._options.reverse
266
- ? duration - (i - delayDuration)
267
- : i - delayDuration;
268
- } else {
269
- n = this._options.reverse ? duration - i : i;
270
- }
271
-
272
- switch (this._options.noise) {
273
- default:
274
- case NoiseType.WHITE:
275
- // White Noise
276
- impulseL[i] = Reverb.whiteNoise();
277
- impulseR[i] = Reverb.whiteNoise();
278
- break;
279
- case NoiseType.PINK:
280
- // ピンクノイズ生成処理
281
- // http://noisehack.com/generate-noise-web-audio-api/
282
- b[0] = 0.99886 * b[0] + Reverb.whiteNoise() * 0.0555179;
283
- b[1] = 0.99332 * b[1] + Reverb.whiteNoise() * 0.0750759;
284
- b[2] = 0.969 * b[2] + Reverb.whiteNoise() * 0.153852;
285
- b[3] = 0.8665 * b[3] + Reverb.whiteNoise() * 0.3104856;
286
- b[4] = 0.55 * b[4] + Reverb.whiteNoise() * 0.5329522;
287
- b[5] = -0.7616 * b[5] - Reverb.whiteNoise() * 0.016898;
288
-
289
- impulseL[i] =
290
- b[0] +
291
- b[1] +
292
- b[2] +
293
- b[3] +
294
- b[4] +
295
- b[5] +
296
- b[6] +
297
- Reverb.whiteNoise() * 0.5362;
298
-
299
- impulseR[i] =
300
- b[0] +
301
- b[1] +
302
- b[2] +
303
- b[3] +
304
- b[4] +
305
- b[5] +
306
- b[6] +
307
- Reverb.whiteNoise() * 0.5362;
308
-
309
- // ゲイン補償処理
310
- impulseL[i] *= 0.11;
311
- impulseR[i] *= 0.11;
312
-
313
- b[6] = Reverb.whiteNoise() * 0.115926;
314
- break;
315
- case NoiseType.BROWN:
316
- // ブラウンノイズ生成処理
317
- impulseL[i] = (b[0] + 0.02 * Reverb.whiteNoise()) / 1.02;
318
- b[0] = impulseL[i];
319
- impulseR[i] = (b[1] + 0.02 * Reverb.whiteNoise()) / 1.02;
320
- b[1] = impulseR[i];
321
-
322
- // ゲイン補償処理
323
- impulseL[i] *= 3.5;
324
- impulseR[i] *= 3.5;
325
- break;
326
- }
327
- // 音を減衰させる
328
- impulseL[i] *= (1 - n / duration) ** this._options.decay;
329
- impulseR[i] *= (1 - n / duration) ** this._options.decay;
330
- }
331
-
332
- // インパルス応答のバッファに生成したWaveTableを代入
333
- impulse.getChannelData(0).set(impulseL);
334
- impulse.getChannelData(1).set(impulseR);
335
-
336
- this.convolverNode.buffer = impulse;
337
- }
338
- /**
339
- * Generate white noise
340
- */
341
- private static whiteNoise(): number {
342
- // TODO: この乱数は本当に偏り無いのだろうか?
343
- return Math.random() * 2 - 1;
344
- }
345
- }
346
-
347
- /**
348
- * デフォルト値
349
- */
350
- const optionDefaults: OptionInterface = {
351
- noise: 1,
352
- decay: 5,
353
- delay: 0,
354
- reverse: false,
355
- time: 3,
356
- filterType: 'lowpass',
357
- filterFreq: 2200,
358
- filterQ: 1,
359
- mix: 0.5,
360
- };
@@ -1,9 +0,0 @@
1
- /**
2
- * メタ情報インターフェース
3
- */
4
- export default interface MetaInterface {
5
- /** @type バージョン */
6
- version: string;
7
- /** @type ビルド日時 */
8
- date: string;
9
- }
@@ -1,25 +0,0 @@
1
- import {NoiseType} from '../NoiseType';
2
-
3
- /**
4
- * オプションのインターフェース
5
- */
6
- export default interface OptionInterface {
7
- /** @type ノイズ種別 */
8
- noise: NoiseType;
9
- /** @type ディケイ */
10
- decay: number;
11
- /** @type ディレイ */
12
- delay: number;
13
- /** @type フィルタ周波数(Hz) */
14
- filterFreq: number;
15
- /** @type フィルタ品質 */
16
- filterQ: number;
17
- /** @type {BiquadFilterType?} フィルタの種類 */
18
- filterType: BiquadFilterType;
19
- /** @type ドライ/ウェット比 */
20
- mix: number;
21
- /** @type レスポンス応答を反転 */
22
- reverse: boolean;
23
- /** @type レスポンス応答の時間(秒) */
24
- time: number;
25
- }
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "extends": "./node_modules/gts/tsconfig-google.json",
3
- "compilerOptions": {
4
- "esModuleInterop": true,
5
- "module": "commonjs",
6
- "outDir": "build",
7
- "rootDir": ".",
8
- "lib": [
9
- "dom"
10
- ],
11
- "target": "esnext",
12
- },
13
- "include": [
14
- "src/**/*.ts",
15
- "test/**/*.ts"
16
- ]
17
- }
package/webpack.config.ts DELETED
@@ -1,97 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import * as webpack from 'webpack';
3
- import * as path from 'path';
4
- import * as fs from 'fs';
5
- import TerserPlugin from 'terser-webpack-plugin';
6
- import WebpackDevServer from 'webpack-dev-server';
7
-
8
- declare module 'webpack' {
9
- interface Configuration {
10
- /**
11
- * Can be used to configure the behaviour of webpack-dev-server when
12
- * the webpack config is passed to webpack-dev-server CLI.
13
- */
14
- devServer?: WebpackDevServer.Configuration;
15
- }
16
- }
17
-
18
- // バージョン情報など
19
- const pjson = require('./package.json');
20
- // 現在の時刻(ビルド時刻)
21
- const build: string = new Date().toISOString();
22
-
23
- // Meta.tsを書き込む
24
- fs.writeFileSync(
25
- path.resolve(path.join(__dirname, 'src/Meta.ts')),
26
- `import MetaInterface from './interfaces/MetaInterface';
27
-
28
- // This file is auto-generated by the build system.
29
- const meta: MetaInterface = {
30
- version: '${pjson.version}',
31
- date: '${build}',
32
- };
33
- export default meta;
34
- `
35
- );
36
-
37
- module.exports = (env: any, argv: any): webpack.Configuration => {
38
- const isProduction: boolean = argv.mode === 'production';
39
- const banner = `${pjson.name} v${pjson.version} | ${pjson.author.name} | license: ${pjson.license} | build: ${build}`;
40
-
41
- return {
42
- mode: isProduction ? 'production' : 'development',
43
- target: 'node',
44
- devtool: !isProduction ? 'source-map' : false,
45
- devServer: {
46
- contentBase: 'docs',
47
- open: false,
48
- },
49
- entry: {
50
- reverb: './src/Reverb.ts',
51
- },
52
- output: {
53
- path: path.resolve(__dirname, 'bin'),
54
- filename: !isProduction ? '[name].js' : '[name].min.js',
55
- library: 'Reverb',
56
- libraryTarget: 'umd',
57
- umdNamedDefine: true,
58
- globalObject: "(typeof self !== 'undefined' ? self : this)",
59
- },
60
- optimization: {
61
- minimize: isProduction,
62
- minimizer: [
63
- new TerserPlugin({
64
- terserOptions: {
65
- ecma: 2016,
66
- compress: {drop_console: true},
67
- output: {
68
- comments: false,
69
- beautify: false,
70
- },
71
- },
72
- }),
73
- ],
74
- splitChunks: {
75
- minSize: 0,
76
- },
77
- concatenateModules: false,
78
- },
79
- module: {
80
- rules: [
81
- {
82
- test: /\.tsx?$/,
83
- loader: 'ts-loader',
84
- },
85
- ],
86
- },
87
- resolve: {
88
- modules: [`${__dirname}/src`, 'node_modules'],
89
- extensions: ['webpack.ts', '.ts', '.tsx'],
90
- },
91
- plugins: [
92
- new webpack.BannerPlugin({
93
- banner: banner,
94
- }),
95
- ],
96
- };
97
- };