Compare commits

..

30 Commits

Author SHA1 Message Date
Felix Roos 42a903ecc0 add --yes flag to readme, making sure first run works 2025-08-21 20:36:42 +02:00
Felix Roos da283eb55a doc: generate strudel.json 2025-08-21 20:36:17 +02:00
Felix Roos b12707316a bump @strudel/sampler to 0.2.1 2025-08-21 20:30:32 +02:00
Felix Roos 66aa3ac1da add --json flag to strudel sampler 2025-08-21 20:30:02 +02:00
froos d81b1ab65c Merge pull request 'remove hs2js postinstall' (#1510) from remove-postinstall into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1510
2025-08-21 10:04:28 +02:00
Felix Roos 8465517c76 remove hs2js postinstall 2025-08-21 10:00:41 +02:00
Felix Roos 215ab87809 fix: prettier 2025-08-21 09:41:21 +02:00
froos e5fe998327 Merge pull request 'Docs: Small additions to Learn Docs and fixed refrences to function Documentation' (#1502) from fyynn/strudel:main into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1502
2025-08-20 15:15:31 +02:00
froos 6f75a0a62b Merge pull request 'fix: repl autocomplete not rendering correctly' (#1480) from robase/strudel:fix-repl-autocomplete-styles into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1480
2025-08-20 12:31:36 +02:00
froos 0464845262 Merge pull request 'BUG FIX: make 'midin' initialization work with multiple controllers' (#1469) from cbabraham/strudel:fix/midi-init-bug into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1469
2025-08-20 12:10:00 +02:00
fyynn d6ee10e05c fixed new scrub refrence 2025-08-19 00:44:46 +02:00
fyynn 36441e7b73 Fixing hush refrence to code documentation
Signed-off-by: fyynn <fyynn@noreply.codeberg.org>
2025-08-19 00:25:31 +02:00
fyynn 189daa3942 Added Refrence to arp() arpWidth() to Docs
Signed-off-by: fyynn <fyynn@noreply.codeberg.org>
2025-08-19 00:21:32 +02:00
fyynn d009b9592e Added Refrence to arp() arpWidth() and hush to Docs
Signed-off-by: fyynn <fyynn@noreply.codeberg.org>
2025-08-19 00:09:41 +02:00
fyynn f7f1bd63e8 Adding the Duck/Sidechain effect to Learn Docs (learn/effects.mdx)
Added the duck Effect to the section global effects.

Signed-off-by: fyynn <fyynn@noreply.codeberg.org>
2025-08-18 23:49:43 +02:00
fyynn 7d24e45692 Added scrub() to Learn Docs
Signed-off-by: fyynn <fyynn@noreply.codeberg.org>
2025-08-18 23:36:39 +02:00
Jade (Rose) Rowland f17a4d045f Publish
- @strudel/codemirror@1.2.3
 - @strudel/core@1.2.3
 - @strudel/csound@1.2.4
 - @strudel/draw@1.2.3
 - @strudel/embed@1.1.1
 - @strudel/gamepad@1.2.3
 - @strudel/hydra@1.2.3
 - @strudel/midi@1.2.4
 - @strudel/mini@1.2.3
 - mondolang@1.1.1
 - @strudel/mondo@1.1.1
 - @strudel/motion@1.2.3
 - @strudel/mqtt@1.2.3
 - @strudel/osc@1.2.3
 - @strudel/reference@1.2.1
 - @strudel/repl@1.2.4
 - @strudel/serial@1.2.3
 - @strudel/soundfonts@1.2.4
 - superdough@1.2.4
 - @strudel/tonal@1.2.3
 - @strudel/transpiler@1.2.3
 - @strudel/web@1.2.4
 - @strudel/webaudio@1.2.4
 - @strudel/xen@1.2.3
2025-08-18 11:46:29 -04:00
Alex McLean 8e1e83f3cf Merge pull request 'euclidish' (#1482) from euclidish into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1482
2025-08-17 23:18:45 +02:00
alex 2d64b538e7 Merge branch 'main' into euclidish 2025-08-17 22:16:58 +01:00
alex b717615558 optimise euclidish, creating new morph function in the process 2025-08-17 22:13:26 +01:00
Switch Angel AKA Jade Rose 8c4f10464b Merge pull request 'feat: Create a duck (sidechain) orbit effect' (#1470) from pump into main
Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1470
2025-08-17 06:37:22 +02:00
Alex McLean 482ebb695a Merge branch 'main' into euclidish 2025-08-10 20:49:27 +02:00
robase 612efad3eb clean up comments 2025-08-07 09:25:01 +02:00
robase e7839a09a1 fix: tooltip should respect themes 2025-08-07 09:25:01 +02:00
robase 3292f88810 fix: repl autocomplete not rendering correctly 2025-08-07 09:25:01 +02:00
alex 03cbe4b6d4 delint 2025-07-28 15:48:09 +02:00
alex 784ee576da fixes 2025-07-28 15:33:18 +02:00
alex ee3e1217b4 add euclidish 2025-07-28 15:24:19 +02:00
Chandler Abraham 2eda047108 Merge branch 'main' of https://codeberg.org/uzu/strudel into fix/midi-init-bug 2025-07-22 21:52:03 -07:00
Chandler Abraham c8cf1dc712 fix(midi): ensure midin initializes device state correctly
The midin function only initialized the refs object for a device on the initial MIDI setup. This caused an error if a second MIDI device was connected in the same session, as its refs object would not exist, leading to a 'cannot read properties of undefined' error when accessed.

This commit fixes the issue by ensuring that the refs object for a given MIDI input is initialized every time  is called, not just on the first call.
2025-07-22 21:49:40 -07:00
38 changed files with 454 additions and 92 deletions
+69 -52
View File
@@ -1,68 +1,91 @@
import jsdoc from '../../doc.json';
// import { javascriptLanguage } from '@codemirror/lang-javascript';
import { autocompletion } from '@codemirror/autocomplete';
import { h } from './html';
function plaintext(str) {
const escapeHtml = (str) => {
const div = document.createElement('div');
div.innerText = str;
return div.innerHTML;
}
};
const getDocLabel = (doc) => doc.name || doc.longname;
const getInnerText = (html) => {
var div = document.createElement('div');
const stripHtml = (html) => {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent || div.innerText || '';
};
export function Autocomplete({ doc, label }) {
return h`<div class="prose dark:prose-invert max-h-[400px] overflow-auto p-2">
<h1 class="pt-0 mt-0">${label || getDocLabel(doc)}</h1>
${doc.description}
<ul>
${doc.params?.map(
({ name, type, description }) =>
`<li>${name} : ${type.names?.join(' | ')} ${description ? ` - ${getInnerText(description)}` : ''}</li>`,
)}
</ul>
<div>
${doc.examples?.map((example) => `<div><pre>${plaintext(example)}</pre></div>`)}
</div>
</div>`[0];
/*
<pre
className="cursor-pointer"
onMouseDown={(e) => {
console.log('ola!');
navigator.clipboard.writeText(example);
e.stopPropagation();
}}
>
{example}
</pre>
*/
}
const getDocLabel = (doc) => doc.name || doc.longname;
const buildParamsList = (params) =>
params?.length
? `
<div class="autocomplete-info-params-section">
<h4 class="autocomplete-info-section-title">Parameters</h4>
<ul class="autocomplete-info-params-list">
${params
.map(
({ name, type, description }) => `
<li class="autocomplete-info-param-item">
<span class="autocomplete-info-param-name">${name}</span>
<span class="autocomplete-info-param-type">${type.names?.join(' | ')}</span>
${description ? `<div class="autocomplete-info-param-desc">${stripHtml(description)}</div>` : ''}
</li>
`,
)
.join('')}
</ul>
</div>
`
: '';
const buildExamples = (examples) =>
examples?.length
? `
<div class="autocomplete-info-examples-section">
<h4 class="autocomplete-info-section-title">Examples</h4>
${examples
.map(
(example) => `
<pre class="autocomplete-info-example-code">${escapeHtml(example)}</pre>
`,
)
.join('')}
</div>
`
: '';
export const Autocomplete = ({ doc, label }) =>
h`
<div class="autocomplete-info-tooltip">
<h3 class="autocomplete-info-function-name">${label || getDocLabel(doc)}</h3>
${doc.description ? `<p class="autocomplete-info-function-description">${doc.description}</p>` : ''}
${buildParamsList(doc.params)}
${buildExamples(doc.examples)}
</div>
`[0];
const isValidDoc = (doc) => {
const label = getDocLabel(doc);
return label && !label.startsWith('_') && !['package'].includes(doc.kind);
};
const hasExcludedTags = (doc) =>
['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag));
const jsdocCompletions = jsdoc.docs
.filter(
(doc) =>
getDocLabel(doc) &&
!getDocLabel(doc).startsWith('_') &&
!['package'].includes(doc.kind) &&
!['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)),
)
.filter((doc) => isValidDoc(doc) && !hasExcludedTags(doc))
// https://codemirror.net/docs/ref/#autocomplete.Completion
.map((doc) /*: Completion */ => ({
.map((doc) => ({
label: getDocLabel(doc),
// detail: 'xxx', // An optional short piece of information to show (with a different style) after the label.
info: () => Autocomplete({ doc }),
type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type
}));
export const strudelAutocomplete = (context /* : CompletionContext */) => {
let word = context.matchBefore(/\w*/);
if (word.from == word.to && !context.explicit) return null;
export const strudelAutocomplete = (context) => {
const word = context.matchBefore(/\w*/);
if (word.from === word.to && !context.explicit) return null;
return {
from: word.from,
options: jsdocCompletions,
@@ -74,11 +97,5 @@ export const strudelAutocomplete = (context /* : CompletionContext */) => {
};
};
export function isAutoCompletionEnabled(on) {
return on
? [
autocompletion({ override: [strudelAutocomplete] }),
//javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
]
: []; // autocompletion({ override: [] })
}
export const isAutoCompletionEnabled = (enabled) =>
enabled ? [autocompletion({ override: [strudelAutocomplete] })] : [];
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/codemirror",
"version": "1.2.2",
"version": "1.2.3",
"description": "Codemirror Extensions for Strudel",
"main": "index.mjs",
"publishConfig": {
+24 -1
View File
@@ -10,7 +10,7 @@ https://rohandrape.net/?t=hmt
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { timeCat, register, silence } from './pattern.mjs';
import { timeCat, register, silence, stack, pure, _morph } from './pattern.mjs';
import { rotate, flatten, splitAt, zipWith } from './util.mjs';
import Fraction, { lcm } from './fraction.mjs';
@@ -196,3 +196,26 @@ export const euclidLegato = register(['euclidLegato'], function (pulses, steps,
export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) {
return _euclidLegato(pulses, steps, rotation, pat);
});
/**
* A 'euclid' variant with an additional parameter that morphs the resulting
* rhythm from 0 (no morphing) to 1 (completely 'even'). For example
* `sound("bd").euclidish(3,8,0)` would be the same as
* `sound("bd").euclid(3,8)`, and `sound("bd").euclidish(3,8,1)` would be the
* same as `sound("bd bd bd")`. `sound("bd").euclidish(3,8,0.5)` would have a
* groove somewhere between.
* Inspired by the work of Malcom Braff.
* @name euclidish
* @synonyms eish
* @memberof Pattern
* @param {number} pulses the number of onsets
* @param {number} steps the number of steps to fill
* @param {number} groove exists between the extremes of 0 (straight euclidian) and 1 (straight pulse)
* @example
* sound("hh").euclidish(7,12,sine.slow(8))
* .pan(sine.slow(8))
*/
export const { euclidish, eish } = register(['euclidish', 'eish'], function (pulses, steps, perc, pat) {
const morphed = _morph(bjork(pulses, steps), new Array(pulses).fill(1), perc);
return pat.struct(morphed).setSteps(steps);
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/core",
"version": "1.2.2",
"version": "1.2.3",
"description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs",
"type": "module",
+67
View File
@@ -21,6 +21,7 @@ import {
numeralArgs,
parseNumeral,
pairs,
zipWith,
stringifyValues,
} from './util.mjs';
import drawLine from './drawLine.mjs';
@@ -3416,3 +3417,69 @@ export const { beat } = register(
['beat'],
__beat((x) => x.innerJoin()),
);
export const _morph = (from, to, by) => {
by = Fraction(by);
const dur = Fraction(1).div(from.length);
const positions = (list) => {
const result = [];
for (const [pos, value] of list.entries()) {
if (value) {
result.push([Fraction(pos).div(list.length), value]);
}
}
return result;
};
const arcs = zipWith(
([posa, valuea], [posb, valueb]) => {
const b = by.mul(posb - posa).add(posa);
const e = b.add(dur);
return new TimeSpan(b, e);
},
positions(from),
positions(to),
);
function query(state) {
const cycle = state.span.begin.sam();
const cycleArc = state.span.cycleArc();
const result = [];
for (const whole of arcs) {
const part = whole.intersection(cycleArc);
if (part !== undefined) {
result.push(
new Hap(
whole.withTime((x) => x.add(cycle)),
part.withTime((x) => x.add(cycle)),
true,
),
);
}
}
return result;
}
return new Pattern(query).splitQueries();
};
/**
* Takes two binary rhythms represented as lists of 1s and 0s, and a number
* between 0 and 1 that morphs between them. The two lists should contain the same
* number of true values.
* @example
* sound("hh").struct(morph([1,0,1,0,1,0,1,0], // straight rhythm
* [1,1,0,1,0,1,0], // wonky rhythm
* 0.25 // creates a slightly wonky rhythm
* )
* )
* @example
* sound("hh").struct(morph("1:0:1:0:1:0:1:0", // straight rhythm
* "1:1:0:1:0:1:0", // wonky rhythm
* sine.slow(8) // slowly morph between the rhythms
* )
* )
*/
export const morph = (frompat, topat, bypat) => {
frompat = reify(frompat);
topat = reify(topat);
bypat = reify(bypat);
return frompat.innerBind((from) => topat.innerBind((to) => bypat.innerBind((by) => _morph(from, to, by))));
};
+1 -1
View File
@@ -72,7 +72,7 @@ export class TimeSpan {
}
intersection(other) {
// Intersection of two timespans, returns None if they don't intersect.
// Intersection of two timespans, returns undefined if they don't intersect.
const intersect_begin = this.begin.max(other.begin);
const intersect_end = this.end.min(other.end);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/csound",
"version": "1.2.3",
"version": "1.2.4",
"description": "csound bindings for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/draw",
"version": "1.2.2",
"version": "1.2.3",
"description": "Helpers for drawing with Strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/embed",
"version": "1.1.0",
"version": "1.1.1",
"description": "Embeddable Web Component to load a Strudel REPL into an iframe",
"main": "embed.js",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/gamepad",
"version": "1.2.2",
"version": "1.2.3",
"description": "Gamepad Inputs for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/hydra",
"version": "1.2.2",
"version": "1.2.3",
"description": "Hydra integration for strudel",
"main": "hydra.mjs",
"type": "module",
+3
View File
@@ -493,6 +493,9 @@ export async function midin(input) {
otherInputs?.length ? `Also available: ${getMidiDeviceNamesString(otherInputs)}` : ''
}`,
);
}
// ensure refs for this input are initialized
if (!refs[input]) {
refs[input] = {};
}
const cc = (cc) => ref(() => refs[input][cc] || 0);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/midi",
"version": "1.2.3",
"version": "1.2.4",
"description": "Midi API for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/mini",
"version": "1.2.2",
"version": "1.2.3",
"description": "Mini notation for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mondolang",
"version": "1.1.0",
"version": "1.1.1",
"description": "a language for functional composition that translates to js",
"main": "mondo.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/mondo",
"version": "1.1.0",
"version": "1.1.1",
"description": "mondo notation for strudel",
"main": "mondough.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/motion",
"version": "1.2.2",
"version": "1.2.3",
"description": "DeviceMotion API for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/mqtt",
"version": "1.2.2",
"version": "1.2.3",
"description": "MQTT API for strudel",
"main": "mqtt.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/osc",
"version": "1.2.2",
"version": "1.2.3",
"description": "OSC messaging for strudel",
"main": "osc.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/reference",
"version": "1.2.0",
"version": "1.2.1",
"description": "Headless reference of all strudel functions",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/repl",
"version": "1.2.3",
"version": "1.2.4",
"description": "Strudel REPL as a Web Component",
"module": "index.mjs",
"publishConfig": {
+10
View File
@@ -20,3 +20,13 @@ samples('http://localhost:5432')
LOG=1 npx @strudel/sampler # adds logging
PORT=5555 npx @strudel/sampler # changes port
```
## static json
when running with `--json`, you will simply get the json logged back:
```sh
npx --yes @strudel/sampler --json > strudel.json
```
this is useful if you want to create a sample pack from the current folder.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/sampler",
"version": "0.2.0",
"version": "0.2.2",
"description": "",
"keywords": [
"tidalcycles",
+18 -8
View File
@@ -10,14 +10,6 @@ import os from 'os';
// eslint-disable-next-line
const LOG = !!process.env.LOG || false;
console.log(
cowsay.say({
text: 'welcome to @strudel/sampler',
e: 'oO',
T: 'U ',
}),
);
async function getFilesInDirectory(directory) {
let files = [];
const dirents = await readdir(directory, { withFileTypes: true });
@@ -60,8 +52,26 @@ async function getBanks(directory) {
return { banks, files };
}
const args = process.argv.slice(2);
// eslint-disable-next-line
const directory = process.cwd();
if (args.includes('--json')) {
const { banks, files } = await getBanks(directory);
const json = JSON.stringify(banks);
console.log(json);
process.exit(0);
}
console.log(
cowsay.say({
text: 'welcome to @strudel/sampler',
e: 'oO',
T: 'U ',
}),
);
const server = http.createServer(async (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
const { banks, files } = await getBanks(directory);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/serial",
"version": "1.2.2",
"version": "1.2.3",
"description": "Webserial API for strudel",
"main": "serial.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/soundfonts",
"version": "1.2.3",
"version": "1.2.4",
"description": "Soundsfont support for strudel",
"main": "index.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "superdough",
"version": "1.2.3",
"version": "1.2.4",
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/tonal",
"version": "1.2.2",
"version": "1.2.3",
"description": "Tonal functions for strudel",
"main": "index.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/transpiler",
"version": "1.2.2",
"version": "1.2.3",
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/web",
"version": "1.2.3",
"version": "1.2.4",
"description": "Easy to setup, opiniated bundle of Strudel for the browser.",
"module": "web.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/webaudio",
"version": "1.2.3",
"version": "1.2.4",
"description": "Web Audio helpers for Strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/xen",
"version": "1.2.2",
"version": "1.2.3",
"description": "Xenharmonic API for strudel",
"main": "index.mjs",
"type": "module",
+75
View File
@@ -3410,6 +3410,39 @@ exports[`runs examples > example "euclidRot" example index 0 1`] = `
]
`;
exports[`runs examples > example "euclidish" example index 0 1`] = `
[
"[ 0/1 → 1/12 | s:hh pan:0.5 ]",
"[ 13/84 → 5/21 | s:hh pan:0.5606253170575308 ]",
"[ 15/56 → 59/168 | s:hh pan:0.604413082836085 ]",
"[ 71/168 → 85/168 | s:hh pan:0.6629314122869361 ]",
"[ 97/168 → 37/56 | s:hh pan:0.7190455010067492 ]",
"[ 29/42 → 65/84 | s:hh pan:0.7580531369037533 ]",
"[ 71/84 → 13/14 | s:hh pan:0.8080762739548087 ]",
"[ 1/1 → 13/12 | s:hh pan:0.8535533905932737 ]",
"[ 451099417/393511398 → 322594689/262340932 | s:hh pan:0.891768001805729 ]",
"[ 335923379/262340932 → 536677685/393511398 | s:hh pan:0.9222657853371297 ]",
"[ 1122946175/787022796 → 99044284/65585233 | s:hh pan:0.9501869591788796 ]",
"[ 1238122213/787022796 → 651853723/393511398 | s:hh pan:0.9721673436944069 ]",
"[ 335923379/196755699 → 469759583/262340932 | s:hh pan:0.9868472639237561 ]",
"[ 729434777/393511398 → 1524454787/787022796 | s:hh pan:0.9967009321321423 ]",
"[ 2/1 → 25/12 | s:hh pan:1 ]",
"[ 15/7 → 187/84 | s:hh pan:0.9968561049466214 ]",
"[ 16/7 → 199/84 | s:hh pan:0.9874639560909118 ]",
"[ 17/7 → 211/84 | s:hh pan:0.9719416651541839 ]",
"[ 18/7 → 223/84 | s:hh pan:0.9504844339512095 ]",
"[ 19/7 → 235/84 | s:hh pan:0.9233620996141421 ]",
"[ 20/7 → 247/84 | s:hh pan:0.890915741234015 ]",
"[ 3/1 → 37/12 | s:hh pan:0.8535533905932737 ]",
"[ 1238122213/393511398 → 847276553/262340932 | s:hh pan:0.8106731928589048 ]",
"[ 860605243/262340932 → 1323700481/393511398 | s:hh pan:0.7677528833339 ]",
"[ 2696991767/787022796 → 230214750/65585233 | s:hh pan:0.7175585019834292 ]",
"[ 2812167805/787022796 → 1438876519/393511398 | s:hh pan:0.6644931595798675 ]",
"[ 729434777/196755699 → 994441447/262340932 | s:hh pan:0.6139286689554151 ]",
"[ 1516457573/393511398 → 3098500379/787022796 | s:hh pan:0.557342689325327 ]",
]
`;
exports[`runs examples > example "every" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:g3 ]",
@@ -6137,6 +6170,48 @@ exports[`runs examples > example "miditouch" example index 0 1`] = `
]
`;
exports[`runs examples > example "morph" example index 0 1`] = `
[
"[ 0/1 → 1/8 | s:hh ]",
"[ 25/112 → 39/112 | s:hh ]",
"[ 27/56 → 17/28 | s:hh ]",
"[ 83/112 → 97/112 | s:hh ]",
"[ 1/1 → 9/8 | s:hh ]",
"[ 137/112 → 151/112 | s:hh ]",
"[ 83/56 → 45/28 | s:hh ]",
"[ 195/112 → 209/112 | s:hh ]",
"[ 2/1 → 17/8 | s:hh ]",
"[ 249/112 → 263/112 | s:hh ]",
"[ 139/56 → 73/28 | s:hh ]",
"[ 307/112 → 321/112 | s:hh ]",
"[ 3/1 → 25/8 | s:hh ]",
"[ 361/112 → 375/112 | s:hh ]",
"[ 195/56 → 101/28 | s:hh ]",
"[ 419/112 → 433/112 | s:hh ]",
]
`;
exports[`runs examples > example "morph" example index 1 1`] = `
[
"[ 0/1 → 1/8 | s:hh ]",
"[ 11/56 → 9/28 | s:hh ]",
"[ 13/28 → 33/56 | s:hh ]",
"[ 41/56 → 6/7 | s:hh ]",
"[ 1/1 → 9/8 | s:hh ]",
"[ 303934523/262340932 → 673454279/524681864 | s:hh ]",
"[ 188758485/131170466 → 820619173/524681864 | s:hh ]",
"[ 451099417/262340932 → 967784067/524681864 | s:hh ]",
"[ 2/1 → 17/8 | s:hh ]",
"[ 15/7 → 127/56 | s:hh ]",
"[ 17/7 → 143/56 | s:hh ]",
"[ 19/7 → 159/56 | s:hh ]",
"[ 3/1 → 25/8 | s:hh ]",
"[ 828616387/262340932 → 1722818007/524681864 | s:hh ]",
"[ 451099417/131170466 → 1869982901/524681864 | s:hh ]",
"[ 975781281/262340932 → 2017147795/524681864 | s:hh ]",
]
`;
exports[`runs examples > example "mousex" example index 0 1`] = `
[
"[ 0/1 → 1/4 | note:C3 ]",
+1 -2
View File
@@ -8,8 +8,7 @@
"start": "astro dev",
"build": "astro build",
"preview": "astro preview --port 3009 --host 0.0.0.0",
"astro": "astro",
"postinstall": "cp node_modules/hs2js/dist/tree-sitter.wasm public && cp node_modules/hs2js/dist/tree-sitter-haskell.wasm public"
"astro": "astro"
},
"dependencies": {
"@algolia/client-search": "^5.20.0",
@@ -34,11 +34,11 @@ import { JsDoc } from '../../docs/JsDoc';
## arp
<JsDoc client:idle name="Pattern#arp" h={0} />
<JsDoc client:idle name="arp" h={0} />
## arpWith 🧪
<JsDoc client:idle name="Pattern#arpWith" h={0} />
<JsDoc client:idle name="arpWith" h={0} />
## struct
@@ -58,7 +58,7 @@ import { JsDoc } from '../../docs/JsDoc';
## hush
<JsDoc client:idle name="hush" h={0} />
<JsDoc client:idle name="Pattern#hush" h={0} />
## invert
+14
View File
@@ -339,4 +339,18 @@ global effects use the same chain for all events of the same orbit:
<JsDoc client:idle name="phasersweep" h={0} />
## Duck
### duckorbit
<JsDoc client:idle name="duckorbit" h={0} />
### duckattack
<JsDoc client:idle name="duckattack" h={0} />
### duckdepth
<JsDoc client:idle name="duckdepth" h={0} />
Next, we'll look at input / output via [MIDI, OSC and other methods](/learn/input-output).
+14
View File
@@ -178,6 +178,16 @@ the version number).
It is also possible, of course, to just remove it from cache (deleting cache in browser Privacy settings,
or from the dev console if you're technically minded, or by using a cache deleting extension).
## Generating strudel.json
You can use [@strudel/sampler](https://www.npmjs.com/package/@strudel/sampler) to generate a strudel.json file for you, by running:
```sh
npx --yes @strudel/sampler --json > strudel.json
```
See other uses of strudel/sampler further below, under "From Disk via @strudel/sampler".
## Github Shortcut
Because loading samples from github is common, there is a shortcut:
@@ -361,6 +371,10 @@ Sampler effects are functions that can be used to change the behaviour of sample
<JsDoc client:idle name="splice" h={0} />
### scrub
<JsDoc client:idle name="Pattern.scrub" h={0} />{' '}
### speed
<JsDoc client:idle name="speed" h={0} />
+130
View File
@@ -69,3 +69,133 @@
text-decoration: underline 0.18rem;
text-underline-offset: 0.22rem;
}
/* Override default styles from the codemirror inline css for autocomplete info tooltip*/
.cm-tooltip.cm-completionInfo {
padding: 12px !important;
padding-bottom: 12px !important;
border: 1px solid var(--foreground) !important;
border-radius: 4px !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
max-width: 500px !important;
min-width: 300px !important;
max-height: 400px !important;
white-space: normal !important;
overflow: auto !important;
background-color: var(--lineHighlight) !important;
}
/* Main tooltip container */
.autocomplete-info-tooltip {
border-radius: 4px !important;
color: var(--foreground);
font-family: var(--font-family, 'SF Mono', 'Monaco', monospace);
font-size: var(--font-size, 13px);
line-height: 1.4;
max-width: 600px;
max-height: 400px;
min-width: 400px;
}
.autocomplete-info-function-name {
font-size: 15px;
font-weight: 600;
color: var(--foreground);
margin: 0 0 8px 0;
}
.autocomplete-info-function-description {
margin: 0 0 12px 0;
color: var(--foreground);
line-height: 1.5;
opacity: 0.8;
}
.autocomplete-info-section-title {
font-size: 12px;
font-weight: 600;
color: var(--foreground);
margin: 16px 0 6px 0;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.autocomplete-info-section-title:first-child {
margin-top: 0;
}
.autocomplete-info-params-section {
margin-top: 12px;
}
.autocomplete-info-params-list {
list-style: none;
margin: 0;
padding: 0;
}
.autocomplete-info-param-item {
margin-bottom: 8px;
padding: 8px;
background-color: var(--lineBackground);
border-radius: 3px;
border-left: 2px solid var(--foreground, #555);
}
.autocomplete-info-param-item:last-child {
margin-bottom: 0;
}
.autocomplete-info-param-name {
font-weight: 600;
color: var(--variable, var(--foreground));
margin-right: 8px;
}
.autocomplete-info-param-type {
color: var(--comment);
font-size: 12px;
background-color: var(--gutterForeground);
padding: 1px 4px;
border-radius: 2px;
}
.autocomplete-info-param-desc {
color: var(--foreground);
font-size: 10px;
margin-top: 4px;
line-height: 1.4;
opacity: 0.7;
}
.autocomplete-info-examples-section {
margin-top: 12px;
}
.autocomplete-info-example-code {
background: var(--lineBackground);
color: var(--foreground);
padding: 8px;
border-radius: 3px;
font-family: var(--font-family, 'SF Mono', 'Monaco', monospace);
font-size: 12px;
line-height: 1.5;
margin: 4px 0;
overflow-x: auto;
white-space: pre;
border: 1px solid var(--foreground, #3a3a3a);
}
.autocomplete-info-tooltip::-webkit-scrollbar {
width: 4px;
}
.autocomplete-info-tooltip::-webkit-scrollbar-track {
}
.autocomplete-info-tooltip::-webkit-scrollbar-thumb {
border-radius: 2px;
}
.autocomplete-info-tooltip::-webkit-scrollbar-thumb:hover {
}