Merge pull request #199 from tidalcycles/docs-update

document random functions
This commit is contained in:
Felix Roos 2022-09-14 23:46:39 +02:00 committed by GitHub
commit f4c31bcf2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 213 additions and 9 deletions

View File

@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import { Hap } from './hap.mjs';
import { Pattern, fastcat, reify, silence, stack } from './pattern.mjs';
import { Pattern, fastcat, reify, silence, stack, isPattern } from './pattern.mjs';
import Fraction from './fraction.mjs';
import { id } from './util.mjs';
@ -156,8 +156,8 @@ export const chooseInWith = (pat, xs) => {
};
/**
* Chooses randomly from the given list of values.
* @param {...any} xs
* Chooses randomly from the given list of elements.
* @param {...any} xs values / patterns to choose from.
* @returns {Pattern} - a continuous pattern.
*/
export const choose = (...xs) => chooseWith(rand, xs);
@ -183,6 +183,14 @@ Pattern.prototype.choose2 = function (...xs) {
return chooseWith(this._fromBipolar(), xs);
};
/**
* Picks one of the elements at random each cycle.
* @returns {Pattern}
* @example
* chooseCycles("bd", "hh", "sd").s().fast(4).out()
* @example
* "bd | hh | sd".s().fast(4).out()
*/
export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs);
export const randcat = chooseCycles;
@ -223,14 +231,53 @@ Pattern.prototype._degradeByWith = function (withPat, x) {
return this.fmap((a) => (_) => a).appLeft(withPat._filterValues((v) => v > x));
};
/**
* Randomly removes events from the pattern by a given amount.
* 0 = 0% chance of removal
* 1 = 100% chance of removal
*
* @name degradeBy
* @memberof Pattern
* @param {number} amount - a number between 0 and 1
* @returns Pattern
* @example
* s("hh*8").degradeBy(0.2).out()
* @example
* s("[hh?0.2]*8").out()
*/
Pattern.prototype._degradeBy = function (x) {
return this._degradeByWith(rand, x);
};
/**
*
* Randomly removes 50% of events from the pattern. Shorthand for `.degradeBy(0.5)`
*
* @name degrade
* @memberof Pattern
* @returns Pattern
* @example
* s("hh*8").degrade().out()
* @example
* s("[hh?]*8").out()
*/
Pattern.prototype.degrade = function () {
return this._degradeBy(0.5);
};
/**
* Inverse of {@link Pattern#degradeBy}: Randomly removes events from the pattern by a given amount.
* 0 = 100% chance of removal
* 1 = 0% chance of removal
* Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example).
*
* @name undegradeBy
* @memberof Pattern
* @param {number} amount - a number between 0 and 1
* @returns Pattern
* @example
* s("hh*8").undegradeBy(0.2).out()
*/
Pattern.prototype._undegradeBy = function (x) {
return this._degradeByWith(
rand.fmap((r) => 1 - r),
@ -246,6 +293,25 @@ Pattern.prototype._sometimesBy = function (x, func) {
return stack(this._degradeBy(x), func(this._undegradeBy(1 - x)));
};
// https://github.com/tidalcycles/strudel/discussions/198
/* Pattern.prototype._sometimesBy = function (x, other) {
other = typeof other === 'function' ? other(this._undegradeBy(1 - x)) : reify(other)._undegradeBy(1 - x);
return stack(this._degradeBy(x), other);
}; */
/**
*
* Randomly applies the given function by the given probability.
* Similar to {@link Pattern#someCyclesBy}
*
* @name sometimesBy
* @memberof Pattern
* @param {number | Pattern} probability - a number between 0 and 1
* @param {function} function - the transformation to apply
* @returns Pattern
* @example
* s("hh(3,8)").sometimesBy(.4, x=>x.speed("0.5")).out()
*/
Pattern.prototype.sometimesBy = function (patx, func) {
const pat = this;
return reify(patx)
@ -253,6 +319,7 @@ Pattern.prototype.sometimesBy = function (patx, func) {
.innerJoin();
};
// why does this exist? it is identical to sometimesBy
Pattern.prototype._sometimesByPre = function (x, func) {
return stack(this._degradeBy(x), func(this).undegradeBy(1 - x));
};
@ -264,6 +331,17 @@ Pattern.prototype.sometimesByPre = function (patx, func) {
.innerJoin();
};
/**
*
* Applies the given function with a 50% chance
*
* @name sometimes
* @memberof Pattern
* @param {function} function - the transformation to apply
* @returns Pattern
* @example
* s("hh*4").sometimes(x=>x.speed("0.5")).out()
*/
Pattern.prototype.sometimes = function (func) {
return this._sometimesBy(0.5, func);
};
@ -279,6 +357,19 @@ Pattern.prototype._someCyclesBy = function (x, func) {
);
};
/**
*
* Randomly applies the given function by the given probability on a cycle by cycle basis.
* Similar to {@link Pattern#sometimesBy}
*
* @name someCyclesBy
* @memberof Pattern
* @param {number | Pattern} probability - a number between 0 and 1
* @param {function} function - the transformation to apply
* @returns Pattern
* @example
* s("hh(3,8)").someCyclesBy(.3, x=>x.speed("0.5")).out()
*/
Pattern.prototype.someCyclesBy = function (patx, func) {
const pat = this;
return reify(patx)
@ -286,30 +377,100 @@ Pattern.prototype.someCyclesBy = function (patx, func) {
.innerJoin();
};
/**
*
* Shorthand for `.someCyclesBy(0.5, fn)`
*
* @name someCycles
* @memberof Pattern
* @returns Pattern
* @example
* s("hh(3,8)").someCycles(x=>x.speed("0.5")).out()
*/
Pattern.prototype.someCycles = function (func) {
return this._someCyclesBy(0.5, func);
};
/**
*
* Shorthand for `.sometimesBy(0.75, fn)`
*
* @name often
* @memberof Pattern
* @returns Pattern
* @example
* s("hh*8").often(x=>x.speed("0.5")).out()
*/
Pattern.prototype.often = function (func) {
return this.sometimesBy(0.75, func);
};
/**
*
* Shorthand for `.sometimesBy(0.25, fn)`
*
* @name rarely
* @memberof Pattern
* @returns Pattern
* @example
* s("hh*8").rarely(x=>x.speed("0.5")).out()
*/
Pattern.prototype.rarely = function (func) {
return this.sometimesBy(0.25, func);
};
/**
*
* Shorthand for `.sometimesBy(0.1, fn)`
*
* @name almostNever
* @memberof Pattern
* @returns Pattern
* @example
* s("hh*8").almostNever(x=>x.speed("0.5")).out()
*/
Pattern.prototype.almostNever = function (func) {
return this.sometimesBy(0.1, func);
};
/**
*
* Shorthand for `.sometimesBy(0.9, fn)`
*
* @name almostAlways
* @memberof Pattern
* @returns Pattern
* @example
* s("hh*8").almostAlways(x=>x.speed("0.5")).out()
*/
Pattern.prototype.almostAlways = function (func) {
return this.sometimesBy(0.9, func);
};
/**
*
* Shorthand for `.sometimesBy(0, fn)` (never calls fn)
*
* @name never
* @memberof Pattern
* @returns Pattern
* @example
* s("hh*8").never(x=>x.speed("0.5")).out()
*/
Pattern.prototype.never = function (func) {
return this;
};
/**
*
* Shorthand for `.sometimesBy(1, fn)` (always calls fn)
*
* @name always
* @memberof Pattern
* @returns Pattern
* @example
* s("hh*8").always(x=>x.speed("0.5")).out()
*/
Pattern.prototype.always = function (func) {
return func(this);
};

View File

@ -38,7 +38,9 @@ export default defineConfig({
'@codemirror/commands',
'@lezer/highlight',
'@codemirror/language',
'@uiw/codemirror-themes'
'@uiw/codemirror-themes',
'@uiw/react-codemirror',
'@lezer/highlight',
],
},
target: 'esnext',

View File

@ -2,8 +2,8 @@ import { Tone } from '@strudel.cycles/tone';
import { evalScope } from '@strudel.cycles/eval';
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
import controls from '@strudel.cycles/core/controls.mjs';
import * as WebDirt from 'WebDirt';
import { loadWebDirt } from '@strudel.cycles/webdirt';
import { samples } from '@strudel.cycles/webaudio';
export const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.Destination).set({
oscillator: { type: 'triangle' },
@ -12,6 +12,15 @@ export const defaultSynth = new Tone.PolySynth().chain(new Tone.Gain(0.5), Tone.
},
});
samples(
{
bd: '808bd/BD0000.WAV',
sd: ['808sd/SD0010.WAV', '808sd/SD0050.WAV', '808sd/SD0000.WAV'],
hh: ['hh27/000_hh27closedhh.wav', 'hh/000_hh3closedhh.wav'],
},
'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/',
);
evalScope(
Tone,
controls,

View File

@ -177,11 +177,11 @@ In essence, the `x!n` is like a shortcut for `[x*n]@n`.
## Euclidian
Using round brackets, we can create rhythmical sub-divisions based on three parameters: beats, segments and offset.
The first parameter controls how may beats will be played.
The second parameter controls the total amount of segments the beats will be distributed over.
Using round brackets, we can create rhythmical sub-divisions based on three parameters: beats, segments and offset.
The first parameter controls how may beats will be played.
The second parameter controls the total amount of segments the beats will be distributed over.
The third (optional) parameter controls the starting position for distributing the beats.
One popular Euclidian rhythm (going by various names, such as "Pop Clave") is "(3,8,1)" or simply "(3,8)",
One popular Euclidian rhythm (going by various names, such as "Pop Clave") is "(3,8,1)" or simply "(3,8)",
resulting in a rhythmical structure of "x ~ ~ x ~ ~ x ~" (3 beats over 8 segments, starting on position 1).
<MiniRepl tune={`"e5(2,8) b4(3,8) d5(2,8) c5(3,8)".slow(4)`} />
@ -450,6 +450,38 @@ Stacks the given pattern to the current pattern:
<MiniRepl tune={`"c4,eb4,g4".stack("bb4,d5")`} />
## Randomness
These methods add random behavior to your Patterns.
{{ 'chooseCycles' | jsdoc }}
{{ 'Pattern.degradeBy' | jsdoc }}
{{ 'Pattern.degrade' | jsdoc }}
{{ 'Pattern.undegradeBy' | jsdoc }}
{{ 'Pattern.sometimesBy' | jsdoc }}
{{ 'Pattern.sometimes' | jsdoc }}
{{ 'Pattern.someCyclesBy' | jsdoc }}
{{ 'Pattern.someCycles' | jsdoc }}
{{ 'Pattern.often' | jsdoc }}
{{ 'Pattern.rarely' | jsdoc }}
{{ 'Pattern.almostNever' | jsdoc }}
{{ 'Pattern.almostAlways' | jsdoc }}
{{ 'Pattern.never' | jsdoc }}
{{ 'Pattern.always' | jsdoc }}
## Tone API
To make the sounds more interesting, we can use Tone.js instruments ands effects.