Top level functions

This commit is contained in:
Aria
2025-11-16 20:28:22 -06:00
parent 7a3bea6f90
commit f5ed6dbe79
4 changed files with 56 additions and 28 deletions
+26 -10
View File
@@ -3644,35 +3644,51 @@ export const parray = (pats) => {
* 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)
* @param {number[] | Pattern} magnitudes List of [0, 1] magnitudes for partials. 0th entry is the fundamental 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))
* .partials(randL(16))
*/
export const { partials } = register('partials', (list, pat) => {
debugger;
register('partials', (list, pat) => {
if (Array.isArray(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
*
* @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
* s("saw").seg(8).n(irand(12)).scale("G#:minor")
* .partials(binaryL(256))
* .phases(randL(20))
* .partials(randL(16))
* .phases(randL(16))
*/
export const { phases } = register('phases', (list, pat) => {
register('phases', (list, pat) => {
if (Array.isArray(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');
};
+2 -2
View File
@@ -796,7 +796,7 @@ describe('Pattern', () => {
});
});
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());
}),
it('Can apply a pattern of functions', () => {
@@ -804,7 +804,7 @@ describe('Pattern', () => {
expect(sequence('a', 'b').apply(fast(2), fast(3)).firstCycle()).toStrictEqual(
sequence('a', 'b').fast(2, 3).firstCycle(),
);
}));
});
});
describe('layer', () => {
it('Can layer up multiple functions', () => {
+2 -2
View File
@@ -18,8 +18,8 @@
--docsearch-modal-background: var(--background);
--docsearch-muted-color: color-mix(in srgb, var(--foreground), #fff 30%);
--docsearch-key-gradient: var(--foreground);
--docsearch-key-shadow:
inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground), 0 1px 2px 1px var(--gutterBackground);
--docsearch-key-shadow: inset 0 -2px 0 0 var(--gutterForeground), inset 0 0 1px 1px var(--foreground),
0 1px 2px 1px var(--gutterBackground);
}
.dark {
--docsearch-muted-color: color-mix(in srgb, var(--foreground), #000 30%);
+26 -14
View File
@@ -48,7 +48,7 @@ You can also use the `crackle` type to play some subtle noise crackles. You can
### 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.
@@ -60,7 +60,7 @@ For the basic waveforms, we offer you control over these harmonics with the `par
client:idle
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
.sound("sawtooth")
.partials([1, 1, 0, 1])
.partials([1, 1, "<1 0>", "<1 0>", "<1 0>", "<1 0>", "<1 0>"])
._scope()`}
/>
@@ -74,25 +74,38 @@ For the basic waveforms, we offer you control over these harmonics with the `par
._scope()`}
/>
We may algorithmically construct lists of partials with Javascript code like:
We may algorithmically construct lists of magnitudes with Javascript code like:
<MiniRepl
client:idle
tune={`const numHarmonics = 22;
note("c2 <eb2 <g2 g1>>".fast(2))
.sound("user")
.sound("saw")
.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`:
which acts as a spectral filter or
<MiniRepl
client:idle
tune={`const numHarmonics = 22;
note("c2 <eb2 <g2 g1>>".fast(2))
tune={`note("c2 <eb2 <g2 g1>>").fast(2)
.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()`}
/>
@@ -110,17 +123,16 @@ Note that the first value in the `partials` array controls the magnitude of the
#### 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.
<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))
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
.sound("saw")
.partials(randL(100))
.phases(randL(100))
._scope()`}
/>