Top level functions
This commit is contained in:
parent
7a3bea6f90
commit
f5ed6dbe79
4 changed files with 56 additions and 28 deletions
|
|
@ -3644,35 +3644,51 @@ export const parray = (pats) => {
|
||||||
* Can also be used to create a new synth via `s('user').partials(...)`
|
* Can also be used to create a new synth via `s('user').partials(...)`
|
||||||
*
|
*
|
||||||
* @name 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)
|
* @param {number[] | Pattern} magnitudes List of [0, 1] magnitudes for partials. 0th entry is the fundamental harmonic (i.e. DC offset is skipped)
|
||||||
* @example
|
* @example
|
||||||
* s("user").seg(16).n(irand(8)).scale("A:major")
|
* s("user").seg(16).n(irand(8)).scale("A:major")
|
||||||
* .partials([1, 0, 1, 0, 0, 1])
|
* .partials([1, 0, 1, 0, 0, 1])
|
||||||
* @example
|
* @example
|
||||||
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
||||||
* .partials(binaryL(256))
|
* .partials(randL(16))
|
||||||
*/
|
*/
|
||||||
export const { partials } = register('partials', (list, pat) => {
|
register('partials', (list, pat) => {
|
||||||
debugger;
|
|
||||||
if (Array.isArray(list)) {
|
if (Array.isArray(list)) {
|
||||||
list = parray(list);
|
list = parray(list);
|
||||||
}
|
}
|
||||||
return pat.withValue((v) => (l) => ({...v, partials: l})).appLeft(list);
|
return pat.withValue((v) => (l) => ({ ...v, partials: l })).appLeft(list);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Also create a top-level function
|
||||||
|
export const partials = (list) => {
|
||||||
|
if (Array.isArray(list)) {
|
||||||
|
list = parray(list);
|
||||||
|
}
|
||||||
|
return list.as('partials');
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases
|
* Rotates the harmonics of one of the core synths ('sine', 'tri', 'saw', 'user', ..) by a list of phases
|
||||||
*
|
*
|
||||||
* @name 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)
|
* @param {number[] | Pattern} phases List of [0, 1) phases for partials. 0th entry is the fundamental phase (i.e. DC offset is skipped)
|
||||||
* @example
|
* @example
|
||||||
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
|
||||||
* .partials(binaryL(256))
|
* .partials(randL(16))
|
||||||
* .phases(randL(20))
|
* .phases(randL(16))
|
||||||
*/
|
*/
|
||||||
export const { phases } = register('phases', (list, pat) => {
|
register('phases', (list, pat) => {
|
||||||
if (Array.isArray(list)) {
|
if (Array.isArray(list)) {
|
||||||
list = parray(list);
|
list = parray(list);
|
||||||
}
|
}
|
||||||
return pat.withValue((v) => (l) => ({...v, phases: l})).appLeft(list);
|
pat = pat ?? pure({});
|
||||||
|
return pat.withValue((v) => (l) => ({ ...v, phases: l })).appLeft(list);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Also create a top-level function
|
||||||
|
export const phases = (list) => {
|
||||||
|
if (Array.isArray(list)) {
|
||||||
|
list = parray(list);
|
||||||
|
}
|
||||||
|
return list.as('phases');
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -796,7 +796,7 @@ describe('Pattern', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('apply', () => {
|
describe('apply', () => {
|
||||||
(it('Can apply a function', () => {
|
it('Can apply a function', () => {
|
||||||
expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle());
|
expect(sequence('a', 'b').apply(fast(2)).firstCycle()).toStrictEqual(sequence('a', 'b').fast(2).firstCycle());
|
||||||
}),
|
}),
|
||||||
it('Can apply a pattern of functions', () => {
|
it('Can apply a pattern of functions', () => {
|
||||||
|
|
@ -804,7 +804,7 @@ describe('Pattern', () => {
|
||||||
expect(sequence('a', 'b').apply(fast(2), fast(3)).firstCycle()).toStrictEqual(
|
expect(sequence('a', 'b').apply(fast(2), fast(3)).firstCycle()).toStrictEqual(
|
||||||
sequence('a', 'b').fast(2, 3).firstCycle(),
|
sequence('a', 'b').fast(2, 3).firstCycle(),
|
||||||
);
|
);
|
||||||
}));
|
});
|
||||||
});
|
});
|
||||||
describe('layer', () => {
|
describe('layer', () => {
|
||||||
it('Can layer up multiple functions', () => {
|
it('Can layer up multiple functions', () => {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
--docsearch-modal-background: var(--background);
|
--docsearch-modal-background: var(--background);
|
||||||
--docsearch-muted-color: color-mix(in srgb, var(--foreground), #fff 30%);
|
--docsearch-muted-color: color-mix(in srgb, var(--foreground), #fff 30%);
|
||||||
--docsearch-key-gradient: var(--foreground);
|
--docsearch-key-gradient: var(--foreground);
|
||||||
--docsearch-key-shadow:
|
--docsearch-key-shadow: inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground),
|
||||||
inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground), 0 1px 2px 1px var(--gutterBackground);
|
0 1px 2px 1px var(--gutterBackground);
|
||||||
}
|
}
|
||||||
.dark {
|
.dark {
|
||||||
--docsearch-muted-color: color-mix(in srgb, var(--foreground), #000 30%);
|
--docsearch-muted-color: color-mix(in srgb, var(--foreground), #000 30%);
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ You can also use the `crackle` type to play some subtle noise crackles. You can
|
||||||
|
|
||||||
### Additive Synthesis
|
### Additive Synthesis
|
||||||
|
|
||||||
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.
|
Periodic waveforms are 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.
|
For the basic waveforms, we offer you control over these harmonics with the `partials` and `phases` functions.
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ For the basic waveforms, we offer you control over these harmonics with the `par
|
||||||
client:idle
|
client:idle
|
||||||
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||||
.sound("sawtooth")
|
.sound("sawtooth")
|
||||||
.partials([1, 1, 0, 1])
|
.partials([1, 1, "<1 0>", "<1 0>", "<1 0>", "<1 0>", "<1 0>"])
|
||||||
._scope()`}
|
._scope()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -74,25 +74,38 @@ For the basic waveforms, we offer you control over these harmonics with the `par
|
||||||
._scope()`}
|
._scope()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
We may algorithmically construct lists of partials with Javascript code like:
|
We may algorithmically construct lists of magnitudes with Javascript code like:
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
client:idle
|
client:idle
|
||||||
tune={`const numHarmonics = 22;
|
tune={`const numHarmonics = 22;
|
||||||
note("c2 <eb2 <g2 g1>>".fast(2))
|
note("c2 <eb2 <g2 g1>>".fast(2))
|
||||||
.sound("user")
|
.sound("saw")
|
||||||
.partials(new Array(numHarmonics).fill(1))
|
.partials(new Array(numHarmonics).fill(1))
|
||||||
._scope()`}
|
._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`:
|
which acts as a spectral filter or
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
client:idle
|
client:idle
|
||||||
tune={`const numHarmonics = 22;
|
tune={`note("c2 <eb2 <g2 g1>>").fast(2)
|
||||||
note("c2 <eb2 <g2 g1>>".fast(2))
|
|
||||||
.sound("user")
|
.sound("user")
|
||||||
.partials(randL(8))
|
.partials(new Array(50).fill(0)
|
||||||
|
.map((_, idx) => ((-1) ** (idx + 1)) / (idx + 1))
|
||||||
|
)
|
||||||
|
._scope()`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
which recovers a familiar waveform.
|
||||||
|
|
||||||
|
`partials` is also compatible with pattern functions designed to produce lists, like `randL` or `binaryL`:
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:idle
|
||||||
|
tune={`note("c2 <eb2 <g2 g1>>").fast(2)
|
||||||
|
.sound("user")
|
||||||
|
.partials(randL(10))
|
||||||
._scope()`}
|
._scope()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -110,17 +123,16 @@ Note that the first value in the `partials` array controls the magnitude of the
|
||||||
|
|
||||||
#### Phases
|
#### 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.
|
Earlier, we mentioned that periodic waveforms can be broken into a set of harmonics above a fundamental frequency. Each harmonic has two defining properties: its magnitude (how loud it is) and its phase, which determines where in its cycle that sine wave starts when the waveform is built.
|
||||||
|
|
||||||
These phases too can be declared in Strudel and can give your sounds interesting depth.
|
These phases too can be declared in Strudel and can give your sounds interesting depth.
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
client:idle
|
client:idle
|
||||||
tune={`const numHarmonics = 22;
|
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
|
||||||
note("c2 <eb2 <g2 g1>>".fast(2))
|
.sound("saw")
|
||||||
.sound("user")
|
.partials(randL(100))
|
||||||
.partials(randL(8))
|
.phases(randL(100))
|
||||||
.phases(randL(8).late(0.3))
|
|
||||||
._scope()`}
|
._scope()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue