littlejsengine 1.13.4 → 1.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +2 -1
  2. package/copyToGithub.bat +28 -0
  3. package/dist/littlejs.d.ts +346 -240
  4. package/dist/littlejs.esm.js +1370 -723
  5. package/dist/littlejs.esm.min.js +1 -1
  6. package/dist/littlejs.js +1350 -709
  7. package/dist/littlejs.min.js +1 -1
  8. package/dist/littlejs.release.js +1333 -692
  9. package/examples/box2d/game.js +1 -1
  10. package/examples/box2d/gameObjects.js +1 -1
  11. package/examples/breakout/game.js +30 -25
  12. package/examples/electron/index.html +2 -2
  13. package/examples/index.html +207 -96
  14. package/examples/platformer/gameEffects.js +19 -18
  15. package/examples/platformer/gameLevel.js +6 -1
  16. package/examples/shorts/base.html +1 -1
  17. package/examples/shorts/flappyGame.js +2 -1
  18. package/examples/shorts/helloWorld.js +1 -1
  19. package/examples/shorts/music.js +106 -0
  20. package/examples/shorts/parallax.js +71 -0
  21. package/examples/shorts/piano.js +7 -8
  22. package/examples/shorts/postProcess.js +25 -8
  23. package/examples/shorts/shapes.js +1 -9
  24. package/examples/shorts/song.mp3 +0 -0
  25. package/examples/shorts/uiSystem.js +15 -8
  26. package/examples/starter/index.html +2 -2
  27. package/examples/stress/index.html +8 -3
  28. package/examples/style.css +14 -4
  29. package/examples/uiSystem/game.js +11 -11
  30. package/package.json +1 -1
  31. package/plugins/box2d.js +10 -8
  32. package/plugins/drawUtilities.js +5 -5
  33. package/plugins/newgrounds.js +6 -6
  34. package/plugins/pluginExport.js +1 -1
  35. package/plugins/uiSystem.js +103 -79
  36. package/plugins/zzfxm.js +10 -10
  37. package/reference.md +90 -54
  38. package/src/engine.js +22 -22
  39. package/src/engineAudio.js +284 -109
  40. package/src/engineBuild.js +9 -9
  41. package/src/engineDebug.js +26 -26
  42. package/src/engineDraw.js +148 -97
  43. package/src/engineExport.js +22 -16
  44. package/src/engineInput.js +57 -67
  45. package/src/engineMedals.js +25 -25
  46. package/src/engineObject.js +25 -22
  47. package/src/engineParticles.js +59 -59
  48. package/src/engineRelease.js +1 -1
  49. package/src/engineSettings.js +20 -13
  50. package/src/engineTileLayer.js +34 -34
  51. package/src/engineUtilities.js +62 -55
  52. package/src/engineWebGL.js +447 -65
  53. /package/examples/shorts/{playSound.js → sound.js} +0 -0
@@ -1,4 +1,4 @@
1
- /**
1
+ /**
2
2
  * LittleJS Audio System
3
3
  * - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a> - ZzFX Sound Effect Generator
4
4
  * - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a> - ZzFXM Music System
@@ -21,10 +21,21 @@ let audioContext = new AudioContext;
21
21
  * @memberof Audio */
22
22
  let audioMasterGain;
23
23
 
24
+ /** Default sample rate used for sounds
25
+ * @default 44100
26
+ * @memberof Audio */
27
+ const audioDefaultSampleRate = 44100;
28
+
29
+ /** Check if the audio context is running and available for playback
30
+ * @return {boolean} - True if the audio context is running
31
+ * @memberof Audio */
32
+ function audioIsRunning()
33
+ { return audioContext.state === 'running'; }
34
+
24
35
  function audioInit()
