mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-18 09:08:30 +00:00
refactor tonal functions to 'register'
This commit is contained in:
parent
4f7c013c78
commit
bc43fc9585
@ -1,2 +1,5 @@
|
|||||||
import './tonal.mjs';
|
import './tonal.mjs';
|
||||||
import './voicings.mjs';
|
import './voicings.mjs';
|
||||||
|
|
||||||
|
export * from './tonal.mjs';
|
||||||
|
export * from './voicings.mjs';
|
||||||
|
|||||||
@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Note, Interval, Scale } from '@tonaljs/tonal';
|
import { Note, Interval, Scale } from '@tonaljs/tonal';
|
||||||
import { Pattern, _mod } from '@strudel.cycles/core';
|
import { register, _mod } from '@strudel.cycles/core';
|
||||||
|
|
||||||
// transpose note inside scale by offset steps
|
// transpose note inside scale by offset steps
|
||||||
// function scaleOffset(scale: string, offset: number, note: string) {
|
// function scaleOffset(scale: string, offset: number, note: string) {
|
||||||
@ -74,8 +74,8 @@ function scaleOffset(scale, offset, note) {
|
|||||||
* "c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).note()
|
* "c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._transpose = function (intervalOrSemitones) {
|
export const transpose = register('transpose', function (intervalOrSemitones, pat) {
|
||||||
return this.withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
const interval = !isNaN(Number(intervalOrSemitones))
|
const interval = !isNaN(Number(intervalOrSemitones))
|
||||||
? Interval.fromSemitones(intervalOrSemitones /* as number */)
|
? Interval.fromSemitones(intervalOrSemitones /* as number */)
|
||||||
: String(intervalOrSemitones);
|
: String(intervalOrSemitones);
|
||||||
@ -87,7 +87,7 @@ Pattern.prototype._transpose = function (intervalOrSemitones) {
|
|||||||
// tone.js doesn't understand multiple sharps flats e.g. F##3 has to be turned into G3
|
// tone.js doesn't understand multiple sharps flats e.g. F##3 has to be turned into G3
|
||||||
return hap.withValue(() => Note.simplify(Note.transpose(hap.value, interval)));
|
return hap.withValue(() => Note.simplify(Note.transpose(hap.value, interval)));
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
// example: transpose(3).late(0.2) will be equivalent to compose(transpose(3), late(0.2))
|
// example: transpose(3).late(0.2) will be equivalent to compose(transpose(3), late(0.2))
|
||||||
// TODO: add Pattern.define(name, function, options) that handles all the meta programming stuff
|
// TODO: add Pattern.define(name, function, options) that handles all the meta programming stuff
|
||||||
@ -110,8 +110,8 @@ Pattern.prototype._transpose = function (intervalOrSemitones) {
|
|||||||
* .note()
|
* .note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
export const scaleTranspose = register('scaleTranspose', function (offset /* : number | string */, pat) {
|
||||||
return this.withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
if (!hap.context.scale) {
|
if (!hap.context.scale) {
|
||||||
throw new Error('can only use scaleTranspose after .scale');
|
throw new Error('can only use scaleTranspose after .scale');
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
|||||||
}
|
}
|
||||||
return hap.withValue(() => scaleOffset(hap.context.scale, Number(offset), hap.value));
|
return hap.withValue(() => scaleOffset(hap.context.scale, Number(offset), hap.value));
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like {@link Pattern#scaleTranspose}.
|
* Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like {@link Pattern#scaleTranspose}.
|
||||||
@ -141,8 +141,8 @@ Pattern.prototype._scaleTranspose = function (offset /* : number | string */) {
|
|||||||
* .note()
|
* .note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype._scale = function (scale /* : string */) {
|
export const scale = register('scale', function (scale /* : string */, pat) {
|
||||||
return this.withHap((hap) => {
|
return pat.withHap((hap) => {
|
||||||
let note = hap.value;
|
let note = hap.value;
|
||||||
const asNumber = Number(note);
|
const asNumber = Number(note);
|
||||||
if (!isNaN(asNumber)) {
|
if (!isNaN(asNumber)) {
|
||||||
@ -152,8 +152,4 @@ Pattern.prototype._scale = function (scale /* : string */) {
|
|||||||
}
|
}
|
||||||
return hap.withValue(() => note).setContext({ ...hap.context, scale });
|
return hap.withValue(() => note).setContext({ ...hap.context, scale });
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.define('transpose', (a, pat) => pat.transpose(a), { composable: true, patternified: true });
|
|
||||||
Pattern.prototype.define('scale', (a, pat) => pat.scale(a), { composable: true, patternified: true });
|
|
||||||
Pattern.prototype.define('scaleTranspose', (a, pat) => pat.scaleTranspose(a), { composable: true, patternified: true });
|
|
||||||
|
|||||||
@ -4,31 +4,47 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
|
|||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Pattern as _Pattern, stack, Hap, reify } from '@strudel.cycles/core';
|
import { stack, register } from '@strudel.cycles/core';
|
||||||
import _voicings from 'chord-voicings';
|
import _voicings from 'chord-voicings';
|
||||||
const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup
|
const { dictionaryVoicing, minTopNoteDiff, lefthand } = _voicings.default || _voicings; // parcel module resolution fuckup
|
||||||
|
|
||||||
const getVoicing = (chord, lastVoicing, range = ['F3', 'A4']) =>
|
export const voicingDictionaries = {
|
||||||
dictionaryVoicing({
|
lefthand: { dictionary: lefthand, range: ['F3', 'A4'] },
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new custom voicing dictionary.
|
||||||
|
*
|
||||||
|
* @name addVoicings
|
||||||
|
* @memberof Pattern
|
||||||
|
* @param {string} name identifier for the voicing dictionary
|
||||||
|
* @param {Object} dictionary maps chord symbol to possible voicings
|
||||||
|
* @param {Array} range min, max note
|
||||||
|
* @returns Pattern
|
||||||
|
* @example
|
||||||
|
* addVoicings('cookie', {
|
||||||
|
* 7: ['3M 7m 9M 12P 15P', '7m 10M 13M 16M 19P'],
|
||||||
|
* '^7': ['3M 6M 9M 12P 14M', '7M 10M 13M 16M 19P'],
|
||||||
|
* m7: ['8P 11P 14m 17m 19P', '5P 8P 11P 14m 17m'],
|
||||||
|
* m7b5: ['3m 5d 8P 11P 14m', '5d 8P 11P 14m 17m'],
|
||||||
|
* o7: ['3m 6M 9M 11A 15P'],
|
||||||
|
* '7alt': ['3M 7m 10m 13m 15P'],
|
||||||
|
* '7#11': ['7m 10m 13m 15P 17m'],
|
||||||
|
* }, ['C3', 'C6'])
|
||||||
|
*/
|
||||||
|
export const addVoicings = (name, dictionary, range = ['F3', 'A4']) => {
|
||||||
|
Object.assign(voicingDictionaries, { [name]: { dictionary, range } });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVoicing = (chord, dictionaryName, lastVoicing) => {
|
||||||
|
const { dictionary, range } = voicingDictionaries[dictionaryName];
|
||||||
|
return dictionaryVoicing({
|
||||||
chord,
|
chord,
|
||||||
dictionary: lefthand,
|
dictionary,
|
||||||
range,
|
range,
|
||||||
picker: minTopNoteDiff,
|
picker: minTopNoteDiff,
|
||||||
lastVoicing,
|
lastVoicing,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Pattern = _Pattern;
|
|
||||||
|
|
||||||
Pattern.prototype.fmapNested = function (func) {
|
|
||||||
return new Pattern((span) =>
|
|
||||||
this.query(span)
|
|
||||||
.map((event) =>
|
|
||||||
reify(func(event))
|
|
||||||
.query(span)
|
|
||||||
.map((hap) => new Hap(event.whole, event.part, hap.value, hap.context)),
|
|
||||||
)
|
|
||||||
.flat(),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,32 +53,38 @@ Pattern.prototype.fmapNested = function (func) {
|
|||||||
*
|
*
|
||||||
* @name voicings
|
* @name voicings
|
||||||
* @memberof Pattern
|
* @memberof Pattern
|
||||||
* @param {range} range note range for possible voicings (optional, defaults to `['F3', 'A4']`)
|
* @param {string} dictionary which voicing dictionary to use.
|
||||||
* @returns Pattern
|
* @returns Pattern
|
||||||
* @example
|
* @example
|
||||||
* stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>").note()
|
* stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>").note()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Pattern.prototype.voicings = function (range) {
|
export const voicings = register('voicings', function (dictionary, pat) {
|
||||||
let lastVoicing;
|
let lastVoicing;
|
||||||
if (!range?.length) {
|
return pat
|
||||||
// allows to pass empty array, if too lazy to specify range
|
.fmap((value) => {
|
||||||
range = ['F3', 'A4'];
|
lastVoicing = getVoicing(value, dictionary, lastVoicing);
|
||||||
}
|
return stack(...lastVoicing);
|
||||||
return this.fmapNested((event) => {
|
})
|
||||||
lastVoicing = getVoicing(event.value, lastVoicing, range);
|
.outerJoin();
|
||||||
return stack(...lastVoicing).withContext(() => ({
|
/* .withContext(() => ({
|
||||||
locations: event.context.locations || [],
|
locations: event.context.locations || [],
|
||||||
}));
|
})); */
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
Pattern.prototype._rootNotes = function (octave = 2) {
|
/**
|
||||||
return this.fmap((value) => {
|
* Maps the chords of the incoming pattern to root notes in the given octave.
|
||||||
|
*
|
||||||
|
* @name rootNotes
|
||||||
|
* @memberof Pattern
|
||||||
|
* @param {octave} octave octave to use
|
||||||
|
* @returns Pattern
|
||||||
|
* @example
|
||||||
|
* "<C^7 A7 Dm7 G7>".rootNotes(2).note()
|
||||||
|
*/
|
||||||
|
export const rootNotes = register('rootNotes', function (octave, pat) {
|
||||||
|
return pat.fmap((value) => {
|
||||||
const root = value.match(/^([a-gA-G][b#]?).*$/)[1];
|
const root = value.match(/^([a-gA-G][b#]?).*$/)[1];
|
||||||
return root + octave;
|
return root + octave;
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
Pattern.prototype.define('voicings', (range, pat) => pat.voicings(range), { composable: true });
|
|
||||||
Pattern.prototype.define('rootNotes', (oct, pat) => pat.rootNotes(oct), { composable: true, patternified: true });
|
|
||||||
|
|||||||
@ -108,7 +108,7 @@ export const struct = `stack(
|
|||||||
export const magicSofa = `stack(
|
export const magicSofa = `stack(
|
||||||
"<C^7 F^7 ~> <Dm7 G7 A7 ~>"
|
"<C^7 F^7 ~> <Dm7 G7 A7 ~>"
|
||||||
.every(2, fast(2))
|
.every(2, fast(2))
|
||||||
.voicings(),
|
.voicings('lefthand'),
|
||||||
"<c2 f2 g2> <d2 g2 a2 e2>"
|
"<c2 f2 g2> <d2 g2 a2 e2>"
|
||||||
).transpose("<0 2 3 4>")`;
|
).transpose("<0 2 3 4>")`;
|
||||||
// below doesn't work anymore due to constructor cleanup
|
// below doesn't work anymore due to constructor cleanup
|
||||||
@ -156,7 +156,7 @@ const synths = stack(
|
|||||||
scaleTranspose(8).early(3/8)
|
scaleTranspose(8).early(3/8)
|
||||||
).apply(thru).tone(keys).mask("<~ x>/16"),
|
).apply(thru).tone(keys).mask("<~ x>/16"),
|
||||||
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).apply(thru).tone(bass),
|
"<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2)).apply(thru).tone(bass),
|
||||||
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings().apply(thru).every(2, early(1/8)).tone(keys).mask("<x@7 ~>/8".early(1/4))
|
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.1 ~]".fast(2)).voicings('lefthand').apply(thru).every(2, early(1/8)).tone(keys).mask("<x@7 ~>/8".early(1/4))
|
||||||
)
|
)
|
||||||
stack(
|
stack(
|
||||||
drums.fast(2),
|
drums.fast(2),
|
||||||
@ -351,7 +351,7 @@ stack(
|
|||||||
|
|
||||||
export const bossa = `const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C locrian','F phrygian']).slow(4)
|
export const bossa = `const scales = sequence('C minor', ['D locrian', 'G phrygian'], 'Bb2 minor', ['C locrian','F phrygian']).slow(4)
|
||||||
stack(
|
stack(
|
||||||
"<Cm7 [Dm7b5 G7b9] Bbm7 [Cm7b5 F7b9]>".fast(2).struct("x ~ x@3 x ~ x ~ ~ ~ x ~ x@3".late(1/8)).early(1/8).slow(2).voicings(),
|
"<Cm7 [Dm7b5 G7b9] Bbm7 [Cm7b5 F7b9]>".fast(2).struct("x ~ x@3 x ~ x ~ ~ ~ x ~ x@3".late(1/8)).early(1/8).slow(2).voicings('lefthand'),
|
||||||
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
"[~ [0 ~]] 0 [~ [4 ~]] 4".sub(7).restart(scales).scale(scales).early(.25)
|
||||||
).note().piano().slow(2)`;
|
).note().piano().slow(2)`;
|
||||||
|
|
||||||
|
|||||||
@ -138,7 +138,7 @@ const synths = stack(
|
|||||||
note("<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".apply(thru))
|
note("<C2 Bb1 Ab1 [G1 [G2 G1]]>/2".apply(thru))
|
||||||
.struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2))
|
.struct("[x [~ x] <[~ [~ x]]!3 [x x]>@2]/2".fast(2))
|
||||||
.s('sawtooth').attack(0.001).decay(0.2).sustain(1).cutoff(500),
|
.s('sawtooth').attack(0.001).decay(0.2).sustain(1).cutoff(500),
|
||||||
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.2 ~]".fast(2)).voicings()
|
"<Cm7 Bb7 Fm7 G7b13>/2".struct("~ [x@0.2 ~]".fast(2)).voicings('lefthand')
|
||||||
.apply(thru).every(2, early(1/8)).note().apply(keys).sustain(0)
|
.apply(thru).every(2, early(1/8)).note().apply(keys).sustain(0)
|
||||||
.delay(.4).delaytime(.12)
|
.delay(.4).delaytime(.12)
|
||||||
.mask("<x@7 ~>/8".early(1/4))
|
.mask("<x@7 ~>/8".early(1/4))
|
||||||
@ -245,7 +245,7 @@ export const festivalOfFingers = `// licensed with CC BY-NC-SA 4.0 https://creat
|
|||||||
// by Felix Roos
|
// by Felix Roos
|
||||||
const chords = "<Cm7 Fm7 G7 F#7>";
|
const chords = "<Cm7 Fm7 G7 F#7>";
|
||||||
stack(
|
stack(
|
||||||
chords.voicings().struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
|
chords.voicings('lefthand').struct("x(3,8,-1)").velocity(.5).off(1/7,x=>x.transpose(12).velocity(.2)),
|
||||||
chords.rootNotes(2).struct("x(4,8,-2)"),
|
chords.rootNotes(2).struct("x(4,8,-2)"),
|
||||||
chords.rootNotes(4)
|
chords.rootNotes(4)
|
||||||
.scale(cat('C minor','F dorian','G dorian','F# mixolydian'))
|
.scale(cat('C minor','F dorian','G dorian','F# mixolydian'))
|
||||||
@ -501,7 +501,7 @@ stack(
|
|||||||
.gain(.4) // turn down
|
.gain(.4) // turn down
|
||||||
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
||||||
//.hush()
|
//.hush()
|
||||||
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
|
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords
|
||||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||||
.add(perlin.range(0,.5)) // random pitch variation
|
.add(perlin.range(0,.5)) // random pitch variation
|
||||||
.n() // wrap in "n"
|
.n() // wrap in "n"
|
||||||
@ -531,7 +531,7 @@ samples({
|
|||||||
perc: ['perc/002_perc2.wav'],
|
perc: ['perc/002_perc2.wav'],
|
||||||
}, 'github:tidalcycles/Dirt-Samples/master/');
|
}, 'github:tidalcycles/Dirt-Samples/master/');
|
||||||
|
|
||||||
"C^7 Am7 Dm7 G7".slow(2).voicings()
|
"C^7 Am7 Dm7 G7".slow(2).voicings('lefthand')
|
||||||
.stack("0@6 [<1 2> <2 0> 1]@2".scale('C5 major'))
|
.stack("0@6 [<1 2> <2 0> 1]@2".scale('C5 major'))
|
||||||
.n().slow(4)
|
.n().slow(4)
|
||||||
.s('0040_FluidR3_GM_sf2_file')
|
.s('0040_FluidR3_GM_sf2_file')
|
||||||
@ -626,7 +626,7 @@ stack(
|
|||||||
s("mt lt ht").struct("x(3,8)").fast(2).gain(.5).room(.5).sometimes(x=>x.speed(".5")),
|
s("mt lt ht").struct("x(3,8)").fast(2).gain(.5).room(.5).sometimes(x=>x.speed(".5")),
|
||||||
s("misc:2").speed(1).delay(.5).delaytime(1/3).gain(.4),
|
s("misc:2").speed(1).delay(.5).delaytime(1/3).gain(.4),
|
||||||
// chords
|
// chords
|
||||||
note("[~ Gm7] ~ [~ Dm7] ~".voicings().superimpose(x=>x.add(.1)))
|
note("[~ Gm7] ~ [~ Dm7] ~".voicings('lefthand').superimpose(x=>x.add(.1)))
|
||||||
.s('sawtooth').gain(.5)
|
.s('sawtooth').gain(.5)
|
||||||
.cutoff(perlin.range(400,3000).slow(8))
|
.cutoff(perlin.range(400,3000).slow(8))
|
||||||
.decay(perlin.range(0.05,.2)).sustain(0)
|
.decay(perlin.range(0.05,.2)).sustain(0)
|
||||||
@ -806,7 +806,7 @@ export const loungeSponge = `
|
|||||||
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
|
await loadOrc('github:kunstmusik/csound-live-code/master/livecode.orc')
|
||||||
|
|
||||||
stack(
|
stack(
|
||||||
note("<C^7 A7 Dm7 Fm7>/2".voicings())
|
note("<C^7 A7 Dm7 Fm7>/2".voicings('lefthand'))
|
||||||
.cutoff(sine.range(500,2000).round().slow(16))
|
.cutoff(sine.range(500,2000).round().slow(16))
|
||||||
.euclidLegato(3,8).csound('FM1')
|
.euclidLegato(3,8).csound('FM1')
|
||||||
,
|
,
|
||||||
@ -823,7 +823,7 @@ export const arpoon = `// licensed with CC BY-NC-SA 4.0 https://creativecommons.
|
|||||||
// "Arpoon" by Felix Roos
|
// "Arpoon" by Felix Roos
|
||||||
await samples('github:tidalcycles/Dirt-Samples/master')
|
await samples('github:tidalcycles/Dirt-Samples/master')
|
||||||
|
|
||||||
"<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings()
|
"<<Am7 C^7> C7 F^7 [Fm7 E7b9]>".voicings('lefthand')
|
||||||
.arp("[0,3] 2 [1,3] 2".fast(3)).legato(2)
|
.arp("[0,3] 2 [1,3] 2".fast(3)).legato(2)
|
||||||
.add(perlin.range(0,0.2)).sub("<0 -12>/8")
|
.add(perlin.range(0,0.2)).sub("<0 -12>/8")
|
||||||
.note().cutoff(perlin.range(500,4000)).resonance(12)
|
.note().cutoff(perlin.range(500,4000)).resonance(12)
|
||||||
|
|||||||
@ -35,7 +35,7 @@ s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
|
|||||||
.s('sawtooth') // waveform
|
.s('sawtooth') // waveform
|
||||||
.gain(.4) // turn down
|
.gain(.4) // turn down
|
||||||
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
|
||||||
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
|
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords
|
||||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||||
.add(perlin.range(0,.5)) // random pitch variation
|
.add(perlin.range(0,.5)) // random pitch variation
|
||||||
.n() // wrap in "n"
|
.n() // wrap in "n"
|
||||||
@ -755,7 +755,7 @@ Transposes notes inside the scale by the number of steps:
|
|||||||
|
|
||||||
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
Turns chord symbols into voicings, using the smoothest voice leading possible:
|
||||||
|
|
||||||
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>").note()`} />
|
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>").note()`} />
|
||||||
|
|
||||||
<!-- TODO: use voicing collection as first param + patternify. -->
|
<!-- TODO: use voicing collection as first param + patternify. -->
|
||||||
|
|
||||||
@ -790,7 +790,7 @@ Either connect a midi device or use the IAC Driver (Mac) or Midi Through Port (L
|
|||||||
If no outputName is given, it uses the first midi output it finds.
|
If no outputName is given, it uses the first midi output it finds.
|
||||||
|
|
||||||
<MiniRepl
|
<MiniRepl
|
||||||
tune={`stack("<C^7 A7 Dm7 G7>".voicings(), "<C3 A2 D3 G2>")
|
tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>")
|
||||||
.midi()`}
|
.midi()`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user