Testing out approach to allow list patterns in partials
This commit is contained in:
parent
b66c0d18b3
commit
cc10122b71
3 changed files with 101 additions and 35 deletions
|
|
@ -2583,31 +2583,3 @@ export const scrub = register(
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Scale the magnitude of the harmonics of one of the core synths ('sine', 'tri', 'saw', ..)
|
|
||||||
*
|
|
||||||
* Can also be used to create a new synth via `s('user').partials(...)`
|
|
||||||
*
|
|
||||||
* @name partials
|
|
||||||
* @param {number[] | Pattern} partials List of [0, 1] magnitudes for partials. 0th entry is the first harmonic (i.e. DC offset is skipped)
|
|
||||||
* @example
|
|
||||||
* s("user").seg(16).n(irand(8)).scale("A:major")
|
|
||||||
* .partials([1, 0, 1, 0, 0, 1])
|
|
||||||
* @example
|
|
||||||
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
|
||||||
* .partials(binaryL(256))
|
|
||||||
*/
|
|
||||||
export const { partials } = registerControl('partials');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases
|
|
||||||
*
|
|
||||||
* @name phases
|
|
||||||
* @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the first phase (i.e. DC offset is skipped)
|
|
||||||
* @example
|
|
||||||
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
|
||||||
* .partials(binaryL(256))
|
|
||||||
* .phases(randL(20))
|
|
||||||
*/
|
|
||||||
export const { phases } = registerControl('phases');
|
|
||||||
|
|
|
||||||
|
|
@ -3624,3 +3624,54 @@ for (const name of distAlgoNames) {
|
||||||
return this.distort(argsPat);
|
return this.distort(argsPat);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns a list of patterns into a single pattern which outputs list-values
|
||||||
|
*
|
||||||
|
* @name parray
|
||||||
|
* @returns Pattern
|
||||||
|
*/
|
||||||
|
export const parray = (pats) => {
|
||||||
|
const pack = (...xs) => xs;
|
||||||
|
let acc = pure(curry(pack, null, pats.length));
|
||||||
|
for (const p of pats) acc = acc.appBoth(reify(p));
|
||||||
|
return acc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale the magnitude of the harmonics of one of the core synths ('sine', 'tri', 'saw', ..)
|
||||||
|
*
|
||||||
|
* Can also be used to create a new synth via `s('user').partials(...)`
|
||||||
|
*
|
||||||
|
* @name partials
|
||||||
|
* @param {number[] | Pattern} partials List of [0, 1] magnitudes for partials. 0th entry is the first harmonic (i.e. DC offset is skipped)
|
||||||
|
* @example
|
||||||
|
* s("user").seg(16).n(irand(8)).scale("A:major")
|
||||||
|
* .partials([1, 0, 1, 0, 0, 1])
|
||||||
|
* @example
|
||||||
|
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
||||||
|
* .partials(binaryL(256))
|
||||||
|
*/
|
||||||
|
export const { partials } = register('partials', (list, pat) => {
|
||||||
|
if (Array.isArray(list)) {
|
||||||
|
list = parray(list);
|
||||||
|
}
|
||||||
|
return pat.withValue((v) => ({...v, partials: list}));
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases
|
||||||
|
*
|
||||||
|
* @name phases
|
||||||
|
* @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the first phase (i.e. DC offset is skipped)
|
||||||
|
* @example
|
||||||
|
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
||||||
|
* .partials(binaryL(256))
|
||||||
|
* .phases(randL(20))
|
||||||
|
*/
|
||||||
|
export const { phases } = register('phases', (list, pat) => {
|
||||||
|
if (Array.isArray(list)) {
|
||||||
|
list = parray(list);
|
||||||
|
}
|
||||||
|
return pat.withValue((v) => ({...v, phases: list}));
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -48,28 +48,71 @@ You can also use the `crackle` type to play some subtle noise crackles. You can
|
||||||
|
|
||||||
### Additive Synthesis
|
### Additive Synthesis
|
||||||
|
|
||||||
To tame the harsh sound of the basic waveforms, we can set the `n` control to limit the overtones of the waveform:
|
Waveforms are often composed of several [harmonics](https://en.wikipedia.org/wiki/Harmonic) above a fundamental frequency, lying at integer multiples. These overtones combine to give a sound its unique timbral quality.
|
||||||
|
|
||||||
|
For the basic waveforms, we offer you control over these harmonics with the `partials` and `phases` functions.
|
||||||
|
|
||||||
|
#### Partials
|
||||||
|
|
||||||
|
`partials` refers to the magnitude of each harmonic relative to the fundamental frequency. They can thus be used to spectrally filter these waveforms and tame some of their harshness:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
client:idle
|
client:idle
|
||||||
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||||
.sound("sawtooth")
|
.sound("sawtooth")
|
||||||
.n("<32 16 8 4>")
|
.partials([1, 1, 0, 1])
|
||||||
._scope()`}
|
._scope()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
When the `n` control is used on a basic waveform, it defines the number of harmonic partials the sound is getting.
|
`partials` can also be used to construct _new_ waveforms not present in our basic set with the 'user' sound source:
|
||||||
You can also set `n` directly in mini notation with `sound`:
|
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
client:idle
|
client:idle
|
||||||
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||||
.sound("sawtooth:<32 16 8 4>")
|
.sound("user")
|
||||||
|
.partials([1, 0, 0.3, 0, 0.1, 0, 0, 0.3])
|
||||||
._scope()`}
|
._scope()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
Note for tidal users: `n` in tidal is synonymous to `note` for synths only.
|
We may algorithmically construct lists of partials with Javascript code like:
|
||||||
In strudel, this is not the case, where `n` will always change timbre, be it though different samples or different waveforms.
|
|
||||||
|
<MiniRepl
|
||||||
|
client:idle
|
||||||
|
tune={`const numHarmonics = 22;
|
||||||
|
note("c2 <eb2 <g2 g1>>".fast(2))
|
||||||
|
.sound("user")
|
||||||
|
.partials(new Array(numHarmonics).fill(1))
|
||||||
|
._scope()`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
This approach can act as a form of bandlimiting. `partials` is also compatible with pattern functions designed to produce lists, like `randL` or `binaryL`:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:idle
|
||||||
|
tune={`const numHarmonics = 22;
|
||||||
|
note("c2 <eb2 <g2 g1>>".fast(2))
|
||||||
|
.sound("user")
|
||||||
|
.partials(randL(8))
|
||||||
|
._scope()`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
Note that the first value in the `partials` array controls the magnitude of the fundamental harmonic rather than the DC offset, which is fixed at 0.
|
||||||
|
|
||||||
|
#### Phases
|
||||||
|
|
||||||
|
We mentioned that our sounds can be broken into a constituent set of harmonics above a fundamental frequency. These are defined by two values: their magnitude (how loud they are) and their [phase](https://en.wikipedia.org/wiki/Phase_(waves)), which can be thought of as which point in its cycle each sine wave is initialized at when we begin adding them.
|
||||||
|
|
||||||
|
These phases too can be declared in Strudel and can give your sounds interesting depth.
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:idle
|
||||||
|
tune={`const numHarmonics = 22;
|
||||||
|
note("c2 <eb2 <g2 g1>>".fast(2))
|
||||||
|
.sound("user")
|
||||||
|
.partials(randL(8))
|
||||||
|
.phases(randL(8).late(0.3))
|
||||||
|
._scope()`}
|
||||||
|
/>
|
||||||
|
|
||||||
## Vibrato
|
## Vibrato
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue