diff --git a/src/__tests__/scale.spec.ts b/src/__tests__/scale.spec.ts index bda1536..e2e9a0c 100644 --- a/src/__tests__/scale.spec.ts +++ b/src/__tests__/scale.spec.ts @@ -558,6 +558,19 @@ describe('Scale', () => { expect(scale.size).toBe(14); }); + it('can generate rank 2 (unsafe fractions)', () => { + const generator = new Interval( + ExtendedMonzo.fromFraction('11/7', 5), + 'ratio' + ); + const octave = new Interval(ExtendedMonzo.fromFraction(2, 5), 'ratio'); + const scale = Scale.fromRank2(generator, octave, 100, 0); + // Expect not to raise. + for (let i = 0; i < 100; ++i) { + scale.getName(i); + } + }); + it('can generate harmonic series segment', () => { const scale = Scale.fromHarmonicSeries(4, 8, 4); expect( diff --git a/src/interval.ts b/src/interval.ts index 9078002..257bd31 100644 --- a/src/interval.ts +++ b/src/interval.ts @@ -1,5 +1,5 @@ import {ExtendedMonzo} from './monzo'; -import {fractionToString, isSafeFraction} from './utils'; +import {fractionToString} from './utils'; import {Fraction} from 'xen-dev-utils'; /** Interval formatting options. */ @@ -502,8 +502,8 @@ export class Interval { maybeFraction.cents = 0; if (maybeFraction.isFractional()) { - const fraction = maybeFraction.toFraction(); - if (isSafeFraction(fraction)) { + try { + const fraction = maybeFraction.toFraction(); return ( fractionToString( fraction, @@ -511,9 +511,10 @@ export class Interval { options.preferredDenominator ) + this.centsString(true) ); - } - if (options.forbidMonzo) { - return cents(); + } catch { + if (options.forbidMonzo) { + return cents(); + } } } } @@ -523,8 +524,8 @@ export class Interval { maybeEt.cents = 0; if (maybeEt.isEqualTemperament()) { - const [fractionOfEquave, equave] = maybeEt.toEqualTemperament(); - if (isSafeFraction(fractionOfEquave) && isSafeFraction(equave)) { + try { + maybeEt.toEqualTemperament(); const et = new Interval( maybeEt, 'equal temperament', @@ -532,9 +533,10 @@ export class Interval { this.options ); return et.equalTemperamentString() + this.centsString(true); - } - if (options.forbidMonzo) { - return cents(); + } catch { + if (options.forbidMonzo) { + return cents(); + } } } } @@ -547,22 +549,15 @@ export class Interval { if (options.forbidComposite) { return cents(); } - if (isSafeFraction(this.monzo.residual)) { - if (this.type === 'monzo') { - console.warn('Failed to represent monzo. Displaying residue.'); - } - return ( - this.monzoString() + - ' + ' + - fractionToString( - this.monzo.residual, - options.preferredNumerator, - options.preferredDenominator - ) - ); - } else { - return cents(); - } + return ( + this.monzoString() + + ' + ' + + fractionToString( + this.monzo.residual, + options.preferredNumerator, + options.preferredDenominator + ) + ); } if (options.forbidComposite) { return cents(); diff --git a/src/scale.ts b/src/scale.ts index c438feb..e300088 100644 --- a/src/scale.ts +++ b/src/scale.ts @@ -167,6 +167,7 @@ export class Scale { period = period.mergeOptions(options); if ( period.type === 'ratio' && + period.monzo.isFractional() && period.monzo.toFraction().mod(genEquave).equals(0) ) { period.type = 'equal temperament'; diff --git a/src/utils.ts b/src/utils.ts index 3e3a665..989b498 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,17 +1,5 @@ import {Fraction} from 'xen-dev-utils'; -/** - * Check if the fraction exactly representable as a `Number`. - * @param fraction Input fraction. - * @returns `true` if both numerator, numerator + 1, denominator and denominator + 1 are exactly representable. - */ -export function isSafeFraction(fraction: Fraction) { - return ( - fraction.n <= Number.MAX_SAFE_INTEGER && - fraction.d <= Number.MAX_SAFE_INTEGER - ); -} - /** * Convert fraction to a string. * @param fraction Input fraction.