Running Prettier on most files

This commit is contained in:
2023-08-30 12:04:49 +02:00
parent dca176cb04
commit fe0e46ec90
15 changed files with 1281 additions and 1198 deletions

View File

@ -51,7 +51,6 @@ export class UserAPI {
private errorTimeoutID: number = 0;
private printTimeoutID: number = 0;
MidiConnection: MidiConnection = new MidiConnection();
load: samples;
@ -60,7 +59,7 @@ export class UserAPI {
_loadUniverseFromInterface = (universe: string) => {
this.app.loadUniverse(universe as string);
this.app.openBuffersModal();
}
};
_deleteUniverseFromInterface = (universe: string) => {
delete this.app.universes[universe];
@ -69,7 +68,7 @@ export class UserAPI {
this.app.settings
);
this.app.updateKnownUniversesView();
}
};
_playDocExample = (code?: string) => {
this.play();
@ -82,7 +81,6 @@ export class UserAPI {
);
};
_playDocExampleOnce = (code?: string) => {
this.play();
console.log("Executing documentation example: " + this.app.selectedExample);
@ -154,7 +152,7 @@ export class UserAPI {
onmousemove = (e: MouseEvent) => {
this.app._mouseX = e.clientX;
this.app._mouseY = e.clientY;
}
};
public mouseX = (): number => {
/**
@ -361,7 +359,6 @@ export class UserAPI {
options: InputOptions = {},
id: number | string = ""
) => {
const zid = "z" + id.toString();
const key = id === "" ? this.generateCacheKey(input, options) : zid;
@ -386,23 +383,40 @@ export class UserAPI {
return player;
};
public z0 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 0);
public z1 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 1);
public z2 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 2);
public z3 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 3);
public z4 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 4);
public z5 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 5);
public z6 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 6);
public z7 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 7);
public z8 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 8);
public z9 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 9);
public z10 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 10);
public z11 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 11);
public z12 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 12);
public z13 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 13);
public z14 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 14);
public z15 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 15);
public z16 = (input: string, opts: InputOptions = {}) => this.z(input, opts, 16);
public z0 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 0);
public z1 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 1);
public z2 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 2);
public z3 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 3);
public z4 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 4);
public z5 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 5);
public z6 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 6);
public z7 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 7);
public z8 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 8);
public z9 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 9);
public z10 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 10);
public z11 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 11);
public z12 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 12);
public z13 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 13);
public z14 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 14);
public z15 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 15);
public z16 = (input: string, opts: InputOptions = {}) =>
this.z(input, opts, 16);
// =============================================================
// Counter and iteration
@ -930,11 +944,14 @@ export class UserAPI {
return current_chunk % 2 === 0;
};
public onbar = (bars: number[] | number, n: number = this.app.clock.time_signature[0]): boolean => {
public onbar = (
bars: number[] | number,
n: number = this.app.clock.time_signature[0]
): boolean => {
let current_bar = (this.bar() % n) + 1;
return (typeof bars === "number")
return typeof bars === "number"
? bars === current_bar
: bars.some((b) => b == current_bar)
: bars.some((b) => b == current_bar);
};
onbeat = (...beat: number[]): boolean => {
@ -949,9 +966,11 @@ export class UserAPI {
*/
let final_pulses: boolean[] = [];
beat.forEach((b) => {
const beat = b % this.app.clock.time_signature[0] || this.app.clock.time_signature[0];
const beat =
b % this.app.clock.time_signature[0] ||
this.app.clock.time_signature[0];
const integral_part = Math.floor(beat);
const decimal_part = ((beat - integral_part) * this.app.clock.ppqn) + 1;
const decimal_part = (beat - integral_part) * this.app.clock.ppqn + 1;
final_pulses.push(
integral_part === this.app.clock.time_position.beat &&
this.app.clock.time_position.pulse === decimal_part
@ -1019,8 +1038,10 @@ export class UserAPI {
length: number,
rotate: number = 0
): boolean => {
return this.mod(div) && this._euclidean_cycle(pulses, length, rotate).div(div);
}
return (
this.mod(div) && this._euclidean_cycle(pulses, length, rotate).div(div)
);
};
_euclidean_cycle(
pulses: number,
@ -1276,7 +1297,7 @@ export class UserAPI {
log = (message: any) => {
console.log(message);
this._logMessage(message);
}
};
scale = scale;

View File

@ -45,7 +45,8 @@ export const makeArrayExtensions = (api: UserAPI) => {
/**
* @returns New array with square roots of values. Throws if any element is negative.
*/
if (this.some(x => x < 0)) throw new Error('Cannot take square root of negative number');
if (this.some((x) => x < 0))
throw new Error("Cannot take square root of negative number");
return this.map((x: number) => Math.sqrt(x));
};
@ -78,7 +79,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
* @param amount - The value to divide each element in the array by.
* @returns New array with divided values. Throws if division by zero.
*/
if (amount === 0) throw new Error('Division by zero');
if (amount === 0) throw new Error("Division by zero");
return this.map((x: number) => x / amount);
};

View File

@ -1,7 +1,7 @@
// @ts-ignore
import { TransportNode } from './TransportNode';
import TransportProcessor from './TransportProcessor?worker&url';
import { Editor } from './main';
import { TransportNode } from "./TransportNode";
import TransportProcessor from "./TransportProcessor?worker&url";
import { Editor } from "./main";
export interface TimePosition {
/**
@ -11,13 +11,12 @@ export interface TimePosition {
* @param beat - The beat number
* @param pulse - The pulse number
*/
bar: number
beat: number
pulse: number
bar: number;
beat: number;
pulse: number;
}
export class Clock {
/**
* The Clock Class is responsible for keeping track of the current time.
* It is also responsible for starting and stopping the Clock TransportNode.
@ -32,30 +31,32 @@ export class Clock {
* @param tick - The current tick since origin
*/
ctx: AudioContext
transportNode: TransportNode | null
private _bpm: number
time_signature: number[]
time_position: TimePosition
private _ppqn: number
tick: number
ctx: AudioContext;
transportNode: TransportNode | null;
private _bpm: number;
time_signature: number[];
time_position: TimePosition;
private _ppqn: number;
tick: number;
constructor(public app: Editor, ctx: AudioContext) {
this.time_position = { bar: 0, beat: 0, pulse: 0 }
this.time_position = { bar: 0, beat: 0, pulse: 0 };
this.time_signature = [4, 4];
this.tick = 0;
this._bpm = 120;
this._ppqn = 48;
this.transportNode = null;
this.ctx = ctx;
ctx.audioWorklet.addModule(TransportProcessor).then((e) => {
ctx.audioWorklet
.addModule(TransportProcessor)
.then((e) => {
this.transportNode = new TransportNode(ctx, {}, this.app);
this.transportNode.connect(ctx.destination);
return e
return e;
})
.catch((e) => {
console.log('Error loading TransportProcessor.js:', e);
})
console.log("Error loading TransportProcessor.js:", e);
});
}
get ticks_before_new_bar(): number {
@ -67,7 +68,7 @@ export class Clock {
*/
const ticskMissingFromBeat = this.ppqn - this.time_position.pulse;
const beatsMissingFromBar = this.beats_per_bar - this.time_position.beat;
return (beatsMissingFromBar * this.ppqn) + ticskMissingFromBeat;
return beatsMissingFromBar * this.ppqn + ticskMissingFromBeat;
}
get next_beat_in_ticks(): number {
@ -103,7 +104,6 @@ export class Clock {
* @returns number of pulses since origin
*/
return this.tick;
}
get pulse_duration(): number {
@ -135,7 +135,7 @@ export class Clock {
/**
* Converts a pulse to a second.
*/
return n * this.pulse_duration
return n * this.pulse_duration;
}
public start(): void {
@ -144,7 +144,6 @@ export class Clock {
*/
this.app.audioContext.resume();
this.transportNode?.start();
}
public pause(): void {

View File

@ -1,5 +1,4 @@
export class MidiConnection {
/**
* Wrapper class for Web MIDI API. Provides methods for sending MIDI messages.
*
@ -29,11 +28,11 @@ export class MidiConnection{
this.midiAccess = await navigator.requestMIDIAccess();
this.midiOutputs = Array.from(this.midiAccess.outputs.values());
if (this.midiOutputs.length === 0) {
console.warn('No MIDI outputs available.');
console.warn("No MIDI outputs available.");
this.currentOutputIndex = -1;
}
} catch (error) {
console.error('Failed to initialize MIDI:', error);
console.error("Failed to initialize MIDI:", error);
}
}
@ -43,10 +42,14 @@ export class MidiConnection{
*
* @returns Name of the currently selected MIDI output or null if no MIDI output is selected or available.
*/
if (this.midiOutputs.length > 0 && this.currentOutputIndex >= 0 && this.currentOutputIndex < this.midiOutputs.length) {
if (
this.midiOutputs.length > 0 &&
this.currentOutputIndex >= 0 &&
this.currentOutputIndex < this.midiOutputs.length
) {
return this.midiOutputs[this.currentOutputIndex].name;
} else {
console.error('No MIDI output selected or available.');
console.error("No MIDI output selected or available.");
return null;
}
}
@ -57,10 +60,14 @@ export class MidiConnection{
*
* @returns Index of the currently selected MIDI output or -1 if no MIDI output is selected or available.
*/
if(this.midiOutputs.length > 0 && this.currentOutputIndex >= 0 && this.currentOutputIndex < this.midiOutputs.length) {
if (
this.midiOutputs.length > 0 &&
this.currentOutputIndex >= 0 &&
this.currentOutputIndex < this.midiOutputs.length
) {
return this.currentOutputIndex;
} else {
console.error('No MIDI output selected or available.');
console.error("No MIDI output selected or available.");
return -1;
}
}
@ -71,9 +78,9 @@ export class MidiConnection{
*/
const output = this.midiOutputs[this.currentOutputIndex];
if (output) {
output.send([0xF8]); // Send a single MIDI clock message
output.send([0xf8]); // Send a single MIDI clock message
} else {
console.error('MIDI output not available.');
console.error("MIDI output not available.");
}
}
@ -101,9 +108,13 @@ export class MidiConnection{
* @returns Index of the new MIDI output or current output if new is not valid
*
*/
if(typeof output === 'number') {
if (typeof output === "number") {
if (output < 0 || output >= this.midiOutputs.length) {
console.error(`Invalid MIDI output index. Index must be in the range 0-${this.midiOutputs.length - 1}.`);
console.error(
`Invalid MIDI output index. Index must be in the range 0-${
this.midiOutputs.length - 1
}.`
);
return this.currentOutputIndex;
} else {
return output;
@ -123,14 +134,21 @@ export class MidiConnection{
/**
* Lists all available MIDI outputs to the console.
*/
let final_string = 'Available MIDI Outputs: ';
let final_string = "Available MIDI Outputs: ";
this.midiOutputs.forEach((output, index) => {
final_string += `(${index + 1}) ${output.name} `;
});
return final_string;
}
public sendMidiNote(noteNumber: number, channel: number, velocity: number, duration: number, port: number|string = this.currentOutputIndex, bend: number|undefined = undefined): void {
public sendMidiNote(
noteNumber: number,
channel: number,
velocity: number,
duration: number,
port: number | string = this.currentOutputIndex,
bend: number | undefined = undefined
): void {
/**
* Sending a MIDI Note on/off message with the same note number and channel. Automatically manages
* the note off message after the specified duration.
@ -142,7 +160,7 @@ export class MidiConnection{
*
*/
if(typeof port === 'string') port = this.getMidiOutputIndex(port);
if (typeof port === "string") port = this.getMidiOutputIndex(port);
const output = this.midiOutputs[port];
noteNumber = Math.min(Math.max(noteNumber, 0), 127);
if (output) {
@ -163,7 +181,7 @@ export class MidiConnection{
this.scheduledNotes[noteNumber] = timeoutId;
} else {
console.error('MIDI output not available.');
console.error("MIDI output not available.");
}
}
@ -181,11 +199,15 @@ export class MidiConnection{
if (output) {
output.send(message);
} else {
console.error('MIDI output not available.');
console.error("MIDI output not available.");
}
}
public sendPitchBend(value: number, channel: number, port: number|string = this.currentOutputIndex): void {
public sendPitchBend(
value: number,
channel: number,
port: number | string = this.currentOutputIndex
): void {
/**
* Sends a MIDI Pitch Bend message to the currently selected MIDI output.
*
@ -194,19 +216,21 @@ export class MidiConnection{
*
*/
if (value < 0 || value > 16383) {
console.error('Invalid pitch bend value. Value must be in the range 0-16383.');
console.error(
"Invalid pitch bend value. Value must be in the range 0-16383."
);
}
if (channel < 0 || channel > 15) {
console.error('Invalid MIDI channel. Channel must be in the range 0-15.');
console.error("Invalid MIDI channel. Channel must be in the range 0-15.");
}
if(typeof port === 'string') port = this.getMidiOutputIndex(port);
if (typeof port === "string") port = this.getMidiOutputIndex(port);
const output = this.midiOutputs[port];
if (output) {
const lsb = value & 0x7F;
const msb = (value >> 7) & 0x7F;
output.send([0xE0 | channel, lsb, msb]);
const lsb = value & 0x7f;
const msb = (value >> 7) & 0x7f;
output.send([0xe0 | channel, lsb, msb]);
} else {
console.error('MIDI output not available.');
console.error("MIDI output not available.");
}
}
@ -223,13 +247,17 @@ export class MidiConnection{
*/
const output = this.midiOutputs[this.currentOutputIndex];
if (output) {
output.send([0xC0 + channel, programNumber]); // Program Change
output.send([0xc0 + channel, programNumber]); // Program Change
} else {
console.error('MIDI output not available.');
console.error("MIDI output not available.");
}
}
public sendMidiControlChange(controlNumber: number, value: number, channel: number): void {
public sendMidiControlChange(
controlNumber: number,
value: number,
channel: number
): void {
/**
* Sends a MIDI Control Change message to the currently selected MIDI output.
*
@ -239,9 +267,9 @@ export class MidiConnection{
*/
const output = this.midiOutputs[this.currentOutputIndex];
if (output) {
output.send([0xB0 + channel, controlNumber, value]); // Control Change
output.send([0xb0 + channel, controlNumber, value]); // Control Change
} else {
console.error('MIDI output not available.');
console.error("MIDI output not available.");
}
}
@ -258,7 +286,7 @@ export class MidiConnection{
}
this.scheduledNotes = {};
} else {
console.error('MIDI output not available.');
console.error("MIDI output not available.");
}
}
}

View File

@ -50,10 +50,14 @@ const SCALES: Record<string, number[]> = {
hindustan: [0, 2, 4, 5, 7, 8, 10],
persian: [0, 1, 4, 5, 6, 8, 11],
eastIndianPurvi: [0, 1, 4, 6, 7, 8, 11],
orientalA: [0, 1, 4, 5, 6, 9, 10]
orientalA: [0, 1, 4, 5, 6, 9, 10],
};
export function scale(n: number, scaleName: string = 'major', octave: number = 4): number {
export function scale(
n: number,
scaleName: string = "major",
octave: number = 4
): number {
/**
* Returns the MIDI note number for the given scale degree in the given scale.
* @param {number} n - The scale degree, where 0 is the tonic.

View File

@ -1,5 +1,4 @@
export class DrunkWalk {
/**
* A class that implements a "drunk walk" algorithm. This is useful for generating random
* numbers in a constrained range. The "drunk" starts at a position, and then makes a step
@ -24,7 +23,6 @@ export class DrunkWalk {
}
step(): void {
/**
* Makes a step in the "drunk walk" algorithm. This is a random step of +1, 0, or -1.
*/

View File

@ -1,5 +1,11 @@
import { type Editor } from '../main';
import { freqToMidi, resolvePitchBend, getScale, isScale, parseScala } from 'zifferjs';
import { type Editor } from "../main";
import {
freqToMidi,
resolvePitchBend,
getScale,
isScale,
parseScala,
} from "zifferjs";
export abstract class Event {
seedValue: string | undefined = undefined;
@ -19,60 +25,59 @@ export abstract class Event {
return this.modify(func);
}
return this;
}
};
almostNever = (func: Function): Event => {
return this.odds(0.025, func);
}
};
rarely = (func: Function): Event => {
return this.odds(0.1, func);
}
};
scarcely = (func: Function): Event => {
return this.odds(0.25, func);
}
};
sometimes = (func: Function): Event => {
return this.odds(0.5, func);
}
};
often = (func: Function): Event => {
return this.odds(0.75, func);
}
};
frequently = (func: Function): Event => {
return this.odds(0.9, func);
}
};
almostAlways = (func: Function): Event => {
return this.odds(0.985, func);
}
};
modify = (func: Function): Event => {
return func(this);
}
};
seed = (value: string | number): Event => {
this.seedValue = value.toString();
this.randomGen = this.app.api.localSeededRandom(this.seedValue);
return this;
}
};
clear = (): Event => {
this.app.api.clearLocalSeed(this.seedValue);
return this;
}
};
apply = (func: Function): Event => {
return this.modify(func);
}
};
duration = (value: number): Event => {
this.values['duration'] = value;
this.values["duration"] = value;
return this;
}
};
}
export abstract class AudibleEvent extends Event {
@ -81,16 +86,16 @@ export abstract class AudibleEvent extends Event {
}
octave = (value: number): this => {
this.values['octave'] = value;
this.values["octave"] = value;
this.update();
return this;
}
};
key = (value: string): this => {
this.values['key'] = value;
this.values["key"] = value;
this.update();
return this;
}
};
scale = (value: string): this => {
if (!isScale(value)) {
@ -101,22 +106,21 @@ export abstract class AudibleEvent extends Event {
}
this.update();
return this;
}
};
freq = (value: number): this => {
this.values['freq'] = value;
this.values["freq"] = value;
const midiNote = freqToMidi(value);
if (midiNote % 1 !== 0) {
this.values['note'] = Math.floor(midiNote);
this.values['bend'] = resolvePitchBend(midiNote)[1];
this.values["note"] = Math.floor(midiNote);
this.values["bend"] = resolvePitchBend(midiNote)[1];
} else {
this.values['note'] = midiNote;
this.values["note"] = midiNote;
}
return this;
}
};
update = (): void => {
// Overwrite in subclasses
}
};
}

View File

@ -1,66 +1,65 @@
import { AudibleEvent } from './AbstractEvents';
import { type Editor } from '../main';
import { AudibleEvent } from "./AbstractEvents";
import { type Editor } from "../main";
import { MidiConnection } from "../IO/MidiConnection";
import { midiToFreq, noteFromPc } from 'zifferjs';
import { midiToFreq, noteFromPc } from "zifferjs";
export class NoteEvent extends AudibleEvent {
midiConnection: MidiConnection;
constructor(input: number | object, public app: Editor) {
super(app);
if(typeof input === 'number') this.values['note'] = input;
if (typeof input === "number") this.values["note"] = input;
else this.values = input;
this.midiConnection = app.api.MidiConnection
this.midiConnection = app.api.MidiConnection;
}
note = (value: number): this => {
this.values['note'] = value;
this.values["note"] = value;
return this;
}
};
sustain = (value: number): this => {
this.values['sustain'] = value;
this.values["sustain"] = value;
return this;
}
};
channel = (value: number): this => {
this.values['channel'] = value;
this.values["channel"] = value;
return this;
}
};
port = (value: number | string): this => {
this.values['port'] = this.midiConnection.getMidiOutputIndex(value);
this.values["port"] = this.midiConnection.getMidiOutputIndex(value);
return this;
}
};
add = (value: number): this => {
this.values.note += value;
return this;
}
};
modify = (func: Function): this => {
const funcResult = func(this);
if (funcResult instanceof Object) {
return funcResult;
}
else {
} else {
func(this.values);
this.update();
return this;
}
}
};
bend = (value: number): this => {
this.values['bend'] = value;
this.values["bend"] = value;
return this;
}
};
random = (min: number = 0, max: number = 127): this => {
min = Math.min(Math.max(min, 0), 127);
max = Math.min(Math.max(max, 0), 127);
this.values['note'] = Math.floor(this.randomGen() * (max - min + 1)) + min;
this.values["note"] = Math.floor(this.randomGen() * (max - min + 1)) + min;
return this;
}
};
update = (): void => {
const [note, bend] = noteFromPc(
@ -72,24 +71,32 @@ export class NoteEvent extends AudibleEvent {
this.values.note = note;
this.values.freq = midiToFreq(note);
if (bend) this.values.bend = bend;
}
};
out = (): void => {
const note = this.values.note ? this.values.note : 60;
const channel = this.values.channel ? this.values.channel : 0;
const velocity = this.values.velocity ? this.values.velocity : 100;
const sustain = this.values.sustain ?
this.values.sustain * this.app.clock.pulse_duration * this.app.api.ppqn() :
this.app.clock.pulse_duration * this.app.api.ppqn();
const sustain = this.values.sustain
? this.values.sustain *
this.app.clock.pulse_duration *
this.app.api.ppqn()
: this.app.clock.pulse_duration * this.app.api.ppqn();
const bend = this.values.bend ? this.values.bend : undefined;
const port = this.values.port ?
this.midiConnection.getMidiOutputIndex(this.values.port) :
this.midiConnection.getCurrentMidiPortIndex();
this.midiConnection.sendMidiNote(note, channel, velocity, sustain, port, bend);
}
const port = this.values.port
? this.midiConnection.getMidiOutputIndex(this.values.port)
: this.midiConnection.getCurrentMidiPortIndex();
this.midiConnection.sendMidiNote(
note,
channel,
velocity,
sustain,
port,
bend
);
};
}

