diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 48e52643..9ff616cf 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -513,11 +513,14 @@ export class Pattern { } /** - * Assumes a numerical pattern, containing unipolar values in the range 0 .. - * 1. Returns a new pattern with values scaled to the given min/max range. - * @param {Number} min - * @param {Number} max + * Assumes a numerical pattern, containing unipolar values in the range 0 .. 1. + * Returns a new pattern with values scaled to the given min/max range. + * Most useful in combination with continuous patterns. + * @name range + * @memberof Pattern * @returns Pattern + * @example + * s("bd sd,hh*4").cutoff(sine.range(500,2000).slow(4)).out() */ range(min, max) { return this.mul(max - min).add(min); @@ -683,6 +686,16 @@ export class Pattern { return func(this); } + /** + * Layers the result of the given function(s). Like {@link superimpose}, but without the original pattern: + * @name layer + * @memberof Pattern + * @returns Pattern + * @example + * "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4" + * .layer(x=>x.add("0,2")) + * .scale('C minor').note().out() + */ layer(...funcs) { return stack(...funcs.map((func) => func(this))); } @@ -734,14 +747,14 @@ export class Pattern { } /** - * Speed up a pattern by the given factor. + * Speed up a pattern by the given factor. Used by "*" in mini notation. * * @name fast * @memberof Pattern * @param {number | Pattern} factor speed up factor * @returns Pattern * @example - * seq(e5, b4, d5, c5).fast(2) + * s(" hh").fast(2).out() // s("[ hh]*2").out() */ _fast(factor) { const fastQuery = this.withQueryTime((t) => t.mul(factor)); @@ -749,14 +762,14 @@ export class Pattern { } /** - * Slow down a pattern over the given number of cycles. + * Slow down a pattern over the given number of cycles. Like the "/" operator in mini notation. * * @name slow * @memberof Pattern * @param {number | Pattern} factor slow down factor * @returns Pattern * @example - * seq(e5, b4, d5, c5).slow(2) + * s(" hh").slow(2).out() // s("[ hh]/2").out() */ _slow(factor) { return this._fast(Fraction(1).div(factor)); @@ -795,14 +808,32 @@ export class Pattern { return this._fast(cpm / 60); } + /** + * Nudge a pattern to start earlier in time. Equivalent of Tidal's <~ operator + * + * @name early + * @memberof Pattern + * @param {number | Pattern} cycles number of cycles to nudge left + * @returns Pattern + * @example + * "bd ~".stack("hh ~".early(.1)).s().out() + */ _early(offset) { - // Equivalent of Tidal's <~ operator offset = Fraction(offset); return this.withQueryTime((t) => t.add(offset)).withHapTime((t) => t.sub(offset)); } + /** + * Nudge a pattern to start later in time. Equivalent of Tidal's ~> operator + * + * @name late + * @memberof Pattern + * @param {number | Pattern} cycles number of cycles to nudge right + * @returns Pattern + * @example + * "bd ~".stack("hh ~".late(.1)).s().out() + */ _late(offset) { - // Equivalent of Tidal's ~> operator offset = Fraction(offset); return this._early(Fraction(0).sub(offset)); } @@ -829,6 +860,17 @@ export class Pattern { return this._zoom(0, t)._slow(t); } + /** + * Applies the given structure to the pattern: + * + * @name struct + * @memberof Pattern + * @returns Pattern + * @example + * "c3,eb3,g3" + * .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~") + * .slow(4).note().out() + */ // struct(...binary_pats) { // // Re structure the pattern according to a binary pattern (false values are dropped) // const binary_pat = sequence(binary_pats); @@ -876,6 +918,16 @@ export class Pattern { return this.invert(); } + /** + * Applies the given function whenever the given pattern is in a true state. + * @name when + * @memberof Pattern + * @param {Pattern} binary_pat + * @param {function} func + * @returns Pattern + * @example + * "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5)) + */ when(binary_pat, func) { //binary_pat = sequence(binary_pat) const true_pat = binary_pat._filterValues(id); @@ -885,10 +937,47 @@ export class Pattern { return stack(with_pat, without_pat); } + /** + * Superimposes the function result on top of the original pattern, delayed by the given time. + * @name off + * @memberof Pattern + * @param {Pattern | number} time offset time + * @param {function} func function to apply + * @returns Pattern + * @example + * "c3 eb3 g3".off(1/8, x=>x.add(7)) + */ off(time_pat, func) { return stack(this, func(this.late(time_pat))); } + /** + * Applies the given function every n cycles. + * @name every + * @memberof Pattern + * @param {number} n how many cycles + * @param {function} func function to apply + * @returns Pattern + * @example + * note("c3 d3 e3 g3").every(4, x=>x.rev()).out() + */ + every(n, func) { + const pat = this; + const pats = Array(n - 1).fill(pat); + // pats.unshift(func(pat)); + pats.push(func(pat)); + return slowcatPrime(...pats); + } + /** + * Applies the given function every n cycles, starting from the first cycle. + * @name every + * @memberof Pattern + * @param {number} n how many cycles + * @param {function} func function to apply + * @returns Pattern + * @example + * note("c3 d3 e3 g3").every(4, x=>x.rev()).out() + */ every(n, func) { const pat = this; const pats = Array(n - 1).fill(pat); @@ -896,6 +985,23 @@ export class Pattern { return slowcatPrime(...pats); } + /** + * Applies the given function every n cycles, starting from the last cycle. + * @name each + * @memberof Pattern + * @param {number} n how many cycles + * @param {function} func function to apply + * @returns Pattern + * @example + * note("c3 d3 e3 g3").every(4, x=>x.rev()).out() + */ + each(n, func) { + const pat = this; + const pats = Array(n - 1).fill(pat); + pats.push(func(pat)); + return slowcatPrime(...pats); + } + /** * Returns a new pattern where every other cycle is played once, twice as * fast, and offset in time by one quarter of a cycle. Creates a kind of @@ -906,6 +1012,15 @@ export class Pattern { return this.when(slowcat(false, true), (x) => fastcat(x, silence)._late(0.25)); } + /** + * Reverse all haps in a pattern + * + * @name rev + * @memberof Pattern + * @returns Pattern + * @example + * "c3 d3 e3 g3".rev() + */ rev() { const pat = this; const query = function (state) { @@ -948,6 +1063,15 @@ export class Pattern { return this.juxBy(1, func); } + /** + * Stacks the given pattern(s) to the current pattern. + * @name stack + * @memberof Pattern + * @example + * s("hh*2").stack( + * n("c2(3,8)") + * ).out() + */ stack(...pats) { return stack(this, ...pats); } @@ -956,11 +1080,28 @@ export class Pattern { return sequence(this, ...pats); } - // shorthand for sequence + /** + * Appends the given pattern(s) to the current pattern. Synonyms: .sequence .fastcat + * @name seq + * @memberof Pattern + * @example + * s("hh*2").seq( + * n("c2(3,8)") + * ).out() + */ seq(...pats) { return sequence(this, ...pats); } + /** + * Appends the given pattern(s) to the next cycle. Synonym: .slowcat + * @name cat + * @memberof Pattern + * @example + * s("hh*2").cat( + * n("c2(3,8)") + * ).out() + */ cat(...pats) { return cat(this, ...pats); } @@ -973,6 +1114,16 @@ export class Pattern { return slowcat(this, ...pats); } + /** + * Superimposes the result of the given function(s) on top of the original pattern: + * @name superimpose + * @memberof Pattern + * @returns Pattern + * @example + * "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4" + * .superimpose(x=>x.add(2)) + * .scale('C minor').note().out() + */ superimpose(...funcs) { return this.stack(...funcs.map((func) => func(this))); } @@ -1184,6 +1335,11 @@ Pattern.prototype.patternified = [ 'slow', 'velocity', ]; + +// aliases +export const polyrhythm = stack; +export const pr = stack; + // methods that create patterns, which are added to patternified Pattern methods Pattern.prototype.factories = { pure, @@ -1206,12 +1362,11 @@ Pattern.prototype.factories = { // Nothing export const silence = new Pattern((_) => []); -/** A discrete value that repeats once per cycle: +/** A discrete value that repeats once per cycle. * - * @param {any} value - The value to repeat * @returns {Pattern} * @example - * pure('e4') + * pure('e4') // "e4" */ export function pure(value) { function query(state) { @@ -1241,12 +1396,11 @@ export function reify(thing) { return pure(thing); } -/** The given items are played at the same time at the same length: +/** The given items are played at the same time at the same length. * - * @param {...any} items - The items to stack * @return {Pattern} * @example - * stack(g3, b3, [e4, d4]) + * stack(g3, b3, [e4, d4]) // "g3,b3,[e4,d4]" */ export function stack(...pats) { // Array test here is to avoid infinite recursions.. @@ -1259,7 +1413,6 @@ export function stack(...pats) { * * synonyms: {@link cat} * - * @param {...any} items - The items to concatenate * @return {Pattern} * @example * slowcat(e5, b4, [d5, c5]) @@ -1315,16 +1468,22 @@ export function fastcat(...pats) { return slowcat(...pats)._fast(pats.length); } -/** See {@link slowcat} */ +/** The given items are con**cat**enated, where each one takes one cycle. Synonym: slowcat + * + * @param {...any} items - The items to concatenate + * @return {Pattern} + * @example + * cat(e5, b4, [d5, c5]) // "" + * + */ export function cat(...pats) { return slowcat(...pats); } -/** Like {@link fastcat}, but where each step has a temporal weight: - * @param {...Array} items - The items to concatenate +/** Like {@link seq}, but each step has a length, relative to the whole. * @return {Pattern} * @example - * timeCat([3,e3],[1, g3]) + * timeCat([3,e3],[1, g3]) // "e3@3 g3" */ export function timeCat(...timepats) { const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0)); @@ -1343,7 +1502,11 @@ export function sequence(...pats) { return fastcat(...pats); } -/** See {@link fastcat} */ +/** Like **cat**, but the items are crammed into one cycle. Synonyms: fastcat, sequence + * @example + * seq(e5, b4, [d5, c5]) // "e5 b4 [d5 c5]" + * + */ export function seq(...pats) { return fastcat(...pats); } @@ -1392,20 +1555,6 @@ export function pm(...args) { polymeter(...args); } -export function polyrhythm(...xs) { - const seqs = xs.map((a) => sequence(a)); - - if (seqs.length == 0) { - return silence; - } - return stack(...seqs); -} - -// alias -export function pr(args) { - polyrhythm(args); -} - export const add = curry((a, pat) => pat.add(a)); export const chop = curry((a, pat) => pat.chop(a)); export const chunk = curry((a, pat) => pat.chunk(a)); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 9a4b5fe5..8637c30e 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -74,7 +74,7 @@ export const square2 = square._toBipolar(); * * @return {Pattern} * @example - * triangle.segment(2).range(0,7).scale('C minor') + * tri.segment(8).range(0,7).scale('C minor') * */ export const tri = fastcat(isaw, saw); @@ -111,7 +111,17 @@ const timeToRandsPrime = (seed, n) => { const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n); /** - * A continuous pattern of random numbers, between 0 and 1 + * + */ + +/** + * A continuous pattern of random numbers, between 0 and 1. + * + * @name rand + * @example + * // randomly change the cutoff + * s("bd sd,hh*4").cutoff(rand.range(500,2000)).out() + * */ export const rand = signal(timeToRand); /** @@ -124,6 +134,17 @@ export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin(); export const brand = _brandBy(0.5); export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i)); + +/** + * A continuous pattern of random integers, between 0 and n-1. + * + * @name irand + * @param {number} n max value (exclusive) + * @example + * // randomly select scale notes from 0 - 7 (= C to C) + * irand(8).struct("x(3,8)").scale('C minor').note().out() + * + */ export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin(); export const __chooseWith = (pat, xs) => { @@ -225,6 +246,15 @@ export const perlinWith = (pat) => { return pat.sub(pata).fmap(interp).appBoth(pata.fmap(timeToRand)).appBoth(patb.fmap(timeToRand)); }; +/** + * Generates a continuous pattern of [perlin noise](https://en.wikipedia.org/wiki/Perlin_noise), in the range 0..1. + * + * @name perlin + * @example + * // randomly change the cutoff + * s("bd sd,hh*4").cutoff(perlin.range(500,2000)).out() + * + */ export const perlin = perlinWith(time); Pattern.prototype._degradeByWith = function (withPat, x) { diff --git a/tutorial/tutorial.mdx b/tutorial/tutorial.mdx index 23fbbc2d..09990cc2 100644 --- a/tutorial/tutorial.mdx +++ b/tutorial/tutorial.mdx @@ -413,65 +413,21 @@ The above is the same as: Using strings, you can also use "#". -## Functions that create Patterns +## Pattern Factories -The following functions will return a pattern. We will see later what that means. +The following functions will return a pattern. -## pure(value) - -To create a pattern from a value, you can wrap the value in pure: - - +{{ 'pure' | jsdoc }} Most of the time, you won't need that function as input values of pattern creating functions are purified by default. -### cat(...values) +{{ 'cat' | jsdoc }} -The given items are con**cat**enated, where each one takes one cycle: +{{ 'seq' | jsdoc }} - +{{ 'stack' | jsdoc }} -- Square brackets will create a subsequence -- The function **slowcat** does the same as **cat**. - -### seq(...values) - -Like **cat**, but the items are crammed into one cycle: - - - -- Synonyms: **fastcat**, **sequence** - -### stack(...values) - -The given items are played at the same time at the same length: - - - -- Square Brackets will create a subsequence - -### Nesting functions - -You can nest functions inside one another: - - - -The above is equivalent to - -"`} /> - -### timeCat(...[weight,value]) - -Like with "@" in mini notation, we can specify weights to the items in a sequence: - - +{{ 'timeCat' | jsdoc }} + -The following functions modify a pattern. +## Combining Patterns -### slow(factor) +You can freely mix JS patterns, mini patterns and values! For example, this pattern: -Like "/" in mini notation, **slow** will slow down a pattern over the given number of cycles: + - +...is equivalent to: -The same in mini notation: + - +... as well as: -### fast(factor) +"`} /> -Like "\*" in mini notation, **fast** will play a pattern times the given number in one cycle: +While mini notation is almost always shorter, it only has a handful of modifiers: \* / ! @. +When using JS patterns, there is a lot more you can do. - +## Time Modifiers -### early(cycles) +The following functions modify a pattern temporal structure in some way. -With early, you can nudge a pattern to start earlier in time: +{{ 'Pattern.slow' | jsdoc }} - +{{ 'Pattern.fast' | jsdoc }} -### late(cycles) +{{ 'Pattern.early' | jsdoc }} -Like early, but in the other direction: +{{ 'Pattern.late' | jsdoc }} - +{{ 'Pattern.rev' | jsdoc }} - +{{ 'Pattern.struct' | jsdoc }} -### rev() +{{ 'Pattern.legato' | jsdoc }} -Will reverse the pattern: +## Conditional Modifiers - +{{ 'Pattern.every' | jsdoc }} -### every(n, func) +{{ 'Pattern.each' | jsdoc }} -Will apply the given function every n cycles: +{{ 'Pattern.when' | jsdoc }} - +## Accumulation Modifiers - +{{ 'Pattern.stack' | jsdoc }} -Note that late is called directly. This is a shortcut for: +{{ 'Pattern.superimpose' | jsdoc }} - x.late(0.5)))`} /> +{{ 'Pattern.layer' | jsdoc }} - +{{ 'Pattern.off' | jsdoc }} + +## Concat Modifiers + +{{ 'Pattern.seq' | jsdoc }} + +{{ 'Pattern.cat' | jsdoc }} + +## Value Modifiers ### add(n) @@ -597,56 +580,42 @@ Rounds all values to the nearest integer: -### struct(binary_pat) - -Applies the given structure to the pattern: - - - -This is also useful to sample signals: - - - -### when(binary_pat, func) - -Applies the given function whenever the given pattern is in a true state. - -/2", sub(5))`} /> - -### superimpose(...func) - -Superimposes the result of the given function(s) on top of the original pattern: - -".scale('C minor').superimpose(scaleTranspose("2,4"))`} /> - -### layer(...func) - -Layers the result of the given function(s) on top of each other. Like superimpose, but the original pattern is not part of the result. - -".scale('C minor').layer(scaleTranspose("0,2,4"))`} /> - ### apply(func) Like layer, but with a single function: ".scale('C minor').apply(scaleTranspose("0,2,4"))`} /> -### off(time, func) +{{ 'Pattern.range' | jsdoc }} -Applies the given function by the given time offset: +## Continuous Signals - +Signals are patterns with continuous values, meaning they have theoretically infinite steps. +They can provide streams of numbers that can be sampled at discrete points in time. -### stack(pat) +## -Stacks the given pattern to the current pattern: +{{ 'saw' | jsdoc }} - +{{ 'sine' | jsdoc }} -## Randomness +{{ 'cosine' | jsdoc }} + +{{ 'tri' | jsdoc }} + +{{ 'square' | jsdoc }} + +### Ranges from -1 to 1 + +There is also `saw2`, `sine2`, `cosine2`, `tri2` and `square2` which have a range from -1 to 1! + +{{ 'rand' | jsdoc }} + +{{ 'perlin' | jsdoc }} + +{{ 'irand' | jsdoc }} + +## Random Modifiers These methods add random behavior to your Patterns. @@ -678,11 +647,186 @@ These methods add random behavior to your Patterns. {{ 'Pattern.always' | jsdoc }} -## Tone API +# Tonal API -To make the sounds more interesting, we can use Tone.js instruments ands effects. +The Tonal API, uses [tonaljs](https://github.com/tonaljs/tonal) to provide helpers for musical operations. -[Show Source on Github](https://github.com/tidalcycles/strudel/blob/main/repl/src/tone.ts) +### transpose(semitones) + +Transposes all notes to the given number of semitones: + +".slow(2)).transpose(0)`} /> + +This method gets really exciting when we use it with a pattern as above. + +Instead of numbers, scientific interval notation can be used as well: + +".slow(2)).transpose(1)`} /> + +### scale(name) + +Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranpose. + + + +Note that the scale root is octaved here. You can also omit the octave, then index zero will default to octave 3. + +All the available scale names can be found [here](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts). + +### scaleTranspose(steps) + +Transposes notes inside the scale by the number of steps: + +")`} +/> + +### voicings(range?) + +Turns chord symbols into voicings, using the smoothest voice leading possible: + +".voicings(), "")`} /> + + + +### rootNotes(octave = 2) + +Turns chord symbols into root notes of chords in given octave. + +".rootNotes(3)`} /> + +Together with layer, struct and voicings, this can be used to create a basic backing track: + +".layer( + x => x.voicings(['d3','g4']).struct("~ x"), + x => x.rootNotes(2).tone(synth(osc('sawtooth4')).chain(out())) +)`} +/> + + + + +## Microtonal API + +TODO + +## MIDI API + +Strudel also supports midi via [webmidi](https://npmjs.com/package/webmidi). + +### midi(outputName?) + +Make sure to have a midi device connected or to use an IAC Driver. +If no outputName is given, it uses the first midi output it finds. + +Midi is currently not supported by the mini repl used here, but you can [open the midi example in the repl](https://strudel.tidalcycles.org/#c3RhY2soIjxDXjcgQTcgRG03IEc3PiIubS52b2ljaW5ncygpLCAnPEMzIEEyIEQzIEcyPicubSkKICAubWlkaSgp). + +In the REPL, you will se a log of the available MIDI devices. + + + +# Superdirt API + +In mainline tidal, the actual sound is generated via Superdirt, which runs inside Supercollider. +Strudel also supports using Superdirt as a backend, although it requires some developer tooling to run. + +## Prequisites + +Getting Superdirt to work with Strudel, you need to + +1. install SuperCollider + sc3 plugins, see [Tidal Docs](https://tidalcycles.org/docs/) (Install Tidal) for more info. +2. install [node.js](https://nodejs.org/en/) +3. download [Strudel Repo](https://github.com/tidalcycles/strudel/) (or git clone, if you have git installed) +4. run `npm i` in the strudel directory +5. run `npm run osc` to start the osc server, which forwards OSC messages from Strudel REPL to SuperCollider + +Now you're all set! + +## Usage + +1. Start SuperCollider, either using SuperCollider IDE or by running `sclang` in a terminal +2. Open the [Strudel REPL](https://strudel.tidalcycles.org/#cygiYmQgc2QiKS5vc2MoKQ%3D%3D) + +...or test it here: + + + +If you now hear sound, congratulations! If not, you can get help on the [#strudel channel in the TidalCycles discord](https://discord.com/invite/HGEdXmRkzT). + +{{ 'Pattern.osc' | jsdoc }} + +## Superdirt Params + +The following functions can be used with superdirt: + +- s +- n +- freq +- channel +- orbit +- cutoff +- resonance +- hcutoff +- hresonance +- bandf +- bandq +- djf +- vowelSa +- cut +- begin +- end +- loop +- fadeTime +- speed +- unitA +- gain +- amp +- accelerate +- crush +- coarse +- delay +- lock +- leslie +- lrate +- lsize +- pan +- panspan +- pansplay +- room +- size +- dry +- shape +- squiz +- waveloss +- attack +- decayS +- octave +- detune +- tremolodepth + +Please refer to [Tidal Docs](https://tidalcycles.org/) for more info. + +# Webdirt API (deprecated) + +You can use the powerful sampling engine [Webdirt](https://github.com/dktr0/WebDirt) with Strudel. + +{{ 'Pattern.webdirt' | jsdoc }} + +
+
+ +# Tone API (deprecated) + +The Tone API uses Tone.js instruments ands effects to create sounds. - -## Tonal API - -The Tonal API, uses [tonaljs](https://github.com/tonaljs/tonal) to provide helpers for musical operations. - -### transpose(semitones) - -Transposes all notes to the given number of semitones: - -".slow(2)).transpose(0)`} /> - -This method gets really exciting when we use it with a pattern as above. - -Instead of numbers, scientific interval notation can be used as well: - -".slow(2)).transpose(1)`} /> - -### scale(name) - -Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranpose. - - - -Note that the scale root is octaved here. You can also omit the octave, then index zero will default to octave 3. - -All the available scale names can be found [here](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts). - -### scaleTranspose(steps) - -Transposes notes inside the scale by the number of steps: - -")`} -/> - -### voicings(range?) - -Turns chord symbols into voicings, using the smoothest voice leading possible: - -".voicings(), "")`} /> - - - -### rootNotes(octave = 2) - -Turns chord symbols into root notes of chords in given octave. - -".rootNotes(3)`} /> - -Together with layer, struct and voicings, this can be used to create a basic backing track: - -".layer( - x => x.voicings(['d3','g4']).struct("~ x"), - x => x.rootNotes(2).tone(synth(osc('sawtooth4')).chain(out())) -)`} -/> - - - - -## Microtonal API - -TODO - -## MIDI API - -Strudel also supports midi via [webmidi](https://npmjs.com/package/webmidi). - -### midi(outputName?) - -Make sure to have a midi device connected or to use an IAC Driver. -If no outputName is given, it uses the first midi output it finds. - -Midi is currently not supported by the mini repl used here, but you can [open the midi example in the repl](https://strudel.tidalcycles.org/#c3RhY2soIjxDXjcgQTcgRG03IEc3PiIubS52b2ljaW5ncygpLCAnPEMzIEEyIEQzIEcyPicubSkKICAubWlkaSgp). - -In the REPL, you will se a log of the available MIDI devices. - - - -# Contributing - -Contributions of any sort are very welcome! You can contribute by editing [this file](https://github.com/tidalcycles/strudel/blob/main/repl/src/tutorial/tutorial.mdx). -All you need is a github account. - -If you want to run the tutorial locally, you can clone the and run: - -```sh -cd repl && npm i && npm run tutorial -``` - -If you want to contribute in another way, either - -- [fork strudel repo on GitHub](https://github.com/tidalcycles/strudel) -- [Join the Discord Channel](https://discord.gg/remJ6gQA) -- [play with the Strudel REPL](https://strudel.tidalcycles.org/) - -
-
- -# API Docs - -The following is generated from the source documentation. - -## Pattern Factories - -The following functions will return a pattern. We will see later what that means. - -{{ 'pure' | jsdoc }} - -{{ 'slowcat' | jsdoc }} - -{{ 'fastcat' | jsdoc }} - -{{ 'stack' | jsdoc }} - -{{ 'timeCat' | jsdoc }} - -{{ 'polyrhythm' | jsdoc }} - -## Pattern Modifiers - -{{ 'Pattern.slow' | jsdoc }} - -{{ 'Pattern.fast' | jsdoc }} - -{{ 'Pattern.early' | jsdoc }} - -{{ 'Pattern.late' | jsdoc }} - -{{ 'Pattern.rev' | jsdoc }} - -{{ 'Pattern.legato' | jsdoc }} - -## Continuous Signals - -Signals are patterns with continuous values, meaning they have theoretically infinite steps. -They can provide streams of numbers that can be sampled at discrete points in time. - -{{ 'Pattern.range' | jsdoc }} - -{{ 'saw' | jsdoc }} - -{{ 'saw2' | jsdoc }} - -{{ 'sine' | jsdoc }} - -{{ 'sine2' | jsdoc }} - -{{ 'cosine' | jsdoc }} - -{{ 'cosine2' | jsdoc }} - -{{ 'tri' | jsdoc }} - -{{ 'tri2' | jsdoc }} - -{{ 'square' | jsdoc }} - -{{ 'square2' | jsdoc }} - -## Using Samples with Webdirt - -You can use the powerful sampling engine [Webdirt](https://github.com/dktr0/WebDirt) with Strudel. - -{{ 'Pattern.webdirt' | jsdoc }} - -## Using Superdirt via OSC - -In mainline tidal, the actual sound is generated via Superdirt, which runs inside Supercollider. -Strudel also supports using Superdirt as a backend, although it requires some developer tooling to run. - -### Getting Started - -Getting Superdirt to work with Strudel, you need to - -1. install SuperCollider + sc3 plugins, see [Tidal Docs](https://tidalcycles.org/docs/) (Install Tidal) for more info. -2. install [node.js](https://nodejs.org/en/) -3. download [Strudel Repo](https://github.com/tidalcycles/strudel/) (or git clone, if you have git installed) -4. run `npm i` in the strudel directory -5. run `npm run osc` to start the osc server, which forwards OSC messages from Strudel REPL to SuperCollider - -Now you're all set! - -### Usage - -1. Start SuperCollider, either using SuperCollider IDE or by running `sclang` in a terminal -2. Open the [Strudel REPL](https://strudel.tidalcycles.org/#cygiYmQgc2QiKS5vc2MoKQ%3D%3D) - -...or test it here: - - - -If you now hear sound, congratulations! If not, you can get help on the [#strudel channel in the TidalCycles discord](https://discord.com/invite/HGEdXmRkzT). - -{{ 'Pattern.osc' | jsdoc }} - -# Superdirt Params - -The following functions are specific to SuperDirt and won't work with other Strudel outputs. - -## Basic Types - -{{ 's' | jsdoc }} - -{{ 'n' | jsdoc }} - -{{ 'freq' | jsdoc }} - -{{ 'channel' | jsdoc }} - -{{ 'orbit' | jsdoc }} - -## Filters - -{{ 'cutoff' | jsdoc }} - -{{ 'resonance' | jsdoc }} - -{{ 'hcutoff' | jsdoc }} - -{{ 'hresonance' | jsdoc }} - -{{ 'bandf' | jsdoc }} - -{{ 'bandq' | jsdoc }} - -{{ 'djf' | jsdoc }} - -{{ 'vowel' | jsdoc }} - -## Sample Editing - -{{ 'cut' | jsdoc }} - -{{ 'begin' | jsdoc }} - -{{ 'end' | jsdoc }} - -{{ 'loop' | jsdoc }} - -{{ 'fadeTime' | jsdoc }} - -{{ 'speed' | jsdoc }} - -{{ 'unit' | jsdoc }} - -## Audio Effects - -{{ 'gain' | jsdoc }} - -{{ 'amp' | jsdoc }} - -{{ 'accelerate' | jsdoc }} - -{{ 'crush' | jsdoc }} - -{{ 'coarse' | jsdoc }} - -{{ 'delay' | jsdoc }} - -{{ 'lock' | jsdoc }} - -{{ 'leslie' | jsdoc }} - -{{ 'lrate' | jsdoc }} - -{{ 'lsize' | jsdoc }} - -{{ 'pan' | jsdoc }} - -{{ 'panspan' | jsdoc }} - -{{ 'pansplay' | jsdoc }} - -{{ 'room' | jsdoc }} - -{{ 'size' | jsdoc }} - -{{ 'dry' | jsdoc }} - -{{ 'shape' | jsdoc }} - -{{ 'squiz' | jsdoc }} - -{{ 'waveloss' | jsdoc }} - -{{ 'attack' | jsdoc }} - -{{ 'decay' | jsdoc }} - -## Synth Effects - -{{ 'octave' | jsdoc }} - -{{ 'detune' | jsdoc }} - -{{ 'tremolodepth' | jsdoc }}