import { IntervalLiteral, MonzoLiteral, ValBasisLiteral, CoIntervalLiteral, WartBasisElement } from './expression'; import { TimeMonzo, TimeReal } from './monzo'; import { type RootContext } from './context'; import { Fraction, FractionValue, FractionalMonzo, GramResult, Monzo, ProtoFractionalMonzo } from 'xen-dev-utils'; import { TuningMap } from './temper'; /** * Interval domain. The operator '+' means addition in the linear domain. In the logarithmic domain '+' correspond to multiplication of the underlying values instead. */ export type IntervalDomain = 'linear' | 'logarithmic'; /** * How to treat subgroups with non-primes during temperament optimization. * * - 'subgroup': Promote composite/fractional subgroup to a prime subgroup and project result. * * - 'inharmonic': Treat formal primes as prime numbers according to their size. * * - 'Tenney-Pakkanen': Weigh formal primes according to their Tenney-height. */ export type FormalPrimeMetric = 'subgroup' | 'inharmonic' | 'Tenney-Pakkanen'; /** * CSS color value. */ export declare class Color { value: string; constructor(value: string); /** * SonicWeave representation of the CSS color. * @returns The color value as a string. */ toString(): string; /** * Check if this color is strictly the same as another. * @param other Another Color instance. * @returns `true` if the colors have the same value. */ strictEquals(other: Color): boolean; } /** * Infer a color, a label and tracking identifiers from two {@link Interval} instances. * @param left The preferred source of information. * @param right The secondary source of information. * @returns Dummy interval with the combined information. */ export declare function infect(left: Interval, right: Interval): Interval; /** * Calculate the logarithm of the first value in the base of the second value. * @param left Logarithmand. * @param right Logdividend. * @returns Left value divided by the right value as a {@link TimeMonzo} or {@link TimeReal}. */ export declare function log(left: Interval, right: Interval): TimeReal | TimeMonzo; /** * A musical interval associated with a domain, an AST node, CSS color, note label and tracking identifiers. */ export declare class Interval { value: TimeMonzo | TimeReal; domain: IntervalDomain; steps: number; node?: IntervalLiteral; color?: Color; label: string; trackingIds: Set; /** * Construct a musical interval. * @param value A time monzo representing the size and echelon (frequency vs. frequency ratio) of the interval. * @param domain Domain determining what addition means. * @param steps A steps offset used in tempering. * @param node Node in the abstract syntax tree used for string representation. * @param convert Another {@link Interval} instance to obtain CSS color, note label and tracking information from. */ constructor(value: TimeMonzo | TimeReal, domain: IntervalDomain, steps?: number, node?: IntervalLiteral, convert?: Interval); /** * Construct a linear domain interval from an integer. * @param value Integer to convert. * @param convert Another {@link Interval} instance to obtain CSS color, note label and tracking information from. * @returns Musical interval representing a harmonic. */ static fromInteger(value: number | bigint, convert?: Interval): Interval; /** * Construct a linear domain interval from a fraction. * @param value Rational number to convert. * @param convert Another {@link Interval} instance to obtain CSS color, note label and tracking information from. * @returns Musical interval representing a frequency ratio. */ static fromFraction(value: FractionValue, convert?: Interval): Interval; /** * Construct a linear domain interval from a real number. * @param value Real number to convert. * @param convert Another {@link Interval} instance to obtain CSS color, note label and tracking information from. * @returns Musical interval representing a (possibly irrational) frequency ratio. */ static fromValue(value: number, convert?: Interval): Interval; /** * Revive an {@link Interval} instance produced by `Interval.toJSON()`. Return everything else as is. * * Intended usage: * ```ts * const data = JSON.parse(serializedData, Interval.reviver); * ``` * * @param key Property name. * @param value Property value. * @returns Deserialized {@link Interval} instance or other data without modifications. */ static reviver(key: string, value: any): any; /** * Serialize the time monzo to a JSON compatible object. * @returns The serialized object with property `type` set to `'Interval'`. */ toJSON(): any; /** * Clone this {@link Interval} instance without deeply copying any of the parts. * @returns An interval like this one but replacing any of the parts won't change the original. */ shallowClone(): Interval; /** * Clone this {@link Interval instance} including the value. * @returns An interval like this one but mutating the value won't change the original. */ clone(): Interval; /** Convert the interval to an integer. */ toInteger(): number; /** Convert the interval to a fraction in linear space. */ toFraction(): Fraction; /** Return `true` if the interval represents a ratio of frequencies. */ isRelative(): boolean; /** Return `true` if the interval could be interpreted as a frequency. */ isAbsolute(): boolean; /** * Return the size of the interval in cents. * @param ignoreSign Compute the size of the absolute value. */ totalCents(ignoreSign?: boolean): number; /** * Negate the frequency/value of a linear interval or reflect a logarithmic interval about the unison. * @returns Negative counterpart of this interval. */ neg(): Interval; /** * Invert the value of a linear interval or convert a logarithmic interval to a val that maps it to unity. * @returns Inverse of this interval. */ inverse(): Interval | Val; /** * Calculate the absolute value of a linear interval or obtain a logarithmic intervals distance to unison. * @returns Absolute value of this interval. */ abs(): Interval; /** * Calculate the geometric absolute value of a linter interval. * @returns Superunitary value. */ pitchAbs(): Interval; /** * Calculate the square root of the underlying value regardless of domain. * @returns The square root. */ sqrt(): Interval; /** * Project the exponent of two to the given base. * @param base New base to replace prime two. * @returns N steps of equal divisions of the new base assuming this interval was N steps of an equally divided octave. */ project(base: Interval): Interval; /** * Add two linear intervals or multiply the underlying values of two logarithmic intervals. * @param other Another interval. * @returns Sum of the intervals. */ add(other: Interval): Interval; /** * Subtract two linear intervals or divide the underlying values of two logarithmic intervals. * @param other Another interval. * @returns Difference of the intervals. */ sub(other: Interval): Interval; /** * Subtract this interval from another. * @param other Another interval. * @returns Difference of the intervals (with swapped arguments). */ lsub(other: Interval): Interval; /** * Harmonically add two intervals. * @param other Another interval. * @returns The lens sum of the intervals. */ lensAdd(other: Interval): Interval; /** * Harmonically subtract two intervals. * @param other Another interval. * @returns The lens difference of the intervals. */ lensSub(other: Interval): Interval; /** * Round a linear interval to a multiple of another or a logarithmic interval to a power of the underlying value of another. * @param other Another interval. * @returns Closest multiple of the other to this one. */ roundTo(other: Interval): Interval; /** * Calculate the modulus of a linear interval with respect to another or reduce (repeatedly divide) the underlying value of a logarithmic interval by another. * @param other Another interval. * @param ceiling If `true` `x.mmod(x)` evaluates to `x`. * @returns This interval modulo other. */ mmod(other: Interval, ceiling?: boolean): Interval; /** * Round a linear interval to a power of another. * @param other Another interval. * @returns The closest power of the other to this one. */ pitchRoundTo(other: Interval): Interval; /** * Multiply two linear intervals or raise the underlying value of a logarithmic interval to the power of a linear one. * @param other Another interval. * @returns The product of the intervals. */ mul(other: Interval): Interval; mul(other: Val): Val; /** * Divide two linear intervals or take the root of the underlying value of a logarithmic interval with respect to a linear one. * The ratio of two logarithmic intervals is a linear scalar equal to the logdivision of the underlying values. * @param other Another interval. * @returns The ratio of the intervals. */ div(other: Interval): Interval; /** * Divide another interval by this one. * @param other Another interval. * @returns The ratio of the intervals (with swapped arguments). */ ldiv(other: Interval): Interval; /** * Compute the dot product of the prime count vectors (monzos) associated with two intervals or an interval and a val. * @param other Another interval or a val. * @returns Linear domain interval representing the cosine of the unweighted angle between the prime counts. */ dot(other: Interval | Val): Interval; /** * Raise a linear interval to the power of another. * @param other Another interval. * @returns This interval exponentiated by the other. */ pow(other: Interval): Interval; /** * Calculate the recipropower between two linear intervals. * @param other Another interval. * @returns The root of this interval with respect to the other. */ ipow(other: Interval): Interval; /** * Calculate logdivision between two linear intervals. * @param other Another interval. * @returns The logarithm of this in the base of the other. */ log(other: Interval): Interval; /** * Equave-reduce a linear interval by another other. * @param other Another interval (the equave). * @param ceiling If `true` `x.reduce(x)` evaluates to `x`. * @returns This interval divided by the other until it's between it and unison. */ reduce(other: Interval, ceiling?: boolean): Interval; /** * Calculate this many steps of the octave equally divided into `other` parts. * @param other Another interval (the edo). * @returns A logarithmic quantity representing two to the power of the ratio of the intervals. */ backslash(other: Interval): Interval; /** * Return a number suitable for `Array.sort()` to indicate the size of this interval w.r.t. the other. * @param other Another interval. * @returns A number that's less than zero if this is less than other, zero if equal and greater than zero otherwise. */ compare(other: Interval): number; /** * Check if this interval has the same size as another. * @param other Another interval. * @returns `true` if this has the same size as the other. */ equals(other: Interval): boolean; /** * Check for strict equality between this and another interval. * @param other Another interval. * @returns `true` if the values share the same time exponent, prime exponents, residual and cents offset and the domains match. */ strictEquals(other: Interval): boolean; /** * Return `true` if this interval is only composed of abstract edosteps. * @returns `true` if the interval is a unit scalar, possibly with edosteps, `false` otherwise. */ isPureSteps(): boolean; /** * (This method is an optimization detail.) Convert aspiring nodes to true AST nodes for formatting. * @param context Current root context with information about root pitch and size of ups and lifts. * @returns A true AST node suitable for string conversion or `undefined` realization is impossible in the given context. */ realizeNode(context: RootContext): IntervalLiteral | undefined; /** * Convert the interval to a virtual AST node representing the universal type. * @param interchange Boolean flag to format everything explicitly. * @returns A virtual monzo literal. */ asMonzoLiteral(interchange?: boolean): MonzoLiteral; /** * Convert this interval to a string that faithfully represents it ignoring formatting, colors and labels. * Doesn't depend on the current root context. * @returns String that has the same value and domain as this interval if evaluated as a SonicWeave expression. */ simpleStr(): string; /** * Convert this interval to a string that faithfully represents it ignoring colors and labels. * @param context Current root context with information about root pitch and size of ups and lifts. * @returns String that has the same value and domain as this interval if evaluated as a SonicWeave expression. */ str(context?: RootContext): string; /** * Convert this interval to a string that faithfully represents it including color and label. * @param context Current root context with information about root pitch and size of ups and lifts. * @param interchange Boolean flag to always include label and color. * @returns String that has the same value and domain as this interval if evaluated as a SonicWeave expression. */ toString(context?: RootContext, interchange?: boolean): string; /** * Convert a relative interval to a real number representing a ratio of frequencies. * Convert an absolute interval to the scalar of its time unit. * @returns A real number. */ valueOf(): number; /** * Apply an up arrow to this interval. * @param context Current root context with the value of "up" to apply. * @returns A new interval with size increased by the current "up" value. */ up(context: RootContext): Interval; /** * Apply a down arrow to this interval. * @param context Current root context with the value of "down" to apply. * @param count How many down arrows to apply. * @returns A new interval with size decreased by the current "up" value. */ down(context: RootContext, count?: number): Interval; /** * Apply a lift to this interval. * @param context Current root context with the value of "lift" to apply. * @returns A new interval with size increased by the current "lift" value. */ lift(context: RootContext): Interval; /** * Apply a drop to this interval. * @param context Current root context with the value of "drop" to apply. * @returns A new interval with size decreased by the current "lift" value. */ drop(context: RootContext): Interval; /** * Remove stored context-dependent formatting information. (Triggered by a context shift.) */ break(force?: boolean): void; } /** * A basis of a fractional just intonation subgroup. */ export declare class ValBasis { value: TimeMonzo[]; node?: ValBasisLiteral; private ortho_?; private dual_?; private tenneyValue_?; private tenneyGram_?; /** * Construct a basis for a fractional just intonation subgroup. * @param basis Array of basis elements or number of primes starting from 2. * @param node Virtual AST node associated with this basis. */ constructor(basis: TimeMonzo[] | number, node?: ValBasisLiteral); /** * Number of basis elements / number of dimensions */ get size(): number; /** * Prime limit of the basis as a 0-based ordinal. */ get numberOfComponents(): number; /** * Basis elements that are not true prime numbers. */ get nonPrimes(): TimeMonzo[]; /** * The value of this basis weighted with the logarithms of the primes. */ get tenneyValue(): number[][]; /** * The unnormalized Gram-Schmidt process applied to tenney-weighted coordinates. */ get tenneyGram(): GramResult; /** * Check if this basis is only "pointing" along prime axis. * @returns `true` if all basis elements are powers of primes. `false` otherwise. */ isPrimewise(): boolean; /** * Obtain the minimal prime basis this basis is embedded in. * @returns The super-basis. */ superBasis(): ValBasis; /** * Create a (2*n + 1)^d hypercube. * @param n Radius (max metric). * @param puncture Remove center. */ hypercube(n: number, puncture?: boolean): TimeMonzo[]; /** * The unnormalized Gram-Schmidt orthogonalized basis. */ get ortho(): TimeMonzo[]; /** * The geometric duals of the unnormalized Gram-Schmidt orthogonalized basis. */ get dual(): TimeMonzo[]; private performGramProcess; /** * Convert this basis to an array of linear intervals. * @returns The basis elements as {@link Interval} instances. */ toArray(): Interval[]; /** * Perform Lenstra-Lenstra-Lovász basis reduction. * @param weighting Weighting to use when judging basis angles and lengths. * @returns A reduced {@link ValBasis} instance. */ lll(weighting: 'none' | 'tenney'): ValBasis; /** * Respell an interval to a simpler comma-equivalent one using a variant of Babai's nearest plane algorithm for approximate CVP. * @param monzo The rational interval to reduce. * @returns The reduced interval. */ respell(monzo: TimeMonzo, weighting: 'none' | 'tenney'): TimeMonzo; /** * Check if this basis is the same as another. * @param other Another basis. * @returns `true` if this basis is the same as the other. */ equals(other: ValBasis): boolean; /** * Check if this basis is strictly the same as another. * @param other Another basis. * @returns `true` if this basis is strictly the same as the other. */ strictEquals(other: ValBasis): boolean; /** * Check if this basis is the standard prime basis. * @param soft Only check for primality, not length of basis. * @returns `true` if the basis consists of prime numbers in order. */ isStandard(soft?: boolean): boolean; /** * Fix a map in this basis to the standard basis. * @param map Tuning map of this basis' elements to cents. * @returns Tuning map of primes to cents. */ standardFix(map: TuningMap): TuningMap; /** * Convert a time monzo in the standard basis to this basis. * @param monzo Standard monzo. * @returns Subgroup monzo with integer coefficients. * @throws An error if the standard monzo is not integral in this basis. */ toSubgroupMonzo(monzo: TimeMonzo): Monzo; /** * Convert a time monzo in the standard basis to this basis and leave a residual of the unconverted tail. * @param monzo Standard monzo. * @returns Subgroup monzo and a standard residual outside of this basis. */ toSmonzoAndResidual(monzo: TimeMonzo): [FractionalMonzo, TimeMonzo]; /** * Convert a subgroup monzo in this basis to the standard basis. * @param subgroupMonzo Subgroup monzo in this basis. * @returns Time monzo in the standard basis. */ dot(subgroupMonzo: ProtoFractionalMonzo): TimeMonzo; /** * Rebase intervals from the standard basis to this basis. * Rebase vals from a foreign subgroup basis to this basis. * @param other {@link Interval} or {@link Val} to reinterpret. * @return The rebased value. */ intrinsicCall(other: Interval): Interval; intrinsicCall(other: Val): Val; /** * Convert this basis to a virtual AST fragment compatible with wart notation. * @returns Array of wart basis elements. */ toWartBasis(): WartBasisElement[]; /** * Convert this basis to a string that faithfully represents it. * @returns String that has the same value as this basis if evaluated as a SonicWeave expression. */ toString(): string; } /** * A mapping vector commonly used to convert intervals in just intonation to steps of an equal temperament. */ export declare class Val { value: TimeMonzo; basis: ValBasis; domain: "cologarithmic"; node?: CoIntervalLiteral; /** * Construct a mapping vector. * @param value A {@link TimeMonzo} instance interpreted as a val. Usually a projective approximation to the Just Intonation Point with integer coefficients. * @param basis The subgroup basis associated with this val. The first basis element determines the equave that is equally divided by this val. * @param node Node in the abstract syntax tree used for string representation. */ constructor(value: TimeMonzo, basis: ValBasis, node?: CoIntervalLiteral); /** * Construct a val from an array of mapping entries representing equal divisions of an equave. * @param primeExponentMap Val components. * @param basis Basis of the subgroup. Defaults to the primes. * @param node Node in the abstract syntax tree used for string representation. * @returns A cologarithmic mapping vector. */ static fromArray(primeExponentMap: FractionValue[], basis?: ValBasis, node?: CoIntervalLiteral): Val; /** * Convert an array of components in a subgroup basis to a val in the standard basis. * @param basisExponentMap Components of the val. * @param basis Basis of the val. * @param node Node in the abstract syntax tree used for string representation. */ static fromBasisMap(basisExponentMap: FractionValue[], basis: ValBasis, node?: CoIntervalLiteral): Val; /** * The interval this val is equally dividing. */ get equave(): TimeMonzo; /** * The number of divisions in the equal temperament associated with this val. */ get divisions(): Fraction; /** * The value of this val within the associated basis. */ get sval(): FractionalMonzo; /** * The additive inverse of this val. * @returns The negative of this val. */ neg(): Val; /** * The geometric inverse of this val. * @returns An interval in the logarithmic domain whose dot product with the original val is unitary. */ inverse(): Interval; /** * Normalize the leading coefficient to be positive. * @returns A new val with a positive division of its equave. */ abs(): Val; /** * A meaningless operation. * @returns A new Val obtained by pretending its value represents a linear quantity. */ sqrt(): Val; /** * Check if this val has the same size and equave as another. * @param other Another val. * @returns `true` if the vals have the same size and their equaves have the same size. */ equals(other: Val): boolean; /** * Check if this val has the same structure as another. * @param other Another val. * @returns `true` if the vals and their equaves have the same components. */ strictEquals(other: Val): boolean; /** * Add this this val to another. * @param other Another val. * @returns The sum of the vals. */ add(other: Val): Val; /** * Subtract another val from this one. * @param other Another val. * @returns The difference of the vals. */ sub(other: Val): Val; /** * Scale this val by a linear interval. * @param other A linear scalar. * @returns A rescaled version of this val. */ mul(other: Interval): Val; /** * Inversely scale this val by a linear interval. * @param other A linear scalar. * @returns A rescaled version of this val. */ div(other: Interval): Val; /** * Map an interval using this val or calculate the cosine of the unweighted angle between two vals. * @param other An interval to map or another val. * @returns The dot product between this and the other as a linear interval. */ dot(other: Interval | Val): Interval; /** * Compute the root mean squared error against the just intonation point. * @param weights Additional weights to apply on top of Tenney weights. * @param unnormalized Return the unnormalized squared error instead. * @returns The TE error. */ errorTE(weights: number[], unnormalized?: boolean): number; /** * Obtain the next val in the basis' generalized patent val sequence or approach it if this val is off-JIP. * @param weights Additional weights to apply on top of Tenney weights. * @returns The val at unit distance that comes after this one (non-projective metric). */ nextGPV(weights: number[]): Val; /** * Obtain a faithful string representation of this val. * @returns A string that evaluates to a val with the same value as this one when interpreted as a SonicWeave expression. */ toString(): string; } /** * Regular temperament combining multiple vals to a more optimal tuning. */ export declare class Temperament { canonicalMapping: number[][]; basis: ValBasis; weights: number[]; pureEquaves: boolean; metric: FormalPrimeMetric; commaBasis: ValBasis; sgens: number[][]; preimage: ValBasis; private subgroupMapping_?; /** * Construct a new temperament from an array of svals. * @param mapping A matrix where the rows are linearly independent subgroup vals. * @param basis Basis of the svals. * @param weights Additional weights on top of Tenney weights to tweak what is considered optimal. * @param pureEquaves Boolean flag to force the tuning of the first basis element to remain pure. */ constructor(mapping: number[][], basis?: ValBasis, weights?: number[], pureEquaves?: boolean, metric?: FormalPrimeMetric); /** * Construct a temperament that retains the properties shared by all of the given vals. * @param vals Vals to mix into a more optimal combination. The number of vals decides the rank of the temperament. * @param weights Additional weights on top of Tenney weights to tweak what is considered optimal. * @param pureEquaves Boolean flag to force the tuning of the first basis element to remain pure. * @returns A higher rank temperament. */ static fromVals(vals: Val[], weights?: number[], pureEquaves?: boolean, metric?: FormalPrimeMetric): Temperament; /** * Construct a temperament that tempers out the given commas. * @param commas Commas to temper out. The number of commas decides the co-rank of the temperament. * @param basis Optional basis. Leave undefined to automatically infer from commas. * @param weights Additional weights on top of Tenney weights to tweak what is considered optimal. * @param pureEquaves Boolean flag to force the tuning of the first basis element to remain pure. * @param fullPrimeLimit If no basis is given, use the full prime limit instead of a minimal prime subgroup. * @returns A higher rank temperament. Lower co-rank than just intonation. */ static fromCommas(commas: TimeMonzo[], basis?: ValBasis, weights?: number[], pureEquaves?: boolean, metric?: FormalPrimeMetric, fullPrimeLimit?: boolean): Temperament; /** * Tuning in cents of this temperament's subgroup basis. */ get subgroupMapping(): number[]; /** * Obtain the number of independent generators of this temperament. */ get rank(): number; /** * Obtain the generators of this temperament in cents. * @returns An array of cents values. */ get generators(): number[]; /** * Obtain the number of periods per first basis element (octave or equave). */ get numberOfPeriods(): number; /** * Simplify a rational value based on equivalences available in this temperament. * @param monzo A rational value to simplify. * @returns A rational value with reduced tenney height if found. The result is tuned the same as the original by this temperament. */ respell(monzo: TimeMonzo): TimeMonzo; /** * Tune a value according to this temperament. * @param monzo A value to tune. * @returns The value retuned according this temperament's optimality criteria. */ temper(monzo: TimeMonzo | TimeReal): TimeReal; /** * Produce an array of generator coefficients. * @param other A value to interpret. * @returns An array representing the number of generators adding up to the value. */ dot(other: Interval): Interval[]; /** * Compute the root mean squared error against the just intonation point. * @returns The TE error. */ errorTE(): number; /** * Check if this temperament is the same as another. * @param other Another temperament. * @returns `true` if this temperament is the same as the other. */ equals(other: Temperament): boolean; /** * Check if this temperament is strictly the same as another. * @param other Another temperament. * @returns `true` if this temperament is strictly the same as the other. */ strictEquals(other: Temperament): boolean; /** * Produce a faithful string representation of this temperament. * @returns A string that when evaluated reproduces a value equal to this temperament. */ toString(): string; } /** * Format a {@link TimeMonzo} instance as a node in the abstract syntax tree if possible. * @param value Time monzo to convert. * @param node Reference node to infer type and formatting information from. * @param simplify Ignore formatting information from the reference AST node. * @returns AST node representing the time monzo. */ export declare function intervalValueAs(value: TimeMonzo | TimeReal, node: IntervalLiteral | undefined, simplify?: boolean): IntervalLiteral | undefined;