Skip to content

Commit cc7c876

Browse files
authored
updatre!!!!
1 parent 3d4bef7 commit cc7c876

1 file changed

Lines changed: 222 additions & 14 deletions

File tree

static/extensions/Gen1x/beat_sync.js

Lines changed: 222 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33

44
const isPenguinMod = Scratch.extensions.isPenguinMod;
55

6+
function scheduleFrame(cb) {
7+
if (document.hidden) {
8+
return setTimeout(cb, 16);
9+
} else {
10+
return requestAnimationFrame(cb);
11+
}
12+
}
13+
614
class BeatSync {
715
constructor() {
816
this.bpm = 120;
@@ -18,8 +26,28 @@
1826
this.pausedElapsed = 0;
1927

2028
this.vmEventBound = false;
29+
this._bgTickHandle = null;
2130

2231
this.loadAutoStart();
32+
33+
this._startBgTick();
34+
document.addEventListener('visibilitychange', () => {
35+
if (document.hidden) {
36+
this._startBgTick();
37+
}
38+
});
39+
}
40+
41+
_startBgTick() {
42+
if (this._bgTickHandle !== null) return;
43+
const loop = () => {
44+
this._bgTickHandle = null;
45+
this.tick();
46+
if (document.hidden) {
47+
this._bgTickHandle = setTimeout(loop, 16);
48+
}
49+
};
50+
this._bgTickHandle = setTimeout(loop, 16);
2351
}
2452

2553
loadAutoStart() {
@@ -30,8 +58,7 @@
3058
if (storage && typeof storage.autoStart === 'boolean') {
3159
this.autoStart = storage.autoStart;
3260
}
33-
} catch (e) {
34-
}
61+
} catch (e) {}
3562
}
3663
}
3764

@@ -46,16 +73,13 @@
4673
vm.runtime.extensionStorage.beatSync = {};
4774
}
4875
vm.runtime.extensionStorage.beatSync.autoStart = this.autoStart;
49-
} catch (e) {
50-
}
76+
} catch (e) {}
5177
}
5278
}
5379

5480
serialize() {
5581
if (isPenguinMod) {
56-
return {
57-
autoStart: this.autoStart
58-
};
82+
return { autoStart: this.autoStart };
5983
}
6084
return {};
6185
}
@@ -81,10 +105,9 @@
81105
vm?.runtime?.scratch?.audioEngine?.audioContext ||
82106
vm?.audioEngine?.audioContext ||
83107
null;
84-
} catch (e) {
85-
}
108+
} catch (e) {}
86109

87-
this.audioCtx = scratchCtx || new(window.AudioContext || window.webkitAudioContext)();
110+
this.audioCtx = scratchCtx || new (window.AudioContext || window.webkitAudioContext)();
88111
if (this.audioCtx.state === 'suspended') this.audioCtx.resume();
89112
return this.audioCtx;
90113
}
@@ -113,7 +136,7 @@
113136
this.vmEventBound = true;
114137

