Fix nasty auto-save bug

This commit is contained in:
2023-11-14 12:51:26 +01:00
parent c46f553f21
commit 3b6f02b525
7 changed files with 130 additions and 165 deletions

View File

@ -36,22 +36,6 @@ if (!self.define) {
} }
}) })
.then(() => {
return registry[uri] || (
new Promise(resolve => {
if ("document" in self) {
const script = document.createElement("script");
script.src = uri;
script.onload = resolve;
document.head.appendChild(script);
} else {
nextDefineUri = uri;
importScripts(uri);
resolve();
}
})
.then(() => { .then(() => {
let promise = registry[uri]; let promise = registry[uri];
if (!promise) { if (!promise) {
@ -70,15 +54,11 @@ if (!self.define) {
} }
let exports = {}; let exports = {};
const require = depUri => singleRequire(depUri, uri); const require = depUri => singleRequire(depUri, uri);
const require = depUri => singleRequire(depUri, uri);
const specialDeps = { const specialDeps = {
module: { uri }, module: { uri },
exports, exports,
require require
}; };
registry[uri] = Promise.all(depsNames.map(
depName => specialDeps[depName] || require(depName)
)).then(deps => {
registry[uri] = Promise.all(depsNames.map( registry[uri] = Promise.all(depsNames.map(
depName => specialDeps[depName] || require(depName) depName => specialDeps[depName] || require(depName)
)).then(deps => { )).then(deps => {
@ -87,7 +67,6 @@ if (!self.define) {
}); });
}; };
} }
define(['./workbox-b7fccfec'], (function (workbox) { 'use strict';
define(['./workbox-b7fccfec'], (function (workbox) { 'use strict'; define(['./workbox-b7fccfec'], (function (workbox) { 'use strict';
self.skipWaiting(); self.skipWaiting();
@ -103,7 +82,7 @@ define(['./workbox-b7fccfec'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "index.html", "url": "index.html",
"revision": "0.93gcaii03k8" "revision": "0.0ufj7f4tmog"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
@ -121,21 +100,5 @@ define(['./workbox-b7fccfec'], (function (workbox) { 'use strict';
})] })]
}), 'GET'); }), 'GET');
}));
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
allowlist: [/^\/$/]
}));
workbox.registerRoute(({
url
}) => [/^https:\/\/raw\.githubusercontent\.com\/.*/i, /^https:\/\/shabda\.ndre\.gr\/.*/i].some(regex => regex.test(url)), new workbox.CacheFirst({
"cacheName": "external-samples",
plugins: [new workbox.ExpirationPlugin({
maxEntries: 5000,
maxAgeSeconds: 2592000
}), new workbox.CacheableResponsePlugin({
statuses: [0, 200]
})]
}), 'GET');
})); }));
//# sourceMappingURL=sw.js.map //# sourceMappingURL=sw.js.map

File diff suppressed because one or more lines are too long

View File

@ -707,8 +707,8 @@ export class UserAPI {
this.app.api.patternCache.set(key, player); this.app.api.patternCache.set(key, player);
} }
if(player.ziffers.generator && player.ziffers.generatorDone) { if (player.ziffers.generator && player.ziffers.generatorDone) {
this.removePatternFromCache(key); this.removePatternFromCache(key);
} }
if (typeof id === "number") player.zid = zid; if (typeof id === "number") player.zid = zid;
@ -1322,6 +1322,12 @@ export class UserAPI {
return results.some((value) => value === true); return results.some((value) => value === true);
}; };
public dur = (n: number | number[]): boolean => {
let nums: number[] = Array.isArray(n) ? n : [n];
// @ts-ignore
return this.beat(nums.dur(...nums));
};
// ============================================================= // =============================================================
// Modulo based time filters // Modulo based time filters
// ============================================================= // =============================================================

View File

