Skip to content

Commit

Permalink
feat(loops): sync loops with master loop
Browse files Browse the repository at this point in the history
  • Loading branch information
domi7777 committed Nov 7, 2024
1 parent 2bd80b6 commit b34e1bb
Showing 1 changed file with 87 additions and 17 deletions.
104 changes: 87 additions & 17 deletions src/Loop.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Phaser from 'phaser';

type LoopEvent = {
callback: Function | null,
callback: Function | 'endOfLoop',
time: number,
}

Expand All @@ -12,11 +14,21 @@ export class Loop {

private state: LoopState = 'readyToRecord';
private events: LoopEvent[] = [];
private startRecordingTime = 0;
private eventEmitter = new Phaser.Events.EventEmitter();
private startRecordingTime?: number;
private startPlayingTime = 0;
private currentLoopIndex = 0;
private loopTimeout: number | null = null;
private static masterLoop: Loop | null = null;

getStartPlayingTime() {
return this.startPlayingTime;
}

getLoopLength() {
return this.events.find(({callback}) => callback === 'endOfLoop')?.time;
}

handleClick() {
this.nextState();
switch (this.state) {
Expand All @@ -34,11 +46,15 @@ export class Loop {

addLoopEvent(callback: Function) {
if (this.isRecording()) {
if (!this.startRecordingTime) {
this.startRecordingTime = Loop.masterLoop!.getStartPlayingTime();
}
const time = Date.now() - this.startRecordingTime;
this.events.push({
callback,
time: Date.now() - this.startRecordingTime
time
});
this.log(`Recording ${callback} at time ${Date.now() - this.startRecordingTime}ms`);
this.log(`Recording ${callback} at time ${time}ms`);
}
}

Expand All @@ -59,16 +75,21 @@ export class Loop {
}

destroy() {
this.eventEmitter.removeAllListeners();
if (this.isPlaying()) {
this.stopPlaying();
}
this.events = [];
if (this === Loop.masterLoop) {
if (this.isMasterLoop()) {
Loop.masterLoop = null;
}
this.log('Loop destroyed');
}

addEventListener(event: 'endOfLoop', callback: Function) {
this.eventEmitter.once(event, callback);
}

private nextState() {
switch (this.state) {
case 'readyToRecord':
Expand All @@ -87,21 +108,53 @@ export class Loop {
}

private startRecording() {
this.startRecordingTime = Date.now();
this.events = [];
this.log('Recording started');
if (!Loop.masterLoop) {
Loop.masterLoop = this;
this.startRecordingTime = Date.now();
} else {
// master loop starts at first event
}
}

private stopRecording(): LoopState {
if (this.events.length === 0) {
this.log('No events recorded');
return 'readyToRecord';
}
this.events.push({
callback: null,
time: Date.now() - this.startRecordingTime
});
this.log('Recording stopped, start playing');
if (!this.startRecordingTime) {
throw new Error('startRecordingTime is not set');
}
const endTime = Date.now() - this.startRecordingTime;

if (this.isMasterLoop()) {
this.events.push({
callback: 'endOfLoop',
time: endTime
});
const firstEventTime = this.events[0].time;
this.events = this.events.map(({time, callback}) => ({
time: time - firstEventTime,
callback
}));
} else {
console.log(this.events);
const masterLoopLength = Loop.masterLoop?.getLoopLength();
if (!masterLoopLength){
throw new Error('masterLoopLength is not set');
}
// check how many times the master loop fits in this loop so that it is always in sync
const loopCount = Math.floor(endTime / masterLoopLength);
const maxTime = loopCount * masterLoopLength;
this.events = this.events.filter(({time}) => time <= maxTime);
this.events.push({
callback: 'endOfLoop',
time: maxTime
});
}

this.log(`Recording stopped at ${endTime} with ${this.events.length} events, start playing`);
return 'playing';
}

Expand All @@ -110,20 +163,32 @@ export class Loop {
if (this.currentLoopIndex >= this.events.length) {
this.currentLoopIndex = 0;
}
if (this.currentLoopIndex === 0) {
this.log('Loop play (re)-started', '#0F0');
this.startPlayingTime = Date.now();
}
const {callback, time} = this.events[this.currentLoopIndex];
const previousTime = this.currentLoopIndex === 0 ? 0 : this.events[this.currentLoopIndex - 1].time;
this.loopTimeout = setTimeout(() => {
this.log(`Playing ${callback} after ${time}ms`);
if (callback) {
this.log(`Playing event ${callback} after ${time}ms`);
if (callback !== 'endOfLoop') {
callback();
} else {
this.eventEmitter.emit('endOfLoop');
}

this.currentLoopIndex++;
playLoop();
}, time - previousTime);
}
this.log('Loop play starting');
playLoop();
if (!this.isMasterLoop() && Loop.masterLoop?.isPlaying()) {
// wait the end of the master loop to start in sync
Loop.masterLoop.addEventListener('endOfLoop', () => {
playLoop();
});
} else {
playLoop();
}
}

private stopPlaying() {
Expand All @@ -132,8 +197,13 @@ export class Loop {
this.log('Loop stopped');
}

private log(msg: string, args?: unknown[]) {
console.log(`Loop ${(this.trackIndex + 1)}: ${msg}`, args);
private isMasterLoop() {
return this === Loop.masterLoop;
}

private log(msg: string, color: string = '#FFF') {
const message = `%cLoop ${(this.trackIndex + 1)}: ${msg}`;
console.log(message, `color: ${color}`);
}

}

0 comments on commit b34e1bb

Please sign in to comment.