Begin breaking up the API file
This commit is contained in:
652
src/API/API.ts
Normal file
652
src/API/API.ts
Normal file
@ -0,0 +1,652 @@
|
||||
import * as Transport from './Transport';
|
||||
import * as Mouse from './Mouse';
|
||||
import * as Theme from './Theme';
|
||||
import * as Canvas from './Canvas';
|
||||
import * as Cache from './Cache';
|
||||
import * as Script from './Script';
|
||||
import * as Drunk from './Drunk';
|
||||
import * as Warp from './Warp';
|
||||
import * as Mathematics from './Math';
|
||||
import * as Ziffers from './Ziffers';
|
||||
import * as Filters from './Filters';
|
||||
import * as LFO from './LFO';
|
||||
import * as Probability from './Probabilities';
|
||||
import * as OSC from './OSC';
|
||||
import * as Randomness from './Randomness';
|
||||
import * as Counter from './Counter';
|
||||
import * as Sound from './Sound';
|
||||
import * as Console from './Console';
|
||||
import { type SoundEvent } from '../Classes/SoundEvent';
|
||||
import { type SkipEvent } from '../Classes/SkipEvent';
|
||||
import { OscilloscopeConfig } from "../DOM/Visuals/Oscilloscope";
|
||||
import { Player } from "../Classes/ZPlayer";
|
||||
import { InputOptions } from "../Classes/ZPlayer";
|
||||
import { type ShapeObject } from "../DOM/Visuals/CanvasVisuals";
|
||||
import { nearScales } from "zifferjs";
|
||||
import { MidiConnection } from "../IO/MidiConnection";
|
||||
import { evaluateOnce } from "../Evaluator";
|
||||
import { DrunkWalk } from "../Utils/Drunk";
|
||||
import { Editor } from "../main";
|
||||
import { LRUCache } from "lru-cache";
|
||||
import {
|
||||
loadUniverse,
|
||||
openUniverseModal,
|
||||
} from "../Editor/FileManagement";
|
||||
import {
|
||||
samples,
|
||||
initAudioOnFirstClick,
|
||||
registerSynthSounds,
|
||||
registerZZFXSounds,
|
||||
soundMap,
|
||||
// @ts-ignore
|
||||
} from "superdough";
|
||||
import { Speaker } from "../Extensions/StringExtensions";
|
||||
import { getScaleNotes } from "zifferjs";
|
||||
import { AbstractEvent, EventOperation } from "../Classes/AbstractEvents";
|
||||
import drums from "../tidal-drum-machines.json";
|
||||
|
||||
export async function loadSamples() {
|
||||
return Promise.all([
|
||||
initAudioOnFirstClick(),
|
||||
samples("github:tidalcycles/Dirt-Samples/master", undefined, {
|
||||
tag: "Tidal",
|
||||
}).then(() => registerSynthSounds()),
|
||||
registerZZFXSounds(),
|
||||
samples(drums, "github:ritchse/tidal-drum-machines/main/machines/", {
|
||||
tag: "Machines",
|
||||
}),
|
||||
samples("github:Bubobubobubobubo/Dough-Fox/main", undefined, {
|
||||
tag: "FoxDot",
|
||||
}),
|
||||
samples("github:Bubobubobubobubo/Dough-Samples/main", undefined, {
|
||||
tag: "Pack",
|
||||
}),
|
||||
samples("github:Bubobubobubobubo/Dough-Amiga/main", undefined, {
|
||||
tag: "Amiga",
|
||||
}),
|
||||
samples("github:Bubobubobubobubo/Dough-Juj/main", undefined, {
|
||||
tag: "Juliette",
|
||||
}),
|
||||
samples("github:Bubobubobubobubo/Dough-Amen/main", undefined, {
|
||||
tag: "Amen",
|
||||
}),
|
||||
samples("github:Bubobubobubobubo/Dough-Waveforms/main", undefined, {
|
||||
tag: "Waveforms",
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export class UserAPI {
|
||||
/**
|
||||
* The UserAPI class is the interface between the user's code and the backend. It provides
|
||||
* access to the AudioContext, to the MIDI Interface, to internal variables, mouse position,
|
||||
* useful functions, etc... This class is exposed to the user's action and any function
|
||||
* destined to the user should be placed here.
|
||||
*/
|
||||
|
||||
public codeExamples: { [key: string]: string } = {};
|
||||
private counters: { [key: string]: any } = {};
|
||||
private _drunk: DrunkWalk = new DrunkWalk(-100, 100, false);
|
||||
public randomGen = Math.random;
|
||||
public currentSeed: string | undefined = undefined;
|
||||
public localSeeds = new Map<string, Function>();
|
||||
public patternCache = new LRUCache({ max: 10000, ttl: 10000 * 60 * 5 });
|
||||
public invalidPatterns: { [key: string]: boolean } = {};
|
||||
public cueTimes: { [key: string]: number } = {};
|
||||
private errorTimeoutID: number = 0;
|
||||
private printTimeoutID: number = 0;
|
||||
public MidiConnection: MidiConnection;
|
||||
public scale_aid: string | number | undefined = undefined;
|
||||
public hydra: any;
|
||||
public onceEvaluator: boolean = true;
|
||||
public forceEvaluator: boolean = false;
|
||||
|
||||
load: samples;
|
||||
public global: { [key: string]: any };
|
||||
time: () => number;
|
||||
play: () => void;
|
||||
pause: () => void;
|
||||
stop: () => void;
|
||||
silence: () => void;
|
||||
onMouseMove: (e: MouseEvent) => void;
|
||||
mouseX: () => number;
|
||||
mouseY: () => number;
|
||||
noteX: () => number;
|
||||
noteY: () => number;
|
||||
tempo: (n?: number | undefined) => number;
|
||||
bpb: (n?: number | undefined) => number;
|
||||
ppqn: (n?: number | undefined) => number;
|
||||
time_signature: (numerator: number, denominator: number) => void;
|
||||
theme: (color_scheme: string) => void;
|
||||
themeName: () => string;
|
||||
randomTheme: () => void;
|
||||
nextTheme: () => void;
|
||||
getThemes: () => string[];
|
||||
pulseLocation: () => number;
|
||||
clear: () => boolean;
|
||||
w: () => number;
|
||||
h: () => number;
|
||||
hc: () => number;
|
||||
wc: () => number;
|
||||
background: (color: string | number, ...gb: number[]) => boolean;
|
||||
linearGradient: (x1: number, y1: number, x2: number, y2: number, ...stops: (number | string)[]) => CanvasGradient;
|
||||
radialGradient: (x1: number, y1: number, r1: number, x2: number, y2: number, r2: number, ...stops: (number | string)[]) => CanvasGradient;
|
||||
conicGradient: (x: number, y: number, angle: number, ...stops: (number | string)[]) => CanvasGradient;
|
||||
draw: (func: Function) => boolean;
|
||||
balloid: (curves: number | ShapeObject, radius: number, curve: number, fillStyle: string, secondary: string, x: number, y: number) => boolean;
|
||||
equilateral: (radius: number | ShapeObject, fillStyle: string, rotation: number, x: number, y: number) => boolean;
|
||||
triangular: (width: number | ShapeObject, height: number, fillStyle: string, rotation: number, x: number, y: number) => boolean;
|
||||
ball: (radius: number | ShapeObject, fillStyle: string, x: number, y: number) => boolean;
|
||||
circle: (radius: number | ShapeObject, fillStyle: string, x: number, y: number) => boolean;
|
||||
donut: (slices: number | ShapeObject, eaten: number, radius: number, hole: number, fillStyle: string, secondary: string, stroke: string, rotation: number, x: number, y: number) => boolean;
|
||||
pie: (slices: number | ShapeObject, eaten: number, radius: number, fillStyle: string, secondary: string, stroke: string, rotation: number, x: number, y: number) => boolean;
|
||||
star: (points: number | ShapeObject, radius: number, fillStyle: string, rotation: number, outerRadius: number, x: number, y: number) => boolean;
|
||||
stroke: (width: number | ShapeObject, strokeStyle: string, rotation: number, x1: number, y1: number, x2: number, y2: number) => boolean;
|
||||
box: (width: number | ShapeObject, height: number, fillStyle: string, rotation: number, x: number, y: number) => boolean;
|
||||
smiley: (happiness: number | ShapeObject, radius: number, eyeSize: number, fillStyle: string, rotation: number, x: number, y: number) => boolean;
|
||||
text: (text: string | ShapeObject, fontSize: number, rotation: number, font: string, x: number, y: number, fillStyle: string, filter: string) => boolean;
|
||||
image: (url: string | ShapeObject, width: number, height: number, rotation: number, x: number, y: number, filter: string) => boolean;
|
||||
randomChar: (length: number, min: number, max: number) => string;
|
||||
randomFromRange: (min: number, max: number) => string;
|
||||
emoji: (n: number) => string;
|
||||
food: (n: number) => string;
|
||||
animals: (n: number) => string;
|
||||
expressions: (n: number) => string;
|
||||
generateCacheKey: (...args: any[]) => string;
|
||||
resetAllFromCache: () => void;
|
||||
clearPatternCache: () => void;
|
||||
removePatternFromCache: (id: string) => void;
|
||||
script: (...args: number[]) => void;
|
||||
s: (...args: number[]) => void;
|
||||
delete_script: (script: number) => void;
|
||||
cs: (script: number) => void;
|
||||
copy_script: (from: number, to: number) => void;
|
||||
cps: (from: number, to: number) => void;
|
||||
copy_universe: (from: string, to: string) => void;
|
||||
delete_universe: (universe: string) => void;
|
||||
big_bang: () => void;
|
||||
drunk: (n?: number | undefined) => number;
|
||||
drunk_max: (max: number) => void;
|
||||
drunk_min: (min: number) => void;
|
||||
drunk_wrap: (wrap: boolean) => void;
|
||||
warp: (n: number) => void;
|
||||
beat_warp: (beat: number) => void;
|
||||
min: (...values: number[]) => number;
|
||||
max: (...values: number[]) => number;
|
||||
mean: (...values: number[]) => number;
|
||||
limit: (value: number, min: number, max: number) => number;
|
||||
abs: (value: number) => number;
|
||||
z: (input: string | Generator<number>, options: InputOptions, id: number | string) => Player;
|
||||
fullseq: (sequence: string, duration: number) => boolean | boolean[];
|
||||
seq: (expr: string, duration?: number) => boolean;
|
||||
beat: (n?: number | number[], nudge?: number) => boolean;
|
||||
bar: (n?: number | number[], nudge?: number) => boolean;
|
||||
pulse: (n?: number | number[], nudge?: number) => boolean;
|
||||
tick: (tick: number | number[], offset?: number) => boolean;
|
||||
dur: (n: number | number[]) => boolean;
|
||||
flip: (chunk: number, ratio?: number) => boolean;
|
||||
flipbar: (chunk?: number) => boolean;
|
||||
onbar: (bars: number | number[], n?: number) => boolean;
|
||||
onbeat: (...beat: number[]) => boolean;
|
||||
oncount: (beats: number | number[], count: number) => boolean;
|
||||
oneuclid: (pulses: number, length: number, rotate?: number) => boolean;
|
||||
euclid: (iterator: number, pulses: number, length: number, rotate?: number) => boolean;
|
||||
ec: any;
|
||||
rhythm: (div: number, pulses: number, length: number, rotate?: number) => boolean;
|
||||
ry: any;
|
||||
nrhythm: (div: number, pulses: number, length: number, rotate?: number) => boolean;
|
||||
nry: any;
|
||||
bin: (iterator: number, n: number) => boolean;
|
||||
binrhythm: (div: number, n: number) => boolean;
|
||||
bry: any;
|
||||
line: any;
|
||||
sine: any;
|
||||
usine: any;
|
||||
saw: any;
|
||||
usaw: any;
|
||||
triangle: any;
|
||||
utriangle: any;
|
||||
square: any;
|
||||
usquare: any;
|
||||
noise: any;
|
||||
unoise: any;
|
||||
prob: (p: number) => boolean;
|
||||
toss: () => boolean;
|
||||
odds: (n: number, beats?: number) => boolean;
|
||||
never: (beats?: number) => boolean;
|
||||
almostNever: (beats?: number) => boolean;
|
||||
rarely: (beats?: number) => boolean;
|
||||
scarcely: (beats?: number) => boolean;
|
||||
sometimes: (beats?: number) => boolean;
|
||||
often: (beats?: number) => boolean;
|
||||
frequently: (beats?: number) => boolean;
|
||||
almostAlways: (beats?: number) => boolean;
|
||||
always: (beats?: number) => boolean;
|
||||
dice: (sides: number) => number;
|
||||
osc: (address: string, port: number, ...args: any[]) => void;
|
||||
getOSC: (address?: string | undefined) => any[];
|
||||
gif: (options: any) => void;
|
||||
scope: (config: OscilloscopeConfig) => void;
|
||||
randI: any;
|
||||
rand: any;
|
||||
seed: any;
|
||||
localSeededRandom: any;
|
||||
clearLocalSeed: any;
|
||||
once: () => boolean;
|
||||
counter: (name: string | number, limit?: number | undefined, step?: number | undefined) => number;
|
||||
$: any;
|
||||
count: any;
|
||||
i: (n?: number | undefined) => any;
|
||||
sound: (sound: string | string[] | null | undefined) => SoundEvent | SkipEvent;
|
||||
snd: any;
|
||||
log: (message: any) => void;
|
||||
logOnce: (message: any) => void;
|
||||
|
||||
constructor(public app: Editor) {
|
||||
this.MidiConnection = new MidiConnection(this, app.settings);
|
||||
this.global = {};
|
||||
this.g = this.global;
|
||||
this.time = Transport.time(this.app);
|
||||
this.play = Transport.play(this.app);
|
||||
this.pause = Transport.pause(this.app);
|
||||
this.stop = Transport.stop(this.app);
|
||||
this.silence = Transport.silence(this.app);
|
||||
this.tempo = Transport.tempo(this.app);
|
||||
this.bpb = Transport.bpb(this.app);
|
||||
this.ppqn = Transport.ppqn(this.app);
|
||||
this.time_signature = Transport.time_signature(this.app);
|
||||
this.onMouseMove = Mouse.onmousemove(this.app);
|
||||
this.mouseX = Mouse.mouseX(this.app);
|
||||
this.mouseY = Mouse.mouseY(this.app);
|
||||
this.noteX = Mouse.noteX(this.app);
|
||||
this.noteY = Mouse.noteY(this.app);
|
||||
this.theme = Theme.theme(this.app);
|
||||
this.themeName = Theme.themeName(this.app);
|
||||
this.randomTheme = Theme.randomTheme(this.app);
|
||||
this.nextTheme = Theme.nextTheme(this.app);
|
||||
this.getThemes = Theme.getThemes();
|
||||
this.pulseLocation = Canvas.pulseLocation(this.app);
|
||||
this.clear = Canvas.clear(this.app);
|
||||
this.w = Canvas.w(this.app);
|
||||
this.h = Canvas.h(this.app);
|
||||
this.hc = Canvas.hc(this.app);
|
||||
this.wc = Canvas.wc(this.app);
|
||||
this.background = Canvas.background(this.app);
|
||||
this.linearGradient = Canvas.linearGradient(this.app);
|
||||
this.radialGradient = Canvas.radialGradient(this.app);
|
||||
this.conicGradient = Canvas.conicGradient(this.app);
|
||||
this.draw = Canvas.draw(this.app);
|
||||
this.balloid = Canvas.balloid(this.app);
|
||||
this.equilateral = Canvas.equilateral(this.app);
|
||||
this.triangular = Canvas.triangular(this.app);
|
||||
this.ball = Canvas.ball(this.app);
|
||||
this.circle = Canvas.circle(this.app);
|
||||
this.donut = Canvas.donut(this.app);
|
||||
this.pie = Canvas.pie(this.app);
|
||||
this.star = Canvas.star(this.app);
|
||||
this.stroke = Canvas.stroke(this.app);
|
||||
this.box = Canvas.box(this.app);
|
||||
this.smiley = Canvas.smiley(this.app);
|
||||
this.text = Canvas.text(this.app);
|
||||
this.image = Canvas.image(this.app);
|
||||
this.randomChar = Canvas.randomChar();
|
||||
this.randomFromRange = Canvas.randomFromRange();
|
||||
this.emoji = Canvas.emoji();
|
||||
this.food = Canvas.food();
|
||||
this.animals = Canvas.animals();
|
||||
this.expressions = Canvas.expressions();
|
||||
this.generateCacheKey = Cache.generateCacheKey(this.app);
|
||||
this.resetAllFromCache = Cache.resetAllFromCache(this.app);
|
||||
this.clearPatternCache = Cache.clearPatternCache(this.app);
|
||||
this.removePatternFromCache = Cache.removePatternFromCache(this.app);
|
||||
this.script = Script.script(this.app);
|
||||
this.s = this.script;
|
||||
this.delete_script = Script.delete_script(this.app);
|
||||
this.cs = this.delete_script;
|
||||
this.copy_script = Script.copy_script(this.app);
|
||||
this.cps = this.copy_script;
|
||||
this.copy_universe = Script.copy_universe(this.app);
|
||||
this.delete_universe = Script.delete_universe(this.app);
|
||||
this.big_bang = Script.big_bang(this.app);
|
||||
this.drunk = Drunk.drunk(this.app);
|
||||
this.drunk_max = Drunk.drunk_max(this.app);
|
||||
this.drunk_min = Drunk.drunk_min(this.app);
|
||||
this.drunk_wrap = Drunk.drunk_wrap(this.app);
|
||||
this.warp = Warp.warp(this.app);
|
||||
this.beat_warp = Warp.beat_warp(this.app);
|
||||
this.min = Mathematics.min(this.app);
|
||||
this.max = Mathematics.max(this.app);
|
||||
this.mean = Mathematics.mean(this.app);
|
||||
this.limit = Mathematics.limit(this.app);
|
||||
this.abs = Mathematics.abs(this.app);
|
||||
this.z = Ziffers.z(this.app);
|
||||
Object.assign(this, Ziffers.generateZFunctions(this.app));
|
||||
this.fullseq = Filters.fullseq();
|
||||
this.seq = Filters.seq(this.app);
|
||||
this.beat = Filters.beat(this.app);
|
||||
this.bar = Filters.bar(this.app);
|
||||
this.pulse = Filters.pulse(this.app);
|
||||
this.tick = Filters.tick(this.app);
|
||||
this.dur = Filters.dur(this.app);
|
||||
this.flip = Filters.flip(this.app);
|
||||
this.flipbar = Filters.flipbar(this.app);
|
||||
this.onbar = Filters.onbar(this.app);
|
||||
this.onbeat = Filters.onbeat(this.app);
|
||||
this.oncount = Filters.oncount(this.app);
|
||||
this.oneuclid = Filters.oneuclid(this.app);
|
||||
this.euclid = Filters.euclid(this.app);
|
||||
this.ec = this.euclid;
|
||||
this.rhythm = Filters.rhythm(this.app);
|
||||
this.ry = this.rhythm;
|
||||
this.nrhythm = Filters.nrhythm(this.app);
|
||||
this.nry = this.nrhythm;
|
||||
this.bin = Filters.bin();
|
||||
this.binrhythm = Filters.binrhythm(this.app);
|
||||
this.bry = this.binrhythm;
|
||||
this.line = LFO.line(this.app);
|
||||
this.sine = LFO.sine(this.app);
|
||||
this.usine = LFO.usine(this.app);
|
||||
this.saw = LFO.saw(this.app);
|
||||
this.usaw = LFO.usaw(this.app);
|
||||
this.triangle = LFO.triangle(this.app);
|
||||
this.utriangle = LFO.utriangle(this.app);
|
||||
this.square = LFO.square(this.app);
|
||||
this.usquare = LFO.usquare(this.app);
|
||||
this.noise = LFO.noise(this.app);
|
||||
this.unoise = LFO.unoise(this.app);
|
||||
this.prob = Probability.prob(this.app);
|
||||
this.toss = Probability.toss(this.app);
|
||||
this.odds = Probability.odds(this.app);
|
||||
this.never = Probability.never(this.app);
|
||||
this.almostNever = Probability.almostNever(this.app);
|
||||
this.rarely = Probability.rarely(this.app);
|
||||
this.scarcely = Probability.scarcely(this.app);
|
||||
this.sometimes = Probability.sometimes(this.app);
|
||||
this.often = Probability.often(this.app);
|
||||
this.frequently = Probability.frequently(this.app);
|
||||
this.almostAlways = Probability.almostAlways(this.app);
|
||||
this.always = Probability.always(this.app);
|
||||
this.dice = Probability.dice(this.app);
|
||||
this.osc = OSC.osc(this.app);
|
||||
this.getOSC = OSC.getOSC(this.app);
|
||||
this.gif = Canvas.gif(this.app);
|
||||
this.scope = Canvas.scope(this.app);
|
||||
this.randI = Randomness.randI(this.app);
|
||||
this.rand = Randomness.rand(this.app);
|
||||
this.seed = Randomness.seed(this.app);
|
||||
this.localSeededRandom = Randomness.localSeededRandom(this.app);
|
||||
this.clearLocalSeed = Randomness.clearLocalSeed(this.app);
|
||||
this.once = Counter.once(this.app);
|
||||
this.counter = Counter.counter(this.app);
|
||||
this.$ = this.counter;
|
||||
this.count = this.counter;
|
||||
this.i = Counter.i(this.app);
|
||||
this.sound = Sound.sound(this.app);
|
||||
this.snd = this.sound; // Alias
|
||||
this.speak = Sound.speak(this.app);
|
||||
this.log = Console.log(this.app);
|
||||
this.logOnce = Console.logOnce(this.app);
|
||||
|
||||
}
|
||||
|
||||
public g: any;
|
||||
|
||||
_loadUniverseFromInterface = (universe: string) => {
|
||||
this.app.selected_universe = universe.trim();
|
||||
this.app.settings.selected_universe = universe.trim();
|
||||
loadUniverse(this.app, universe as string);
|
||||
openUniverseModal();
|
||||
};
|
||||
|
||||
_deleteUniverseFromInterface = (universe: string) => {
|
||||
delete this.app.universes[universe];
|
||||
if (this.app.settings.selected_universe === universe) {
|
||||
this.app.settings.selected_universe = "Welcome";
|
||||
this.app.selected_universe = "Welcome";
|
||||
}
|
||||
this.app.settings.saveApplicationToLocalStorage(
|
||||
this.app.universes,
|
||||
this.app.settings,
|
||||
);
|
||||
this.app.updateKnownUniversesView();
|
||||
};
|
||||
|
||||
_playDocExample = (code?: string) => {
|
||||
/**
|
||||
* Play an example from the documentation. The example is going
|
||||
* to be stored in the example buffer belonging to the universe.
|
||||
* This buffer is going to be cleaned everytime the user press
|
||||
* pause or leaves the documentation window.
|
||||
*
|
||||
* @param code - The code example to play (identifier)
|
||||
*/
|
||||
let current_universe = this.app.universes[this.app.selected_universe];
|
||||
this.app.exampleIsPlaying = true;
|
||||
if (!current_universe.example) {
|
||||
current_universe.example = {
|
||||
candidate: "",
|
||||
committed: "",
|
||||
evaluations: 0,
|
||||
};
|
||||
current_universe.example.candidate! = code
|
||||
? code
|
||||
: (this.app.selectedExample as string);
|
||||
} else {
|
||||
current_universe.example.candidate! = code
|
||||
? code
|
||||
: (this.app.selectedExample as string);
|
||||
}
|
||||
this.clearPatternCache();
|
||||
this.stop();
|
||||
this.play();
|
||||
};
|
||||
|
||||
_stopDocExample = () => {
|
||||
let current_universe = this.app.universes[this.app.selected_universe];
|
||||
if (current_universe?.example !== undefined) {
|
||||
this.app.exampleIsPlaying = false;
|
||||
current_universe.example.candidate! = "";
|
||||
current_universe.example.committed! = "";
|
||||
}
|
||||
this.clearPatternCache();
|
||||
this.stop();
|
||||
};
|
||||
|
||||
_playDocExampleOnce = (code?: string) => {
|
||||
let current_universe = this.app.universes[this.app.selected_universe];
|
||||
if (current_universe?.example !== undefined) {
|
||||
current_universe.example.candidate! = "";
|
||||
current_universe.example.committed! = "";
|
||||
}
|
||||
this.clearPatternCache();
|
||||
this.stop();
|
||||
this.play();
|
||||
this.app.exampleIsPlaying = true;
|
||||
evaluateOnce(this.app, code as string);
|
||||
};
|
||||
|
||||
_all_samples = (): object => {
|
||||
return soundMap.get();
|
||||
};
|
||||
|
||||
_reportError = (error: any): void => {
|
||||
const extractLineAndColumn = (error: Error) => {
|
||||
const stackLines = error.stack?.split("\n");
|
||||
if (stackLines) {
|
||||
for (const line of stackLines) {
|
||||
if (line.includes("<anonymous>")) {
|
||||
const match = line.match(/<anonymous>:(\d+):(\d+)/);
|
||||
if (match)
|
||||
return {
|
||||
line: parseInt(match[1], 10),
|
||||
column: parseInt(match[2], 10),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return { line: null, column: null };
|
||||
};
|
||||
|
||||
const { line, column } = extractLineAndColumn(error);
|
||||
const errorMessage =
|
||||
line && column
|
||||
? `${error.message} (Line: ${line - 2}, Column: ${column})`
|
||||
: error.message;
|
||||
|
||||
clearTimeout(this.errorTimeoutID);
|
||||
clearTimeout(this.printTimeoutID);
|
||||
this.app.interface.error_line.innerHTML = errorMessage;
|
||||
this.app.interface.error_line.style.color = "red";
|
||||
this.app.interface.error_line.classList.remove("hidden");
|
||||
// @ts-ignore
|
||||
this.errorTimeoutID = setTimeout(
|
||||
() => this.app.interface.error_line.classList.add("hidden"),
|
||||
2000,
|
||||
);
|
||||
};
|
||||
|
||||
_logMessage = (message: any, error: boolean = false): void => {
|
||||
console.log(message);
|
||||
clearTimeout(this.printTimeoutID);
|
||||
clearTimeout(this.errorTimeoutID);
|
||||
this.app.interface.error_line.innerHTML = message as string;
|
||||
this.app.interface.error_line.style.color = error ? "red" : "white";
|
||||
this.app.interface.error_line.classList.remove("hidden");
|
||||
// @ts-ignore
|
||||
this.printTimeoutID = setTimeout(
|
||||
() => this.app.interface.error_line.classList.add("hidden"),
|
||||
4000,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// =============================================================
|
||||
// Quantification functions
|
||||
// =============================================================
|
||||
|
||||
public quantize = (value: number, quantization: number[]): number => {
|
||||
/**
|
||||
* Returns the closest value in an array to a given value.
|
||||
*
|
||||
* @param value - The value to quantize
|
||||
* @param quantization - The array of values to quantize to
|
||||
* @returns The closest value in the array to the given value
|
||||
*/
|
||||
if (quantization.length === 0) {
|
||||
return value;
|
||||
}
|
||||
let closest = quantization[0];
|
||||
quantization.forEach((q) => {
|
||||
if (Math.abs(q - value) < Math.abs(closest - value)) {
|
||||
closest = q;
|
||||
}
|
||||
});
|
||||
return closest;
|
||||
};
|
||||
quant = this.quantize;
|
||||
|
||||
public clamp = (value: number, min: number, max: number): number => {
|
||||
/**
|
||||
* Returns a value clamped between min and max.
|
||||
*
|
||||
* @param value - The value to clamp
|
||||
* @param min - The minimum value of the clamped value
|
||||
* @param max - The maximum value of the clamped value
|
||||
* @returns A value clamped between min and max
|
||||
*/
|
||||
return Math.min(Math.max(value, min), max);
|
||||
};
|
||||
cmp = this.clamp;
|
||||
|
||||
// =============================================================
|
||||
// Time markers
|
||||
// =============================================================
|
||||
|
||||
cbar = (): number => {
|
||||
/**
|
||||
* Returns the current bar number
|
||||
*
|
||||
* @returns The current bar number
|
||||
*/
|
||||
return this.app.clock.time_position.bar + 1;
|
||||
};
|
||||
|
||||
ctick = (): number => {
|
||||
/**
|
||||
* Returns the current tick number
|
||||
*
|
||||
* @returns The current tick number
|
||||
*/
|
||||
return this.app.clock.tick + 1;
|
||||
};
|
||||
|
||||
cpulse = (): number => {
|
||||
/**
|
||||
* Returns the current pulse number
|
||||
*
|
||||
* @returns The current pulse number
|
||||
*/
|
||||
return this.app.clock.time_position.pulse + 1;
|
||||
};
|
||||
|
||||
cbeat = (): number => {
|
||||
/**
|
||||
* Returns the current beat number
|
||||
*
|
||||
* @returns The current beat number
|
||||
*/
|
||||
return this.app.clock.time_position.beat + 1;
|
||||
};
|
||||
|
||||
ebeat = (): number => {
|
||||
/**
|
||||
* Returns the current beat number since the origin of time
|
||||
*/
|
||||
return this.app.clock.beats_since_origin + 1;
|
||||
};
|
||||
|
||||
epulse = (): number => {
|
||||
/**
|
||||
* Returns the current number of pulses elapsed since origin of time
|
||||
*/
|
||||
return this.app.clock.pulses_since_origin + 1;
|
||||
};
|
||||
|
||||
nominator = (): number => {
|
||||
/**
|
||||
* Returns the current nominator of the time signature
|
||||
*/
|
||||
return this.app.clock.time_signature[0];
|
||||
};
|
||||
|
||||
meter = (): number => {
|
||||
/**
|
||||
* Returns the current meter (denominator of the time signature)
|
||||
*/
|
||||
return this.app.clock.time_signature[1];
|
||||
};
|
||||
|
||||
denominator = this.meter;
|
||||
|
||||
pulsesForBar = (): number => {
|
||||
/**
|
||||
* Returns the number of pulses in a given bar
|
||||
*/
|
||||
return (this.tempo() * this.ppqn() * this.nominator()) / 60;
|
||||
}
|
||||
|
||||
// =============================================================
|
||||
// Fill
|
||||
// =============================================================
|
||||
|
||||
public fill = (): boolean => this.app.fill;
|
||||
|
||||
scale = getScaleNotes;
|
||||
nearScales = nearScales;
|
||||
|
||||
public cue = (functionName: string | Function): void => {
|
||||
functionName = typeof functionName === "function" ? functionName.name : functionName;
|
||||
this.cueTimes[functionName] = this.app.clock.pulses_since_origin;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user