View File

@ -1,4 +1,4 @@
import { type Editor } from '../main';
import { type Editor } from "../main";
import { Event } from "./AbstractEvents";
export class RestEvent extends Event {
@ -9,15 +9,18 @@ export class RestEvent extends Event {
_fallbackMethod = (): Event => {
return RestEvent.createRestProxy(this.values["duration"], this.app);
}
};
public static createRestProxy = (duration: number, app: Editor): RestEvent => {
public static createRestProxy = (
duration: number,
app: Editor
): RestEvent => {
const instance = new RestEvent(duration, app);
return new Proxy(instance, {
// @ts-ignore
get(target, propKey, receiver) {
// @ts-ignore
if (typeof target[propKey] === 'undefined') {
if (typeof target[propKey] === "undefined") {
return target._fallbackMethod;
}
// @ts-ignore
@ -26,12 +29,11 @@ export class RestEvent extends Event {
// @ts-ignore
set(target, propKey, value, receiver) {
return false;
}
},
});
}
};
out = (): void => {
// TODO?
}
};
}

View File

@ -1,8 +1,7 @@
export class SkipEvent {
_fallbackMethod = (): SkipEvent => {
return SkipEvent.createSkipProxy();
}
};
public static createSkipProxy = (): SkipEvent => {
const instance = new SkipEvent();
@ -10,7 +9,7 @@ export class SkipEvent {
// @ts-ignore
get(target, propKey, receiver) {
// @ts-ignore
if (typeof target[propKey] === 'undefined') {
if (typeof target[propKey] === "undefined") {
return target._fallbackMethod;
}
// @ts-ignore
@ -19,10 +18,9 @@ export class SkipEvent {
// @ts-ignore
set(target, propKey, value, receiver) {
return false;
}
},
});
}
out = (): void => {}
};
out = (): void => {};
}

