Skip to content

Commit

Permalink
feat: Use AudioWorkletNode instead of ScriptProcessorNode in clas…
Browse files Browse the repository at this point in the history
…ses that extends `Effector`
  • Loading branch information
Korilakkuma committed Mar 4, 2023
1 parent 1cfb043 commit d7f5e44
Show file tree
Hide file tree
Showing 30 changed files with 222 additions and 529 deletions.
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Autopanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class Autopanner extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.panner = this.context.createStereoPanner();

Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/BitCrusher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class BitCrusher extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.shaper = this.context.createWaveShaper();
this.inputShaper = this.context.createWaveShaper();
Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Chorus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class Chorus extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.delay = context.createDelay();
this.mix = context.createGain();
Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Compressor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class Compressor extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.compressor = context.createDynamicsCompressor();

Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Delay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class Delay extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.delay = context.createDelay(Delay.MAX_DELAY_TIME);
this.dry = context.createGain();
Expand Down
12 changes: 4 additions & 8 deletions src/SoundModule/Effectors/Effector.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Connectable, Statable } from '../../interfaces';
import { BufferSize } from '../../types';

/**
* This class is superclass for effector classes.
Expand All @@ -17,16 +16,14 @@ export abstract class Effector implements Connectable, Statable {
protected lfo: OscillatorNode;
protected depth: GainNode;
protected rate: AudioParam;
protected processor: ScriptProcessorNode;

protected isActive = true;
protected paused = true;

/**
* @param {AudioContext} context This argument is in order to use Web Audio API.
* @param {BufferSize} bufferSize This argument is buffer size for `ScriptProcessorNode`.
*/
constructor(context: AudioContext, bufferSize: BufferSize) {
constructor(context: AudioContext) {
this.context = context;

// for connecting external modules
Expand All @@ -35,10 +32,9 @@ export abstract class Effector implements Connectable, Statable {

// for LFO (Low Frequency Oscillator)
// LFO changes parameter cyclically
this.lfo = context.createOscillator();
this.depth = context.createGain();
this.rate = this.lfo.frequency;
this.processor = context.createScriptProcessor(bufferSize, 2, 2);
this.lfo = context.createOscillator();
this.depth = context.createGain();
this.rate = this.lfo.frequency;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Equalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class Equalizer extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
**/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.bass = context.createBiquadFilter();
this.middle = context.createBiquadFilter();
Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class Filter extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.filter = context.createBiquadFilter();

Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Flanger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class Flanger extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.delay = context.createDelay();
this.mix = context.createGain();
Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Fuzz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class Fuzz extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.positiveShaper = this.context.createWaveShaper();
this.negativeShaper = this.context.createWaveShaper();
Expand Down
2 changes: 1 addition & 1 deletion src/SoundModule/Effectors/Listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class Listener extends Effector {
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

// instance of `AudioListener`
this.listener = context.listener;
Expand Down
75 changes: 26 additions & 49 deletions src/SoundModule/Effectors/NoiseGate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Effector } from './Effector';
import { NoiseGateProcessor } from './AudioWorkletProcessors/NoiseGateProcessor';

export type NoiseGateParams = {
state?: boolean,
Expand All @@ -11,55 +12,26 @@ export type NoiseGateParams = {
* @extends {Effector}
*/
export class NoiseGate extends Effector {
private processor: AudioWorkletNode;

private level = 0;

/**
* @param {AudioContext} context This argument is in order to use Web Audio API.
*/
constructor(context: AudioContext) {
super(context, 0);
super(context);

this.processor = new AudioWorkletNode(this.context, NoiseGateProcessor.name);
this.activate();
}

/** @override */
public override start(): void {
if (!this.isActive || !this.paused) {
return;
}

this.paused = false;

const bufferSize = this.processor.bufferSize;

this.processor.onaudioprocess = (event: AudioProcessingEvent) => {
const inputLs = event.inputBuffer.getChannelData(0);
const inputRs = event.inputBuffer.getChannelData(1);
const outputLs = event.outputBuffer.getChannelData(0);
const outputRs = event.outputBuffer.getChannelData(1);

for (let i = 0; i < bufferSize; i++) {
outputLs[i] = this.gate(inputLs[i]);
outputRs[i] = this.gate(inputRs[i]);
}
};
}

/** @override */
public override stop(): void {
// Effector's state is active ?
if (!this.isActive) {
return;
}

this.paused = true;

// Stop `onaudioprocess` event
this.processor.disconnect(0);
this.processor.onaudioprocess = null;

// Connect `AudioNode`s again
this.connect();
}

/** @override */
Expand All @@ -68,8 +40,16 @@ export class NoiseGate extends Effector {
this.input.disconnect(0);
this.processor.disconnect(0);

this.processor = new AudioWorkletNode(this.context, NoiseGateProcessor.name);

const message: NoiseGateParams = {
level: this.level
};

this.processor.port.postMessage(message);

if (this.isActive) {
// GainNode (Input) -> ScriptProcessorNode (Noise Gate) -> GainNode (Output);
// GainNode (Input) -> AudioWorkletNode (Noise Gate) -> GainNode (Output);
this.input.connect(this.processor);
this.processor.connect(this.output);
} else {
Expand Down Expand Up @@ -106,12 +86,24 @@ export class NoiseGate extends Effector {
case 'state':
if (typeof value === 'boolean') {
this.isActive = value;

if (this.processor) {
const message: NoiseGateParams = { state: value };

this.processor.port.postMessage(message);
}
}

break;
case 'level':
if (typeof value === 'number') {
this.level = value;

if (this.processor) {
const message: NoiseGateParams = { level: value };

this.processor.port.postMessage(message);
}
}

break;
Expand Down Expand Up @@ -142,19 +134,4 @@ export class NoiseGate extends Effector {
super.deactivate();
return this;
}

/**
* This method detects background noise and removes this.
* @param {number} data This argument is amplitude (between -1 and 1).
* @return {number} Return value is `0` or raw data.
*/
private gate(data: number): number {
if (!this.isActive) {
return data;
}

// data : Amplitude is equal to argument.
// 0 : Because signal is detected as background noise, amplitude is `0`.
return (Math.abs(data) > this.level) ? data : 0;
}
}
Loading

0 comments on commit d7f5e44

Please sign in to comment.