First batch of code cleaning

This commit is contained in:
2024-04-14 13:28:48 +02:00
parent d2dee8f371
commit b222fc25c9
61 changed files with 1172 additions and 2359 deletions

View File

@ -57,7 +57,6 @@ export const singleElements = {
hydra_canvas: "hydra-bg",
feedback: "feedback",
drawings: "drawings",
scope: "scope",
};
export const buttonGroups = {

View File

@ -321,7 +321,7 @@ export const installEditor = (app: Editor) => {
),
editorSetup,
app.themeCompartment.of(
getCodeMirrorTheme(app.getColorScheme("Tomorrow Night Burns")),
getCodeMirrorTheme(app.getColorScheme("Batman")),
// debug
),
app.chosenLanguage.of(javascript()),

View File

@ -1,15 +1,12 @@
import type { Editor } from "./main";
import type { File } from "./FileManagement";
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
const codeReplace = (code: string): string => {
return code.replace(/->|::/g, "&&");
};
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const codeReplace = (code: string): string => code.replace(/->|::/g, "&&");
const cache = new Map<string, Function>();
const MAX_CACHE_SIZE = 40;
const tryCatchWrapper = async (
application: Editor,
code: string,
): Promise<boolean> => {
async function tryCatchWrapper(application: Editor, code: string): Promise<boolean> {
/**
* Wraps the provided code in a try-catch block and executes it.
*
@ -17,21 +14,15 @@ const tryCatchWrapper = async (
* @param code - The code to be executed.
* @returns A promise that resolves to a boolean indicating whether the code executed successfully or not.
*/
try {
await new Function(`"use strict"; ${codeReplace(code)}`).call(
application.api,
);
await new Function(`"use strict"; ${codeReplace(code)}`).call(application.api);
return true;
} catch (error) {
application.interface.error_line.innerHTML = error as string;
application.api._reportError(error as string);
return false;
}
};
const cache = new Map<string, Function>();
const MAX_CACHE_SIZE = 40;
}
const addFunctionToCache = (code: string, fn: Function) => {
/**
@ -45,11 +36,8 @@ const addFunctionToCache = (code: string, fn: Function) => {
cache.set(code, fn);
};
export const tryEvaluate = async (
application: Editor,
code: File,
timeout = 5000,
): Promise<void> => {
export async function tryEvaluate(application: Editor, code: File, timeout = 5000): Promise<void> {
/**
* Tries to evaluate the provided code within a specified timeout period.
* Increments the evaluation count of the code file.
@ -63,39 +51,30 @@ export const tryEvaluate = async (
code.evaluations!++;
const candidateCode = code.candidate;
try {
const cachedFunction = cache.get(candidateCode);
if (cachedFunction) {
cachedFunction.call(application.api);
} else {
return;
}
const wrappedCode = `let i = ${code.evaluations}; ${candidateCode}`;
const isCodeValid = await Promise.race([
tryCatchWrapper(application, wrappedCode),
delay(timeout),
delay(timeout)
]);
if (isCodeValid) {
code.committed = code.candidate;
const newFunction = new Function(
`"use strict"; ${codeReplace(wrappedCode)}`,
);
code.committed = candidateCode;
const newFunction = new Function(`"use strict"; ${codeReplace(wrappedCode)}`);
addFunctionToCache(candidateCode, newFunction);
} else {
application.api.logOnce("Compilation error!");
await evaluate(application, code, timeout);
await delay(100);
await tryEvaluate(application, code, timeout);
}
}
} catch (error) {
application.interface.error_line.innerHTML = error as string;
application.api._reportError(error as string);
}
};
}
export const evaluate = async (
application: Editor,
code: File,
timeout = 1000,
): Promise<void> => {
export async function evaluate(application: Editor, code: File, timeout = 1000): Promise<void> {
/**
* Evaluates the given code using the provided application and timeout.
* @param application The editor application.
@ -103,18 +82,17 @@ export const evaluate = async (
* @param timeout The timeout value in milliseconds (default: 1000).
* @returns A Promise that resolves when the evaluation is complete.
*/
try {
await Promise.race([
tryCatchWrapper(application, code.committed as string),
delay(timeout),
delay(timeout)
]);
if (code.evaluations) code.evaluations++;
code.evaluations!++;
} catch (error) {
application.interface.error_line.innerHTML = error as string;
console.log(error);
console.error(error);
}
};
}
export const evaluateOnce = async (
application: Editor,

View File

@ -1,6 +1,4 @@
// import { tutorial_universe } from "./universes/tutorial";
import { gzipSync, decompressSync, strFromU8 } from "fflate";
// import { examples } from "./examples/excerpts";
import { type Editor } from "./main";
import { uniqueNamesGenerator, colors, animals } from "unique-names-generator";
import { tryEvaluate } from "./Evaluator";

View File

@ -1,7 +1,7 @@
import { type Editor } from "./main";
import { vim } from "@replit/codemirror-vim";
import { tryEvaluate } from "./Evaluator";
import { hideDocumentation, showDocumentation } from "./Documentation";
import { hideDocumentation, showDocumentation } from "./documentation/Documentation";
import { openSettingsModal, openUniverseModal } from "./FileManagement";
export const registerFillKeys = (app: Editor) => {

View File

@ -1,47 +0,0 @@
class TransportProcessor extends AudioWorkletProcessor {
constructor(options) {
super(options);
this.port.addEventListener("message", this.handleMessage);
this.port.start();
this.nudge = 0;
this.started = false;
this.bpm = 120;
this.ppqn = 48;
this.currentPulsePosition = 0;
}
handleMessage = (message) => {
if (message.data && message.data.type === "ping") {
this.port.postMessage(message.data);
} else if (message.data.type === "start") {
this.started = true;
} else if (message.data.type === "pause") {
this.started = false;
} else if (message.data.type === "stop") {
this.started = false;
} else if (message.data.type === "bpm") {
this.bpm = message.data.value;
this.currentPulsePosition = currentTime;
} else if (message.data.type === "ppqn") {
this.ppqn = message.data.value;
this.currentPulsePosition = currentTime;
} else if (message.data.type === "nudge") {
this.nudge = message.data.value;
}
};
process(inputs, outputs, parameters) {
if (this.started) {
const adjustedCurrentTime = currentTime + this.nudge / 100;
const beatNumber = adjustedCurrentTime / (60 / this.bpm);
const currentPulsePosition = Math.ceil(beatNumber * this.ppqn);
if (currentPulsePosition > this.currentPulsePosition) {
this.currentPulsePosition = currentPulsePosition;
this.port.postMessage({ type: "bang", bpm: this.bpm });
}
}
return true;
}
}
registerProcessor("transport", TransportProcessor);

View File

@ -8,7 +8,7 @@ import {
hideDocumentation,
showDocumentation,
updateDocumentationContent,
} from "./Documentation";
} from "./documentation/Documentation";
import {
type Universe,
template_universe,

View File

@ -1,7 +1,6 @@
// @ts-ignore
import { TransportNode } from "./TransportNode";
import TransportProcessor from "./TransportProcessor?worker&url";
import { Editor } from "./main";
import { TransportNode } from "./ClockNode";
import TransportProcessor from "./ClockProcessor?worker&url";
import { Editor } from "../main";
export interface TimePosition {
/**
@ -57,7 +56,7 @@ export class Clock {
this.logicalTime = 0;
this.tick = 0;
this._bpm = 120;
this._ppqn = 48;
this._ppqn = 48 * 2;
this.transportNode = null;
this.ctx = ctx;
this.running = true;
@ -189,6 +188,11 @@ export class Clock {
}
public incrementTick(bpm: number) {
/**
* Increment the clock tick by 1.
* @param bpm - The current beats per minute value
* @returns void
*/
this.tick++;
this.logicalTime += this.pulse_duration_at_bpm(bpm);
}

53
src/clock/ClockNode.d.ts vendored Normal file
View File

@ -0,0 +1,53 @@
export class TransportNode {
constructor(context: AudioContext, something, app: Editor);
/**
* Starts the clock.
*/
start(): void;
/**
* Stops the clock.
*/
stop(): void;
connect(destionation: AudioNode);
setNudge(nudge: number): void;
setPPQN(ppq: number): void;
setBPM(bpm: number): void;
pause(): void;
/**
* Resets the clock to its initial state.
*/
reset(): void;
/**
* Sets the interval at which the clock updates.
* @param interval The interval in milliseconds.
*/
setInterval(interval: number): void;
/**
* Gets the current time of the clock.
* @returns The current time as a number.
*/
getTime(): number;
}
export interface ClockNodeConfig {
/**
* The initial time for the clock.
*/
startTime?: number;
/**
* The interval in milliseconds at which the clock should update.
*/
updateInterval?: number;
}

View File

@ -1,4 +1,4 @@
import { tryEvaluate } from "./Evaluator";
import { tryEvaluate } from "../Evaluator";
const zeroPad = (num, places) => String(num).padStart(places, "0");
export class TransportNode extends AudioWorkletNode {
@ -12,9 +12,9 @@ export class TransportNode extends AudioWorkletNode {
/** @type {(this: MessagePort, ev: MessageEvent<any>) => any} */
handleMessage = (message) => {
if(message.data) {
if (message.data) {
if (message.data.type === "bang") {
if(this.app.clock.running) {
if (this.app.clock.running) {
if (this.app.settings.send_clock) {
this.app.api.MidiConnection.sendMidiClock();
}
@ -60,6 +60,6 @@ export class TransportNode extends AudioWorkletNode {
}
stop() {
this.port.postMessage({type: "stop" });
this.port.postMessage({ type: "stop" });
}
}

View File

@ -0,0 +1,47 @@
class TransportProcessor extends AudioWorkletProcessor {
constructor(options) {
super(options);
this.port.addEventListener("message", this.handleMessage);
this.port.start();
this.nudge = 0;
this.started = false;
this.bpm = 120;
this.ppqn = 48 * 2;
this.currentPulsePosition = 0;
}
handleMessage = (message) => {
if (message.data && message.data.type === "ping") {
this.port.postMessage(message.data);
} else if (message.data.type === "start") {
this.started = true;
} else if (message.data.type === "pause") {
this.started = false;
} else if (message.data.type === "stop") {
this.started = false;
} else if (message.data.type === "bpm") {
this.bpm = message.data.value;
this.currentPulsePosition = currentTime;
} else if (message.data.type === "ppqn") {
this.ppqn = message.data.value;
this.currentPulsePosition = currentTime;
} else if (message.data.type === "nudge") {
this.nudge = message.data.value;
}
};
process(inputs, outputs, parameters) {
if (this.started) {
const adjustedCurrentTime = currentTime + this.nudge / 100;
const beatNumber = adjustedCurrentTime / (60 / this.bpm);
const currentPulsePosition = Math.ceil(beatNumber * this.ppqn);
if (currentPulsePosition > this.currentPulsePosition) {
this.currentPulsePosition = currentPulsePosition;
this.port.postMessage({ type: "bang", bpm: this.bpm });
}
}
return true;
}
}
registerProcessor("transport", TransportProcessor);

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +1,29 @@
import { Editor } from "./main";
// Basics
import { introduction } from "./documentation/basics/welcome";
import { atelier } from "./documentation/basics/atelier";
import { loading_samples } from "./documentation/learning/samples/loading_samples";
import { amplitude } from "./documentation/learning/audio_engine/amplitude";
import { effects } from "./documentation/learning/audio_engine/effects";
import { sampler } from "./documentation/learning/audio_engine/sampler";
import { sample_banks } from "./documentation/learning/samples/sample_banks";
import { audio_basics } from "./documentation/learning/audio_engine/audio_basics";
import { sample_list } from "./documentation/learning/samples/sample_list";
import { software_interface } from "./documentation/basics/interface";
import { shortcuts } from "./documentation/basics/keyboard";
import { code } from "./documentation/basics/code";
import { mouse } from "./documentation/basics/mouse";
// More
import { oscilloscope } from "./documentation/more/oscilloscope";
import { synchronisation } from "./documentation/more/synchronisation";
import { about } from "./documentation/more/about";
import { bonus } from "./documentation/more/bonus";
import { visualization } from "./documentation/more/visualization";
import { chaining } from "./documentation/patterns/chaining";
import { interaction } from "./documentation/basics/interaction";
import { time } from "./documentation/learning/time/time";
import { linear_time } from "./documentation/learning/time/linear_time";
import { cyclical_time } from "./documentation/learning/time/cyclical_time";
import { long_forms } from "./documentation/learning/time/long_forms";
import { midi } from "./documentation/learning/midi";
import { osc } from "./documentation/learning/osc";
import { patterns } from "./documentation/patterns/patterns";
import { functions } from "./documentation/patterns/functions";
import { generators } from "./documentation/patterns/generators";
import { variables } from "./documentation/patterns/variables";
import { probabilities } from "./documentation/patterns/probabilities";
import { lfos } from "./documentation/patterns/lfos";
import { ziffers_basics } from "./documentation/patterns/ziffers/ziffers_basics";
import { ziffers_scales } from "./documentation/patterns/ziffers/ziffers_scales";
import { ziffers_rhythm } from "./documentation/patterns/ziffers/ziffers_rhythm";
import { ziffers_algorithmic } from "./documentation/patterns/ziffers/ziffers_algorithmic";
import { ziffers_tonnetz } from "./documentation/patterns/ziffers/ziffers_tonnetz";
import { ziffers_syncing } from "./documentation/patterns/ziffers/ziffers_syncing";
import { synths } from "./documentation/learning/audio_engine/synths";
import { Editor } from "../main";
import { introduction, atelier, software_interface, shortcuts, code, mouse, interaction } from "./basics";
import { amplitude, effects, sampler, synths, filters, audio_basics } from "./learning/audio_engine";
import { lfos, functions, generators, variables, probabilities } from './patterns';
import { ziffers_basics, ziffers_scales, ziffers_rhythm, ziffers_algorithmic, ziffers_tonnetz, ziffers_syncing } from "./patterns/ziffers";
import { loading_samples } from "./learning/samples/loading_samples";
import { sample_banks } from "./learning/samples/sample_banks";
import { sample_list } from "./learning/samples/sample_list";
import { oscilloscope } from "./more/oscilloscope";
import { synchronisation } from "./more/synchronisation";
import { about } from "./more/about";
import { bonus } from "./more/bonus";
import { visualization } from "./more/visualization";
import { chaining } from "./patterns/chaining";
import { time } from "./learning/time/time";
import { linear_time } from "./learning/time/linear_time";
import { cyclical_time } from "./learning/time/cyclical_time";
import { long_forms } from "./learning/time/long_forms";
import { midi } from "./learning/midi";
import { osc } from "./learning/osc";
import { patterns } from "./patterns/patterns";
// Setting up the Markdown converter with syntax highlighting
import showdown from "showdown";
import showdownHighlight from "showdown-highlight";
import "highlight.js/styles/atom-one-dark-reasonable.min.css";
import { createDocumentationStyle } from "./DomElements";
import { filters } from "./documentation/learning/audio_engine/filters";
import { createDocumentationStyle } from "../DomElements";
showdown.setFlavor("github");
type StyleBinding = {

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const atelier = (application: Editor): string => {
const makeExample = makeExampleFactory(application);

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory, key_shortcut } from "../../Documentation";
import { makeExampleFactory, key_shortcut } from "../Documentation";
export const code = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -43,7 +43,7 @@ beat(1) :: snd('bd').out()
`,
true,
)}
)}
${makeExample(
"More complex conditions using ?",
@ -53,7 +53,7 @@ beat(4) ? snd('kick').out() : beat(2) :: snd('snare').out()
// (true) ? log('very true') : log('very false')
`,
false,
)}
)}
${makeExample(
@ -64,7 +64,7 @@ beat(4) ? snd('kick').out() : beat(2) :: snd('snare').out()
!beat(2) :: beat(0.5) :: snd('clap').out()
`,
false,
)}
)}
# About crashes and bugs
@ -77,7 +77,7 @@ ${makeExample(
qjldfqsdklqsjdlkqjsdlqkjdlksjd
`,
true,
)}
)}
`;
};

View File

@ -0,0 +1,7 @@
export { introduction } from './welcome';
export { atelier } from './atelier';
export { software_interface } from './interface';
export { shortcuts } from './keyboard';
export { code } from './code';
export { mouse } from './mouse';
export { interaction } from './interaction';

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
// @ts-ignore
export const interaction = (application: Editor): string => {

View File

@ -1,4 +1,4 @@
import { key_shortcut, makeExampleFactory } from "../../Documentation";
import { key_shortcut, makeExampleFactory } from "../Documentation";
import { type Editor } from "../../main";
import topos_arch from "./topos_arch.svg";
import many_universes from "./many_universes.svg";
@ -44,7 +44,7 @@ beat(1) :: script(1) // Calling local script n°1
flip(4) :: beat(.5) :: script(2) // Calling script n°2
`,
true,
)}
)}
${makeExample(
"Script execution can become musical too!",
@ -55,7 +55,7 @@ flip(4) :: beat([.5, .25].beat(16)) :: script(
[5, 6, 7, 8].beat())
`,
false,
)}
)}
### Navigating the interface

View File

@ -1,6 +1,6 @@
import { key_shortcut } from "../../Documentation";
import { key_shortcut } from "../Documentation";
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const shortcuts = (app: Editor): string => {
let makeExample = makeExampleFactory(app);
@ -74,7 +74,7 @@ ${makeExample(
beat(fill() ? 1/4 : 1/2)::sound('cp').out()
`,
true,
)}
)}
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const mouse = (app: Editor): string => {
let makeExample = makeExampleFactory(app);
@ -28,7 +28,7 @@ beat(.25) :: sound('sine')
.room(0.35).size(4).out()
`,
true,
)}
)}
<br>
@ -47,7 +47,7 @@ beat(.25) :: sound('sine')
.note(noteX())
.room(0.35).size(4).out()`,
true,
)}
)}
## Mouse and Arrays
@ -65,7 +65,7 @@ log([4,5,6,7].mouseY())
`,
true,
)}
)}

View File

@ -1,6 +1,6 @@
import { makeExampleFactory, key_shortcut } from "../../Documentation";
import { makeExampleFactory, key_shortcut } from "../Documentation";
import { type Editor } from "../../main";
import { examples } from "../../examples/excerpts";
import { examples } from "../excerpts";
export const introduction = (application: Editor): string => {
const makeExample = makeExampleFactory(application);

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const amplitude = (application: Editor): string => {
// @ts-ignore
@ -21,7 +21,7 @@ ${makeExample(
`
beat(.5)::snd('cp').vel($(1)%10 / 10).out()`,
true,
)}
)}
## Amplitude Enveloppe
@ -52,7 +52,7 @@ beat(.25)::sound('sawtooth')
.smooth().out();
`,
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:
@ -72,7 +72,7 @@ beat(.25)::sound('sawtooth')
.smooth().out();
`,
true,
)};
)};
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const audio_basics = (application: Editor): string => {
// @ts-ignore
@ -23,7 +23,7 @@ beat(1) && sound('bd').out()
beat(0.5) && sound('hh').out()
`,
true,
)}
)}
These commands, in plain english, can be translated to:
@ -39,7 +39,7 @@ 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();
`,
true,
)}
)}
Now, it translates as follows:
@ -60,7 +60,7 @@ ${makeExample(
beat(1)::sound('cp').out(2)
`,
true,
)}
)}
Try to remove <ic>.out</ic>. You will see that no sound is playing at all!
@ -78,7 +78,7 @@ beat(1) :: sound('pad').n(1)
.velocity(0.25)
.pan(usine()).release(2).out()`,
true,
)}
)}
## Picking a specific sound
@ -107,7 +107,7 @@ ${makeExample(
beat(1) && sound('kick').n([1,2,3,4,5,6,7,8].pick()).out()
`,
true,
)}
)}
You can also use the <ic>:</ic> to pick a sample number directly from the <ic>sound</ic> function:
@ -127,7 +127,7 @@ ${makeExample(
// Move your mouse to change the sample being used!
beat(.25) && sound('ST09').n(Math.floor(mouseX())).out()`,
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.
@ -160,7 +160,7 @@ beat(1)::sound('hh').out()
beat(2)::sound('cp').orbit(2).room(0.5).size(8).out()
`,
true,
)}
)}
## The art of chaining
@ -175,7 +175,7 @@ beat(0.25) && sound('fhh')
.cutoff(usine(1/2) * 5000)
.out()`,
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.
`;

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const distortion = (application: Editor): string => {
// @ts-ignore
@ -24,7 +24,7 @@ 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()
`,
true,
)};
)};
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const effects = (application: Editor): string => {
// @ts-ignore
@ -29,7 +29,7 @@ ${makeExample(
beat(2)::snd('cp').room(0.5).size(4).out()
`,
true,
)};
)};
## Delay
@ -48,7 +48,7 @@ beat(2)::snd('cp').delay(0.5).delaytime(0.75).delayfb(0.8).out()
beat(4)::snd('snare').out()
beat(1)::snd('kick').out()`,
true,
)}
)}
## Phaser
@ -69,7 +69,7 @@ rhythm(.5, 7, 8)::sound('wt_stereo')
.room(0.5).size(4).out()
`,
true,
)}
)}
## Distorsion, saturation, destruction
@ -89,7 +89,7 @@ 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()
`,
true,
)};
)};
## Vibrato
@ -105,7 +105,7 @@ beat(1) :: sound('triangle')
.vibmod([1,2,4,8].beat(2))
.out()`,
true,
)}
)}
## Compression

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const filters = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -16,7 +16,7 @@ ${makeExample(
"Filtering the high frequencies of an oscillator",
`beat(.5) :: sound('sawtooth').cutoff(50 + usine(1/8) * 2000).out()`,
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:
@ -32,7 +32,7 @@ ${makeExample(
"Filtering a bass",
`beat(.5) :: sound('jvbass').lpf([250,1000,8000].beat()).out()`,
true,
)}
)}
### Highpass filter
@ -45,7 +45,7 @@ ${makeExample(
"Filtering a noise source",
`beat(.5) :: sound('gtr').hpf([250,1000, 2000, 3000, 4000].beat()).end(0.5).out()`,
true,
)}
)}
### Bandpass filter
@ -58,7 +58,7 @@ ${makeExample(
"Sweeping the filter on the same guitar sample",
`beat(.5) :: sound('gtr').bandf(100 + usine(1/8) * 4000).end(0.5).out()`,
true,
)}
)}
Alternatively, <ic>lpf</ic>, <ic>hpf</ic> and <ic>bpf</ic> can take a second argument, the **resonance**.
@ -72,7 +72,7 @@ ${makeExample(
"Filtering a bass",
`beat(.5) :: sound('jvbass').ftype(['12db', '24db'].beat(4)).lpf([250,1000,8000].beat()).out()`,
true,
)}
)}
## Filter envelopes
@ -96,7 +96,7 @@ ${makeExample(
.cutoff(5000).lpa([0.05, 0.25, 0.5].beat(2))
.lpenv(-8).lpq(10).out()`,
true,
)}
)}
### Highpass envelope
@ -116,7 +116,7 @@ ${makeExample(
.hcutoff(1000).hpa([0.05, 0.25, 0.5].beat(2))
.hpenv(8).hpq(10).out()`,
true,
)}
)}
### Bandpass envelope
@ -138,7 +138,7 @@ ${makeExample(
.bpenv(-4).release(2).out()
`,
true,
)}
)}
`;

View File

@ -0,0 +1,6 @@
export { amplitude } from './amplitude';
export { effects } from './effects';
export { sampler } from './sampler';
export { synths } from './synths';
export { filters } from './filters';
export { audio_basics } from './audio_basics';

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const sampler = (application: Editor): string => {
// @ts-ignore
@ -39,7 +39,7 @@ beat(.5)::snd('pad').begin(0.2)
.clip(1).out()
`,
true,
)};
)};
## Playback speed / pitching samples
@ -53,7 +53,7 @@ beat(0.5)::sound('notes')
.speed([1,2,3,4].palindrome().beat(0.5)).out()
`,
true,
)}
)}
It also works by using negative values. It reverses the playback:
@ -64,7 +64,7 @@ beat(0.5)::sound('notes')
.speed(-[1,2,3,4].palindrome().beat(0.5)).out()
`,
true,
)}
)}
Of course you can play melodies using samples:
@ -77,7 +77,7 @@ beat(0.5)::sound('notes')
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.5)).out()
`,
true,
)}
)}
## Panning
@ -92,7 +92,7 @@ beat(0.25)::sound('notes')
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
`,
true,
)}
)}
## Looping over a sample
@ -110,7 +110,7 @@ beat(0.25)::sound('fikea').loop(1)
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
`,
true,
)}
)}
## Stretching a sample
@ -123,7 +123,7 @@ ${makeExample(
beat(4) :: sound('amen1').n(11).stretch(4).out()
beat(1) :: sound('kick').shape(0.35).out()`,
true,
)};
)};
## Cutting samples
@ -140,7 +140,7 @@ beat(0.25)::sound('notes')
.note([0, 2, 3, 4, 5].scale('minor', 50).beat(0.25)).out()
`,
true,
)}
)}
You can also use <ic>clip</ic> to cut the sample everytime a new sample comes in:
@ -155,7 +155,7 @@ beat(0.125)::sound('notes')
+ [-12,12].beat()).out()
`,
true,
)}
)}
## Adding vibrato to samples
@ -168,7 +168,7 @@ ${makeExample(
beat(1)::sound('fhang').vib([1, 2, 4].bar()).vibmod([0.5, 2].beat()).out()
`,
true,
)}
)}
`;

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const synths = (application: Editor): string => {
const makeExample = makeExampleFactory(application);

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory, key_shortcut } from "../../Documentation";
import { makeExampleFactory, key_shortcut } from "../Documentation";
export const midi = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -27,7 +27,7 @@ ${makeExample(
midi_outputs()
`,
true,
)}
)}
- <ic>midi_output(output_name: string)</ic>: enter your desired output to connect to it.
@ -37,7 +37,7 @@ ${makeExample(
midi_output("MIDI Rocket-Trumpet")
`,
true,
)}
)}
That's it! You are now ready to play with MIDI.
@ -55,7 +55,7 @@ ${makeExample(
rhythm(.5, 5, 8) :: midi(50).out()
`,
true,
)}
)}
${makeExample(
"MIDI note using three parameters: note, velocity, channel",
@ -64,7 +64,7 @@ ${makeExample(
rhythm(.5, 5, 8) :: midi(50, 50 + usine(.5) * 20, 0).out()
`,
false,
)}
)}
${makeExample(
"MIDI note by passing an object",
@ -73,7 +73,7 @@ ${makeExample(
rhythm(.5, 5, 8) :: midi({note: 50, velocity: 50 + usine(.5) * 20, channel: 0}).out()
`,
false,
)}
)}
We can now have some fun and starting playing a small piano piece:
@ -87,7 +87,7 @@ beat(.75) && midi([64, 67, 69].beat()).sustain(0.05).out()
beat(.25) && midi([64, 67, 69].beat() + 24).sustain(0.05).out()
`,
true,
)}
)}
## Control and Program Changes
@ -100,7 +100,7 @@ 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})
`,
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>).
@ -110,7 +110,7 @@ ${makeExample(
program_change([1,2,3,4,5,6,7,8].pick(), 1)
`,
true,
)}
)}
## System Exclusive Messages
@ -124,7 +124,7 @@ ${makeExample(
sysex(0x90, 0x40, 0x7f)
`,
true,
)}
)}
## Clock
@ -136,7 +136,7 @@ ${makeExample(
beat(.25) && midi_clock() // Sending clock to MIDI device from the global buffer
`,
true,
)}
)}
## Using midi with ziffers
@ -148,7 +148,7 @@ ${makeExample(
z1('0 2 e 5 2 q 4 2').midi().port(2).channel(4).out()
`,
true,
)}
)}
${makeExample(
"Setting the channel within the pattern",
@ -156,7 +156,7 @@ ${makeExample(
z1('(0 2 e 5 2):0 (4 2):1').midi().out()
`,
true,
)}
)}
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const osc = (application: Editor): string => {
// @ts-ignore
@ -31,7 +31,7 @@ beat(1)::getOsc()
// 1 : {data: Array(2), address: '/lala'}
// 2 : {data: Array(2), address: '/lala'}`,
true,
)}
)}
### Filtered messages
@ -46,7 +46,7 @@ beat(1)::getOsc("/lala")
// 2 : (2) [82, 'bob']
`,
true,
)}
)}
## Output
@ -58,7 +58,7 @@ ${makeExample(
beat(1)::sound('cp').speed(2).vel(0.5).osc()
`,
true,
)}
)}
This is a simple **OSC** message that will inherit all the properties of the sound. You can also send customized OSC messages using the <ic>osc()</ic> function:
@ -69,7 +69,7 @@ ${makeExample(
osc('/my/osc/address', 5000, 1, 2, 3)
`,
true,
)}
)}
`;
};

View File

@ -0,0 +1,3 @@
export { loading_samples } from './loading_samples';
export { sample_banks } from './sample_banks';
export { sample_list } from './sample_list';

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const loading_samples = (application: Editor): string => {
// @ts-ignore
@ -19,7 +19,7 @@ ${makeExample(
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');`,
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:
@ -28,7 +28,7 @@ ${makeExample(
`rhythm(.5, 5, 8)::sound('bd').n(ir(1,2)).end(1).out()
`,
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:
@ -41,7 +41,7 @@ samples("github:Bubobubobubobubo/Dough-Samples/main");
samples("github:Bubobubobubobubo/Dough-Amiga/main");
`,
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!
@ -59,7 +59,7 @@ samples("shabda:ocean")
beat(1)::sound('ocean').clip(1).out()
`,
true,
)}
)}
You can also use the <ic>.n</ic> attribute like usual to load a different sample.
`;

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const sample_banks = (application: Editor): string => {
// @ts-ignore

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const samples_to_markdown = (
application: Editor,

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const cyclical_time = (app: Editor): string => {
// @ts-ignore
@ -22,7 +22,7 @@ ${makeExample(
beat([1,1/2,1/4,1/8].beat(2)) :: sound('hat').n(0).out()
`,
true,
)}
)}
${makeExample(
"Some sort of ringtone",
@ -42,7 +42,7 @@ 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();
`,
false,
)}
)}
${makeExample(
"Beat can match multiple values",
@ -50,7 +50,7 @@ ${makeExample(
beat([.5, 1.25])::sound('hat').out()
`,
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>number</ic>: if <ic>number = 1</ic>, the function will return <ic>true</ic> every pulse. Lists can be used too.
@ -64,7 +64,7 @@ pulse([24, 16])::sound('hat').ad(0, .02).out()
pulse([48, [36,24].dur(4, 1)])::sound('fhardkick').ad(0, .1).out()
`,
true,
)}
)}
${makeExample(
"pulse is the OG rhythmic function in Topos",
`
@ -72,7 +72,7 @@ pulse([48, 24, 16].beat(4)) :: sound('linnhats').out()
beat(1)::snd(['bd', '808oh'].beat(1)).out()
`,
false,
)}
)}
- <ic>bar(n: number | number[] = 1, offset: number = 1)</ic>: return true every _n_ bars.
@ -86,7 +86,7 @@ bar(1)::sound('kick').out()
beat(1)::sound('hat').speed(2).out()
`,
true,
)}
)}
${makeExample(
@ -98,7 +98,7 @@ beat(1, 0.5)::sound('hat').speed(4).out()
bar(1, 0.5)::sound('sn').out()
`,
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.
@ -110,7 +110,7 @@ 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
`,
true,
)}
)}
## XOX Style sequencers
@ -126,7 +126,7 @@ seq('ooxo')::sound('fsoftsnare').out()
seq('xoxo', 0.25)::sound('fhh').out()
`,
true,
)}
)}
${makeExample(
"Another sequence using more complex parameters",
@ -136,7 +136,7 @@ seq('ooxo', [1, 2].bar())::sound('fsoftsnare').speed(0.5).out()
seq(['xoxoxoxx', 'xxoo'].bar())::sound('fhh').out()
`,
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>expr: string</ic>: any string composed of <ic>x</ic> or <ic>o</ic> like so: <ic>"xooxoxxoxoo"</ic>.
@ -160,7 +160,7 @@ function complexPat() {
fullseq('xooxooxx', 4) ? simplePat() : complexPat()
`,
true,
)}
)}
@ -182,7 +182,7 @@ rhythm(.5, 7, 8)::sound('sine')
rhythm(.5, 3, 8)::sound('sine').freq(500).ad(0, .5).out()
`,
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.
@ -195,7 +195,7 @@ ${makeExample(
oneuclid(7,16) :: snd('east').end(0.5).n(irand(3,5)).out()
`,
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>binrhythm(divisor: number, n: number): boolean: boolean</ic>: iterator-less version of the binary rhythm generator.
@ -208,7 +208,7 @@ 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()
`,
true,
)}
)}
${makeExample(
"binrhythm for fast cool binary rhythms!",
@ -222,7 +222,7 @@ binrhythm([.5, .25].beat(1), 30) && snd('wt_granular').n(a)
.room(1).size(1).out()
`,
true,
)}
)}
${makeExample(
"Submarine jungle music",
@ -234,7 +234,7 @@ beat(.5) && bin($(1), 911) && snd('ST69').n([2,3,4].beat())
beat(.5) && sound('amencutup').n(irand(2,7)).shape(0.3).out()
`,
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.
@ -248,7 +248,7 @@ prob(60)::beat(.5) && euclid($(2), 3, 8) && snd('mash')
prob(80)::beat(.5) && sound(['hh', 'hat'].pick()).out()
`,
true,
)}
)}

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
import pulses from "./pulses.svg";
export const linear_time = (app: Editor): string => {
@ -27,7 +27,7 @@ ${makeExample(
log(\`\$\{cbar()}\, \$\{cbeat()\}, \$\{cpulse()\}\`)
`,
true,
)}
)}
### BPM and PPQN
@ -84,7 +84,7 @@ if((cbar() % 4) > 1) {
beat([.5, .5, 1, .25].beat(0.5)) :: sound('shaker').out()
`,
true,
)}
)}
## Time Warping
@ -109,7 +109,7 @@ flip(3) :: beat([.25,.5].beat(.5)) :: sound('dr')
beat(.25) :: warp([12, 48, 24, 1, 120, 30].pick())
`,
true,
)}
)}
- <ic>beat_warp(beat: number)</ic>: this function jumps to the _n_ beat of the clock. The first beat is <ic>1</ic>.
@ -131,7 +131,7 @@ beat(.5) :: snd('arpy').note(
beat(1) :: beat_warp([2,4,5,10,11].pick())
`,
true,
)}
)}
## Transport-based rhythm generators
@ -145,7 +145,7 @@ 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
`,
true,
)}
)}
${makeExample(
"Let's do something more complex",
@ -157,7 +157,7 @@ beat([.25, 1/8].beat(1.5))::snd('hat').n(2)
.pan(usine()).out()
`,
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.
@ -172,7 +172,7 @@ 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()
`,
true,
)}
)}
${makeExample(
"Using oncount to create rhythms with a custom meter",
@ -184,7 +184,7 @@ 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()
`,
true,
)}
)}

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const long_forms = (app: Editor): string => {
// @ts-ignore
@ -20,7 +20,7 @@ ${makeExample(
script([1,2,3,4].bar(8))
`,
true,
)}
)}
You can also give a specific duration to each section using <ic>.dur</ic>:
@ -30,7 +30,7 @@ ${makeExample(
script([1,2,3,4].dur(8, 2, 16, 4))
`,
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_.
@ -45,7 +45,7 @@ ${makeExample(
flip(4) :: beat(1) :: snd('kick').out()
`,
true,
)}
)}
${makeExample(
"Clapping on the edge",
@ -57,7 +57,7 @@ flip(2.5) :: beat(.5) :: snd('bd').out()
beat(.25) :: sound('hat').end(0.1).cutoff(1200).pan(usine(1/4)).out()
`,
false,
)}
)}
${makeExample(
"Good old true and false",
@ -69,7 +69,7 @@ if (flip(4, 75)) {
}
`,
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.
@ -94,7 +94,7 @@ if (flip(8)) {
}
`,
true,
)}
)}
You can use it everywhere to spice things up, including as a method parameter picker:
@ -104,7 +104,7 @@ ${makeExample(
beat(.5)::snd(flip(2) ? 'kick' : 'hat').out()
`,
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.
@ -123,7 +123,7 @@ flipbar(2) && a()
flipbar(3) && b()
`,
true,
)}
)}
${makeExample(
"Alternating over four bars",
`
@ -132,7 +132,7 @@ flipbar(2)
: beat(.5) && snd(['east', 'east:2'].beat(1)).out()
`,
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.
@ -156,7 +156,7 @@ if (onbar([1,2], 4)) {
rhythm(.5, 2, 7) :: snd('snare').n(3).out();
}`,
true,
)}
)}
`;
};

View File

@ -1,4 +1,4 @@
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
import { type Editor } from "../../../main";
import times from "./times.svg";

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { key_shortcut, makeExampleFactory } from "../../Documentation";
import { key_shortcut, makeExampleFactory } from "../Documentation";
export const bonus = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -39,7 +39,7 @@ ${makeExample(
"Hydra integration",
`beat(4) :: hydra.osc(3, 0.5, 2).out()`,
true,
)}
)}
Close the documentation to see the effect: ${key_shortcut(
"Ctrl+D",
@ -56,7 +56,7 @@ beat(4) :: stop_hydra() // this one
beat(4) :: hydra.hush() // or this one
`,
true,
)}
)}
### Changing the resolution
@ -67,7 +67,7 @@ ${makeExample(
"Changing Hydra resolution",
`hydra.setResolution(1024, 768)`,
true,
)}
)}
### Documentation
@ -101,6 +101,6 @@ beat(0.25)::gif({
posY: ir(1, 800), // CSS Vertical Position
`,
true,
)}
)}
`;
};

View File

@ -0,0 +1,5 @@
export { about } from './about';
export { bonus } from './bonus';
export { oscilloscope } from './oscilloscope';
export { synchronisation } from './synchronisation';
export { visualization } from './visualization.ts';

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const oscilloscope = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -15,7 +15,7 @@ ${makeExample(
beat(1)::sound('sine').freq(200).ad(0, .2).scope().out()
`,
true,
)}
)}
Here is a layout of the scope configuration options:
@ -36,7 +36,7 @@ scope({
})
`,
true,
)}
)}
${makeExample(
"Demo with multiple scope mode",
@ -57,7 +57,7 @@ scope({enabled: true, thickness: 8,
size: 0.5, fftSize: 2048})
`,
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 :)

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const synchronisation = (app: Editor): string => {
// @ts-ignore

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { key_shortcut, makeExampleFactory } from "../../Documentation";
import { key_shortcut, makeExampleFactory } from "../Documentation";
export const visualization = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -21,7 +21,7 @@ ${makeExample(
"Hydra integration",
`beat(4) :: hydra.osc(3, 0.5, 2).out()`,
false,
)}
)}
Close the documentation to see the effect: ${key_shortcut(
"Ctrl+D",
@ -38,7 +38,7 @@ beat(4) :: stop_hydra() // this one
beat(4) :: hydra.hush() // or this one
`,
false,
)}
)}
### Changing the resolution
@ -48,7 +48,7 @@ ${makeExample(
"Changing Hydra resolution",
`hydra.setResolution(1024, 768)`,
false,
)}
)}
### Hydra documentation
@ -82,7 +82,7 @@ beat(0.25)::gif({
posY: ir(1, 800), // CSS Vertical Position
`,
false,
)}
)}
## Canvas live coding
@ -118,29 +118,29 @@ beat(0.5) && clear() && draw(context => {
})
`,
false,
)}
)}
${makeExample(
"Using draw with events and shapes",
`
"Using draw with events and shapes",
`
beat(0.25) && sound("bass1:5").pitch(rI(1,6)).draw(x => {
donut(x.pitch)
}).out()
`,
false,
)}
false,
)}
${makeExample(
"Using draw with ziffers and shapes",
`
"Using draw with ziffers and shapes",
`
z1("1/8 (0 2 1 4)+(2 1)").sound("sine").ad(0.05,.25).clear()
.draw(x => {
pie({slices:7,eaten:(7-x.pitch-1),fillStyle:"green", rotate: 250})
}).log("pitch").out()
`,
false,
)}
false,
)}
* <ic<image(url, x, y, width, height, rotation)</ic> - Draws an image to a canvas.
@ -166,22 +166,22 @@ Text can be drawn to canvas using the <ic>drawText()</ic> function. The function
* <ic>drawText(text, fontSize, rotation, font, x, y)</ic> - Draws text to a canvas.
${makeExample(
"Writing to canvas",
`
"Writing to canvas",
`
beat(0.5) && clear() && drawText("Hello world!", 100, 0, "Arial", 100, 100)
`,
false,
)}
false,
)}
* <ic>randomChar(number, min, max)</ic> - Returns a number of random characters from given unicode range.
${makeExample(
"Drawing random characters to canvas",
`
"Drawing random characters to canvas",
`
beat(0.5) && clear() && drawText(randomChar(10,1000,2000),30)
`,
false,
)}
false,
)}
* <ic>emoji(size)</ic> - Returns a random emojis as text.

