1 | import { Gain } from "../core/context/Gain.js";
|
2 | import { ToneAudioNode, } from "../core/context/ToneAudioNode.js";
|
3 | import { noOp } from "../core/util/Interface.js";
|
4 | import { assert } from "../core/util/Debug.js";
|
5 |
|
6 |
|
7 |
|
8 | export class OneShotSource extends ToneAudioNode {
|
9 | constructor(options) {
|
10 | super(options);
|
11 | |
12 |
|
13 |
|
14 |
|
15 | this.onended = noOp;
|
16 | |
17 |
|
18 |
|
19 | this._startTime = -1;
|
20 | |
21 |
|
22 |
|
23 | this._stopTime = -1;
|
24 | |
25 |
|
26 |
|
27 | this._timeout = -1;
|
28 | |
29 |
|
30 |
|
31 | this.output = new Gain({
|
32 | context: this.context,
|
33 | gain: 0,
|
34 | });
|
35 | |
36 |
|
37 |
|
38 | this._gainNode = this.output;
|
39 | |
40 |
|
41 |
|
42 | this.getStateAtTime = function (time) {
|
43 | const computedTime = this.toSeconds(time);
|
44 | if (this._startTime !== -1 &&
|
45 | computedTime >= this._startTime &&
|
46 | (this._stopTime === -1 || computedTime <= this._stopTime)) {
|
47 | return "started";
|
48 | }
|
49 | else {
|
50 | return "stopped";
|
51 | }
|
52 | };
|
53 | this._fadeIn = options.fadeIn;
|
54 | this._fadeOut = options.fadeOut;
|
55 | this._curve = options.curve;
|
56 | this.onended = options.onended;
|
57 | }
|
58 | static getDefaults() {
|
59 | return Object.assign(ToneAudioNode.getDefaults(), {
|
60 | curve: "linear",
|
61 | fadeIn: 0,
|
62 | fadeOut: 0,
|
63 | onended: noOp,
|
64 | });
|
65 | }
|
66 | |
67 |
|
68 |
|
69 |
|
70 | _startGain(time, gain = 1) {
|
71 | assert(this._startTime === -1, "Source cannot be started more than once");
|
72 |
|
73 | const fadeInTime = this.toSeconds(this._fadeIn);
|
74 |
|
75 | this._startTime = time + fadeInTime;
|
76 | this._startTime = Math.max(this._startTime, this.context.currentTime);
|
77 |
|
78 | if (fadeInTime > 0) {
|
79 | this._gainNode.gain.setValueAtTime(0, time);
|
80 | if (this._curve === "linear") {
|
81 | this._gainNode.gain.linearRampToValueAtTime(gain, time + fadeInTime);
|
82 | }
|
83 | else {
|
84 | this._gainNode.gain.exponentialApproachValueAtTime(gain, time, fadeInTime);
|
85 | }
|
86 | }
|
87 | else {
|
88 | this._gainNode.gain.setValueAtTime(gain, time);
|
89 | }
|
90 | return this;
|
91 | }
|
92 | |
93 |
|
94 |
|
95 |
|
96 | stop(time) {
|
97 | this.log("stop", time);
|
98 | this._stopGain(this.toSeconds(time));
|
99 | return this;
|
100 | }
|
101 | |
102 |
|
103 |
|
104 |
|
105 | _stopGain(time) {
|
106 | assert(this._startTime !== -1, "'start' must be called before 'stop'");
|
107 |
|
108 | this.cancelStop();
|
109 |
|
110 | const fadeOutTime = this.toSeconds(this._fadeOut);
|
111 |
|
112 | this._stopTime = this.toSeconds(time) + fadeOutTime;
|
113 | this._stopTime = Math.max(this._stopTime, this.now());
|
114 | if (fadeOutTime > 0) {
|
115 |
|
116 | if (this._curve === "linear") {
|
117 | this._gainNode.gain.linearRampTo(0, fadeOutTime, time);
|
118 | }
|
119 | else {
|
120 | this._gainNode.gain.targetRampTo(0, fadeOutTime, time);
|
121 | }
|
122 | }
|
123 | else {
|
124 |
|
125 | this._gainNode.gain.cancelAndHoldAtTime(time);
|
126 | this._gainNode.gain.setValueAtTime(0, time);
|
127 | }
|
128 | this.context.clearTimeout(this._timeout);
|
129 | this._timeout = this.context.setTimeout(() => {
|
130 |
|
131 | const additionalTail = this._curve === "exponential" ? fadeOutTime * 2 : 0;
|
132 | this._stopSource(this.now() + additionalTail);
|
133 | this._onended();
|
134 | }, this._stopTime - this.context.currentTime);
|
135 | return this;
|
136 | }
|
137 | |
138 |
|
139 |
|
140 | _onended() {
|
141 | if (this.onended !== noOp) {
|
142 | this.onended(this);
|
143 |
|
144 | this.onended = noOp;
|
145 |
|
146 | if (!this.context.isOffline) {
|
147 | const disposeCallback = () => this.dispose();
|
148 |
|
149 | if (typeof window.requestIdleCallback !== "undefined") {
|
150 |
|
151 | window.requestIdleCallback(disposeCallback);
|
152 | }
|
153 | else {
|
154 | setTimeout(disposeCallback, 1000);
|
155 | }
|
156 | }
|
157 | }
|
158 | }
|
159 | |
160 |
|
161 |
|
162 | get state() {
|
163 | return this.getStateAtTime(this.now());
|
164 | }
|
165 | |
166 |
|
167 |
|
168 | cancelStop() {
|
169 | this.log("cancelStop");
|
170 | assert(this._startTime !== -1, "Source is not started");
|
171 |
|
172 | this._gainNode.gain.cancelScheduledValues(this._startTime + this.sampleTime);
|
173 | this.context.clearTimeout(this._timeout);
|
174 | this._stopTime = -1;
|
175 | return this;
|
176 | }
|
177 | dispose() {
|
178 | super.dispose();
|
179 | this._gainNode.dispose();
|
180 | this.onended = noOp;
|
181 | return this;
|
182 | }
|
183 | }
|
184 |
|
\ | No newline at end of file |