25
36
  {
26
37
  if (!soundEnable || headlessMode) return;
27
-
38
+
28
39
  audioMasterGain = audioContext.createGain();
29
40
  audioMasterGain.connect(audioContext.destination);
30
41
  audioMasterGain.gain.value = soundVolume; // set starting value
@@ -32,14 +43,14 @@ function audioInit()
32
43
 
33
44
  ///////////////////////////////////////////////////////////////////////////////
34
45
 
35
- /**
46
+ /**
36
47
  * Sound Object - Stores a sound for later use and can be played positionally
37
- *
48
+ *
38
49
  * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
39
50
  * @example
40
51
  * // create a sound
41
52
  * const sound_example = new Sound([.5,.5]);
42
- *
53
+ *
43
54
  * // play the sound
44
55
  * sound_example.play();
45
56
  */
@@ -56,33 +67,41 @@ class Sound
56
67
 
57
68
  /** @property {number} - World space max range of sound */
58
69
  this.range = range;
59
-
60
70
  /** @property {number} - At what percentage of range should it start tapering */
61
71
  this.taper = taper;
62
-
63
72
  /** @property {number} - How much to randomize frequency each time sound plays */
64
73
  this.randomness = 0;
74
+ /** @property {number} - Sample rate for this sound */
75
+ this.sampleRate = audioDefaultSampleRate;
76
+ /** @property {number} - Percentage of this sound currently loaded */
77
+ this.loadedPercent = 0;
65
78
 
79
+ // generate zzfx sound now for fast playback
66
80
  if (zzfxSound)
67
81
  {
68
- // generate zzfx sound now for fast playback
69
- const defaultRandomness = .05;
70
- this.randomness = zzfxSound[1] != undefined ? zzfxSound[1] : defaultRandomness;
71
- zzfxSound[1] = 0; // generate without randomness
82
+ // remove randomness so it can be applied on playback
83
+ const randomnessIndex = 1, defaultRandomness = .05;
84
+ this.randomness = zzfxSound[randomnessIndex] !== undefined ?
85
+ zzfxSound[randomnessIndex] : defaultRandomness;
86
+ zzfxSound[randomnessIndex] = 0;
87
+
88
+ // generate the zzfx samples
72
89
  this.sampleChannels = [zzfxG(...zzfxSound)];
73
- this.sampleRate = zzfxR;
90
+ this.loadedPercent = 1;
74
91
  }
75
92
  }
76
93
 
77
94
  /** Play the sound
78
- * @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
79
- * @param {number} [volume] - How much to scale volume by (in addition to range fade)
80
- * @param {number} [pitch] - How much to scale pitch by (also adjusted by this.randomness)
81
- * @param {number} [randomnessScale] - How much to scale randomness
82
- * @param {boolean} [loop] - Should the sound loop
83
- * @return {AudioBufferSourceNode} - The audio source node
95
+ * Sounds may not play until a user interaction occurs
96
+ * @param {Vector2} [pos] - World space position to play the sound if any
97
+ * @param {number} [volume] - How much to scale volume by
98
+ * @param {number} [pitch] - How much to scale pitch by
99
+ * @param {number} [randomnessScale] - How much to scale pitch randomness
100
+ * @param {boolean} [loop] - Should the sound loop?
101
+ * @param {boolean} [paused] - Should the sound start paused
102
+ * @return {SoundInstance} - The audio source node
84
103
  */
85
- play(pos, volume=1, pitch=1, randomnessScale=1, loop=false)
104
+ play(pos, volume=1, pitch=1, randomnessScale=1, loop=false, paused=false)
86
105
  {
87
106
  if (!soundEnable || headlessMode) return;
88
107
  if (!this.sampleChannels) return;
@@ -105,75 +124,55 @@ class Sound
105
124
  // get pan from screen space coords
106
125
  pan = worldToScreen(pos).x * 2/mainCanvas.width - 1;
107
126
  }
108
-
109
- // play the sound
110
- const playbackRate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
111
- this.gainNode = audioContext.createGain();
112
- this.source = playSamples(this.sampleChannels, volume, playbackRate, pan, loop, this.sampleRate, this.gainNode);
113
- return this.source;
114
- }
115
-
116
- /** Set the sound volume of the most recently played instance of this sound
117
- * @param {number} [volume] - How much to scale volume by
118
- */
119
- setVolume(volume=1)
120
- {
121
- if (this.gainNode)
122
- this.gainNode.gain.value = volume;
123
- }
124
-
125
- /** Stop the last instance of this sound that was played
126
- * @param {number} [fadeTime] - How long to fade out (seconds)
127
- */
128
- stop(fadeTime=0)
129
- {
130
- if (!this.source)
131
- return;
132
127
 
133
- // ramp off gain
134
- const startFade = audioContext.currentTime;
135
- const endFade = startFade + fadeTime;
136
- this.gainNode.gain.linearRampToValueAtTime(1, startFade);
137
- this.gainNode.gain.linearRampToValueAtTime(0, endFade);
138
- this.source.stop(endFade);
139
- this.source = undefined;
128
+ // Create and return sound instance
129
+ const rate = pitch + pitch * this.randomness*randomnessScale*rand(-1,1);
130
+ return new SoundInstance(this, volume, rate, pan, loop, paused);
140
131
  }
