minor changes

This commit is contained in:
2023-11-10 12:08:29 +01:00
parent c21ce61c16
commit 7820d85b40
5 changed files with 142 additions and 137 deletions

View File

@ -82,7 +82,7 @@ define(['./workbox-5357ef54'], (function (workbox) { 'use strict';
"revision": "3ca0b8505b4bec776b69afdba2768812" "revision": "3ca0b8505b4bec776b69afdba2768812"
}, { }, {
"url": "index.html", "url": "index.html",
"revision": "0.7orc2ranod8" "revision": "0.juov83h1sno"
}], {}); }], {});
workbox.cleanupOutdatedCaches(); workbox.cleanupOutdatedCaches();
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import { EditorView } from '@codemirror/view'; import { EditorView } from "@codemirror/view";
import { getAllScaleNotes, seededRandom } from "zifferjs"; import { getAllScaleNotes, seededRandom } from "zifferjs";
import { import {
MidiCCEvent, MidiCCEvent,
@ -28,7 +28,7 @@ import {
import { Speaker } from "./StringExtensions"; import { Speaker } from "./StringExtensions";
import { getScaleNotes } from "zifferjs"; import { getScaleNotes } from "zifferjs";
import { OscilloscopeConfig, blinkScript } from "./AudioVisualisation"; import { OscilloscopeConfig, blinkScript } from "./AudioVisualisation";
import { SkipEvent } from './classes/SkipEvent'; import { SkipEvent } from "./classes/SkipEvent";
interface ControlChange { interface ControlChange {
channel: number; channel: number;
@ -151,9 +151,13 @@ export class UserAPI {
const stackLines = error.stack?.split("\n"); const stackLines = error.stack?.split("\n");
if (stackLines) { if (stackLines) {
for (const line of stackLines) { for (const line of stackLines) {
if (line.includes('<anonymous>')) { if (line.includes("<anonymous>")) {
const match = line.match(/<anonymous>:(\d+):(\d+)/); const match = line.match(/<anonymous>:(\d+):(\d+)/);
if (match) return { line: parseInt(match[1], 10), column: parseInt(match[2], 10) }; if (match)
return {
line: parseInt(match[1], 10),
column: parseInt(match[2], 10),
};
} }
} }
} }
@ -161,10 +165,10 @@ export class UserAPI {
}; };
const { line, column } = extractLineAndColumn(error); const { line, column } = extractLineAndColumn(error);
const errorMessage = line && column const errorMessage =
? `${error.message} (Line: ${line - 2}, Column: ${column})` line && column
: error.message; ? `${error.message} (Line: ${line - 2}, Column: ${column})`
: error.message;
clearTimeout(this.errorTimeoutID); clearTimeout(this.errorTimeoutID);
clearTimeout(this.printTimeoutID); clearTimeout(this.printTimeoutID);
@ -406,7 +410,7 @@ export class UserAPI {
* { channel: 0, velocity: 100, duration: 0.5 } * { channel: 0, velocity: 100, duration: 0.5 }
*/ */
const event = { note: value, velocity, channel, port } as MidiParams const event = { note: value, velocity, channel, port } as MidiParams;
return new MidiEvent(event, this.app); return new MidiEvent(event, this.app);
}; };
@ -675,7 +679,7 @@ export class UserAPI {
public resetAllFromCache = (): void => { public resetAllFromCache = (): void => {
this.patternCache.forEach((player) => (player as Player).reset()); this.patternCache.forEach((player) => (player as Player).reset());
} };
public z = ( public z = (
input: string, input: string,
@ -1005,74 +1009,6 @@ export class UserAPI {
}; };
cmp = this.clamp; cmp = this.clamp;
// =============================================================
// Transport functions
// =============================================================
public nudge = (nudge?: number): number => {
/**
* Sets or returns the current clock nudge.
*
* @param nudge - [optional] the nudge to set
* @returns The current nudge
*/
if (nudge) {
this.app.clock.nudge = nudge;
}
return this.app.clock.nudge;
};
public bpm = (n?: number): number => {
/**
* Sets or returns the current bpm.
*
* @param bpm - [optional] The bpm to set
* @returns The current bpm
*/
if (n === undefined) return this.app.clock.bpm;
if (n < 1 || n > 500) console.log(`Setting bpm to ${n}`);
this.app.clock.bpm = n;
return n;
};
tempo = this.bpm;
public bpb = (n?: number): number => {
/**
* Sets or returns the number of beats per bar.
*
* @param bpb - [optional] The number of beats per bar to set
* @returns The current bpb
*/
if (n === undefined) return this.app.clock.time_signature[0];
if (n < 1) console.log(`Setting bpb to ${n}`);
this.app.clock.time_signature[0] = n;
return n;
};
public ppqn = (n?: number) => {
/**
* Sets or returns the number of pulses per quarter note.
*/
if (n === undefined) return this.app.clock.ppqn;
if (n < 1) console.log(`Setting ppqn to ${n}`);
this.app.clock.ppqn = n;
return n;
};
public time_signature = (numerator: number, denominator: number): void => {
/**
* Sets the time signature.
*
* @param numerator - The numerator of the time signature
* @param denominator - The denominator of the time signature
* @returns The current time signature
*/
this.app.clock.time_signature = [numerator, denominator];
};
// ============================================================= // =============================================================
// Probability functions // Probability functions
// ============================================================= // =============================================================
@ -1293,30 +1229,29 @@ export class UserAPI {
// ============================================================= // =============================================================
public fullseq = (sequence: string, duration: number) => { public fullseq = (sequence: string, duration: number) => {
if (sequence.split('').every(c => c === 'x' || c === 'o')) { if (sequence.split("").every((c) => c === "x" || c === "o")) {
return [...sequence].map(c => c === 'x').beat(duration); return [...sequence].map((c) => c === "x").beat(duration);
} else { } else {
return false return false;
} }
} };
public seq = (expr: string, duration: number = 0.5): boolean => { public seq = (expr: string, duration: number = 0.5): boolean => {
let len = expr.length * duration let len = expr.length * duration;
let output: number[] = []; let output: number[] = [];
for (let i = 1; i <= len + 1; i += duration) { for (let i = 1; i <= len + 1; i += duration) {
output.push(Math.floor(i * 10) / 10); output.push(Math.floor(i * 10) / 10);
} }
output.pop() output.pop();
output = output.filter((_, idx) => { output = output.filter((_, idx) => {
const exprIdx = idx % expr.length; const exprIdx = idx % expr.length;
return expr[exprIdx] === 'x'; return expr[exprIdx] === "x";
}); });
return this.oncount(output, len) return this.oncount(output, len);
} };
public beat = (n: number | number[] = 1, nudge: number = 0): boolean => { public beat = (n: number | number[] = 1, nudge: number = 0): boolean => {
/** /**
@ -1329,7 +1264,7 @@ export class UserAPI {
const results: boolean[] = nArray.map( const results: boolean[] = nArray.map(
(value) => (value) =>
(this.app.clock.pulses_since_origin - Math.floor(nudge * this.ppqn())) % (this.app.clock.pulses_since_origin - Math.floor(nudge * this.ppqn())) %
Math.floor(value * this.ppqn()) === Math.floor(value * this.ppqn()) ===
0 0
); );
return results.some((value) => value === true); return results.some((value) => value === true);
@ -1349,7 +1284,7 @@ export class UserAPI {
const results: boolean[] = nArray.map( const results: boolean[] = nArray.map(
(value) => (value) =>
(this.app.clock.pulses_since_origin - nudgeInPulses) % (this.app.clock.pulses_since_origin - nudgeInPulses) %
Math.floor(value * barLength) === Math.floor(value * barLength) ===
0 0
); );
return results.some((value) => value === true); return results.some((value) => value === true);
@ -2032,7 +1967,7 @@ export class UserAPI {
effects: this.app.fontSize.reconfigure( effects: this.app.fontSize.reconfigure(
EditorView.theme({ EditorView.theme({
"&": { fontFamily: mainFont }, "&": { fontFamily: mainFont },
".cm-gutters": { fontFamily: mainFont, }, ".cm-gutters": { fontFamily: mainFont },
".cm-content": { ".cm-content": {
fontFamily: mainFont, fontFamily: mainFont,
}, },
@ -2042,8 +1977,7 @@ export class UserAPI {
}) })
), ),
}); });
} };
// ============================================================= // =============================================================
// Resolution // Resolution
@ -2051,56 +1985,57 @@ export class UserAPI {
public gif = (options: any) => { public gif = (options: any) => {
/** /**
* Displays a GIF on the webpage with customizable options including rotation and timed fade-out. * Displays a GIF on the webpage with customizable options including rotation and timed fade-out.
* @param {Object} options - The configuration object for displaying the GIF. * @param {Object} options - The configuration object for displaying the GIF.
* @param {string} options.url - The URL of the GIF to display. * @param {string} options.url - The URL of the GIF to display.
* @param {number} [options.posX=0] - The X-coordinate to place the GIF at. * @param {number} [options.posX=0] - The X-coordinate to place the GIF at.
* @param {number} [options.posY=0] - The Y-coordinate to place the GIF at. * @param {number} [options.posY=0] - The Y-coordinate to place the GIF at.
* @param {number} [options.opacity=1] - The initial opacity level of the GIF. * @param {number} [options.opacity=1] - The initial opacity level of the GIF.
* @param {string} [options.size='auto'] - The size of the GIF (can be 'cover', 'contain', or specific dimensions). * @param {string} [options.size='auto'] - The size of the GIF (can be 'cover', 'contain', or specific dimensions).
* @param {boolean} [options.center=false] - Whether to center the GIF in the window. * @param {boolean} [options.center=false] - Whether to center the GIF in the window.
* @param {number} [options.rotation=0] - The rotation angle of the GIF in degrees. * @param {number} [options.rotation=0] - The rotation angle of the GIF in degrees.
* @param {string} [options.filter='none'] - The CSS filter function to apply for color alterations. * @param {string} [options.filter='none'] - The CSS filter function to apply for color alterations.
* @param {number} [options.duration=10] - The total duration the GIF is displayed, in pulses. * @param {number} [options.duration=10] - The total duration the GIF is displayed, in pulses.
*/ */
const { const {
url, url,
posX = 0, posX = 0,
posY = 0, posY = 0,
opacity = 1, opacity = 1,
size = 'auto', size = "auto",
center = false, center = false,
rotation = 0, rotation = 0,
filter = 'none', filter = "none",
dur = 1 dur = 1,
} = options; } = options;
let real_duration = dur * this.app.clock.pulse_duration * this.app.clock.ppqn; let real_duration =
dur * this.app.clock.pulse_duration * this.app.clock.ppqn;
let fadeOutDuration = real_duration * 0.1; let fadeOutDuration = real_duration * 0.1;
let visibilityDuration = real_duration - fadeOutDuration; let visibilityDuration = real_duration - fadeOutDuration;
const gifElement = document.createElement('img'); const gifElement = document.createElement("img");
gifElement.src = url; gifElement.src = url;
gifElement.style.position = 'fixed'; gifElement.style.position = "fixed";
gifElement.style.left = center ? '50%' : `${posX}px`; gifElement.style.left = center ? "50%" : `${posX}px`;
gifElement.style.top = center ? '50%' : `${posY}px`; gifElement.style.top = center ? "50%" : `${posY}px`;
gifElement.style.opacity = `${opacity}`; gifElement.style.opacity = `${opacity}`;
gifElement.style.zIndex = '-1'; gifElement.style.zIndex = "-1";
if (size !== 'auto') { if (size !== "auto") {
gifElement.style.width = size; gifElement.style.width = size;
gifElement.style.height = size; gifElement.style.height = size;
} }
const transformRules = [`rotate(${rotation}deg)`]; const transformRules = [`rotate(${rotation}deg)`];
if (center) { if (center) {
transformRules.unshift('translate(-50%, -50%)'); transformRules.unshift("translate(-50%, -50%)");
} }
gifElement.style.transform = transformRules.join(' '); gifElement.style.transform = transformRules.join(" ");
gifElement.style.filter = filter; gifElement.style.filter = filter;
gifElement.style.transition = `opacity ${fadeOutDuration}s ease`; gifElement.style.transition = `opacity ${fadeOutDuration}s ease`;
document.body.appendChild(gifElement); document.body.appendChild(gifElement);
// Start the fade-out at the end of the visibility duration // Start the fade-out at the end of the visibility duration
setTimeout(() => { setTimeout(() => {
gifElement.style.opacity = '0'; gifElement.style.opacity = "0";
}, visibilityDuration * 1000); }, visibilityDuration * 1000);
// Remove the GIF from the DOM after the fade-out duration // Remove the GIF from the DOM after the fade-out duration
@ -2109,6 +2044,73 @@ export class UserAPI {
document.body.removeChild(gifElement); document.body.removeChild(gifElement);
} }
}, real_duration * 1000); }, real_duration * 1000);
} };
// =============================================================
// Transport functions
// =============================================================
public nudge = (nudge?: number): number => {
/**
* Sets or returns the current clock nudge.
*
* @param nudge - [optional] the nudge to set
* @returns The current nudge
*/
if (nudge) {
this.app.clock.nudge = nudge;
}
return this.app.clock.nudge;
};
public bpm = (n?: number): number => {
/**
* Sets or returns the current bpm.
*
* @param bpm - [optional] The bpm to set
* @returns The current bpm
*/
if (n === undefined) return this.app.clock.bpm;
if (n < 1 || n > 500) console.log(`Setting bpm to ${n}`);
this.app.clock.bpm = n;
return n;
};
tempo = this.bpm;
public bpb = (n?: number): number => {
/**
* Sets or returns the number of beats per bar.
*
* @param bpb - [optional] The number of beats per bar to set
* @returns The current bpb
*/
if (n === undefined) return this.app.clock.time_signature[0];
if (n < 1) console.log(`Setting bpb to ${n}`);
this.app.clock.time_signature[0] = n;
return n;
};
public ppqn = (n?: number) => {
/**
* Sets or returns the number of pulses per quarter note.
*/
if (n === undefined) return this.app.clock.ppqn;
if (n < 1) console.log(`Setting ppqn to ${n}`);
this.app.clock.ppqn = n;
return n;
};
public time_signature = (numerator: number, denominator: number): void => {
/**
* Sets the time signature.
*
* @param numerator - The numerator of the time signature
* @param denominator - The denominator of the time signature
* @returns The current time signature
*/
this.app.clock.time_signature = [numerator, denominator];
};
} }

