adding the slice and seqslice functions
This commit is contained in:
261
src/API.ts
261
src/API.ts
@ -2,10 +2,10 @@ import { Pitch, Chord, Rest, Event, cachedPattern } from "zifferjs";
|
||||
import { MidiConnection } from "./IO/MidiConnection";
|
||||
import { tryEvaluate } from "./Evaluator";
|
||||
import { DrunkWalk } from "./Utils/Drunk";
|
||||
import { LRUCache } from 'lru-cache';
|
||||
import { LRUCache } from "lru-cache";
|
||||
import { scale } from "./Scales";
|
||||
import { Editor } from "./main";
|
||||
import { Sound } from './Sound';
|
||||
import { Sound } from "./Sound";
|
||||
import {
|
||||
samples,
|
||||
initAudioOnFirstClick,
|
||||
@ -17,12 +17,11 @@ import {
|
||||
const cache = new LRUCache({ max: 1000, ttl: 1000 * 60 * 5 });
|
||||
|
||||
interface ControlChange {
|
||||
channel: number
|
||||
control: number
|
||||
value: number
|
||||
channel: number;
|
||||
control: number;
|
||||
value: number;
|
||||
}
|
||||
|
||||
|
||||
interface Pattern<T> {
|
||||
pattern: any[];
|
||||
options: {
|
||||
@ -42,7 +41,6 @@ Array.prototype.in = function <T>(this: T[], value: T): boolean {
|
||||
return this.includes(value);
|
||||
};
|
||||
|
||||
|
||||
async function loadSamples() {
|
||||
const ds = "https://raw.githubusercontent.com/felixroos/dough-samples/main/";
|
||||
return Promise.all([
|
||||
@ -57,8 +55,7 @@ async function loadSamples() {
|
||||
]);
|
||||
}
|
||||
|
||||
loadSamples()
|
||||
|
||||
loadSamples();
|
||||
|
||||
export class UserAPI {
|
||||
/**
|
||||
@ -88,7 +85,7 @@ export class UserAPI {
|
||||
* @returns the current AudioContext time (wall clock)
|
||||
*/
|
||||
return this.app.audioContext.currentTime;
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Mouse functions
|
||||
@ -99,14 +96,14 @@ export class UserAPI {
|
||||
* @returns The current x position of the mouse
|
||||
*/
|
||||
return this.app._mouseX;
|
||||
}
|
||||
};
|
||||
|
||||
public mouseY = (): number => {
|
||||
/**
|
||||
* @returns The current y position of the mouse
|
||||
*/
|
||||
return this.app._mouseY;
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Utility functions
|
||||
@ -125,7 +122,7 @@ export class UserAPI {
|
||||
this.app.universes[this.app.selected_universe].locals[arg]
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
s = this.script;
|
||||
|
||||
clear_script = (script: number): void => {
|
||||
@ -139,7 +136,7 @@ export class UserAPI {
|
||||
committed: "",
|
||||
evaluations: 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
cs = this.clear_script;
|
||||
|
||||
copy_script = (from: number, to: number): void => {
|
||||
@ -151,7 +148,7 @@ export class UserAPI {
|
||||
*/
|
||||
this.app.universes[this.app.selected_universe].locals[to] =
|
||||
this.app.universes[this.app.selected_universe].locals[from];
|
||||
}
|
||||
};
|
||||
cps = this.copy_script;
|
||||
|
||||
// =============================================================
|
||||
@ -166,7 +163,7 @@ export class UserAPI {
|
||||
*/
|
||||
console.log(this.MidiConnection.listMidiOutputs());
|
||||
return this.MidiConnection.midiOutputs;
|
||||
}
|
||||
};
|
||||
|
||||
public midi_output = (outputName: string): void => {
|
||||
/**
|
||||
@ -179,9 +176,12 @@ export class UserAPI {
|
||||
} else {
|
||||
this.MidiConnection.switchMidiOutput(outputName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public note = (note: number, options: { [key: string]: number } = {}): void => {
|
||||
public note = (
|
||||
note: number,
|
||||
options: { [key: string]: number } = {}
|
||||
): void => {
|
||||
/**
|
||||
* Sends a MIDI note to the current MIDI output.
|
||||
*
|
||||
@ -193,7 +193,7 @@ export class UserAPI {
|
||||
const velocity = options.velocity ? options.velocity : 100;
|
||||
const duration = options.duration ? options.duration : 0.5;
|
||||
this.MidiConnection.sendMidiNote(note, channel, velocity, duration);
|
||||
}
|
||||
};
|
||||
|
||||
public sysex = (data: Array<number>): void => {
|
||||
/**
|
||||
@ -202,7 +202,7 @@ export class UserAPI {
|
||||
* @param data - The sysex data to send
|
||||
*/
|
||||
this.MidiConnection.sendSysExMessage(data);
|
||||
}
|
||||
};
|
||||
|
||||
public pitch_bend = (value: number, channel: number): void => {
|
||||
/**
|
||||
@ -214,7 +214,7 @@ export class UserAPI {
|
||||
* @returns The value of the pitch bend
|
||||
*/
|
||||
this.MidiConnection.sendPitchBend(value, channel);
|
||||
}
|
||||
};
|
||||
|
||||
public program_change = (program: number, channel: number): void => {
|
||||
/**
|
||||
@ -224,17 +224,20 @@ export class UserAPI {
|
||||
* @param channel - The MIDI channel to send the program change on
|
||||
*/
|
||||
this.MidiConnection.sendProgramChange(program, channel);
|
||||
}
|
||||
};
|
||||
|
||||
public midi_clock = (): void => {
|
||||
/**
|
||||
* Sends a MIDI clock to the current MIDI output.
|
||||
*/
|
||||
this.MidiConnection.sendMidiClock();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public control_change = ({control= 20, value= 0, channel=0 }: ControlChange): void => {
|
||||
public control_change = ({
|
||||
control = 20,
|
||||
value = 0,
|
||||
channel = 0,
|
||||
}: ControlChange): void => {
|
||||
/**
|
||||
* Sends a MIDI control change to the current MIDI output.
|
||||
*
|
||||
@ -242,25 +245,26 @@ export class UserAPI {
|
||||
* @param value - The value of the control
|
||||
*/
|
||||
this.MidiConnection.sendMidiControlChange(control, value, channel);
|
||||
}
|
||||
};
|
||||
|
||||
public midi_panic = (): void => {
|
||||
/**
|
||||
* Sends a MIDI panic message to the current MIDI output.
|
||||
*/
|
||||
this.MidiConnection.panic();
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Ziffers related functions
|
||||
// =============================================================
|
||||
|
||||
public zn = (input: string,
|
||||
options: {[key: string]: string|number} = {}): Event => {
|
||||
public zn = (
|
||||
input: string,
|
||||
options: { [key: string]: string | number } = {}
|
||||
): Event => {
|
||||
const pattern = cachedPattern(input, options);
|
||||
//@ts-ignore
|
||||
if (pattern.hasStarted()) {
|
||||
|
||||
const event = pattern.peek();
|
||||
|
||||
// Check if event is modified
|
||||
@ -271,12 +275,23 @@ export class UserAPI {
|
||||
|
||||
if (node instanceof Pitch) {
|
||||
if (node.bend) this.MidiConnection.sendPitchBend(node.bend, channel);
|
||||
this.MidiConnection.sendMidiNote(node.note!, channel, velocity, sustain);
|
||||
this.MidiConnection.sendMidiNote(
|
||||
node.note!,
|
||||
channel,
|
||||
velocity,
|
||||
sustain
|
||||
);
|
||||
if (node.bend) this.MidiConnection.sendPitchBend(8192, channel);
|
||||
} else if (node instanceof Chord) {
|
||||
node.pitches.forEach((pitch: Pitch) => {
|
||||
if(pitch.bend) this.MidiConnection.sendPitchBend(pitch.bend, channel);
|
||||
this.MidiConnection.sendMidiNote(pitch.note!, channel, velocity, sustain);
|
||||
if (pitch.bend)
|
||||
this.MidiConnection.sendPitchBend(pitch.bend, channel);
|
||||
this.MidiConnection.sendMidiNote(
|
||||
pitch.note!,
|
||||
channel,
|
||||
velocity,
|
||||
sustain
|
||||
);
|
||||
if (pitch.bend) this.MidiConnection.sendPitchBend(8192, channel);
|
||||
});
|
||||
} else if (node instanceof Rest) {
|
||||
@ -288,7 +303,7 @@ export class UserAPI {
|
||||
}
|
||||
//@ts-ignore
|
||||
return pattern.next();
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Iterator related functions
|
||||
@ -339,7 +354,7 @@ export class UserAPI {
|
||||
|
||||
// Return current iterator value
|
||||
return this.iterators[name].value;
|
||||
}
|
||||
};
|
||||
$ = this.iterator;
|
||||
|
||||
// =============================================================
|
||||
@ -361,7 +376,7 @@ export class UserAPI {
|
||||
}
|
||||
this._drunk.step();
|
||||
return this._drunk.getPosition();
|
||||
}
|
||||
};
|
||||
|
||||
public drunk_max = (max: number) => {
|
||||
/**
|
||||
@ -370,7 +385,7 @@ export class UserAPI {
|
||||
* @param max - The maximum value of the drunk mechanism
|
||||
*/
|
||||
this._drunk.max = max;
|
||||
}
|
||||
};
|
||||
|
||||
public drunk_min = (min: number) => {
|
||||
/**
|
||||
@ -379,7 +394,7 @@ export class UserAPI {
|
||||
* @param min - The minimum value of the drunk mechanism
|
||||
*/
|
||||
this._drunk.min = min;
|
||||
}
|
||||
};
|
||||
|
||||
public drunk_wrap = (wrap: boolean) => {
|
||||
/**
|
||||
@ -388,7 +403,7 @@ export class UserAPI {
|
||||
* @param wrap - Whether the drunk mechanism should wrap around
|
||||
*/
|
||||
this._drunk.toggleWrap(wrap);
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Variable related functions
|
||||
@ -408,7 +423,7 @@ export class UserAPI {
|
||||
this.variables[a] = b;
|
||||
return this.variables[a];
|
||||
}
|
||||
}
|
||||
};
|
||||
v = this.variable;
|
||||
|
||||
public delete_variable = (name: string): void => {
|
||||
@ -418,7 +433,7 @@ export class UserAPI {
|
||||
* @param name - The name of the variable to delete
|
||||
*/
|
||||
delete this.variables[name];
|
||||
}
|
||||
};
|
||||
dv = this.delete_variable;
|
||||
|
||||
public clear_variables = (): void => {
|
||||
@ -430,7 +445,7 @@ export class UserAPI {
|
||||
* Use with caution.
|
||||
*/
|
||||
this.variables = {};
|
||||
}
|
||||
};
|
||||
cv = this.clear_variables;
|
||||
|
||||
// =============================================================
|
||||
@ -448,6 +463,20 @@ export class UserAPI {
|
||||
return btoa(JSON.stringify(pattern));
|
||||
}
|
||||
|
||||
public slice = (chunk: number): boolean => {
|
||||
const time_pos = this.epulse();
|
||||
const current_chunk = Math.floor(time_pos / chunk);
|
||||
return current_chunk % 2 === 0;
|
||||
};
|
||||
|
||||
public seqslice = (...args: any): any => {
|
||||
const chunk_size = args[0]; // Get the first argument (chunk size)
|
||||
const elements = args.slice(1); // Get the rest of the arguments as an array
|
||||
const timepos = epulse();
|
||||
const slice_count = Math.floor(timepos / chunk_size);
|
||||
return elements[slice_count % elements.length];
|
||||
};
|
||||
|
||||
public seqmod = (...input: any[]) => {
|
||||
if (cache.has(this._sequence_key_generator(input))) {
|
||||
let sequence = cache.get(
|
||||
@ -458,48 +487,49 @@ export class UserAPI {
|
||||
|
||||
if (sequence.options.currentIteration === sequence.options.nextTarget) {
|
||||
sequence.options.index++;
|
||||
sequence.options.nextTarget = input[sequence.options.index % input.length];
|
||||
sequence.options.nextTarget =
|
||||
input[sequence.options.index % input.length];
|
||||
sequence.options.currentIteration = 0;
|
||||
}
|
||||
|
||||
cache.set(this._sequence_key_generator(input), {
|
||||
pattern: input as any[],
|
||||
options: sequence.options
|
||||
options: sequence.options,
|
||||
});
|
||||
|
||||
return sequence.options.currentIteration === 0;
|
||||
|
||||
} else {
|
||||
|
||||
let pattern_options = {
|
||||
index: -1, nextTarget: this.app.clock.ticks_before_new_bar,
|
||||
currentIteration: 0
|
||||
index: -1,
|
||||
nextTarget: this.app.clock.ticks_before_new_bar,
|
||||
currentIteration: 0,
|
||||
};
|
||||
if (typeof input[input.length - 1] === "object") {
|
||||
pattern_options = {
|
||||
...input.pop(),
|
||||
...pattern_options as object
|
||||
...(pattern_options as object),
|
||||
};
|
||||
}
|
||||
|
||||
// pattern_options.currentIteration++;
|
||||
// TEST
|
||||
pattern_options.nextTarget = this.app.clock.ticks_before_new_bar
|
||||
pattern_options.nextTarget = this.app.clock.ticks_before_new_bar;
|
||||
|
||||
if (pattern_options.currentIteration === pattern_options.nextTarget) {
|
||||
pattern_options.index++;
|
||||
pattern_options.nextTarget = input[pattern_options.index % input.length];
|
||||
pattern_options.nextTarget =
|
||||
input[pattern_options.index % input.length];
|
||||
pattern_options.currentIteration = 0;
|
||||
}
|
||||
|
||||
cache.set(this._sequence_key_generator(input), {
|
||||
pattern: input as any[],
|
||||
options: pattern_options
|
||||
options: pattern_options,
|
||||
});
|
||||
|
||||
return pattern_options.currentIteration === 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public seq = (...input: any[]) => {
|
||||
/**
|
||||
@ -516,24 +546,24 @@ export class UserAPI {
|
||||
* @returns A value in a sequence stored using an LRU Cache
|
||||
*/
|
||||
if (cache.has(this._sequence_key_generator(input))) {
|
||||
let sequence = cache.get(this._sequence_key_generator(input)) as Pattern<any>;
|
||||
let sequence = cache.get(
|
||||
this._sequence_key_generator(input)
|
||||
) as Pattern<any>;
|
||||
sequence.options.index += 1;
|
||||
cache.set(this._sequence_key_generator(input), sequence);
|
||||
return sequence.pattern[
|
||||
sequence.options.index % sequence.pattern.length
|
||||
];
|
||||
return sequence.pattern[sequence.options.index % sequence.pattern.length];
|
||||
} else {
|
||||
let pattern_options = { index: 0 };
|
||||
if (typeof input[input.length - 1] === "object") {
|
||||
pattern_options = { ...input.pop(), ...pattern_options as object };
|
||||
pattern_options = { ...input.pop(), ...(pattern_options as object) };
|
||||
}
|
||||
cache.set(this._sequence_key_generator(input), {
|
||||
pattern: input as any[],
|
||||
options: pattern_options
|
||||
options: pattern_options,
|
||||
});
|
||||
return cache.get(this._sequence_key_generator(input));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pick = <T>(...array: T[]): T => {
|
||||
/**
|
||||
@ -542,7 +572,7 @@ export class UserAPI {
|
||||
* @param array - The array of values to pick from
|
||||
*/
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
};
|
||||
|
||||
seqbeat = <T>(...array: T[]): T => {
|
||||
/**
|
||||
@ -551,7 +581,7 @@ export class UserAPI {
|
||||
* @param array - The array of values to pick from
|
||||
*/
|
||||
return array[this.ebeat() % array.length];
|
||||
}
|
||||
};
|
||||
|
||||
mel = <T>(iterator: number, array: T[]): T => {
|
||||
/**
|
||||
@ -561,7 +591,7 @@ export class UserAPI {
|
||||
* @param array - The array of values to pick from
|
||||
*/
|
||||
return array[iterator % array.length];
|
||||
}
|
||||
};
|
||||
|
||||
seqbar = <T>(...array: T[]): T => {
|
||||
/**
|
||||
@ -570,7 +600,7 @@ export class UserAPI {
|
||||
* @param array - The array of values to pick from
|
||||
*/
|
||||
return array[(this.app.clock.time_position.bar + 1) % array.length];
|
||||
}
|
||||
};
|
||||
|
||||
seqpulse = <T>(...array: T[]): T => {
|
||||
/**
|
||||
@ -579,7 +609,7 @@ export class UserAPI {
|
||||
* @param array - The array of values to pick from
|
||||
*/
|
||||
return array[this.app.clock.time_position.pulse % array.length];
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Randomness functions
|
||||
@ -594,7 +624,7 @@ export class UserAPI {
|
||||
* @returns A random integer between min and max
|
||||
*/
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
};
|
||||
|
||||
rand = (min: number, max: number): number => {
|
||||
/**
|
||||
@ -605,7 +635,7 @@ export class UserAPI {
|
||||
* @returns A random float between min and max
|
||||
*/
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
};
|
||||
rI = this.randI;
|
||||
r = this.rand;
|
||||
|
||||
@ -631,7 +661,7 @@ export class UserAPI {
|
||||
}
|
||||
});
|
||||
return closest;
|
||||
}
|
||||
};
|
||||
quant = this.quantize;
|
||||
|
||||
public clamp = (value: number, min: number, max: number): number => {
|
||||
@ -644,7 +674,7 @@ export class UserAPI {
|
||||
* @returns A value clamped between min and max
|
||||
*/
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
};
|
||||
cmp = this.clamp;
|
||||
|
||||
// =============================================================
|
||||
@ -663,7 +693,7 @@ export class UserAPI {
|
||||
if (n < 1 || n > 500) console.log(`Setting bpm to ${n}`);
|
||||
this.app.clock.bpm = n;
|
||||
return n;
|
||||
}
|
||||
};
|
||||
tempo = this.bpm;
|
||||
|
||||
public bpb = (n?: number): number => {
|
||||
@ -678,7 +708,7 @@ export class UserAPI {
|
||||
if (n < 1) console.log(`Setting bpb to ${n}`);
|
||||
this.app.clock.time_signature[0] = n;
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
public ppqn = (n?: number) => {
|
||||
/**
|
||||
@ -689,7 +719,7 @@ export class UserAPI {
|
||||
if (n < 1) console.log(`Setting ppqn to ${n}`);
|
||||
this.app.clock.ppqn = n;
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
public time_signature = (numerator: number, denominator: number): void => {
|
||||
/**
|
||||
@ -700,7 +730,7 @@ export class UserAPI {
|
||||
* @returns The current time signature
|
||||
*/
|
||||
this.app.clock.time_signature = [numerator, denominator];
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Probability functions
|
||||
@ -713,7 +743,7 @@ export class UserAPI {
|
||||
* @returns True 10% of the time
|
||||
*/
|
||||
return Math.random() > 0.9;
|
||||
}
|
||||
};
|
||||
|
||||
public sometimes = (): boolean => {
|
||||
/**
|
||||
@ -722,7 +752,7 @@ export class UserAPI {
|
||||
* @returns True 50% of the time
|
||||
*/
|
||||
return Math.random() > 0.5;
|
||||
}
|
||||
};
|
||||
|
||||
public rarely = (): boolean => {
|
||||
/**
|
||||
@ -731,7 +761,7 @@ export class UserAPI {
|
||||
* @returns True 25% of the time
|
||||
*/
|
||||
return Math.random() > 0.75;
|
||||
}
|
||||
};
|
||||
|
||||
public often = (): boolean => {
|
||||
/**
|
||||
@ -740,7 +770,7 @@ export class UserAPI {
|
||||
* @returns True 75% of the time
|
||||
*/
|
||||
return Math.random() > 0.25;
|
||||
}
|
||||
};
|
||||
|
||||
public almostAlways = (): boolean => {
|
||||
/**
|
||||
@ -749,7 +779,7 @@ export class UserAPI {
|
||||
* @returns True 90% of the time
|
||||
*/
|
||||
return Math.random() > 0.1;
|
||||
}
|
||||
};
|
||||
|
||||
public dice = (sides: number): number => {
|
||||
/**
|
||||
@ -759,7 +789,7 @@ export class UserAPI {
|
||||
* @returns The value of a dice roll with n sides
|
||||
*/
|
||||
return Math.floor(Math.random() * sides) + 1;
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Iterator functions (for loops, with evaluation count, etc...)
|
||||
@ -773,12 +803,11 @@ export class UserAPI {
|
||||
*/
|
||||
if (n !== undefined) {
|
||||
this.app.universes[this.app.selected_universe].global.evaluations = n;
|
||||
return this.app.universes[this.app.selected_universe]
|
||||
return this.app.universes[this.app.selected_universe];
|
||||
}
|
||||
return this.app.universes[this.app.selected_universe]
|
||||
.global
|
||||
return this.app.universes[this.app.selected_universe].global
|
||||
.evaluations as number;
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Time markers
|
||||
@ -791,7 +820,7 @@ export class UserAPI {
|
||||
* @returns The current bar number
|
||||
*/
|
||||
return this.app.clock.time_position.bar;
|
||||
}
|
||||
};
|
||||
|
||||
tick = (): number => {
|
||||
/**
|
||||
@ -800,7 +829,7 @@ export class UserAPI {
|
||||
* @returns The current tick number
|
||||
*/
|
||||
return this.app.clock.tick;
|
||||
}
|
||||
};
|
||||
|
||||
pulse = (): number => {
|
||||
/**
|
||||
@ -809,7 +838,7 @@ export class UserAPI {
|
||||
* @returns The current pulse number
|
||||
*/
|
||||
return this.app.clock.time_position.pulse;
|
||||
}
|
||||
};
|
||||
|
||||
beat = (): number => {
|
||||
/**
|
||||
@ -818,28 +847,28 @@ export class UserAPI {
|
||||
* @returns The current beat number
|
||||
*/
|
||||
return this.app.clock.time_position.beat;
|
||||
}
|
||||
};
|
||||
|
||||
ebeat = (): number => {
|
||||
/**
|
||||
* Returns the current beat number since the origin of time
|
||||
*/
|
||||
return this.app.clock.beats_since_origin;
|
||||
}
|
||||
};
|
||||
|
||||
epulse = (): number => {
|
||||
/**
|
||||
* Returns the current number of pulses elapsed since origin of time
|
||||
*/
|
||||
return this.app.clock.pulses_since_origin;
|
||||
}
|
||||
};
|
||||
|
||||
onbar = (n: number, ...bar: number[]): boolean => {
|
||||
// n is acting as a modulo on the bar number
|
||||
const bar_list = [...Array(n).keys()].map((i) => i + 1);
|
||||
console.log(bar.some((b) => bar_list.includes(b % n)));
|
||||
return bar.some((b) => bar_list.includes(b % n));
|
||||
}
|
||||
};
|
||||
|
||||
onbeat = (...beat: number[]): boolean => {
|
||||
/**
|
||||
@ -863,7 +892,7 @@ export class UserAPI {
|
||||
);
|
||||
});
|
||||
return final_pulses.some((p) => p == true);
|
||||
}
|
||||
};
|
||||
|
||||
stop = (): void => {
|
||||
/**
|
||||
@ -874,7 +903,7 @@ export class UserAPI {
|
||||
*/
|
||||
this.app.clock.pause();
|
||||
this.app.setButtonHighlighting("pause", true);
|
||||
}
|
||||
};
|
||||
silence = this.stop;
|
||||
hush = this.stop;
|
||||
|
||||
@ -886,7 +915,7 @@ export class UserAPI {
|
||||
* @returns True p% of the time
|
||||
*/
|
||||
return Math.random() * 100 < p;
|
||||
}
|
||||
};
|
||||
|
||||
toss = (): boolean => {
|
||||
/**
|
||||
@ -900,7 +929,7 @@ export class UserAPI {
|
||||
* @see almostNever
|
||||
*/
|
||||
return Math.random() > 0.5;
|
||||
}
|
||||
};
|
||||
|
||||
min = (...values: number[]): number => {
|
||||
/**
|
||||
@ -910,7 +939,7 @@ export class UserAPI {
|
||||
* @returns The minimum value of the list of numbers
|
||||
*/
|
||||
return Math.min(...values);
|
||||
}
|
||||
};
|
||||
|
||||
max = (...values: number[]): number => {
|
||||
/**
|
||||
@ -920,7 +949,7 @@ export class UserAPI {
|
||||
* @returns The maximum value of the list of numbers
|
||||
*/
|
||||
return Math.max(...values);
|
||||
}
|
||||
};
|
||||
|
||||
limit = (value: number, min: number, max: number): number => {
|
||||
/**
|
||||
@ -932,7 +961,7 @@ export class UserAPI {
|
||||
* @returns The limited value
|
||||
*/
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
};
|
||||
|
||||
delay = (ms: number, func: Function): void => {
|
||||
/**
|
||||
@ -943,7 +972,7 @@ export class UserAPI {
|
||||
* @returns The current time signature
|
||||
*/
|
||||
setTimeout(func, ms);
|
||||
}
|
||||
};
|
||||
|
||||
delayr = (ms: number, nb: number, func: Function): void => {
|
||||
/**
|
||||
@ -958,7 +987,7 @@ export class UserAPI {
|
||||
list.forEach((ms, _) => {
|
||||
setTimeout(func, ms);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
mod = (...pulse: number[]): boolean => {
|
||||
/**
|
||||
@ -968,7 +997,7 @@ export class UserAPI {
|
||||
* @returns True if the current pulse is a modulo of any of the given pulses
|
||||
*/
|
||||
return pulse.some((p) => this.app.clock.time_position.pulse % p === 0);
|
||||
}
|
||||
};
|
||||
|
||||
modbar = (...bar: number[]): boolean => {
|
||||
/**
|
||||
@ -979,7 +1008,7 @@ export class UserAPI {
|
||||
*
|
||||
*/
|
||||
return bar.some((b) => this.app.clock.time_position.bar % b === 0);
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Rythmic generators
|
||||
@ -1001,7 +1030,7 @@ export class UserAPI {
|
||||
* @returns boolean value based on the euclidian sequence
|
||||
*/
|
||||
return this._euclidean_cycle(pulses, length, rotate)[iterator % length];
|
||||
}
|
||||
};
|
||||
|
||||
_euclidean_cycle(
|
||||
pulses: number,
|
||||
@ -1037,7 +1066,7 @@ export class UserAPI {
|
||||
let convert: string = n.toString(2);
|
||||
let tobin: boolean[] = convert.split("").map((x: string) => x === "1");
|
||||
return tobin[iterator % tobin.length];
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Low Frequency Oscillators
|
||||
@ -1063,7 +1092,7 @@ export class UserAPI {
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
sine = (freq: number = 1, offset: number = 0): number => {
|
||||
/**
|
||||
@ -1076,7 +1105,7 @@ export class UserAPI {
|
||||
return (
|
||||
Math.sin(this.app.clock.ctx.currentTime * Math.PI * 2 * freq) + offset
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
usine = (freq: number = 1, offset: number = 0): number => {
|
||||
/**
|
||||
@ -1088,7 +1117,7 @@ export class UserAPI {
|
||||
* @see sine
|
||||
*/
|
||||
return (this.sine(freq, offset) + 1) / 2;
|
||||
}
|
||||
};
|
||||
|
||||
saw = (freq: number = 1, offset: number = 0): number => {
|
||||
/**
|
||||
@ -1103,7 +1132,7 @@ export class UserAPI {
|
||||
* @see noise
|
||||
*/
|
||||
return ((this.app.clock.ctx.currentTime * freq) % 1) * 2 - 1 + offset;
|
||||
}
|
||||
};
|
||||
|
||||
usaw = (freq: number = 1, offset: number = 0): number => {
|
||||
/**
|
||||
@ -1115,7 +1144,7 @@ export class UserAPI {
|
||||
* @see saw
|
||||
*/
|
||||
return (this.saw(freq, offset) + 1) / 2;
|
||||
}
|
||||
};
|
||||
|
||||
triangle = (freq: number = 1, offset: number = 0): number => {
|
||||
/**
|
||||
@ -1128,7 +1157,7 @@ export class UserAPI {
|
||||
* @see noise
|
||||
*/
|
||||
return Math.abs(this.saw(freq, offset)) * 2 - 1;
|
||||
}
|
||||
};
|
||||
|
||||
utriangle = (freq: number = 1, offset: number = 0): number => {
|
||||
/**
|
||||
@ -1140,7 +1169,7 @@ export class UserAPI {
|
||||
* @see triangle
|
||||
*/
|
||||
return (this.triangle(freq, offset) + 1) / 2;
|
||||
}
|
||||
};
|
||||
|
||||
square = (freq: number = 1, offset: number = 0): number => {
|
||||
/**
|
||||
@ -1153,7 +1182,7 @@ export class UserAPI {
|
||||
* @see noise
|
||||
*/
|
||||
return this.saw(freq, offset) > 0 ? 1 : -1;
|
||||
}
|
||||
};
|
||||
|
||||
usquare = (freq: number = 1, offset: number = 0): number => {
|
||||
/**
|
||||
@ -1165,7 +1194,7 @@ export class UserAPI {
|
||||
* @see square
|
||||
*/
|
||||
return (this.square(freq, offset) + 1) / 2;
|
||||
}
|
||||
};
|
||||
|
||||
noise = (): number => {
|
||||
/**
|
||||
@ -1179,7 +1208,7 @@ export class UserAPI {
|
||||
* @see noise
|
||||
*/
|
||||
return Math.random() * 2 - 1;
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Math functions
|
||||
@ -1193,7 +1222,7 @@ export class UserAPI {
|
||||
|
||||
sound = (sound: string) => {
|
||||
return new Sound(sound, this.app);
|
||||
}
|
||||
};
|
||||
|
||||
samples = samples;
|
||||
|
||||
@ -1206,7 +1235,5 @@ export class UserAPI {
|
||||
// TODO: Implement this. This function should change the rate at which the global script
|
||||
// is evaluated. This is useful for slowing down the script, or speeding it up. The default
|
||||
// would be 1.0, which is the current rate (very speedy).
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@ -284,9 +284,7 @@ const about: string = `
|
||||
|
||||
## The Topos Project
|
||||
|
||||
Topos is an experimental web based algorithmic sequencer programmed by **BuboBubo** (Raphaël Forment) and **Amiika** (Miika Alonen). It is written using [TypeScript](https://google.fr) and [Vite](https://google.fr). Many thanks to Felix Roos for making the [Superdough](https://www.npmjs.com/package/superdough) audio backend available for experimentation.
|
||||
|
||||
This project is based on the [Monome Teletype](https://monome.org) by Brian Crabtree. We hope to follow and honor the same spirit of sharing and experimentation. How much can the Teletype be extended while staying accessible and installation-free?
|
||||
Topos is an experimental web based algorithmic sequencer programmed by **BuboBubo** ([Raphaël Forment](https://raphaelforment.fr) and **Amiika** ([Miika Alonen](https//github.com/amiika). It is written using [TypeScript](https://google.fr) and [Vite](https://google.fr). Many thanks to Felix Roos for making the [Superdough](https://www.npmjs.com/package/superdough) audio backend available for experimentation. This project is based on the [Monome Teletype](https://monome.org) by [Brian Crabtree](https://nnnnnnnn.co/) and [Kelli Cain](https://kellicain.com/). We hope to follow and honor the same spirit of sharing and experimentation. How much can the Teletype be extended while staying accessible and installation-free?
|
||||
|
||||
## About Live Coding
|
||||
|
||||
@ -294,9 +292,7 @@ This project is based on the [Monome Teletype](https://monome.org) by Brian Crab
|
||||
|
||||
## Free and open-source software
|
||||
|
||||
Topos is a free and open-source software distributed under [GPL-3.0](https://github.com/Bubobubobubobubo/Topos/blob/main/LICENSE) licence.
|
||||
We welcome all contributions and ideas. You can find the source code on [GitHub](https://github.com/Bubobubobubobubo/topos).
|
||||
You can also join us on [Discord](https://discord.gg/8Q2QV6Z6) to discuss about the project and live coding in general.
|
||||
Topos is a free and open-source software distributed under [GPL-3.0](https://github.com/Bubobubobubobubo/Topos/blob/main/LICENSE) licence. We welcome all contributions and ideas. You can find the source code on [GitHub](https://github.com/Bubobubobubobubo/topos). You can also join us on [Discord](https://discord.gg/8Q2QV6Z6) to discuss about the project and live coding in general.
|
||||
|
||||
**Have fun!**
|
||||
`;
|
||||
@ -304,7 +300,7 @@ You can also join us on [Discord](https://discord.gg/8Q2QV6Z6) to discuss about
|
||||
const code: string = `
|
||||
# Code
|
||||
|
||||
Topos is using the [JavaScript](https://en.wikipedia.org/wiki/JavaScript) syntax because it lives in a web browser where JS is the default programming language. It is also a language that you can learn to speak quite fast if you are already familiar with other programming lanugages. You are not going to write a lot of code anyway but familiarity with the language can help. Here are some good resources:
|
||||
Topos is using the [JavaScript](https://en.wikipedia.org/wiki/JavaScript) syntax because it lives in a web browser where JS is the default programming language. It is also a language that you can learn to speak quite fast if you are already familiar with other programming languages. You are not going to write a lot of code anyway but familiarity with the language can help. Here are some good resources:
|
||||
|
||||
- [MDN (Mozilla Web Docs)](https://developer.mozilla.org/): it covers pretty much anything and is considered to be a reliable source to learn how the web currently works. We use it quite a lot to develop Topos.
|
||||
|
||||
@ -319,7 +315,9 @@ You **do not need to have any prior knowledge of programming** to use Topos. It
|
||||
The code you enter in any of the scripts is evaluated in strict mode. This tells your browser that the code you run can be optimized quite agressively. We need this because by default, **the global script is evaluated 48 times per beat**. It also means that you can crash at the speed of light :smile:. The local and initialisation scripts are evaluated on demand, one run at a time. There are some things to keep in mind:
|
||||
|
||||
- **about variables:** the state of your variables is not kept between iterations. If you write <icode>let a = 2</icode> and change the value later on, the value will be reset to <icode>2</icode> after each run! There are other ways to deal with variables and to share variables between scripts! Some variables like **iterators** can keep their state between iterations because they are saved **with the file itself**.
|
||||
- **about errors and printing:** your code will crash! Don't worry, it will hopefully try to crash in the most gracious way possible. To check if your code is erroring, you will have to open the dev console with ${key_shortcut( "Ctrl + Shift + I")}. You cannot directly use <icode>console.log('hello, world')</icode> in the interface. You will have to open the console as well to see your messages being printed there!
|
||||
- **about errors and printing:** your code will crash! Don't worry, it will hopefully try to crash in the most gracious way possible. To check if your code is erroring, you will have to open the dev console with ${key_shortcut(
|
||||
"Ctrl + Shift + I"
|
||||
)}. You cannot directly use <icode>console.log('hello, world')</icode> in the interface. You will have to open the console as well to see your messages being printed there!
|
||||
`;
|
||||
|
||||
const functions: string = `
|
||||
|
||||
Reference in New Issue
Block a user