141
132
 
142
- /** Get source of most recent instance of this sound that was played
143
- * @return {AudioBufferSourceNode}
133
+ /** Play a music track that loops by default
134
+ * @param {number} [volume] - Volume to play the music at
135
+ * @param {boolean} [loop] - Should the music loop?
136
+ * @param {boolean} [paused] - Should the music start paused
137
+ * @return {SoundInstance} - The audio source node
144
138
  */
145
- getSource() { return this.source; }
139
+ playMusic(volume=1, loop=true, paused=false)
140
+ { return this.play(undefined, volume, 1, 0, loop, paused); }
146
141
 
147
- /** Play the sound as a note with a semitone offset
142
+ /** Play the sound as a musical note with a semitone offset
143
+ * This can be used to play music with chromatic scales
148
144
  * @param {number} semitoneOffset - How many semitones to offset pitch
149
- * @param {Vector2} [pos] - World space position to play the sound, sound is not attenuated if null
150
- * @param {number} [volume=1] - How much to scale volume by (in addition to range fade)
151
- * @return {AudioBufferSourceNode} - The audio source node
145
+ * @param {Vector2} [pos] - World space position to play the sound if any
146
+ * @param {number} [volume=1] - How much to scale volume by
147
+ * @return {SoundInstance} - The audio source node
152
148
  */
153
149
  playNote(semitoneOffset, pos, volume)
154
- { return this.play(pos, volume, 2**(semitoneOffset/12), 0); }
150
+ {
151
+ const pitch = getNoteFrequency(semitoneOffset, 1);
152
+ return this.play(pos, volume, pitch, 0);
153
+ }
155
154
 
156
155
  /** Get how long this sound is in seconds
157
156
  * @return {number} - How long the sound is in seconds (undefined if loading)
158
157
  */
159
- getDuration()
158
+ getDuration()
160
159
  { return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
161
-
162
- /** Check if sound is loading, for sounds fetched from a url
163
- * @return {boolean} - True if sound is loading and not ready to play
160
+
161
+ /** Check if sound is loaded, for sounds fetched from a url
162
+ * @return {boolean} - True if sound is loaded and ready to play
164
163
  */
165
- isLoading() { return !this.sampleChannels; }
164
+ isLoaded() { return this.loadedPercent === 1; }
166
165
  }
167
166
 
168
167
  ///////////////////////////////////////////////////////////////////////////////
169
168
 
170
- /**
169
+ /**
171
170
  * Sound Wave Object - Stores a wave sound for later use and can be played positionally
172
171
  * - this can be used to play wave, mp3, and ogg files
173
172
  * @example
174
173
  * // create a sound
175
174
  * const sound_example = new SoundWave('sound.mp3');
176
- *
175
+ *
177
176
  * // play the sound
178
177
  * sound_example.play();
179
178
  */
@@ -205,26 +204,202 @@ class SoundWave extends Sound
205
204
  const response = await fetch(filename);
206
205
  const arrayBuffer = await response.arrayBuffer();
207
206
  const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
