Skip to content

Commit c44f202

Browse files
committed
fix: scale errors safety net
1 parent 9059b16 commit c44f202

File tree

1 file changed

+38
-14
lines changed

1 file changed

+38
-14
lines changed

packages/tonal/tonal.mjs

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,20 @@ This program is free software: you can redistribute it and/or modify it under th
55
*/
66

77
import { Note, Interval, Scale } from '@tonaljs/tonal';
8-
import { register, _mod } from '@strudel.cycles/core';
8+
import { register, _mod, silence, logger, pure, isNote } from '@strudel.cycles/core';
99

1010
const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P';
1111

1212
function scaleStep(step, scale) {
1313
scale = scale.replaceAll(':', ' ');
1414
step = Math.ceil(step);
15-
const { intervals, tonic } = Scale.get(scale);
15+
let { intervals, tonic, empty } = Scale.get(scale);
16+
if ((empty && isNote(scale)) || (!empty && !tonic)) {
17+
throw new Error(`incomplete scale. Make sure to use ":" instead of spaces, example: .scale("C:major")`);
18+
} else if (empty) {
19+
throw new Error(`invalid scale "${scale}"`);
20+
}
21+
tonic = tonic || 'C';
1622
const { pc, oct = 3 } = Note.get(tonic);
1723
const octaveOffset = Math.floor(step / intervals.length);
1824
const scaleStep = _mod(step, intervals.length);
@@ -160,16 +166,34 @@ export const scale = register('scale', function (scale, pat) {
160166
if (Array.isArray(scale)) {
161167
scale = scale.flat().join(' ');
162168
}
163-
return pat.withHap((hap) => {
164-
const isObject = typeof hap.value === 'object';
165-
let note = isObject ? hap.value.n : hap.value;
166-
if (isObject) {
167-
delete hap.value.n; // remove n so it won't cause trouble
168-
}
169-
const asNumber = Number(note);
170-
if (!isNaN(asNumber)) {
171-
note = scaleStep(asNumber, scale);
172-
}
173-
return hap.withValue(() => (isObject ? { ...hap.value, note } : note)).setContext({ ...hap.context, scale });
174-
});
169+
return (
170+
pat
171+
.fmap((value) => {
172+
const isObject = typeof value === 'object';
173+
let step = isObject ? value.n : value;
174+
if (isObject) {
175+
delete value.n; // remove n so it won't cause trouble
176+
}
177+
if (isNote(step)) {
178+
// legacy..
179+
return pure(step);
180+
}
181+
const asNumber = Number(step);
182+
if (isNaN(asNumber)) {
183+
logger(`[tonal] invalid scale step "${step}", expected number`, 'error');
184+
return silence;
185+
}
186+
try {
187+
const note = scaleStep(asNumber, scale);
188+
value = pure(isObject ? { ...value, note } : note);
189+
} catch (err) {
190+
logger(`[tonal] ${err.message}`, 'error');
191+
value = silence;
192+
}
193+
return value;
194+
})
195+
.outerJoin()
196+
// legacy:
197+
.withHap((hap) => hap.setContext({ ...hap.context, scale }))
198+
);
175199
});

0 commit comments

Comments
 (0)