View File

@ -1,4 +1,4 @@
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
import { type Editor } from "../../main";
export const chaining = (application: Editor): string => {
@ -14,7 +14,7 @@ ${makeExample(
beat(1)::sound('bd').speed(2).lpf(500).out()
`,
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!
@ -32,7 +32,7 @@ register('juxrev', n=>n.pan([0, 1]).speed([1, -1]))
beat(1)::sound('fhh').juxrev().out()
`,
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:
@ -52,15 +52,15 @@ rhythm(.25, [6, 8].beat(), 12)::sound('sine')
.note([0, 2, 4, 5].scale('minor', 50).beat(0.5))
.sub(8).out()`,
true,
)}
)}
## Registering chain for all events
The chain can also be registered automatically for all events. This is useful if you want to add a specific effect to all your events.
${makeExample(
"Registering chain to all events at once",
`
"Registering chain to all events at once",
`
z0("h 9 ^ <7 5 3 1>")
.sound("sine")
.out()
@ -71,8 +71,8 @@ z1("0 4 3 2")
all(x=>x.room(1).delay(rI(0,0.5)))
`,
true,
)}
true,
)}
## Logging values from the chain
@ -84,7 +84,7 @@ ${makeExample(
beat(1) :: sound("sine").pitch(rI(1,6)).log("note").out()
`,
true,
)}
)}
${makeExample(
"Logging values from ziffers pattern",
@ -92,7 +92,7 @@ ${makeExample(
z1("0 3 2 5").scale("rocritonic").sound("sine").log("pitch","note","key").out()
`,
true,
)}
)}
## Conditional chaining
@ -108,7 +108,7 @@ beat(.5) && sound('fhh')
.rarely(s => s.room(0.5).size(8).speed(0.5))
.out()`,
true,
)}
)}
${makeExample(
"Chance to play a random note",
`
@ -120,7 +120,7 @@ beat(.5) && sound('pluck').note(60)
.room(0.5).size(3)
.out()`,
false,
)}
)}
There is a growing collection of probability and chance methods you can use:
@ -152,7 +152,7 @@ ${makeExample(
.sometimes(s => s.velocity(irand(50,100)))
.out()`,
true,
)};
)};
## Ziffers
@ -175,6 +175,6 @@ z1('s 0 5 7 0 3 7 0 2 7 0 1 7 0 1 6 5 4 3 2')
.room(0.5).size(0.5).out()
`,
true,
)};
)};
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const functions = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -20,7 +20,7 @@ ${makeExample(
beat(1) :: script(1)
`,
true,
)}
)}
${makeExample(
"Calling mutliple scripts at the same time.",
@ -28,7 +28,7 @@ ${makeExample(
beat(1) :: script(1, 3, 5)
`,
false,
)}
)}
## Math functions
@ -52,7 +52,7 @@ ${makeExample(
"Scaling an LFO",
`usine(1/2).linlin(0, 1, 0, 100)`,
true,
)}
)}
@ -68,7 +68,7 @@ beat(.5) :: delay(usine(.125) * 80, () => sound('east').out())
beat(.5) :: delay(50, () => sound('east').out())
`,
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.
@ -79,6 +79,6 @@ 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())
`,
true,
)};
)};
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const generators = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -15,8 +15,8 @@ Once the generator is cached the values will be returned from the named cache ev
The resulted values can be played using either <ic>pitch()</ic> or <ic>freq()</ic> or as Ziffers patterns. When playing the values using <ic>pitch()</ic> different scales and chained methods can be used to alter the result, for example <ic>mod(value: number)</ic> to limit the integer range or <ic>scale(name: string)</ic> etc. to change the resulting note.
${makeExample(
"Simple looping generator function",
`
"Simple looping generator function",
`
function* simple() {
let x = 0;
while (x < 12) {
@ -27,12 +27,12 @@ function* simple() {
beat(.25) && sound("triangle").pitch(cache("simple",simple())).scale("minor").out()
`,
true,
)};
true,
)};
${makeExample(
"Infinite frequency generator",
`
"Infinite frequency generator",
`
function* poly(x=0) {
while (true) {
const s = Math.tan(x/10)+Math.sin(x/20);
@ -43,8 +43,8 @@ ${makeExample(
beat(.125) && sound("triangle").freq(cache("mathyshit",poly())).out()
`,
true,
)};
true,
)};
When you want to dance with a dynamical system in controlled musical chaos, Topos is waiting for you:
@ -72,7 +72,7 @@ ${makeExample(
.log("freq").out()
`,
true,
)};
)};
${makeExample(
"Henon and his discrete music",
@ -123,7 +123,7 @@ ${makeExample(
.log("freq").out()
`,
true,
)};
)};
## OEIS integer sequences
@ -153,7 +153,7 @@ Alternatively generators can be used with Ziffers to generate longer patterns. I
${makeExample(
"Ziffers patterns using a generator functions",
`
`
function* poly(x) {
while (true) {
yield 64 * Math.pow(x, 6) - 480 * Math.pow(x, 4) + 720 * Math.pow(x, 2);

View File

@ -0,0 +1,7 @@
export { chaining } from './chaining';
export { functions } from './functions';
export { generators } from './generators';
export { lfos } from './lfos';
export { patterns } from './patterns';
export { probabilities } from './probabilities';
export { variables } from './variables';

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const lfos = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -17,7 +17,7 @@ ${makeExample(
"Modulating the speed of a sample player using a sine LFO",
`beat(.25) && snd('cp').speed(1 + usine(0.25) * 2).out()`,
true,
)};
)};
- <ic>triangle(freq: number = 1, phase: number = 0): number</ic>: returns a triangle oscillation between <ic>-1</ic> and <ic>1</ic>.
- <ic>utriangle(freq: number = 1, phase: number = 0): number</ic>: returns a triangle oscillation between <ic>0</ic> and <ic>1</ic>. The <ic>u</ic> stands for _unipolar_.
@ -26,7 +26,7 @@ ${makeExample(
"Modulating the speed of a sample player using a triangle LFO",
`beat(.25) && snd('cp').speed(1 + utriangle(0.25) * 2).out()`,
true,
)}
)}
- <ic>saw(freq: number = 1, phase: number = 0): number</ic>: returns a sawtooth-like oscillation between <ic>-1</ic> and <ic>1</ic>.
- <ic>usaw(freq: number = 1, phase: 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,7 +35,7 @@ ${makeExample(
"Modulating the speed of a sample player using a saw LFO",
`beat(.25) && snd('cp').speed(1 + usaw(0.25) * 2).out()`,
true,
)}
)}
- <ic>square(freq: number = 1, 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, 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,7 +44,7 @@ ${makeExample(
"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()`,
true,
)};
)};
- <ic>noise(times: number = 1)</ic>: returns a random value between -1 and 1.
- <ic>unoise(times: number = 1)</ic>: returns a random value between 0 and 1.
@ -53,7 +53,7 @@ ${makeExample(
"Modulating the speed of a sample player using noise",
`beat(.25) && snd('cp').speed(1 + noise() * 2).out()`,
true,
)};
)};
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const patterns = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -15,7 +15,7 @@ ${makeExample(
beat(1)::sound('kick').out()
`,
true,
)}
)}
can be turned into something more interesting like this easily:
@ -27,7 +27,7 @@ beat([1, 0.5, 0.25].dur(0.75, 0.25, 1) / c)::sound(['kick', 'fsoftsnare'].beat(0
.ad(0, .25).shape(usine(1/2)*0.5).speed([1, 2, 4].beat(0.5)).out()
`,
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>.
@ -45,7 +45,7 @@ beat([0.5, 1].beat(4)) :: sound('kick').out()
beat(2)::snd('snare').shape(.5).out()
`,
true,
)}
)}
${makeExample(
"Using beat to create arpeggios",
`
@ -63,7 +63,7 @@ beat([.5, .25].beat(0.5)) :: sound('sine')
.out()
`,
false,
)}
)}
${makeExample(
"Cool ambiance",
`
@ -74,7 +74,7 @@ flip(4)::beat(2)::snd('pad').n(2).shape(.5)
.orbit(2).room(0.9).size(0.9).release(0.5).out()
`,
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.
@ -89,7 +89,7 @@ beat([1/4, 1/2].dur(1.5, 0.5))::sound(['jvbass', 'fikea'].bar())
.out()
`,
true,
)}
)}
${makeExample(
"Using beat and bar in the same example",
@ -102,7 +102,7 @@ beat([1, 0.5].beat()) :: sound(['bass3'].bar())
.speed([1,2,3].beat())
.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.
@ -118,7 +118,7 @@ beat(1)::sound(['kick', 'fsnare'].dur(3, 1))
.n([0,3].dur(3, 1)).out()
`,
true,
)}
)}
${makeExample(
"Patterning with ternary statements",
@ -137,7 +137,7 @@ onbeat([0.5,0.8].beat(1),2) :: sound('snare').out()
onbeat(0.5,0.8,1,1.5,2,2.5,3,4) :: sound('hh').out()
`,
true,
)}
)}
## Iteration using a counter
@ -150,7 +150,7 @@ ${makeExample(
beat(0.5) :: sound("bd").gain(line(0,1,0.01).$("ramp")).out()
`,
true,
)}
)}
## Manipulating notes and scales
@ -165,7 +165,7 @@ beat(0.25) :: snd('sine')
.scale("minor").ad(0, .25).out()
`,
true,
)}
)}
- <ic>semitones(number[], ...args?)</ic>: Create scale from semitone intervals.
@ -176,7 +176,7 @@ ${makeExample(
.semitones(1, 1, 3, 1, 1, 2, 3).out()
`,
true,
)}
)}
- <ic>cents(number[], ...args?)</ic>: Create scale from cent intervals.
@ -188,7 +188,7 @@ ${makeExample(
.cents(120,270,540,670,785,950,1215).out()
`,
true,
)}
)}
- <ic>ratios(number[], ...args?)</ic>: Create scale from ratios.
@ -200,7 +200,7 @@ ${makeExample(
.ratios(2/11,4/11,6/11,8/11,10/11,11/11).out()
`,
true,
)}
)}
- <ic>edo(number, scale?: string|number[])</ic>: Create scale from equal divisions of the octave. Creates chromatic scale by default.
@ -219,7 +219,7 @@ rhythm(2.0,26,32) :: sound("ST20").n([22,5,24,34,31,5,11,19].pick()).stretch(rI(
.out()
`,
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/).
@ -232,7 +232,7 @@ beat(1) :: snd('gtr')
.out()
`,
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>.
@ -244,7 +244,7 @@ beat(1) :: snd('gtr')
.out()
`,
true,
)}
)}
## Iteration using the mouse
@ -260,7 +260,7 @@ beat(0.25)::sound('wt_piano')
.ad(0, .2).out()
`,
true,
)}
)}
## Simple data operations
@ -278,7 +278,7 @@ beat([1,.5,.25].beat()) :: snd('wt_stereo')
.lpad(4, 0, .25).sustain(0.125).out()
`,
true,
)}
)}
- <ic>random(index: number)</ic>: pick a random element in the given list.
- <ic>rand(index: number)</ic>: shorter alias for the same method.
@ -297,7 +297,7 @@ beat([.5, 1].rand() / 2) :: snd(
.end(0.5).out()
`,
true,
)}
)}
- <ic>pick()</ic>: pick a random element in the list.
@ -309,7 +309,7 @@ beat(0.25)::sound(['ftabla', 'fwood'].pick())
.room(0.5).size(1).out()
`,
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.
@ -320,7 +320,7 @@ ${makeExample(
beat(.25)::snd('amencutup').n([1,2,3,4,5,6,7,8,9].degrade(20).loop($(1))).out()
`,
true,
)}
)}
- <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.
@ -332,7 +332,7 @@ ${makeExample(
beat(.25)::sound('amencutup').n([1,2,3,4,5,6,7,8].repeat(4).beat(.25)).out()
`,
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>.
@ -342,7 +342,7 @@ ${makeExample(
beat(1) :: sound('numbers').n([1,2,3,4,5].loop($(3, 10, 2))).out()
`,
true,
)}
)}
- <ic>shuffle(): this</ic>: shuffles a list! Simple enough!
@ -352,7 +352,7 @@ ${makeExample(
beat(1) :: sound('numbers').n([1,2,3,4,5].shuffle().loop($(1)).out()
`,
true,
)}
)}
- <ic>rotate(steps: number)</ic>: rotate a list to the right _n_ times. The last value become the first, rinse and repeat.
@ -370,7 +370,7 @@ beat(.25) :: snd('sine').fmi([1.99, 2])
.out()
`,
true,
)}
)}
## Filtering
@ -383,7 +383,7 @@ ${makeExample(
beat(1)::snd('sine').sustain(0.1).freq([100,100,100,100,200].unique().beat()).out()
`,
true,
)}
)}
## Simple math operations

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const probabilities = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -18,7 +18,7 @@ ${makeExample(
rhythm(0.125, 10, 16) :: sound('sid').n(4).note(50 + irand(50, 62) % 8).out()
`,
true,
)}
)}
- <ic>prob(p: number)</ic>: return <ic>true</ic> _p_% of time, <ic>false</ic> in other cases.
@ -33,7 +33,7 @@ prob(60) :: script(2);
prob(80) :: script(toss() ? script(3) : script(4))
`,
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.
@ -66,7 +66,7 @@ ${makeExample(
rarely(8) :: sound('east').out(); // Rarely in 8 beats is even less
`,
true,
)}
)}
${makeExample(
"Using chance with other operators",
@ -76,7 +76,7 @@ ${makeExample(
sometimes() :: onbeat(1,3) :: sound('snare').out();
`,
false,
)}
)}
${makeExample(
"Using chance with chaining",
@ -93,6 +93,6 @@ ${makeExample(
.out()
`,
false,
)}
)}
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../main";
import { makeExampleFactory } from "../../Documentation";
import { makeExampleFactory } from "../Documentation";
export const variables = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -18,7 +18,7 @@ ${makeExample(
global.my_variable = 2
`,
true,
)}
)}
${makeExample(
"Getting that variable back and printing!",
@ -27,7 +27,7 @@ ${makeExample(
log(global.my_variable)
`,
true,
)}
)}
Now your scripts can share information with each other!
@ -52,7 +52,7 @@ ${makeExample(
rhythm(.25, 6, 8) :: sound('dr').n($(1)).end(.25).out()
`,
true,
)}
)}
${makeExample(
"Using a more complex counter",
@ -61,7 +61,7 @@ ${makeExample(
rhythm(.25, 6, 8) :: sound('dr').n($(1, 20, 5)).end(.25).out()
`,
false,
)}
)}
${makeExample(
"Calling the drunk mechanism",
@ -70,7 +70,7 @@ ${makeExample(
rhythm(.25, 6, 8) :: sound('dr').n(drunk()).end(.25).out()
`,
false,
)}
)}

