partylights/boilerbloat/src/main/backend.ts
2021-11-13 00:22:24 +01:00

152 lines
3.6 KiB
TypeScript

import { ipcMain } from 'electron';
import { blackout } from '../patterns/blackout';
import { Pattern, PatternOutput, Time } from '../patterns/proto';
import { TestPattern } from '../patterns/test';
import rust, { BeatTrackerHandle, MovingHeadState, OutputHandle, TrackerConfig } from 'rust_native_module';
import { ChaserPattern } from '../patterns/chaser';
export type AppState = {
patterns: { [key: string]: PatternOutput },
selectedPattern: string | null,
beatProgress: number | null,
graphData: {
bassFiltered: Array<number>,
autoCorrelated: Array<number>,
} | null,
trackerConfig: TrackerConfig,
};
class Backend {
beatTracker: BeatTrackerHandle;
dmxOutput: OutputHandle;
patterns: Map<string, Pattern>;
// patternOutputs: Map<string, PatternOutput>;
// selectedPattern: string;
state: AppState;
constructor() {
// beat tracking
let beatTracker = rust.getBeatTracker();
if (beatTracker.type !== 'success') {
throw new Error("could not initialize beat tracking");
}
this.beatTracker = beatTracker.value;
ipcMain.on('beat-tracking', async (_, arg) => {
if (arg === 'tap') {
this.beatTracker.tap();
}
});
// output
let dmxOutput = rust.openOutput("/dev/ttyUSB0");
if (dmxOutput.type !== 'success') {
throw new Error("could not open DMX output");
}
this.dmxOutput = dmxOutput.value;
// patterns
this.patterns = new Map();
this.patterns.set("test", new TestPattern());
this.patterns.set("chaser", new ChaserPattern());
this.state = {
patterns: {},
selectedPattern: null,
beatProgress: null,
graphData: null,
trackerConfig: {
mode: "auto",
acThreshold: 1000,
zeroCrossingBeatDelay: 0,
}
}
ipcMain.on('pattern-select', async (_, arg) => {
this.state.selectedPattern = arg;
});
ipcMain.on('update-delay', async (_, delay) => {
this.state.trackerConfig.zeroCrossingBeatDelay = Math.floor(delay);
this.beatTracker.setConfig(this.state.trackerConfig);
});
ipcMain.on('manual-mode', async (_, is_manual) => {
this.state.trackerConfig.mode = is_manual ? "manual" : "auto";
this.beatTracker.setConfig(this.state.trackerConfig);
});
let time: Time = {
absolute: 0,
beatRelative: this.state.beatProgress,
}
for (let [patternId, pattern] of this.patterns.entries()) {
let patternOutput = pattern.render(time);
this.state.patterns[patternId] = patternOutput;
}
ipcMain.handle('poll', async (_) => {
return this.getState();
});
}
getState(): AppState {
return this.state;
}
update() {
let beatProgress = this.beatTracker.getProgress();
if (beatProgress.type == 'some') {
this.state.beatProgress = beatProgress.value;
} else {
this.state.beatProgress = null;
}
this.state.graphData = this.beatTracker.getGraphPoints();
let date = new Date();
let time: Time = {
absolute: date.getTime() / 1000,
beatRelative: this.state.beatProgress,
}
// render all patterns and write selected pattern to DMX
let output: Array<MovingHeadState> = blackout;
for (let [patternId, pattern] of this.patterns.entries()) {
let patternOutput = pattern.render(time);
this.state.patterns[patternId] = patternOutput;
if (patternId === this.state.selectedPattern && patternOutput) {
output = patternOutput;
}
}
this.dmxOutput.set(output);
}
close() {
this.dmxOutput.close();
}
}
export default Backend;