208
- this.sampleChannels = [];
209
- for (let i = audioBuffer.numberOfChannels; i--;)
210
- this.sampleChannels[i] = Array.from(audioBuffer.getChannelData(i));
207
+
208
+ // convert audio buffer to sample channels across multiple frames
209
+ const channelCount = audioBuffer.numberOfChannels;
210
+ const samplesPerFrame = 1e5;
211
+ const sampleChannels = [];
212
+ for (let channel = 0; channel < channelCount; channel++)
213
+ {
214
+ const channelData = audioBuffer.getChannelData(channel);
215
+ const channelLength = channelData.length;
216
+ sampleChannels[channel] = new Array(channelLength);
217
+ let sampleIndex = 0;
218
+ while (sampleIndex < channelLength)
219
+ {
220
+ // yield to next frame
221
+ await new Promise(resolve => setTimeout(resolve, 0));
222
+
223
+ // copy chunk of samples
224
+ const endIndex = min(sampleIndex + samplesPerFrame, channelLength);
225
+ for (; sampleIndex < endIndex; sampleIndex++)
226
+ sampleChannels[channel][sampleIndex] = channelData[sampleIndex];
227
+
228
+ // update loaded percent
229
+ const samplesTotal = channelCount * channelLength;
230
+ const samplesProcessed = channel * channelLength + sampleIndex;
231
+ this.loadedPercent = samplesProcessed / samplesTotal;
232
+ }
233
+ }
234
+
235
+ // setup the sound to be played
211
236
  this.sampleRate = audioBuffer.sampleRate;
237
+ this.sampleChannels = sampleChannels;
238
+ this.loadedPercent = 1;
212
239
  if (this.onloadCallback)
213
240
  this.onloadCallback();
214
241
  }
215
242
  }
216
243
 
