UNPKG

5 kBJavaScriptView Raw
1import { Volume } from "../component/channel/Volume.js";
2import { ToneAudioNode, } from "../core/context/ToneAudioNode.js";
3import { optionsFromArguments } from "../core/util/Defaults.js";
4import { readOnly } from "../core/util/Interface.js";
5/**
6 * Base-class for all instruments
7 */
8export class Instrument extends ToneAudioNode {
9 constructor() {
10 const options = optionsFromArguments(Instrument.getDefaults(), arguments);
11 super(options);
12 /**
13 * Keep track of all events scheduled to the transport
14 * when the instrument is 'synced'
15 */
16 this._scheduledEvents = [];
17 /**
18 * If the instrument is currently synced
19 */
20 this._synced = false;
21 this._original_triggerAttack = this.triggerAttack;
22 this._original_triggerRelease = this.triggerRelease;
23 /**
24 * The release which is scheduled to the timeline.
25 */
26 this._syncedRelease = (time) => this._original_triggerRelease(time);
27 this._volume = this.output = new Volume({
28 context: this.context,
29 volume: options.volume,
30 });
31 this.volume = this._volume.volume;
32 readOnly(this, "volume");
33 }
34 static getDefaults() {
35 return Object.assign(ToneAudioNode.getDefaults(), {
36 volume: 0,
37 });
38 }
39 /**
40 * Sync the instrument to the Transport. All subsequent calls of
41 * {@link triggerAttack} and {@link triggerRelease} will be scheduled along the transport.
42 * @example
43 * const fmSynth = new Tone.FMSynth().toDestination();
44 * fmSynth.volume.value = -6;
45 * fmSynth.sync();
46 * // schedule 3 notes when the transport first starts
47 * fmSynth.triggerAttackRelease("C4", "8n", 0);
48 * fmSynth.triggerAttackRelease("E4", "8n", "8n");
49 * fmSynth.triggerAttackRelease("G4", "8n", "4n");
50 * // start the transport to hear the notes
51 * Tone.Transport.start();
52 */
53 sync() {
54 if (this._syncState()) {
55 this._syncMethod("triggerAttack", 1);
56 this._syncMethod("triggerRelease", 0);
57 this.context.transport.on("stop", this._syncedRelease);
58 this.context.transport.on("pause", this._syncedRelease);
59 this.context.transport.on("loopEnd", this._syncedRelease);
60 }
61 return this;
62 }
63 /**
64 * set _sync
65 */
66 _syncState() {
67 let changed = false;
68 if (!this._synced) {
69 this._synced = true;
70 changed = true;
71 }
72 return changed;
73 }
74 /**
75 * Wrap the given method so that it can be synchronized
76 * @param method Which method to wrap and sync
77 * @param timePosition What position the time argument appears in
78 */
79 _syncMethod(method, timePosition) {
80 const originalMethod = (this["_original_" + method] = this[method]);
81 this[method] = (...args) => {
82 const time = args[timePosition];
83 const id = this.context.transport.schedule((t) => {
84 args[timePosition] = t;
85 originalMethod.apply(this, args);
86 }, time);
87 this._scheduledEvents.push(id);
88 };
89 }
90 /**
91 * Unsync the instrument from the Transport
92 */
93 unsync() {
94 this._scheduledEvents.forEach((id) => this.context.transport.clear(id));
95 this._scheduledEvents = [];
96 if (this._synced) {
97 this._synced = false;
98 this.triggerAttack = this._original_triggerAttack;
99 this.triggerRelease = this._original_triggerRelease;
100 this.context.transport.off("stop", this._syncedRelease);
101 this.context.transport.off("pause", this._syncedRelease);
102 this.context.transport.off("loopEnd", this._syncedRelease);
103 }
104 return this;
105 }
106 /**
107 * Trigger the attack and then the release after the duration.
108 * @param note The note to trigger.
109 * @param duration How long the note should be held for before
110 * triggering the release. This value must be greater than 0.
111 * @param time When the note should be triggered.
112 * @param velocity The velocity the note should be triggered at.
113 * @example
114 * const synth = new Tone.Synth().toDestination();
115 * // trigger "C4" for the duration of an 8th note
116 * synth.triggerAttackRelease("C4", "8n");
117 */
118 triggerAttackRelease(note, duration, time, velocity) {
119 const computedTime = this.toSeconds(time);
120 const computedDuration = this.toSeconds(duration);
121 this.triggerAttack(note, computedTime, velocity);
122 this.triggerRelease(computedTime + computedDuration);
123 return this;
124 }
125 /**
126 * clean up
127 * @returns {Instrument} this
128 */
129 dispose() {
130 super.dispose();
131 this._volume.dispose();
132 this.unsync();
133 this._scheduledEvents = [];
134 return this;
135 }
136}
137//# sourceMappingURL=Instrument.js.map
\No newline at end of file