152 lines
3.6 KiB
TypeScript
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;
|