@harmonia-audio/effects 0.1.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.
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Interface for audio effects that can be chained in a pipeline.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * class MyEffect implements AudioEffect {
7
+ * process(samples: Buffer): Buffer { return samples; }
8
+ * reset(): void {}
9
+ * destroy(): void {}
10
+ * }
11
+ * ```
12
+ */
13
+ interface AudioEffect {
14
+ /** Process a buffer of audio samples in-place or returning a new buffer. */
15
+ process(samples: Buffer): Buffer;
16
+ /** Reset effect state (e.g., filter history). */
17
+ reset(): void;
18
+ /** Release any native resources. */
19
+ destroy(): void;
20
+ }
21
+
22
+ /**
23
+ * Chainable audio effects processing pipeline.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { EffectsPipeline, VolumeEffect, EqualizerEffect } from '@harmonia/effects';
28
+ *
29
+ * const pipeline = new EffectsPipeline()
30
+ * .add(new VolumeEffect(0.8))
31
+ * .add(new EqualizerEffect([{ frequency: 1000, gain: 3, q: 1.0 }]));
32
+ *
33
+ * const processed = pipeline.process(pcmBuffer);
34
+ * ```
35
+ */
36
+ declare class EffectsPipeline {
37
+ private readonly effects;
38
+ /**
39
+ * Add an effect to the pipeline.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * pipeline.add(new VolumeEffect(0.5));
44
+ * ```
45
+ */
46
+ add(effect: AudioEffect): this;
47
+ /**
48
+ * Remove an effect from the pipeline.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * pipeline.remove(volumeEffect);
53
+ * ```
54
+ */
55
+ remove(effect: AudioEffect): this;
56
+ /**
57
+ * Clear all effects from the pipeline.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * pipeline.clear();
62
+ * ```
63
+ */
64
+ clear(): this;
65
+ /**
66
+ * Process audio through all effects in the pipeline.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * const processed = pipeline.process(pcmBuffer);
71
+ * ```
72
+ */
73
+ process(samples: Buffer): Buffer;
74
+ /**
75
+ * Reset all effects in the pipeline.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * pipeline.reset();
80
+ * ```
81
+ */
82
+ reset(): void;
83
+ /**
84
+ * Destroy the pipeline and all effects.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * pipeline.destroy();
89
+ * ```
90
+ */
91
+ destroy(): void;
92
+ /** The number of effects in the pipeline. */
93
+ get length(): number;
94
+ }
95
+
96
+ /**
97
+ * Volume control effect.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * const vol = new VolumeEffect(0.5); // 50% volume
102
+ * const output = vol.process(pcmBuffer);
103
+ * ```
104
+ */
105
+ declare class VolumeEffect implements AudioEffect {
106
+ private _volume;
107
+ constructor(volume?: number);
108
+ /** Get the current volume level (0.0 to 10.0). */
109
+ get volume(): number;
110
+ /**
111
+ * Set the volume level.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * vol.setVolume(0.8);
116
+ * ```
117
+ */
118
+ setVolume(volume: number): void;
119
+ process(samples: Buffer): Buffer;
120
+ reset(): void;
121
+ destroy(): void;
122
+ }
123
+
124
+ /**
125
+ * Configuration for a single equalizer band.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * const band: EqualizerBand = { frequency: 1000, gain: 3, q: 1.0 };
130
+ * ```
131
+ */
132
+ interface EqualizerBand {
133
+ readonly frequency: number;
134
+ readonly gain: number;
135
+ readonly q: number;
136
+ }
137
+ /**
138
+ * Multi-band parametric equalizer effect using biquad filters.
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * const eq = new EqualizerEffect([
143
+ * { frequency: 60, gain: 3, q: 0.7 },
144
+ * { frequency: 1000, gain: -2, q: 1.0 },
145
+ * { frequency: 8000, gain: 1, q: 1.4 },
146
+ * ]);
147
+ * const output = eq.process(pcmBuffer);
148
+ * ```
149
+ */
150
+ declare class EqualizerEffect implements AudioEffect {
151
+ private readonly filters;
152
+ private readonly sampleRate;
153
+ constructor(bands: readonly EqualizerBand[], sampleRate?: number);
154
+ process(samples: Buffer): Buffer;
155
+ reset(): void;
156
+ destroy(): void;
157
+ }
158
+
159
+ /**
160
+ * Options for the compressor effect.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const options: CompressorOptions = {
165
+ * threshold: -20,
166
+ * ratio: 4,
167
+ * attack: 5,
168
+ * release: 100,
169
+ * };
170
+ * ```
171
+ */
172
+ interface CompressorOptions {
173
+ readonly threshold: number;
174
+ readonly ratio: number;
175
+ readonly attack: number;
176
+ readonly release: number;
177
+ readonly makeupGain?: number;
178
+ readonly knee?: number;
179
+ readonly sampleRate?: number;
180
+ }
181
+ /**
182
+ * Dynamic range compressor effect.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * const compressor = new CompressorEffect({
187
+ * threshold: -20,
188
+ * ratio: 4,
189
+ * attack: 5,
190
+ * release: 100,
191
+ * });
192
+ * const output = compressor.process(pcmBuffer);
193
+ * ```
194
+ */
195
+ declare class CompressorEffect implements AudioEffect {
196
+ private readonly native;
197
+ constructor(options: CompressorOptions);
198
+ process(samples: Buffer): Buffer;
199
+ reset(): void;
200
+ destroy(): void;
201
+ }
202
+
203
+ /**
204
+ * Options for the noise gate effect.
205
+ *
206
+ * @example
207
+ * ```ts
208
+ * const gate = new NoiseGateEffect({ threshold: -40, attack: 1, release: 50, hold: 20 });
209
+ * ```
210
+ */
211
+ interface NoiseGateOptions {
212
+ readonly threshold: number;
213
+ readonly attack: number;
214
+ readonly release: number;
215
+ readonly hold: number;
216
+ readonly sampleRate?: number;
217
+ }
218
+ /**
219
+ * Noise gate effect that silences audio below a threshold.
220
+ *
221
+ * @example
222
+ * ```ts
223
+ * const gate = new NoiseGateEffect({
224
+ * threshold: -40,
225
+ * attack: 1,
226
+ * release: 50,
227
+ * hold: 20,
228
+ * });
229
+ * const output = gate.process(pcmBuffer);
230
+ * ```
231
+ */
232
+ declare class NoiseGateEffect implements AudioEffect {
233
+ private readonly native;
234
+ constructor(options: NoiseGateOptions);
235
+ process(samples: Buffer): Buffer;
236
+ reset(): void;
237
+ destroy(): void;
238
+ }
239
+
240
+ /**
241
+ * Options for the delay/echo effect.
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * const delay = new DelayEffect({ delayMs: 300, feedback: 0.4, wet: 0.3 });
246
+ * ```
247
+ */
248
+ interface DelayOptions {
249
+ readonly delayMs: number;
250
+ readonly feedback: number;
251
+ readonly wet: number;
252
+ readonly sampleRate?: number;
253
+ }
254
+ /**
255
+ * Delay/echo audio effect.
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * const delay = new DelayEffect({
260
+ * delayMs: 300,
261
+ * feedback: 0.4,
262
+ * wet: 0.3,
263
+ * });
264
+ * const output = delay.process(pcmBuffer);
265
+ * ```
266
+ */
267
+ declare class DelayEffect implements AudioEffect {
268
+ private readonly native;
269
+ constructor(options: DelayOptions);
270
+ process(samples: Buffer): Buffer;
271
+ reset(): void;
272
+ destroy(): void;
273
+ }
274
+
275
+ /**
276
+ * Convolution reverb effect using an impulse response.
277
+ *
278
+ * @example
279
+ * ```ts
280
+ * import { readFileSync } from 'fs';
281
+ *
282
+ * const ir = readFileSync('./hall-ir.pcm');
283
+ * const reverb = new ReverbEffect(ir);
284
+ * const output = reverb.process(pcmBuffer);
285
+ * ```
286
+ */
287
+ declare class ReverbEffect implements AudioEffect {
288
+ private readonly native;
289
+ constructor(impulseResponse: Buffer);
290
+ process(samples: Buffer): Buffer;
291
+ reset(): void;
292
+ destroy(): void;
293
+ }
294
+
295
+ /**
296
+ * Pitch shifting effect using a phase vocoder.
297
+ *
298
+ * @example
299
+ * ```ts
300
+ * const pitch = new PitchShiftEffect(1.5); // shift up by 50%
301
+ * const output = pitch.process(pcmBuffer);
302
+ * ```
303
+ */
304
+ declare class PitchShiftEffect implements AudioEffect {
305
+ private readonly native;
306
+ constructor(pitchFactor: number, fftSize?: number);
307
+ /**
308
+ * Update the pitch factor at runtime.
309
+ *
310
+ * @example
311
+ * ```ts
312
+ * pitch.setPitchFactor(0.8);
313
+ * ```
314
+ */
315
+ setPitchFactor(factor: number): void;
316
+ process(samples: Buffer): Buffer;
317
+ reset(): void;
318
+ destroy(): void;
319
+ }
320
+
321
+ /**
322
+ * Stereo widening effect with mono downmix capability.
323
+ *
324
+ * @example
325
+ * ```ts
326
+ * const widener = new StereoWidenerEffect(1.5);
327
+ * const wider = widener.process(stereoBuffer);
328
+ * ```
329
+ */
330
+ declare class StereoWidenerEffect implements AudioEffect {
331
+ private _width;
332
+ private _monoOutput;
333
+ constructor(width?: number, monoOutput?: boolean);
334
+ /**
335
+ * Set the stereo width.
336
+ *
337
+ * @example
338
+ * ```ts
339
+ * widener.setWidth(2.0);
340
+ * ```
341
+ */
342
+ setWidth(width: number): void;
343
+ process(samples: Buffer): Buffer;
344
+ reset(): void;
345
+ destroy(): void;
346
+ }
347
+
348
+ /**
349
+ * Options for loudness normalization.
350
+ *
351
+ * @example
352
+ * ```ts
353
+ * const normalizer = new LoudnessNormalizerEffect({ targetLufs: -14 });
354
+ * ```
355
+ */
356
+ interface LoudnessOptions {
357
+ readonly targetLufs: number;
358
+ readonly sampleRate?: number;
359
+ }
360
+ /**
361
+ * Loudness normalization effect targeting a specific LUFS level.
362
+ *
363
+ * @example
364
+ * ```ts
365
+ * const normalizer = new LoudnessNormalizerEffect({ targetLufs: -14 });
366
+ * const normalized = normalizer.process(pcmBuffer);
367
+ * ```
368
+ */
369
+ declare class LoudnessNormalizerEffect implements AudioEffect {
370
+ private readonly native;
371
+ constructor(options: LoudnessOptions);
372
+ process(samples: Buffer): Buffer;
373
+ reset(): void;
374
+ destroy(): void;
375
+ }
376
+
377
+ export { type AudioEffect, CompressorEffect, type CompressorOptions, DelayEffect, type DelayOptions, EffectsPipeline, type EqualizerBand, EqualizerEffect, LoudnessNormalizerEffect, type LoudnessOptions, NoiseGateEffect, type NoiseGateOptions, PitchShiftEffect, ReverbEffect, StereoWidenerEffect, VolumeEffect };
package/dist/index.js ADDED
@@ -0,0 +1,328 @@
1
+ // src/pipeline.ts
2
+ var EffectsPipeline = class {
3
+ effects = [];
4
+ /**
5
+ * Add an effect to the pipeline.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * pipeline.add(new VolumeEffect(0.5));
10
+ * ```
11
+ */
12
+ add(effect) {
13
+ this.effects.push(effect);
14
+ return this;
15
+ }
16
+ /**
17
+ * Remove an effect from the pipeline.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * pipeline.remove(volumeEffect);
22
+ * ```
23
+ */
24
+ remove(effect) {
25
+ const index = this.effects.indexOf(effect);
26
+ if (index !== -1) {
27
+ this.effects.splice(index, 1);
28
+ }
29
+ return this;
30
+ }
31
+ /**
32
+ * Clear all effects from the pipeline.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * pipeline.clear();
37
+ * ```
38
+ */
39
+ clear() {
40
+ for (const effect of this.effects) {
41
+ effect.destroy();
42
+ }
43
+ this.effects.length = 0;
44
+ return this;
45
+ }
46
+ /**
47
+ * Process audio through all effects in the pipeline.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const processed = pipeline.process(pcmBuffer);
52
+ * ```
53
+ */
54
+ process(samples) {
55
+ let current = samples;
56
+ for (const effect of this.effects) {
57
+ current = effect.process(current);
58
+ }
59
+ return current;
60
+ }
61
+ /**
62
+ * Reset all effects in the pipeline.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * pipeline.reset();
67
+ * ```
68
+ */
69
+ reset() {
70
+ for (const effect of this.effects) {
71
+ effect.reset();
72
+ }
73
+ }
74
+ /**
75
+ * Destroy the pipeline and all effects.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * pipeline.destroy();
80
+ * ```
81
+ */
82
+ destroy() {
83
+ this.clear();
84
+ }
85
+ /** The number of effects in the pipeline. */
86
+ get length() {
87
+ return this.effects.length;
88
+ }
89
+ };
90
+
91
+ // src/volume.ts
92
+ import { applyVolume } from "@harmonia-audio/native";
93
+ var VolumeEffect = class {
94
+ _volume;
95
+ constructor(volume = 1) {
96
+ this._volume = Math.max(0, Math.min(volume, 10));
97
+ }
98
+ /** Get the current volume level (0.0 to 10.0). */
99
+ get volume() {
100
+ return this._volume;
101
+ }
102
+ /**
103
+ * Set the volume level.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * vol.setVolume(0.8);
108
+ * ```
109
+ */
110
+ setVolume(volume) {
111
+ this._volume = Math.max(0, Math.min(volume, 10));
112
+ }
113
+ process(samples) {
114
+ if (this._volume === 1) {
115
+ return samples;
116
+ }
117
+ return applyVolume(samples, this._volume);
118
+ }
119
+ reset() {
120
+ }
121
+ destroy() {
122
+ }
123
+ };
124
+
125
+ // src/equalizer.ts
126
+ import { BiquadFilter } from "@harmonia-audio/native";
127
+ var EqualizerEffect = class {
128
+ filters;
129
+ sampleRate;
130
+ constructor(bands, sampleRate = 48e3) {
131
+ this.sampleRate = sampleRate;
132
+ this.filters = bands.map(
133
+ (band) => BiquadFilter.peakingEq(sampleRate, band.frequency, band.q, band.gain)
134
+ );
135
+ }
136
+ process(samples) {
137
+ let current = samples;
138
+ for (const filter of this.filters) {
139
+ current = filter.process(current);
140
+ }
141
+ return current;
142
+ }
143
+ reset() {
144
+ for (const filter of this.filters) {
145
+ filter.reset();
146
+ }
147
+ }
148
+ destroy() {
149
+ }
150
+ };
151
+
152
+ // src/compressor.ts
153
+ import { Compressor } from "@harmonia-audio/native";
154
+ var CompressorEffect = class {
155
+ native;
156
+ constructor(options) {
157
+ this.native = new Compressor(
158
+ options.threshold,
159
+ options.ratio,
160
+ options.attack,
161
+ options.release,
162
+ options.makeupGain ?? 0,
163
+ options.knee ?? 6,
164
+ options.sampleRate ?? 48e3
165
+ );
166
+ }
167
+ process(samples) {
168
+ return this.native.process(samples);
169
+ }
170
+ reset() {
171
+ this.native.reset();
172
+ }
173
+ destroy() {
174
+ }
175
+ };
176
+
177
+ // src/noise-gate.ts
178
+ import { NoiseGate } from "@harmonia-audio/native";
179
+ var NoiseGateEffect = class {
180
+ native;
181
+ constructor(options) {
182
+ this.native = new NoiseGate(
183
+ options.threshold,
184
+ options.attack,
185
+ options.release,
186
+ options.hold,
187
+ options.sampleRate ?? 48e3
188
+ );
189
+ }
190
+ process(samples) {
191
+ return this.native.process(samples);
192
+ }
193
+ reset() {
194
+ }
195
+ destroy() {
196
+ }
197
+ };
198
+
199
+ // src/delay.ts
200
+ import { Delay } from "@harmonia-audio/native";
201
+ var DelayEffect = class {
202
+ native;
203
+ constructor(options) {
204
+ this.native = new Delay(
205
+ options.delayMs,
206
+ options.feedback,
207
+ options.wet,
208
+ options.sampleRate ?? 48e3
209
+ );
210
+ }
211
+ process(samples) {
212
+ return this.native.process(samples);
213
+ }
214
+ reset() {
215
+ }
216
+ destroy() {
217
+ }
218
+ };
219
+
220
+ // src/reverb.ts
221
+ import { ConvolutionReverb } from "@harmonia-audio/native";
222
+ var ReverbEffect = class {
223
+ native;
224
+ constructor(impulseResponse) {
225
+ this.native = new ConvolutionReverb(impulseResponse);
226
+ }
227
+ process(samples) {
228
+ return this.native.process(samples);
229
+ }
230
+ reset() {
231
+ }
232
+ destroy() {
233
+ }
234
+ };
235
+
236
+ // src/pitch-shift.ts
237
+ import { PhaseVocoder } from "@harmonia-audio/native";
238
+ var PitchShiftEffect = class {
239
+ native;
240
+ constructor(pitchFactor, fftSize = 2048) {
241
+ this.native = new PhaseVocoder(fftSize, pitchFactor);
242
+ }
243
+ /**
244
+ * Update the pitch factor at runtime.
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * pitch.setPitchFactor(0.8);
249
+ * ```
250
+ */
251
+ setPitchFactor(factor) {
252
+ this.native.setPitchFactor(factor);
253
+ }
254
+ process(samples) {
255
+ return this.native.process(samples);
256
+ }
257
+ reset() {
258
+ }
259
+ destroy() {
260
+ }
261
+ };
262
+
263
+ // src/stereo-widener.ts
264
+ import { stereoWiden, monoDownmix } from "@harmonia-audio/native";
265
+ var StereoWidenerEffect = class {
266
+ _width;
267
+ _monoOutput;
268
+ constructor(width = 1, monoOutput = false) {
269
+ this._width = width;
270
+ this._monoOutput = monoOutput;
271
+ }
272
+ /**
273
+ * Set the stereo width.
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * widener.setWidth(2.0);
278
+ * ```
279
+ */
280
+ setWidth(width) {
281
+ this._width = width;
282
+ }
283
+ process(samples) {
284
+ if (this._monoOutput) {
285
+ return monoDownmix(samples);
286
+ }
287
+ if (this._width === 1) {
288
+ return samples;
289
+ }
290
+ return stereoWiden(samples, this._width);
291
+ }
292
+ reset() {
293
+ }
294
+ destroy() {
295
+ }
296
+ };
297
+
298
+ // src/loudness.ts
299
+ import { LoudnessNormalizer } from "@harmonia-audio/native";
300
+ var LoudnessNormalizerEffect = class {
301
+ native;
302
+ constructor(options) {
303
+ this.native = new LoudnessNormalizer(
304
+ options.targetLufs,
305
+ options.sampleRate ?? 48e3
306
+ );
307
+ }
308
+ process(samples) {
309
+ return this.native.process(samples);
310
+ }
311
+ reset() {
312
+ }
313
+ destroy() {
314
+ }
315
+ };
316
+ export {
317
+ CompressorEffect,
318
+ DelayEffect,
319
+ EffectsPipeline,
320
+ EqualizerEffect,
321
+ LoudnessNormalizerEffect,
322
+ NoiseGateEffect,
323
+ PitchShiftEffect,
324
+ ReverbEffect,
325
+ StereoWidenerEffect,
326
+ VolumeEffect
327
+ };
328
+ //# sourceMappingURL=index.js.map