View File

@ -0,0 +1,6 @@
export { ziffers_basics } from './ziffers_basics';
export { ziffers_scales } from './ziffers_scales';
export { ziffers_rhythm } from './ziffers_rhythm';
export { ziffers_algorithmic } from './ziffers_algorithmic';
export { ziffers_tonnetz } from './ziffers_tonnetz';
export { ziffers_syncing } from './ziffers_syncing';

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const ziffers_algorithmic = (application: Editor): string => {
const makeExample = makeExampleFactory(application);

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const ziffers_basics = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -30,7 +30,7 @@ z3('can can:2').sound().gain(1).cutoff(osci).out()
z4('1/4 kick kick snare kick').sound().gain(1).cutoff(osci).out()
`,
true,
)}
)}
## Evaluation
@ -63,7 +63,7 @@ ${makeExample(
z1('0.25 0 1 2 3 4 5 6 7 8 9').sound('wt_stereo')
.adsr(0, .1, 0, 0).out()`,
true,
)}
)}
${makeExample(
"Escaped pitches using curly brackets",
@ -72,7 +72,7 @@ ${makeExample(
.cutoff(usaw(1/2) * 4000)
.room(0.9).size(0.9).out()`,
false,
)}
)}
${makeExample(
"Durations using fractions and floating point numbers",
@ -82,7 +82,7 @@ z1('1/8 0 2 4 0 2 4 1/4 0 3 5 0.25 _ 0 7 0 7')
.adsr(0, .1, 0, 0).delayfb(0.45).out()
`,
false,
)}
)}
${makeExample(
"Disco was invented thanks to Ziffers",
@ -92,7 +92,7 @@ beat(1)::snd('bd').out(); beat(2)::snd('sd').out()
beat(3) :: snd('cp').room(0.5).size(0.5).orbit(2).out()
`,
false,
)}
)}
${makeExample(
"Accidentals and rests for nice melodies",
@ -104,7 +104,7 @@ z1('^ 1/8 0 1 b2 3 4 _ 4 b5 4 3 b2 1 0')
.adsr(0, .1, 0, 0).out()
`,
false,
)}
)}
${makeExample(
"Repeat items n-times",
@ -117,7 +117,7 @@ z2('1/8 _ 0!4 5!4 4!2 7!2')
.shape(0.2).sustain(0.1).out()
`,
false,
)}
)}
${makeExample(
"Subdivided durations",
@ -127,7 +127,7 @@ z1('w [0 [5 [3 7]]] h [0 4]')
.fmi(usine(.5)).fmh(2).out()
`,
false,
)}
)}
## Rests
@ -138,7 +138,7 @@ z1('q 0 ^ e0 r _ 0 _ r 4 ^4 4')
.sound('sine').scale("godian").out()
`,
true,
)}
)}
${makeExample(
"Rests with durations",
@ -147,7 +147,7 @@ ${makeExample(
.sound('sine').scale("aeryptian").out()
`,
true,
)}
)}
## Chords
@ -232,7 +232,7 @@ ${makeExample(
z1("(i v vi%-3 iv%-2)@(s 0 2 0 1 2 1 0 2)")
.sound("sine").out()
`,
)}
)}
${makeExample(
"Arpeggio from named chords with durations",
@ -241,7 +241,7 @@ z1("_ Gm7 ^ C9 D7 Gm7")
.arpeggio("e 0 2 q 3 e 1 2")
.sound("sine").out()
`,
)}
)}
${makeExample(
"Arpeggio from roman chords with inversions",
@ -251,7 +251,7 @@ ${makeExample(
.noteLength(0.125)
.sound("sine").out()
`,
)}
)}
## Chaining
@ -264,7 +264,7 @@ z1('0 1 2 3').key('G3')
.scale('minor').sound('sine').out()
`,
true,
)}
)}
${makeExample(
"More complex chaining",
@ -272,7 +272,7 @@ ${makeExample(
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,
)}
)}
${makeExample(
"Alternative way for inputting options",
@ -280,7 +280,7 @@ ${makeExample(
z1('0 3 2 4',{key: 'D3', scale: 'minor pentatonic'}).sound('sine').out()
`,
true,
)}
)}
## String prototypes
@ -294,7 +294,7 @@ ${makeExample(
"q 2 7 8 6".z2({key: "C2", scale: "aeolian"}).sound('sine').scale("minor").out()
`,
true,
)}
)}
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const ziffers_rhythm = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -14,7 +14,7 @@ ${makeExample(
z1('q 0 0 4 4 5 5 h4 q 3 3 2 2 1 1 h0').sound('sine').out()
`,
true,
)}
)}
${makeExample(
"Fraction durations",
@ -23,7 +23,7 @@ ${makeExample(
.sound('sine').out()
`,
true,
)}
)}
${makeExample(
"Decimal durations",
@ -32,7 +32,7 @@ z1('0.25 5 1 2 6 0.125 3 8 0.5 4 1.0 0')
.sound('sine').scale("galian").out()
`,
true,
)}
)}
## List of all duration characters
@ -95,7 +95,7 @@ ${makeExample(
z1('bd [hh hh]').octave(-2).sound('sine').out()
`,
true,
)}
)}
${makeExample(
"More complex pattern",
@ -103,7 +103,7 @@ ${makeExample(
z1('bd [hh <hh <cp cp:2>>]').octave(-2).sound('sine').out()
`,
true,
)}
)}
${makeExample(
"Pitched samples",
@ -113,7 +113,7 @@ ${makeExample(
.adsr(0.25,0.125,0.125,0.25).out()
`,
true,
)}
)}
${makeExample(
"Pitched samples from list operation",
@ -124,7 +124,7 @@ ${makeExample(
.sound().out()
`,
true,
)}
)}
${makeExample(
"Pitched samples with list notation",
@ -134,7 +134,7 @@ ${makeExample(
.adsr(0.25,0.125,0.125,0.25).out()
`,
true,
)}
)}
${makeExample(
"Sample indices",
@ -143,7 +143,7 @@ ${makeExample(
.octave(-1).sound("east").out()
`,
true,
)}
)}
${makeExample(
"Pitched samples with sample indices",
@ -151,7 +151,7 @@ ${makeExample(
z1('_e 1@east:2 4@bd:3 6@arp:2 9@baa').sound().out()
`,
true,
)}
)}
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const ziffers_scales = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -95,7 +95,7 @@ ${makeExample(
onbeat(1,1.5,3) :: sound('bd').cutoff(100 + usine(.25) * 1000).out()
`,
true,
)}
)}
`;
};

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const ziffers_syncing = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
@ -13,13 +13,13 @@ Ziffers patterns can be synced to any event by using **cue**, **sync**, **wait**
The <ic>cue(name: string)</ic> methods can be used to send cue messages for ziffers patterns. The <ic>wait(name: string)</ic> method is used to wait for the cue message to be received before starting the next cycle.
${makeExample(
"Sending cue from event and wait",
`
"Sending cue from event and wait",
`
beat(4.0) :: sound("bd").cue("foo").out()
z1("e 0 3 2 1 2 1").wait("foo").sound("sine").out()
`,
true,
)}
true,
)}
The <ic>sync(name: string)</ic> method is used to sync the ziffers pattern to the cue message.
@ -65,7 +65,7 @@ beat([2.0,0.5,1.5].bar(1)) ::
.dur(0.5).out()
`,
true,
)}
)}
## Automatic sync for ziffers patterns

