diff --git a/src/ArrayExtensions.ts b/src/ArrayExtensions.ts index 7a5fc3f..3e1e8b2 100644 --- a/src/ArrayExtensions.ts +++ b/src/ArrayExtensions.ts @@ -1,6 +1,6 @@ import { type UserAPI } from "./API"; import { safeScale } from "zifferjs"; -export { }; +export {}; declare global { interface Array { @@ -37,14 +37,14 @@ export const makeArrayExtensions = (api: UserAPI) => { return this.includes(value); }; - Array.prototype.square = function(): number[] { + Array.prototype.square = function (): number[] { /** * @returns New array with squared values. */ return this.map((x: number) => x * x); }; - Array.prototype.sqrt = function(): number[] { + Array.prototype.sqrt = function (): number[] { /** * @returns New array with square roots of values. Throws if any element is negative. */ @@ -53,7 +53,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this.map((x: number) => Math.sqrt(x)); }; - Array.prototype.add = function(amount: number): number[] { + Array.prototype.add = function (amount: number): number[] { /** * @param amount - The value to add to each element in the array. * @returns New array with added values. @@ -61,7 +61,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this.map((x: number) => x + amount); }; - Array.prototype.sub = function(amount: number): number[] { + Array.prototype.sub = function (amount: number): number[] { /** * @param amount - The value to subtract from each element in the array. * @returns New array with subtracted values. @@ -69,7 +69,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this.map((x: number) => x - amount); }; - Array.prototype.mult = function(amount: number): number[] { + Array.prototype.mult = function (amount: number): number[] { /** * @param amount - The value to multiply with each element in the array. * @returns New array with multiplied values. @@ -77,7 +77,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this.map((x: number) => x * amount); }; - Array.prototype.div = function(amount: number): number[] { + Array.prototype.div = function (amount: number): number[] { /** * @param amount - The value to divide each element in the array by. * @returns New array with divided values. Throws if division by zero. @@ -86,7 +86,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this.map((x: number) => x / amount); }; - Array.prototype.pick = function() { + Array.prototype.pick = function () { /** * Returns a random element from an array. * @@ -95,7 +95,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this[Math.floor(api.randomGen() * this.length)]; }; - Array.prototype.gen = function(min: number, max: number, times: number) { + Array.prototype.gen = function (min: number, max: number, times: number) { /** * Returns an array of random numbers. * @param min - The minimum value of the random numbers @@ -112,7 +112,7 @@ export const makeArrayExtensions = (api: UserAPI) => { ); }; - Array.prototype.bar = function() { + Array.prototype.bar = function () { /** * Returns an element from an array based on the current bar. * @@ -121,7 +121,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this[api.app.clock.time_position.bar % this.length]; }; - Array.prototype.pulse = function() { + Array.prototype.pulse = function () { /** * Returns an element from an array based on the current pulse. * @@ -130,7 +130,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this[api.app.clock.time_position.pulse % this.length]; }; - Array.prototype.beat = function(divisor: number = 1) { + Array.prototype.beat = function (divisor: number = 1) { const chunk_size = divisor; // Get the first argument (chunk size) const timepos = api.app.clock.pulses_since_origin; const slice_count = Math.floor( @@ -139,7 +139,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this[slice_count % this.length]; }; - Array.prototype.shuffle = function() { + Array.prototype.shuffle = function () { /** * Shuffles the array in place. * @@ -158,7 +158,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this; }; - Array.prototype.rotate = function(steps: number) { + Array.prototype.rotate = function (steps: number) { /** * Rotates the array in place. * @@ -178,7 +178,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this; }; - Array.prototype.unique = function() { + Array.prototype.unique = function () { /** * Removes duplicate elements from the array in place. * @@ -211,7 +211,7 @@ export const makeArrayExtensions = (api: UserAPI) => { if (this.length <= 1) { return this; } - for (let i = 0; i < this.length;) { + for (let i = 0; i < this.length; ) { const rand = api.randomGen() * 100; if (rand < amount) { if (this.length > 1) { @@ -324,7 +324,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return left_to_right.concat(right_to_left); }; - Array.prototype.loop = function(index: number) { + Array.prototype.loop = function (index: number) { /** * Returns an element from the array based on the index. * The index will wrap over the array. @@ -335,7 +335,7 @@ export const makeArrayExtensions = (api: UserAPI) => { return this[index % this.length]; }; - Array.prototype.random = function() { + Array.prototype.random = function () { /** * Returns a random element from the array. * @@ -346,52 +346,79 @@ export const makeArrayExtensions = (api: UserAPI) => { Array.prototype.rand = Array.prototype.random; }; -Array.prototype.scale = function(scale: string = "major", base_note: number = 0) { +Array.prototype.scale = function ( + scale: string = "major", + base_note: number = 0 +) { /** - * @param scale - the scale name - * @param base_note - the base note to start at (MIDI note number) - */ + * @param scale - the scale name + * @param base_note - the base note to start at (MIDI note number) + */ + const selected_scale = SCALES[scale]; + return this.map((value) => { + const octaveShift = + Math.floor(value / selected_scale.length) * 12 * Math.sign(value); + return ( + selected_scale[Math.abs(value) % selected_scale.length] + + base_note + + octaveShift + ); + }); +}; + +Array.prototype.scale = function ( + scale: string = "major", + base_note: number = 0 +) { + /** + * @param scale - the scale name + * @param base_note - the base note to start at (MIDI note number) + * + * @returns notes from the desired scalek + */ + + // This is a helper function to handle up or down octaviation. + const mod = (n: number, m: number) => ((n % m) + m) % m; + if (SCALES.hasOwnProperty(scale)) { const selected_scale = SCALES[scale]; return this.map((value) => { - const octaveShift = Math.floor(value / selected_scale.length) * 12 * Math.sign(value); - return selected_scale[Math.abs(value) % selected_scale.length] + base_note + octaveShift; + const octaveShift = Math.floor(value / selected_scale.length) * 12; + return ( + selected_scale[mod(value, selected_scale.length)] + + base_note + + octaveShift + ); }); } else { return this.map((value) => value + base_note); } }; - -Array.prototype.scale = function(scale: string = "major", base_note: number = 0) { - /** - * @param scale - the scale name - * @param base_note - the base note to start at (MIDI note number) - * - * @returns notes from the desired scalek - */ - - // This is a helper function to handle up or down octaviation. - const mod = (n: number, m: number) => ((n % m) + m) % m; - let selected_scale = safeScale(scale); - return this.map((value) => { - const octaveShift = Math.floor(value / selected_scale.length) * 12; - return selected_scale[mod(value, selected_scale.length)] + base_note + octaveShift; - }); -}; - -Array.prototype.arp = function(scaleName: string = "major") { - /* - * Ajouter documentation +Array.prototype.arp = function (scaleName: string = "major", mask: number = 0) { + /* + * @param scaleName - the scale name + * @param mask - the length of the mask * */ const scale = safeScale[scaleName]; + //input protect from unknow scale + + if (!scale) { + throw new Error(`Unknown scale ${scaleName}`); + } + let result = []; - for (let j = 0; j < scale.length; j++) { + + mask = mask > scale.length ? scale.length : mask; + mask = mask == 0 ? scale.length : mask; + + for (let j = 0; j < mask; j++) { for (let i = 0; i < this.length; i++) { result.push(this[i] + scale[j]); } } + return result; }; diff --git a/src/documentation/patterns.ts b/src/documentation/patterns.ts index ffeeb48..2e07c9f 100644 --- a/src/documentation/patterns.ts +++ b/src/documentation/patterns.ts @@ -183,7 +183,75 @@ ${makeExample( beat(1)::snd('sine').sustain(0.1).freq([100,100,100,100,200].unique().beat()).out() `, true -)} + )} + +- scale(scale: string, mask: number): extrapolate a custom-masked scale from each list elements. _[0].scale("major", 3)_ returns _[0,2,4]_ + +${makeExample( + "Extrapolate a 3-elements Persian scale from 2 notes", + ` +beat(1) :: snd('gtr') + .note([0,5].scale("persian", 3).beat() + 50) + .out() +`, + true + )} + +- Currently supported scales : +| Scale name | Values | +|------------|------------------------| +| major | 0, 2, 4, 5, 7, 9, 11 +| naturalMinor | 0, 2, 3, 5, 7, 8, 10 +| harmonicMinor | 0, 2, 3, 5, 7, 8, 11 +| melodicMinor | 0, 2, 3, 5, 7, 9, 11 +| dorian | 0, 2, 3, 5, 7, 9, 10 +| phrygian | 0, 1, 3, 5, 7, 8, 10 +| lydian | 0, 2, 4, 6, 7, 9, 11 +| mixolydian | 0, 2, 4, 5, 7, 9, 10 +| aeolian | 0, 2, 3, 5, 7, 8, 10 +| locrian | 0, 1, 3, 5, 6, 8, 10 +| wholeTone | 0, 2, 4, 6, 8, 10 +| majorPentatonic | 0, 2, 4, 7, 9 +| minorPentatonic | 0, 3, 5, 7, 10 +| chromatic | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +| blues | 0, 3, 5, 6, 7, 10 +| diminished | 0, 2, 3, 5, 6, 8, 9, 11 +| neapolitanMinor | 0, 1, 3, 5, 7, 8, 11 +| neapolitanMajor | 0, 1, 3, 5, 7, 9, 11 +| enigmatic | 0, 1, 4, 6, 8, 10, 11], +| doubleHarmonic | 0, 1, 4, 5, 7, 8, 11 +| octatonic | 0, 2, 3, 5, 6, 8, 9, 11 +| bebopDominant | 0, 2, 4, 5, 7, 9, 10, 11 +| bebopMajor | 0, 2, 4, 5, 7, 8, 9, 11 +| bebopMinor | 0, 2, 3, 5, 7, 8, 9, 11 +| bebopDorian | 0, 2, 3, 4, 5, 7, 9, 10 +| harmonicMajor | 0, 2, 4, 5, 7, 8, 11 +| hungarianMinor | 0, 2, 3, 6, 7, 8, 11 +| hungarianMajor | 0, 3, 4, 6, 7, 9, 10 +| oriental | 0, 1, 4, 5, 6, 9, 10 +| romanianMinor | 0, 2, 3, 6, 7, 9, 10 +| spanishGypsy | 0, 1, 4, 5, 7, 8, 10 +| jewish | 0, 1, 4, 5, 7, 8, 10 +| hindi | 0, 2, 4, 5, 7, 8, 10 +| japanese | 0, 1, 5, 7, 8 +| hirajoshi | 0, 2, 3, 7, 8 +| kumoi | 0, 2, 3, 7, 9 +| inSen | 0, 1, 5, 7, 10 +| iwato | 0, 1, 5, 6, 10 +| yo | 0, 2, 5, 7, 9 +| minorBlues | 0, 3, 5, 6, 7, 10 +| algerian | 0, 2, 3, 5, 6, 7, 8, 11 +| augmented | 0, 3, 4, 7, 8, 11 +| balinese | 0, 1, 3, 7, 8 +| byzantine | 0, 1, 4, 5, 7, 8, 11 +| chinese | 0, 4, 6, 7, 11 +| egyptian |0, 2, 5, 7, 10 +| eightToneSpanish | 0, 1, 3, 4, 5, 6, 8, 10 +| hawaiian | 0, 2, 3, 5, 7, 9, 10 +| hindustan | 0, 2, 4, 5, 7, 8, 10 +| persian | 0, 1, 4, 5, 6, 8, 11 +| eastIndianPurvi | 0, 1, 4, 6, 7, 8, 11 +| orientalA | 0, 1, 4, 5, 6, 9, 10 - add(): add a given amount to every list element. - sub(): add a given amount to every list element.