115138
const vm = Scratch.vm;
116-
139+
117140
vm.on('BEFORE_EXECUTE', () => {
118141
this.tick();
119142
});
@@ -143,7 +166,7 @@
143166
opcode: 'toggleAutoStart',
144167
blockType: Scratch.BlockType.BUTTON,
145168
text: this.autoStart ? 'Auto start toggle: ON' : 'Auto start toggle: OFF'
146-
},
169+
},
147170
{
148171
opcode: 'setBPM',
149172
blockType: Scratch.BlockType.COMMAND,
@@ -187,6 +210,28 @@
187210
blockType: Scratch.BlockType.COMMAND,
188211
text: 'wait until next beat'
189212
},
213+
{
214+
opcode: 'waitUntilBeat',
215+
blockType: Scratch.BlockType.COMMAND,
216+
text: 'wait until beat [BEAT]',
217+
arguments: {
218+
BEAT: {
219+
type: Scratch.ArgumentType.NUMBER,
220+
defaultValue: 16
221+
}
222+
}
223+
},
224+
{
225+
opcode: 'waitUntilBeatPosition',
226+
blockType: Scratch.BlockType.COMMAND,
227+
text: 'wait until beat position reaches [STEP]',
228+
arguments: {
229+
STEP: {
230+
type: Scratch.ArgumentType.NUMBER,
231+
defaultValue: 0.5
232+
}
233+
}
234+
},
190235
'---',
191236
{
192237
opcode: 'getBeatValue',
@@ -203,12 +248,46 @@
203248
blockType: Scratch.BlockType.REPORTER,
204249
text: 'current measure number'
205250
},
251+
{
252+
opcode: 'getBeatInMeasure',
253+
blockType: Scratch.BlockType.REPORTER,
254+
text: 'beat within measure'
255+
},
206256
{
207257
opcode: 'getTimeBetweenBeats',
208258
blockType: Scratch.BlockType.REPORTER,
209259
text: 'time between beats (s)'
210260
},
261+
{
262+
opcode: 'getElapsedTime',
263+
blockType: Scratch.BlockType.REPORTER,
264+
text: 'elapsed time (s)'
265+
},
211266
'---',
267+
{
268+
opcode: 'onBeat',
269+
blockType: Scratch.BlockType.HAT,
270+
text: 'on beat [BEAT]',
271+
isEdgeActivated: true,
272+
arguments: {
273+
BEAT: {
274+
type: Scratch.ArgumentType.NUMBER,
275+
defaultValue: 8
276+
}
277+
}
278+
},
279+
{
280+
opcode: 'everyNBeats',
281+
blockType: Scratch.BlockType.HAT,
282+
text: 'every [N] beats',
283+
isEdgeActivated: true,
284+
arguments: {
285+
N: {
286+
type: Scratch.ArgumentType.NUMBER,
287+
defaultValue: 2
288+
}
289+
}
290+
},
212291
{
213292
opcode: 'whenBeatReachesStep',
214293
blockType: Scratch.BlockType.HAT,
@@ -221,6 +300,22 @@
221300
}
222301
}
223302
},
303+
{
304+
opcode: 'whenBeatBetween',
305+
blockType: Scratch.BlockType.HAT,
306+
text: 'when beat position between [A] and [B]',
307+
isEdgeActivated: true,
308+
arguments: {
309+
A: {
310+
type: Scratch.ArgumentType.NUMBER,
311+
defaultValue: 0
312+
},
313+
B: {
314+
type: Scratch.ArgumentType.NUMBER,
315+
defaultValue: 0.25
316+
}
317+
}
318+
},
224319
{
225320
opcode: 'whenFullBeat',
226321
blockType: Scratch.BlockType.HAT,
@@ -232,6 +327,30 @@
232327
blockType: Scratch.BlockType.HAT,
233328
text: 'when new measure starts',
234329
isEdgeActivated: true
330+
},
331+
{
332+
opcode: 'whenNthBeatInMeasure',
333+
blockType: Scratch.BlockType.HAT,
334+
text: 'when beat [N] in measure starts',
335+
isEdgeActivated: true,
336+
arguments: {
337+
N: {
338+
type: Scratch.ArgumentType.NUMBER,
339+
defaultValue: 3
340+
}
341+
}
342+
},
343+
{
344+
opcode: 'whenBeatStarted',
345+
blockType: Scratch.BlockType.HAT,
346+
text: 'when syncing starts',
347+
isEdgeActivated: true
348+
},
349+
{
350+
opcode: 'whenBeatStopped',
351+
blockType: Scratch.BlockType.HAT,
352+
text: 'when syncing stops',
353+
isEdgeActivated: true
235354
}
236355
],
237356
menus: {}
@@ -258,6 +377,7 @@
258377

