Rewrite part of evaluation logic, run prettier
This commit is contained in:
37
src/API.ts
37
src/API.ts
@ -41,16 +41,28 @@ interface ControlChange {
|
|||||||
export async function loadSamples() {
|
export async function loadSamples() {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
initAudioOnFirstClick(),
|
initAudioOnFirstClick(),
|
||||||
samples("github:tidalcycles/Dirt-Samples/master", undefined, { tag: "Tidal" }).then(() =>
|
samples("github:tidalcycles/Dirt-Samples/master", undefined, {
|
||||||
registerSynthSounds()
|
tag: "Tidal",
|
||||||
),
|
}).then(() => registerSynthSounds()),
|
||||||
registerZZFXSounds(),
|
registerZZFXSounds(),
|
||||||
samples(drums, "github:ritchse/tidal-drum-machines/main/machines/", { tag: "Machines" }),
|
samples(drums, "github:ritchse/tidal-drum-machines/main/machines/", {
|
||||||
samples("github:Bubobubobubobubo/Dough-Fox/main", undefined, { tag: "FoxDot" }),
|
tag: "Machines",
|
||||||
samples("github:Bubobubobubobubo/Dough-Samples/main", undefined, { tag: "Pack" }),
|
}),
|
||||||
samples("github:Bubobubobubobubo/Dough-Amiga/main", undefined, { tag: "Amiga" }),
|
samples("github:Bubobubobubobubo/Dough-Fox/main", undefined, {
|
||||||
samples("github:Bubobubobubobubo/Dough-Amen/main", undefined, { tag: "Amen" }),
|
tag: "FoxDot",
|
||||||
samples("github:Bubobubobubobubo/Dough-Waveforms/main", undefined, { tag: "Waveforms" }),
|
}),
|
||||||
|
samples("github:Bubobubobubobubo/Dough-Samples/main", undefined, {
|
||||||
|
tag: "Pack",
|
||||||
|
}),
|
||||||
|
samples("github:Bubobubobubobubo/Dough-Amiga/main", undefined, {
|
||||||
|
tag: "Amiga",
|
||||||
|
}),
|
||||||
|
samples("github:Bubobubobubobubo/Dough-Amen/main", undefined, {
|
||||||
|
tag: "Amen",
|
||||||
|
}),
|
||||||
|
samples("github:Bubobubobubobubo/Dough-Waveforms/main", undefined, {
|
||||||
|
tag: "Waveforms",
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1901,10 +1913,13 @@ export class UserAPI {
|
|||||||
// =============================================================
|
// =============================================================
|
||||||
|
|
||||||
register = (name: string, operation: EventOperation<AbstractEvent>): void => {
|
register = (name: string, operation: EventOperation<AbstractEvent>): void => {
|
||||||
AbstractEvent.prototype[name] = function(this: AbstractEvent, ...args: any[]) {
|
AbstractEvent.prototype[name] = function (
|
||||||
|
this: AbstractEvent,
|
||||||
|
...args: any[]
|
||||||
|
) {
|
||||||
return operation(this, ...args);
|
return operation(this, ...args);
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
public shuffle = <T>(array: T[]): T[] => {
|
public shuffle = <T>(array: T[]): T[] => {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export const drawCircle = (
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
radius: number,
|
radius: number,
|
||||||
color: string
|
color: string,
|
||||||
): void => {
|
): void => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const canvas: HTMLCanvasElement = app.interface.feedback;
|
const canvas: HTMLCanvasElement = app.interface.feedback;
|
||||||
@ -36,7 +36,7 @@ export const drawCircle = (
|
|||||||
export const blinkScript = (
|
export const blinkScript = (
|
||||||
app: Editor,
|
app: Editor,
|
||||||
script: "local" | "global" | "init",
|
script: "local" | "global" | "init",
|
||||||
no?: number
|
no?: number,
|
||||||
) => {
|
) => {
|
||||||
if (no !== undefined && no < 1 && no > 9) return;
|
if (no !== undefined && no < 1 && no > 9) return;
|
||||||
const blinkDuration =
|
const blinkDuration =
|
||||||
@ -55,7 +55,7 @@ export const blinkScript = (
|
|||||||
horizontalOffset + shift,
|
horizontalOffset + shift,
|
||||||
app.interface.feedback.clientHeight - 15,
|
app.interface.feedback.clientHeight - 15,
|
||||||
8,
|
8,
|
||||||
"#fdba74"
|
"#fdba74",
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ export const blinkScript = (
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
(app.interface.feedback as HTMLCanvasElement).width,
|
(app.interface.feedback as HTMLCanvasElement).width,
|
||||||
(app.interface.feedback as HTMLCanvasElement).height
|
(app.interface.feedback as HTMLCanvasElement).height,
|
||||||
);
|
);
|
||||||
}, blinkDuration);
|
}, blinkDuration);
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ let lastRenderTime: number = 0;
|
|||||||
*/
|
*/
|
||||||
export const runOscilloscope = (
|
export const runOscilloscope = (
|
||||||
canvas: HTMLCanvasElement,
|
canvas: HTMLCanvasElement,
|
||||||
app: Editor
|
app: Editor,
|
||||||
): void => {
|
): void => {
|
||||||
let config = app.osc;
|
let config = app.osc;
|
||||||
let analyzer = getAnalyser(config.fftSize);
|
let analyzer = getAnalyser(config.fftSize);
|
||||||
@ -155,7 +155,7 @@ export const runOscilloscope = (
|
|||||||
width: number,
|
width: number,
|
||||||
height: number,
|
height: number,
|
||||||
offset_height: number,
|
offset_height: number,
|
||||||
offset_width: number
|
offset_width: number,
|
||||||
) {
|
) {
|
||||||
const maxFPS = 30;
|
const maxFPS = 30;
|
||||||
const now = performance.now();
|
const now = performance.now();
|
||||||
@ -169,10 +169,12 @@ export const runOscilloscope = (
|
|||||||
canvasCtx.clearRect(0, 0, width, height);
|
canvasCtx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
const performanceFactor = 1;
|
const performanceFactor = 1;
|
||||||
const reducedDataSize = Math.floor(freqDataArray.length * performanceFactor);
|
const reducedDataSize = Math.floor(
|
||||||
|
freqDataArray.length * performanceFactor,
|
||||||
|
);
|
||||||
const numBars = Math.min(
|
const numBars = Math.min(
|
||||||
reducedDataSize,
|
reducedDataSize,
|
||||||
app.osc.orientation === "horizontal" ? width : height
|
app.osc.orientation === "horizontal" ? width : height,
|
||||||
);
|
);
|
||||||
const barWidth =
|
const barWidth =
|
||||||
app.osc.orientation === "horizontal" ? width / numBars : height / numBars;
|
app.osc.orientation === "horizontal" ? width / numBars : height / numBars;
|
||||||
@ -184,7 +186,8 @@ export const runOscilloscope = (
|
|||||||
|
|
||||||
for (let i = 0; i < numBars; i++) {
|
for (let i = 0; i < numBars; i++) {
|
||||||
barHeight = Math.floor(
|
barHeight = Math.floor(
|
||||||
freqDataArray[Math.floor(i * freqDataArray.length / numBars)] * ((height / 256) * app.osc.size)
|
freqDataArray[Math.floor((i * freqDataArray.length) / numBars)] *
|
||||||
|
((height / 256) * app.osc.size),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (app.osc.orientation === "horizontal") {
|
if (app.osc.orientation === "horizontal") {
|
||||||
@ -192,7 +195,7 @@ export const runOscilloscope = (
|
|||||||
x + offset_width,
|
x + offset_width,
|
||||||
(height - barHeight) / 2 + offset_height,
|
(height - barHeight) / 2 + offset_height,
|
||||||
barWidth + 1,
|
barWidth + 1,
|
||||||
barHeight
|
barHeight,
|
||||||
);
|
);
|
||||||
x += barWidth;
|
x += barWidth;
|
||||||
} else {
|
} else {
|
||||||
@ -200,14 +203,13 @@ export const runOscilloscope = (
|
|||||||
(width - barHeight) / 2 + offset_width,
|
(width - barHeight) / 2 + offset_width,
|
||||||
y + offset_height,
|
y + offset_height,
|
||||||
barHeight,
|
barHeight,
|
||||||
barWidth + 1
|
barWidth + 1,
|
||||||
);
|
);
|
||||||
y += barWidth;
|
y += barWidth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function draw() {
|
function draw() {
|
||||||
// Update the canvas position on each cycle
|
// Update the canvas position on each cycle
|
||||||
const WIDTH = canvas.width;
|
const WIDTH = canvas.width;
|
||||||
@ -230,7 +232,7 @@ export const runOscilloscope = (
|
|||||||
-OFFSET_WIDTH,
|
-OFFSET_WIDTH,
|
||||||
-OFFSET_HEIGHT,
|
-OFFSET_HEIGHT,
|
||||||
WIDTH + 2 * OFFSET_WIDTH,
|
WIDTH + 2 * OFFSET_WIDTH,
|
||||||
HEIGHT + 2 * OFFSET_HEIGHT
|
HEIGHT + 2 * OFFSET_HEIGHT,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -250,7 +252,7 @@ export const runOscilloscope = (
|
|||||||
-OFFSET_WIDTH,
|
-OFFSET_WIDTH,
|
||||||
-OFFSET_HEIGHT,
|
-OFFSET_HEIGHT,
|
||||||
WIDTH + 2 * OFFSET_WIDTH,
|
WIDTH + 2 * OFFSET_WIDTH,
|
||||||
HEIGHT + 2 * OFFSET_HEIGHT
|
HEIGHT + 2 * OFFSET_HEIGHT,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
canvasCtx.lineWidth = app.osc.thickness;
|
canvasCtx.lineWidth = app.osc.thickness;
|
||||||
|
|||||||
@ -48,7 +48,10 @@ export class Clock {
|
|||||||
lastPlayPressTime: number;
|
lastPlayPressTime: number;
|
||||||
totalPauseTime: number;
|
totalPauseTime: number;
|
||||||
|
|
||||||
constructor(public app: Editor, ctx: AudioContext) {
|
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.time_signature = [4, 4];
|
||||||
this.logicalTime = 0;
|
this.logicalTime = 0;
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export const makeExampleFactory = (application: Editor): Function => {
|
|||||||
const make_example = (
|
const make_example = (
|
||||||
description: string,
|
description: string,
|
||||||
code: string,
|
code: string,
|
||||||
open: boolean = false
|
open: boolean = false,
|
||||||
) => {
|
) => {
|
||||||
const codeId = `codeExample${application.exampleCounter++}`;
|
const codeId = `codeExample${application.exampleCounter++}`;
|
||||||
// Store the code snippet in the data structure
|
// Store the code snippet in the data structure
|
||||||
@ -143,7 +143,7 @@ export const updateDocumentationContent = (app: Editor, bindings: any) => {
|
|||||||
extensions: [showdownHighlight({ auto_detection: true }), ...bindings],
|
extensions: [showdownHighlight({ auto_detection: true }), ...bindings],
|
||||||
});
|
});
|
||||||
const converted_markdown = converter.makeHtml(
|
const converted_markdown = converter.makeHtml(
|
||||||
app.docs[app.currentDocumentationPane]
|
app.docs[app.currentDocumentationPane],
|
||||||
);
|
);
|
||||||
document.getElementById("documentation-content")!.innerHTML =
|
document.getElementById("documentation-content")!.innerHTML =
|
||||||
converted_markdown;
|
converted_markdown;
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { type Editor } from "./main";
|
import { type Editor } from "./main";
|
||||||
|
|
||||||
|
|
||||||
export type ElementMap = {
|
export type ElementMap = {
|
||||||
[key: string]:
|
[key: string]:
|
||||||
| HTMLElement
|
| HTMLElement
|
||||||
@ -10,8 +9,7 @@ export type ElementMap = {
|
|||||||
| HTMLSelectElement
|
| HTMLSelectElement
|
||||||
| HTMLCanvasElement
|
| HTMLCanvasElement
|
||||||
| HTMLFormElement
|
| HTMLFormElement
|
||||||
| HTMLInputElement
|
| HTMLInputElement;
|
||||||
;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const singleElements = {
|
export const singleElements = {
|
||||||
@ -92,6 +90,4 @@ export const createDocumentationStyle = (app: Editor) => {
|
|||||||
tr: "",
|
tr: "",
|
||||||
box: "border bg-red-500",
|
box: "border bg-red-500",
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import {
|
|||||||
bracketMatching,
|
bracketMatching,
|
||||||
} from "@codemirror/language";
|
} from "@codemirror/language";
|
||||||
import { defaultKeymap, historyKeymap, history } from "@codemirror/commands";
|
import { defaultKeymap, historyKeymap, history } from "@codemirror/commands";
|
||||||
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search"
|
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
|
||||||
import {
|
import {
|
||||||
autocompletion,
|
autocompletion,
|
||||||
closeBrackets,
|
closeBrackets,
|
||||||
@ -34,15 +34,15 @@ import { toposTheme } from "./themes/toposTheme";
|
|||||||
import { javascript } from "@codemirror/lang-javascript";
|
import { javascript } from "@codemirror/lang-javascript";
|
||||||
import { inlineHoveringTips } from "./documentation/inlineHelp";
|
import { inlineHoveringTips } from "./documentation/inlineHelp";
|
||||||
import { toposCompletions, soundCompletions } from "./documentation/inlineHelp";
|
import { toposCompletions, soundCompletions } from "./documentation/inlineHelp";
|
||||||
import { javascriptLanguage } from "@codemirror/lang-javascript"
|
import { javascriptLanguage } from "@codemirror/lang-javascript";
|
||||||
|
|
||||||
export const jsCompletions = javascriptLanguage.data.of({
|
export const jsCompletions = javascriptLanguage.data.of({
|
||||||
autocomplete: toposCompletions
|
autocomplete: toposCompletions,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const toposSoundCompletions = javascriptLanguage.data.of({
|
export const toposSoundCompletions = javascriptLanguage.data.of({
|
||||||
autocomplete: soundCompletions
|
autocomplete: soundCompletions,
|
||||||
})
|
});
|
||||||
|
|
||||||
export const editorSetup: Extension = (() => [
|
export const editorSetup: Extension = (() => [
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
@ -95,7 +95,9 @@ export const installEditor = (app: Editor) => {
|
|||||||
app.withLineNumbers.of(lines),
|
app.withLineNumbers.of(lines),
|
||||||
app.fontSize.of(fontModif),
|
app.fontSize.of(fontModif),
|
||||||
app.hoveringCompartment.of(app.settings.tips ? inlineHoveringTips : []),
|
app.hoveringCompartment.of(app.settings.tips ? inlineHoveringTips : []),
|
||||||
app.completionsCompartment.of(app.settings.completions ? [jsCompletions, toposSoundCompletions] : []),
|
app.completionsCompartment.of(
|
||||||
|
app.settings.completions ? [jsCompletions, toposSoundCompletions] : [],
|
||||||
|
),
|
||||||
editorSetup,
|
editorSetup,
|
||||||
toposTheme,
|
toposTheme,
|
||||||
app.chosenLanguage.of(javascript()),
|
app.chosenLanguage.of(javascript()),
|
||||||
@ -114,7 +116,7 @@ export const installEditor = (app: Editor) => {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
]),
|
||||||
),
|
),
|
||||||
keymap.of([indentWithTab]),
|
keymap.of([indentWithTab]),
|
||||||
],
|
],
|
||||||
@ -139,7 +141,7 @@ export const installEditor = (app: Editor) => {
|
|||||||
".cm-gutters": {
|
".cm-gutters": {
|
||||||
fontSize: `${app.settings.font_size}px`,
|
fontSize: `${app.settings.font_size}px`,
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,72 +1,61 @@
|
|||||||
import type { Editor } from "./main";
|
import type { Editor } from "./main";
|
||||||
import type { File } from "./FileManagement";
|
import type { File } from "./FileManagement";
|
||||||
|
|
||||||
const delay = (ms: number) =>
|
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
new Promise((_, reject) =>
|
|
||||||
setTimeout(() => reject(new Error("Operation took too long")), ms)
|
|
||||||
);
|
|
||||||
|
|
||||||
const codeReplace = (code: string): string => {
|
const codeReplace = (code: string): string => {
|
||||||
let new_code = code.replace(/->/g, "&&").replace(/::/g, "&&");
|
return code.replace(/->|::/g, "&&");
|
||||||
return new_code;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const tryCatchWrapper = (
|
const tryCatchWrapper = async (
|
||||||
application: Editor,
|
application: Editor,
|
||||||
code: string
|
code: string,
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
return new Promise((resolve, _) => {
|
|
||||||
try {
|
try {
|
||||||
Function(
|
await new Function(`"use strict"; ${codeReplace(code)}`).call(
|
||||||
`"use strict";try{
|
application.api,
|
||||||
${codeReplace(code)}; /* break block comments */;
|
);
|
||||||
} catch (e) {console.log(e); _reportError(e);};`
|
return true;
|
||||||
).call(application.api);
|
|
||||||
resolve(true);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
application.interface.error_line.innerHTML = error as string;
|
application.interface.error_line.innerHTML = error as string;
|
||||||
application.api._reportError(error as string)
|
application.api._reportError(error as string);
|
||||||
resolve(false);
|
return false;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const cache = new Map<string, Function>();
|
const cache = new Map<string, Function>();
|
||||||
const MAX_CACHE_SIZE = 20;
|
const MAX_CACHE_SIZE = 40;
|
||||||
|
|
||||||
const addFunctionToCache = (code: string, fn: Function) => {
|
const addFunctionToCache = (code: string, fn: Function) => {
|
||||||
if (cache.size >= MAX_CACHE_SIZE) {
|
if (cache.size >= MAX_CACHE_SIZE) {
|
||||||
// Delete the first item if cache size exceeds max size
|
|
||||||
cache.delete(cache.keys().next().value);
|
cache.delete(cache.keys().next().value);
|
||||||
}
|
}
|
||||||
cache.set(code, fn);
|
cache.set(code, fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Optimized evaluate function with reduced complexity
|
||||||
export const tryEvaluate = async (
|
export const tryEvaluate = async (
|
||||||
application: Editor,
|
application: Editor,
|
||||||
code: File,
|
code: File,
|
||||||
timeout = 5000
|
timeout = 5000,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
|
||||||
code.evaluations!++;
|
code.evaluations!++;
|
||||||
const candidateCode = code.candidate;
|
const candidateCode = code.candidate;
|
||||||
|
|
||||||
if (cache.has(candidateCode)) {
|
try {
|
||||||
// If the code is already in cache, use it
|
const cachedFunction = cache.get(candidateCode);
|
||||||
cache.get(candidateCode)!.call(application.api);
|
if (cachedFunction) {
|
||||||
|
cachedFunction.call(application.api);
|
||||||
} else {
|
} else {
|
||||||
const wrappedCode = `let i = ${code.evaluations};` + candidateCode;
|
const wrappedCode = `let i = ${code.evaluations}; ${candidateCode}`;
|
||||||
// Otherwise, evaluate the code and if valid, add it to the cache
|
|
||||||
const isCodeValid = await Promise.race([
|
const isCodeValid = await Promise.race([
|
||||||
tryCatchWrapper(application, wrappedCode as string),
|
tryCatchWrapper(application, wrappedCode),
|
||||||
delay(timeout),
|
delay(timeout),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isCodeValid) {
|
if (isCodeValid) {
|
||||||
code.committed = code.candidate;
|
code.committed = code.candidate;
|
||||||
const newFunction = new Function(
|
const newFunction = new Function(
|
||||||
`"use strict";try{${codeReplace(
|
`"use strict"; ${codeReplace(wrappedCode)}`,
|
||||||
wrappedCode
|
|
||||||
)}} catch (e) {console.log(e); _reportError(e);};`
|
|
||||||
);
|
);
|
||||||
addFunctionToCache(candidateCode, newFunction);
|
addFunctionToCache(candidateCode, newFunction);
|
||||||
} else {
|
} else {
|
||||||
@ -75,14 +64,14 @@ export const tryEvaluate = async (
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
application.interface.error_line.innerHTML = error as string;
|
application.interface.error_line.innerHTML = error as string;
|
||||||
application.api._reportError(error as string)
|
application.api._reportError(error as string);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const evaluate = async (
|
export const evaluate = async (
|
||||||
application: Editor,
|
application: Editor,
|
||||||
code: File,
|
code: File,
|
||||||
timeout = 1000
|
timeout = 1000,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
@ -98,7 +87,7 @@ export const evaluate = async (
|
|||||||
|
|
||||||
export const evaluateOnce = async (
|
export const evaluateOnce = async (
|
||||||
application: Editor,
|
application: Editor,
|
||||||
code: string
|
code: string,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
/**
|
/**
|
||||||
* Evaluates the code once without any caching or error-handling mechanisms besides the tryCatchWrapper.
|
* Evaluates the code once without any caching or error-handling mechanisms besides the tryCatchWrapper.
|
||||||
|
|||||||
@ -154,7 +154,7 @@ export class AppSettings {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const settingsFromStorage = JSON.parse(
|
const settingsFromStorage = JSON.parse(
|
||||||
localStorage.getItem("topos") || "{}"
|
localStorage.getItem("topos") || "{}",
|
||||||
);
|
);
|
||||||
|
|
||||||
if (settingsFromStorage && Object.keys(settingsFromStorage).length !== 0) {
|
if (settingsFromStorage && Object.keys(settingsFromStorage).length !== 0) {
|
||||||
@ -210,7 +210,7 @@ export class AppSettings {
|
|||||||
|
|
||||||
saveApplicationToLocalStorage(
|
saveApplicationToLocalStorage(
|
||||||
universes: Universes,
|
universes: Universes,
|
||||||
settings: Settings
|
settings: Settings,
|
||||||
): void {
|
): void {
|
||||||
/**
|
/**
|
||||||
* Main method to store the application to local storage.
|
* Main method to store the application to local storage.
|
||||||
@ -263,7 +263,9 @@ export const initializeSelectedUniverse = (app: Editor): void => {
|
|||||||
app.universes[app.selected_universe] = structuredClone(template_universe);
|
app.universes[app.selected_universe] = structuredClone(template_universe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(app.interface.universe_viewer as HTMLInputElement).placeholder! = `${app.selected_universe}`;
|
(
|
||||||
|
app.interface.universe_viewer as HTMLInputElement
|
||||||
|
).placeholder! = `${app.selected_universe}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const emptyUrl = () => {
|
export const emptyUrl = () => {
|
||||||
@ -321,7 +323,7 @@ export const loadUniverserFromUrl = (app: Editor): void => {
|
|||||||
export const loadUniverse = (
|
export const loadUniverse = (
|
||||||
app: Editor,
|
app: Editor,
|
||||||
universeName: string,
|
universeName: string,
|
||||||
universe: Universe = template_universe
|
universe: Universe = template_universe,
|
||||||
): void => {
|
): void => {
|
||||||
let selectedUniverse = universeName.trim();
|
let selectedUniverse = universeName.trim();
|
||||||
if (app.universes[selectedUniverse] === undefined) {
|
if (app.universes[selectedUniverse] === undefined) {
|
||||||
@ -334,7 +336,9 @@ export const loadUniverse = (
|
|||||||
// Updating references to the currently selected universe
|
// Updating references to the currently selected universe
|
||||||
app.settings.selected_universe = selectedUniverse;
|
app.settings.selected_universe = selectedUniverse;
|
||||||
app.selected_universe = selectedUniverse;
|
app.selected_universe = selectedUniverse;
|
||||||
(app.interface.universe_viewer as HTMLInputElement).placeholder! = `${selectedUniverse}`;
|
(
|
||||||
|
app.interface.universe_viewer as HTMLInputElement
|
||||||
|
).placeholder! = `${selectedUniverse}`;
|
||||||
// Updating the editor View to reflect the selected universe
|
// Updating the editor View to reflect the selected universe
|
||||||
app.updateEditorView();
|
app.updateEditorView();
|
||||||
// Evaluating the initialisation script for the selected universe
|
// Evaluating the initialisation script for the selected universe
|
||||||
|
|||||||
@ -175,10 +175,10 @@ export class MidiConnection {
|
|||||||
*/
|
*/
|
||||||
if (this.midiInputs.length > 0) {
|
if (this.midiInputs.length > 0) {
|
||||||
const midiClockSelect = document.getElementById(
|
const midiClockSelect = document.getElementById(
|
||||||
"midi-clock-input"
|
"midi-clock-input",
|
||||||
) as HTMLSelectElement;
|
) as HTMLSelectElement;
|
||||||
const midiInputSelect = document.getElementById(
|
const midiInputSelect = document.getElementById(
|
||||||
"default-midi-input"
|
"default-midi-input",
|
||||||
) as HTMLSelectElement;
|
) as HTMLSelectElement;
|
||||||
|
|
||||||
midiClockSelect.innerHTML = "";
|
midiClockSelect.innerHTML = "";
|
||||||
@ -207,7 +207,7 @@ export class MidiConnection {
|
|||||||
|
|
||||||
if (this.settings.midi_clock_input) {
|
if (this.settings.midi_clock_input) {
|
||||||
const clockMidiInputIndex = this.getMidiInputIndex(
|
const clockMidiInputIndex = this.getMidiInputIndex(
|
||||||
this.settings.midi_clock_input
|
this.settings.midi_clock_input,
|
||||||
);
|
);
|
||||||
midiClockSelect.value = clockMidiInputIndex.toString();
|
midiClockSelect.value = clockMidiInputIndex.toString();
|
||||||
if (clockMidiInputIndex > 0) {
|
if (clockMidiInputIndex > 0) {
|
||||||
@ -220,7 +220,7 @@ export class MidiConnection {
|
|||||||
|
|
||||||
if (this.settings.default_midi_input) {
|
if (this.settings.default_midi_input) {
|
||||||
const defaultMidiInputIndex = this.getMidiInputIndex(
|
const defaultMidiInputIndex = this.getMidiInputIndex(
|
||||||
this.settings.default_midi_input
|
this.settings.default_midi_input,
|
||||||
);
|
);
|
||||||
midiInputSelect.value = defaultMidiInputIndex.toString();
|
midiInputSelect.value = defaultMidiInputIndex.toString();
|
||||||
if (defaultMidiInputIndex > 0) {
|
if (defaultMidiInputIndex > 0) {
|
||||||
@ -400,14 +400,14 @@ export class MidiConnection {
|
|||||||
|
|
||||||
public removeFromActiveNotes(note: number, channel: number): void {
|
public removeFromActiveNotes(note: number, channel: number): void {
|
||||||
const index = this.activeNotes.findIndex(
|
const index = this.activeNotes.findIndex(
|
||||||
(e) => e.note === note && e.channel === channel
|
(e) => e.note === note && e.channel === channel,
|
||||||
);
|
);
|
||||||
if (index >= 0) this.activeNotes.splice(index, 1);
|
if (index >= 0) this.activeNotes.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeFromStickyNotes(note: number, channel: number): boolean {
|
public removeFromStickyNotes(note: number, channel: number): boolean {
|
||||||
const index = this.stickyNotes.findIndex(
|
const index = this.stickyNotes.findIndex(
|
||||||
(e) => e.note === note && e.channel === channel
|
(e) => e.note === note && e.channel === channel,
|
||||||
);
|
);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
this.stickyNotes.splice(index, 1);
|
this.stickyNotes.splice(index, 1);
|
||||||
@ -578,8 +578,9 @@ export class MidiConnection {
|
|||||||
if (typeof output === "number") {
|
if (typeof output === "number") {
|
||||||
if (output < 0 || output >= this.midiOutputs.length) {
|
if (output < 0 || output >= this.midiOutputs.length) {
|
||||||
console.error(
|
console.error(
|
||||||
`Invalid MIDI output index. Index must be in the range 0-${this.midiOutputs.length - 1
|
`Invalid MIDI output index. Index must be in the range 0-${
|
||||||
}.`
|
this.midiOutputs.length - 1
|
||||||
|
}.`,
|
||||||
);
|
);
|
||||||
return this.currentOutputIndex;
|
return this.currentOutputIndex;
|
||||||
} else {
|
} else {
|
||||||
@ -607,8 +608,9 @@ export class MidiConnection {
|
|||||||
if (typeof input === "number") {
|
if (typeof input === "number") {
|
||||||
if (input < 0 || input >= this.midiInputs.length) {
|
if (input < 0 || input >= this.midiInputs.length) {
|
||||||
console.error(
|
console.error(
|
||||||
`Invalid MIDI input index. Index must be in the range 0-${this.midiInputs.length - 1
|
`Invalid MIDI input index. Index must be in the range 0-${
|
||||||
}.`
|
this.midiInputs.length - 1
|
||||||
|
}.`,
|
||||||
);
|
);
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
@ -642,7 +644,7 @@ export class MidiConnection {
|
|||||||
velocity: number,
|
velocity: number,
|
||||||
duration: number,
|
duration: number,
|
||||||
port: number | string = this.currentOutputIndex,
|
port: number | string = this.currentOutputIndex,
|
||||||
bend: number | undefined = undefined
|
bend: number | undefined = undefined,
|
||||||
): void {
|
): void {
|
||||||
/**
|
/**
|
||||||
* Sending a MIDI Note on/off message with the same note number and channel. Automatically manages
|
* Sending a MIDI Note on/off message with the same note number and channel. Automatically manages
|
||||||
@ -668,11 +670,14 @@ export class MidiConnection {
|
|||||||
if (bend) this.sendPitchBend(bend, channel, port);
|
if (bend) this.sendPitchBend(bend, channel, port);
|
||||||
|
|
||||||
// Schedule Note Off
|
// Schedule Note Off
|
||||||
const timeoutId = setTimeout(() => {
|
const timeoutId = setTimeout(
|
||||||
|
() => {
|
||||||
output.send(noteOffMessage);
|
output.send(noteOffMessage);
|
||||||
if (bend) this.sendPitchBend(8192, channel, port);
|
if (bend) this.sendPitchBend(8192, channel, port);
|
||||||
delete this.scheduledNotes[noteNumber];
|
delete this.scheduledNotes[noteNumber];
|
||||||
}, (duration - 0.02) * 1000);
|
},
|
||||||
|
(duration - 0.02) * 1000,
|
||||||
|
);
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.scheduledNotes[noteNumber] = timeoutId;
|
this.scheduledNotes[noteNumber] = timeoutId;
|
||||||
@ -685,7 +690,7 @@ export class MidiConnection {
|
|||||||
note: number,
|
note: number,
|
||||||
channel: number,
|
channel: number,
|
||||||
velocity: number,
|
velocity: number,
|
||||||
port: number | string = this.currentOutputIndex
|
port: number | string = this.currentOutputIndex,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Sending Midi Note on message
|
* Sending Midi Note on message
|
||||||
@ -704,7 +709,7 @@ export class MidiConnection {
|
|||||||
sendMidiOff(
|
sendMidiOff(
|
||||||
note: number,
|
note: number,
|
||||||
channel: number,
|
channel: number,
|
||||||
port: number | string = this.currentOutputIndex
|
port: number | string = this.currentOutputIndex,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Sending Midi Note off message
|
* Sending Midi Note off message
|
||||||
@ -722,7 +727,7 @@ export class MidiConnection {
|
|||||||
|
|
||||||
sendAllNotesOff(
|
sendAllNotesOff(
|
||||||
channel: number,
|
channel: number,
|
||||||
port: number | string = this.currentOutputIndex
|
port: number | string = this.currentOutputIndex,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Sending Midi Note off message
|
* Sending Midi Note off message
|
||||||
@ -739,7 +744,7 @@ export class MidiConnection {
|
|||||||
|
|
||||||
sendAllSoundOff(
|
sendAllSoundOff(
|
||||||
channel: number,
|
channel: number,
|
||||||
port: number | string = this.currentOutputIndex
|
port: number | string = this.currentOutputIndex,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Sending all sound off
|
* Sending all sound off
|
||||||
@ -775,7 +780,7 @@ export class MidiConnection {
|
|||||||
public sendPitchBend(
|
public sendPitchBend(
|
||||||
value: number,
|
value: number,
|
||||||
channel: number,
|
channel: number,
|
||||||
port: number | string = this.currentOutputIndex
|
port: number | string = this.currentOutputIndex,
|
||||||
): void {
|
): void {
|
||||||
/**
|
/**
|
||||||
* Sends a MIDI Pitch Bend message to the currently selected MIDI output.
|
* Sends a MIDI Pitch Bend message to the currently selected MIDI output.
|
||||||
@ -786,7 +791,7 @@ export class MidiConnection {
|
|||||||
*/
|
*/
|
||||||
if (value < 0 || value > 16383) {
|
if (value < 0 || value > 16383) {
|
||||||
console.error(
|
console.error(
|
||||||
"Invalid pitch bend value. Value must be in the range 0-16383."
|
"Invalid pitch bend value. Value must be in the range 0-16383.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (channel < 0 || channel > 15) {
|
if (channel < 0 || channel > 15) {
|
||||||
@ -825,7 +830,7 @@ export class MidiConnection {
|
|||||||
public sendMidiControlChange(
|
public sendMidiControlChange(
|
||||||
controlNumber: number,
|
controlNumber: number,
|
||||||
value: number,
|
value: number,
|
||||||
channel: number
|
channel: number,
|
||||||
): void {
|
): void {
|
||||||
/**
|
/**
|
||||||
* Sends a MIDI Control Change message to the currently selected MIDI output.
|
* Sends a MIDI Control Change message to the currently selected MIDI output.
|
||||||
|
|||||||
@ -118,32 +118,40 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
|
|
||||||
app.interface.universe_viewer.addEventListener("keydown", (event: any) => {
|
app.interface.universe_viewer.addEventListener("keydown", (event: any) => {
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
let content = (app.interface.universe_viewer as HTMLInputElement).value.trim();
|
let content = (
|
||||||
|
app.interface.universe_viewer as HTMLInputElement
|
||||||
|
).value.trim();
|
||||||
if (content.length > 2 && content.length < 40) {
|
if (content.length > 2 && content.length < 40) {
|
||||||
if (content !== app.selected_universe) {
|
if (content !== app.selected_universe) {
|
||||||
Object.defineProperty(app.universes, content,
|
Object.defineProperty(
|
||||||
|
app.universes,
|
||||||
|
content,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
Object.getOwnPropertyDescriptor(app.universes, app.selected_universe));
|
Object.getOwnPropertyDescriptor(
|
||||||
|
app.universes,
|
||||||
|
app.selected_universe,
|
||||||
|
),
|
||||||
|
);
|
||||||
delete app.universes[app.selected_universe];
|
delete app.universes[app.selected_universe];
|
||||||
}
|
}
|
||||||
app.selected_universe = content;
|
app.selected_universe = content;
|
||||||
loadUniverse(app, app.selected_universe);
|
loadUniverse(app, app.selected_universe);
|
||||||
(app.interface.universe_viewer as HTMLInputElement).placeholder = content;
|
(app.interface.universe_viewer as HTMLInputElement).placeholder =
|
||||||
(app.interface.universe_viewer as HTMLInputElement).value = '';
|
content;
|
||||||
|
(app.interface.universe_viewer as HTMLInputElement).value = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.interface.audio_nudge_range.addEventListener("input", () => {
|
app.interface.audio_nudge_range.addEventListener("input", () => {
|
||||||
app.clock.nudge = parseInt(
|
app.clock.nudge = parseInt(
|
||||||
(app.interface.audio_nudge_range as HTMLInputElement).value
|
(app.interface.audio_nudge_range as HTMLInputElement).value,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.interface.dough_nudge_range.addEventListener("input", () => {
|
app.interface.dough_nudge_range.addEventListener("input", () => {
|
||||||
app.dough_nudge = parseInt(
|
app.dough_nudge = parseInt(
|
||||||
(app.interface.dough_nudge_range as HTMLInputElement).value
|
(app.interface.dough_nudge_range as HTMLInputElement).value,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -227,16 +235,16 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.interface.local_button.addEventListener("click", () =>
|
app.interface.local_button.addEventListener("click", () =>
|
||||||
app.changeModeFromInterface("local")
|
app.changeModeFromInterface("local"),
|
||||||
);
|
);
|
||||||
app.interface.global_button.addEventListener("click", () =>
|
app.interface.global_button.addEventListener("click", () =>
|
||||||
app.changeModeFromInterface("global")
|
app.changeModeFromInterface("global"),
|
||||||
);
|
);
|
||||||
app.interface.init_button.addEventListener("click", () =>
|
app.interface.init_button.addEventListener("click", () =>
|
||||||
app.changeModeFromInterface("init")
|
app.changeModeFromInterface("init"),
|
||||||
);
|
);
|
||||||
app.interface.note_button.addEventListener("click", () =>
|
app.interface.note_button.addEventListener("click", () =>
|
||||||
app.changeModeFromInterface("notes")
|
app.changeModeFromInterface("notes"),
|
||||||
);
|
);
|
||||||
|
|
||||||
app.interface.font_family_selector.addEventListener("change", () => {
|
app.interface.font_family_selector.addEventListener("change", () => {
|
||||||
@ -255,7 +263,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
fontSize: app.settings.font_size + "px",
|
fontSize: app.settings.font_size + "px",
|
||||||
},
|
},
|
||||||
".cm-gutters": { fontSize: app.settings.font_size + "px" },
|
".cm-gutters": { fontSize: app.settings.font_size + "px" },
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -275,7 +283,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
fontSize: app.settings.font_size + "px",
|
fontSize: app.settings.font_size + "px",
|
||||||
},
|
},
|
||||||
".cm-gutters": { fontSize: app.settings.font_size + "px" },
|
".cm-gutters": { fontSize: app.settings.font_size + "px" },
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -283,7 +291,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
app.interface.settings_button.addEventListener("click", () => {
|
app.interface.settings_button.addEventListener("click", () => {
|
||||||
// Populate the font selector
|
// Populate the font selector
|
||||||
const fontFamilySelect = document.getElementById(
|
const fontFamilySelect = document.getElementById(
|
||||||
"font-family"
|
"font-family",
|
||||||
) as HTMLSelectElement | null;
|
) as HTMLSelectElement | null;
|
||||||
if (fontFamilySelect) {
|
if (fontFamilySelect) {
|
||||||
fontFamilySelect.value = app.settings.font;
|
fontFamilySelect.value = app.settings.font;
|
||||||
@ -294,7 +302,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
doughNudgeRange.value = app.dough_nudge.toString();
|
doughNudgeRange.value = app.dough_nudge.toString();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const doughNumber = document.getElementById(
|
const doughNumber = document.getElementById(
|
||||||
"doughnumber"
|
"doughnumber",
|
||||||
) as HTMLInputElement;
|
) as HTMLInputElement;
|
||||||
doughNumber.value = app.dough_nudge.toString();
|
doughNumber.value = app.dough_nudge.toString();
|
||||||
if (app.settings.font_size === null) {
|
if (app.settings.font_size === null) {
|
||||||
@ -350,7 +358,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
fontSize: app.settings.font_size + "px",
|
fontSize: app.settings.font_size + "px",
|
||||||
},
|
},
|
||||||
".cm-gutters": { fontSize: app.settings.font_size + "px" },
|
".cm-gutters": { fontSize: app.settings.font_size + "px" },
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -408,7 +416,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
app.settings.tips = checked;
|
app.settings.tips = checked;
|
||||||
app.view.dispatch({
|
app.view.dispatch({
|
||||||
effects: app.hoveringCompartment.reconfigure(
|
effects: app.hoveringCompartment.reconfigure(
|
||||||
checked ? inlineHoveringTips : []
|
checked ? inlineHoveringTips : [],
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -421,7 +429,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
app.settings.completions = checked;
|
app.settings.completions = checked;
|
||||||
app.view.dispatch({
|
app.view.dispatch({
|
||||||
effects: app.completionsCompartment.reconfigure(
|
effects: app.completionsCompartment.reconfigure(
|
||||||
checked ? jsCompletions : []
|
checked ? jsCompletions : [],
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -444,7 +452,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
|||||||
|
|
||||||
app.interface.midi_clock_ppqn.addEventListener("change", () => {
|
app.interface.midi_clock_ppqn.addEventListener("change", () => {
|
||||||
let value = parseInt(
|
let value = parseInt(
|
||||||
(app.interface.midi_clock_ppqn as HTMLInputElement).value
|
(app.interface.midi_clock_ppqn as HTMLInputElement).value,
|
||||||
);
|
);
|
||||||
app.settings.midi_clock_ppqn = value;
|
app.settings.midi_clock_ppqn = value;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -19,11 +19,12 @@ export class TransportNode extends AudioWorkletNode {
|
|||||||
this.app.api.MidiConnection.sendMidiClock();
|
this.app.api.MidiConnection.sendMidiClock();
|
||||||
}
|
}
|
||||||
const futureTimeStamp = this.app.clock.convertTicksToTimeposition(
|
const futureTimeStamp = this.app.clock.convertTicksToTimeposition(
|
||||||
this.app.clock.tick
|
this.app.clock.tick,
|
||||||
);
|
);
|
||||||
this.app.clock.time_position = futureTimeStamp;
|
this.app.clock.time_position = futureTimeStamp;
|
||||||
if (futureTimeStamp.pulse % this.app.clock.ppqn == 0) {
|
if (futureTimeStamp.pulse % this.app.clock.ppqn == 0) {
|
||||||
this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${futureTimeStamp.beat + 1
|
this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${
|
||||||
|
futureTimeStamp.beat + 1
|
||||||
} / ${this.app.clock.bpm}`;
|
} / ${this.app.clock.bpm}`;
|
||||||
}
|
}
|
||||||
if (this.app.exampleIsPlaying) {
|
if (this.app.exampleIsPlaying) {
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
export function objectWithArraysToArrayOfObjects(
|
export function objectWithArraysToArrayOfObjects(
|
||||||
input: Record<string, any>,
|
input: Record<string, any>,
|
||||||
arraysToArrays: string[]
|
arraysToArrays: string[],
|
||||||
): Record<string, any>[] {
|
): Record<string, any>[] {
|
||||||
const inputCopy = { ...input };
|
const inputCopy = { ...input };
|
||||||
arraysToArrays.forEach((k) => {
|
arraysToArrays.forEach((k) => {
|
||||||
@ -24,7 +24,7 @@ export function objectWithArraysToArrayOfObjects(
|
|||||||
acc.keys.push(key);
|
acc.keys.push(key);
|
||||||
return acc;
|
return acc;
|
||||||
},
|
},
|
||||||
{ keys: [] as string[], maxLength: 0 }
|
{ keys: [] as string[], maxLength: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
const output: Record<string, any>[] = [];
|
const output: Record<string, any>[] = [];
|
||||||
@ -52,9 +52,10 @@ export function objectWithArraysToArrayOfObjects(
|
|||||||
*/
|
*/
|
||||||
export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
|
export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
|
||||||
array: T[],
|
array: T[],
|
||||||
mergeObject: Record<string, any> = {}
|
mergeObject: Record<string, any> = {},
|
||||||
): Record<string, any> {
|
): Record<string, any> {
|
||||||
return array.reduce((acc, obj) => {
|
return array.reduce(
|
||||||
|
(acc, obj) => {
|
||||||
const mergedObj = { ...obj, ...mergeObject };
|
const mergedObj = { ...obj, ...mergeObject };
|
||||||
Object.keys(mergedObj).forEach((key) => {
|
Object.keys(mergedObj).forEach((key) => {
|
||||||
if (!acc[key]) {
|
if (!acc[key]) {
|
||||||
@ -63,7 +64,9 @@ export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
|
|||||||
acc[key].push(mergedObj[key]);
|
acc[key].push(mergedObj[key]);
|
||||||
});
|
});
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, any>);
|
},
|
||||||
|
{} as Record<string, any>,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -76,9 +79,9 @@ export function arrayOfObjectsToObjectWithArrays<T extends Record<string, any>>(
|
|||||||
*/
|
*/
|
||||||
export function filterObject(
|
export function filterObject(
|
||||||
obj: Record<string, any>,
|
obj: Record<string, any>,
|
||||||
filter: string[]
|
filter: string[],
|
||||||
): Record<string, any> {
|
): Record<string, any> {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(obj).filter(([key]) => filter.includes(key))
|
Object.entries(obj).filter(([key]) => filter.includes(key)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,8 @@ const handleResize = (canvas: HTMLCanvasElement) => {
|
|||||||
const dpr = window.devicePixelRatio || 1;
|
const dpr = window.devicePixelRatio || 1;
|
||||||
|
|
||||||
// Assuming the canvas takes up the whole window
|
// Assuming the canvas takes up the whole window
|
||||||
canvas.width = window.innerWidth * dpr * 0.25;
|
canvas.width = window.innerWidth * dpr;
|
||||||
canvas.height = window.innerHeight * dpr * 0.25;
|
canvas.height = window.innerHeight * dpr;
|
||||||
|
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
ctx.scale(dpr, dpr);
|
ctx.scale(dpr, dpr);
|
||||||
@ -32,13 +32,13 @@ export const saveBeforeExit = (app: Editor): null => {
|
|||||||
export const installWindowBehaviors = (
|
export const installWindowBehaviors = (
|
||||||
app: Editor,
|
app: Editor,
|
||||||
window: Window,
|
window: Window,
|
||||||
preventMultipleTabs: boolean = false
|
preventMultipleTabs: boolean = false,
|
||||||
) => {
|
) => {
|
||||||
window.addEventListener("resize", () =>
|
window.addEventListener("resize", () =>
|
||||||
handleResize(app.interface.scope as HTMLCanvasElement)
|
handleResize(app.interface.scope as HTMLCanvasElement),
|
||||||
);
|
);
|
||||||
window.addEventListener("resize", () =>
|
window.addEventListener("resize", () =>
|
||||||
handleResize(app.interface.feedback as HTMLCanvasElement)
|
handleResize(app.interface.feedback as HTMLCanvasElement),
|
||||||
);
|
);
|
||||||
window.addEventListener("beforeunload", (event) => {
|
window.addEventListener("beforeunload", (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -61,11 +61,11 @@ export const installWindowBehaviors = (
|
|||||||
if (e.key == "page_available") {
|
if (e.key == "page_available") {
|
||||||
document.getElementById("all")!.classList.add("invisible");
|
document.getElementById("all")!.classList.add("invisible");
|
||||||
alert(
|
alert(
|
||||||
"Topos is already opened in another tab. Close this tab now to prevent data loss."
|
"Topos is already opened in another tab. Close this tab now to prevent data loss.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,14 +1,10 @@
|
|||||||
import { type Editor } from "../main";
|
import { type Editor } from "../main";
|
||||||
import {
|
import { freqToMidi, resolvePitchBend, safeScale } from "zifferjs";
|
||||||
freqToMidi,
|
|
||||||
resolvePitchBend,
|
|
||||||
safeScale
|
|
||||||
} from "zifferjs";
|
|
||||||
|
|
||||||
export type EventOperation<T> = (instance: T, ...args: any[]) => void;
|
export type EventOperation<T> = (instance: T, ...args: any[]) => void;
|
||||||
|
|
||||||
export interface AbstractEvent {
|
export interface AbstractEvent {
|
||||||
[key: string]: any
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AbstractEvent {
|
export class AbstractEvent {
|
||||||
@ -208,19 +204,26 @@ export class AbstractEvent {
|
|||||||
return this.modify(func);
|
return this.modify(func);
|
||||||
};
|
};
|
||||||
|
|
||||||
noteLength = (value: number | number[], ...kwargs: number[]): AbstractEvent => {
|
noteLength = (
|
||||||
|
value: number | number[],
|
||||||
|
...kwargs: number[]
|
||||||
|
): AbstractEvent => {
|
||||||
/**
|
/**
|
||||||
* This function is used to set the note length of the Event.
|
* This function is used to set the note length of the Event.
|
||||||
*/
|
*/
|
||||||
if(kwargs.length > 0) {
|
if (kwargs.length > 0) {
|
||||||
value = (Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs]);
|
value = Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs];
|
||||||
}
|
}
|
||||||
if(Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
this.values["noteLength"] = value;
|
this.values["noteLength"] = value;
|
||||||
this.values.dur = value.map((v) => this.app.clock.convertPulseToSecond(v*4*this.app.clock.ppqn));
|
this.values.dur = value.map((v) =>
|
||||||
|
this.app.clock.convertPulseToSecond(v * 4 * this.app.clock.ppqn),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.values["noteLength"] = value;
|
this.values["noteLength"] = value;
|
||||||
this.values.dur = this.app.clock.convertPulseToSecond(value*4*this.app.clock.ppqn);
|
this.values.dur = this.app.clock.convertPulseToSecond(
|
||||||
|
value * 4 * this.app.clock.ppqn,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@ -237,13 +240,13 @@ export abstract class AudibleEvent extends AbstractEvent {
|
|||||||
* @param value - The pitch value
|
* @param value - The pitch value
|
||||||
* @returns The Event
|
* @returns The Event
|
||||||
*/
|
*/
|
||||||
if(kwargs.length > 0) {
|
if (kwargs.length > 0) {
|
||||||
value = (Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs]);
|
value = Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs];
|
||||||
}
|
}
|
||||||
this.values["pitch"] = value;
|
this.values["pitch"] = value;
|
||||||
if(this.values.key && this.values.parsedScale) this.update();
|
if (this.values.key && this.values.parsedScale) this.update();
|
||||||
return this;
|
return this;
|
||||||
}
|
};
|
||||||
|
|
||||||
pc = this.pitch;
|
pc = this.pitch;
|
||||||
|
|
||||||
@ -253,11 +256,16 @@ export abstract class AudibleEvent extends AbstractEvent {
|
|||||||
* @param value - The octave value
|
* @param value - The octave value
|
||||||
* @returns The Event
|
* @returns The Event
|
||||||
*/
|
*/
|
||||||
if(kwargs.length > 0) {
|
if (kwargs.length > 0) {
|
||||||
value = (Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs]);
|
value = Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs];
|
||||||
}
|
}
|
||||||
this.values["octave"] = value;
|
this.values["octave"] = value;
|
||||||
if(this.values.key && (this.values.pitch || this.values.pitch === 0) && this.values.parsedScale) this.update();
|
if (
|
||||||
|
this.values.key &&
|
||||||
|
(this.values.pitch || this.values.pitch === 0) &&
|
||||||
|
this.values.parsedScale
|
||||||
|
)
|
||||||
|
this.update();
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -267,29 +275,36 @@ export abstract class AudibleEvent extends AbstractEvent {
|
|||||||
* @param value - The key value
|
* @param value - The key value
|
||||||
* @returns The Event
|
* @returns The Event
|
||||||
*/
|
*/
|
||||||
if(kwargs.length > 0) {
|
if (kwargs.length > 0) {
|
||||||
value = (Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs]);
|
value = Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs];
|
||||||
}
|
}
|
||||||
this.values["key"] = value;
|
this.values["key"] = value;
|
||||||
if((this.values.pitch || this.values.pitch === 0) && this.values.parsedScale) this.update();
|
if (
|
||||||
|
(this.values.pitch || this.values.pitch === 0) &&
|
||||||
|
this.values.parsedScale
|
||||||
|
)
|
||||||
|
this.update();
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
scale = (value: string | number | (number|string)[], ...kwargs: (string|number)[]): this => {
|
scale = (
|
||||||
|
value: string | number | (number | string)[],
|
||||||
|
...kwargs: (string | number)[]
|
||||||
|
): this => {
|
||||||
/*
|
/*
|
||||||
* This function is used to set the scale of the Event.
|
* This function is used to set the scale of the Event.
|
||||||
* @param value - The scale value
|
* @param value - The scale value
|
||||||
* @returns The Event
|
* @returns The Event
|
||||||
*/
|
*/
|
||||||
if(kwargs.length > 0) {
|
if (kwargs.length > 0) {
|
||||||
value = (Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs]);
|
value = Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs];
|
||||||
}
|
}
|
||||||
if (typeof value === "string" || typeof value === "number") {
|
if (typeof value === "string" || typeof value === "number") {
|
||||||
this.values.parsedScale = safeScale(value) as number[];
|
this.values.parsedScale = safeScale(value) as number[];
|
||||||
} else if(Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
this.values.parsedScale = value.map((v) => safeScale(v));
|
this.values.parsedScale = value.map((v) => safeScale(v));
|
||||||
}
|
}
|
||||||
if(this.values.key && (this.values.pitch || this.values.pitch === 0)) {
|
if (this.values.key && (this.values.pitch || this.values.pitch === 0)) {
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@ -301,14 +316,14 @@ export abstract class AudibleEvent extends AbstractEvent {
|
|||||||
* @param value - The frequency value
|
* @param value - The frequency value
|
||||||
* @returns The Event
|
* @returns The Event
|
||||||
*/
|
*/
|
||||||
if(kwargs.length > 0) {
|
if (kwargs.length > 0) {
|
||||||
value = (Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs]);
|
value = Array.isArray(value) ? value.concat(kwargs) : [value, ...kwargs];
|
||||||
}
|
}
|
||||||
this.values["freq"] = value;
|
this.values["freq"] = value;
|
||||||
if(Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
this.values["note"] = [];
|
this.values["note"] = [];
|
||||||
this.values["bend"] = [];
|
this.values["bend"] = [];
|
||||||
for(const v of value) {
|
for (const v of value) {
|
||||||
const midiNote = freqToMidi(v);
|
const midiNote = freqToMidi(v);
|
||||||
if (midiNote % 1 !== 0) {
|
if (midiNote % 1 !== 0) {
|
||||||
this.values["note"].push(Math.floor(midiNote));
|
this.values["note"].push(Math.floor(midiNote));
|
||||||
@ -317,7 +332,7 @@ export abstract class AudibleEvent extends AbstractEvent {
|
|||||||
this.values["note"].push(midiNote);
|
this.values["note"].push(midiNote);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(this.values.bend.length === 0) delete this.values.bend;
|
if (this.values.bend.length === 0) delete this.values.bend;
|
||||||
} else {
|
} else {
|
||||||
const midiNote = freqToMidi(value);
|
const midiNote = freqToMidi(value);
|
||||||
if (midiNote % 1 !== 0) {
|
if (midiNote % 1 !== 0) {
|
||||||
|
|||||||
@ -2,7 +2,11 @@ import { AudibleEvent } from "./AbstractEvents";
|
|||||||
import { type Editor } from "../main";
|
import { type Editor } from "../main";
|
||||||
import { MidiConnection } from "../IO/MidiConnection";
|
import { MidiConnection } from "../IO/MidiConnection";
|
||||||
import { noteFromPc, chord as parseChord } from "zifferjs";
|
import { noteFromPc, chord as parseChord } from "zifferjs";
|
||||||
import { filterObject, arrayOfObjectsToObjectWithArrays, objectWithArraysToArrayOfObjects } from "../Utils/Generic";
|
import {
|
||||||
|
filterObject,
|
||||||
|
arrayOfObjectsToObjectWithArrays,
|
||||||
|
objectWithArraysToArrayOfObjects,
|
||||||
|
} from "../Utils/Generic";
|
||||||
|
|
||||||
export type MidiParams = {
|
export type MidiParams = {
|
||||||
note: number;
|
note: number;
|
||||||
@ -11,12 +15,15 @@ export type MidiParams = {
|
|||||||
port?: number;
|
port?: number;
|
||||||
sustain?: number;
|
sustain?: number;
|
||||||
velocity?: number;
|
velocity?: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class MidiEvent extends AudibleEvent {
|
export class MidiEvent extends AudibleEvent {
|
||||||
midiConnection: MidiConnection;
|
midiConnection: MidiConnection;
|
||||||
|
|
||||||
constructor(input: MidiParams, public app: Editor) {
|
constructor(
|
||||||
|
input: MidiParams,
|
||||||
|
public app: Editor,
|
||||||
|
) {
|
||||||
super(app);
|
super(app);
|
||||||
this.values = input;
|
this.values = input;
|
||||||
this.midiConnection = app.api.MidiConnection;
|
this.midiConnection = app.api.MidiConnection;
|
||||||
@ -40,7 +47,7 @@ export class MidiEvent extends AudibleEvent {
|
|||||||
velocity = (value: number | number[]): this => {
|
velocity = (value: number | number[]): this => {
|
||||||
this.values["velocity"] = value;
|
this.values["velocity"] = value;
|
||||||
return this;
|
return this;
|
||||||
}
|
};
|
||||||
|
|
||||||
channel = (value: number | number[]): this => {
|
channel = (value: number | number[]): this => {
|
||||||
this.values["channel"] = value;
|
this.values["channel"] = value;
|
||||||
@ -48,10 +55,12 @@ export class MidiEvent extends AudibleEvent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
port = (value: number | string | number[] | string[]): this => {
|
port = (value: number | string | number[] | string[]): this => {
|
||||||
if(typeof value === "string"){
|
if (typeof value === "string") {
|
||||||
this.values["port"] = this.midiConnection.getMidiOutputIndex(value);
|
this.values["port"] = this.midiConnection.getMidiOutputIndex(value);
|
||||||
} else if(Array.isArray(value)){
|
} else if (Array.isArray(value)) {
|
||||||
this.values["port"] = value.map((v) => typeof v === "string" ? this.midiConnection.getMidiOutputIndex(v) : v);
|
this.values["port"] = value.map((v) =>
|
||||||
|
typeof v === "string" ? this.midiConnection.getMidiOutputIndex(v) : v,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@ -86,25 +95,32 @@ export class MidiEvent extends AudibleEvent {
|
|||||||
|
|
||||||
update = (): void => {
|
update = (): void => {
|
||||||
// Get key, pitch, parsedScale and octave from this.values object
|
// Get key, pitch, parsedScale and octave from this.values object
|
||||||
const filteredValues = filterObject(this.values, ["key", "pitch", "parsedScale", "octave"]);
|
const filteredValues = filterObject(this.values, [
|
||||||
|
"key",
|
||||||
|
"pitch",
|
||||||
|
"parsedScale",
|
||||||
|
"octave",
|
||||||
|
]);
|
||||||
|
|
||||||
const events = objectWithArraysToArrayOfObjects(filteredValues,["parsedScale"]);
|
const events = objectWithArraysToArrayOfObjects(filteredValues, [
|
||||||
|
"parsedScale",
|
||||||
|
]);
|
||||||
|
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
const [note, bend] = noteFromPc(
|
const [note, bend] = noteFromPc(
|
||||||
event.key as number || "C4",
|
(event.key as number) || "C4",
|
||||||
event.pitch as number || 0,
|
(event.pitch as number) || 0,
|
||||||
event.parsedScale as number[] || event.scale || "MAJOR",
|
(event.parsedScale as number[]) || event.scale || "MAJOR",
|
||||||
event.octave as number || 0
|
(event.octave as number) || 0,
|
||||||
);
|
);
|
||||||
event.note = note;
|
event.note = note;
|
||||||
if(bend) event.bend = bend;
|
if (bend) event.bend = bend;
|
||||||
});
|
});
|
||||||
|
|
||||||
const newArrays = arrayOfObjectsToObjectWithArrays(events) as MidiParams;
|
const newArrays = arrayOfObjectsToObjectWithArrays(events) as MidiParams;
|
||||||
|
|
||||||
this.values.note = newArrays.note;
|
this.values.note = newArrays.note;
|
||||||
if(newArrays.bend) this.values.bend = newArrays.bend;
|
if (newArrays.bend) this.values.bend = newArrays.bend;
|
||||||
};
|
};
|
||||||
|
|
||||||
out = (): void => {
|
out = (): void => {
|
||||||
@ -114,9 +130,7 @@ export class MidiEvent extends AudibleEvent {
|
|||||||
const note = params.note ? params.note : 60;
|
const note = params.note ? params.note : 60;
|
||||||
|
|
||||||
const sustain = params.sustain
|
const sustain = params.sustain
|
||||||
? params.sustain *
|
? params.sustain * event.app.clock.pulse_duration * event.app.api.ppqn()
|
||||||
event.app.clock.pulse_duration *
|
|
||||||
event.app.api.ppqn()
|
|
||||||
: event.app.clock.pulse_duration * event.app.api.ppqn();
|
: event.app.clock.pulse_duration * event.app.api.ppqn();
|
||||||
|
|
||||||
const bend = params.bend ? params.bend : undefined;
|
const bend = params.bend ? params.bend : undefined;
|
||||||
@ -131,15 +145,16 @@ export class MidiEvent extends AudibleEvent {
|
|||||||
velocity,
|
velocity,
|
||||||
sustain,
|
sustain,
|
||||||
port,
|
port,
|
||||||
bend
|
bend,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const events = objectWithArraysToArrayOfObjects(this.values,["parsedScale"]) as MidiParams[];
|
const events = objectWithArraysToArrayOfObjects(this.values, [
|
||||||
|
"parsedScale",
|
||||||
|
]) as MidiParams[];
|
||||||
|
|
||||||
events.forEach((p: MidiParams) => {
|
events.forEach((p: MidiParams) => {
|
||||||
play(this,p);
|
play(this, p);
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,10 +11,7 @@ export class RestEvent extends AbstractEvent {
|
|||||||
return RestEvent.createRestProxy(this.values["noteLength"], this.app);
|
return RestEvent.createRestProxy(this.values["noteLength"], this.app);
|
||||||
};
|
};
|
||||||
|
|
||||||
public static createRestProxy = (
|
public static createRestProxy = (length: number, app: Editor): RestEvent => {
|
||||||
length: number,
|
|
||||||
app: Editor
|
|
||||||
): RestEvent => {
|
|
||||||
const instance = new RestEvent(length, app);
|
const instance = new RestEvent(length, app);
|
||||||
return new Proxy(instance, {
|
return new Proxy(instance, {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|||||||
@ -293,7 +293,10 @@ export class SoundEvent extends AudibleEvent {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(sound: string | string[] | SoundParams, public app: Editor) {
|
constructor(
|
||||||
|
sound: string | string[] | SoundParams,
|
||||||
|
public app: Editor,
|
||||||
|
) {
|
||||||
super(app);
|
super(app);
|
||||||
this.nudge = app.dough_nudge / 100;
|
this.nudge = app.dough_nudge / 100;
|
||||||
|
|
||||||
@ -312,7 +315,7 @@ export class SoundEvent extends AudibleEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private processSound = (
|
private processSound = (
|
||||||
sound: string | string[] | SoundParams | SoundParams[]
|
sound: string | string[] | SoundParams | SoundParams[],
|
||||||
): SoundParams => {
|
): SoundParams => {
|
||||||
if (Array.isArray(sound) && typeof sound[0] === "string") {
|
if (Array.isArray(sound) && typeof sound[0] === "string") {
|
||||||
const s: string[] = [];
|
const s: string[] = [];
|
||||||
@ -356,7 +359,7 @@ export class SoundEvent extends AudibleEvent {
|
|||||||
|
|
||||||
private updateValue<T>(
|
private updateValue<T>(
|
||||||
key: string,
|
key: string,
|
||||||
value: T | T[] | SoundParams[] | null
|
value: T | T[] | SoundParams[] | null,
|
||||||
): this {
|
): this {
|
||||||
if (value == null) return this;
|
if (value == null) return this;
|
||||||
this.values[key] = value;
|
this.values[key] = value;
|
||||||
@ -392,7 +395,7 @@ export class SoundEvent extends AudibleEvent {
|
|||||||
(event.key as number) || "C4",
|
(event.key as number) || "C4",
|
||||||
(event.pitch as number) || 0,
|
(event.pitch as number) || 0,
|
||||||
(event.parsedScale as number[]) || event.scale || "MAJOR",
|
(event.parsedScale as number[]) || event.scale || "MAJOR",
|
||||||
(event.octave as number) || 0
|
(event.octave as number) || 0,
|
||||||
);
|
);
|
||||||
event.note = note;
|
event.note = note;
|
||||||
event.freq = midiToFreq(note);
|
event.freq = midiToFreq(note);
|
||||||
@ -412,7 +415,7 @@ export class SoundEvent extends AudibleEvent {
|
|||||||
public invert = (howMany: number = 0) => {
|
public invert = (howMany: number = 0) => {
|
||||||
if (this.values.chord) {
|
if (this.values.chord) {
|
||||||
let notes = this.values.chord.map(
|
let notes = this.values.chord.map(
|
||||||
(obj: { [key: string]: number }) => obj.note
|
(obj: { [key: string]: number }) => obj.note,
|
||||||
);
|
);
|
||||||
notes = howMany < 0 ? [...notes].reverse() : notes;
|
notes = howMany < 0 ? [...notes].reverse() : notes;
|
||||||
for (let i = 0; i < Math.abs(howMany); i++) {
|
for (let i = 0; i < Math.abs(howMany); i++) {
|
||||||
@ -448,8 +451,14 @@ export class SoundEvent extends AudibleEvent {
|
|||||||
// const filteredEvent = filterObject(event, ["analyze","note","dur","freq","s"]);
|
// const filteredEvent = filterObject(event, ["analyze","note","dur","freq","s"]);
|
||||||
const filteredEvent = event;
|
const filteredEvent = event;
|
||||||
// No need for note if there is freq
|
// No need for note if there is freq
|
||||||
if (filteredEvent.freq) { delete filteredEvent.note; }
|
if (filteredEvent.freq) {
|
||||||
superdough(filteredEvent, this.nudge - this.app.clock.deviation, filteredEvent.dur);
|
delete filteredEvent.note;
|
||||||
|
}
|
||||||
|
superdough(
|
||||||
|
filteredEvent,
|
||||||
|
this.nudge - this.app.clock.deviation,
|
||||||
|
filteredEvent.dur,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { TonnetzSpaces } from "zifferjs/src/tonnetz";
|
|||||||
export type InputOptions = { [key: string]: string | number };
|
export type InputOptions = { [key: string]: string | number };
|
||||||
|
|
||||||
export class Player extends AbstractEvent {
|
export class Player extends AbstractEvent {
|
||||||
input: string|number;
|
input: string | number;
|
||||||
ziffers: Ziffers;
|
ziffers: Ziffers;
|
||||||
initCallTime: number = 0;
|
initCallTime: number = 0;
|
||||||
startCallTime: number = 0;
|
startCallTime: number = 0;
|
||||||
@ -26,7 +26,7 @@ export class Player extends AbstractEvent {
|
|||||||
skipIndex = 0;
|
skipIndex = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
input: string|number|Generator<number>,
|
input: string | number | Generator<number>,
|
||||||
options: InputOptions,
|
options: InputOptions,
|
||||||
public app: Editor,
|
public app: Editor,
|
||||||
zid: string = ""
|
zid: string = ""
|
||||||
@ -38,9 +38,9 @@ export class Player extends AbstractEvent {
|
|||||||
this.ziffers = new Ziffers(input, options);
|
this.ziffers = new Ziffers(input, options);
|
||||||
} else if (typeof input === "number") {
|
} else if (typeof input === "number") {
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.ziffers = Ziffers.fromNumber(input,options);
|
this.ziffers = Ziffers.fromNumber(input, options);
|
||||||
} else {
|
} else {
|
||||||
this.ziffers = Ziffers.fromGenerator(input,options);
|
this.ziffers = Ziffers.fromGenerator(input, options);
|
||||||
this.input = this.ziffers.input;
|
this.input = this.ziffers.input;
|
||||||
}
|
}
|
||||||
this.zid = zid;
|
this.zid = zid;
|
||||||
@ -246,6 +246,7 @@ export class Player extends AbstractEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tonnetz(transform: string, tonnetz: TonnetzSpaces = [3, 4, 5]) {
|
tonnetz(transform: string, tonnetz: TonnetzSpaces = [3, 4, 5]) {
|
||||||
|
// @ts-ignore
|
||||||
if (this.atTheBeginning()) this.ziffers.tonnetz(transform, tonnetz);
|
if (this.atTheBeginning()) this.ziffers.tonnetz(transform, tonnetz);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,8 +20,8 @@ ${makeExample(
|
|||||||
"Velocity manipulated by a counter",
|
"Velocity manipulated by a counter",
|
||||||
`
|
`
|
||||||
beat(.5)::snd('cp').vel($(1)%10 / 10).out()`,
|
beat(.5)::snd('cp').vel($(1)%10 / 10).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Amplitude Enveloppe
|
## Amplitude Enveloppe
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ beat(.25)::smooth(sound('sawtooth')
|
|||||||
beat(.25)::smooth(sound('sawtooth')
|
beat(.25)::smooth(sound('sawtooth')
|
||||||
.note([50,57,55,60].add(12).beat(1.5))).out();
|
.note([50,57,55,60].add(12).beat(1.5))).out();
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
Sometimes, using a full ADSR envelope is a bit overkill. There are other simpler controls to manipulate the envelope like the <ic>.ad</ic> method:
|
Sometimes, using a full ADSR envelope is a bit overkill. There are other simpler controls to manipulate the envelope like the <ic>.ad</ic> method:
|
||||||
|
|
||||||
@ -70,9 +70,8 @@ beat(.25)::smooth(sound('sawtooth')
|
|||||||
beat(.25)::smooth(sound('sawtooth')
|
beat(.25)::smooth(sound('sawtooth')
|
||||||
.note([50,57,55,60].add(12).beat(1.5))).out();
|
.note([50,57,55,60].add(12).beat(1.5))).out();
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
`}
|
|
||||||
|
|
||||||
|
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|||||||
@ -22,8 +22,8 @@ ${makeExample(
|
|||||||
beat(1) && sound('bd').out()
|
beat(1) && sound('bd').out()
|
||||||
beat(0.5) && sound('hh').out()
|
beat(0.5) && sound('hh').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
These commands, in plain english, can be translated to:
|
These commands, in plain english, can be translated to:
|
||||||
|
|
||||||
@ -38,8 +38,8 @@ ${makeExample(
|
|||||||
beat(1) && sound('bd').coarse(0.25).room(0.5).orbit(2).out();
|
beat(1) && sound('bd').coarse(0.25).room(0.5).orbit(2).out();
|
||||||
beat(0.5) && sound('hh').delay(0.25).delaytime(0.125).out();
|
beat(0.5) && sound('hh').delay(0.25).delaytime(0.125).out();
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Now, it translates as follows:
|
Now, it translates as follows:
|
||||||
|
|
||||||
@ -53,12 +53,14 @@ If you remove <ic>beat</ic> instruction, you will end up with a deluge of kick d
|
|||||||
|
|
||||||
To play a sound, you always need the <ic>.out()</ic> method at the end of your chain. THis method tells **Topos** to send the chain to the audio engine. The <ic>.out</ic> method can take an optional argument to send the sound to a numbered effect bus, from <ic>0</ic> to <ic>n</ic> :
|
To play a sound, you always need the <ic>.out()</ic> method at the end of your chain. THis method tells **Topos** to send the chain to the audio engine. The <ic>.out</ic> method can take an optional argument to send the sound to a numbered effect bus, from <ic>0</ic> to <ic>n</ic> :
|
||||||
|
|
||||||
${makeExample("Using the .out method",
|
${makeExample(
|
||||||
|
"Using the .out method",
|
||||||
`
|
`
|
||||||
// Playing a clap on the third bus (0-indexed)
|
// Playing a clap on the third bus (0-indexed)
|
||||||
beat(1)::sound('cp').out(2)
|
beat(1)::sound('cp').out(2)
|
||||||
`, true
|
`,
|
||||||
)}
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
Try to remove <ic>.out</ic>. You will see that no sound is playing at all!
|
Try to remove <ic>.out</ic>. You will see that no sound is playing at all!
|
||||||
|
|
||||||
@ -67,7 +69,7 @@ Try to remove <ic>.out</ic>. You will see that no sound is playing at all!
|
|||||||
- Sounds are **composed** by adding qualifiers/parameters that modify the sound or synthesizer you have picked (_e.g_ <ic>sound('...').blabla(...)..something(...).out()</ic>. Think of it as _audio chains_.
|
- Sounds are **composed** by adding qualifiers/parameters that modify the sound or synthesizer you have picked (_e.g_ <ic>sound('...').blabla(...)..something(...).out()</ic>. Think of it as _audio chains_.
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
'Complex sonic object',
|
"Complex sonic object",
|
||||||
`
|
`
|
||||||
beat(1) :: sound('pad').n(1)
|
beat(1) :: sound('pad').n(1)
|
||||||
.begin(rand(0, 0.4))
|
.begin(rand(0, 0.4))
|
||||||
@ -75,8 +77,8 @@ beat(1) :: sound('pad').n(1)
|
|||||||
.size(0.9).room(0.9)
|
.size(0.9).room(0.9)
|
||||||
.velocity(0.25)
|
.velocity(0.25)
|
||||||
.pan(usine()).release(2).out()`,
|
.pan(usine()).release(2).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Picking a specific sound
|
## Picking a specific sound
|
||||||
|
|
||||||
@ -104,8 +106,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(1) && sound('kick').n([1,2,3,4,5,6,7,8].pick()).out()
|
beat(1) && sound('kick').n([1,2,3,4,5,6,7,8].pick()).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
You can also use the <ic>:</ic> to pick a sample number directly from the <ic>sound</ic> function:
|
You can also use the <ic>:</ic> to pick a sample number directly from the <ic>sound</ic> function:
|
||||||
|
|
||||||
@ -114,7 +116,7 @@ You can also use the <ic>:</ic> to pick a sample number directly from the <ic>so
|
|||||||
`
|
`
|
||||||
beat(1) && sound('kick:3').out()
|
beat(1) && sound('kick:3').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
You can use any number to pick a sound. Don't be afraid of using a number too big. If the number exceeds the number of available samples, it will simply wrap around and loop infinitely over the folder. Let's demonstrate this by using the mouse over a very large sample folder:
|
You can use any number to pick a sound. Don't be afraid of using a number too big. If the number exceeds the number of available samples, it will simply wrap around and loop infinitely over the folder. Let's demonstrate this by using the mouse over a very large sample folder:
|
||||||
@ -124,8 +126,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
// Move your mouse to change the sample being used!
|
// Move your mouse to change the sample being used!
|
||||||
beat(.25) && sound('ST09').n(Math.floor(mouseX())).out()`,
|
beat(.25) && sound('ST09').n(Math.floor(mouseX())).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
The <ic>.n</ic> method is also used for synthesizers but it behaves differently. When using a synthesizer, this method can help you determine the number of harmonics in your waveform. See the **Synthesizers** section to learn more about this.
|
The <ic>.n</ic> method is also used for synthesizers but it behaves differently. When using a synthesizer, this method can help you determine the number of harmonics in your waveform. See the **Synthesizers** section to learn more about this.
|
||||||
@ -147,14 +149,18 @@ There is a special method to choose the _orbit_ that your sound is going to use:
|
|||||||
|
|
||||||
You can play a sound _dry_ and another sound _wet_. Take a look at this example where the reverb is only affecting one of the sounds:
|
You can play a sound _dry_ and another sound _wet_. Take a look at this example where the reverb is only affecting one of the sounds:
|
||||||
|
|
||||||
${makeExample("Dry and wet", `
|
${makeExample(
|
||||||
|
"Dry and wet",
|
||||||
|
`
|
||||||
|
|
||||||
// This sound is dry
|
// This sound is dry
|
||||||
beat(1)::sound('hh').out()
|
beat(1)::sound('hh').out()
|
||||||
|
|
||||||
// This sound is wet (reverb)
|
// This sound is wet (reverb)
|
||||||
beat(2)::sound('cp').orbit(2).room(0.5).size(8).out()
|
beat(2)::sound('cp').orbit(2).room(0.5).size(8).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
## The art of chaining
|
## The art of chaining
|
||||||
|
|
||||||
@ -168,10 +174,9 @@ beat(0.25) && sound('fhh')
|
|||||||
.room(0.9).size(0.9).gain(1)
|
.room(0.9).size(0.9).gain(1)
|
||||||
.cutoff(usine(1/2) * 5000)
|
.cutoff(usine(1/2) * 5000)
|
||||||
.out()`,
|
.out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Most audio parameters can be used both for samples and synthesizers. This is quite unconventional if you are familiar with a more traditional music software.
|
Most audio parameters can be used both for samples and synthesizers. This is quite unconventional if you are familiar with a more traditional music software.
|
||||||
`}
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,9 +23,8 @@ ${makeExample(
|
|||||||
beat(.5)::snd('pad').coarse($(1) % 16).clip(.5).out(); // Comment me
|
beat(.5)::snd('pad').coarse($(1) % 16).clip(.5).out(); // Comment me
|
||||||
beat(.5)::snd('pad').crush([16, 8, 4].beat(2)).clip(.5).out()
|
beat(.5)::snd('pad').crush([16, 8, 4].beat(2)).clip(.5).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
`}
|
|
||||||
|
|
||||||
|
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|||||||
@ -28,8 +28,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(2)::snd('cp').room(0.5).size(4).out()
|
beat(2)::snd('cp').room(0.5).size(4).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
## Delay
|
## Delay
|
||||||
|
|
||||||
@ -42,10 +42,13 @@ A good sounding delay unit that can go into feedback territory. Use it without m
|
|||||||
| <ic>delayfeedback</ic> | delayfb | Delay feedback (between <ic>0</ic> and <ic>1</ic>) |
|
| <ic>delayfeedback</ic> | delayfb | Delay feedback (between <ic>0</ic> and <ic>1</ic>) |
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Who doesn't like delay?", `
|
"Who doesn't like delay?",
|
||||||
|
`
|
||||||
beat(2)::snd('cp').delay(0.5).delaytime(0.75).delayfb(0.8).out()
|
beat(2)::snd('cp').delay(0.5).delaytime(0.75).delayfb(0.8).out()
|
||||||
beat(4)::snd('snare').out()
|
beat(4)::snd('snare').out()
|
||||||
beat(1)::snd('kick').out()`, true)}
|
beat(1)::snd('kick').out()`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
## Phaser
|
## Phaser
|
||||||
|
|
||||||
@ -56,13 +59,17 @@ beat(1)::snd('kick').out()`, true)}
|
|||||||
| <ic>phaserSweep</ic> | <ic>phassweep</ic> | Phaser frequency sweep (in hertz) |
|
| <ic>phaserSweep</ic> | <ic>phassweep</ic> | Phaser frequency sweep (in hertz) |
|
||||||
| <ic>phaserCenter</ic> | <ic>phascenter</ic> | Phaser center frequency (default to 1000) |
|
| <ic>phaserCenter</ic> | <ic>phascenter</ic> | Phaser center frequency (default to 1000) |
|
||||||
|
|
||||||
${makeExample("Super cool phaser lick", `
|
${makeExample(
|
||||||
|
"Super cool phaser lick",
|
||||||
|
`
|
||||||
rhythm(.5, 7, 8)::sound('wt_stereo')
|
rhythm(.5, 7, 8)::sound('wt_stereo')
|
||||||
.phaser(0.75).phaserSweep(3000)
|
.phaser(0.75).phaserSweep(3000)
|
||||||
.phaserCenter(1500).phaserDepth(1)
|
.phaserCenter(1500).phaserDepth(1)
|
||||||
.note([0, 1, 2, 3, 4, 5, 6].scale('pentatonic', 50).beat(0.25))
|
.note([0, 1, 2, 3, 4, 5, 6].scale('pentatonic', 50).beat(0.25))
|
||||||
.room(0.5).size(4).out()
|
.room(0.5).size(4).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
## Distorsion, saturation, destruction
|
## Distorsion, saturation, destruction
|
||||||
|
|
||||||
@ -81,6 +88,7 @@ ${makeExample(
|
|||||||
beat(.5)::snd('pad').coarse($(1) % 16).clip(.5).out(); // Comment me
|
beat(.5)::snd('pad').coarse($(1) % 16).clip(.5).out(); // Comment me
|
||||||
beat(.5)::snd('pad').crush([16, 8, 4].beat(2)).clip(.5).out()
|
beat(.5)::snd('pad').crush([16, 8, 4].beat(2)).clip(.5).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
`}
|
`;
|
||||||
|
};
|
||||||
|
|||||||
@ -38,45 +38,61 @@ beat(.5)::snd('pad').begin(0.2)
|
|||||||
.room(0.8).size(0.5)
|
.room(0.8).size(0.5)
|
||||||
.clip(1).out()
|
.clip(1).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
|
|
||||||
## Playback speed / pitching samples
|
## Playback speed / pitching samples
|
||||||
|
|
||||||
Let's play with the <ic>speed</ic> parameter to control the pitch of sample playback:
|
Let's play with the <ic>speed</ic> parameter to control the pitch of sample playback:
|
||||||
|
|
||||||
${makeExample("Controlling the playback speed", `
|
${makeExample(
|
||||||
|
"Controlling the playback speed",
|
||||||
|
`
|
||||||
beat(0.5)::sound('notes')
|
beat(0.5)::sound('notes')
|
||||||
.speed([1,2,3,4].palindrome().beat(0.5)).out()
|
.speed([1,2,3,4].palindrome().beat(0.5)).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
It also works by using negative values. It reverses the playback:
|
It also works by using negative values. It reverses the playback:
|
||||||
|
|
||||||
${makeExample("Playing samples backwards", `
|
${makeExample(
|
||||||
|
"Playing samples backwards",
|
||||||
|
`
|
||||||
beat(0.5)::sound('notes')
|
beat(0.5)::sound('notes')
|
||||||
.speed(-[1,2,3,4].palindrome().beat(0.5)).out()
|
.speed(-[1,2,3,4].palindrome().beat(0.5)).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
Of course you can play melodies using samples:
|
Of course you can play melodies using samples:
|
||||||
|
|
||||||
|
|
||||||
${makeExample("Playing melodies using samples", `
|
${makeExample(
|
||||||
|
"Playing melodies using samples",
|
||||||
|
`
|
||||||
beat(0.5)::sound('notes')
|
beat(0.5)::sound('notes')
|
||||||
.room(0.5).size(4)
|
.room(0.5).size(4)
|
||||||
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.5)).out()
|
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.5)).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
## Panning
|
## Panning
|
||||||
|
|
||||||
To pan samples, use the <ic>.pan</ic> method with a number between <ic>0</ic> and <ic>1</ic>.
|
To pan samples, use the <ic>.pan</ic> method with a number between <ic>0</ic> and <ic>1</ic>.
|
||||||
|
|
||||||
|
|
||||||
${makeExample("Playing melodies using samples", `
|
${makeExample(
|
||||||
|
"Playing melodies using samples",
|
||||||
|
`
|
||||||
beat(0.25)::sound('notes')
|
beat(0.25)::sound('notes')
|
||||||
.room(0.5).size(4).pan(r(0, 1))
|
.room(0.5).size(4).pan(r(0, 1))
|
||||||
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
|
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
## Looping over a sample
|
## Looping over a sample
|
||||||
@ -84,13 +100,17 @@ beat(0.25)::sound('notes')
|
|||||||
Using <ic>loop</ic> (<ic>1</ic> for looping), <ic>loopBegin</ic> and <ic>loopEnd</ic> (between <ic>0</ic> and <ic>1</ic>), you can loop over the length of a sample. It can be super effective to create granular effects.
|
Using <ic>loop</ic> (<ic>1</ic> for looping), <ic>loopBegin</ic> and <ic>loopEnd</ic> (between <ic>0</ic> and <ic>1</ic>), you can loop over the length of a sample. It can be super effective to create granular effects.
|
||||||
|
|
||||||
|
|
||||||
${makeExample("Granulation using loop", `
|
${makeExample(
|
||||||
|
"Granulation using loop",
|
||||||
|
`
|
||||||
beat(0.25)::sound('fikea').loop(1)
|
beat(0.25)::sound('fikea').loop(1)
|
||||||
.lpf(ir(2000, 5000))
|
.lpf(ir(2000, 5000))
|
||||||
.loopBegin(0).loopEnd(r(0, 1))
|
.loopBegin(0).loopEnd(r(0, 1))
|
||||||
.room(0.5).size(4).pan(r(0, 1))
|
.room(0.5).size(4).pan(r(0, 1))
|
||||||
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
|
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
## Stretching a sample
|
## Stretching a sample
|
||||||
|
|
||||||
@ -103,7 +123,7 @@ ${makeExample(
|
|||||||
beat(4) :: sound('amen1').n(11).stretch(4).out()
|
beat(4) :: sound('amen1').n(11).stretch(4).out()
|
||||||
beat(1) :: sound('kick').shape(0.35).out()`,
|
beat(1) :: sound('kick').shape(0.35).out()`,
|
||||||
true,
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
## Cutting samples
|
## Cutting samples
|
||||||
|
|
||||||
@ -111,34 +131,45 @@ Sometimes, you will find it necessary to cut a sample. It can be because the sam
|
|||||||
|
|
||||||
Know about the <ic>begin</ic> and <ic>end</ic> parameters. They are not related to the sampler itself, but to the length of the event you are playing. Let's cut the granular example:
|
Know about the <ic>begin</ic> and <ic>end</ic> parameters. They are not related to the sampler itself, but to the length of the event you are playing. Let's cut the granular example:
|
||||||
|
|
||||||
${makeExample("Cutting a sample using end", `
|
${makeExample(
|
||||||
|
"Cutting a sample using end",
|
||||||
|
`
|
||||||
beat(0.25)::sound('notes')
|
beat(0.25)::sound('notes')
|
||||||
.end(usine(1/2)/0.5)
|
.end(usine(1/2)/0.5)
|
||||||
.room(0.5).size(4).pan(r(0, 1))
|
.room(0.5).size(4).pan(r(0, 1))
|
||||||
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
|
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
You can also use <ic>clip</ic> to cut the sample everytime a new sample comes in:
|
You can also use <ic>clip</ic> to cut the sample everytime a new sample comes in:
|
||||||
|
|
||||||
|
|
||||||
${makeExample("Cutting a sample using end", `
|
${makeExample(
|
||||||
|
"Cutting a sample using end",
|
||||||
|
`
|
||||||
beat(0.125)::sound('notes')
|
beat(0.125)::sound('notes')
|
||||||
.cut(1)
|
.cut(1)
|
||||||
.room(0.5).size(4).pan(r(0, 1))
|
.room(0.5).size(4).pan(r(0, 1))
|
||||||
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.125)
|
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.125)
|
||||||
+ [-12,12].beat()).out()
|
+ [-12,12].beat()).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
## Adding vibrato to samples
|
## Adding vibrato to samples
|
||||||
|
|
||||||
You can add vibrato to any sample using <ic>vib</ic> and <ic>vibmod</ic>:
|
You can add vibrato to any sample using <ic>vib</ic> and <ic>vibmod</ic>:
|
||||||
|
|
||||||
${makeExample("Adding vibrato to a sample", `
|
${makeExample(
|
||||||
|
"Adding vibrato to a sample",
|
||||||
|
`
|
||||||
|
|
||||||
beat(1)::sound('fhang').vib([1, 2, 4].bar()).vibmod([0.5, 2].beat()).out()
|
beat(1)::sound('fhang').vib([1, 2, 4].bar()).vibmod([0.5, 2].beat()).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
`}
|
|
||||||
|
|
||||||
|
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|||||||
@ -22,7 +22,7 @@ The code you enter in any of the scripts is evaluated in strict mode. This tells
|
|||||||
|
|
||||||
- **about variables:** the state of your variables is not kept between iterations. If you write <ic>let a = 2</ic> and remove that value from your script, **it will crash**! Variable and state is not preserved between each run of the script. 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**. There is also **global variables**.
|
- **about variables:** the state of your variables is not kept between iterations. If you write <ic>let a = 2</ic> and remove that value from your script, **it will crash**! Variable and state is not preserved between each run of the script. 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**. There is also **global variables**.
|
||||||
- **about errors and printing:** your code will crash! Don't worry, we do our best to make it crash in the most gracious way possible. Most errors are caught and displayed in the interface. For weirder bugs, open the dev console with ${key_shortcut(
|
- **about errors and printing:** your code will crash! Don't worry, we do our best to make it crash in the most gracious way possible. Most errors are caught and displayed in the interface. For weirder bugs, open the dev console with ${key_shortcut(
|
||||||
"Ctrl + Shift + I"
|
"Ctrl + Shift + I",
|
||||||
)}. You cannot directly use <ic>console.log('hello, world')</ic> in the interface but you can use <ic>log(message)</ic> to print a one line message. You will have to open the console as well to see your messages being printed there!
|
)}. You cannot directly use <ic>console.log('hello, world')</ic> in the interface but you can use <ic>log(message)</ic> to print a one line message. You will have to open the console as well to see your messages being printed there!
|
||||||
- **about new syntax:** sometimes, we had some fun with JavaScript's syntax in order to make it easier/faster to write on stage. <ic>&&</ic> can also be written <ic>::</ic> or <ic>-></ic> because it is faster to type or better for the eyes!
|
- **about new syntax:** sometimes, we had some fun with JavaScript's syntax in order to make it easier/faster to write on stage. <ic>&&</ic> can also be written <ic>::</ic> or <ic>-></ic> because it is faster to type or better for the eyes!
|
||||||
|
|
||||||
@ -42,8 +42,8 @@ beat(1) :: snd('bd').out()
|
|||||||
//// beat(1) :: snd('bd').out()
|
//// beat(1) :: snd('bd').out()
|
||||||
|
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"More complex conditions using ?",
|
"More complex conditions using ?",
|
||||||
@ -52,8 +52,8 @@ ${makeExample(
|
|||||||
beat(4) ? snd('kick').out() : beat(2) :: snd('snare').out()
|
beat(4) ? snd('kick').out() : beat(2) :: snd('snare').out()
|
||||||
// (true) ? log('very true') : log('very false')
|
// (true) ? log('very true') : log('very false')
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
@ -63,8 +63,8 @@ ${makeExample(
|
|||||||
beat(4) ? snd('kick').out() : beat(2) :: snd('snare').out()
|
beat(4) ? snd('kick').out() : beat(2) :: snd('snare').out()
|
||||||
!beat(2) :: beat(0.5) :: snd('clap').out()
|
!beat(2) :: beat(0.5) :: snd('clap').out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
# About crashes and bugs
|
# About crashes and bugs
|
||||||
|
|
||||||
@ -76,8 +76,8 @@ ${makeExample(
|
|||||||
// This is crashing. See? No harm!
|
// This is crashing. See? No harm!
|
||||||
qjldfqsdklqsjdlkqjsdlqkjdlksjd
|
qjldfqsdklqsjdlkqjsdlqkjdlksjd
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -18,22 +18,22 @@ The Topos interface is designed on a simple concept: _scripts_ and _universes_.
|
|||||||
Every Topos session is composed of **local**, **global** and **init** scripts. These scripts form a structure called a "_universe_". The scripts can describe whatever you want: songs, sketches, small tools, or whatever. All the scripts are written using the JavaScript programming language. They describe a musical or algorithmic process. You can call them anytime.
|
Every Topos session is composed of **local**, **global** and **init** scripts. These scripts form a structure called a "_universe_". The scripts can describe whatever you want: songs, sketches, small tools, or whatever. All the scripts are written using the JavaScript programming language. They describe a musical or algorithmic process. You can call them anytime.
|
||||||
|
|
||||||
- **the global script** (${key_shortcut(
|
- **the global script** (${key_shortcut(
|
||||||
"Ctrl + G"
|
"Ctrl + G",
|
||||||
)}): _Evaluated for every clock pulse_. The central piece, acting as the conductor for all the other scripts. You can also jam directly from the global script to test your ideas before pushing them to a separate script. You can also access that script using the ${key_shortcut(
|
)}): _Evaluated for every clock pulse_. The central piece, acting as the conductor for all the other scripts. You can also jam directly from the global script to test your ideas before pushing them to a separate script. You can also access that script using the ${key_shortcut(
|
||||||
"F10"
|
"F10",
|
||||||
)} key.
|
)} key.
|
||||||
- **the local scripts** (${key_shortcut(
|
- **the local scripts** (${key_shortcut(
|
||||||
"Ctrl + L"
|
"Ctrl + L",
|
||||||
)}): _Evaluated on demand_. Local scripts are used to store anything too complex to sit in the global script. It can be a musical process, a whole section of your composition, a complex controller that you've built for your hardware, etc... You can also switch to one of the local scripts by using the function keys (${key_shortcut(
|
)}): _Evaluated on demand_. Local scripts are used to store anything too complex to sit in the global script. It can be a musical process, a whole section of your composition, a complex controller that you've built for your hardware, etc... You can also switch to one of the local scripts by using the function keys (${key_shortcut(
|
||||||
"F1"
|
"F1",
|
||||||
)} to ${key_shortcut("F9")}).
|
)} to ${key_shortcut("F9")}).
|
||||||
- **the init script** (${key_shortcut(
|
- **the init script** (${key_shortcut(
|
||||||
"Ctrl + I"
|
"Ctrl + I",
|
||||||
)}): _Evaluated on program load_. Used to set up the software the session to the desired state before playing, for example changing bpm or to initialize global variables (See Functions). You can also access that script using the ${key_shortcut(
|
)}): _Evaluated on program load_. Used to set up the software the session to the desired state before playing, for example changing bpm or to initialize global variables (See Functions). You can also access that script using the ${key_shortcut(
|
||||||
"F11"
|
"F11",
|
||||||
)} key.
|
)} key.
|
||||||
- **the note file** (${key_shortcut(
|
- **the note file** (${key_shortcut(
|
||||||
"Ctrl + N"
|
"Ctrl + N",
|
||||||
)}): _Not evaluated_. Used to store your thoughts or commentaries about the session you are currently playing. It is nothing more than a scratchpad really!
|
)}): _Not evaluated_. Used to store your thoughts or commentaries about the session you are currently playing. It is nothing more than a scratchpad really!
|
||||||
|
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ ${makeExample(
|
|||||||
beat(1) :: script(1) // Calling local script n°1
|
beat(1) :: script(1) // Calling local script n°1
|
||||||
flip(4) :: beat(.5) :: script(2) // Calling script n°2
|
flip(4) :: beat(.5) :: script(2) // Calling script n°2
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Script execution can become musical too!",
|
"Script execution can become musical too!",
|
||||||
@ -54,8 +54,8 @@ beat(1) :: script([1, 3, 5].pick())
|
|||||||
flip(4) :: beat([.5, .25].beat(16)) :: script(
|
flip(4) :: beat([.5, .25].beat(16)) :: script(
|
||||||
[5, 6, 7, 8].beat())
|
[5, 6, 7, 8].beat())
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
### Navigating the interface
|
### Navigating the interface
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ There are some useful functions to help you manage your scripts:
|
|||||||
|
|
||||||
|
|
||||||
A set of files is called a _universe_. You can switch between universes immediately immediately by pressing ${key_shortcut(
|
A set of files is called a _universe_. You can switch between universes immediately immediately by pressing ${key_shortcut(
|
||||||
"Ctrl + B"
|
"Ctrl + B",
|
||||||
)}. You can also create a new universe by entering a name. Load a universe by typing its name. Once a universe is loaded, it is not possible to call any data/code from any other universe. Switching between universes does not stop the transport nor reset the clock. The context switches but time keeps flowing. This can be useful for transitioning between songs / parts.
|
)}. You can also create a new universe by entering a name. Load a universe by typing its name. Once a universe is loaded, it is not possible to call any data/code from any other universe. Switching between universes does not stop the transport nor reset the clock. The context switches but time keeps flowing. This can be useful for transitioning between songs / parts.
|
||||||
|
|
||||||
There are some useful functions to help you manage your universes:
|
There are some useful functions to help you manage your universes:
|
||||||
|
|||||||
@ -14,10 +14,10 @@ Topos is made to be controlled entirely with a keyboard. It is recommanded to st
|
|||||||
| Shortcut | Key | Description |
|
| Shortcut | Key | Description |
|
||||||
|----------|-------|------------------------------------------------------------|
|
|----------|-------|------------------------------------------------------------|
|
||||||
|**Start/Pause** transport|${key_shortcut(
|
|**Start/Pause** transport|${key_shortcut(
|
||||||
"Ctrl + P"
|
"Ctrl + P",
|
||||||
)}|Start or pause audio playback|
|
)}|Start or pause audio playback|
|
||||||
|**Stop** the transport |${key_shortcut(
|
|**Stop** the transport |${key_shortcut(
|
||||||
"Ctrl + S"
|
"Ctrl + S",
|
||||||
)}|Stop and rewind audio playback|
|
)}|Stop and rewind audio playback|
|
||||||
|
|
||||||
### Moving in the interface
|
### Moving in the interface
|
||||||
@ -26,15 +26,15 @@ Topos is made to be controlled entirely with a keyboard. It is recommanded to st
|
|||||||
|----------|-------|------------------------------------------------------------|
|
|----------|-------|------------------------------------------------------------|
|
||||||
|Universe switch|${key_shortcut("Ctrl + B")}|Switch to a new universe|
|
|Universe switch|${key_shortcut("Ctrl + B")}|Switch to a new universe|
|
||||||
|Global Script|${key_shortcut("Ctrl + G")} or ${key_shortcut(
|
|Global Script|${key_shortcut("Ctrl + G")} or ${key_shortcut(
|
||||||
"F10"
|
"F10",
|
||||||
)}|Switch to global script |
|
)}|Switch to global script |
|
||||||
|Local scripts|${key_shortcut("Ctrl + L")} or ${key_shortcut(
|
|Local scripts|${key_shortcut("Ctrl + L")} or ${key_shortcut(
|
||||||
"F11"
|
"F11",
|
||||||
)}|Switch to local scripts |
|
)}|Switch to local scripts |
|
||||||
|Init script|${key_shortcut("Ctrl + L")}|Switch to init script|
|
|Init script|${key_shortcut("Ctrl + L")}|Switch to init script|
|
||||||
|Note File|${key_shortcut("Ctrl + N")}|Switch to note file|
|
|Note File|${key_shortcut("Ctrl + N")}|Switch to note file|
|
||||||
|Local Script|${key_shortcut("F1")} to ${key_shortcut(
|
|Local Script|${key_shortcut("F1")} to ${key_shortcut(
|
||||||
"F9"
|
"F9",
|
||||||
)}|Switch to a specific local script|
|
)}|Switch to a specific local script|
|
||||||
|Documentation|${key_shortcut("Ctrl + D")}|Open the documentation|
|
|Documentation|${key_shortcut("Ctrl + D")}|Open the documentation|
|
||||||
|
|
||||||
@ -44,10 +44,10 @@ Topos is made to be controlled entirely with a keyboard. It is recommanded to st
|
|||||||
|----------|-------|------------------------------------------------------------|
|
|----------|-------|------------------------------------------------------------|
|
||||||
|Evaluate|${key_shortcut("Ctrl + Enter")}| Evaluate the current script |
|
|Evaluate|${key_shortcut("Ctrl + Enter")}| Evaluate the current script |
|
||||||
|Local Eval|${key_shortcut("Ctrl + F1")} to ${key_shortcut(
|
|Local Eval|${key_shortcut("Ctrl + F1")} to ${key_shortcut(
|
||||||
"Ctrl + F9"
|
"Ctrl + F9",
|
||||||
)}|Local File Evaluation|
|
)}|Local File Evaluation|
|
||||||
|Force Eval|${key_shortcut(
|
|Force Eval|${key_shortcut(
|
||||||
"Ctrl + Shift + Enter"
|
"Ctrl + Shift + Enter",
|
||||||
)}|Force evaluation of the current script|
|
)}|Force evaluation of the current script|
|
||||||
|
|
||||||
### Special
|
### Special
|
||||||
@ -60,9 +60,9 @@ Topos is made to be controlled entirely with a keyboard. It is recommanded to st
|
|||||||
# Keyboard Fill
|
# Keyboard Fill
|
||||||
|
|
||||||
By pressing the ${key_shortcut(
|
By pressing the ${key_shortcut(
|
||||||
"Alt"
|
"Alt",
|
||||||
)} key, you can trigger the <ic>Fill</ic> mode which can either be <ic>true</ic> or <ic>false</ic>. The fill will be set to <ic>true</ic> as long as the key is held. Try pressing ${key_shortcut(
|
)} key, you can trigger the <ic>Fill</ic> mode which can either be <ic>true</ic> or <ic>false</ic>. The fill will be set to <ic>true</ic> as long as the key is held. Try pressing ${key_shortcut(
|
||||||
"Alt"
|
"Alt",
|
||||||
)} when playing this example:
|
)} when playing this example:
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
@ -70,7 +70,7 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(fill() ? 1/4 : 1/2)::sound('cp').out()
|
beat(fill() ? 1/4 : 1/2)::sound('cp').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -27,8 +27,8 @@ beat(.25) :: sound('sine')
|
|||||||
.pan(r(0, 1))
|
.pan(r(0, 1))
|
||||||
.room(0.35).size(4).out()
|
.room(0.35).size(4).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@ -46,8 +46,8 @@ beat(.25) :: sound('sine')
|
|||||||
.delay(0.5).delayt(1/6).delayfb(0.2)
|
.delay(0.5).delayt(1/6).delayfb(0.2)
|
||||||
.note(noteX())
|
.note(noteX())
|
||||||
.room(0.35).size(4).out()`,
|
.room(0.35).size(4).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Mouse and Arrays
|
## Mouse and Arrays
|
||||||
|
|
||||||
@ -64,8 +64,8 @@ log([1,2,3,4].mouseX())
|
|||||||
log([4,5,6,7].mouseY())
|
log([4,5,6,7].mouseY())
|
||||||
|
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -8,14 +8,14 @@ export const introduction = (application: Editor): string => {
|
|||||||
# Welcome
|
# Welcome
|
||||||
|
|
||||||
Welcome to the **Topos** documentation. You can jump here anytime by pressing ${key_shortcut(
|
Welcome to the **Topos** documentation. You can jump here anytime by pressing ${key_shortcut(
|
||||||
"Ctrl + D"
|
"Ctrl + D",
|
||||||
)}. Press again to make the documentation disappear. Contributions are much appreciated! The documentation [lives here](https://github.com/Bubobubobubobubo/topos/tree/main/src/documentation).
|
)}. Press again to make the documentation disappear. Contributions are much appreciated! The documentation [lives here](https://github.com/Bubobubobubobubo/topos/tree/main/src/documentation).
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Welcome! Eval to get started",
|
"Welcome! Eval to get started",
|
||||||
examples[Math.floor(Math.random() * examples.length)],
|
examples[Math.floor(Math.random() * examples.length)],
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
# What is Topos?
|
# What is Topos?
|
||||||
|
|
||||||
@ -30,8 +30,8 @@ rhythm(.25, [5, 7].beat(2), 8) :: sound(['hc', 'fikea', 'hat'].pick(1))
|
|||||||
.db(-ir(1,8)).speed([1,[0.5, 2].pick()]).room(0.5).size(3).o(4).out()
|
.db(-ir(1,8)).speed([1,[0.5, 2].pick()]).room(0.5).size(3).o(4).out()
|
||||||
beat([2,0.5].dur(13.5, 0.5))::snd('fsoftsnare')
|
beat([2,0.5].dur(13.5, 0.5))::snd('fsoftsnare')
|
||||||
.n(0).speed([1, 0.5]).o(4).out()`,
|
.n(0).speed([1, 0.5]).o(4).out()`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Computer music should be immediate and intuitive",
|
"Computer music should be immediate and intuitive",
|
||||||
@ -47,8 +47,8 @@ beat(.25)::snd('sine')
|
|||||||
.delay(0.5).delayt(0.25).delayfb(0.7) // Delay
|
.delay(0.5).delayt(0.25).delayfb(0.7) // Delay
|
||||||
.room(0.5).size(8) // Reverb
|
.room(0.5).size(8) // Reverb
|
||||||
.out()`,
|
.out()`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Making the web less dreadful, one beep at at time",
|
"Making the web less dreadful, one beep at at time",
|
||||||
@ -58,15 +58,15 @@ beat(.5) :: sound('sid').n($(2))
|
|||||||
beat(.25) :: sound('sid').note(
|
beat(.25) :: sound('sid').note(
|
||||||
[34, 36, 41].beat(.25) + [[0,-24].pick(),12].beat())
|
[34, 36, 41].beat(.25) + [[0,-24].pick(),12].beat())
|
||||||
.room(0.9).size(0.9).n(4).out()`,
|
.room(0.9).size(0.9).n(4).out()`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Topos is deeply inspired by the [Monome Teletype](https://monome.org/). The Teletype is/was an open source hardware module for Eurorack synthesizers. While the Teletype was initially born as an hardware module, Topos aims to be a web-browser based cousin of it! It is a sequencer, a scriptable interface, a companion for algorithmic music-making. Topos wishes to fullfill the same goal as the Teletype, keeping the same spirit alive on the web. It is free, open-source, and made to be shared and used by everyone. Learn more about live coding on [livecoding.fr](https://livecoding.fr).
|
Topos is deeply inspired by the [Monome Teletype](https://monome.org/). The Teletype is/was an open source hardware module for Eurorack synthesizers. While the Teletype was initially born as an hardware module, Topos aims to be a web-browser based cousin of it! It is a sequencer, a scriptable interface, a companion for algorithmic music-making. Topos wishes to fullfill the same goal as the Teletype, keeping the same spirit alive on the web. It is free, open-source, and made to be shared and used by everyone. Learn more about live coding on [livecoding.fr](https://livecoding.fr).
|
||||||
|
|
||||||
## Demo Songs
|
## Demo Songs
|
||||||
|
|
||||||
Reloading the application will get you one random song example to study every time. Press ${key_shortcut(
|
Reloading the application will get you one random song example to study every time. Press ${key_shortcut(
|
||||||
"F5"
|
"F5",
|
||||||
)} and listen to them all! The demo songs are also used a bit everywhere in the documentation to illustrate some of the working principles :).
|
)} and listen to them all! The demo songs are also used a bit everywhere in the documentation to illustrate some of the working principles :).
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,8 +12,9 @@ ${makeExample(
|
|||||||
"Method chaining",
|
"Method chaining",
|
||||||
`
|
`
|
||||||
beat(1)::sound('bd').speed(2).lpf(500).out()
|
beat(1)::sound('bd').speed(2).lpf(500).out()
|
||||||
`, true
|
`,
|
||||||
)}
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
Method chains become fun if you add just a little bit of complexity to them. You can start to add conditions, start to register complex chains to be re-used later on, etc.. We will not remind you how to write basic chains. The whole documentation is full of examples! Let's explore more delicate patterns!
|
Method chains become fun if you add just a little bit of complexity to them. You can start to add conditions, start to register complex chains to be re-used later on, etc.. We will not remind you how to write basic chains. The whole documentation is full of examples! Let's explore more delicate patterns!
|
||||||
|
|
||||||
@ -29,8 +30,9 @@ register('juxrev', n=>n.pan([0, 1]).speed([1, -1]))
|
|||||||
|
|
||||||
// Using our new abstraction
|
// Using our new abstraction
|
||||||
beat(1)::sound('fhh').juxrev().out()
|
beat(1)::sound('fhh').juxrev().out()
|
||||||
`, true
|
`,
|
||||||
)}
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
This is an extremely powerful construct. For example, you can use it to create synthesizer presets and reuse them later on. You can also define parameters for your registered functions. For example:
|
This is an extremely powerful construct. For example, you can use it to create synthesizer presets and reuse them later on. You can also define parameters for your registered functions. For example:
|
||||||
|
|
||||||
@ -48,8 +50,9 @@ register('sub', (n,x=4,y=80)=>n.ad(0, .25)
|
|||||||
// Using it with an arpeggio
|
// Using it with an arpeggio
|
||||||
rhythm(.25, [6, 8].beat(), 12)::sound('sine')
|
rhythm(.25, [6, 8].beat(), 12)::sound('sine')
|
||||||
.note([0, 2, 4, 5].scale('minor', 50).beat(0.5))
|
.note([0, 2, 4, 5].scale('minor', 50).beat(0.5))
|
||||||
.sub(8).out()`, true
|
.sub(8).out()`,
|
||||||
)}
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
## Conditional chaining
|
## Conditional chaining
|
||||||
@ -65,8 +68,8 @@ beat(.5) && sound('fhh')
|
|||||||
.odds(1/4, s => s.speed(irand(1,4)))
|
.odds(1/4, s => s.speed(irand(1,4)))
|
||||||
.rarely(s => s.room(0.5).size(8).speed(0.5))
|
.rarely(s => s.room(0.5).size(8).speed(0.5))
|
||||||
.out()`,
|
.out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Chance to play a random note",
|
"Chance to play a random note",
|
||||||
`
|
`
|
||||||
@ -77,8 +80,8 @@ beat(.5) && sound('pluck').note(60)
|
|||||||
.note(62)
|
.note(62)
|
||||||
.room(0.5).size(3)
|
.room(0.5).size(3)
|
||||||
.out()`,
|
.out()`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
There is a growing collection of probability and chance methods you can use:
|
There is a growing collection of probability and chance methods you can use:
|
||||||
|
|
||||||
@ -111,8 +114,8 @@ ${makeExample(
|
|||||||
.often(n => n.note+=4)
|
.often(n => n.note+=4)
|
||||||
.sometimes(s => s.velocity(irand(50,100)))
|
.sometimes(s => s.velocity(irand(50,100)))
|
||||||
.out()`,
|
.out()`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
## Ziffers
|
## Ziffers
|
||||||
|
|
||||||
@ -134,7 +137,7 @@ z1('s 0 5 7 0 3 7 0 2 7 0 1 7 0 1 6 5 4 3 2')
|
|||||||
.odds(1/2, n => n.speed(0.5))
|
.odds(1/2, n => n.speed(0.5))
|
||||||
.room(0.5).size(0.5).out()
|
.room(0.5).size(0.5).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,8 +35,8 @@ beat(.5)::snd('pad').begin(0.2)
|
|||||||
.room(0.8).size(0.5)
|
.room(0.8).size(0.5)
|
||||||
.clip(1).out()
|
.clip(1).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Playing an amen break",
|
"Playing an amen break",
|
||||||
@ -45,7 +45,7 @@ ${makeExample(
|
|||||||
beat(4) :: sound('amen1').n(11).stretch(4).out()
|
beat(4) :: sound('amen1').n(11).stretch(4).out()
|
||||||
beat(1) :: sound('kick').shape(0.35).out()`,
|
beat(1) :: sound('kick').shape(0.35).out()`,
|
||||||
true,
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
|
|
||||||
## Filters
|
## Filters
|
||||||
@ -70,8 +70,8 @@ beat(.5) && snd('sawtooth')
|
|||||||
.resonance(0.9).freq([100,150].pick())
|
.resonance(0.9).freq([100,150].pick())
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
|
|
||||||
## Compression
|
## Compression
|
||||||
|
|||||||
@ -19,16 +19,16 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(1) :: script(1)
|
beat(1) :: script(1)
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Calling mutliple scripts at the same time.",
|
"Calling mutliple scripts at the same time.",
|
||||||
`
|
`
|
||||||
beat(1) :: script(1, 3, 5)
|
beat(1) :: script(1, 3, 5)
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Math functions
|
## Math functions
|
||||||
|
|
||||||
@ -48,8 +48,8 @@ ${makeExample(
|
|||||||
beat(.5) :: delay(usine(.125) * 80, () => sound('east').out())
|
beat(.5) :: delay(usine(.125) * 80, () => sound('east').out())
|
||||||
beat(.5) :: delay(50, () => sound('east').out())
|
beat(.5) :: delay(50, () => sound('east').out())
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>delayr(ms: number, nb: number, func: Function): void</ic>: Delays the execution of a function by a given number of milliseconds, repeated a given number of times.
|
- <ic>delayr(ms: number, nb: number, func: Function): void</ic>: Delays the execution of a function by a given number of milliseconds, repeated a given number of times.
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ ${makeExample(
|
|||||||
beat(1) :: delayr(50, 4, () => sound('east').speed([0.5,.25].beat()).out())
|
beat(1) :: delayr(50, 4, () => sound('east').speed([0.5,.25].beat()).out())
|
||||||
flip(2) :: beat(2) :: delayr(150, 4, () => sound('east').speed([0.5,.25].beat() * 4).out())
|
flip(2) :: beat(2) :: delayr(150, 4, () => sound('east').speed([0.5,.25].beat() * 4).out())
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -969,7 +969,7 @@ export const inlineHoveringTips = hoverTooltip(
|
|||||||
return { dom };
|
return { dom };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const toposCompletions = (context: CompletionContext) => {
|
export const toposCompletions = (context: CompletionContext) => {
|
||||||
|
|||||||
@ -24,7 +24,7 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(1) && active_notes() && sound('sine').chord(active_notes()).out()
|
beat(1) && active_notes() && sound('sine').chord(active_notes()).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
@ -34,7 +34,7 @@ ${makeExample(
|
|||||||
active_notes().beat(0.5)+[12,24].beat(0.25)
|
active_notes().beat(0.5)+[12,24].beat(0.25)
|
||||||
).cutoff(300 + usine(1/4) * 2000).out()
|
).cutoff(300 + usine(1/4) * 2000).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ ${makeExample(
|
|||||||
beat(0.25) && sticky_notes() && sound('arp')
|
beat(0.25) && sticky_notes() && sound('arp')
|
||||||
.note(sticky_notes().palindrome().beat(0.25)).out()
|
.note(sticky_notes().palindrome().beat(0.25)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
* <ic>last_note(channel?: number)</ic>: returns the last note that has been received. Returns 60 if no other notes have been received.
|
* <ic>last_note(channel?: number)</ic>: returns the last note that has been received. Returns 60 if no other notes have been received.
|
||||||
@ -58,7 +58,7 @@ ${makeExample(
|
|||||||
.vib([1, 3, 5].beat(1))
|
.vib([1, 3, 5].beat(1))
|
||||||
.vibmod([1,3,2,4].beat(2)).out()
|
.vibmod([1,3,2,4].beat(2)).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
* <ic>buffer()</ic>: return true if there are notes in the buffer.
|
* <ic>buffer()</ic>: return true if there are notes in the buffer.
|
||||||
@ -69,7 +69,7 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(1) && buffer() && sound('sine').note(buffer_note()).out()
|
beat(1) && buffer() && sound('sine').note(buffer_note()).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(0.5) && sound('arp').note(last_cc(74)).out()
|
beat(0.5) && sound('arp').note(last_cc(74)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
@ -105,7 +105,7 @@ beat(last_cc(74)/127*.5) :: sound('sine')
|
|||||||
.sustain(last_cc(74)/127*.25)
|
.sustain(last_cc(74)/127*.25)
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ Topos can output scales to external keyboards lighted keys using the following f
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Show scale on external keyboard",
|
"Show scale on external keyboard",
|
||||||
`show_scale("F","aeolian",0,4)`,
|
`show_scale("F","aeolian",0,4)`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample("Hide scale", `hide_scale("F","aeolian",0,4)`, true)}
|
${makeExample("Hide scale", `hide_scale("F","aeolian",0,4)`, true)}
|
||||||
|
|||||||
@ -17,8 +17,8 @@ Low Frequency Oscillators (_LFOs_) are an important piece in any digital audio w
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Modulating the speed of a sample player using a sine LFO",
|
"Modulating the speed of a sample player using a sine LFO",
|
||||||
`beat(.25) && snd('cp').speed(1 + usine(0.25) * 2).out()`,
|
`beat(.25) && snd('cp').speed(1 + usine(0.25) * 2).out()`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
- <ic>triangle(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a triangle oscillation between <ic>-1</ic> and <ic>1</ic>.
|
- <ic>triangle(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a triangle oscillation between <ic>-1</ic> and <ic>1</ic>.
|
||||||
- <ic>utriangle(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a triangle oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_.
|
- <ic>utriangle(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a triangle oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_.
|
||||||
@ -26,8 +26,8 @@ ${makeExample(
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Modulating the speed of a sample player using a triangle LFO",
|
"Modulating the speed of a sample player using a triangle LFO",
|
||||||
`beat(.25) && snd('cp').speed(1 + utriangle(0.25) * 2).out()`,
|
`beat(.25) && snd('cp').speed(1 + utriangle(0.25) * 2).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>saw(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a sawtooth-like oscillation between <ic>-1</ic> and <ic>1</ic>.
|
- <ic>saw(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a sawtooth-like oscillation between <ic>-1</ic> and <ic>1</ic>.
|
||||||
- <ic>usaw(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a sawtooth-like oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_.
|
- <ic>usaw(freq: number = 1, times: number = 1, offset: number= 0): number</ic>: returns a sawtooth-like oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_.
|
||||||
@ -35,8 +35,8 @@ ${makeExample(
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Modulating the speed of a sample player using a saw LFO",
|
"Modulating the speed of a sample player using a saw LFO",
|
||||||
`beat(.25) && snd('cp').speed(1 + usaw(0.25) * 2).out()`,
|
`beat(.25) && snd('cp').speed(1 + usaw(0.25) * 2).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>square(freq: number = 1, times: number = 1, offset: number= 0, duty: number = .5): number</ic>: returns a square wave oscillation between <ic>-1</ic> and <ic>1</ic>. You can also control the duty cycle using the <ic>duty</ic> parameter.
|
- <ic>square(freq: number = 1, times: number = 1, offset: number= 0, duty: number = .5): number</ic>: returns a square wave oscillation between <ic>-1</ic> and <ic>1</ic>. You can also control the duty cycle using the <ic>duty</ic> parameter.
|
||||||
- <ic>usquare(freq: number = 1, times: number = 1, offset: number= 0, duty: number = .5): number</ic>: returns a square wave oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_. You can also control the duty cycle using the <ic>duty</ic> parameter.
|
- <ic>usquare(freq: number = 1, times: number = 1, offset: number= 0, duty: number = .5): number</ic>: returns a square wave oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_. You can also control the duty cycle using the <ic>duty</ic> parameter.
|
||||||
@ -44,16 +44,16 @@ ${makeExample(
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Modulating the speed of a sample player using a square LFO",
|
"Modulating the speed of a sample player using a square LFO",
|
||||||
`beat(.25) && snd('cp').speed(1 + usquare(0.25, 0, 0.25) * 2).out()`,
|
`beat(.25) && snd('cp').speed(1 + usquare(0.25, 0, 0.25) * 2).out()`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
- <ic>noise(times: number = 1)</ic>: returns a random value between -1 and 1.
|
- <ic>noise(times: number = 1)</ic>: returns a random value between -1 and 1.
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Modulating the speed of a sample player using noise",
|
"Modulating the speed of a sample player using noise",
|
||||||
`beat(.25) && snd('cp').speed(1 + noise() * 2).out()`,
|
`beat(.25) && snd('cp').speed(1 + noise() * 2).out()`,
|
||||||
true
|
true,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
`
|
`;
|
||||||
}
|
};
|
||||||
|
|||||||
@ -19,8 +19,8 @@ ${makeExample(
|
|||||||
// Playing each script for 8 bars in succession
|
// Playing each script for 8 bars in succession
|
||||||
script([1,2,3,4].bar(8))
|
script([1,2,3,4].bar(8))
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
You can also give a specific duration to each section using <ic>.dur</ic>:
|
You can also give a specific duration to each section using <ic>.dur</ic>:
|
||||||
|
|
||||||
@ -29,8 +29,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
script([1,2,3,4].dur(8, 2, 16, 4))
|
script([1,2,3,4].dur(8, 2, 16, 4))
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- **Use universes as well**. Transitions between universes are _seamless_, instantaneous. Just switch to different content if you ever hit the limitations of the current _universe_.
|
- **Use universes as well**. Transitions between universes are _seamless_, instantaneous. Just switch to different content if you ever hit the limitations of the current _universe_.
|
||||||
|
|
||||||
@ -44,8 +44,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
flip(4) :: beat(1) :: snd('kick').out()
|
flip(4) :: beat(1) :: snd('kick').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Clapping on the edge",
|
"Clapping on the edge",
|
||||||
@ -56,8 +56,8 @@ flip(2.5, 75) :: beat(.25) :: snd('click')
|
|||||||
flip(2.5) :: beat(.5) :: snd('bd').out()
|
flip(2.5) :: beat(.5) :: snd('bd').out()
|
||||||
beat(.25) :: sound('hat').end(0.1).cutoff(1200).pan(usine(1/4)).out()
|
beat(.25) :: sound('hat').end(0.1).cutoff(1200).pan(usine(1/4)).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Good old true and false",
|
"Good old true and false",
|
||||||
@ -68,8 +68,8 @@ if (flip(4, 75)) {
|
|||||||
beat(.5) :: snd('snare').out()
|
beat(.5) :: snd('snare').out()
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ic>flip</ic> is extremely powerful and is used internally for a lot of other Topos functions. You can also use it to think about **longer durations** spanning over multiple bars. Here is a silly composition that is using <ic>flip</ic> to generate a 4 bars long pattern.
|
<ic>flip</ic> is extremely powerful and is used internally for a lot of other Topos functions. You can also use it to think about **longer durations** spanning over multiple bars. Here is a silly composition that is using <ic>flip</ic> to generate a 4 bars long pattern.
|
||||||
|
|
||||||
@ -93,8 +93,8 @@ if (flip(8)) {
|
|||||||
beat(.5)::snd('diphone').end(0.5).n([1,2,3,4].pick()).out()
|
beat(.5)::snd('diphone').end(0.5).n([1,2,3,4].pick()).out()
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
You can use it everywhere to spice things up, including as a method parameter picker:
|
You can use it everywhere to spice things up, including as a method parameter picker:
|
||||||
|
|
||||||
@ -103,8 +103,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(.5)::snd(flip(2) ? 'kick' : 'hat').out()
|
beat(.5)::snd(flip(2) ? 'kick' : 'hat').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>flipbar(n: number = 1)</ic>: this method works just like <ic>flip</ic> but counts in bars instead of beats. It allows you to think about even larger time cycles. You can also pair it with regular <ic>flip</ic> for writing complex and long-spanning algorithmic beats.
|
- <ic>flipbar(n: number = 1)</ic>: this method works just like <ic>flip</ic> but counts in bars instead of beats. It allows you to think about even larger time cycles. You can also pair it with regular <ic>flip</ic> for writing complex and long-spanning algorithmic beats.
|
||||||
|
|
||||||
@ -122,8 +122,8 @@ function b() {
|
|||||||
flipbar(2) && a()
|
flipbar(2) && a()
|
||||||
flipbar(3) && b()
|
flipbar(3) && b()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Alternating over four bars",
|
"Alternating over four bars",
|
||||||
`
|
`
|
||||||
@ -131,8 +131,8 @@ flipbar(2)
|
|||||||
? beat(.5) && snd(['kick', 'hh'].beat(1)).out()
|
? beat(.5) && snd(['kick', 'hh'].beat(1)).out()
|
||||||
: beat(.5) && snd(['east', 'east:2'].beat(1)).out()
|
: beat(.5) && snd(['east', 'east:2'].beat(1)).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
|
|
||||||
- <ic>onbar(bars: number | number[], n: number)</ic>: The second argument, <ic>n</ic>, is used to divide the time in a period of <ic>n</ic> consecutive bars. The first argument should be a bar number or a list of bar numbers to play on. For example, <ic>onbar([1, 4], 5)</ic> will return <ic>true</ic> on bar <ic>1</ic> and <ic>4</ic> but return <ic>false</ic> the rest of the time. You can easily divide time that way.
|
- <ic>onbar(bars: number | number[], n: number)</ic>: The second argument, <ic>n</ic>, is used to divide the time in a period of <ic>n</ic> consecutive bars. The first argument should be a bar number or a list of bar numbers to play on. For example, <ic>onbar([1, 4], 5)</ic> will return <ic>true</ic> on bar <ic>1</ic> and <ic>4</ic> but return <ic>false</ic> the rest of the time. You can easily divide time that way.
|
||||||
@ -155,8 +155,8 @@ if (onbar([1,2], 4)) {
|
|||||||
rhythm(.5, 1, 7) :: snd('jvbass').n(2).out();
|
rhythm(.5, 1, 7) :: snd('jvbass').n(2).out();
|
||||||
rhythm(.5, 2, 7) :: snd('snare').n(3).out();
|
rhythm(.5, 2, 7) :: snd('snare').n(3).out();
|
||||||
}`,
|
}`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,9 +15,9 @@ You can use Topos to play MIDI thanks to the [WebMIDI API](https://developer.moz
|
|||||||
Your web browser is capable of sending and receiving MIDI information through the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API). The support for MIDI on browsers is a bit shaky. Please, take some time to configure and test. To our best knowledge, **Chrome** is currently leading on this feature, followed closely by **Firefox**. The other major web browsers are also starting to support this API. **There are two important functions for configuration:**
|
Your web browser is capable of sending and receiving MIDI information through the [Web MIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API). The support for MIDI on browsers is a bit shaky. Please, take some time to configure and test. To our best knowledge, **Chrome** is currently leading on this feature, followed closely by **Firefox**. The other major web browsers are also starting to support this API. **There are two important functions for configuration:**
|
||||||
|
|
||||||
- <ic>midi_outputs()</ic>: prints the list of available MIDI devices on the screen. You will have to open the web console using ${key_shortcut(
|
- <ic>midi_outputs()</ic>: prints the list of available MIDI devices on the screen. You will have to open the web console using ${key_shortcut(
|
||||||
"Ctrl+Shift+I"
|
"Ctrl+Shift+I",
|
||||||
)} or sometimes ${key_shortcut(
|
)} or sometimes ${key_shortcut(
|
||||||
"F12"
|
"F12",
|
||||||
)}. You can also open it from the menu of your web browser. **Note:** close the docs to see it printed.
|
)}. You can also open it from the menu of your web browser. **Note:** close the docs to see it printed.
|
||||||
|
|
||||||
|
|
||||||
@ -26,8 +26,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
midi_outputs()
|
midi_outputs()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>midi_output(output_name: string)</ic>: enter your desired output to connect to it.
|
- <ic>midi_output(output_name: string)</ic>: enter your desired output to connect to it.
|
||||||
|
|
||||||
@ -36,8 +36,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
midi_output("MIDI Rocket-Trumpet")
|
midi_output("MIDI Rocket-Trumpet")
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
That's it! You are now ready to play with MIDI.
|
That's it! You are now ready to play with MIDI.
|
||||||
|
|
||||||
@ -54,8 +54,8 @@ ${makeExample(
|
|||||||
// => midi_output("MIDI Bus 1")
|
// => midi_output("MIDI Bus 1")
|
||||||
rhythm(.5, 5, 8) :: midi(50).out()
|
rhythm(.5, 5, 8) :: midi(50).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"MIDI note using three parameters: note, velocity, channel",
|
"MIDI note using three parameters: note, velocity, channel",
|
||||||
@ -63,8 +63,8 @@ ${makeExample(
|
|||||||
// MIDI Note 50, Velocity 50 + LFO, Channel 0
|
// MIDI Note 50, Velocity 50 + LFO, Channel 0
|
||||||
rhythm(.5, 5, 8) :: midi(50, 50 + usine(.5) * 20, 0).out()
|
rhythm(.5, 5, 8) :: midi(50, 50 + usine(.5) * 20, 0).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"MIDI note by passing an object",
|
"MIDI note by passing an object",
|
||||||
@ -72,8 +72,8 @@ ${makeExample(
|
|||||||
// MIDI Note 50, Velocity 50 + LFO, Channel 0
|
// MIDI Note 50, Velocity 50 + LFO, Channel 0
|
||||||
rhythm(.5, 5, 8) :: midi({note: 50, velocity: 50 + usine(.5) * 20, channel: 0}).out()
|
rhythm(.5, 5, 8) :: midi({note: 50, velocity: 50 + usine(.5) * 20, channel: 0}).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
We can now have some fun and starting playing a small piano piece:
|
We can now have some fun and starting playing a small piano piece:
|
||||||
|
|
||||||
@ -86,8 +86,8 @@ beat(.25) && midi([64, 76].pick()).sustain(0.05).out()
|
|||||||
beat(.75) && midi([64, 67, 69].beat()).sustain(0.05).out()
|
beat(.75) && midi([64, 67, 69].beat()).sustain(0.05).out()
|
||||||
beat(.25) && midi([64, 67, 69].beat() + 24).sustain(0.05).out()
|
beat(.25) && midi([64, 67, 69].beat() + 24).sustain(0.05).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Control and Program Changes
|
## Control and Program Changes
|
||||||
|
|
||||||
@ -99,8 +99,8 @@ ${makeExample(
|
|||||||
control_change({control: [24,25].pick(), value: irand(1,120), channel: 1})
|
control_change({control: [24,25].pick(), value: irand(1,120), channel: 1})
|
||||||
control_change({control: [30,35].pick(), value: irand(1,120) / 2, channel: 1})
|
control_change({control: [30,35].pick(), value: irand(1,120) / 2, channel: 1})
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>program_change(program: number, channel: number)</ic>: send a MIDI Program Change. This function takes two arguments to specify the program and the channel (_e.g._ <ic>program_change(1, 1)</ic>).
|
- <ic>program_change(program: number, channel: number)</ic>: send a MIDI Program Change. This function takes two arguments to specify the program and the channel (_e.g._ <ic>program_change(1, 1)</ic>).
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
program_change([1,2,3,4,5,6,7,8].pick(), 1)
|
program_change([1,2,3,4,5,6,7,8].pick(), 1)
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
## System Exclusive Messages
|
## System Exclusive Messages
|
||||||
@ -123,8 +123,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
sysex(0x90, 0x40, 0x7f)
|
sysex(0x90, 0x40, 0x7f)
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Clock
|
## Clock
|
||||||
|
|
||||||
@ -135,8 +135,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(.25) && midi_clock() // Sending clock to MIDI device from the global buffer
|
beat(.25) && midi_clock() // Sending clock to MIDI device from the global buffer
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Using midi with ziffers
|
## Using midi with ziffers
|
||||||
|
|
||||||
@ -147,16 +147,16 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
z1('0 2 e 5 2 q 4 2').midi().port(2).channel(4).out()
|
z1('0 2 e 5 2 q 4 2').midi().port(2).channel(4).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Setting the channel within the pattern",
|
"Setting the channel within the pattern",
|
||||||
`
|
`
|
||||||
z1('(0 2 e 5 2):0 (4 2):1').midi().out()
|
z1('(0 2 e 5 2):0 (4 2):1').midi().out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -20,11 +20,11 @@ Some features are here "just for fun" or "just because I can". They are not very
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Hydra integration",
|
"Hydra integration",
|
||||||
`beat(4) :: app.hydra.osc(3, 0.5, 2).out()`,
|
`beat(4) :: app.hydra.osc(3, 0.5, 2).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
You may feel like it's doing nothing! Press ${key_shortcut(
|
You may feel like it's doing nothing! Press ${key_shortcut(
|
||||||
"Ctrl+D"
|
"Ctrl+D",
|
||||||
)} to close the documentation. **Boom, all shiny!**
|
)} to close the documentation. **Boom, all shiny!**
|
||||||
|
|
||||||
Be careful not to call <ic>app.hydra</ic> too often as it can impact performances. You can use any rhythmical function like <ic>mod()</ic> function to limit the number of function calls. You can write any Topos code like <ic>[1,2,3].beat()</ic> to bring some life and movement in your Hydra sketches.
|
Be careful not to call <ic>app.hydra</ic> too often as it can impact performances. You can use any rhythmical function like <ic>mod()</ic> function to limit the number of function calls. You can write any Topos code like <ic>[1,2,3].beat()</ic> to bring some life and movement in your Hydra sketches.
|
||||||
@ -37,8 +37,8 @@ ${makeExample(
|
|||||||
beat(4) :: stop_hydra() // this one
|
beat(4) :: stop_hydra() // this one
|
||||||
beat(4) :: app.hydra.hush() // or this one
|
beat(4) :: app.hydra.hush() // or this one
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
I won't teach you how to play with Hydra. You can find some great resources on the [Hydra website](https://hydra.ojack.xyz/):
|
I won't teach you how to play with Hydra. You can find some great resources on the [Hydra website](https://hydra.ojack.xyz/):
|
||||||
- [Hydra interactive documentation](https://hydra.ojack.xyz/docs/)
|
- [Hydra interactive documentation](https://hydra.ojack.xyz/docs/)
|
||||||
@ -62,8 +62,8 @@ beat(0.25)::gif({
|
|||||||
rotation: ir(1, 360), // Rotation (in degrees)
|
rotation: ir(1, 360), // Rotation (in degrees)
|
||||||
posX: ir(1,1200), // CSS Horizontal Position
|
posX: ir(1,1200), // CSS Horizontal Position
|
||||||
posY: ir(1, 800), // CSS Vertical Position
|
posY: ir(1, 800), // CSS Vertical Position
|
||||||
`, true
|
`,
|
||||||
)}
|
true,
|
||||||
|
)}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -23,8 +23,8 @@ scope({
|
|||||||
refresh: 1 // refresh rate (in pulses)
|
refresh: 1 // refresh rate (in pulses)
|
||||||
})
|
})
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Demo with multiple scope mode",
|
"Demo with multiple scope mode",
|
||||||
@ -44,8 +44,8 @@ scope({enabled: true, thickness: 8,
|
|||||||
color: ['purple', 'green', 'random'].beat(),
|
color: ['purple', 'green', 'random'].beat(),
|
||||||
size: 0.5, fftSize: 2048})
|
size: 0.5, fftSize: 2048})
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Note that these values can be patterned as well! You can transform the oscilloscope into its own light show if you want. The picture is not stable anyway so you won't have much use of it for precision work :)
|
Note that these values can be patterned as well! You can transform the oscilloscope into its own light show if you want. The picture is not stable anyway so you won't have much use of it for precision work :)
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(1)::sound('kick').out()
|
beat(1)::sound('kick').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
can be turned into something more interesting like this easily:
|
can be turned into something more interesting like this easily:
|
||||||
|
|
||||||
@ -26,8 +26,8 @@ let c = [1,2].dur(3, 1)
|
|||||||
beat([1, 0.5, 0.25].dur(0.75, 0.25, 1) / c)::sound(['kick', 'fsoftsnare'].beat(0.75))
|
beat([1, 0.5, 0.25].dur(0.75, 0.25, 1) / c)::sound(['kick', 'fsoftsnare'].beat(0.75))
|
||||||
.ad(0, .25).shape(usine(1/2)*0.5).speed([1, 2, 4].beat(0.5)).out()
|
.ad(0, .25).shape(usine(1/2)*0.5).speed([1, 2, 4].beat(0.5)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
**Topos** comes with a lot of array methods to deal with musical patterns of increasing complexity. Some knowledge of patterns and how to use them will help you to break out of simple loops and repeating structures. The most basic JavaScript data structure is the [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). Topos is extending it with custom methods to describe patterns that evolve over time. These methods can often be chained to compose more complex expressions: <ic>[1, 2, 3].repeatOdd(5).palindrome().beat()</ic>.
|
**Topos** comes with a lot of array methods to deal with musical patterns of increasing complexity. Some knowledge of patterns and how to use them will help you to break out of simple loops and repeating structures. The most basic JavaScript data structure is the [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array). Topos is extending it with custom methods to describe patterns that evolve over time. These methods can often be chained to compose more complex expressions: <ic>[1, 2, 3].repeatOdd(5).palindrome().beat()</ic>.
|
||||||
@ -44,8 +44,8 @@ beat([1, 0.75].beat(4)) :: sound('cp').out()
|
|||||||
beat([0.5, 1].beat(4)) :: sound('kick').out()
|
beat([0.5, 1].beat(4)) :: sound('kick').out()
|
||||||
beat(2)::snd('snare').shape(.5).out()
|
beat(2)::snd('snare').shape(.5).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Using beat to create arpeggios",
|
"Using beat to create arpeggios",
|
||||||
`
|
`
|
||||||
@ -62,8 +62,8 @@ beat([.5, .25].beat(0.5)) :: sound('sine')
|
|||||||
.delayfb(0.5)
|
.delayfb(0.5)
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Cool ambiance",
|
"Cool ambiance",
|
||||||
`
|
`
|
||||||
@ -73,8 +73,8 @@ flip(2)::beat(1)::snd('froomy').out()
|
|||||||
flip(4)::beat(2)::snd('pad').n(2).shape(.5)
|
flip(4)::beat(2)::snd('pad').n(2).shape(.5)
|
||||||
.orbit(2).room(0.9).size(0.9).release(0.5).out()
|
.orbit(2).room(0.9).size(0.9).release(0.5).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>bar(value: number = 1)</ic>: returns the next value every bar (if <ic>value = 1</ic>). Using a larger value will return the next value every <ic>n</ic> bars.
|
- <ic>bar(value: number = 1)</ic>: returns the next value every bar (if <ic>value = 1</ic>). Using a larger value will return the next value every <ic>n</ic> bars.
|
||||||
|
|
||||||
@ -88,8 +88,8 @@ beat([1/4, 1/2].dur(1.5, 0.5))::sound(['jvbass', 'fikea'].bar())
|
|||||||
* [1, 2].bar())
|
* [1, 2].bar())
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Using beat and bar in the same example",
|
"Using beat and bar in the same example",
|
||||||
@ -101,8 +101,8 @@ beat([1, 0.5].beat()) :: sound(['bass3'].bar())
|
|||||||
.pan(r(0, 1))
|
.pan(r(0, 1))
|
||||||
.speed([1,2,3].beat())
|
.speed([1,2,3].beat())
|
||||||
.out()
|
.out()
|
||||||
`
|
`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>dur(...list: numbers[])</ic> : keeps the same value for a duration of <ic>n</ic> beats corresponding to the <ic>nth</ic> number of the list you provide.
|
- <ic>dur(...list: numbers[])</ic> : keeps the same value for a duration of <ic>n</ic> beats corresponding to the <ic>nth</ic> number of the list you provide.
|
||||||
|
|
||||||
@ -117,8 +117,8 @@ beat(0.5)::sound('notes').n([1,2].dur(1, 2))
|
|||||||
beat(1)::sound(['kick', 'fsnare'].dur(3, 1))
|
beat(1)::sound(['kick', 'fsnare'].dur(3, 1))
|
||||||
.n([0,3].dur(3, 1)).out()
|
.n([0,3].dur(3, 1)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Manipulating notes and scales
|
## Manipulating notes and scales
|
||||||
|
|
||||||
@ -133,8 +133,8 @@ beat(0.25) :: snd('sine')
|
|||||||
.key(["F4","F3"].beat(2.0))
|
.key(["F4","F3"].beat(2.0))
|
||||||
.scale("minor").ad(0, .25).out()
|
.scale("minor").ad(0, .25).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>scale(scale: string, base note: number)</ic>: Map each element of the list to the closest note of the slected scale. [0, 2, 3, 5 ].scale("major", 50) returns [50, 52, <ic>54</ic>, 55]. You can use western scale names like (Major, Minor, Minor pentatonic ...) or [zeitler](https://ianring.com/musictheory/scales/traditions/zeitler) scale names. Alternatively you can also use the integers as used by Ian Ring in his [study of scales](https://ianring.com/musictheory/scales/).
|
- <ic>scale(scale: string, base note: number)</ic>: Map each element of the list to the closest note of the slected scale. [0, 2, 3, 5 ].scale("major", 50) returns [50, 52, <ic>54</ic>, 55]. You can use western scale names like (Major, Minor, Minor pentatonic ...) or [zeitler](https://ianring.com/musictheory/scales/traditions/zeitler) scale names. Alternatively you can also use the integers as used by Ian Ring in his [study of scales](https://ianring.com/musictheory/scales/).
|
||||||
|
|
||||||
@ -145,8 +145,8 @@ beat(1) :: snd('gtr')
|
|||||||
.note([0, 5, 2, 1, 7].scale("Major", 52).beat())
|
.note([0, 5, 2, 1, 7].scale("Major", 52).beat())
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>scaleArp(scale: string, mask: number)</ic>: extrapolate a custom-masked scale from each list elements. [0].scale("major", 3) returns [0,2,4]. <ic>scaleArp</ic> supports the same scales as <ic>scale</ic>.
|
- <ic>scaleArp(scale: string, mask: number)</ic>: extrapolate a custom-masked scale from each list elements. [0].scale("major", 3) returns [0,2,4]. <ic>scaleArp</ic> supports the same scales as <ic>scale</ic>.
|
||||||
|
|
||||||
@ -157,8 +157,8 @@ beat(1) :: snd('gtr')
|
|||||||
.note([0, 5].scaleArp("mixolydian", 3).beat() + 50)
|
.note([0, 5].scaleArp("mixolydian", 3).beat() + 50)
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Iteration using the mouse
|
## Iteration using the mouse
|
||||||
|
|
||||||
@ -173,8 +173,8 @@ beat(0.25)::sound('wt_piano')
|
|||||||
.room(0.5).size(4).lpad(-2, .2).lpf(500, 0.3)
|
.room(0.5).size(4).lpad(-2, .2).lpf(500, 0.3)
|
||||||
.ad(0, .2).out()
|
.ad(0, .2).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Simple data operations
|
## Simple data operations
|
||||||
|
|
||||||
@ -191,8 +191,8 @@ beat([1,.5,.25].beat()) :: snd('wt_stereo')
|
|||||||
.lpf([500,1000,2000,4000].palindrome().beat())
|
.lpf([500,1000,2000,4000].palindrome().beat())
|
||||||
.lpad(4, 0, .25).sustain(0.125).out()
|
.lpad(4, 0, .25).sustain(0.125).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>random(index: number)</ic>: pick a random element in the given list.
|
- <ic>random(index: number)</ic>: pick a random element in the given list.
|
||||||
- <ic>rand(index: number)</ic>: shorter alias for the same method.
|
- <ic>rand(index: number)</ic>: shorter alias for the same method.
|
||||||
@ -210,8 +210,8 @@ beat([.5, 1].rand() / 2) :: snd(
|
|||||||
.lpf([5000,3000,2000].pick())
|
.lpf([5000,3000,2000].pick())
|
||||||
.end(0.5).out()
|
.end(0.5).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>pick()</ic>: pick a random element in the list.
|
- <ic>pick()</ic>: pick a random element in the list.
|
||||||
|
|
||||||
@ -222,8 +222,8 @@ beat(0.25)::sound(['ftabla', 'fwood'].pick())
|
|||||||
.speed([1,2,3,4].pick()).ad(0, .125).n(ir(1,10))
|
.speed([1,2,3,4].pick()).ad(0, .125).n(ir(1,10))
|
||||||
.room(0.5).size(1).out()
|
.room(0.5).size(1).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>degrade(amount: number)</ic>: removes _n_% of the list elements. Lists can be degraded as long as one element remains. The amount of degradation is given as a percentage.
|
- <ic>degrade(amount: number)</ic>: removes _n_% of the list elements. Lists can be degraded as long as one element remains. The amount of degradation is given as a percentage.
|
||||||
|
|
||||||
@ -233,8 +233,8 @@ ${makeExample(
|
|||||||
// Tweak the value to degrade this amen break even more!
|
// Tweak the value to degrade this amen break even more!
|
||||||
beat(.25)::snd('amencutup').n([1,2,3,4,5,6,7,8,9].degrade(20).loop($(1))).out()
|
beat(.25)::snd('amencutup').n([1,2,3,4,5,6,7,8,9].degrade(20).loop($(1))).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>repeat(amount: number)</ic>: repeat every list elements _n_ times.
|
- <ic>repeat(amount: number)</ic>: repeat every list elements _n_ times.
|
||||||
- <ic>repeatEven(amount: number)</ic>: repeat every pair element of the list _n_ times.
|
- <ic>repeatEven(amount: number)</ic>: repeat every pair element of the list _n_ times.
|
||||||
@ -245,8 +245,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(.25)::sound('amencutup').n([1,2,3,4,5,6,7,8].repeat(4).beat(.25)).out()
|
beat(.25)::sound('amencutup').n([1,2,3,4,5,6,7,8].repeat(4).beat(.25)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>loop(index: number)</ic>: loop takes one argument, the _index_. It allows you to iterate over a list using an iterator such as a counter. This is super useful to control how you are accessing values in a list without relying on a temporal method such as <ic>.beat()</ic> or </ic>.bar()</ic>.
|
- <ic>loop(index: number)</ic>: loop takes one argument, the _index_. It allows you to iterate over a list using an iterator such as a counter. This is super useful to control how you are accessing values in a list without relying on a temporal method such as <ic>.beat()</ic> or </ic>.bar()</ic>.
|
||||||
|
|
||||||
@ -255,8 +255,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(1) :: sound('numbers').n([1,2,3,4,5].loop($(3, 10, 2))).out()
|
beat(1) :: sound('numbers').n([1,2,3,4,5].loop($(3, 10, 2))).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>shuffle(): this</ic>: shuffles a list! Simple enough!
|
- <ic>shuffle(): this</ic>: shuffles a list! Simple enough!
|
||||||
|
|
||||||
@ -265,8 +265,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(1) :: sound('numbers').n([1,2,3,4,5].shuffle().loop($(1)).out()
|
beat(1) :: sound('numbers').n([1,2,3,4,5].shuffle().loop($(1)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>rotate(steps: number)</ic>: rotate a list to the right _n_ times. The last value become the first, rinse and repeat.
|
- <ic>rotate(steps: number)</ic>: rotate a list to the right _n_ times. The last value become the first, rinse and repeat.
|
||||||
|
|
||||||
@ -283,8 +283,8 @@ beat(.25) :: snd('sine').fmi([1.99, 2])
|
|||||||
.beat(.25)) // while the index changes
|
.beat(.25)) // while the index changes
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Filtering
|
## Filtering
|
||||||
|
|
||||||
@ -296,8 +296,8 @@ ${makeExample(
|
|||||||
// Remove unique and 100 will repeat four times!
|
// Remove unique and 100 will repeat four times!
|
||||||
beat(1)::snd('sine').sustain(0.1).freq([100,100,100,100,200].unique().beat()).out()
|
beat(1)::snd('sine').sustain(0.1).freq([100,100,100,100,200].unique().beat()).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Simple math operations
|
## Simple math operations
|
||||||
|
|
||||||
|
|||||||
@ -17,8 +17,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
rhythm(0.125, 10, 16) :: sound('sid').n(4).note(50 + irand(50, 62) % 8).out()
|
rhythm(0.125, 10, 16) :: sound('sid').n(4).note(50 + irand(50, 62) % 8).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
- <ic>prob(p: number)</ic>: return <ic>true</ic> _p_% of time, <ic>false</ic> in other cases.
|
- <ic>prob(p: number)</ic>: return <ic>true</ic> _p_% of time, <ic>false</ic> in other cases.
|
||||||
@ -32,8 +32,8 @@ prob(50) :: script(1);
|
|||||||
prob(60) :: script(2);
|
prob(60) :: script(2);
|
||||||
prob(80) :: script(toss() ? script(3) : script(4))
|
prob(80) :: script(toss() ? script(3) : script(4))
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>seed(val: number|string)</ic>: sets the seed of the random number generator. You can use a number or a string. The same seed will always return the same sequence of random numbers.
|
- <ic>seed(val: number|string)</ic>: sets the seed of the random number generator. You can use a number or a string. The same seed will always return the same sequence of random numbers.
|
||||||
|
|
||||||
@ -64,8 +64,8 @@ ${makeExample(
|
|||||||
rarely(4) :: sound('bd').out(); // Rarely in 4 beats is bit less
|
rarely(4) :: sound('bd').out(); // Rarely in 4 beats is bit less
|
||||||
rarely(8) :: sound('east').out(); // Rarely in 8 beats is even less
|
rarely(8) :: sound('east').out(); // Rarely in 8 beats is even less
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Using chance with other operators",
|
"Using chance with other operators",
|
||||||
@ -74,8 +74,8 @@ ${makeExample(
|
|||||||
often() :: beat(0.5) :: sound('hh').out();
|
often() :: beat(0.5) :: sound('hh').out();
|
||||||
sometimes() :: onbeat(1,3) :: sound('snare').out();
|
sometimes() :: onbeat(1,3) :: sound('snare').out();
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Using chance with chaining",
|
"Using chance with chaining",
|
||||||
@ -91,8 +91,7 @@ ${makeExample(
|
|||||||
.almostNever(n=>n.freq(400))
|
.almostNever(n=>n.freq(400))
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
`
|
`;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|||||||
@ -18,8 +18,8 @@ ${makeExample(
|
|||||||
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
|
||||||
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');`,
|
}, 'github:tidalcycles/Dirt-Samples/master/');`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
This example is loading two samples from each folder declared in the original repository (in the <ic>strudel.json</ic> file). You can then play with them using the syntax you are already used to:
|
This example is loading two samples from each folder declared in the original repository (in the <ic>strudel.json</ic> file). You can then play with them using the syntax you are already used to:
|
||||||
|
|
||||||
@ -27,8 +27,8 @@ ${makeExample(
|
|||||||
"Playing with the loaded samples",
|
"Playing with the loaded samples",
|
||||||
`rhythm(.5, 5, 8)::sound('bd').n(ir(1,2)).end(1).out()
|
`rhythm(.5, 5, 8)::sound('bd').n(ir(1,2)).end(1).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Internally, Topos is loading samples using a different technique where sample maps are directly taken from the previously mentioned <ic>strudel.json</ic> file that lives in each repository:
|
Internally, Topos is loading samples using a different technique where sample maps are directly taken from the previously mentioned <ic>strudel.json</ic> file that lives in each repository:
|
||||||
|
|
||||||
@ -40,8 +40,8 @@ samples("github:tidalcycles/Dirt-Samples/master");
|
|||||||
samples("github:Bubobubobubobubo/Dough-Samples/main");
|
samples("github:Bubobubobubobubo/Dough-Samples/main");
|
||||||
samples("github:Bubobubobubobubo/Dough-Amiga/main");
|
samples("github:Bubobubobubobubo/Dough-Amiga/main");
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
To learn more about the audio sample loading mechanism, please refer to [this page](https://strudel.tidalcycles.org/learn/samples) written by Felix Roos who has implemented the sample loading mechanism. The API is absolutely identic in Topos!
|
To learn more about the audio sample loading mechanism, please refer to [this page](https://strudel.tidalcycles.org/learn/samples) written by Felix Roos who has implemented the sample loading mechanism. The API is absolutely identic in Topos!
|
||||||
|
|
||||||
@ -57,9 +57,10 @@ samples("shabda:ocean")
|
|||||||
|
|
||||||
// Use the sound without 'shabda:'
|
// Use the sound without 'shabda:'
|
||||||
beat(1)::sound('ocean').clip(1).out()
|
beat(1)::sound('ocean').clip(1).out()
|
||||||
`, true
|
`,
|
||||||
)}
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
You can also use the <ic>.n</ic> attribute like usual to load a different sample.
|
You can also use the <ic>.n</ic> attribute like usual to load a different sample.
|
||||||
`
|
`;
|
||||||
}
|
};
|
||||||
|
|||||||
@ -9,5 +9,5 @@ export const sample_banks = (application: Editor): string => {
|
|||||||
There is a <ic>bank</ic> attribute that can help you to sort audio samples from large collections.
|
There is a <ic>bank</ic> attribute that can help you to sort audio samples from large collections.
|
||||||
|
|
||||||
**AJKPercusyn**, **AkaiLinn**, **AkaiMPC60**, **AkaiXR10**, **AlesisHR16**, **AlesisSR16**, **BossDR110**, **BossDR220**, **BossDR55**, **BossDR550**, **BossDR660**, **CasioRZ1**, **CasioSK1**, **CasioVL1**, **DoepferMS404**, **EmuDrumulator**, **EmuModular**, **EmuSP12**, **KorgDDM110**, **KorgKPR77**, **KorgKR55**, **KorgKRZ**, **KorgM1**, **KorgMinipops**, **KorgPoly800**, **KorgT3**, **Linn9000**, **LinnDrum**, **LinnLM1**, **LinnLM2**, **MFB512**, **MPC1000**, **MoogConcertMateMG1**, **OberheimDMX**, **RhodesPolaris**, **RhythmAce**, **RolandCompurhythm1000**, **RolandCompurhythm78**, **RolandCompurhythm8000**, **RolandD110**, **RolandD70**, **RolandDDR30**, **RolandJD990**, **RolandMC202**, **RolandMC303**, **RolandMT32**, **RolandR8**, **RolandS50**, **RolandSH09**, **RolandSystem100**, **RolandTR505**, **RolandTR606**, **RolandTR626**, **RolandTR707**, **RolandTR727**, **RolandTR808**, **RolandTR909**, **SakataDPM48**, **SequentialCircuitsDrumtracks**, **SequentialCircuitsTom**, **SergeModular**, **SimmonsSDS400**, **SimmonsSDS5**, **SoundmastersR88**, **UnivoxMicroRhythmer12**, **ViscoSpaceDrum**, **XdrumLM8953**, **YamahaRM50**, **YamahaRX21**, **YamahaRX5**, **YamahaRY30**, **YamahaTG33**.
|
**AJKPercusyn**, **AkaiLinn**, **AkaiMPC60**, **AkaiXR10**, **AlesisHR16**, **AlesisSR16**, **BossDR110**, **BossDR220**, **BossDR55**, **BossDR550**, **BossDR660**, **CasioRZ1**, **CasioSK1**, **CasioVL1**, **DoepferMS404**, **EmuDrumulator**, **EmuModular**, **EmuSP12**, **KorgDDM110**, **KorgKPR77**, **KorgKR55**, **KorgKRZ**, **KorgM1**, **KorgMinipops**, **KorgPoly800**, **KorgT3**, **Linn9000**, **LinnDrum**, **LinnLM1**, **LinnLM2**, **MFB512**, **MPC1000**, **MoogConcertMateMG1**, **OberheimDMX**, **RhodesPolaris**, **RhythmAce**, **RolandCompurhythm1000**, **RolandCompurhythm78**, **RolandCompurhythm8000**, **RolandD110**, **RolandD70**, **RolandDDR30**, **RolandJD990**, **RolandMC202**, **RolandMC303**, **RolandMT32**, **RolandR8**, **RolandS50**, **RolandSH09**, **RolandSystem100**, **RolandTR505**, **RolandTR606**, **RolandTR626**, **RolandTR707**, **RolandTR727**, **RolandTR808**, **RolandTR909**, **SakataDPM48**, **SequentialCircuitsDrumtracks**, **SequentialCircuitsTom**, **SergeModular**, **SimmonsSDS400**, **SimmonsSDS5**, **SoundmastersR88**, **UnivoxMicroRhythmer12**, **ViscoSpaceDrum**, **XdrumLM8953**, **YamahaRM50**, **YamahaRX21**, **YamahaRX5**, **YamahaRY30**, **YamahaTG33**.
|
||||||
`
|
`;
|
||||||
}
|
};
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import { type Editor } from "../../main";
|
import { type Editor } from "../../main";
|
||||||
import { makeExampleFactory } from "../../Documentation";
|
import { makeExampleFactory } from "../../Documentation";
|
||||||
|
|
||||||
export const samples_to_markdown = (application: Editor, tag_filter?: string) => {
|
export const samples_to_markdown = (
|
||||||
|
application: Editor,
|
||||||
|
tag_filter?: string,
|
||||||
|
) => {
|
||||||
let samples = application.api._all_samples();
|
let samples = application.api._all_samples();
|
||||||
let markdownList = "";
|
let markdownList = "";
|
||||||
let keys = Object.keys(samples);
|
let keys = Object.keys(samples);
|
||||||
@ -44,13 +47,11 @@ export const injectAllSamples = (application: Editor): string => {
|
|||||||
return generatedPage;
|
return generatedPage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const injectDrumMachineSamples = (application: Editor): string => {
|
export const injectDrumMachineSamples = (application: Editor): string => {
|
||||||
let generatedPage = samples_to_markdown(application, "Machines");
|
let generatedPage = samples_to_markdown(application, "Machines");
|
||||||
return generatedPage;
|
return generatedPage;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const sample_list = (application: Editor): string => {
|
export const sample_list = (application: Editor): string => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const makeExample = makeExampleFactory(application);
|
const makeExample = makeExampleFactory(application);
|
||||||
@ -63,9 +64,13 @@ On this page, you will find an exhaustive list of all the samples currently load
|
|||||||
|
|
||||||
A very large collection of wavetables for wavetable synthesis. This collection has been released by Kristoffer Ekstrand: [AKWF Waveforms](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/). Every sound sample that starts with <ic>wt_</ic> will be looped. Look at this demo:
|
A very large collection of wavetables for wavetable synthesis. This collection has been released by Kristoffer Ekstrand: [AKWF Waveforms](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/). Every sound sample that starts with <ic>wt_</ic> will be looped. Look at this demo:
|
||||||
|
|
||||||
${makeExample("Wavetable synthesis made easy :)", `
|
${makeExample(
|
||||||
|
"Wavetable synthesis made easy :)",
|
||||||
|
`
|
||||||
beat(0.5)::sound('wt_stereo').n([0, 1].pick()).ad(0, .25).out()
|
beat(0.5)::sound('wt_stereo').n([0, 1].pick()).ad(0, .25).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
Pick one folder and spend some time exploring it. There is a lot of different waveforms.
|
Pick one folder and spend some time exploring it. There is a lot of different waveforms.
|
||||||
@ -79,9 +84,12 @@ ${samples_to_markdown(application, "Waveforms")}
|
|||||||
A set of 72 classic drum machines created by **Geikha**: [Geikha Drum Machines](https://github.com/geikha/tidal-drum-machines). To use them efficiently, it is best to use the <ic>.bank()</ic> parameter like so:
|
A set of 72 classic drum machines created by **Geikha**: [Geikha Drum Machines](https://github.com/geikha/tidal-drum-machines). To use them efficiently, it is best to use the <ic>.bank()</ic> parameter like so:
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Using a classic drum machine", `
|
"Using a classic drum machine",
|
||||||
|
`
|
||||||
beat(0.5)::sound(['bd', 'cp'].pick()).bank("AkaiLinn").out()
|
beat(0.5)::sound(['bd', 'cp'].pick()).bank("AkaiLinn").out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
Here is the complete list of available machines:
|
Here is the complete list of available machines:
|
||||||
|
|
||||||
@ -111,10 +119,12 @@ ${samples_to_markdown(application, "Amiga")}
|
|||||||
A collection of many different amen breaks. Use <ic>.stretch()</ic> to play with these:
|
A collection of many different amen breaks. Use <ic>.stretch()</ic> to play with these:
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Stretching an amen break", `
|
"Stretching an amen break",
|
||||||
|
`
|
||||||
beat(4)::sound('amen1').stretch(4).out()
|
beat(4)::sound('amen1').stretch(4).out()
|
||||||
`, true,
|
`,
|
||||||
)}
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
The stretch should be adapted based on the length of each amen break.
|
The stretch should be adapted based on the length of each amen break.
|
||||||
|
|
||||||
@ -130,5 +140,5 @@ Many live coders are expecting to find the Tidal sample library wherever they go
|
|||||||
<div class="lg:pl-6 lg:pr-6 w-fit rounded-lg bg-neutral-600 mx-6 mt-2 my-6 px-2 py-2 max-h-96 flex flex-row flex-wrap gap-x-2 gap-y-2 overflow-y-scroll">
|
<div class="lg:pl-6 lg:pr-6 w-fit rounded-lg bg-neutral-600 mx-6 mt-2 my-6 px-2 py-2 max-h-96 flex flex-row flex-wrap gap-x-2 gap-y-2 overflow-y-scroll">
|
||||||
${samples_to_markdown(application, "Tidal")}
|
${samples_to_markdown(application, "Tidal")}
|
||||||
</div>
|
</div>
|
||||||
`
|
`;
|
||||||
}
|
};
|
||||||
|
|||||||
@ -17,8 +17,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(.5) && snd(['sine', 'triangle', 'sawtooth', 'square'].beat()).freq(100).out()
|
beat(.5) && snd(['sine', 'triangle', 'sawtooth', 'square'].beat()).freq(100).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Note that you can also use noise if you do not want to use a periodic oscillator:
|
Note that you can also use noise if you do not want to use a periodic oscillator:
|
||||||
|
|
||||||
@ -28,8 +28,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(.5) && snd(['brown', 'pink', 'white'].beat()).adsr(0,.1,0,0).out()
|
beat(.5) && snd(['brown', 'pink', 'white'].beat()).adsr(0,.1,0,0).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Two functions are primarily used to control the frequency of the synthesizer:
|
Two functions are primarily used to control the frequency of the synthesizer:
|
||||||
- <ic>freq(hz: number)</ic>: sets the frequency of the oscillator.
|
- <ic>freq(hz: number)</ic>: sets the frequency of the oscillator.
|
||||||
@ -40,15 +40,15 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(.5) && snd('triangle').freq([100,200,400].beat(2)).out()
|
beat(.5) && snd('triangle').freq([100,200,400].beat(2)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Selecting a note",
|
"Selecting a note",
|
||||||
`
|
`
|
||||||
beat(.5) && snd('triangle').note([60,"F4"].pick()).out()
|
beat(.5) && snd('triangle').note([60,"F4"].pick()).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Chords can also played using different parameters:
|
Chords can also played using different parameters:
|
||||||
@ -60,16 +60,16 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(1) && snd('triangle').chord(["C","Em7","Fmaj7","Emin"].beat(2)).adsr(0,.2).out()
|
beat(1) && snd('triangle').chord(["C","Em7","Fmaj7","Emin"].beat(2)).adsr(0,.2).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Playing a chord from a list of notes and doing inversions",
|
"Playing a chord from a list of notes and doing inversions",
|
||||||
`
|
`
|
||||||
beat(.5) && snd('triangle').chord(60,64,67,72).invert([1,-3,4,-5].pick()).adsr(0,.2).out()
|
beat(.5) && snd('triangle').chord(60,64,67,72).invert([1,-3,4,-5].pick()).adsr(0,.2).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Vibrato
|
## Vibrato
|
||||||
|
|
||||||
@ -84,8 +84,8 @@ beat(1) :: sound('triangle')
|
|||||||
.vib([1/2, 1, 2, 4].beat())
|
.vib([1/2, 1, 2, 4].beat())
|
||||||
.vibmod([1,2,4,8].beat(2))
|
.vibmod([1,2,4,8].beat(2))
|
||||||
.out()`,
|
.out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Noise
|
## Noise
|
||||||
|
|
||||||
@ -101,8 +101,8 @@ beat(1) :: sound('triangle')
|
|||||||
.vib([1/2, 1, 2, 4].beat())
|
.vib([1/2, 1, 2, 4].beat())
|
||||||
.vibmod([1,2,4,8].beat(2))
|
.vibmod([1,2,4,8].beat(2))
|
||||||
.out()`,
|
.out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
## Controlling the amplitude
|
## Controlling the amplitude
|
||||||
@ -114,14 +114,14 @@ Controlling the amplitude and duration of the sound can be done using various te
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Setting the gain",
|
"Setting the gain",
|
||||||
`beat(0.25) :: sound('sawtooth').gain([0.0, 1/8, 1/4, 1/2, 1].beat(0.5)).out()`,
|
`beat(0.25) :: sound('sawtooth').gain([0.0, 1/8, 1/4, 1/2, 1].beat(0.5)).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Setting the velocity",
|
"Setting the velocity",
|
||||||
`beat(0.25) :: sound('sawtooth').velocity([0.0, 1/8, 1/4, 1/2, 1].beat(0.5)).out()`,
|
`beat(0.25) :: sound('sawtooth').velocity([0.0, 1/8, 1/4, 1/2, 1].beat(0.5)).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div class="mt-4 mb-4 lg:grid lg:grid-cols-4 lg:gap-4">
|
<div class="mt-4 mb-4 lg:grid lg:grid-cols-4 lg:gap-4">
|
||||||
<img class="col-span-1 lg:ml-12 bg-gray-100 rounded-lg px-2 py-2", src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/ADSR_Envelope_Graph.svg/1280px-ADSR_Envelope_Graph.svg.png" width="400" />
|
<img class="col-span-1 lg:ml-12 bg-gray-100 rounded-lg px-2 py-2", src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/ADSR_Envelope_Graph.svg/1280px-ADSR_Envelope_Graph.svg.png" width="400" />
|
||||||
@ -141,8 +141,8 @@ beat(0.5) :: sound('wt_piano')
|
|||||||
.freq(100).decay(.2)
|
.freq(100).decay(.2)
|
||||||
.sustain([0.1,0.5].beat(4))
|
.sustain([0.1,0.5].beat(4))
|
||||||
.out()`,
|
.out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
This ADSR envelope design is important to know because it is used for other aspects of the synthesis engine such as the filters that we are now going to talk about. But wait, I've kept the best for the end. The <ic>adsr()</ic> combines all the parameters together. It is a shortcut for setting the ADSR envelope:
|
This ADSR envelope design is important to know because it is used for other aspects of the synthesis engine such as the filters that we are now going to talk about. But wait, I've kept the best for the end. The <ic>adsr()</ic> combines all the parameters together. It is a shortcut for setting the ADSR envelope:
|
||||||
|
|
||||||
@ -157,8 +157,8 @@ beat(0.5) :: sound('wt_piano')
|
|||||||
.adsr(0, .2, [0.1,0.5].beat(4), 0)
|
.adsr(0, .2, [0.1,0.5].beat(4), 0)
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>ad(attack: number, decay: number)</ic>: sets the attack and decay phases, setting sustain and release to <ic>0</ic>.
|
- <ic>ad(attack: number, decay: number)</ic>: sets the attack and decay phases, setting sustain and release to <ic>0</ic>.
|
||||||
|
|
||||||
@ -171,8 +171,8 @@ beat(0.5) :: sound('wt_piano')
|
|||||||
.ad(0, .2)
|
.ad(0, .2)
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Substractive synthesis using filters
|
## Substractive synthesis using filters
|
||||||
|
|
||||||
@ -185,8 +185,8 @@ The most basic synthesis technique used since the 1970s is called substractive s
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Filtering the high frequencies of an oscillator",
|
"Filtering the high frequencies of an oscillator",
|
||||||
`beat(.5) :: sound('sawtooth').cutoff(50 + usine(1/8) * 2000).out()`,
|
`beat(.5) :: sound('sawtooth').cutoff(50 + usine(1/8) * 2000).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
These filters all come with their own set of parameters. Note that we are describing the parameters of the three different filter types here. Choose the right parameters depending on the filter type you are using:
|
These filters all come with their own set of parameters. Note that we are describing the parameters of the three different filter types here. Choose the right parameters depending on the filter type you are using:
|
||||||
|
|
||||||
@ -201,8 +201,8 @@ These filters all come with their own set of parameters. Note that we are descri
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Filtering a bass",
|
"Filtering a bass",
|
||||||
`beat(.5) :: sound('jvbass').lpf([250,1000,8000].beat()).out()`,
|
`beat(.5) :: sound('jvbass').lpf([250,1000,8000].beat()).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
### Highpass filter
|
### Highpass filter
|
||||||
|
|
||||||
@ -214,8 +214,8 @@ ${makeExample(
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Filtering a noise source",
|
"Filtering a noise source",
|
||||||
`beat(.5) :: sound('gtr').hpf([250,1000, 2000, 3000, 4000].beat()).end(0.5).out()`,
|
`beat(.5) :: sound('gtr').hpf([250,1000, 2000, 3000, 4000].beat()).end(0.5).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
### Bandpass filter
|
### Bandpass filter
|
||||||
|
|
||||||
@ -227,8 +227,8 @@ ${makeExample(
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Sweeping the filter on the same guitar sample",
|
"Sweeping the filter on the same guitar sample",
|
||||||
`beat(.5) :: sound('gtr').bandf(100 + usine(1/8) * 4000).end(0.5).out()`,
|
`beat(.5) :: sound('gtr').bandf(100 + usine(1/8) * 4000).end(0.5).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Alternatively, <ic>lpf</ic>, <ic>hpf</ic> and <ic>bpf</ic> can take a second argument, the **resonance**.
|
Alternatively, <ic>lpf</ic>, <ic>hpf</ic> and <ic>bpf</ic> can take a second argument, the **resonance**.
|
||||||
|
|
||||||
@ -241,8 +241,8 @@ You can also use the <ic>ftype</ic> method to change the filter type (order). Th
|
|||||||
${makeExample(
|
${makeExample(
|
||||||
"Filtering a bass",
|
"Filtering a bass",
|
||||||
`beat(.5) :: sound('jvbass').ftype(['12db', '24db'].beat(4)).lpf([250,1000,8000].beat()).out()`,
|
`beat(.5) :: sound('jvbass').ftype(['12db', '24db'].beat(4)).lpf([250,1000,8000].beat()).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
I also encourage you to study these simple examples to get more familiar with the construction of basic substractive synthesizers:
|
I also encourage you to study these simple examples to get more familiar with the construction of basic substractive synthesizers:
|
||||||
|
|
||||||
@ -254,8 +254,8 @@ beat(.5) && snd('sawtooth')
|
|||||||
.resonance(0.2).freq([100,150].pick())
|
.resonance(0.2).freq([100,150].pick())
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Blessed by the square wave",
|
"Blessed by the square wave",
|
||||||
@ -265,8 +265,8 @@ beat(.5) :: [100,101].forEach((freq) => sound('square').freq(freq*2).sustain(0.0
|
|||||||
beat([.5, .75, 2].beat()) :: [100,101].forEach((freq) => sound('square')
|
beat([.5, .75, 2].beat()) :: [100,101].forEach((freq) => sound('square')
|
||||||
.freq(freq*4 + usquare(2) * 200).sustain(0.125).out())
|
.freq(freq*4 + usquare(2) * 200).sustain(0.125).out())
|
||||||
beat(.25) :: sound('square').freq(100*[1,2,4,8].beat()).sustain(0.1).out()`,
|
beat(.25) :: sound('square').freq(100*[1,2,4,8].beat()).sustain(0.1).out()`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Ghost carillon (move your mouse!)",
|
"Ghost carillon (move your mouse!)",
|
||||||
@ -279,8 +279,8 @@ beat(1/8)::sound('sine')
|
|||||||
.freq(mouseX())
|
.freq(mouseX())
|
||||||
.gain(0.25)
|
.gain(0.25)
|
||||||
.out()`,
|
.out()`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Filter envelopes
|
## Filter envelopes
|
||||||
|
|
||||||
@ -303,8 +303,8 @@ ${makeExample(
|
|||||||
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
||||||
.cutoff(5000).lpa([0.05, 0.25, 0.5].beat(2))
|
.cutoff(5000).lpa([0.05, 0.25, 0.5].beat(2))
|
||||||
.lpenv(-8).lpq(10).out()`,
|
.lpenv(-8).lpq(10).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
### Highpass envelope
|
### Highpass envelope
|
||||||
|
|
||||||
@ -323,8 +323,8 @@ ${makeExample(
|
|||||||
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
`beat(.5) :: sound('sawtooth').note([48,60].beat())
|
||||||
.hcutoff(1000).hpa([0.05, 0.25, 0.5].beat(2))
|
.hcutoff(1000).hpa([0.05, 0.25, 0.5].beat(2))
|
||||||
.hpenv(8).hpq(10).out()`,
|
.hpenv(8).hpq(10).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
### Bandpass envelope
|
### Bandpass envelope
|
||||||
|
|
||||||
@ -345,8 +345,8 @@ ${makeExample(
|
|||||||
.bpa([0.25, 0.125, 0.5].beat(2) * 4)
|
.bpa([0.25, 0.125, 0.5].beat(2) * 4)
|
||||||
.bpenv(-4).release(2).out()
|
.bpenv(-4).release(2).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
## Wavetable synthesis
|
## Wavetable synthesis
|
||||||
@ -363,8 +363,8 @@ beat(.25) :: sound('wt_symetric:8').note([50,55,57,60].beat(.25) - [12,0]
|
|||||||
beat(1) :: sound('kick').n(4).out()
|
beat(1) :: sound('kick').n(4).out()
|
||||||
beat(2) :: sound('snare').out()
|
beat(2) :: sound('snare').out()
|
||||||
beat(.5) :: sound('hh').out()`,
|
beat(.5) :: sound('hh').out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
Let's explore the galaxy of possible waveforms. It can be hard to explore them all, there is a **lot** of them:
|
Let's explore the galaxy of possible waveforms. It can be hard to explore them all, there is a **lot** of them:
|
||||||
@ -381,8 +381,8 @@ beat(2) :: v('selec', irand(1, 100))
|
|||||||
beat(2) :: v('swave', collection.pick())
|
beat(2) :: v('swave', collection.pick())
|
||||||
beat(0.5) :: sound(v('swave')).n(v('selec')).out()
|
beat(0.5) :: sound(v('swave')).n(v('selec')).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
You can work with them just like with any other waveform. Having so many of them makes them also very useful for generating sound effects, percussive, sounds, etc...
|
You can work with them just like with any other waveform. Having so many of them makes them also very useful for generating sound effects, percussive, sounds, etc...
|
||||||
|
|
||||||
@ -407,8 +407,8 @@ beat(.25) && snd('triangle').adsr(0.02, 0.1, 0.1, 0.1)
|
|||||||
.pan(noise()).note([60,55, 60, 63].beat() + [0, 7].pick()).out()
|
.pan(noise()).note([60,55, 60, 63].beat() + [0, 7].pick()).out()
|
||||||
beat(2) :: sound('cp').room(1).sz(1).out()
|
beat(2) :: sound('cp').room(1).sz(1).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Giving some love to ugly inharmonic sounds",
|
"Giving some love to ugly inharmonic sounds",
|
||||||
@ -418,8 +418,8 @@ beat([4].bar()) :: sound('sine').fm('5.2183:4.5').sustain(0.05).out()
|
|||||||
beat(.5) :: sound('sine')
|
beat(.5) :: sound('sine')
|
||||||
.fmh([1, 1.75].beat())
|
.fmh([1, 1.75].beat())
|
||||||
.fmi($(1) % 30).orbit(2).room(0.5).out()`,
|
.fmi($(1) % 30).orbit(2).room(0.5).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Peace and serenity through FM synthesis",
|
"Peace and serenity through FM synthesis",
|
||||||
@ -432,8 +432,8 @@ beat(0.25) :: sound('sine')
|
|||||||
.cutoff(1500).delay(0.5).delayt(0.125)
|
.cutoff(1500).delay(0.5).delayt(0.125)
|
||||||
.delayfb(0.8).fmh(Math.floor(usine(.5) * 4))
|
.delayfb(0.8).fmh(Math.floor(usine(.5) * 4))
|
||||||
.out()`,
|
.out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
**Note:** you can also set the _modulation index_ and the _harmonic ratio_ with the <ic>fm</ic> argument. You will have to feed both as a string: <ic>fm('2:4')</ic>. If you only feed one number, only the _modulation index_ will be updated.
|
**Note:** you can also set the _modulation index_ and the _harmonic ratio_ with the <ic>fm</ic> argument. You will have to feed both as a string: <ic>fm('2:4')</ic>. If you only feed one number, only the _modulation index_ will be updated.
|
||||||
|
|
||||||
@ -453,8 +453,8 @@ beat(.5) :: sound('sine')
|
|||||||
.fmwave('triangle')
|
.fmwave('triangle')
|
||||||
.fmsus(0).fmdec(0.2).out()
|
.fmsus(0).fmdec(0.2).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## ZzFX
|
## ZzFX
|
||||||
|
|
||||||
@ -467,8 +467,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(.5) :: sound(['z_sine', 'z_triangle', 'z_sawtooth', 'z_tan', 'z_noise'].beat()).out()
|
beat(.5) :: sound(['z_sine', 'z_triangle', 'z_sawtooth', 'z_tan', 'z_noise'].beat()).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Minimalist chiptune",
|
"Minimalist chiptune",
|
||||||
`
|
`
|
||||||
@ -481,8 +481,8 @@ beat(.5) :: sound('z_triangle')
|
|||||||
.room(0.5).size(0.9)
|
.room(0.5).size(0.9)
|
||||||
.pitchJumpTime(0.01).out()
|
.pitchJumpTime(0.01).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
It comes with a set of parameters that can be used to tweak the sound. Don't underestimate this synth! It is very powerful for generating anything ranging from chaotic noise sources to lush pads:
|
It comes with a set of parameters that can be used to tweak the sound. Don't underestimate this synth! It is very powerful for generating anything ranging from chaotic noise sources to lush pads:
|
||||||
|
|
||||||
@ -519,8 +519,8 @@ beat(.25) :: sound('z_tan')
|
|||||||
.sustain(0).decay([0.2, 0.1].pick())
|
.sustain(0).decay([0.2, 0.1].pick())
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"What is happening to me?",
|
"What is happening to me?",
|
||||||
`
|
`
|
||||||
@ -529,8 +529,8 @@ beat(1) :: snd('zzfx').zzfx([
|
|||||||
[1.12,,97,.11,.16,.01,4,.77,,,30,.17,,,-1.9,,.01,.67,.2]
|
[1.12,,97,.11,.16,.01,4,.77,,,30,.17,,,-1.9,,.01,.67,.2]
|
||||||
].beat()).out()
|
].beat()).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Les voitures dans le futur",
|
"Les voitures dans le futur",
|
||||||
`
|
`
|
||||||
@ -541,8 +541,8 @@ beat(1) :: sound(['z_triangle', 'z_sine'].pick())
|
|||||||
.room(0.9).size(0.9)
|
.room(0.9).size(0.9)
|
||||||
.delayt(0.75).delayfb(0.5).out()
|
.delayt(0.75).delayfb(0.5).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Note that you can also design sounds [on this website](https://killedbyapixel.github.io/ZzFX/) and copy the generated code in Topos. To do so, please use the <ic>zzfx</ic> method with the generated array:
|
Note that you can also design sounds [on this website](https://killedbyapixel.github.io/ZzFX/) and copy the generated code in Topos. To do so, please use the <ic>zzfx</ic> method with the generated array:
|
||||||
${makeExample(
|
${makeExample(
|
||||||
@ -551,8 +551,8 @@ ${makeExample(
|
|||||||
|
|
||||||
beat(2) :: sound('zzfx').zzfx([3.62,,452,.16,.1,.21,,2.5,,,403,.05,.29,,,,.17,.34,.22,.68]).out()
|
beat(2) :: sound('zzfx').zzfx([3.62,,452,.16,.1,.21,,2.5,,,403,.05,.29,,,,.17,.34,.22,.68]).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
# Speech synthesis
|
# Speech synthesis
|
||||||
|
|
||||||
@ -571,16 +571,16 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
beat(4) :: speak("Hello world!")
|
beat(4) :: speak("Hello world!")
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Let's hear people talking about Topos",
|
"Let's hear people talking about Topos",
|
||||||
`
|
`
|
||||||
beat(2) :: speak("Topos!","fr",irand(0,5))
|
beat(2) :: speak("Topos!","fr",irand(0,5))
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
You can also use speech by chaining methods to a string:
|
You can also use speech by chaining methods to a string:
|
||||||
@ -590,8 +590,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
onbeat(4) :: "Foobaba".voice(irand(0,10)).speak()
|
onbeat(4) :: "Foobaba".voice(irand(0,10)).speak()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Building string and chaining",
|
"Building string and chaining",
|
||||||
@ -603,8 +603,8 @@ ${makeExample(
|
|||||||
|
|
||||||
beat(6) :: sentence.pitch(0).rate(0).voice([0,2].pick()).speak()
|
beat(6) :: sentence.pitch(0).rate(0).voice([0,2].pick()).speak()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Live coded poetry with array and string chaining",
|
"Live coded poetry with array and string chaining",
|
||||||
@ -623,7 +623,7 @@ ${makeExample(
|
|||||||
.rate(rand(.4,.6))
|
.rate(rand(.4,.6))
|
||||||
.speak();
|
.speak();
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -21,8 +21,8 @@ ${makeExample(
|
|||||||
// This code is alternating between different mod values
|
// This code is alternating between different mod values
|
||||||
beat([1,1/2,1/4,1/8].beat(2)) :: sound('hat').n(0).out()
|
beat([1,1/2,1/4,1/8].beat(2)) :: sound('hat').n(0).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Some sort of ringtone",
|
"Some sort of ringtone",
|
||||||
@ -41,16 +41,16 @@ beat(1/3) :: blip(400).pan(r(0,1)).out();
|
|||||||
flip(3) :: beat(1/6) :: blip(800).pan(r(0,1)).out();
|
flip(3) :: beat(1/6) :: blip(800).pan(r(0,1)).out();
|
||||||
beat([1,0.75].beat(2)) :: blip([50, 100].beat(2)).pan(r(0,1)).out();
|
beat([1,0.75].beat(2)) :: blip([50, 100].beat(2)).pan(r(0,1)).out();
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Beat can match multiple values",
|
"Beat can match multiple values",
|
||||||
`
|
`
|
||||||
beat([.5, 1.25])::sound('hat').out()
|
beat([.5, 1.25])::sound('hat').out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>pulse(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ pulses. A pulse is the tiniest possible rhythmic value.
|
- <ic>pulse(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ pulses. A pulse is the tiniest possible rhythmic value.
|
||||||
- <ic>number</ic>: if <ic>number = 1</ic>, the function will return <ic>true</ic> every pulse. Lists can be used too.
|
- <ic>number</ic>: if <ic>number = 1</ic>, the function will return <ic>true</ic> every pulse. Lists can be used too.
|
||||||
@ -63,16 +63,16 @@ ${makeExample(
|
|||||||
pulse([24, 16])::sound('hat').ad(0, .02).out()
|
pulse([24, 16])::sound('hat').ad(0, .02).out()
|
||||||
pulse([48, [36,24].dur(4, 1)])::sound('fhardkick').ad(0, .1).out()
|
pulse([48, [36,24].dur(4, 1)])::sound('fhardkick').ad(0, .1).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"pulse is the OG rhythmic function in Topos",
|
"pulse is the OG rhythmic function in Topos",
|
||||||
`
|
`
|
||||||
pulse([48, 24, 16].beat(4)) :: sound('linnhats').out()
|
pulse([48, 24, 16].beat(4)) :: sound('linnhats').out()
|
||||||
beat(1)::snd(['bd', '808oh'].beat(1)).out()
|
beat(1)::snd(['bd', '808oh'].beat(1)).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
- <ic>bar(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ bars.
|
- <ic>bar(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ bars.
|
||||||
@ -85,8 +85,8 @@ ${makeExample(
|
|||||||
bar(1)::sound('kick').out()
|
bar(1)::sound('kick').out()
|
||||||
beat(1)::sound('hat').speed(2).out()
|
beat(1)::sound('hat').speed(2).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
@ -97,8 +97,8 @@ beat(1)::sound('hat').speed(2).out()
|
|||||||
beat(1, 0.5)::sound('hat').speed(4).out()
|
beat(1, 0.5)::sound('hat').speed(4).out()
|
||||||
bar(1, 0.5)::sound('sn').out()
|
bar(1, 0.5)::sound('sn').out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>onbeat(...n: number[])</ic>: The <ic>onbeat</ic> function allows you to lock on to a specific beat from the clock to execute code. It can accept multiple arguments. It's usage is very straightforward and not hard to understand. You can pass either integers or floating point numbers. By default, topos is using a <ic>4/4</ic> bar meaning that you can target any of these beats (or in-between) with this function.
|
- <ic>onbeat(...n: number[])</ic>: The <ic>onbeat</ic> function allows you to lock on to a specific beat from the clock to execute code. It can accept multiple arguments. It's usage is very straightforward and not hard to understand. You can pass either integers or floating point numbers. By default, topos is using a <ic>4/4</ic> bar meaning that you can target any of these beats (or in-between) with this function.
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ onbeat(1,2,3,4)::snd('kick').out() // Bassdrum on each beat
|
|||||||
onbeat(2,4)::snd('snare').n([8,4].beat(4)).out() // Snare on acccentuated beats
|
onbeat(2,4)::snd('snare').n([8,4].beat(4)).out() // Snare on acccentuated beats
|
||||||
onbeat(1.5,2.5,3.5, 3.75)::snd('hat').gain(r(0.9,1.1)).out() // Cool high-hats
|
onbeat(1.5,2.5,3.5, 3.75)::snd('hat').gain(r(0.9,1.1)).out() // Cool high-hats
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## XOX Style sequencers
|
## XOX Style sequencers
|
||||||
|
|
||||||
@ -125,8 +125,8 @@ seq('xoxo')::sound('fhardkick').out()
|
|||||||
seq('ooxo')::sound('fsoftsnare').out()
|
seq('ooxo')::sound('fsoftsnare').out()
|
||||||
seq('xoxo', 0.25)::sound('fhh').out()
|
seq('xoxo', 0.25)::sound('fhh').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Another sequence using more complex parameters",
|
"Another sequence using more complex parameters",
|
||||||
@ -135,8 +135,8 @@ seq('xoxooxxoo', [0.5, 0.25].dur(2, 1))::sound('fhardkick').out()
|
|||||||
seq('ooxo', [1, 2].bar())::sound('fsoftsnare').speed(0.5).out()
|
seq('ooxo', [1, 2].bar())::sound('fsoftsnare').speed(0.5).out()
|
||||||
seq(['xoxoxoxx', 'xxoo'].bar())::sound('fhh').out()
|
seq(['xoxoxoxx', 'xxoo'].bar())::sound('fhh').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>fullseq(expr: string, duration: number = 0.5): boolean</ic> : a variant. Will return <ic>true</ic> or <ic>false</ic> for a whole period, depending on the symbol. Useful for long structure patterns.
|
- <ic>fullseq(expr: string, duration: number = 0.5): boolean</ic> : a variant. Will return <ic>true</ic> or <ic>false</ic> for a whole period, depending on the symbol. Useful for long structure patterns.
|
||||||
- <ic>expr: string</ic>: any string composed of <ic>x</ic> or <ic>o</ic> like so: <ic>"xooxoxxoxoo"</ic>.
|
- <ic>expr: string</ic>: any string composed of <ic>x</ic> or <ic>o</ic> like so: <ic>"xooxoxxoxoo"</ic>.
|
||||||
@ -159,8 +159,8 @@ function complexPat() {
|
|||||||
}
|
}
|
||||||
fullseq('xooxooxx', 4) ? simplePat() : complexPat()
|
fullseq('xooxooxx', 4) ? simplePat() : complexPat()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -181,8 +181,8 @@ rhythm(.5, 7, 8)::sound('sine')
|
|||||||
.freq(125).ad(0, .2).out()
|
.freq(125).ad(0, .2).out()
|
||||||
rhythm(.5, 3, 8)::sound('sine').freq(500).ad(0, .5).out()
|
rhythm(.5, 3, 8)::sound('sine').freq(500).ad(0, .5).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
- <ic>oneuclid(pulses: number, length: number, rotate: number): boolean</ic>: generates <ic>true</ic> or <ic>false</ic> values from an euclidian rhythm sequence. This is another version of <ic>euclid</ic> that does not take an iterator.
|
- <ic>oneuclid(pulses: number, length: number, rotate: number): boolean</ic>: generates <ic>true</ic> or <ic>false</ic> values from an euclidian rhythm sequence. This is another version of <ic>euclid</ic> that does not take an iterator.
|
||||||
@ -194,8 +194,8 @@ ${makeExample(
|
|||||||
oneuclid(5, 9) :: snd('kick').out()
|
oneuclid(5, 9) :: snd('kick').out()
|
||||||
oneuclid(7,16) :: snd('east').end(0.5).n(irand(3,5)).out()
|
oneuclid(7,16) :: snd('east').end(0.5).n(irand(3,5)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>bin(iterator: number, n: number): boolean</ic>: a binary rhythm generator. It transforms the given number into its binary representation (_e.g_ <ic>34</ic> becomes <ic>100010</ic>). It then returns a boolean value based on the iterator in order to generate a rhythm.
|
- <ic>bin(iterator: number, n: number): boolean</ic>: a binary rhythm generator. It transforms the given number into its binary representation (_e.g_ <ic>34</ic> becomes <ic>100010</ic>). It then returns a boolean value based on the iterator in order to generate a rhythm.
|
||||||
- <ic>binrhythm(divisor: number, n: number): boolean: boolean</ic>: iterator-less version of the binary rhythm generator.
|
- <ic>binrhythm(divisor: number, n: number): boolean: boolean</ic>: iterator-less version of the binary rhythm generator.
|
||||||
@ -207,8 +207,8 @@ bpm(135);
|
|||||||
beat(.5) && bin($(1), 12) && snd('kick').n([4,9].beat(1.5)).out()
|
beat(.5) && bin($(1), 12) && snd('kick').n([4,9].beat(1.5)).out()
|
||||||
beat(.5) && bin($(2), 34) && snd('snare').n([3,5].beat(1)).out()
|
beat(.5) && bin($(2), 34) && snd('snare').n([3,5].beat(1)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"binrhythm for fast cool binary rhythms!",
|
"binrhythm for fast cool binary rhythms!",
|
||||||
@ -221,8 +221,8 @@ binrhythm([.5, .25].beat(1), 30) && snd('wt_granular').n(a)
|
|||||||
.adsr(0, r(.1, .4), 0, 0).freq([50, 60, 72].beat(4))
|
.adsr(0, r(.1, .4), 0, 0).freq([50, 60, 72].beat(4))
|
||||||
.room(1).size(1).out()
|
.room(1).size(1).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Submarine jungle music",
|
"Submarine jungle music",
|
||||||
@ -233,8 +233,8 @@ beat(.5) && bin($(1), 911) && snd('ST69').n([2,3,4].beat())
|
|||||||
.room(1).size(1).out()
|
.room(1).size(1).out()
|
||||||
beat(.5) && sound('amencutup').n(irand(2,7)).shape(0.3).out()
|
beat(.5) && sound('amencutup').n(irand(2,7)).shape(0.3).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
If you don't find it spicy enough, you can add some more probabilities to your rhythms by taking advantage of the probability functions. See the functions documentation page to learn more about them.
|
If you don't find it spicy enough, you can add some more probabilities to your rhythms by taking advantage of the probability functions. See the functions documentation page to learn more about them.
|
||||||
|
|
||||||
@ -247,8 +247,8 @@ prob(60)::beat(.5) && euclid($(2), 3, 8) && snd('mash')
|
|||||||
.pan(usine(1/4)).out()
|
.pan(usine(1/4)).out()
|
||||||
prob(80)::beat(.5) && sound(['hh', 'hat'].pick()).out()
|
prob(80)::beat(.5) && sound(['hh', 'hat'].pick()).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -26,8 +26,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
log(\`\$\{cbar()}\, \$\{cbeat()\}, \$\{cpulse()\}\`)
|
log(\`\$\{cbar()}\, \$\{cbeat()\}, \$\{cpulse()\}\`)
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
### BPM and PPQN
|
### BPM and PPQN
|
||||||
|
|
||||||
@ -83,8 +83,8 @@ if((cbar() % 4) > 1) {
|
|||||||
// This is always playing no matter what happens
|
// This is always playing no matter what happens
|
||||||
beat([.5, .5, 1, .25].beat(0.5)) :: sound('shaker').out()
|
beat([.5, .5, 1, .25].beat(0.5)) :: sound('shaker').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Time Warping
|
## Time Warping
|
||||||
|
|
||||||
@ -108,8 +108,8 @@ flip(3) :: beat([.25,.5].beat(.5)) :: sound('dr')
|
|||||||
// Jumping back and forth in time
|
// Jumping back and forth in time
|
||||||
beat(.25) :: warp([12, 48, 24, 1, 120, 30].pick())
|
beat(.25) :: warp([12, 48, 24, 1, 120, 30].pick())
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>beat_warp(beat: number)</ic>: this function jumps to the _n_ beat of the clock. The first beat is <ic>1</ic>.
|
- <ic>beat_warp(beat: number)</ic>: this function jumps to the _n_ beat of the clock. The first beat is <ic>1</ic>.
|
||||||
|
|
||||||
@ -130,8 +130,8 @@ beat(.5) :: snd('arpy').note(
|
|||||||
// Comment me to stop warping!
|
// Comment me to stop warping!
|
||||||
beat(1) :: beat_warp([2,4,5,10,11].pick())
|
beat(1) :: beat_warp([2,4,5,10,11].pick())
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Transport-based rhythm generators
|
## Transport-based rhythm generators
|
||||||
|
|
||||||
@ -144,8 +144,8 @@ onbeat(1,2,3,4)::snd('kick').out() // Bassdrum on each beat
|
|||||||
onbeat(2,4)::snd('snare').n([8,4].beat(4)).out() // Snare on acccentuated beats
|
onbeat(2,4)::snd('snare').n([8,4].beat(4)).out() // Snare on acccentuated beats
|
||||||
onbeat(1.5,2.5,3.5, 3.75)::snd('hat').gain(r(0.9,1.1)).out() // Cool high-hats
|
onbeat(1.5,2.5,3.5, 3.75)::snd('hat').gain(r(0.9,1.1)).out() // Cool high-hats
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Let's do something more complex",
|
"Let's do something more complex",
|
||||||
@ -156,8 +156,8 @@ beat([.25, 1/8].beat(1.5))::snd('hat').n(2)
|
|||||||
.gain(rand(0.4, 0.7)).end(0.05)
|
.gain(rand(0.4, 0.7)).end(0.05)
|
||||||
.pan(usine()).out()
|
.pan(usine()).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- <ic>oncount(beats: number[], meter: number)</ic>: This function is similar to <ic>onbeat</ic> but it allows you to specify a custom number of beats as the last argument.
|
- <ic>oncount(beats: number[], meter: number)</ic>: This function is similar to <ic>onbeat</ic> but it allows you to specify a custom number of beats as the last argument.
|
||||||
|
|
||||||
@ -171,8 +171,8 @@ z1('1/16 (0 2 3 4)+(0 2 4 6)').scale('pentatonic').sound('sawtooth')
|
|||||||
onbeat(1,1.5,2,3,4) :: sound('bd').gain(2.0).out()
|
onbeat(1,1.5,2,3,4) :: sound('bd').gain(2.0).out()
|
||||||
oncount([1,3,5.5,7,7.5,8],8) :: sound('hh').gain(irand(1.0,4.0)).out()
|
oncount([1,3,5.5,7,7.5,8],8) :: sound('hh').gain(irand(1.0,4.0)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Using oncount to create rhythms with a custom meter",
|
"Using oncount to create rhythms with a custom meter",
|
||||||
@ -183,8 +183,8 @@ oncount([5, 6, 13],16) :: sound('shaker').room(0.25).gain(0.9).out()
|
|||||||
oncount([2, 3, 3.5, 6, 7, 10, 15],16) :: sound('hh').n(8).gain(0.8).out()
|
oncount([2, 3, 3.5, 6, 7, 10, 15],16) :: sound('hh').n(8).gain(0.8).out()
|
||||||
oncount([1, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16],16) :: sound('hh').out()
|
oncount([1, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16],16) :: sound('hh').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -22,8 +22,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
v('my_cool_variable', 2)
|
v('my_cool_variable', 2)
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Getting that variable back and printing!",
|
"Getting that variable back and printing!",
|
||||||
@ -31,8 +31,8 @@ ${makeExample(
|
|||||||
// Note that we just use one argument
|
// Note that we just use one argument
|
||||||
log(v('my_cool_variable'))
|
log(v('my_cool_variable'))
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
## Counter and iterators
|
## Counter and iterators
|
||||||
@ -55,8 +55,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
rhythm(.25, 6, 8) :: sound('dr').n($(1)).end(.25).out()
|
rhythm(.25, 6, 8) :: sound('dr').n($(1)).end(.25).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Using a more complex counter",
|
"Using a more complex counter",
|
||||||
@ -64,8 +64,8 @@ ${makeExample(
|
|||||||
// Limit is 20, step is 5
|
// Limit is 20, step is 5
|
||||||
rhythm(.25, 6, 8) :: sound('dr').n($(1, 20, 5)).end(.25).out()
|
rhythm(.25, 6, 8) :: sound('dr').n($(1, 20, 5)).end(.25).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Calling the drunk mechanism",
|
"Calling the drunk mechanism",
|
||||||
@ -73,10 +73,10 @@ ${makeExample(
|
|||||||
// Limit is 20, step is 5
|
// Limit is 20, step is 5
|
||||||
rhythm(.25, 6, 8) :: sound('dr').n(drunk()).end(.25).out()
|
rhythm(.25, 6, 8) :: sound('dr').n(drunk()).end(.25).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
`
|
`;
|
||||||
}
|
};
|
||||||
|
|||||||
@ -12,7 +12,9 @@ Ziffers is a **musical number based notation** tuned for _live coding_. It is a
|
|||||||
- exploring **generative / aleatoric / stochastic** melodies and applying them to sounds and synths.
|
- exploring **generative / aleatoric / stochastic** melodies and applying them to sounds and synths.
|
||||||
- embracing a different mindset and approach to time and **patterning**.
|
- embracing a different mindset and approach to time and **patterning**.
|
||||||
|
|
||||||
${makeExample("Super Fancy Ziffers example", `
|
${makeExample(
|
||||||
|
"Super Fancy Ziffers example",
|
||||||
|
`
|
||||||
z1('1/8 024!3 035 024 0124').sound('wt_stereo')
|
z1('1/8 024!3 035 024 0124').sound('wt_stereo')
|
||||||
.adsr(0, .4, 0.5, .4).gain(0.1)
|
.adsr(0, .4, 0.5, .4).gain(0.1)
|
||||||
.lpadsr(4, 0, .2, 0, 0)
|
.lpadsr(4, 0, .2, 0, 0)
|
||||||
@ -26,7 +28,9 @@ z2('<1/8 1/16> __ 0 <(^) (^ ^)> (0,8)').sound('wt_stereo')
|
|||||||
let osci = 1500 + usine(1/2) * 2000;
|
let osci = 1500 + usine(1/2) * 2000;
|
||||||
z3('can can:2').sound().gain(1).cutoff(osci).out()
|
z3('can can:2').sound().gain(1).cutoff(osci).out()
|
||||||
z4('1/4 kick kick snare kick').sound().gain(1).cutoff(osci).out()
|
z4('1/4 kick kick snare kick').sound().gain(1).cutoff(osci).out()
|
||||||
`, true)}
|
`,
|
||||||
|
true,
|
||||||
|
)}
|
||||||
|
|
||||||
## Notation
|
## Notation
|
||||||
|
|
||||||
@ -53,8 +57,8 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
z1('0.25 0 1 2 3 4 5 6 7 8 9').sound('wt_stereo')
|
z1('0.25 0 1 2 3 4 5 6 7 8 9').sound('wt_stereo')
|
||||||
.adsr(0, .1, 0, 0).out()`,
|
.adsr(0, .1, 0, 0).out()`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Escaped pitches using curly brackets",
|
"Escaped pitches using curly brackets",
|
||||||
@ -62,8 +66,8 @@ ${makeExample(
|
|||||||
.sound('wt_05').pan(r(0,1))
|
.sound('wt_05').pan(r(0,1))
|
||||||
.cutoff(usaw(1/2) * 4000)
|
.cutoff(usaw(1/2) * 4000)
|
||||||
.room(0.9).size(0.9).out()`,
|
.room(0.9).size(0.9).out()`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Durations using fractions and floating point numbers",
|
"Durations using fractions and floating point numbers",
|
||||||
@ -72,8 +76,8 @@ z1('1/8 0 2 4 0 2 4 1/4 0 3 5 0.25 _ 0 7 0 7')
|
|||||||
.sound('square').delay(0.5).delayt(1/8)
|
.sound('square').delay(0.5).delayt(1/8)
|
||||||
.adsr(0, .1, 0, 0).delayfb(0.45).out()
|
.adsr(0, .1, 0, 0).delayfb(0.45).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Disco was invented thanks to Ziffers",
|
"Disco was invented thanks to Ziffers",
|
||||||
@ -82,8 +86,8 @@ z1('e _ _ 0 ^ 0 _ 0 ^ 0').sound('jvbass').out()
|
|||||||
beat(1)::snd('bd').out(); beat(2)::snd('sd').out()
|
beat(1)::snd('bd').out(); beat(2)::snd('sd').out()
|
||||||
beat(3) :: snd('cp').room(0.5).size(0.5).orbit(2).out()
|
beat(3) :: snd('cp').room(0.5).size(0.5).orbit(2).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Accidentals and rests for nice melodies",
|
"Accidentals and rests for nice melodies",
|
||||||
@ -94,8 +98,8 @@ z1('^ 1/8 0 1 b2 3 4 _ 4 b5 4 3 b2 1 0')
|
|||||||
.fmi(0.5).fmh(2).delay(0.5).delayt(1/3)
|
.fmi(0.5).fmh(2).delay(0.5).delayt(1/3)
|
||||||
.adsr(0, .1, 0, 0).out()
|
.adsr(0, .1, 0, 0).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Repeat items n-times",
|
"Repeat items n-times",
|
||||||
@ -107,8 +111,8 @@ z2('1/8 _ 0!4 5!4 4!2 7!2')
|
|||||||
.scale('major').sound('wt_oboe')
|
.scale('major').sound('wt_oboe')
|
||||||
.shape(0.2).sustain(0.1).out()
|
.shape(0.2).sustain(0.1).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Subdivided durations",
|
"Subdivided durations",
|
||||||
@ -117,8 +121,8 @@ z1('w [0 [5 [3 7]]] h [0 4]')
|
|||||||
.scale('major').sound('sine')
|
.scale('major').sound('sine')
|
||||||
.fmi(usine(.5)).fmh(2).out()
|
.fmi(usine(.5)).fmh(2).out()
|
||||||
`,
|
`,
|
||||||
false
|
false,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Chords
|
## Chords
|
||||||
|
|
||||||
@ -131,8 +135,8 @@ z1('1.0 024 045 058 046 014')
|
|||||||
.sound('sine').adsr(0.5, 1, 0, 0)
|
.sound('sine').adsr(0.5, 1, 0, 0)
|
||||||
.room(0.5).size(0.9)
|
.room(0.5).size(0.9)
|
||||||
.scale("minor").out()
|
.scale("minor").out()
|
||||||
`
|
`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Chords from roman numerals",
|
"Chords from roman numerals",
|
||||||
@ -140,8 +144,8 @@ ${makeExample(
|
|||||||
z1('2/4 i vi ii v')
|
z1('2/4 i vi ii v')
|
||||||
.sound('triangle').adsr(0.2, 0.3, 0, 0)
|
.sound('triangle').adsr(0.2, 0.3, 0, 0)
|
||||||
.room(0.5).size(0.9).scale("major").out()
|
.room(0.5).size(0.9).scale("major").out()
|
||||||
`
|
`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Named chords with repeats",
|
"Named chords with repeats",
|
||||||
@ -150,16 +154,16 @@ z1('0.25 Bmaj7!2 D7!2 _ Gmaj7!2 Bb7!2 ^ Ebmaj7!2')
|
|||||||
.sound('square').room(0.5).cutoff(500)
|
.sound('square').room(0.5).cutoff(500)
|
||||||
.lpadsr(4, 0, .4, 0, 0).size(0.9)
|
.lpadsr(4, 0, .4, 0, 0).size(0.9)
|
||||||
.scale("major").out()
|
.scale("major").out()
|
||||||
`
|
`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Transposing chords",
|
"Transposing chords",
|
||||||
`
|
`
|
||||||
z1('q Amin!2').key(["A2", "E2"].beat(4))
|
z1('q Amin!2').key(["A2", "E2"].beat(4))
|
||||||
.sound('sawtooth').cutoff(500)
|
.sound('sawtooth').cutoff(500)
|
||||||
.lpadsr(2, 0, .5, 0, 0, 0).out()`
|
.lpadsr(2, 0, .5, 0, 0, 0).out()`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Chord transposition with roman numerals",
|
"Chord transposition with roman numerals",
|
||||||
@ -169,8 +173,8 @@ z1('i i v%-4 v%-2 vi%-5 vi%-3 iv%-2 iv%-1')
|
|||||||
.delay(0.5).delayt([1/8, 1/4].beat(4))
|
.delay(0.5).delayt([1/8, 1/4].beat(4))
|
||||||
.delayfb(0.5).out()
|
.delayfb(0.5).out()
|
||||||
beat(4) :: sound('breaks165').stretch(4).out()
|
beat(4) :: sound('breaks165').stretch(4).out()
|
||||||
`
|
`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Chord transposition with named chords",
|
"Chord transposition with named chords",
|
||||||
@ -178,8 +182,8 @@ ${makeExample(
|
|||||||
z1('1/4 Cmin!3 Fmin!3 Fmin%-1 Fmin%-2 Fmin%-1')
|
z1('1/4 Cmin!3 Fmin!3 Fmin%-1 Fmin%-2 Fmin%-1')
|
||||||
.sound("sine").bpf(500 + usine(1/4) * 2000)
|
.sound("sine").bpf(500 + usine(1/4) * 2000)
|
||||||
.out()
|
.out()
|
||||||
`
|
`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Programmatic inversions",
|
"Programmatic inversions",
|
||||||
@ -187,8 +191,8 @@ ${makeExample(
|
|||||||
z1('1/6 i v 1/3 vi iv').invert([1,-1,-2,0].beat(4))
|
z1('1/6 i v 1/3 vi iv').invert([1,-1,-2,0].beat(4))
|
||||||
.sound("sawtooth").cutoff(1000)
|
.sound("sawtooth").cutoff(1000)
|
||||||
.lpadsr(2, 0, .2, 0, 0).out()
|
.lpadsr(2, 0, .2, 0, 0).out()
|
||||||
`
|
`,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Algorithmic operations
|
## Algorithmic operations
|
||||||
|
|
||||||
@ -204,8 +208,8 @@ z1("1/8 _ 0 (0 1 3)+(1 2) 0 (2 3 5)-(1 2)").sound('sine')
|
|||||||
.room(0.9).size(0.9).sustain(0.1).delay(0.5).delay(0.125)
|
.room(0.9).size(0.9).sustain(0.1).delay(0.5).delay(0.125)
|
||||||
.delayfb(0.25).out();
|
.delayfb(0.25).out();
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
* **Random numbers:** <ic>(4,6)</ic> Random number between 4 and 6
|
* **Random numbers:** <ic>(4,6)</ic> Random number between 4 and 6
|
||||||
|
|
||||||
@ -219,8 +223,8 @@ z1("s _ (0,8) 0 0 (0,5) 0 0").sound('sine')
|
|||||||
.delay(0.125).delayfb(0.25).out();
|
.delay(0.125).delayfb(0.25).out();
|
||||||
beat(.5) :: snd(['kick', 'hat'].beat(.5)).out()
|
beat(.5) :: snd(['kick', 'hat'].beat(.5)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Keys and scales
|
## Keys and scales
|
||||||
|
|
||||||
@ -249,8 +253,8 @@ z1("s (0,8) 0 0 (0,5) 0 0").sound('sine')
|
|||||||
.delay(0.125).delayfb(0.25).out();
|
.delay(0.125).delayfb(0.25).out();
|
||||||
beat(.5) :: snd(['kick', 'hat'].beat(.5)).out()
|
beat(.5) :: snd(['kick', 'hat'].beat(.5)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -282,8 +286,8 @@ z1("s (0,8) 0 0 (0,5) 0 0").sound('sine')
|
|||||||
.delay(0.25).delayfb(0.5).out();
|
.delay(0.25).delayfb(0.5).out();
|
||||||
beat(1, 1.75) :: snd(['kick', 'hat'].beat(1)).out()
|
beat(1, 1.75) :: snd(['kick', 'hat'].beat(1)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
Microtonal scales can be defined using <a href="https://www.huygens-fokker.org/scala/scl_format.html" target="_blank">Scala format</a> or by extended notation defined by Sevish <a href="https://sevish.com/scaleworkshop/" target="_blank">Scale workshop</a>, for example:
|
Microtonal scales can be defined using <a href="https://www.huygens-fokker.org/scala/scl_format.html" target="_blank">Scala format</a> or by extended notation defined by Sevish <a href="https://sevish.com/scaleworkshop/" target="_blank">Scale workshop</a>, for example:
|
||||||
|
|
||||||
@ -300,8 +304,8 @@ z1("s ^ (0,8) 0 0 _ (0,5) 0 0").sound('sine')
|
|||||||
.delay(0.25).delayfb(0.5).out();
|
.delay(0.25).delayfb(0.5).out();
|
||||||
beat(1, 1.75) :: snd(['kick', 'hat'].beat(1)).out()
|
beat(1, 1.75) :: snd(['kick', 'hat'].beat(1)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Synchronization
|
## Synchronization
|
||||||
|
|
||||||
@ -315,8 +319,8 @@ ${makeExample(
|
|||||||
z0('w 0 8').sound('peri').out()
|
z0('w 0 8').sound('peri').out()
|
||||||
z1('e 0 4 5 9').sound('bell').out()
|
z1('e 0 4 5 9').sound('bell').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Sync with wait",
|
"Sync with wait",
|
||||||
@ -324,8 +328,8 @@ ${makeExample(
|
|||||||
z1('w 0 5').sound('pluck').release(0.1).sustain(0.25).out()
|
z1('w 0 5').sound('pluck').release(0.1).sustain(0.25).out()
|
||||||
z2('q 6 3').wait(z1).sound('sine').release(0.16).sustain(0.55).out()
|
z2('q 6 3').wait(z1).sound('sine').release(0.16).sustain(0.55).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Sync on first run",
|
"Sync on first run",
|
||||||
@ -333,8 +337,8 @@ ${makeExample(
|
|||||||
z1('w __ 0 5 9 3').sound('bin').out()
|
z1('w __ 0 5 9 3').sound('bin').out()
|
||||||
z2('q __ 4 2 e 6 3 q 6').sync(z1).sound('east').out()
|
z2('q __ 4 2 e 6 3 q 6').sync(z1).sound('east').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
@ -346,32 +350,32 @@ ${makeExample(
|
|||||||
z1('0 1 2 3').key('G3')
|
z1('0 1 2 3').key('G3')
|
||||||
.scale('minor').sound('sine').out()
|
.scale('minor').sound('sine').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"More complex chaining",
|
"More complex chaining",
|
||||||
`
|
`
|
||||||
z1('0 1 2 3 4').key('G3').scale('minor').sound('sine').often(n => n.pitch+=3).rarely(s => s.delay(0.5)).out()
|
z1('0 1 2 3 4').key('G3').scale('minor').sound('sine').often(n => n.pitch+=3).rarely(s => s.delay(0.5)).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Simple options",
|
"Simple options",
|
||||||
`
|
`
|
||||||
z1('0 3 2 4',{key: 'D3', scale: 'minor pentatonic'}).sound('sine').out()
|
z1('0 3 2 4',{key: 'D3', scale: 'minor pentatonic'}).sound('sine').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Duration chars",
|
"Duration chars",
|
||||||
`
|
`
|
||||||
z1('q 0 0 4 4 5 5 h4 q 3 3 2 2 1 1 h0').sound('sine').out()
|
z1('q 0 0 4 4 5 5 h4 q 3 3 2 2 1 1 h0').sound('sine').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Fraction durations",
|
"Fraction durations",
|
||||||
@ -379,8 +383,8 @@ ${makeExample(
|
|||||||
z1('1/4 0 0 4 4 5 5 2/4 4 1/4 3 3 2 2 1 1 2/4 0')
|
z1('1/4 0 0 4 4 5 5 2/4 4 1/4 3 3 2 2 1 1 2/4 0')
|
||||||
.sound('sine').out()
|
.sound('sine').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Decimal durations",
|
"Decimal durations",
|
||||||
@ -388,8 +392,8 @@ ${makeExample(
|
|||||||
z1('0.25 5 1 2 6 0.125 3 8 0.5 4 1.0 0')
|
z1('0.25 5 1 2 6 0.125 3 8 0.5 4 1.0 0')
|
||||||
.sound('sine').scale("galian").out()
|
.sound('sine').scale("galian").out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Rest and octaves",
|
"Rest and octaves",
|
||||||
@ -397,8 +401,8 @@ ${makeExample(
|
|||||||
z1('q 0 ^ e0 r _ 0 _ r 4 ^4 4')
|
z1('q 0 ^ e0 r _ 0 _ r 4 ^4 4')
|
||||||
.sound('sine').scale("godian").out()
|
.sound('sine').scale("godian").out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Rests with durations",
|
"Rests with durations",
|
||||||
@ -406,7 +410,7 @@ ${makeExample(
|
|||||||
z1('q 0 4 e^r 3 e3 0.5^r h4 1/4^r e 5 r 0.125^r 0')
|
z1('q 0 4 e^r 3 e3 0.5^r h4 1/4^r e 5 r 0.125^r 0')
|
||||||
.sound('sine').scale("aeryptian").out()
|
.sound('sine').scale("aeryptian").out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- Scales
|
- Scales
|
||||||
@ -419,8 +423,8 @@ z1('q 0 3 {10 14} e 8 4 {5 10 12 14 7 0}').sound('sine')
|
|||||||
.scale("17/16 9/8 6/5 5/4 4/3 11/8 3/2 13/8 5/3 7/4 15/8 2/1")
|
.scale("17/16 9/8 6/5 5/4 4/3 11/8 3/2 13/8 5/3 7/4 15/8 2/1")
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Scala scale from variable",
|
"Scala scale from variable",
|
||||||
@ -437,8 +441,8 @@ ${makeExample(
|
|||||||
|
|
||||||
onbeat(1,1.5,3) :: sound('bd').cutoff(100 + usine(.25) * 1000).out()
|
onbeat(1,1.5,3) :: sound('bd').cutoff(100 + usine(.25) * 1000).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
- Algorithmic operations
|
- Algorithmic operations
|
||||||
|
|
||||||
@ -449,8 +453,8 @@ z1('q 0 (2,4) 4 (5,9)').sound('sine')
|
|||||||
.scale("Bebop minor")
|
.scale("Bebop minor")
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"List operations",
|
"List operations",
|
||||||
@ -459,8 +463,8 @@ z1('q (0 3 1 5)+(2 5) e (0 5 2)*(2 3) (0 5 2)>>(2 3) (0 5 2)%(2 3)').sound('sine
|
|||||||
.scale("Bebop major")
|
.scale("Bebop major")
|
||||||
.out()
|
.out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
## Samples
|
## Samples
|
||||||
|
|
||||||
@ -471,16 +475,16 @@ ${makeExample(
|
|||||||
`
|
`
|
||||||
z1('bd [hh hh]').octave(-2).sound('sine').out()
|
z1('bd [hh hh]').octave(-2).sound('sine').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"More complex pattern",
|
"More complex pattern",
|
||||||
`
|
`
|
||||||
z1('bd [hh <hh <cp cp:2>>]').octave(-2).sound('sine').out()
|
z1('bd [hh <hh <cp cp:2>>]').octave(-2).sound('sine').out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Pitched samples",
|
"Pitched samples",
|
||||||
@ -489,8 +493,8 @@ ${makeExample(
|
|||||||
.octave(-1).sound()
|
.octave(-1).sound()
|
||||||
.adsr(0.25,0.125,0.125,0.25).out()
|
.adsr(0.25,0.125,0.125,0.25).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Pitched samples from list operation",
|
"Pitched samples from list operation",
|
||||||
@ -500,8 +504,8 @@ ${makeExample(
|
|||||||
.scale('110 220 320 450')
|
.scale('110 220 320 450')
|
||||||
.sound().out()
|
.sound().out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Pitched samples with list notation",
|
"Pitched samples with list notation",
|
||||||
@ -510,8 +514,8 @@ ${makeExample(
|
|||||||
.octave(-1).sound()
|
.octave(-1).sound()
|
||||||
.adsr(0.25,0.125,0.125,0.25).out()
|
.adsr(0.25,0.125,0.125,0.25).out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Sample indices",
|
"Sample indices",
|
||||||
@ -519,16 +523,16 @@ ${makeExample(
|
|||||||
z1('e 1:2 4:3 6:2')
|
z1('e 1:2 4:3 6:2')
|
||||||
.octave(-1).sound("east").out()
|
.octave(-1).sound("east").out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Pitched samples with sample indices",
|
"Pitched samples with sample indices",
|
||||||
`
|
`
|
||||||
z1('_e 1@east:2 4@bd:3 6@arp:2 9@baa').sound().out()
|
z1('_e 1@east:2 4@bd:3 6@arp:2 9@baa').sound().out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -543,8 +547,8 @@ ${makeExample(
|
|||||||
"q 2 7 8 6".z1().octave(-1).sound('sine').out()
|
"q 2 7 8 6".z1().octave(-1).sound('sine').out()
|
||||||
"q 2 7 8 6".z2({key: "C2", scale: "aeolian"}).sound('sine').scale("minor").out()
|
"q 2 7 8 6".z2({key: "C2", scale: "aeolian"}).sound('sine').scale("minor").out()
|
||||||
`,
|
`,
|
||||||
true
|
true,
|
||||||
)}
|
)}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -141,7 +141,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
|
|||||||
}
|
}
|
||||||
return Array.from(
|
return Array.from(
|
||||||
{ length: times },
|
{ length: times },
|
||||||
() => Math.floor(api.randomGen() * (max - min + 1)) + min
|
() => Math.floor(api.randomGen() * (max - min + 1)) + min,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
|
|||||||
const chunk_size = divisor; // Get the first argument (chunk size)
|
const chunk_size = divisor; // Get the first argument (chunk size)
|
||||||
const timepos = api.app.clock.pulses_since_origin;
|
const timepos = api.app.clock.pulses_since_origin;
|
||||||
const slice_count = Math.floor(
|
const slice_count = Math.floor(
|
||||||
timepos / Math.floor(chunk_size * api.ppqn())
|
timepos / Math.floor(chunk_size * api.ppqn()),
|
||||||
);
|
);
|
||||||
return this[slice_count % this.length];
|
return this[slice_count % this.length];
|
||||||
};
|
};
|
||||||
@ -174,12 +174,12 @@ export const makeArrayExtensions = (api: UserAPI) => {
|
|||||||
const timepos = api.app.clock.pulses_since_origin;
|
const timepos = api.app.clock.pulses_since_origin;
|
||||||
const ppqn = api.ppqn();
|
const ppqn = api.ppqn();
|
||||||
const adjustedDurations: number[] = this.map(
|
const adjustedDurations: number[] = this.map(
|
||||||
(_, index) => durations[index % durations.length]
|
(_, index) => durations[index % durations.length],
|
||||||
);
|
);
|
||||||
const totalDurationInPulses = adjustedDurations.reduce(
|
const totalDurationInPulses = adjustedDurations.reduce(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
(acc, duration) => acc + duration * ppqn,
|
(acc, duration) => acc + duration * ppqn,
|
||||||
0
|
0,
|
||||||
);
|
);
|
||||||
const loopPosition = timepos % totalDurationInPulses;
|
const loopPosition = timepos % totalDurationInPulses;
|
||||||
let cumulativeDuration = 0;
|
let cumulativeDuration = 0;
|
||||||
@ -402,7 +402,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
|
|||||||
|
|
||||||
Array.prototype.scale = function (
|
Array.prototype.scale = function (
|
||||||
scale: string = "major",
|
scale: string = "major",
|
||||||
base_note: number = 0
|
base_note: number = 0,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* @param scale - the scale name
|
* @param scale - the scale name
|
||||||
@ -426,7 +426,7 @@ Array.prototype.scale = function (
|
|||||||
|
|
||||||
Array.prototype.scaleArp = function (
|
Array.prototype.scaleArp = function (
|
||||||
scaleName: string = "major",
|
scaleName: string = "major",
|
||||||
boundary: number = 0
|
boundary: number = 0,
|
||||||
) {
|
) {
|
||||||
/*
|
/*
|
||||||
* @param scaleName - the scale name
|
* @param scaleName - the scale name
|
||||||
|
|||||||
@ -29,85 +29,83 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const makeNumberExtensions = (api: UserAPI) => {
|
export const makeNumberExtensions = (api: UserAPI) => {
|
||||||
|
Number.prototype.z0 = function (options: { [key: string]: any } = {}) {
|
||||||
Number.prototype.z0 = function (options: {[key: string]: any} = {}) {
|
|
||||||
return api.z0(this.valueOf().toString().split("").join(" "), options);
|
return api.z0(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z1 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z1 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z1(this.valueOf().toString().split("").join(" "), options);
|
return api.z1(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z2 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z2 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z2(this.valueOf().toString().split("").join(" "), options);
|
return api.z2(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z3 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z3 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z3(this.valueOf().toString().split("").join(" "), options);
|
return api.z3(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z4 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z4 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z4(this.valueOf().toString().split("").join(" "), options);
|
return api.z4(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z5 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z5 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z5(this.valueOf().toString().split("").join(" "), options);
|
return api.z5(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z6 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z6 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z6(this.valueOf().toString().split("").join(" "), options);
|
return api.z6(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z7 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z7 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z7(this.valueOf().toString().split("").join(" "), options);
|
return api.z7(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z8 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z8 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z8(this.valueOf().toString().split("").join(" "), options);
|
return api.z8(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z9 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z9 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z9(this.valueOf().toString().split("").join(" "), options);
|
return api.z9(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z10 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z10 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z10(this.valueOf().toString().split("").join(" "), options);
|
return api.z10(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z11 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z11 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z11(this.valueOf().toString().split("").join(" "), options);
|
return api.z11(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z12 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z12 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z12(this.valueOf().toString().split("").join(" "), options);
|
return api.z12(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z13 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z13 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z13(this.valueOf().toString().split("").join(" "), options);
|
return api.z13(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z14 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z14 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z14(this.valueOf().toString().split("").join(" "), options);
|
return api.z14(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z15 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z15 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z15(this.valueOf().toString().split("").join(" "), options);
|
return api.z15(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.z16 = function (options: {[key: string]: any} = {}) {
|
Number.prototype.z16 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z16(this.valueOf().toString().split("").join(" "), options);
|
return api.z16(this.valueOf().toString().split("").join(" "), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Number.prototype.midi = function (...kwargs: any[]) {
|
Number.prototype.midi = function (...kwargs: any[]) {
|
||||||
return api.midi(this.valueOf(), ...kwargs);
|
return api.midi(this.valueOf(), ...kwargs);
|
||||||
}
|
};
|
||||||
|
|
||||||
Number.prototype.sound = function (name: string) {
|
Number.prototype.sound = function (name: string) {
|
||||||
if(Number.isInteger(this.valueOf())) {
|
if (Number.isInteger(this.valueOf())) {
|
||||||
return (api.sound(name) as SoundEvent).note(this.valueOf());
|
return (api.sound(name) as SoundEvent).note(this.valueOf());
|
||||||
} else {
|
} else {
|
||||||
return (api.sound(name) as SoundEvent).freq(this.valueOf());
|
return (api.sound(name) as SoundEvent).freq(this.valueOf());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
};
|
||||||
}
|
|
||||||
|
|||||||
@ -35,118 +35,121 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isJsonString = (str: string):boolean => {
|
const isJsonString = (str: string): boolean => {
|
||||||
return str[0] === '{' && str[str.length - 1] === '}'
|
return str[0] === "{" && str[str.length - 1] === "}";
|
||||||
}
|
};
|
||||||
|
|
||||||
const stringObject = (str: string, params: object) => {
|
const stringObject = (str: string, params: object) => {
|
||||||
if(isJsonString(str)) {
|
if (isJsonString(str)) {
|
||||||
const obj = JSON.parse(str);
|
const obj = JSON.parse(str);
|
||||||
return JSON.stringify({...obj, ...params});
|
return JSON.stringify({ ...obj, ...params });
|
||||||
} else {
|
} else {
|
||||||
return JSON.stringify({...params, text: str});
|
return JSON.stringify({ ...params, text: str });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const makeStringExtensions = (api: UserAPI) => {
|
export const makeStringExtensions = (api: UserAPI) => {
|
||||||
String.prototype.speak = function () {
|
String.prototype.speak = function () {
|
||||||
const options = JSON.parse(this.valueOf());
|
const options = JSON.parse(this.valueOf());
|
||||||
new Speaker({ ...options, text: options.text }).speak().then(() => {
|
new Speaker({ ...options, text: options.text })
|
||||||
|
.speak()
|
||||||
|
.then(() => {
|
||||||
// Done
|
// Done
|
||||||
}).catch((e) => {
|
})
|
||||||
|
.catch((e) => {
|
||||||
console.log("Error speaking:", e);
|
console.log("Error speaking:", e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.rate = function (speed: number) {
|
String.prototype.rate = function (speed: number) {
|
||||||
return stringObject(this.valueOf(), {rate: speed});
|
return stringObject(this.valueOf(), { rate: speed });
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.pitch = function (pitch: number) {
|
String.prototype.pitch = function (pitch: number) {
|
||||||
return stringObject(this.valueOf(), {pitch: pitch});
|
return stringObject(this.valueOf(), { pitch: pitch });
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.lang = function (language: string) {
|
String.prototype.lang = function (language: string) {
|
||||||
return stringObject(this.valueOf(),{lang: language});
|
return stringObject(this.valueOf(), { lang: language });
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.volume = function (volume: number) {
|
String.prototype.volume = function (volume: number) {
|
||||||
return stringObject(this.valueOf(), {volume: volume});
|
return stringObject(this.valueOf(), { volume: volume });
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.voice = function (voice: number) {
|
String.prototype.voice = function (voice: number) {
|
||||||
return stringObject(this.valueOf(), {voice: voice});
|
return stringObject(this.valueOf(), { voice: voice });
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z = function (options: {[key: string]: any} = {}) {
|
String.prototype.z = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z(this.valueOf(), options);
|
return api.z(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z0 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z0 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z0(this.valueOf(), options);
|
return api.z0(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z1 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z1 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z1(this.valueOf(), options);
|
return api.z1(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z2 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z2 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z2(this.valueOf(), options);
|
return api.z2(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z3 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z3 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z3(this.valueOf(), options);
|
return api.z3(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z4 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z4 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z4(this.valueOf(), options);
|
return api.z4(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z5 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z5 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z5(this.valueOf(), options);
|
return api.z5(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z6 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z6 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z6(this.valueOf(), options);
|
return api.z6(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z7 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z7 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z7(this.valueOf(), options);
|
return api.z7(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z8 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z8 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z8(this.valueOf(), options);
|
return api.z8(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z9 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z9 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z9(this.valueOf(), options);
|
return api.z9(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z10 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z10 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z10(this.valueOf(), options);
|
return api.z10(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z11 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z11 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z11(this.valueOf(), options);
|
return api.z11(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z12 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z12 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z12(this.valueOf(), options);
|
return api.z12(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z13 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z13 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z13(this.valueOf(), options);
|
return api.z13(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z14 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z14 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z14(this.valueOf(), options);
|
return api.z14(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z15 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z15 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z15(this.valueOf(), options);
|
return api.z15(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
String.prototype.z16 = function (options: {[key: string]: any} = {}) {
|
String.prototype.z16 = function (options: { [key: string]: any } = {}) {
|
||||||
return api.z16(this.valueOf(), options);
|
return api.z16(this.valueOf(), options);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -157,7 +160,7 @@ export const makeStringExtensions = (api: UserAPI) => {
|
|||||||
return noteNameToMidi(this.valueOf());
|
return noteNameToMidi(this.valueOf());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
type SpeechOptions = {
|
type SpeechOptions = {
|
||||||
text?: string;
|
text?: string;
|
||||||
@ -166,20 +169,18 @@ type SpeechOptions = {
|
|||||||
volume?: number;
|
volume?: number;
|
||||||
voice?: number;
|
voice?: number;
|
||||||
lang?: string;
|
lang?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
let speakerTimeout: number;
|
let speakerTimeout: number;
|
||||||
|
|
||||||
export class Speaker {
|
export class Speaker {
|
||||||
constructor(
|
constructor(public options: SpeechOptions) {}
|
||||||
public options: SpeechOptions
|
|
||||||
) {}
|
|
||||||
|
|
||||||
speak = () => {
|
speak = () => {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
if (this.options.text) {
|
if (this.options.text) {
|
||||||
const synth = window.speechSynthesis;
|
const synth = window.speechSynthesis;
|
||||||
if(synth.speaking) synth.cancel();
|
if (synth.speaking) synth.cancel();
|
||||||
|
|
||||||
const utterance = new SpeechSynthesisUtterance(this.options.text);
|
const utterance = new SpeechSynthesisUtterance(this.options.text);
|
||||||
utterance.rate = this.options.rate || 1;
|
utterance.rate = this.options.rate || 1;
|
||||||
@ -188,15 +189,17 @@ export class Speaker {
|
|||||||
if (this.options.voice) {
|
if (this.options.voice) {
|
||||||
utterance.voice = synth.getVoices()[this.options.voice];
|
utterance.voice = synth.getVoices()[this.options.voice];
|
||||||
}
|
}
|
||||||
if(this.options.lang) {
|
if (this.options.lang) {
|
||||||
// Check if language has country code
|
// Check if language has country code
|
||||||
if (this.options.lang.length === 2) {
|
if (this.options.lang.length === 2) {
|
||||||
utterance.lang = `${this.options.lang}-${this.options.lang.toUpperCase()}`
|
utterance.lang = `${
|
||||||
|
this.options.lang
|
||||||
|
}-${this.options.lang.toUpperCase()}`;
|
||||||
} else if (this.options.lang.length === 5) {
|
} else if (this.options.lang.length === 5) {
|
||||||
utterance.lang = this.options.lang;
|
utterance.lang = this.options.lang;
|
||||||
} else {
|
} else {
|
||||||
// Fallback to en us
|
// Fallback to en us
|
||||||
utterance.lang = 'en-US';
|
utterance.lang = "en-US";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,11 +211,11 @@ export class Speaker {
|
|||||||
reject(error);
|
reject(error);
|
||||||
};
|
};
|
||||||
|
|
||||||
if(synth.speaking) {
|
if (synth.speaking) {
|
||||||
// Cancel again?
|
// Cancel again?
|
||||||
synth.cancel();
|
synth.cancel();
|
||||||
// Set timeout
|
// Set timeout
|
||||||
if(speakerTimeout) clearTimeout(speakerTimeout);
|
if (speakerTimeout) clearTimeout(speakerTimeout);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
speakerTimeout = setTimeout(() => {
|
speakerTimeout = setTimeout(() => {
|
||||||
synth.speak(utterance);
|
synth.speak(utterance);
|
||||||
@ -220,11 +223,9 @@ export class Speaker {
|
|||||||
} else {
|
} else {
|
||||||
synth.speak(utterance);
|
synth.speak(utterance);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
reject("No text provided");
|
reject("No text provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/main.ts
26
src/main.ts
@ -222,7 +222,7 @@ export class Editor {
|
|||||||
|
|
||||||
updateKnownUniversesView = () => {
|
updateKnownUniversesView = () => {
|
||||||
let itemTemplate = document.getElementById(
|
let itemTemplate = document.getElementById(
|
||||||
"ui-known-universe-item-template"
|
"ui-known-universe-item-template",
|
||||||
) as HTMLTemplateElement;
|
) as HTMLTemplateElement;
|
||||||
if (!itemTemplate) {
|
if (!itemTemplate) {
|
||||||
console.warn("Missing template #ui-known-universe-item-template");
|
console.warn("Missing template #ui-known-universe-item-template");
|
||||||
@ -250,10 +250,10 @@ export class Editor {
|
|||||||
item
|
item
|
||||||
.querySelector(".delete-universe")
|
.querySelector(".delete-universe")
|
||||||
?.addEventListener("click", () =>
|
?.addEventListener("click", () =>
|
||||||
api._deleteUniverseFromInterface(it)
|
api._deleteUniverseFromInterface(it),
|
||||||
);
|
);
|
||||||
return item;
|
return item;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
existing_universes.innerHTML = "";
|
existing_universes.innerHTML = "";
|
||||||
@ -334,7 +334,7 @@ export class Editor {
|
|||||||
|
|
||||||
this.view.dispatch({
|
this.view.dispatch({
|
||||||
effects: this.chosenLanguage.reconfigure(
|
effects: this.chosenLanguage.reconfigure(
|
||||||
this.editor_mode == "notes" ? [markdown()] : [javascript()]
|
this.editor_mode == "notes" ? [markdown()] : [javascript()],
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -343,7 +343,7 @@ export class Editor {
|
|||||||
|
|
||||||
setButtonHighlighting(
|
setButtonHighlighting(
|
||||||
button: "play" | "pause" | "stop" | "clear",
|
button: "play" | "pause" | "stop" | "clear",
|
||||||
highlight: boolean
|
highlight: boolean,
|
||||||
) {
|
) {
|
||||||
document.getElementById("play-label")!.textContent =
|
document.getElementById("play-label")!.textContent =
|
||||||
button !== "pause" ? "Pause" : "Play";
|
button !== "pause" ? "Pause" : "Play";
|
||||||
@ -391,7 +391,7 @@ export class Editor {
|
|||||||
// All other buttons must lose the highlighting
|
// All other buttons must lose the highlighting
|
||||||
document
|
document
|
||||||
.querySelectorAll(
|
.querySelectorAll(
|
||||||
possible_selectors.filter((_, index) => index != selector).join(",")
|
possible_selectors.filter((_, index) => index != selector).join(","),
|
||||||
)
|
)
|
||||||
.forEach((button) => {
|
.forEach((button) => {
|
||||||
button.children[0].classList.remove("animate-pulse");
|
button.children[0].classList.remove("animate-pulse");
|
||||||
@ -437,28 +437,28 @@ export class Editor {
|
|||||||
flashBackground(color: string, duration: number): void {
|
flashBackground(color: string, duration: number): void {
|
||||||
const domElement = this.view.dom;
|
const domElement = this.view.dom;
|
||||||
const gutters = domElement.getElementsByClassName(
|
const gutters = domElement.getElementsByClassName(
|
||||||
"cm-gutter"
|
"cm-gutter",
|
||||||
) as HTMLCollectionOf<HTMLElement>;
|
) as HTMLCollectionOf<HTMLElement>;
|
||||||
|
|
||||||
domElement.classList.add("fluid-bg-transition");
|
domElement.classList.add("fluid-bg-transition");
|
||||||
Array.from(gutters).forEach((gutter) =>
|
Array.from(gutters).forEach((gutter) =>
|
||||||
gutter.classList.add("fluid-bg-transition")
|
gutter.classList.add("fluid-bg-transition"),
|
||||||
);
|
);
|
||||||
|
|
||||||
domElement.style.backgroundColor = color;
|
domElement.style.backgroundColor = color;
|
||||||
Array.from(gutters).forEach(
|
Array.from(gutters).forEach(
|
||||||
(gutter) => (gutter.style.backgroundColor = color)
|
(gutter) => (gutter.style.backgroundColor = color),
|
||||||
);
|
);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
domElement.style.backgroundColor = "";
|
domElement.style.backgroundColor = "";
|
||||||
Array.from(gutters).forEach(
|
Array.from(gutters).forEach(
|
||||||
(gutter) => (gutter.style.backgroundColor = "")
|
(gutter) => (gutter.style.backgroundColor = ""),
|
||||||
);
|
);
|
||||||
|
|
||||||
domElement.classList.remove("fluid-bg-transition");
|
domElement.classList.remove("fluid-bg-transition");
|
||||||
Array.from(gutters).forEach((gutter) =>
|
Array.from(gutters).forEach((gutter) =>
|
||||||
gutter.classList.remove("fluid-bg-transition")
|
gutter.classList.remove("fluid-bg-transition"),
|
||||||
);
|
);
|
||||||
}, duration);
|
}, duration);
|
||||||
}
|
}
|
||||||
@ -466,7 +466,7 @@ export class Editor {
|
|||||||
private initializeElements(): void {
|
private initializeElements(): void {
|
||||||
for (const [key, value] of Object.entries(singleElements)) {
|
for (const [key, value] of Object.entries(singleElements)) {
|
||||||
this.interface[key] = document.getElementById(
|
this.interface[key] = document.getElementById(
|
||||||
value
|
value,
|
||||||
) as ElementMap[keyof ElementMap];
|
) as ElementMap[keyof ElementMap];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -474,7 +474,7 @@ export class Editor {
|
|||||||
private initializeButtonGroups(): void {
|
private initializeButtonGroups(): void {
|
||||||
for (const [key, ids] of Object.entries(buttonGroups)) {
|
for (const [key, ids] of Object.entries(buttonGroups)) {
|
||||||
this.buttonElements[key] = ids.map(
|
this.buttonElements[key] = ids.map(
|
||||||
(id) => document.getElementById(id) as HTMLButtonElement
|
(id) => document.getElementById(id) as HTMLButtonElement,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
::before,
|
::before,
|
||||||
::after {
|
::after {
|
||||||
--tw-content: '';
|
--tw-content: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -44,7 +44,21 @@ html {
|
|||||||
-o-tab-size: 4;
|
-o-tab-size: 4;
|
||||||
tab-size: 4;
|
tab-size: 4;
|
||||||
/* 3 */
|
/* 3 */
|
||||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
font-family:
|
||||||
|
ui-sans-serif,
|
||||||
|
system-ui,
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
"Segoe UI",
|
||||||
|
Roboto,
|
||||||
|
"Helvetica Neue",
|
||||||
|
Arial,
|
||||||
|
"Noto Sans",
|
||||||
|
sans-serif,
|
||||||
|
"Apple Color Emoji",
|
||||||
|
"Segoe UI Emoji",
|
||||||
|
"Segoe UI Symbol",
|
||||||
|
"Noto Color Emoji";
|
||||||
/* 4 */
|
/* 4 */
|
||||||
font-feature-settings: normal;
|
font-feature-settings: normal;
|
||||||
/* 5 */
|
/* 5 */
|
||||||
@ -129,7 +143,8 @@ code,
|
|||||||
kbd,
|
kbd,
|
||||||
samp,
|
samp,
|
||||||
pre {
|
pre {
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||||
|
"Liberation Mono", "Courier New", monospace;
|
||||||
/* 1 */
|
/* 1 */
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
/* 2 */
|
/* 2 */
|
||||||
@ -224,9 +239,9 @@ select {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
button,
|
button,
|
||||||
[type='button'],
|
[type="button"],
|
||||||
[type='reset'],
|
[type="reset"],
|
||||||
[type='submit'] {
|
[type="submit"] {
|
||||||
-webkit-appearance: button;
|
-webkit-appearance: button;
|
||||||
/* 1 */
|
/* 1 */
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
@ -273,7 +288,7 @@ Correct the cursor style of increment and decrement buttons in Safari.
|
|||||||
2. Correct the outline style in Safari.
|
2. Correct the outline style in Safari.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[type='search'] {
|
[type="search"] {
|
||||||
-webkit-appearance: textfield;
|
-webkit-appearance: textfield;
|
||||||
/* 1 */
|
/* 1 */
|
||||||
outline-offset: -2px;
|
outline-offset: -2px;
|
||||||
@ -366,7 +381,8 @@ textarea {
|
|||||||
2. Set the default placeholder color to the user's configured gray 400 color.
|
2. Set the default placeholder color to the user's configured gray 400 color.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
input::-moz-placeholder, textarea::-moz-placeholder {
|
input::-moz-placeholder,
|
||||||
|
textarea::-moz-placeholder {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
/* 1 */
|
/* 1 */
|
||||||
color: #9ca3af;
|
color: #9ca3af;
|
||||||
@ -434,7 +450,9 @@ video {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
*, ::before, ::after {
|
*,
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
--tw-border-spacing-x: 0;
|
--tw-border-spacing-x: 0;
|
||||||
--tw-border-spacing-y: 0;
|
--tw-border-spacing-y: 0;
|
||||||
--tw-translate-x: 0;
|
--tw-translate-x: 0;
|
||||||
@ -538,7 +556,7 @@ video {
|
|||||||
display: block;
|
display: block;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
background: #F0F0F0;
|
background: #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hljs,
|
.hljs,
|
||||||
@ -583,11 +601,11 @@ video {
|
|||||||
.hljs-link,
|
.hljs-link,
|
||||||
.hljs-selector-attr,
|
.hljs-selector-attr,
|
||||||
.hljs-selector-pseudo {
|
.hljs-selector-pseudo {
|
||||||
color: #BC6060;
|
color: #bc6060;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hljs-literal {
|
.hljs-literal {
|
||||||
color: #78A960;
|
color: #78a960;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hljs-built_in,
|
.hljs-built_in,
|
||||||
@ -1333,8 +1351,10 @@ video {
|
|||||||
|
|
||||||
.shadow {
|
.shadow {
|
||||||
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||||
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
|
||||||
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
0 1px 2px -1px var(--tw-shadow-color);
|
||||||
|
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
|
||||||
|
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.outline {
|
.outline {
|
||||||
@ -1350,11 +1370,14 @@ video {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.filter {
|
.filter {
|
||||||
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
|
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast)
|
||||||
|
var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate)
|
||||||
|
var(--tw-sepia) var(--tw-drop-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.transition-colors {
|
.transition-colors {
|
||||||
transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
|
transition-property: color, background-color, border-color,
|
||||||
|
text-decoration-color, fill, stroke;
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
transition-duration: 150ms;
|
transition-duration: 150ms;
|
||||||
}
|
}
|
||||||
@ -1399,15 +1422,21 @@ video {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.focus\:ring-2:focus {
|
.focus\:ring-2:focus {
|
||||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0
|
||||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0
|
||||||
|
calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||||
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
|
||||||
|
var(--tw-shadow, 0 0 #0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
.focus\:ring-4:focus {
|
.focus\:ring-4:focus {
|
||||||
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0
|
||||||
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
||||||
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0
|
||||||
|
calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
||||||
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow),
|
||||||
|
var(--tw-shadow, 0 0 #0000);
|
||||||
}
|
}
|
||||||
|
|
||||||
.focus\:ring-blue-500:focus {
|
.focus\:ring-blue-500:focus {
|
||||||
@ -1431,7 +1460,7 @@ video {
|
|||||||
@media (prefers-reduced-motion: no-preference) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
50% {
|
50% {
|
||||||
opacity: .5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.striped .col-span-3, .striped .col-span-2 {
|
.striped .col-span-3,
|
||||||
@apply bg-neutral-300
|
.striped .col-span-2 {
|
||||||
|
@apply bg-neutral-300;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -113,7 +113,7 @@ export const toposDarkTheme = EditorView.theme(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ dark: true }
|
{ dark: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The highlighting style for code in the Material Dark theme.
|
/// The highlighting style for code in the Material Dark theme.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user