217
- /** Play an mp3, ogg, or wav audio from a local file or url
218
- * @param {string} filename - Location of sound file to play
219
- * @param {number} [volume] - How much to scale volume by
220
- * @param {boolean} [loop] - True if the music should loop
221
- * @return {SoundWave} - The sound object for this file
222
- * @memberof Audio */
223
- function playAudioFile(filename, volume=1, loop=false)
244
+ ///////////////////////////////////////////////////////////////////////////////
245
+
246
+ /**
247
+ * Sound Instance - Wraps an AudioBufferSourceNode for individual sound control
248
+ * Represents a single playing instance of a sound with pause/resume capabilities
249
+ * @example
250
+ * // Play a sound and get an instance for control
251
+ * const jumpSound = new Sound([.5,.5,220]);
252
+ * const instance = jumpSound.play();
253
+ *
254
+ * // Control the individual instance
255
+ * instance.setVolume(.5);
256
+ * instance.pause();
257
+ * instance.unpause();
258
+ * instance.stop();
259
+ */
260
+ class SoundInstance
224
261
  {
225
- if (!soundEnable || headlessMode) return;
262
+ /** Create a sound instance
263
+ * @param {Sound} sound - The sound object
264
+ * @param {number} [volume] - How much to scale volume by
265
+ * @param {number} [rate] - The playback rate to use
266
+ * @param {number} [pan] - How much to apply stereo panning
267
+ * @param {boolean} [loop] - Should the sound loop?
268
+ * @param {boolean} [paused] - Should the sound start paused? */
269
+ constructor(sound, volume=1, rate=1, pan=0, loop=false, paused=false)
270
+ {
271
+ ASSERT(sound instanceof Sound, 'SoundInstance requires a valid Sound object');
272
+ ASSERT(volume >= 0, 'Sound volume must be positive or zero');
273
+ ASSERT(rate >= 0, 'Sound rate must be positive or zero');
274
+ ASSERT(isNumber(pan), 'Sound pan must be a number');
275
+
276
+ /** @property {Sound} - The sound object */
277
+ this.sound = sound;
278
+ /** @property {number} - How much to scale volume by */
279
+ this.volume = volume;
280
+ /** @property {number} - The playback rate to use */
281
+ this.rate = rate;
282
+ /** @property {number} - How much to apply stereo panning */
283
+ this.pan = pan;
284
+ /** @property {boolean} - Should the sound loop */
285
+ this.loop = loop;
286
+ /** @property {number} - Timestamp for audio context when paused */
287
+ this.pausedTime = 0;
288
+ /** @property {number} - Timestamp for audio context when started */
289
+ this.startTime = undefined;
290
+ /** @property {GainNode} - Gain node for the sound */
291
+ this.gainNode = undefined;
292
+ /** @property {AudioBufferSourceNode} - Source node of the audio */
293
+ this.source = undefined;
294
+ // setup end callback and start sound
295
+ this.onendedCallback = (source)=>
296
+ {
297
+ if (source === this.source)
298
+ this.source = undefined;
299
+ };
300
+ if (!paused)
301
+ this.start();
302
+ }
303
+
304
+ /** Start playing the sound instance from the offset time
305
+ * @param {number} [offset] - Offset in seconds to start playback from
306
+ */
307
+ start(offset=0)
308
+ {
309
+ ASSERT(offset >= 0, 'Sound start offset must be positive or zero');
310
+ if (this.isPlaying())
311
+ this.stop();
312
+ this.gainNode = audioContext.createGain();
313
+ this.source = playSamples(this.sound.sampleChannels, this.volume, this.rate, this.pan, this.loop, this.sound.sampleRate, this.gainNode, offset, this.onendedCallback);
314
+ this.startTime = audioContext.currentTime - offset;
315
+ this.pausedTime = undefined;
316
+ }
317
+
318
+ /** Set the volume of this sound instance
319
+ * @param {number} volume */
320
+ setVolume(volume)
321
+ {
322
+ ASSERT(volume >= 0, 'Sound volume must be positive or zero');
323
+ this.volume = volume;
324
+ if (this.gainNode)
325
+ this.gainNode.gain.value = volume;
326
+ }
327
+
328
+ /** Stop this sound instance and reset position to the start */
329
+ stop(fadeTime=0)
330
+ {
331
+ ASSERT(fadeTime >= 0, 'Sound fade time must be positive or zero');
332
+ if (this.isPlaying())
333
+ {
334
+ if (fadeTime)
335
+ {
336
+ // ramp off gain
337
+ const startFade = audioContext.currentTime;
338
+ const endFade = startFade + fadeTime;
339
+ this.gainNode.gain.linearRampToValueAtTime(1, startFade);
340
+ this.gainNode.gain.linearRampToValueAtTime(0, endFade);
341
+ this.source.stop(endFade);
342
+ }
343
+ else
344
+ this.source.stop();
345
+ }
346
+ this.pausedTime = 0;
347
+ this.source = undefined;
348
+ this.startTime = undefined;
349
+ }
350
+
351
+ /** Pause this sound instance */
352
+ pause()
353
+ {
354
+ if (this.isPaused())
355
+ return;
356
+
357
+ // save current time and stop sound
358
+ this.pausedTime = this.getCurrentTime();
359
+ this.source.stop();
360
+ this.source = undefined;
361
+ this.startTime = undefined;
362
+ }
226
363
 
227
- return new SoundWave(filename,0,0,0, s=>s.play(undefined, volume, 1, 1, loop));
364
+ /** Unpauses this sound instance */
365
+ resume()
366
+ {
367
+ if (!this.isPaused())
368
+ return;
369
+
370
+ // restart sound from paused time
371
+ this.start(this.pausedTime);
372
+ }
373
+
374
+ /** Check if this instance is currently playing
375
+ * @return {boolean} - True if playing
376
+ */
377
+ isPlaying() { return !!this.source; }
378
+
379
+ /** Check if this instance is paused and was not stopped
380
+ * @return {boolean} - True if paused
381
+ */
382
+ isPaused() { return !this.isPlaying(); }
383
+
384
+ /** Get the current playback time in seconds
385
+ * @return {number} - Current playback time
386
+ */
387
+ getCurrentTime()
388
+ {
389
+ const deltaTime = mod(audioContext.currentTime - this.startTime,
390
+ this.getDuration());
391
+ return this.isPlaying() ? deltaTime : this.pausedTime;
392
+ }
393
+
394
+ /** Get the total duration of this sound
395
+ * @return {number} - Total duration in seconds
396
+ */
397
+ getDuration() { return this.sound.getDuration() / this.rate; }
398
+
399
+ /** Get source of this sound instance
400
+ * @return {AudioBufferSourceNode}
401
+ */
402
+ getSource() { return this.source; }
228
403
  }
229
404
 
230
405
  ///////////////////////////////////////////////////////////////////////////////
@@ -278,9 +453,11 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
278
453
  * @param {boolean} [loop] - True if the sound should loop when it reaches the end
279
454
  * @param {number} [sampleRate=44100] - Sample rate for the sound
280
455
  * @param {GainNode} [gainNode] - Optional gain node for volume control while playing
456
+ * @param {number} [offset] - Offset in seconds to start playback from
457
+ * @param {Function} [onended] - Callback for when the sound ends
281
458
  * @return {AudioBufferSourceNode} - The audio node of the sound played