259378
startBeat() {
260379
if (!this.isRunning) {
380+
this._wasRunning = false;
261381
this.isRunning = true;
262382
const ctx = this.getAudioCtx();
263383
this.startAudioTime = ctx.currentTime;
@@ -268,6 +388,7 @@
268388
if (this.isRunning) {
269389
this.pausedElapsed = this.getElapsedSeconds();
270390
this.isRunning = false;
391+
this._wasStopped = true;
271392
}
272393
}
273394

@@ -290,10 +411,45 @@
290411
if (Math.floor(this.totalBeats) > startBeat) {
291412
resolve();
292413
} else {
293-
requestAnimationFrame(poll);
414+
scheduleFrame(poll);
294415
}
295416
};
296-
requestAnimationFrame(poll);
417+
scheduleFrame(poll);
418+
});
419+
}
420+
421+
waitUntilBeat(args) {
422+
const target = Number(args.BEAT);
423+
return new Promise(resolve => {
424+
const poll = () => {
425+
this.updateTime();
426+
if (this.totalBeats >= target) {
427+
resolve();
428+
} else {
429+
scheduleFrame(poll);
430+
}
431+
};
432+
scheduleFrame(poll);
433+
});
434+
}
435+
436+
waitUntilBeatPosition(args) {
437+
const target = Number(args.STEP) % 1;
438+
this.updateTime();
439+
const startBeat = Math.floor(this.totalBeats);
440+
return new Promise(resolve => {
441+
const poll = () => {
442+
this.updateTime();
443+
const currentBeat = Math.floor(this.totalBeats);
444+
if (currentBeat > startBeat && this.beatPosition >= target) {
445+
resolve();
446+
} else if (currentBeat === startBeat && this.beatPosition >= target && this.beatPosition < target + 0.5) {
447+
resolve();
448+
} else {
449+
scheduleFrame(poll);
450+
}
451+
};
452+
scheduleFrame(poll);
297453
});
298454
}
299455

@@ -312,16 +468,52 @@
312468
return Math.floor(this.totalBeats / this.beatsPerMeasure);
313469
}
314470

471+
getBeatInMeasure() {
472+
this.updateTime();
473+
return Math.floor(this.totalBeats % this.beatsPerMeasure) + 1;
474+
}
475+
315476
getTimeBetweenBeats() {
316477
return 60 / this.bpm;
317478
}
318479

480+
getElapsedTime() {
481+
this.updateTime();
482+
return Math.round(this.getElapsedSeconds() * 1000) / 1000;
483+
}
484+
485+
onBeat(args) {
486+
if (!this.isRunning) return false;
487+
this.updateTime();
488+
const target = Number(args.BEAT);
489+
return this.totalBeats >= target && this.totalBeats < target + 0.5;
490+
}
491+
492+
everyNBeats(args) {
493+
if (!this.isRunning) return false;
494+
this.updateTime();
495+
const n = Math.max(1, Number(args.N));
496+
return (this.totalBeats % n) < 0.5;
497+
}
498+
319499
whenBeatReachesStep(args) {
320500
if (!this.isRunning) return false;
321501
this.updateTime();
322502
return this.beatPosition >= (Number(args.STEP) % 1);
323503
}
324504

505+
whenBeatBetween(args) {
506+
if (!this.isRunning) return false;
507+
this.updateTime();
508+
const a = Number(args.A) % 1;
509+
const b = Number(args.B) % 1;
510+
if (a <= b) {
511+
return this.beatPosition >= a && this.beatPosition < b;
512+
} else {
513+
return this.beatPosition >= a || this.beatPosition < b;
514+
}
515+
}
516+
325517
whenFullBeat() {
326518
if (!this.isRunning) return false;
327519
this.updateTime();
@@ -335,6 +527,22 @@
335527
return beatInMeasure < 0.5;
336528
}
337529

530+
whenNthBeatInMeasure(args) {
531+
if (!this.isRunning) return false;
532+
this.updateTime();
533+
const n = Math.max(1, Number(args.N));
534+
const beatInMeasure = this.totalBeats % this.beatsPerMeasure;
535+
return beatInMeasure >= (n - 1) && beatInMeasure < (n - 1) + 0.5;
536+
}
537+
538+
whenBeatStarted() {
539+
return this.isRunning;
540+
}
541+
542+
whenBeatStopped() {
543+
return !this.isRunning;
544+
}
545+
338546
onGreenFlag() {
339547
this.resetBeat();
340548
if (this.autoStart) {

0 commit comments

Comments
 (0)