UNPKG

3.41 kBPlain TextView Raw
1import {
2 ToneAudioWorklet,
3 ToneAudioWorkletOptions,
4} from "../core/worklet/ToneAudioWorklet.js";
5import { Effect, EffectOptions } from "./Effect.js";
6import { Positive } from "../core/type/Units.js";
7import { Gain } from "../core/context/Gain.js";
8import { optionsFromArguments } from "../core/util/Defaults.js";
9import { connectSeries } from "../core/context/ToneAudioNode.js";
10import { Param } from "../core/context/Param.js";
11import { workletName } from "./BitCrusher.worklet.js";
12
13export interface BitCrusherOptions extends EffectOptions {
14 bits: Positive;
15}
16
17/**
18 * BitCrusher down-samples the incoming signal to a different bit depth.
19 * Lowering the bit depth of the signal creates distortion. Read more about BitCrushing
20 * on [Wikipedia](https://en.wikipedia.org/wiki/Bitcrusher).
21 * @example
22 * // initialize crusher and route a synth through it
23 * const crusher = new Tone.BitCrusher(4).toDestination();
24 * const synth = new Tone.Synth().connect(crusher);
25 * synth.triggerAttackRelease("C2", 2);
26 *
27 * @category Effect
28 */
29export class BitCrusher extends Effect<BitCrusherOptions> {
30 readonly name: string = "BitCrusher";
31
32 /**
33 * The bit depth of the effect
34 * @min 1
35 * @max 16
36 */
37 readonly bits: Param<"positive">;
38
39 /**
40 * The node which does the bit crushing effect. Runs in an AudioWorklet when possible.
41 */
42 private _bitCrusherWorklet: BitCrusherWorklet;
43
44 constructor(bits?: Positive);
45 constructor(options?: Partial<BitCrusherWorkletOptions>);
46 constructor() {
47 const options = optionsFromArguments(
48 BitCrusher.getDefaults(),
49 arguments,
50 ["bits"]
51 );
52 super(options);
53
54 this._bitCrusherWorklet = new BitCrusherWorklet({
55 context: this.context,
56 bits: options.bits,
57 });
58 // connect it up
59 this.connectEffect(this._bitCrusherWorklet);
60
61 this.bits = this._bitCrusherWorklet.bits;
62 }
63
64 static getDefaults(): BitCrusherOptions {
65 return Object.assign(Effect.getDefaults(), {
66 bits: 4,
67 });
68 }
69
70 dispose(): this {
71 super.dispose();
72 this._bitCrusherWorklet.dispose();
73 return this;
74 }
75}
76
77interface BitCrusherWorkletOptions extends ToneAudioWorkletOptions {
78 bits: number;
79}
80
81/**
82 * Internal class which creates an AudioWorklet to do the bit crushing
83 */
84class BitCrusherWorklet extends ToneAudioWorklet<BitCrusherWorkletOptions> {
85 readonly name: string = "BitCrusherWorklet";
86
87 readonly input: Gain;
88 readonly output: Gain;
89
90 readonly bits: Param<"positive">;
91
92 constructor(options?: Partial<BitCrusherWorkletOptions>);
93 constructor() {
94 const options = optionsFromArguments(
95 BitCrusherWorklet.getDefaults(),
96 arguments
97 );
98 super(options);
99
100 this.input = new Gain({ context: this.context });
101 this.output = new Gain({ context: this.context });
102
103 this.bits = new Param<"positive">({
104 context: this.context,
105 value: options.bits,
106 units: "positive",
107 minValue: 1,
108 maxValue: 16,
109 param: this._dummyParam,
110 swappable: true,
111 });
112 }
113
114 static getDefaults(): BitCrusherWorkletOptions {
115 return Object.assign(ToneAudioWorklet.getDefaults(), {
116 bits: 12,
117 });
118 }
119
120 protected _audioWorkletName(): string {
121 return workletName;
122 }
123
124 onReady(node: AudioWorkletNode) {
125 connectSeries(this.input, node, this.output);
126 const bits = node.parameters.get("bits") as AudioParam;
127 this.bits.setParam(bits);
128 }
129
130 dispose(): this {
131 super.dispose();
132 this.input.dispose();
133 this.output.dispose();
134 this.bits.dispose();
135 return this;
136 }
137}