282
459
  * @memberof Audio */
283
- function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=zzfxR, gainNode)
460
+ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=audioDefaultSampleRate, gainNode, offset=0, onended)
284
461
  {
285
462
  if (!soundEnable || headlessMode) return;
286
463
 
@@ -305,16 +482,20 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
305
482
  const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
306
483
  source.connect(pannerNode).connect(gainNode);
307
484
 
308
- // play the sound
309
- if (audioContext.state != 'running')
485
+ // callback when the sound ends
486
+ if (onended)
487
+ source.addEventListener('ended', ()=> onended(source));
488
+
489
+ if (!audioIsRunning())
310
490
  {
311
- // fix stalled audio and play
312
- audioContext.resume().then(()=>source.start());
491
+ // fix stalled audio, this sound won't be able to play
492
+ audioContext.resume();
493
+ return;
313
494
  }
314
- else
315
- source.start();
316
495
 
317
- // return sound
496
+ // play and return sound
497
+ const startOffset = offset * rate;
498
+ source.start(0, startOffset);
318
499
  return source;
319
500
  }
320
501
 
@@ -322,18 +503,13 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
322
503
  // ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.2 by Frank Force
323
504
 
324
505
  /** Generate and play a ZzFX sound
325
- *
506
+ *
326
507
  * <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
327
508
  * @param {Array} zzfxSound - Array of ZzFX parameters, ex. [.5,.5]
328
509
  * @return {AudioBufferSourceNode} - The audio node of the sound played
329
510
  * @memberof Audio */
330
511
  function zzfx(...zzfxSound) { return playSamples([zzfxG(...zzfxSound)]); }
331
512
 
332
- /** Sample rate used for all ZzFX sounds
333
- * @default 44100
334
- * @memberof Audio */
335
- const zzfxR = 44100;
336
-
337
513
  /** Generate samples for a ZzFX sound
338
514
  * @param {number} [volume] - Volume scale (percent)
339
515
  * @param {number} [randomness] - How much to randomize frequency (percent Hz)
@@ -357,11 +533,10 @@ const zzfxR = 44100;
357
533
  * @param {number} [tremolo] - Trembling effect, rate controlled by repeat time (percent)
358
534
  * @param {number} [filter] - Filter cutoff frequency, positive for HPF, negative for LPF (Hz)
359
535
  * @return {Array} - Array of audio samples
360
- * @memberof Audio
361
- */
536
+ * @memberof Audio */
362
537
  function zzfxG
363
538
  (
364
- volume = 1,
539
+ volume = 1,
365
540
  randomness = .05,
366
541
  frequency = 220,
367
542
  attack = 0,
@@ -369,11 +544,11 @@ function zzfxG
369
544
  release = .1,
370
545
  shape = 0,
371
546
  shapeCurve = 1,
372
- slide = 0,
373
- deltaSlide = 0,
374
- pitchJump = 0,
375
- pitchJumpTime = 0,
376
- repeatTime = 0,
547
+ slide = 0,
548
+ deltaSlide = 0,
549
+ pitchJump = 0,
550
+ pitchJumpTime = 0,
551
+ repeatTime = 0,
377
552
  noise = 0,
378
553
  modulation = 0,
379
554
  bitCrush = 0,
@@ -385,19 +560,19 @@ function zzfxG
385
560
  )
386
561
  {
387
562
  // init parameters
388
- let sampleRate = zzfxR,
389
- PI2 = PI*2,
563
+ let sampleRate = audioDefaultSampleRate,
564
+ PI2 = PI*2,
390
565
  startSlide = slide *= 500 * PI2 / sampleRate / sampleRate,
391
- startFrequency = frequency *=
566
+ startFrequency = frequency *=
392
567
  (1 + rand(randomness,-randomness)) * PI2 / sampleRate,
393
- modOffset = 0, // modulation offset
568
+ modOffset = 0, // modulation offset
394
569
  repeat = 0, // repeat offset
395
570
  crush = 0, // bit crush offset
396
571
  jump = 1, // pitch jump timer
397
572
  length, // sample length
398
573
  b = [], // sample buffer
399
574
  t = 0, // sample time
400
- i = 0, // sample index
575
+ i = 0, // sample index
401
576
  s = 0, // sample value
402
577
  f, // wave frequency
403
578
 
@@ -405,7 +580,7 @@ function zzfxG
405
580
  quality = 2, w = PI2 * abs(filter) * 2 / sampleRate,
406
581
  cos = Math.cos(w), alpha = Math.sin(w) / 2 / quality,
407
582
  a0 = 1 + alpha, a1 = -2*cos / a0, a2 = (1 - alpha) / a0,
408
- b0 = (1 + sign(filter) * cos) / 2 / a0,
583
+ b0 = (1 + sign(filter) * cos) / 2 / a0,
409
584
  b1 = -(sign(filter) + cos) / a0, b2 = b0,
410
585
  x2 = 0, x1 = 0, y2 = 0, y1 = 0;
411
586
 
@@ -451,7 +626,7 @@ function zzfxG
451
626
  0); // post release