View File

@ -56,39 +56,38 @@ export class Player extends Event {
this.current = this.ziffers.next() as Pitch | Chord | ZRest;
this.played = true;
return this.current;
}
};
pulseToSecond = (pulse: number): number => {
return this.app.clock.convertPulseToSecond(pulse);
}
};
firstRun = (): boolean => {
return this.notStarted();
}
};
atTheBeginning = (): boolean => {
return this.skipIndex === 0 && this.ziffers.index <= 0;
}
};
origin = (): number => {
return this.app.clock.pulses_since_origin;
}
};
pulse = (): number => {
return this.app.clock.time_position.pulse;
}
};
beat = (): number => {
return this.app.clock.time_position.beat;
}
};
nextBeat = (): number => {
return this.app.clock.next_beat_in_ticks;
}
};
// Check if it's time to play the event
areWeThereYet = (): boolean => {
// If clock has stopped
if (this.app.clock.pulses_since_origin < this.lastCallTime) {
this.lastCallTime = 1;
@ -98,22 +97,20 @@ export class Player extends Event {
}
// Main logic
const howAboutNow = (
( // If pattern is just starting
this.notStarted() &&
const howAboutNow =
// If pattern is just starting
(this.notStarted() &&
(this.app.clock.time_position.pulse === 1 ||
this.app.clock.pulses_since_origin >= this.app.clock.next_beat_in_ticks) &&
(this.app.clock.pulses_since_origin >= this.waitTime)
)
||
( // If pattern is already playing
this.current &&
(this.pulseToSecond(this.app.clock.pulses_since_origin) >=
this.app.clock.pulses_since_origin >=
this.app.clock.next_beat_in_ticks) &&
this.app.clock.pulses_since_origin >= this.waitTime) || // If pattern is already playing
(this.current &&
this.pulseToSecond(this.app.clock.pulses_since_origin) >=
this.pulseToSecond(this.lastCallTime) +
(this.current.duration*4) * this.pulseToSecond(this.app.api.ppqn())) &&
(this.app.clock.pulses_since_origin >= this.waitTime)
)
);
this.current.duration *
4 *
this.pulseToSecond(this.app.api.ppqn()) &&
this.app.clock.pulses_since_origin >= this.waitTime);
// Increment index of how many times call is skipped
this.skipIndex = howAboutNow ? 0 : this.skipIndex + 1;
@ -121,7 +118,6 @@ export class Player extends Event {
// Increment index of how many times sound/midi have been called
this.index = howAboutNow ? this.index + 1 : this.index;
if (howAboutNow && this.notStarted()) {
this.initCallTime = this.app.clock.pulses_since_origin;
}
@ -131,13 +127,20 @@ export class Player extends Event {
}
return howAboutNow;
}
};
sound(name: string) {
if (this.areWeThereYet()) {
const event = this.next() as Pitch | Chord | ZRest;
if (event instanceof Pitch) {
const obj = event.getExisting("freq","pitch","key","scale","octave","parsedScale");
const obj = event.getExisting(
"freq",
"pitch",
"key",
"scale",
"octave",
"parsedScale"
);
return new SoundEvent(obj, this.app).sound(name);
} else if (event instanceof ZRest) {
return RestEvent.createRestProxy(event.duration, this.app);
@ -151,7 +154,15 @@ export class Player extends Event {
if (this.areWeThereYet()) {
const event = this.next() as Pitch | Chord | ZRest;
if (event instanceof Pitch) {
const obj = event.getExisting("note","pitch","bend","key","scale","octave","parsedScale");
const obj = event.getExisting(
"note",
"pitch",
"bend",
"key",
"scale",
"octave",
"parsedScale"
);
const note = new NoteEvent(obj, this.app);
return value ? note.note(value) : note;
} else if (event instanceof ZRest) {
@ -189,14 +200,13 @@ export class Player extends Event {
if (refPat) this.waitTime = refPat.nextEndTime();
return this;
}
this.waitTime = this.origin() + Math.ceil(value*4*this.app.clock.ppqn);
this.waitTime =
this.origin() + Math.ceil(value * 4 * this.app.clock.ppqn);
}
return this;
}
out = (): void => {
// TODO?
}
};
}

View File

@ -1,8 +1,4 @@
import {
uniqueNamesGenerator,
colors,
animals,
} from "unique-names-generator";
import { uniqueNamesGenerator, colors, animals } from "unique-names-generator";
import { examples } from "./examples/excerpts";
import { EditorState, Compartment } from "@codemirror/state";
import { ViewUpdate, lineNumbers, keymap } from "@codemirror/view";
@ -28,7 +24,7 @@ import {
} from "./AppSettings";
import { tryEvaluate } from "./Evaluator";
// @ts-ignore
import { gzipSync, decompressSync, strFromU8 } from 'fflate';
import { gzipSync, decompressSync, strFromU8 } from "fflate";
// Importing showdown and setting up the markdown converter
import showdown from "showdown";
@ -94,7 +90,9 @@ export class Editor {
public _mouseY: number = 0;
// Topos Logo
topos_logo: HTMLElement = document.getElementById('topos-logo') as HTMLElement;
topos_logo: HTMLElement = document.getElementById(
"topos-logo"
) as HTMLElement;
// Transport elements
play_buttons: HTMLButtonElement[] = [
@ -108,7 +106,9 @@ export class Editor {
document.getElementById("clear-button-1") as HTMLButtonElement,
//document.getElementById("clear-button-2") as HTMLButtonElement,
];
load_universe_button: HTMLButtonElement = document.getElementById("load-universe-button") as HTMLButtonElement;
load_universe_button: HTMLButtonElement = document.getElementById(
"load-universe-button"
) as HTMLButtonElement;
documentation_button: HTMLButtonElement = document.getElementById(
"doc-button-1"
@ -255,7 +255,9 @@ export class Editor {
// ================================================================================
// Building the documentation
let pre_loading = async () => { await loadSamples(); };
let pre_loading = async () => {
await loadSamples();
};
pre_loading();
this.docs = documentation_factory(this);
// ================================================================================
@ -265,7 +267,6 @@ export class Editor {
// ================================================================================
window.addEventListener("keydown", (event: KeyboardEvent) => {
if (event.key === "Tab") {
event.preventDefault();
}
@ -278,7 +279,7 @@ export class Editor {
if (event.ctrlKey && event.key === "p") {
event.preventDefault();
if (this.isPlaying) {
if (this.isPlaying) {
this.isPlaying = false;
this.setButtonHighlighting("pause", true);
this.clock.pause();
@ -422,7 +423,7 @@ export class Editor {
this.hideDocumentation();
this.updateKnownUniversesView();
this.openBuffersModal();
})
});
this.play_buttons.forEach((button) => {
button.addEventListener("click", () => {
@ -452,7 +453,6 @@ export class Editor {
this.showDocumentation();
});
this.load_universe_button.addEventListener("click", () => {
let query = this.buffer_search.value;
if (query.length > 2 && query.length < 20 && !query.includes(" ")) {
@ -470,7 +470,6 @@ export class Editor {
this.flashBackground("#2d313d", 200);
});
this.stop_buttons.forEach((button) => {
button.addEventListener("click", () => {
this.setButtonHighlighting("stop", true);
@ -565,7 +564,6 @@ export class Editor {
});
this.universe_creator.addEventListener("submit", (event) => {
event.preventDefault();
let data = new FormData(this.universe_creator);
@ -607,9 +605,9 @@ export class Editor {
this.currentDocumentationPane = e;
this.updateDocumentationContent();
} else {
console.log('Loading samples!');
console.log("Loading samples!");
await loadSamples().then(() => {
this.docs = documentation_factory(this)
this.docs = documentation_factory(this);
this.currentDocumentationPane = e;
this.updateDocumentationContent();
});
@ -656,14 +654,18 @@ export class Editor {
if (url !== null) {
const universeParam = url.get("universe");
if (universeParam !== null) {
let data = Uint8Array.from(atob(universeParam), c => c.charCodeAt(0))
let data = Uint8Array.from(atob(universeParam), (c) =>
c.charCodeAt(0)
);
new_universe = JSON.parse(strFromU8(decompressSync(data)));
const randomName: string = uniqueNamesGenerator({
length: 2, separator: '_',
length: 2,
separator: "_",
dictionaries: [colors, animals],
});
this.loadUniverse(randomName, new_universe["universe"]);
this.emptyUrl(); this.emptyUrl();
this.emptyUrl();
this.emptyUrl();
}
}
}
@ -698,7 +700,8 @@ export class Editor {
updateKnownUniversesView = () => {
let existing_universes = document.getElementById("existing-universes");
let known_universes = Object.keys(this.universes);
let final_html = "<ul class='lg:h-80 lg:w-80 lg:pb-2 lg:pt-2 overflow-y-scroll text-white lg:mb-4 border rounded-lg bg-gray-800'>";
let final_html =
"<ul class='lg:h-80 lg:w-80 lg:pb-2 lg:pt-2 overflow-y-scroll text-white lg:mb-4 border rounded-lg bg-gray-800'>";
known_universes.forEach((name) => {
final_html += `
<li onclick="_loadUniverseFromInterface('${name}')" class="hover:fill-black hover:bg-white py-2 hover:text-black flex justify-between px-4">
@ -708,17 +711,16 @@ export class Editor {
});
final_html = final_html + "</ul>";
existing_universes!.innerHTML = final_html;
}
};
async share() {
async function bufferToBase64(buffer: Uint8Array) {
const base64url: string = await new Promise(r => {
const reader = new FileReader()
reader.onload = () => r(reader.result as string)
reader.readAsDataURL(new Blob([buffer]))
const base64url: string = await new Promise((r) => {
const reader = new FileReader();
reader.onload = () => r(reader.result as string);
reader.readAsDataURL(new Blob([buffer]));
});
return base64url.slice(base64url.indexOf(',') + 1);
return base64url.slice(base64url.indexOf(",") + 1);
}
let data = JSON.stringify({
@ -852,23 +854,23 @@ export class Editor {
button: "play" | "pause" | "stop" | "clear",
highlight: boolean
) {
document.getElementById('play-label')!.textContent = button !== "pause" ? "Pause" : "Play";
if (button !== "pause") {
document.getElementById('pause-icon')!.classList.remove('hidden');
document.getElementById('play-icon')!.classList.add('hidden');
document.getElementById("play-label")!.textContent =
button !== "pause" ? "Pause" : "Play";
if (button !== "pause") {
document.getElementById("pause-icon")!.classList.remove("hidden");
document.getElementById("play-icon")!.classList.add("hidden");
} else {
document.getElementById('pause-icon')!.classList.add('hidden');
document.getElementById('play-icon')!.classList.remove('hidden');
document.getElementById("pause-icon")!.classList.add("hidden");
document.getElementById("play-icon")!.classList.remove("hidden");
}
if (button === "stop") {
this.isPlaying == false;
document.getElementById('play-label')!.textContent = "Play";
document.getElementById('pause-icon')!.classList.add('hidden');
document.getElementById('play-icon')!.classList.remove('hidden');
document.getElementById("play-label")!.textContent = "Play";
document.getElementById("pause-icon")!.classList.add("hidden");
document.getElementById("play-icon")!.classList.remove("hidden");
}
this.flashBackground("#2d313d", 200);
const possible_selectors = [
'[id^="play-button-"]',

View File

@ -1,116 +1,125 @@
import { EditorView } from '@codemirror/view'
import { Extension } from '@codemirror/state'
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'
import { tags as t } from '@lezer/highlight'
import { EditorView } from "@codemirror/view";
import { Extension } from "@codemirror/state";
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
import { tags as t } from "@lezer/highlight";
const base00 = '#171717', base01 = '#505d64',
base02 = 'white', base03 = '#707d8b',
base04 = '#a0a4ae', base05 = '#bdbdbd',
base06 = '#e0e0e0', base07 = '#fdf6e3',
base_red = '#ff5f52', base_deeporange = '#ff6e40',
base_pink = '#fa5788', base_yellow = '#facf4e',
base_orange = '#ffad42', base_cyan = '#1E6AE1',
base_indigo = '#7186f0', base_purple = '#D09EBF',
base_green = '#82d47c', base_lightgreen = '#82d47c',
base_teal = '#4ebaaa'
const base00 = "#171717",
base01 = "#505d64",
base02 = "white",
base03 = "#707d8b",
base04 = "#a0a4ae",
base05 = "#bdbdbd",
base06 = "#e0e0e0",
base07 = "#fdf6e3",
base_red = "#ff5f52",
base_deeporange = "#ff6e40",
base_pink = "#fa5788",
base_yellow = "#facf4e",
base_orange = "#ffad42",
base_cyan = "#1E6AE1",
base_indigo = "#7186f0",
base_purple = "#D09EBF",
base_green = "#82d47c",
base_lightgreen = "#82d47c",
base_teal = "#4ebaaa";
const invalid = base_red,
darkBackground = '#fdf6e3',
highlightBackground = '#545b61',
darkBackground = "#fdf6e3",
highlightBackground = "#545b61",
background = base00,
tooltipBackground = base01,
selection = base07,
cursor = base04
cursor = base04;
/// The editor theme styles for Material Dark.
export const materialDarkTheme = EditorView.theme(
{
'&': {
"&": {
color: base05,
backgroundColor: background,
fontSize: '24px',
fontSize: "24px",
},
'.cm-content': {
caretColor: cursor
".cm-content": {
caretColor: cursor,
},
'.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
'&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
".cm-cursor, .cm-dropCursor": { borderLeftColor: cursor },
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
{ backgroundColor: selection, border: `0.5px solid ${base_teal}` },
'.cm-panels': { backgroundColor: darkBackground, color: base03 },
'.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
'.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
".cm-panels": { backgroundColor: darkBackground, color: base03 },
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" },
'.cm-searchMatch': {
".cm-searchMatch": {
outline: `1px solid ${base_yellow}`,
backgroundColor: 'transparent'
backgroundColor: "transparent",
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: highlightBackground
".cm-searchMatch.cm-searchMatch-selected": {
backgroundColor: highlightBackground,
},
'.cm-activeLine': { backgroundColor: highlightBackground },
'.cm-selectionMatch': {
".cm-activeLine": { backgroundColor: highlightBackground },
".cm-selectionMatch": {
backgroundColor: darkBackground,
outline: `1px solid ${base_teal}`
outline: `1px solid ${base_teal}`,
},
'&.cm-focused .cm-matchingBracket': {
"&.cm-focused .cm-matchingBracket": {
color: base06,
outline: `1px solid ${base_teal}`
outline: `1px solid ${base_teal}`,
},
'&.cm-focused .cm-nonmatchingBracket': {
color: base_red
"&.cm-focused .cm-nonmatchingBracket": {
color: base_red,
},
'.cm-gutters': {
".cm-gutters": {
backgroundColor: base00,
borderRight: `1px solid ${base07}`,
color: base02
color: base02,
},
'.cm-activeLineGutter': {
".cm-activeLineGutter": {
backgroundColor: highlightBackground,
color: base07
color: base07,
},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
".cm-foldPlaceholder": {
backgroundColor: "transparent",
border: "none",
color: `${base07}`,
},
'.cm-tooltip': {
border: 'none',
backgroundColor: tooltipBackground
".cm-tooltip": {
border: "none",
backgroundColor: tooltipBackground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent'
".cm-tooltip .cm-tooltip-arrow:before": {
borderTopColor: "transparent",
borderBottomColor: "transparent",
},
'.cm-tooltip .cm-tooltip-arrow:after': {
".cm-tooltip .cm-tooltip-arrow:after": {
borderTopColor: tooltipBackground,
borderBottomColor: tooltipBackground
borderBottomColor: tooltipBackground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
".cm-tooltip-autocomplete": {
"& > ul > li[aria-selected]": {
backgroundColor: highlightBackground,
color: base03
}
}
color: base03,
},
},
},
{ dark: true }
)
);
/// The highlighting style for code in the Material Dark theme.
export const materialDarkHighlightStyle = HighlightStyle.define([
{ tag: t.keyword, color: base_purple },
{
tag: [t.name, t.deleted, t.character, t.macroName],
color: base_cyan
color: base_cyan,
},
{ tag: [t.propertyName], color: base_yellow },
{ tag: [t.variableName], color: base05 },
@ -118,93 +127,93 @@ export const materialDarkHighlightStyle = HighlightStyle.define([
{ tag: [t.labelName], color: base_purple },
{
tag: [t.color, t.constant(t.name), t.standard(t.name)],
color: base_yellow
color: base_yellow,
},
{ tag: [t.definition(t.name), t.separator], color: base_pink },
{ tag: [t.brace], color: base_purple },
{
tag: [t.annotation],
color: invalid
color: invalid,
},
{
tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace],
color: base_orange
color: base_orange,
},
{
tag: [t.typeName, t.className],
color: base_orange
color: base_orange,
},
{
tag: [t.operator, t.operatorKeyword],
color: base_indigo
color: base_indigo,
},
{
tag: [t.tagName],
color: base_deeporange
color: base_deeporange,
},
{
tag: [t.squareBracket],
color: base_red
color: base_red,
},
{
tag: [t.angleBracket],
color: base02
color: base02,
},
{
tag: [t.attributeName],
color: base05
color: base05,
},
{
tag: [t.regexp],
color: invalid
color: invalid,
},
{
tag: [t.quote],
color: base_green
color: base_green,
},
{ tag: [t.string], color: base_lightgreen },
{
tag: t.link,
color: base_cyan,
textDecoration: 'underline',
textUnderlinePosition: 'under'
textDecoration: "underline",
textUnderlinePosition: "under",
},
{
tag: [t.url, t.escape, t.special(t.string)],
color: base_yellow
color: base_yellow,
},
{ tag: [t.meta], color: base03 },
{ tag: [t.comment], color: base02, fontStyle: 'italic' },
{ tag: [t.comment], color: base02, fontStyle: "italic" },
{ tag: t.monospace, color: base05 },
{ tag: t.strong, fontWeight: 'bold', color: base_red },
{ tag: t.emphasis, fontStyle: 'italic', color: base_lightgreen },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.heading, fontWeight: 'bold', color: base_yellow },
{ tag: t.heading1, fontWeight: 'bold', color: base_yellow },
{ tag: t.strong, fontWeight: "bold", color: base_red },
{ tag: t.emphasis, fontStyle: "italic", color: base_lightgreen },
{ tag: t.strikethrough, textDecoration: "line-through" },
{ tag: t.heading, fontWeight: "bold", color: base_yellow },
{ tag: t.heading1, fontWeight: "bold", color: base_yellow },
{
tag: [t.heading2, t.heading3, t.heading4],
fontWeight: 'bold',
color: base_yellow
fontWeight: "bold",
color: base_yellow,
},
{
tag: [t.heading5, t.heading6],
color: base_yellow
color: base_yellow,
},
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: base_cyan },
{
tag: [t.processingInstruction, t.inserted],
color: base_red
color: base_red,
},
{
tag: [t.contentSeparator],
color: base_cyan
color: base_cyan,
},
{ tag: t.invalid, color: base02, borderBottom: `1px dotted ${base_red}` }
])
{ tag: t.invalid, color: base02, borderBottom: `1px dotted ${base_red}` },
]);
/// Extension to enable the Material Dark theme (both the editor theme and
/// the highlight style).
export const materialDark: Extension = [
materialDarkTheme,
syntaxHighlighting(materialDarkHighlightStyle)
]
syntaxHighlighting(materialDarkHighlightStyle),
];