UNPKG

7.49 kBJavaScriptView Raw
1import { TicksClass } from "../core/type/Ticks.js";
2import { omitFromObject, optionsFromArguments } from "../core/util/Defaults.js";
3import { isArray, isString } from "../core/util/TypeCheck.js";
4import { Part } from "./Part.js";
5import { ToneEvent } from "./ToneEvent.js";
6/**
7 * A sequence is an alternate notation of a part. Instead
8 * of passing in an array of [time, event] pairs, pass
9 * in an array of events which will be spaced at the
10 * given subdivision. Sub-arrays will subdivide that beat
11 * by the number of items are in the array.
12 * Sequence notation inspiration from [Tidal Cycles](http://tidalcycles.org/)
13 * @example
14 * const synth = new Tone.Synth().toDestination();
15 * const seq = new Tone.Sequence((time, note) => {
16 * synth.triggerAttackRelease(note, 0.1, time);
17 * // subdivisions are given as subarrays
18 * }, ["C4", ["E4", "D4", "E4"], "G4", ["A4", "G4"]]).start(0);
19 * Tone.Transport.start();
20 * @category Event
21 */
22export class Sequence extends ToneEvent {
23 constructor() {
24 const options = optionsFromArguments(Sequence.getDefaults(), arguments, ["callback", "events", "subdivision"]);
25 super(options);
26 this.name = "Sequence";
27 /**
28 * The object responsible for scheduling all of the events
29 */
30 this._part = new Part({
31 callback: this._seqCallback.bind(this),
32 context: this.context,
33 });
34 /**
35 * private reference to all of the sequence proxies
36 */
37 this._events = [];
38 /**
39 * The proxied array
40 */
41 this._eventsArray = [];
42 this._subdivision = this.toTicks(options.subdivision);
43 this.events = options.events;
44 // set all of the values
45 this.loop = options.loop;
46 this.loopStart = options.loopStart;
47 this.loopEnd = options.loopEnd;
48 this.playbackRate = options.playbackRate;
49 this.probability = options.probability;
50 this.humanize = options.humanize;
51 this.mute = options.mute;
52 this.playbackRate = options.playbackRate;
53 }
54 static getDefaults() {
55 return Object.assign(omitFromObject(ToneEvent.getDefaults(), ["value"]), {
56 events: [],
57 loop: true,
58 loopEnd: 0,
59 loopStart: 0,
60 subdivision: "8n",
61 });
62 }
63 /**
64 * The internal callback for when an event is invoked
65 */
66 _seqCallback(time, value) {
67 if (value !== null && !this.mute) {
68 this.callback(time, value);
69 }
70 }
71 /**
72 * The sequence
73 */
74 get events() {
75 return this._events;
76 }
77 set events(s) {
78 this.clear();
79 this._eventsArray = s;
80 this._events = this._createSequence(this._eventsArray);
81 this._eventsUpdated();
82 }
83 /**
84 * Start the part at the given time.
85 * @param time When to start the part.
86 * @param offset The offset index to start at
87 */
88 start(time, offset) {
89 this._part.start(time, offset ? this._indexTime(offset) : offset);
90 return this;
91 }
92 /**
93 * Stop the part at the given time.
94 * @param time When to stop the part.
95 */
96 stop(time) {
97 this._part.stop(time);
98 return this;
99 }
100 /**
101 * The subdivision of the sequence. This can only be
102 * set in the constructor. The subdivision is the
103 * interval between successive steps.
104 */
105 get subdivision() {
106 return new TicksClass(this.context, this._subdivision).toSeconds();
107 }
108 /**
109 * Create a sequence proxy which can be monitored to create subsequences
110 */
111 _createSequence(array) {
112 return new Proxy(array, {
113 get: (target, property) => {
114 // property is index in this case
115 return target[property];
116 },
117 set: (target, property, value) => {
118 if (isString(property) && isFinite(parseInt(property, 10))) {
119 if (isArray(value)) {
120 target[property] = this._createSequence(value);
121 }
122 else {
123 target[property] = value;
124 }
125 }
126 else {
127 target[property] = value;
128 }
129 this._eventsUpdated();
130 // return true to accept the changes
131 return true;
132 },
133 });
134 }
135 /**
136 * When the sequence has changed, all of the events need to be recreated
137 */
138 _eventsUpdated() {
139 this._part.clear();
140 this._rescheduleSequence(this._eventsArray, this._subdivision, this.startOffset);
141 // update the loopEnd
142 this.loopEnd = this.loopEnd;
143 }
144 /**
145 * reschedule all of the events that need to be rescheduled
146 */
147 _rescheduleSequence(sequence, subdivision, startOffset) {
148 sequence.forEach((value, index) => {
149 const eventOffset = index * subdivision + startOffset;
150 if (isArray(value)) {
151 this._rescheduleSequence(value, subdivision / value.length, eventOffset);
152 }
153 else {
154 const startTime = new TicksClass(this.context, eventOffset, "i").toSeconds();
155 this._part.add(startTime, value);
156 }
157 });
158 }
159 /**
160 * Get the time of the index given the Sequence's subdivision
161 * @param index
162 * @return The time of that index
163 */
164 _indexTime(index) {
165 return new TicksClass(this.context, index * this._subdivision + this.startOffset).toSeconds();
166 }
167 /**
168 * Clear all of the events
169 */
170 clear() {
171 this._part.clear();
172 return this;
173 }
174 dispose() {
175 super.dispose();
176 this._part.dispose();
177 return this;
178 }
179 //-------------------------------------
180 // PROXY CALLS
181 //-------------------------------------
182 get loop() {
183 return this._part.loop;
184 }
185 set loop(l) {
186 this._part.loop = l;
187 }
188 /**
189 * The index at which the sequence should start looping
190 */
191 get loopStart() {
192 return this._loopStart;
193 }
194 set loopStart(index) {
195 this._loopStart = index;
196 this._part.loopStart = this._indexTime(index);
197 }
198 /**
199 * The index at which the sequence should end looping
200 */
201 get loopEnd() {
202 return this._loopEnd;
203 }
204 set loopEnd(index) {
205 this._loopEnd = index;
206 if (index === 0) {
207 this._part.loopEnd = this._indexTime(this._eventsArray.length);
208 }
209 else {
210 this._part.loopEnd = this._indexTime(index);
211 }
212 }
213 get startOffset() {
214 return this._part.startOffset;
215 }
216 set startOffset(start) {
217 this._part.startOffset = start;
218 }
219 get playbackRate() {
220 return this._part.playbackRate;
221 }
222 set playbackRate(rate) {
223 this._part.playbackRate = rate;
224 }
225 get probability() {
226 return this._part.probability;
227 }
228 set probability(prob) {
229 this._part.probability = prob;
230 }
231 get progress() {
232 return this._part.progress;
233 }
234 get humanize() {
235 return this._part.humanize;
236 }
237 set humanize(variation) {
238 this._part.humanize = variation;
239 }
240 /**
241 * The number of scheduled events
242 */
243 get length() {
244 return this._part.length;
245 }
246}
247//# sourceMappingURL=Sequence.js.map
\No newline at end of file