452
627
 
453
628
  s = delay ? s/2 + (delay > i ? 0 : // delay
454
- (i<length-delay? 1 : (length-i)/delay) * // release delay
629
+ (i<length-delay? 1 : (length-i)/delay) * // release delay
455
630
  b[i-delay|0]/2/volume) : s; // sample delay
456
631
 
457
632
  if (filter) // apply filter
@@ -463,14 +638,14 @@ function zzfxG
463
638
  t += f + f*noise*Math.sin(i**5); // noise
464
639
 
465
640
  if (jump && ++jump > pitchJumpTime) // pitch jump
466
- {
641
+ {
467
642
  frequency += pitchJump; // apply pitch jump
468
643
  startFrequency += pitchJump; // also apply to start
469
644
  jump = 0; // stop pitch jump time
470
- }
645
+ }
471
646
 
472
647
  if (repeatTime && !(++repeat % repeatTime)) // repeat
473
- {
648
+ {
474
649
  frequency = startFrequency; // reset frequency
475
650
  slide = startSlide; // reset slide
476
651
  jump ||= 1; // reset pitch jump time
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
3
+ /**
4
4
  * LittleJS Build System
5
5
  * - Combine input files
6
6
  * - Run custom build steps
@@ -76,8 +76,8 @@ Build
76
76
  `${BUILD_FOLDER}/${ENGINE_NAME}.js`,
77
77
  [
78
78
  `${SOURCE_FOLDER}/engine.js`,
79
- `${SOURCE_FOLDER}/engineDebug.js`,
80
- ...engineSourceFiles,
79
+ `${SOURCE_FOLDER}/engineDebug.js`,
80
+ ...engineSourceFiles,
81
81
  ...enginePluginFiles
82
82
  ],
83
83
  [], true
@@ -89,8 +89,8 @@ Build
89
89
  `${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
90
90
  [
91
91
  `${SOURCE_FOLDER}/engine.js`,
92
- `${SOURCE_FOLDER}/engineRelease.js`,
93
- ...engineSourceFiles,
92
+ `${SOURCE_FOLDER}/engineRelease.js`,
93
+ ...engineSourceFiles,
94
94
  ...enginePluginFiles
95
95
  ],
96
96
  [], true
@@ -109,8 +109,8 @@ Build
109
109
  'Build Engine -- ESM',
110
110
  `${BUILD_FOLDER}/${ENGINE_NAME}.esm.js`,
111
111
  [
112
- `${BUILD_FOLDER}/${ENGINE_NAME}.js`,
113
- `${SOURCE_FOLDER}/engineExport.js`,
112
+ `${BUILD_FOLDER}/${ENGINE_NAME}.js`,
113
+ `${SOURCE_FOLDER}/engineExport.js`,
114
114
  `${PLUGIN_FOLDER}/pluginExport.js`
115
115
  ],
116
116
  [typeScriptBuildStep]
@@ -121,8 +121,8 @@ Build
121
121
  'Build Engine -- ESM minified release',
122
122
  `${BUILD_FOLDER}/${ENGINE_NAME}.esm.min.js`,
123
123
  [
124
- `${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
125
- `${SOURCE_FOLDER}/engineExport.js`,
124
+ `${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
125
+ `${SOURCE_FOLDER}/engineExport.js`,
126
126
  `${PLUGIN_FOLDER}/pluginExport.js`
127
127
  ],
128
128
  [uglifyBuildStep, addLicenseStep]