View File

@ -1,5 +1,5 @@
import { type Editor } from "../../../main";
import { makeExampleFactory } from "../../../Documentation";
import { makeExampleFactory } from "../../Documentation";
export const ziffers_tonnetz = (application: Editor): string => {
const makeExample = makeExampleFactory(application);

View File

@ -13,11 +13,11 @@ import {
loadUniverserFromUrl,
} from "./FileManagement";
import { singleElements, buttonGroups, ElementMap, createDocumentationStyle } from "./DomElements";
import { registerFillKeys, registerOnKeyDown } from "./KeyActions";
import { registerFillKeys, registerOnKeyDown } from "./Keyboard";
import { installEditor } from "./EditorSetup";
import { documentation_factory, documentation_pages, showDocumentation, updateDocumentationContent } from "./Documentation";
import { documentation_factory, documentation_pages, showDocumentation, updateDocumentationContent } from "./documentation/Documentation";
import { EditorView } from "codemirror";
import { Clock } from "./Clock";
import { Clock } from "./clock/Clock";
import { loadSamples, UserAPI } from "./API";
import * as oeis from "jisg";
import * as zpatterns from "zifferjs/src/patterns.ts";
@ -28,7 +28,7 @@ import { tryEvaluate } from "./Evaluator";
// @ts-ignore
import showdown from "showdown";
import { makeStringExtensions } from "./extensions/StringExtensions";
import { installInterfaceLogic } from "./InterfaceLogic";
import { installInterfaceLogic } from "./UILogic";
import { installWindowBehaviors } from "./WindowBehavior";
import { makeNumberExtensions } from "./extensions/NumberExtensions";
import colors from "./colors.json";
@ -124,7 +124,6 @@ export class Editor {
this.initializeElements();
this.initializeButtonGroups();
this.setCanvas(this.interface.feedback as HTMLCanvasElement);
this.setCanvas(this.interface.scope as HTMLCanvasElement);
this.setCanvas(this.interface.drawings as HTMLCanvasElement);
try {
this.loadHydraSynthAsync();
@ -198,7 +197,7 @@ export class Editor {
// ================================================================================
installEditor(this);
runOscilloscope(this.interface.scope as HTMLCanvasElement, this);
runOscilloscope(this.interface.feedback as HTMLCanvasElement, this);
// First evaluation of the init file
tryEvaluate(this, this.universes[this.selected_universe.toString()].init);
@ -581,7 +580,6 @@ export class Editor {
private setCanvas(canvas: HTMLCanvasElement): void {
/**
* Sets the canvas element and configures its size and context.
*
* @param canvas - The HTMLCanvasElement to set.
*/
if (!canvas) return;