Skip to content

Commit

Permalink
Use relative notation for automos if J4 <> 1
Browse files Browse the repository at this point in the history
  • Loading branch information
frostburn committed Jul 25, 2024
1 parent 44a0855 commit 0ece475
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 23 deletions.
2 changes: 1 addition & 1 deletion documentation/BUILTIN.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Calculate atan2(y, x) which is the angle between (1, 0) and (x, y), chosen to li
Calculate atanXY(x, y) = atan2(y, x) which is the angle between (1, 0) and (x, y), chosen to lie in (−π; π], positive anticlockwise.

### automos()
If the current scale is empty, generate absolute Diamond-mos notation based on the current config.
If the current scale is empty, generate absolute Diamond-mos notation based on the current config. Uses relative notation if J4 does not coincide with 1/1.

### basis(*...intervals*)
Construct a subgroup basis from intervals.
Expand Down
2 changes: 1 addition & 1 deletion src/diamond-mos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export type MosStep = {
type: 'MosStep';
quality: IntervalQuality;
augmentations?: AugmentedQuality[];
degree: 0;
degree: number;
};

/**
Expand Down
42 changes: 42 additions & 0 deletions src/parser/__tests__/source.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2280,4 +2280,46 @@ describe('SonicWeave parser', () => {
const scale = expand('[1..5] \\ 5;respell([16/15], 1)');
expect(scale).toEqual(['1\\5', '2\\5', '3\\5', '4\\5', '5\\5']);
});

it('gives up on absolute diamond MOS if J4 is not unison (relative)', () => {
const scale = expand('A4 = 9\\12;MOS 4L 2s;automos()');
expect(scale).toEqual([
'MOS {LLsLLs;L=2^1/5;s=2^1/10}',
'P1ms',
'Aug2ms',
'P3ms',
'P4ms',
'Aug5ms',
'P6ms',
]);
});

it('gives up on absolute diamond MOS if J4 is not unison (absolute)', () => {
const scale = expand('A4 = 440 Hz;MOS 2L 6s;automos()');
expect(scale).toEqual([
'MOS {LsssLsss;L=2^1/5;s=2^1/10}',
'Aug1ms',
'Maj2ms',
'P3ms',
'P4ms',
'Aug5ms',
'Maj6ms',
'P7ms',
'P8ms',
]);
});

it('recreates diatonic with automos if J4 is not unison', () => {
const scale = expand('MOS {LLsLLLs;L=9/8};J4=2;automos()');
expect(scale).toEqual([
'MOS {LLsLLLs;L=9/8;s=256/243}',
'Maj1ms',
'Maj2ms',
'P3ms',
'P4ms',
'Maj5ms',
'Maj6ms',
'P7ms',
]);
});
});
107 changes: 86 additions & 21 deletions src/stdlib/builtin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
BasisElement,
FJSFlavor,
MonzoLiteral,
MosStepLiteral,
NedjiLiteral,
RadicalLiteral,
SquareSuperparticular,
Expand Down Expand Up @@ -1846,33 +1847,97 @@ keepUnique.__node__ = builtinNode(keepUnique);

function automos(this: ExpressionVisitor) {
const scale = this.currentScale;
if (!this.rootContext?.mosConfig || scale.length) {
const context = this.rootContext;
if (!context?.mosConfig || scale.length) {
return;
}
this.spendGas(this.rootContext.mosConfig.scale.size);
const J4 = this.rootContext.C4;
const monzos = scaleMonzos(this.rootContext.mosConfig).map(m => J4.mul(m));
const result = monzos.map(
(m, i) =>
new Interval(m, 'logarithmic', 0, {
type: 'AbsoluteFJS',
pitch: {
type: 'AbsolutePitch',
nominal: nthNominal(mmod(i + 1, monzos.length)),
accidentals: [{accidental: '♮', fraction: ''}],
octave: i === monzos.length - 1 ? 5 : 4,
this.spendGas(context.mosConfig.scale.size);
if (
context.C4.isUnity() ||
(context.unisonFrequency && context.C4.equals(context.unisonFrequency))
) {
const J4 = context.C4;
const monzos = scaleMonzos(context.mosConfig).map(m => J4.mul(m));
const result = monzos.map(
(m, i) =>
new Interval(m, 'logarithmic', 0, {
type: 'AbsoluteFJS',
pitch: {
type: 'AbsolutePitch',
nominal: nthNominal(mmod(i + 1, monzos.length)),
accidentals: [{accidental: '♮', fraction: ''}],
octave: i === monzos.length - 1 ? 5 : 4,
},
ups: 0,
lifts: 0,
superscripts: [],
subscripts: [],
})
);
context.fragiles.push(...result);
return result;
}
const monzos = scaleMonzos(context.mosConfig);
const result: Interval[] = [];
for (let i = 0; i < monzos.length; ++i) {
const monzo = monzos[i];
const reference = monzo.reduce(context.mosConfig.period);
const j = i + 1;
const degree =
context.mosConfig.degrees[j % context.mosConfig.degrees.length];
const node: MosStepLiteral = {
type: 'MosStepLiteral',
mosStep: {
type: 'MosStep',
degree: j,
quality: {
fraction: '',
quality: 'P',
},
ups: 0,
lifts: 0,
superscripts: [],
subscripts: [],
})
);
this.rootContext.fragiles.push(...result);
},
ups: 0,
lifts: 0,
superscripts: [],
subscripts: [],
};
if (degree.imperfect) {
const large = degree.center.mul(context.mosConfig.semiam);
if (large.equals(reference)) {
node.mosStep.quality.quality = 'Maj';
result.push(new Interval(monzo, 'logarithmic', 0, node));
continue;
}
const small = degree.center.div(context.mosConfig.semiam);
if (small.equals(reference)) {
node.mosStep.quality.quality = 'min';
result.push(new Interval(monzo, 'logarithmic', 0, node));
continue;
}
} else {
const large = degree.center.mul(context.mosConfig.am);
if (large.equals(reference)) {
node.mosStep.quality.quality = 'Aug';
result.push(new Interval(monzo, 'logarithmic', 0, node));
continue;
}
const small = degree.center.div(context.mosConfig.am);
if (small.equals(reference)) {
node.mosStep.quality.quality = 'dim';
result.push(new Interval(monzo, 'logarithmic', 0, node));
continue;
}
}
if (degree.center.equals(reference)) {
result.push(new Interval(monzo, 'logarithmic', 0, node));
continue;
}
// User misconfiguration: Scale not MOS
result.push(new Interval(monzo, 'logarithmic', 0));
}
return result;
}
automos.__doc__ =
'If the current scale is empty, generate absolute Diamond-mos notation based on the current config.';
'If the current scale is empty, generate absolute Diamond-mos notation based on the current config. Uses relative notation if J4 does not coincide with 1/1.';
automos.__node__ = builtinNode(automos);

function reverseInPlace(
Expand Down

0 comments on commit 0ece475

Please sign in to comment.