@ -16,6 +16,11 @@ const handleResize = (canvas: HTMLCanvasElement) => {
} }
}; };
export const saveState = (app: Editor): null => {
app.settings.saveApplicationToLocalStorage(app.universes, app.settings);
return null;
};
export const saveBeforeExit = (app: Editor): null => { export const saveBeforeExit = (app: Editor): null => {
// Iterate over all local files and set the candidate to the committed // Iterate over all local files and set the candidate to the committed
app.currentFile().candidate = app.view.state.doc.toString(); app.currentFile().candidate = app.view.state.doc.toString();
@ -29,7 +34,6 @@ export const installWindowBehaviors = (
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)
); );
@ -37,19 +41,19 @@ export const installWindowBehaviors = (
handleResize(app.interface.feedback as HTMLCanvasElement) handleResize(app.interface.feedback as HTMLCanvasElement)
); );
window.addEventListener("beforeunload", (event) => { window.addEventListener("beforeunload", (event) => {
event.preventDefault() event.preventDefault();
saveBeforeExit(app) saveBeforeExit(app);
}); });
window.addEventListener("visibilitychange", (event) => { window.addEventListener("visibilitychange", (event) => {
event.preventDefault(); event.preventDefault();
saveBeforeExit(app) saveBeforeExit(app);
}); });
if (preventMultipleTabs) { if (preventMultipleTabs) {
localStorage.openpages = Date.now(); localStorage.openpages = Date.now();
window.addEventListener( window.addEventListener(
"storage", "storage",
function(e) { function (e) {
if (e.key == "openpages") { if (e.key == "openpages") {
// Listen if anybody else is opening the same page! // Listen if anybody else is opening the same page!
localStorage.page_available = Date.now(); localStorage.page_available = Date.now();
@ -65,11 +69,21 @@ export const installWindowBehaviors = (
); );
} }
window.addEventListener('error', e => { window.addEventListener(
console.log("Je suis bien installé !") "error",
console.log(e.message (e) => {
, '\n', e.filename, ':', e.lineno, (e.colno ? ':' + e.colno : '') console.log("Je suis bien installé !");
, e.error && e.error.stack ? '\n' : '', e.error ? e.error.stack : undefined console.log(
); e.message,
}, false); "\n",
e.filename,
":",
e.lineno,
e.colno ? ":" + e.colno : "",
e.error && e.error.stack ? "\n" : "",
e.error ? e.error.stack : undefined
);
},
false
);
}; };

View File

@ -16,17 +16,17 @@ Time as a cycle. A cycle can be quite long (a few bars) or very short (a few pul
- <ic>offset</ic>: offset (in beats) to apply. An offset of <ic>0.5</ic> will return true against the beat. - <ic>offset</ic>: offset (in beats) to apply. An offset of <ic>0.5</ic> will return true against the beat.
${makeExample( ${makeExample(
"Using different mod values", "Using different mod values",
` `
// 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",
` `
// Blip generator :) // Blip generator :)
let blip = (freq) => { let blip = (freq) => {
return sound('wt_piano') return sound('wt_piano')
@ -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.
@ -58,21 +58,21 @@ beat([.5, 1.25])::sound('hat').out()
${makeExample( ${makeExample(
"Intriguing rhythms", "Intriguing rhythms",
` `
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.
@ -80,37 +80,37 @@ beat(1)::snd(['bd', '808oh'].beat(1)).out()
- <ic>offset</ic>: offset (in bars) to apply. - <ic>offset</ic>: offset (in bars) to apply.
${makeExample( ${makeExample(
"Four beats per bar: proof", "Four beats per bar: proof",
` `
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(
"Offsetting beat and bar", "Offsetting beat and bar",
` `
bar(1)::sound('kick').out() bar(1)::sound('kick').out()
beat(1)::sound('hat').speed(2).out() 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.
${makeExample( ${makeExample(
"Some simple yet detailed rhythms", "Some simple yet detailed rhythms",
` `
onbeat(1,2,3,4)::snd('kick').out() // Bassdrum on each beat 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
)} )}
## Cyclical rhythm generators ## Cyclical rhythm generators
@ -119,8 +119,8 @@ We included a bunch of popular rhythm generators in Topos such as the euclidian
- <ic>rhythm(divisor: number, 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>rhythm(divisor: number, 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.
${makeExample( ${makeExample(
"rhythm is a beginner friendly rhythmic function!", "rhythm is a beginner friendly rhythmic function!",
` `
rhythm(.5, 4, 8)::sound('sine') rhythm(.5, 4, 8)::sound('sine')
.fmi(2) .fmi(2)
.room(0.5).size(8) .room(0.5).size(8)
@ -129,38 +129,38 @@ 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.
${makeExample( ${makeExample(
"Using oneuclid to create a rhythm without iterators", "Using oneuclid to create a rhythm without iterators",
` `
// Change speed using bpm // Change speed using bpm
bpm(250) bpm(250)
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.
${makeExample( ${makeExample(
"Change the integers for a surprise rhythm!", "Change the integers for a surprise rhythm!",
` `
bpm(135); 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!",
` `
let a = 0; let a = 0;
a = beat(4) ? irand(1,20) : a; a = beat(4) ? irand(1,20) : a;
binrhythm(.5, 6) && snd(['kick', 'snare'].beat(0.5)).n(11).out() binrhythm(.5, 6) && snd(['kick', 'snare'].beat(0.5)).n(11).out()
@ -169,34 +169,34 @@ 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",
` `
bpm(145); bpm(145);
beat(.5) && bin($(1), 911) && snd('ST69').n([2,3,4].beat()) beat(.5) && bin($(1), 911) && snd('ST69').n([2,3,4].beat())
.delay(0.125).delayt(0.25).end(0.25).speed(1/3) .delay(0.125).delayt(0.25).end(0.25).speed(1/3)
.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.
${makeExample( ${makeExample(
"Probablistic drums in one line!", "Probablistic drums in one line!",
` `
prob(60)::beat(.5) && euclid($(1), 5, 8) && snd('kick').out() prob(60)::beat(.5) && euclid($(1), 5, 8) && snd('kick').out()
prob(60)::beat(.5) && euclid($(2), 3, 8) && snd('mash') prob(60)::beat(.5) && euclid($(2), 3, 8) && snd('mash')
.n([1,2,3].beat(1)) .n([1,2,3].beat(1))
.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
)} )}

View File

@ -1,6 +1,6 @@
import { type UserAPI } from "../API"; import { type UserAPI } from "../API";
import { safeScale, stepsToScale } from "zifferjs"; import { safeScale, stepsToScale } from "zifferjs";
export { }; export {};
declare global { declare global {
interface Array<T> { interface Array<T> {
@ -8,8 +8,8 @@ declare global {
sub(amount: number): number[]; sub(amount: number): number[];
mult(amount: number): number[]; mult(amount: number): number[];
div(amount: number): number[]; div(amount: number): number[];
mouseX(): T[], mouseX(): T[];
mouseY(): T[], mouseY(): T[];
palindrome(): T[]; palindrome(): T[];
random(index: number): T; random(index: number): T;
rand(index: number): T; rand(index: number): T;
@ -37,7 +37,6 @@ declare global {
} }
export const makeArrayExtensions = (api: UserAPI) => { export const makeArrayExtensions = (api: UserAPI) => {
Array.prototype.mouseX = function <T>(this: T[]): T { Array.prototype.mouseX = function <T>(this: T[]): T {
/** /**
* @returns Value from list based on mouse X position * @returns Value from list based on mouse X position
@ -59,14 +58,14 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this[zoneIndex]; return this[zoneIndex];
}; };
Array.prototype.square = function(): number[] { Array.prototype.square = function (): number[] {
/** /**
* @returns New array with squared values. * @returns New array with squared values.
*/ */
return this.map((x: number) => x * x); return this.map((x: number) => x * x);
}; };
Array.prototype.sometimes = function(func: Function): number[] { Array.prototype.sometimes = function (func: Function): number[] {
if (api.randomGen() < 0.5) { if (api.randomGen() < 0.5) {
return func(this); return func(this);
} else { } else {
@ -74,11 +73,11 @@ export const makeArrayExtensions = (api: UserAPI) => {
} }
}; };
Array.prototype.apply = function(func: Function): number[] { Array.prototype.apply = function (func: Function): number[] {
return func(this); return func(this);
}; };
Array.prototype.sqrt = function(): number[] { Array.prototype.sqrt = function (): number[] {
/** /**
* @returns New array with square roots of values. Throws if any element is negative. * @returns New array with square roots of values. Throws if any element is negative.
*/ */
@ -87,7 +86,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this.map((x: number) => Math.sqrt(x)); return this.map((x: number) => Math.sqrt(x));
}; };
Array.prototype.add = function(amount: number): number[] { Array.prototype.add = function (amount: number): number[] {
/** /**
* @param amount - The value to add to each element in the array. * @param amount - The value to add to each element in the array.
* @returns New array with added values. * @returns New array with added values.
@ -95,7 +94,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this.map((x: number) => x + amount); return this.map((x: number) => x + amount);
}; };
Array.prototype.sub = function(amount: number): number[] { Array.prototype.sub = function (amount: number): number[] {
/** /**
* @param amount - The value to subtract from each element in the array. * @param amount - The value to subtract from each element in the array.
* @returns New array with subtracted values. * @returns New array with subtracted values.
@ -103,7 +102,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this.map((x: number) => x - amount); return this.map((x: number) => x - amount);
}; };
Array.prototype.mult = function(amount: number): number[] { Array.prototype.mult = function (amount: number): number[] {
/** /**
* @param amount - The value to multiply with each element in the array. * @param amount - The value to multiply with each element in the array.
* @returns New array with multiplied values. * @returns New array with multiplied values.
@ -111,7 +110,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this.map((x: number) => x * amount); return this.map((x: number) => x * amount);
}; };
Array.prototype.div = function(amount: number): number[] { Array.prototype.div = function (amount: number): number[] {
/** /**
* @param amount - The value to divide each element in the array by. * @param amount - The value to divide each element in the array by.
* @returns New array with divided values. Throws if division by zero. * @returns New array with divided values. Throws if division by zero.
@ -120,7 +119,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this.map((x: number) => x / amount); return this.map((x: number) => x / amount);
}; };
Array.prototype.pick = function() { Array.prototype.pick = function () {
/** /**
* Returns a random element from an array. * Returns a random element from an array.
* *
@ -129,7 +128,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this[Math.floor(api.randomGen() * this.length)]; return this[Math.floor(api.randomGen() * this.length)];
}; };
Array.prototype.gen = function(min: number, max: number, times: number) { Array.prototype.gen = function (min: number, max: number, times: number) {
/** /**
* Returns an array of random numbers. * Returns an array of random numbers.
* @param min - The minimum value of the random numbers * @param min - The minimum value of the random numbers
@ -146,7 +145,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
); );
}; };
Array.prototype.bar = function(value: number = 1) { Array.prototype.bar = function (value: number = 1) {
/** /**
* Returns an element from an array based on the current bar. * Returns an element from an array based on the current bar.
* *
@ -161,7 +160,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
} }
}; };
Array.prototype.beat = function(divisor: number = 1) { Array.prototype.beat = function (divisor: number = 1) {
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(
@ -171,47 +170,30 @@ export const makeArrayExtensions = (api: UserAPI) => {
}; };
Array.prototype.b = Array.prototype.beat; Array.prototype.b = Array.prototype.beat;
// Array.prototype.dur = function(...durations) { Array.prototype.dur = function (...durations) {
// const timepos = api.app.clock.pulses_since_origin;
// const ppqn = api.ppqn();
// let adjustedDurations = [];
// for (let i = 0; i < this.length; i++) {
// adjustedDurations.push(durations[i % durations.length]);
// }
// let cumulativeDuration = 0;
// let totalDuration = adjustedDurations.reduce((acc, duration) => acc + duration * ppqn, 0);
// let modulatedTimePos = timepos % totalDuration;
// for (let i = 0; i < this.length; i++) {
// const valueDuration = adjustedDurations[i] as any * ppqn;
// if (modulatedTimePos < cumulativeDuration + valueDuration) {
// return this[i];
// }
// cumulativeDuration += valueDuration;
// }
// // This point should not be reached if durations are correctly specified
// throw new Error('Durations array does not match the pattern length.');
// };
Array.prototype.dur = function(...durations) {
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 = this.map((_, index) => durations[index % durations.length]); const adjustedDurations = this.map(
(_, index) => durations[index % durations.length]
);
// @ts-ignore // @ts-ignore
const totalDurationInPulses = adjustedDurations.reduce((acc, duration) => acc + duration * ppqn, 0); const totalDurationInPulses = adjustedDurations.reduce(
(acc, duration) => acc + duration * ppqn,
0
);
const loopPosition = timepos % totalDurationInPulses; const loopPosition = timepos % totalDurationInPulses;
let cumulativeDuration = 0; let cumulativeDuration = 0;
for (let i = 0; i < this.length; i++) { for (let i = 0; i < this.length; i++) {
const valueDurationInPulses = adjustedDurations[i] as any * ppqn; const valueDurationInPulses = (adjustedDurations[i] as any) * ppqn;
cumulativeDuration += valueDurationInPulses; cumulativeDuration += valueDurationInPulses;
if (loopPosition < cumulativeDuration) { if (loopPosition < cumulativeDuration) {
return this[i]; return this[i];
} }
} }
throw new Error('Durations array does not match the pattern length.'); throw new Error("Durations array does not match the pattern length.");
}; };
Array.prototype.shuffle = function () {
Array.prototype.shuffle = function() {
/** /**
* Shuffles the array in place. * Shuffles the array in place.
* *
@ -230,7 +212,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this; return this;
}; };
Array.prototype.rotate = function(steps: number) { Array.prototype.rotate = function (steps: number) {
/** /**
* Rotates the array in place. * Rotates the array in place.
* *
@ -250,7 +232,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this; return this;
}; };
Array.prototype.unique = function() { Array.prototype.unique = function () {
/** /**
* Removes duplicate elements from the array in place. * Removes duplicate elements from the array in place.
* *
@ -283,7 +265,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
if (this.length <= 1) { if (this.length <= 1) {
return this; return this;
} }
for (let i = 0; i < this.length;) { for (let i = 0; i < this.length; ) {
const rand = api.randomGen() * 100; const rand = api.randomGen() * 100;
if (rand < amount) { if (rand < amount) {
if (this.length > 1) { if (this.length > 1) {
@ -396,7 +378,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return left_to_right.concat(right_to_left); return left_to_right.concat(right_to_left);
}; };
Array.prototype.loop = function(index: number) { Array.prototype.loop = function (index: number) {
/** /**
* Returns an element from the array based on the index. * Returns an element from the array based on the index.
* The index will wrap over the array. * The index will wrap over the array.
@ -407,7 +389,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this[index % this.length]; return this[index % this.length];
}; };
Array.prototype.random = function() { Array.prototype.random = function () {
/** /**
* Returns a random element from the array. * Returns a random element from the array.
* *
@ -418,7 +400,7 @@ export const makeArrayExtensions = (api: UserAPI) => {
Array.prototype.rand = Array.prototype.random; Array.prototype.rand = Array.prototype.random;
}; };
Array.prototype.scale = function( Array.prototype.scale = function (
scale: string = "major", scale: string = "major",
base_note: number = 0 base_note: number = 0
) { ) {
@ -442,7 +424,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
) { ) {

View File

@ -16,8 +16,8 @@ import { documentation_factory } from "./Documentation";
import { EditorView } from "codemirror"; import { EditorView } from "codemirror";
import { Clock } from "./Clock"; import { Clock } from "./Clock";
import { loadSamples, UserAPI } from "./API"; import { loadSamples, UserAPI } from "./API";
import * as oeis from 'jisg' import * as oeis from "jisg";
import * as zpatterns from 'zifferjs/src/patterns.ts' import * as zpatterns from "zifferjs/src/patterns.ts";
import { makeArrayExtensions } from "./extensions/ArrayExtensions"; import { makeArrayExtensions } from "./extensions/ArrayExtensions";
import "./style.css"; import "./style.css";
import { Universes, File, template_universes } from "./FileManagement"; import { Universes, File, template_universes } from "./FileManagement";
@ -26,7 +26,7 @@ import { tryEvaluate } from "./Evaluator";
import showdown from "showdown"; import showdown from "showdown";
import { makeStringExtensions } from "./extensions/StringExtensions"; import { makeStringExtensions } from "./extensions/StringExtensions";
import { installInterfaceLogic } from "./InterfaceLogic"; import { installInterfaceLogic } from "./InterfaceLogic";
import { installWindowBehaviors, saveBeforeExit } from "./WindowBehavior"; import { installWindowBehaviors, saveState } from "./WindowBehavior";
import { drawEmptyBlinkers } from "./AudioVisualisation"; import { drawEmptyBlinkers } from "./AudioVisualisation";
import { makeNumberExtensions } from "./extensions/NumberExtensions"; import { makeNumberExtensions } from "./extensions/NumberExtensions";
// @ts-ignore // @ts-ignore
@ -482,7 +482,7 @@ export class Editor {
console.log("Hydra loaded successfully"); console.log("Hydra loaded successfully");
this.initializeHydra(); this.initializeHydra();
}; };
script.onerror = function() { script.onerror = function () {
console.error("Error loading Hydra script"); console.error("Error loading Hydra script");
}; };
document.head.appendChild(script); document.head.appendChild(script);
@ -514,7 +514,7 @@ export class Editor {
} }
private setPeriodicSave(interval: number): void { private setPeriodicSave(interval: number): void {
setInterval(() => saveBeforeExit(this), interval) setInterval(() => saveState(this), interval);
} }
} }