diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 6e34c3d0..6b669660 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -62,12 +62,12 @@ const generic_params = [ */ ['f', 'accelerate', 'a pattern of numbers that speed up (or slow down) samples while they play.'], /** - * Like {@link amp}, but exponential. + * Controls the gain by an exponential amount. * * @name gain * @param {number | Pattern} amount gain. * @example - * s("bd*8").gain(".7*2 1 .7*2 1 .7 1").osc() + * s("hh*8").gain(".4!2 1 .4!2 1 .4 1").out() * */ [ @@ -147,10 +147,12 @@ const generic_params = [ /** * a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample. * + * @memberof Pattern * @name begin * @param {number | Pattern} amount between 0 and 1, where 1 is the length of the sample * @example - * s("rave").begin("<0 .25 .5 .75>").osc() + * samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/Dirt-Samples/master/') + * s("rave").begin("<0 .25 .5 .75>").out() * */ [ @@ -159,12 +161,13 @@ const generic_params = [ 'a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample.', ], /** - * The same as {@link begin}, but cuts off the end off each sample. + * The same as .begin, but cuts off the end off each sample. * + * @memberof Pattern * @name end * @param {number | Pattern} length 1 = whole sample, .5 = half sample, .25 = quarter sample etc.. * @example - * s("bd*2,ho*4").end("<.1 .2 .5 1>").osc() + * s("bd*2,ho*4").end("<.1 .2 .5 1>").out() * */ [ diff --git a/packages/core/euclid.mjs b/packages/core/euclid.mjs index c95b3409..96f5b40d 100644 --- a/packages/core/euclid.mjs +++ b/packages/core/euclid.mjs @@ -25,14 +25,19 @@ const euclid = (pulses, steps, rotation = 0) => { * describe a large number of rhythms used in the most important music world traditions. * * @memberof Pattern + * @name euclid * @param {number} pulses the number of onsets / beats * @param {number} steps the number of steps to fill * @param {number} rotation (optional) offset in steps * @returns Pattern - * @example // The Cuban tresillo pattern. - * "c3".euclid(3,8) + * @example + * // The Cuban tresillo pattern. + * n("c3").euclid(3,8).out() + */ + +/** * @example // A thirteenth century Persian rhythm called Khafif-e-ramal. - * "c3".euclid(2,5) + * n("c3").euclid(2,5) * @example // The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad. * "c3".euclid(3,4) * @example // Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm. @@ -79,7 +84,11 @@ Pattern.prototype.euclid = function (pulses, steps, rotation = 0) { }; /** - * Similar to {@link Pattern#euclid}, but each pulse is held until the next pulse, so there will be no gaps. + * Similar to `.euclid`, but each pulse is held until the next pulse, so there will be no gaps. + * @name euclidLegato + * @memberof Pattern + * @example + * n("g2").decay(.1).sustain(.3).euclidLegato(3,8).out() */ Pattern.prototype.euclidLegato = function (pulses, steps, rotation = 0) { const bin_pat = euclid(pulses, steps, rotation); diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index ec7bc19c..c5c77761 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -468,7 +468,11 @@ export class Pattern { /** * Assumes a numerical pattern. Returns a new pattern with all values rounded * to the nearest integer. + * @name round + * @memberof Pattern * @returns Pattern + * @example + * "0.5 1.5 2.5".round().scale('C major') */ round() { return this._asNumber().fmap((v) => Math.round(v)); @@ -695,6 +699,13 @@ export class Pattern { return this.fmap(func)._squeezeJoin(); } + /** + * Like layer, but with a single function: + * @name apply + * @memberof Pattern + * @example + * "".scale('C minor').apply(scaleTranspose("0,2,4")) + */ _apply(func) { return func(this); } @@ -772,7 +783,7 @@ export class Pattern { _focus(b, e) { return this._fast(Fraction(1).div(e.sub(b))).late(b.cyclePos()); } - + _focusSpan(span) { return this._focus(span.begin, span.end); } @@ -818,6 +829,20 @@ export class Pattern { return this.fmap((x) => pure(x)._fast(factor))._squeezeJoin(); } + /** + * Cuts each sample into the given number of parts, allowing you to explore a technique known as 'granular synthesis'. + * It turns a pattern of samples into a pattern of parts of samples. + * @name chop + * @memberof Pattern + * @returns Pattern + * @example + * samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' }) + * s("rhodes") + * .chop(4) + * .rev() // reverse order of chops + * .loopAt(4,1) // fit sample into 4 cycles + * .out() + */ _chop(n) { const slices = Array.from({ length: n }, (x, i) => i); const slice_objects = slices.map((i) => ({ begin: i / n, end: (i + 1) / n })); @@ -1189,6 +1214,9 @@ export class Pattern { * @name echo * @memberof Pattern * @returns Pattern + * @param {number} times how many times to repeat + * @param {number} time cycle offset between iterations + * @param {number} feedback velocity multiplicator for each iteration * @example * s("bd sd").echo(3, 1/6, .8).out() */ @@ -1261,16 +1289,40 @@ export class Pattern { return this.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(value))); } - // sets hap relative duration of haps + /** + * + * Multiplies the hap duration with the given factor. + * @name legato + * @memberof Pattern + * @example + * n("c3 eb3 g3 c4").legato("<.25 .5 1 2>").out() + */ _legato(value) { return this.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value)))); } + /** + * + * Sets the velocity from 0 to 1. Is multiplied together with gain. + * @name velocity + * @example + * s("hh*8") + * .gain(".4!2 1 .4!2 1 .4 1") + * .velocity(".4 1").out() + */ _velocity(velocity) { return this._withContext((context) => ({ ...context, velocity: (context.velocity || 1) * velocity })); } - // move this to controls? (speed and unit are controls) + /** + * Makes the sample fit the given number of cycles by changing the speed. + * @name loopAt + * @memberof Pattern + * @returns Pattern + * @example + * samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' }) + * s("rhodes").loopAt(4,1).out() + */ _loopAt(factor, cps = 1) { return this.speed((1 / factor) * cps) .unit('c') @@ -1326,9 +1378,48 @@ function _composeOp(a, b, func) { keepif: [(a, b) => (b ? a : undefined)], // numerical functions + /** + * + * Assumes a pattern of numbers. Adds the given number to each item in the pattern. + * @name add + * @memberof Pattern + * @example + * // Here, the triad 0, 2, 4 is shifted by different amounts + * "0 2 4".add("<0 3 4 0>").scale('C major') + * // Without add, the equivalent would be: + * // "<[0 2 4] [3 5 7] [4 6 8] [0 2 4]>".scale('C major') + * @example + * // You can also use add with notes: + * "c3 e3 g3".add("<0 5 7 0>") + * // Behind the scenes, the notes are converted to midi numbers: + * // "48 52 55".add("<0 5 7 0>") + */ add: [(a, b) => a + b, numOrString], // support string concatenation + /** + * + * Like add, but the given numbers are subtracted. + * @name sub + * @memberof Pattern + * @example + * "0 2 4".sub("<0 1 2 3>").scale('C4 minor') + * // See add for more information. + */ sub: [(a, b) => a - b, num], + /** + * + * Multiplies each number by the given factor. + * @name mul + * @memberof Pattern + * @example + * "1 1.5 [1.66, <2 2.33>]".mul(150).freq().out() + */ mul: [(a, b) => a * b, num], + /** + * + * Divides each number by the given factor. + * @name div + * @memberof Pattern + */ div: [(a, b) => a / b, num], mod: [mod, num], pow: [Math.pow, num], @@ -1370,8 +1461,7 @@ function _composeOp(a, b, func) { // avoid union, as we want to throw away the value of 'b' completely result = pat['_op' + how](other, (a) => (b) => op(a, b)); result = result._removeUndefineds(); - } - else { + } else { result = pat['_op' + how](other, (a) => (b) => _composeOp(a, b, op)); } return result; diff --git a/packages/webaudio/reverb.mjs b/packages/webaudio/reverb.mjs index 10e6dcd1..341731e4 100644 --- a/packages/webaudio/reverb.mjs +++ b/packages/webaudio/reverb.mjs @@ -18,3 +18,6 @@ if (typeof AudioContext !== 'undefined') { return convolver; }; } + +// TODO: make the reverb more exciting +// check out https://blog.gskinner.com/archives/2019/02/reverb-web-audio-api.html diff --git a/tutorial/Tutorial.jsx b/tutorial/Tutorial.jsx index f41489a4..66117b46 100644 --- a/tutorial/Tutorial.jsx +++ b/tutorial/Tutorial.jsx @@ -13,9 +13,9 @@ import '@strudel.cycles/react/dist/style.css'; ReactDOM.render( -
-
-
+
+
+
logo

window.scrollTo(0, 0)}> @@ -23,15 +23,14 @@ ReactDOM.render(

{!window.location.href.includes('localhost') && ( -
+ )}
-
+
- {/* */}
, diff --git a/tutorial/tutorial.mdx b/tutorial/tutorial.mdx index 61c6e398..bee7bef4 100644 --- a/tutorial/tutorial.mdx +++ b/tutorial/tutorial.mdx @@ -365,10 +365,24 @@ note("g2!2 !2, g4 f4]>") The sampler will always pick the closest matching sample for the current note! -## Effects +## Sampler Effects + +{{ 'Pattern.begin' | jsdoc }} + +{{ 'Pattern.end' | jsdoc }} + +{{ 'Pattern.loopAt' | jsdoc }} + +{{ 'Pattern.chop' | jsdoc }} + +## Audio Effects Wether you're using a synth or a sample, you can apply these effects: +{{ 'gain' | jsdoc }} + +{{ 'velocity' | jsdoc }} + {{ 'cutoff' | jsdoc }} {{ 'resonance' | jsdoc }} @@ -496,11 +510,15 @@ The following functions modify a pattern temporal structure in some way. {{ 'Pattern.late' | jsdoc }} -{{ 'Pattern.rev' | jsdoc }} +{{ 'Pattern.legato' | jsdoc }} {{ 'Pattern.struct' | jsdoc }} -{{ 'Pattern.legato' | jsdoc }} +{{ 'Pattern.euclid' | jsdoc }} + +{{ 'Pattern.euclidLegato' | jsdoc }} + +{{ 'Pattern.rev' | jsdoc }} {{ 'Pattern.iter' | jsdoc }} @@ -536,63 +554,17 @@ The following functions modify a pattern temporal structure in some way. ## Value Modifiers -### add(n) +{{ 'Pattern.add' | jsdoc }} -Adds the given number to each item in the pattern: +{{ 'Pattern.sub' | jsdoc }} -").scale('C major')`} /> +{{ 'Pattern.mul' | jsdoc }} -Here, the triad `0, 2, 4` is shifted by different amounts. Without add, the equivalent would be: +{{ 'Pattern.div' | jsdoc }} -".scale('C major')`} /> +{{ 'Pattern.round' | jsdoc }} -You can also use add with notes: - -")`} /> - -Behind the scenes, the notes are converted to midi numbers as soon before add is applied, which is equivalent to: - -")`} /> - -### sub(n) - -Like add, but the given numbers are subtracted: - -").scale('C4 minor')`} /> - -See add for more information. - -### mul(n) - -Multiplies each number by the given factor: - -").scale('C4 minor')`} /> - -... is equivalent to: - -".scale('C4 minor')`} /> - -This function is really useful in combination with signals: - - - -Here, we sample a sine wave 16 times, and multiply each sample by 7. This way, we let values oscillate between 0 and 7. - -### div(n) - -Like mul, but dividing by the given number. - -### round() - -Rounds all values to the nearest integer: - - - -### apply(func) - -Like layer, but with a single function: - -".scale('C minor').apply(scaleTranspose("0,2,4"))`} /> +{{ 'Pattern.apply' | jsdoc }} {{ 'Pattern.range' | jsdoc }}