View File

@ -432,7 +432,6 @@ export class SoundEvent extends AudibleEvent {
}; };
out = (): void => { out = (): void => {
console.log(this.app.clock.time_position.pulse)
const events = objectWithArraysToArrayOfObjects(this.values, [ const events = objectWithArraysToArrayOfObjects(this.values, [
"parsedScale", "parsedScale",
]); ]);

View File

@ -1,10 +1,9 @@
import { hoverTooltip } from "@codemirror/view"; import { hoverTooltip } from "@codemirror/view";
import { type EditorView } from "@codemirror/view"; import { type EditorView } from "@codemirror/view";
import { CompletionContext } from "@codemirror/autocomplete" import { CompletionContext } from "@codemirror/autocomplete";
// @ts-ignore // @ts-ignore
import { soundMap } from "superdough"; import { soundMap } from "superdough";
interface InlineCompletion { interface InlineCompletion {
name: string; name: string;
category: string; category: string;
@ -562,6 +561,13 @@ const completionDatabase: CompletionDatabase = {
description: "Get or set the current beats per minute.", description: "Get or set the current beats per minute.",
example: "bpm(135) // set the bpm to 135", example: "bpm(135) // set the bpm to 135",
}, },
tempo: {
name: "tempo",
category: "time",
description: "Get or set the current beats per minute.",
example: "tempo(135) // set the bpm to 135",
},
out: { out: {
name: "out", name: "out",
category: "audio", category: "audio",
@ -974,30 +980,28 @@ export const inlineHoveringTips = hoverTooltip(
); );
export const toposCompletions = (context: CompletionContext) => { export const toposCompletions = (context: CompletionContext) => {
let word = context.matchBefore(/\w*/) let word = context.matchBefore(/\w*/);
if (word) { if (word) {
if (word.from == word.to && !context.explicit) if (word.from == word.to && !context.explicit) return null;
return null
return { return {
from: word.from, from: word.from,
options: Object.keys(completionDatabase).map((key) => ({ options: Object.keys(completionDatabase).map((key) => ({
label: key, label: key,
type: completionDatabase[key].category, type: completionDatabase[key].category,
info: () => { info: () => {
let div = document.createElement('div'); let div = document.createElement("div");
div.innerHTML = ` div.innerHTML = `
<h1 class="text-orange-300 text-base pb-1">${completionDatabase[key].name} [<em class="text-white">${completionDatabase[key].category}</em>]</h1> <h1 class="text-orange-300 text-base pb-1">${completionDatabase[key].name} [<em class="text-white">${completionDatabase[key].category}</em>]</h1>
<p class="text-base pl-4">${completionDatabase[key].description}</p> <p class="text-base pl-4">${completionDatabase[key].description}</p>
<div class="overflow-hidden overflow-scroll rounded px-2 ml-4 mt-2 bg-neutral-800"><code class="text-sm">${completionDatabase[key].example}</code></div> <div class="overflow-hidden overflow-scroll rounded px-2 ml-4 mt-2 bg-neutral-800"><code class="text-sm">${completionDatabase[key].example}</code></div>
` `;
div.classList.add("px-4", "py-2", "rounded-lg", "w-92"); div.classList.add("px-4", "py-2", "rounded-lg", "w-92");
return div return div;
} },
})) })),
} };
} }
} };
export const soundCompletions = (context: CompletionContext) => { export const soundCompletions = (context: CompletionContext) => {
let map = soundMap.get(); let map = soundMap.get();
@ -1007,10 +1011,10 @@ export const soundCompletions = (context: CompletionContext) => {
let from = match.from + "sound(".length; let from = match.from + "sound(".length;
return { return {
from, from,
options: Object.keys(map).map(key => ({ options: Object.keys(map).map((key) => ({
label: key, label: key,
type: map[key].data.type, type: map[key].data.type,
apply: `"${key}"` apply: `"${key}"`,
})), })),
}; };
} }