@libraz/libcantus 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/analyze/index.cjs +41 -1676
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +194 -122
- package/dist/analyze/index.d.ts +194 -122
- package/dist/analyze/index.js +19 -8
- package/dist/chunk-36N73KZ3.cjs +246 -0
- package/dist/chunk-36N73KZ3.cjs.map +1 -0
- package/dist/chunk-4HLLZPXT.cjs +3077 -0
- package/dist/chunk-4HLLZPXT.cjs.map +1 -0
- package/dist/chunk-7MGZ7D62.cjs +498 -0
- package/dist/chunk-7MGZ7D62.cjs.map +1 -0
- package/dist/{chunk-R3S2LUBH.js → chunk-7NKDPF74.js} +660 -305
- package/dist/chunk-7NKDPF74.js.map +1 -0
- package/dist/chunk-CAN76NCU.cjs +679 -0
- package/dist/chunk-CAN76NCU.cjs.map +1 -0
- package/dist/{chunk-7Y7WF64D.js → chunk-D2FAJBYL.js} +104 -35
- package/dist/chunk-D2FAJBYL.js.map +1 -0
- package/dist/{chunk-COKTX2G5.js → chunk-EHVEBWM5.js} +1052 -510
- package/dist/chunk-EHVEBWM5.js.map +1 -0
- package/dist/chunk-FCANXBAT.cjs +30 -0
- package/dist/chunk-FCANXBAT.cjs.map +1 -0
- package/dist/chunk-FNDWXRRI.cjs +479 -0
- package/dist/chunk-FNDWXRRI.cjs.map +1 -0
- package/dist/{chunk-GQKN5KDT.js → chunk-GQSGS2N4.js} +43 -29
- package/dist/chunk-GQSGS2N4.js.map +1 -0
- package/dist/chunk-HKGYCQ34.js +53 -0
- package/dist/chunk-HKGYCQ34.js.map +1 -0
- package/dist/chunk-I24MV65U.js +106 -0
- package/dist/chunk-I24MV65U.js.map +1 -0
- package/dist/{chunk-LN7ZZMD2.js → chunk-KMHOICIY.js} +322 -173
- package/dist/chunk-KMHOICIY.js.map +1 -0
- package/dist/chunk-LW53AHQU.cjs +1117 -0
- package/dist/chunk-LW53AHQU.cjs.map +1 -0
- package/dist/chunk-N6IHQUOX.js +1 -0
- package/dist/chunk-N6IHQUOX.js.map +1 -0
- package/dist/chunk-NKIBQ6PT.js +30 -0
- package/dist/chunk-NKIBQ6PT.js.map +1 -0
- package/dist/chunk-NP4NA2FB.cjs +106 -0
- package/dist/chunk-NP4NA2FB.cjs.map +1 -0
- package/dist/chunk-NVE34YGZ.cjs +1449 -0
- package/dist/chunk-NVE34YGZ.cjs.map +1 -0
- package/dist/{chunk-A4532ZFR.js → chunk-NYYWMV2C.js} +115 -47
- package/dist/chunk-NYYWMV2C.js.map +1 -0
- package/dist/{chunk-5HTNMQW4.js → chunk-Q2XUP227.js} +310 -118
- package/dist/chunk-Q2XUP227.js.map +1 -0
- package/dist/chunk-Q6OP4U43.cjs +327 -0
- package/dist/chunk-Q6OP4U43.cjs.map +1 -0
- package/dist/chunk-RC6GBMCD.cjs +845 -0
- package/dist/chunk-RC6GBMCD.cjs.map +1 -0
- package/dist/{chunk-XTC3X6MS.js → chunk-RK35RIN7.js} +453 -152
- package/dist/chunk-RK35RIN7.js.map +1 -0
- package/dist/chunk-RUMU3UA6.cjs +53 -0
- package/dist/chunk-RUMU3UA6.cjs.map +1 -0
- package/dist/chunk-T64C7ZD7.cjs +1 -0
- package/dist/chunk-T64C7ZD7.cjs.map +1 -0
- package/dist/chunk-XEBG3G53.js +498 -0
- package/dist/chunk-XEBG3G53.js.map +1 -0
- package/dist/chunk-YLEMGEFH.js +246 -0
- package/dist/chunk-YLEMGEFH.js.map +1 -0
- package/dist/chunk-ZEJR4TKL.cjs +173 -0
- package/dist/chunk-ZEJR4TKL.cjs.map +1 -0
- package/dist/core/index.cjs +130 -431
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +331 -17
- package/dist/core/index.d.ts +331 -17
- package/dist/core/index.js +71 -13
- package/dist/{function-2bJW1Zp_.d.ts → function-BtiZb9i9.d.ts} +28 -7
- package/dist/{function-r2sh-jOO.d.cts → function-DNugAOrt.d.cts} +28 -7
- package/dist/generate/index.cjs +30 -3751
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +373 -59
- package/dist/generate/index.d.ts +373 -59
- package/dist/generate/index.js +23 -9
- package/dist/{index-DZ1M21do.d.cts → index-6VR8-kit.d.ts} +20 -4
- package/dist/index-70p168VM.d.ts +192 -0
- package/dist/{index-D_ZTUNKp.d.cts → index-Be74mcon.d.cts} +18 -17
- package/dist/index-BtXT6UQ0.d.cts +192 -0
- package/dist/{index-BMPxwjwV.d.ts → index-C5UBbQ4G.d.ts} +18 -17
- package/dist/{index-BOBXBNwP.d.cts → index-C83mZI1P.d.ts} +93 -8
- package/dist/{index-BdeQ1r_p.d.cts → index-D6b6na69.d.cts} +14 -11
- package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
- package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
- package/dist/index-OG65ocOr.d.cts +272 -0
- package/dist/index-OG65ocOr.d.ts +272 -0
- package/dist/{index-BreoJRw0.d.ts → index-tJhkmQgE.d.cts} +20 -4
- package/dist/{index-CP3wAfEH.d.ts → index-tSQh-xwY.d.ts} +14 -11
- package/dist/{index-CM5W4rcm.d.ts → index-y_ak9_Hi.d.cts} +93 -8
- package/dist/index.cjs +419 -6848
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -13
- package/dist/index.d.ts +14 -13
- package/dist/index.js +132 -27
- package/dist/model/index.cjs +8 -2423
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +433 -74
- package/dist/model/index.d.ts +433 -74
- package/dist/model/index.js +6 -7
- package/dist/styled-DIRkk9Uz.d.ts +506 -0
- package/dist/styled-DJbkJQfj.d.cts +506 -0
- package/dist/theory/index.cjs +172 -1676
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +151 -233
- package/dist/theory/index.d.ts +151 -233
- package/dist/theory/index.js +31 -7
- package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
- package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
- package/package.json +73 -24
- package/dist/chunk-5HTNMQW4.js.map +0 -1
- package/dist/chunk-7Y7WF64D.js.map +0 -1
- package/dist/chunk-A4532ZFR.js.map +0 -1
- package/dist/chunk-BHUT2S5G.js +0 -22
- package/dist/chunk-BHUT2S5G.js.map +0 -1
- package/dist/chunk-CCBLVAPK.js +0 -42
- package/dist/chunk-CCBLVAPK.js.map +0 -1
- package/dist/chunk-COKTX2G5.js.map +0 -1
- package/dist/chunk-DYWFNC4A.js +0 -152
- package/dist/chunk-DYWFNC4A.js.map +0 -1
- package/dist/chunk-GQKN5KDT.js.map +0 -1
- package/dist/chunk-LN7ZZMD2.js.map +0 -1
- package/dist/chunk-MSWFZF7J.js +0 -118
- package/dist/chunk-MSWFZF7J.js.map +0 -1
- package/dist/chunk-R3S2LUBH.js.map +0 -1
- package/dist/chunk-SDM3VJJ5.js +0 -41
- package/dist/chunk-SDM3VJJ5.js.map +0 -1
- package/dist/chunk-XTC3X6MS.js.map +0 -1
- package/dist/chunk-ZXW5YIWK.js +0 -55
- package/dist/chunk-ZXW5YIWK.js.map +0 -1
- package/dist/index-CGMZNCx6.d.cts +0 -140
- package/dist/index-CGMZNCx6.d.ts +0 -140
- package/dist/styled-BjXtOFNk.d.cts +0 -282
- package/dist/styled-D7BAx3S3.d.ts +0 -282
- package/src/analyze/arrange/index.ts +0 -21
- package/src/analyze/arrange/internal.ts +0 -125
- package/src/analyze/arrange/tension.ts +0 -187
- package/src/analyze/arrange/tracks.ts +0 -279
- package/src/analyze/detect/index.ts +0 -235
- package/src/analyze/functional/borrowed.ts +0 -84
- package/src/analyze/functional/cadence.ts +0 -66
- package/src/analyze/functional/function.ts +0 -184
- package/src/analyze/functional/index.ts +0 -23
- package/src/analyze/functional/internal.ts +0 -29
- package/src/analyze/functional/roman.ts +0 -393
- package/src/analyze/index.ts +0 -49
- package/src/analyze/timeline/index.ts +0 -415
- package/src/analyze/voice/index.ts +0 -330
- package/src/core/index.ts +0 -47
- package/src/core/interval/index.ts +0 -81
- package/src/core/meter/index.ts +0 -282
- package/src/core/pitch/index.ts +0 -311
- package/src/core/random/index.ts +0 -53
- package/src/core/tuning/index.ts +0 -156
- package/src/core/types.ts +0 -29
- package/src/generate/bass/index.ts +0 -350
- package/src/generate/countermelody/index.ts +0 -379
- package/src/generate/drums/beat.ts +0 -266
- package/src/generate/drums/euclid.ts +0 -58
- package/src/generate/drums/fills.ts +0 -356
- package/src/generate/drums/ghost.ts +0 -119
- package/src/generate/drums/hihat.ts +0 -285
- package/src/generate/drums/hit.ts +0 -31
- package/src/generate/drums/index.ts +0 -329
- package/src/generate/drums/internal.ts +0 -257
- package/src/generate/drums/kick.ts +0 -160
- package/src/generate/drums/percussion.ts +0 -121
- package/src/generate/drums/rng.ts +0 -7
- package/src/generate/drums/swing.ts +0 -71
- package/src/generate/groove/index.ts +0 -326
- package/src/generate/harmonize/index.ts +0 -440
- package/src/generate/index.ts +0 -59
- package/src/generate/motif/index.ts +0 -398
- package/src/generate/progression/index.ts +0 -370
- package/src/generate/reharmony/index.ts +0 -330
- package/src/generate/rhythm/index.ts +0 -165
- package/src/index.ts +0 -247
- package/src/model/chord.ts +0 -423
- package/src/model/index.ts +0 -15
- package/src/model/interval.ts +0 -82
- package/src/model/key.ts +0 -266
- package/src/model/note.ts +0 -176
- package/src/model/progression.ts +0 -161
- package/src/model/shared.ts +0 -12
- package/src/theory/chord/index.ts +0 -373
- package/src/theory/chordscale/index.ts +0 -374
- package/src/theory/counterpoint/index.ts +0 -230
- package/src/theory/harmony/index.ts +0 -92
- package/src/theory/index.ts +0 -107
- package/src/theory/safety/index.ts +0 -419
- package/src/theory/scale/degrees.ts +0 -123
- package/src/theory/scale/index.ts +0 -28
- package/src/theory/scale/key.ts +0 -56
- package/src/theory/scale/masks.ts +0 -153
- package/src/theory/spelling/index.ts +0 -182
- package/src/theory/symbol/index.ts +0 -268
- package/src/theory/voicing/index.ts +0 -5
- package/src/theory/voicing/internal.ts +0 -168
- package/src/theory/voicing/leading.ts +0 -112
- package/src/theory/voicing/satb.ts +0 -195
- package/src/theory/voicing/styled.ts +0 -176
- package/tsconfig.json +0 -23
package/src/theory/index.ts
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Public API of the theory layer. Re-exports every theory-layer module;
|
|
3
|
-
* also available from the package root.
|
|
4
|
-
*/
|
|
5
|
-
export type { Chord as ChordData, ChordQuality } from './chord/index.js';
|
|
6
|
-
export {
|
|
7
|
-
chordFromDegree,
|
|
8
|
-
chordPitchClasses,
|
|
9
|
-
chordQualities,
|
|
10
|
-
chordToneRole,
|
|
11
|
-
diatonicSeventh,
|
|
12
|
-
diatonicTriad,
|
|
13
|
-
makeChord,
|
|
14
|
-
} from './chord/index.js';
|
|
15
|
-
export type {
|
|
16
|
-
ChordScaleMatch,
|
|
17
|
-
ChordScaleReportEntry,
|
|
18
|
-
ScaleChoice,
|
|
19
|
-
} from './chordscale/index.js';
|
|
20
|
-
export {
|
|
21
|
-
availableTensions,
|
|
22
|
-
avoidNotes,
|
|
23
|
-
chordScaleReport,
|
|
24
|
-
chordScales,
|
|
25
|
-
scaleMatchesChord,
|
|
26
|
-
scalesForChanges,
|
|
27
|
-
} from './chordscale/index.js';
|
|
28
|
-
export {
|
|
29
|
-
createsHiddenParallelPerfect,
|
|
30
|
-
createsParallelOctave,
|
|
31
|
-
createsParallelPerfect,
|
|
32
|
-
createsParallelUnison,
|
|
33
|
-
createsVerticalDissonance,
|
|
34
|
-
createsVoiceCrossing,
|
|
35
|
-
createsVoiceOverlap,
|
|
36
|
-
exceedsSpacing,
|
|
37
|
-
isForbiddenMelodicLeap,
|
|
38
|
-
isLeadingToneResolution,
|
|
39
|
-
} from './counterpoint/index.js';
|
|
40
|
-
export type { HarmonyRole, LockLevel, VoicedRole } from './harmony/index.js';
|
|
41
|
-
export { roleOf } from './harmony/index.js';
|
|
42
|
-
export type {
|
|
43
|
-
SafetyProfile,
|
|
44
|
-
SafetyQuery,
|
|
45
|
-
SafetyResult,
|
|
46
|
-
VoiceSnapshot,
|
|
47
|
-
} from './safety/index.js';
|
|
48
|
-
export {
|
|
49
|
-
enumerateSafePitches,
|
|
50
|
-
evaluateSafety,
|
|
51
|
-
NoteSafety,
|
|
52
|
-
ReasonFlag,
|
|
53
|
-
} from './safety/index.js';
|
|
54
|
-
export {
|
|
55
|
-
BLUES_MASK,
|
|
56
|
-
CHROMATIC_MASK,
|
|
57
|
-
DORIAN_MASK,
|
|
58
|
-
diatonicPitchClasses,
|
|
59
|
-
HARMONIC_MINOR_MASK,
|
|
60
|
-
isScaleTone,
|
|
61
|
-
LOCRIAN_MASK,
|
|
62
|
-
LYDIAN_MASK,
|
|
63
|
-
MAJOR_MASK,
|
|
64
|
-
MAJOR_PENTATONIC_MASK,
|
|
65
|
-
MELODIC_MINOR_MASK,
|
|
66
|
-
MINOR_PENTATONIC_MASK,
|
|
67
|
-
MIXOLYDIAN_MASK,
|
|
68
|
-
majorKey,
|
|
69
|
-
maskFromOffsets,
|
|
70
|
-
minorKey,
|
|
71
|
-
NAMED_SCALES,
|
|
72
|
-
NATURAL_MINOR_MASK,
|
|
73
|
-
nearestScaleTone,
|
|
74
|
-
OCTATONIC_HALF_WHOLE_MASK,
|
|
75
|
-
OCTATONIC_WHOLE_HALF_MASK,
|
|
76
|
-
PHRYGIAN_MASK,
|
|
77
|
-
pitchToScaleDegree,
|
|
78
|
-
scaleByName,
|
|
79
|
-
scaleTonesInDegreeOrder,
|
|
80
|
-
WHOLE_TONE_MASK,
|
|
81
|
-
} from './scale/index.js';
|
|
82
|
-
export {
|
|
83
|
-
noteNames,
|
|
84
|
-
spellChord,
|
|
85
|
-
spellPitchClass,
|
|
86
|
-
spellPitchClasses,
|
|
87
|
-
spellScale,
|
|
88
|
-
} from './spelling/index.js';
|
|
89
|
-
export {
|
|
90
|
-
formatChordSymbol,
|
|
91
|
-
parseChordSymbol,
|
|
92
|
-
transposeChordSymbol,
|
|
93
|
-
} from './symbol/index.js';
|
|
94
|
-
export type {
|
|
95
|
-
StyledVoicingOptions,
|
|
96
|
-
VoiceRange,
|
|
97
|
-
VoicingOptions,
|
|
98
|
-
VoicingStyle,
|
|
99
|
-
} from './voicing/index.js';
|
|
100
|
-
export {
|
|
101
|
-
nextVoicing,
|
|
102
|
-
SATB_RANGES,
|
|
103
|
-
voiceChord,
|
|
104
|
-
voiceChordStyled,
|
|
105
|
-
voiceLeadingCost,
|
|
106
|
-
voiceProgression,
|
|
107
|
-
} from './voicing/index.js';
|
|
@@ -1,419 +0,0 @@
|
|
|
1
|
-
import type { KeyScale } from '../../core/types.js';
|
|
2
|
-
import type { Chord } from '../chord/index.js';
|
|
3
|
-
import { chordPitchClasses } from '../chord/index.js';
|
|
4
|
-
import {
|
|
5
|
-
createsHiddenParallelPerfect,
|
|
6
|
-
createsParallelPerfect,
|
|
7
|
-
createsVerticalDissonance,
|
|
8
|
-
isForbiddenMelodicLeap,
|
|
9
|
-
} from '../counterpoint/index.js';
|
|
10
|
-
import { isScaleTone } from '../scale/index.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Severity policy: `strict` counterpoint vs. lenient `pop` voice-leading.
|
|
14
|
-
*
|
|
15
|
-
* @category Arrangement & Analysis
|
|
16
|
-
*/
|
|
17
|
-
export type SafetyProfile = 'strict' | 'pop';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Overall placeability verdict for a candidate pitch.
|
|
21
|
-
*
|
|
22
|
-
* @category Arrangement & Analysis
|
|
23
|
-
*/
|
|
24
|
-
export enum NoteSafety {
|
|
25
|
-
Safe = 0,
|
|
26
|
-
Warning = 1,
|
|
27
|
-
Dissonant = 2,
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Bit flags describing why a pitch received its verdict.
|
|
32
|
-
*
|
|
33
|
-
* @category Arrangement & Analysis
|
|
34
|
-
*/
|
|
35
|
-
export enum ReasonFlag {
|
|
36
|
-
ChordTone = 1 << 0,
|
|
37
|
-
Tension = 1 << 1,
|
|
38
|
-
AvoidNote = 1 << 2,
|
|
39
|
-
ScaleTone = 1 << 3,
|
|
40
|
-
NonScale = 1 << 4,
|
|
41
|
-
OutOfRange = 1 << 5,
|
|
42
|
-
Tritone = 1 << 6,
|
|
43
|
-
LargeLeap = 1 << 7,
|
|
44
|
-
MinorSecond = 1 << 8,
|
|
45
|
-
MajorSeventh = 1 << 9,
|
|
46
|
-
VerticalDissonance = 1 << 10,
|
|
47
|
-
ParallelPerfect = 1 << 11,
|
|
48
|
-
HiddenParallel = 1 << 12,
|
|
49
|
-
VoiceCrossing = 1 << 13,
|
|
50
|
-
Suspension = 1 << 14,
|
|
51
|
-
NeedsResolution = 1 << 15,
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* A neighbouring voice's current and previous pitch.
|
|
56
|
-
*
|
|
57
|
-
* @category Arrangement & Analysis
|
|
58
|
-
*/
|
|
59
|
-
export type VoiceSnapshot = {
|
|
60
|
-
pitch: number;
|
|
61
|
-
prevPitch?: number;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Everything needed to evaluate one candidate pitch.
|
|
66
|
-
*
|
|
67
|
-
* @category Arrangement & Analysis
|
|
68
|
-
*/
|
|
69
|
-
export type SafetyQuery = {
|
|
70
|
-
profile: SafetyProfile;
|
|
71
|
-
candidatePitch: number;
|
|
72
|
-
prevPitch?: number;
|
|
73
|
-
chord: Chord | null;
|
|
74
|
-
key: KeyScale;
|
|
75
|
-
otherVoices: VoiceSnapshot[];
|
|
76
|
-
strongBeat: boolean;
|
|
77
|
-
vocalLow?: number;
|
|
78
|
-
vocalHigh?: number;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* The verdict, reason flags, and optional resolution guidance.
|
|
83
|
-
*
|
|
84
|
-
* @category Arrangement & Analysis
|
|
85
|
-
*/
|
|
86
|
-
export type SafetyResult = {
|
|
87
|
-
safety: NoteSafety;
|
|
88
|
-
reasons: number;
|
|
89
|
-
resolveTo?: number;
|
|
90
|
-
suggestions?: number[];
|
|
91
|
-
rationale?: string;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
/** Semitone radius searched around a rejected candidate for safe alternatives. */
|
|
95
|
-
const SUGGESTION_WINDOW = 12;
|
|
96
|
-
/** Maximum number of alternative pitches returned in `SafetyResult.suggestions`. */
|
|
97
|
-
const MAX_SUGGESTIONS = 3;
|
|
98
|
-
|
|
99
|
-
function pitchClass(pitch: number): number {
|
|
100
|
-
return ((Math.trunc(pitch) % 12) + 12) % 12;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function intervalAboveRoot(pitch: number, chord: Chord): number {
|
|
104
|
-
return (((pitchClass(pitch) - pitchClass(chord.rootPc)) % 12) + 12) % 12;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function isChordMember(pitch: number, chord: Chord): boolean {
|
|
108
|
-
return chordPitchClasses(chord).includes(pitchClass(pitch));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Whether the perfect fourth is an avoid note over this chord. */
|
|
112
|
-
function avoidsFourth(chord: Chord): boolean {
|
|
113
|
-
return chord.intervals.includes(4) && !chord.intervals.includes(5);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** Whether the major seventh is an avoid note over this chord (dominant). */
|
|
117
|
-
function avoidsMajorSeventh(chord: Chord): boolean {
|
|
118
|
-
return chord.intervals.includes(4) && chord.intervals.includes(10);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** Nearest chord tone at or below the candidate, within two semitones. */
|
|
122
|
-
function stepResolution(pitch: number, chord: Chord): number | undefined {
|
|
123
|
-
for (let delta = 1; delta <= 2; delta += 1) {
|
|
124
|
-
if (isChordMember(pitch - delta, chord)) {
|
|
125
|
-
return pitch - delta;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Evaluate the safety of placing a candidate pitch in a voice.
|
|
133
|
-
*
|
|
134
|
-
* Reasons are merged from harmonic context (chord tone / tension / avoid / scale)
|
|
135
|
-
* and counterpoint (vertical dissonance, parallels, voice crossing). The `strict`
|
|
136
|
-
* profile treats avoid notes and parallels as dissonant; `pop` treats them as
|
|
137
|
-
* warnings. The final verdict is the worst severity among the reasons.
|
|
138
|
-
*
|
|
139
|
-
* @param q The candidate and its harmonic/voice-leading context.
|
|
140
|
-
* @returns The verdict, reason bitmask, and optional resolution guidance.
|
|
141
|
-
* @example
|
|
142
|
-
* ```ts
|
|
143
|
-
* import { evaluateSafety, makeChord, majorKey, NoteSafety } from '@libraz/libcantus';
|
|
144
|
-
* const result = evaluateSafety({
|
|
145
|
-
* profile: 'pop',
|
|
146
|
-
* candidatePitch: 60, // C over a C major chord
|
|
147
|
-
* chord: makeChord(0, 'maj'),
|
|
148
|
-
* key: majorKey(0),
|
|
149
|
-
* otherVoices: [],
|
|
150
|
-
* strongBeat: true,
|
|
151
|
-
* });
|
|
152
|
-
* result.safety === NoteSafety.Safe; // true — C is a chord tone
|
|
153
|
-
* ```
|
|
154
|
-
* @category Arrangement & Analysis
|
|
155
|
-
*/
|
|
156
|
-
export function evaluateSafety(q: SafetyQuery): SafetyResult {
|
|
157
|
-
return evaluateInternal(q, true);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Core safety evaluation.
|
|
162
|
-
*
|
|
163
|
-
* @param q The candidate and its harmonic/voice-leading context.
|
|
164
|
-
* @param collectSuggestions Whether to search for safe alternatives on a
|
|
165
|
-
* non-Safe verdict. Set false during that search itself to bound recursion.
|
|
166
|
-
* @returns The verdict, reason bitmask, and optional guidance.
|
|
167
|
-
*/
|
|
168
|
-
function evaluateInternal(q: SafetyQuery, collectSuggestions: boolean): SafetyResult {
|
|
169
|
-
let reasons = 0;
|
|
170
|
-
let level = NoteSafety.Safe;
|
|
171
|
-
let resolveTo: number | undefined;
|
|
172
|
-
const raise = (l: NoteSafety) => {
|
|
173
|
-
if (l > level) {
|
|
174
|
-
level = l;
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
const avoidSeverity = q.profile === 'strict' ? NoteSafety.Dissonant : NoteSafety.Warning;
|
|
178
|
-
const parallelSeverity = avoidSeverity;
|
|
179
|
-
const pitch = q.candidatePitch;
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
(q.vocalLow !== undefined && pitch < q.vocalLow) ||
|
|
183
|
-
(q.vocalHigh !== undefined && pitch > q.vocalHigh)
|
|
184
|
-
) {
|
|
185
|
-
reasons |= ReasonFlag.OutOfRange;
|
|
186
|
-
raise(NoteSafety.Warning);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const inScale = isScaleTone(pitch, q.key);
|
|
190
|
-
const chord = q.chord;
|
|
191
|
-
|
|
192
|
-
if (chord && isChordMember(pitch, chord)) {
|
|
193
|
-
reasons |= ReasonFlag.ChordTone;
|
|
194
|
-
} else if (chord) {
|
|
195
|
-
const ic = intervalAboveRoot(pitch, chord);
|
|
196
|
-
const isTension = ic === 2 || ic === 5 || ic === 9;
|
|
197
|
-
const avoid = (ic === 5 && avoidsFourth(chord)) || (ic === 11 && avoidsMajorSeventh(chord));
|
|
198
|
-
if (avoid) {
|
|
199
|
-
reasons |= ReasonFlag.AvoidNote;
|
|
200
|
-
if (isTension) {
|
|
201
|
-
reasons |= ReasonFlag.Tension;
|
|
202
|
-
}
|
|
203
|
-
reasons |= ReasonFlag.NeedsResolution;
|
|
204
|
-
resolveTo = stepResolution(pitch, chord);
|
|
205
|
-
raise(avoidSeverity);
|
|
206
|
-
} else if (isTension && inScale) {
|
|
207
|
-
reasons |= ReasonFlag.Tension;
|
|
208
|
-
raise(NoteSafety.Warning);
|
|
209
|
-
} else if (inScale) {
|
|
210
|
-
reasons |= ReasonFlag.ScaleTone;
|
|
211
|
-
raise(NoteSafety.Warning);
|
|
212
|
-
} else {
|
|
213
|
-
reasons |= ReasonFlag.NonScale;
|
|
214
|
-
raise(NoteSafety.Dissonant);
|
|
215
|
-
}
|
|
216
|
-
} else if (inScale) {
|
|
217
|
-
reasons |= ReasonFlag.ScaleTone;
|
|
218
|
-
raise(NoteSafety.Warning);
|
|
219
|
-
} else {
|
|
220
|
-
reasons |= ReasonFlag.NonScale;
|
|
221
|
-
raise(NoteSafety.Dissonant);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Harmonic tritone against a chord tone (except in dominant chords, where the
|
|
225
|
-
// tritone is a defining, stable colour).
|
|
226
|
-
if (chord && !(chord.intervals.includes(4) && chord.intervals.includes(10))) {
|
|
227
|
-
const ic = intervalAboveRoot(pitch, chord);
|
|
228
|
-
const toneOffsets = chord.intervals.map((i) => ((i % 12) + 12) % 12);
|
|
229
|
-
if (toneOffsets.some((t) => Math.abs(ic - t) === 6)) {
|
|
230
|
-
reasons |= ReasonFlag.Tritone;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (q.prevPitch !== undefined) {
|
|
235
|
-
// Quality flags reduce the melodic interval to its pitch-class interval so
|
|
236
|
-
// compound intervals are treated uniformly with their simple form. The
|
|
237
|
-
// forbidden-leap flag defers to the counterpoint rule, which preserves the
|
|
238
|
-
// octave boundary (an octave leap is allowed, wider leaps are not).
|
|
239
|
-
const pc = Math.abs(pitch - q.prevPitch) % 12;
|
|
240
|
-
if (pc === 6) {
|
|
241
|
-
reasons |= ReasonFlag.Tritone;
|
|
242
|
-
}
|
|
243
|
-
if (isForbiddenMelodicLeap(q.prevPitch, pitch)) {
|
|
244
|
-
reasons |= ReasonFlag.LargeLeap;
|
|
245
|
-
}
|
|
246
|
-
if (pc === 1) {
|
|
247
|
-
reasons |= ReasonFlag.MinorSecond;
|
|
248
|
-
}
|
|
249
|
-
if (pc === 11) {
|
|
250
|
-
reasons |= ReasonFlag.MajorSeventh;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (q.strongBeat) {
|
|
255
|
-
const twoVoice = q.otherVoices.length === 1;
|
|
256
|
-
const prev = q.prevPitch;
|
|
257
|
-
for (const ov of q.otherVoices) {
|
|
258
|
-
if (!createsVerticalDissonance(pitch, ov.pitch, twoVoice)) {
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
reasons |= ReasonFlag.VerticalDissonance;
|
|
262
|
-
raise(NoteSafety.Dissonant);
|
|
263
|
-
// A held pitch that was consonant on the previous step and is now
|
|
264
|
-
// dissonant against the same voice is a prepared suspension.
|
|
265
|
-
if (
|
|
266
|
-
prev !== undefined &&
|
|
267
|
-
pitch === prev &&
|
|
268
|
-
ov.prevPitch !== undefined &&
|
|
269
|
-
!createsVerticalDissonance(prev, ov.prevPitch, twoVoice)
|
|
270
|
-
) {
|
|
271
|
-
reasons |= ReasonFlag.Suspension;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
if (q.prevPitch !== undefined) {
|
|
277
|
-
const prev = q.prevPitch;
|
|
278
|
-
for (const ov of q.otherVoices) {
|
|
279
|
-
if (ov.prevPitch === undefined) {
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
282
|
-
// createsParallelPerfect covers every perfect class (unison/octave and
|
|
283
|
-
// fifth), so no separate octave check is needed.
|
|
284
|
-
if (createsParallelPerfect(prev, pitch, ov.prevPitch, ov.pitch)) {
|
|
285
|
-
reasons |= ReasonFlag.ParallelPerfect;
|
|
286
|
-
raise(parallelSeverity);
|
|
287
|
-
}
|
|
288
|
-
if (createsHiddenParallelPerfect(prev, pitch, ov.prevPitch, ov.pitch)) {
|
|
289
|
-
reasons |= ReasonFlag.HiddenParallel;
|
|
290
|
-
raise(parallelSeverity);
|
|
291
|
-
}
|
|
292
|
-
const crossedNow = pitch - ov.pitch;
|
|
293
|
-
const crossedPrev = prev - ov.prevPitch;
|
|
294
|
-
if (crossedNow !== 0 && crossedPrev !== 0 && crossedNow > 0 !== crossedPrev > 0) {
|
|
295
|
-
reasons |= ReasonFlag.VoiceCrossing;
|
|
296
|
-
raise(parallelSeverity);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const result: SafetyResult = { safety: level, reasons, rationale: describe(reasons, q) };
|
|
302
|
-
if (resolveTo !== undefined) {
|
|
303
|
-
result.resolveTo = resolveTo;
|
|
304
|
-
}
|
|
305
|
-
if (collectSuggestions && level !== NoteSafety.Safe) {
|
|
306
|
-
const suggestions = findSafeNearby(q, pitch);
|
|
307
|
-
if (suggestions.length > 0) {
|
|
308
|
-
result.suggestions = suggestions;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
return result;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Find safe alternative pitches near a rejected candidate.
|
|
316
|
-
*
|
|
317
|
-
* Searches outward from the candidate within `SUGGESTION_WINDOW` semitones,
|
|
318
|
-
* probing the pitch below before the one above at each distance so the result
|
|
319
|
-
* is deterministic and ordered by nearness.
|
|
320
|
-
*
|
|
321
|
-
* @param q The safety context of the rejected candidate.
|
|
322
|
-
* @param candidate The candidate pitch that was not Safe.
|
|
323
|
-
* @returns Up to `MAX_SUGGESTIONS` safe pitches, nearest first.
|
|
324
|
-
*/
|
|
325
|
-
function findSafeNearby(q: SafetyQuery, candidate: number): number[] {
|
|
326
|
-
const out: number[] = [];
|
|
327
|
-
for (let d = 1; d <= SUGGESTION_WINDOW; d += 1) {
|
|
328
|
-
for (const p of [candidate - d, candidate + d]) {
|
|
329
|
-
if (evaluateInternal({ ...q, candidatePitch: p }, false).safety === NoteSafety.Safe) {
|
|
330
|
-
out.push(p);
|
|
331
|
-
if (out.length >= MAX_SUGGESTIONS) {
|
|
332
|
-
return out;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return out;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/** Build a short human explanation from the reason flags. */
|
|
341
|
-
function describe(reasons: number, q: SafetyQuery): string {
|
|
342
|
-
if (reasons & ReasonFlag.OutOfRange) {
|
|
343
|
-
return 'Outside the target vocal range';
|
|
344
|
-
}
|
|
345
|
-
if (reasons & ReasonFlag.Suspension) {
|
|
346
|
-
return 'Prepared suspension — a held consonance now dissonant, awaiting resolution';
|
|
347
|
-
}
|
|
348
|
-
if (reasons & ReasonFlag.VerticalDissonance) {
|
|
349
|
-
return 'Dissonant against a sounding voice on a strong beat';
|
|
350
|
-
}
|
|
351
|
-
if (reasons & ReasonFlag.AvoidNote) {
|
|
352
|
-
return 'Avoid note — clashes with a chord tone a semitone away';
|
|
353
|
-
}
|
|
354
|
-
if (reasons & ReasonFlag.ParallelPerfect) {
|
|
355
|
-
return 'Parallel perfect interval with another voice';
|
|
356
|
-
}
|
|
357
|
-
if (reasons & ReasonFlag.HiddenParallel) {
|
|
358
|
-
return 'Hidden perfect interval by similar motion';
|
|
359
|
-
}
|
|
360
|
-
if (reasons & ReasonFlag.VoiceCrossing) {
|
|
361
|
-
return 'Crosses another voice';
|
|
362
|
-
}
|
|
363
|
-
if (reasons & ReasonFlag.ChordTone) {
|
|
364
|
-
return 'Chord tone';
|
|
365
|
-
}
|
|
366
|
-
if (reasons & ReasonFlag.Tension) {
|
|
367
|
-
return 'Chord tension';
|
|
368
|
-
}
|
|
369
|
-
if (reasons & ReasonFlag.NonScale) {
|
|
370
|
-
return 'Outside the key';
|
|
371
|
-
}
|
|
372
|
-
if (reasons & ReasonFlag.ScaleTone) {
|
|
373
|
-
return 'Scale tone, not in the chord';
|
|
374
|
-
}
|
|
375
|
-
return q.chord ? 'Placeable' : 'No chord context';
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Enumerate placeable pitches in a range, chord tones first, descending.
|
|
380
|
-
*
|
|
381
|
-
* @param q The safety context minus the candidate pitch.
|
|
382
|
-
* @param pitchLow Lowest MIDI pitch to consider (inclusive).
|
|
383
|
-
* @param pitchHigh Highest MIDI pitch to consider (inclusive).
|
|
384
|
-
* @returns Placeable pitches (non-dissonant), chord tones before others, each group descending.
|
|
385
|
-
* @example
|
|
386
|
-
* ```ts
|
|
387
|
-
* import { enumerateSafePitches, makeChord, majorKey } from '@libraz/libcantus';
|
|
388
|
-
* const pitches = enumerateSafePitches(
|
|
389
|
-
* { profile: 'pop', chord: makeChord(0, 'maj'), key: majorKey(0), otherVoices: [], strongBeat: true },
|
|
390
|
-
* 60,
|
|
391
|
-
* 72,
|
|
392
|
-
* );
|
|
393
|
-
* pitches; // placeable pitches in [60, 72], chord tones first, each group descending
|
|
394
|
-
* ```
|
|
395
|
-
* @category Arrangement & Analysis
|
|
396
|
-
*/
|
|
397
|
-
export function enumerateSafePitches(
|
|
398
|
-
q: Omit<SafetyQuery, 'candidatePitch'>,
|
|
399
|
-
pitchLow: number,
|
|
400
|
-
pitchHigh: number,
|
|
401
|
-
): number[] {
|
|
402
|
-
const chordTones: number[] = [];
|
|
403
|
-
const others: number[] = [];
|
|
404
|
-
if (!Number.isFinite(pitchLow) || !Number.isFinite(pitchHigh)) {
|
|
405
|
-
return [];
|
|
406
|
-
}
|
|
407
|
-
for (let pitch = pitchHigh; pitch >= pitchLow; pitch -= 1) {
|
|
408
|
-
const result = evaluateInternal({ ...q, candidatePitch: pitch }, false);
|
|
409
|
-
if (result.safety === NoteSafety.Dissonant) {
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
if (result.reasons & ReasonFlag.ChordTone) {
|
|
413
|
-
chordTones.push(pitch);
|
|
414
|
-
} else {
|
|
415
|
-
others.push(pitch);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
return [...chordTones, ...others];
|
|
419
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import type { KeyScale } from '../../core/types.js';
|
|
2
|
-
|
|
3
|
-
/** Reduce a pitch (or pitch class) to a pitch class in [0, 11]. */
|
|
4
|
-
function pitchClass(pitch: number): number {
|
|
5
|
-
return ((Math.trunc(pitch) % 12) + 12) % 12;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
/** Scale offset of a pitch relative to the key root, in [0, 11]. */
|
|
9
|
-
function scaleOffset(pitch: number, key: KeyScale): number {
|
|
10
|
-
return (pitchClass(pitch) - pitchClass(key.rootPc) + 12) % 12;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Test whether a pitch belongs to the scale.
|
|
15
|
-
*
|
|
16
|
-
* @param pitch MIDI pitch or bare pitch class.
|
|
17
|
-
* @param key The key/scale to test against.
|
|
18
|
-
* @returns True if the pitch class is a member of the scale.
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```ts
|
|
22
|
-
* import { majorKey, isScaleTone } from '@libraz/libcantus';
|
|
23
|
-
* const key = majorKey(0); // C major
|
|
24
|
-
* isScaleTone(64, key); // true — E is in C major
|
|
25
|
-
* isScaleTone(61, key); // false — C# is not
|
|
26
|
-
* ```
|
|
27
|
-
*
|
|
28
|
-
* @category Scales
|
|
29
|
-
*/
|
|
30
|
-
export function isScaleTone(pitch: number, key: KeyScale): boolean {
|
|
31
|
-
return ((key.modeMask12 >> scaleOffset(pitch, key)) & 1) === 1;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Find the nearest MIDI pitch whose pitch class is in the scale.
|
|
36
|
-
*
|
|
37
|
-
* The search expands symmetrically outward from `pitch`. On a tie (equal
|
|
38
|
-
* distance above and below) the lower pitch is returned.
|
|
39
|
-
*
|
|
40
|
-
* @param pitch MIDI pitch to snap.
|
|
41
|
-
* @param key The key/scale to snap to.
|
|
42
|
-
* @returns The nearest in-scale MIDI pitch.
|
|
43
|
-
*
|
|
44
|
-
* @category Scales
|
|
45
|
-
*/
|
|
46
|
-
export function nearestScaleTone(pitch: number, key: KeyScale): number {
|
|
47
|
-
const base = Math.round(pitch);
|
|
48
|
-
for (let distance = 0; distance < 12; distance += 1) {
|
|
49
|
-
const lower = base - distance;
|
|
50
|
-
if (isScaleTone(lower, key)) {
|
|
51
|
-
return lower;
|
|
52
|
-
}
|
|
53
|
-
const higher = base + distance;
|
|
54
|
-
if (isScaleTone(higher, key)) {
|
|
55
|
-
return higher;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return base;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get the 0-based scale degree of a pitch.
|
|
63
|
-
*
|
|
64
|
-
* @param pitch MIDI pitch or bare pitch class.
|
|
65
|
-
* @param key The key/scale to measure against.
|
|
66
|
-
* @returns The 0-based degree, or -1 if the pitch is not in the scale.
|
|
67
|
-
*
|
|
68
|
-
* @category Scales
|
|
69
|
-
*/
|
|
70
|
-
export function pitchToScaleDegree(pitch: number, key: KeyScale): number {
|
|
71
|
-
const offset = scaleOffset(pitch, key);
|
|
72
|
-
if (((key.modeMask12 >> offset) & 1) === 0) {
|
|
73
|
-
return -1;
|
|
74
|
-
}
|
|
75
|
-
let degree = 0;
|
|
76
|
-
for (let n = 0; n < offset; n += 1) {
|
|
77
|
-
if (((key.modeMask12 >> n) & 1) === 1) {
|
|
78
|
-
degree += 1;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return degree;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* List the pitch classes of a key's scale.
|
|
86
|
-
*
|
|
87
|
-
* @param key The key/scale to enumerate.
|
|
88
|
-
* @returns The member pitch classes, sorted ascending in [0, 11].
|
|
89
|
-
*
|
|
90
|
-
* @category Scales
|
|
91
|
-
*/
|
|
92
|
-
export function diatonicPitchClasses(key: KeyScale): number[] {
|
|
93
|
-
const root = pitchClass(key.rootPc);
|
|
94
|
-
const pcs: number[] = [];
|
|
95
|
-
for (let n = 0; n < 12; n += 1) {
|
|
96
|
-
if (((key.modeMask12 >> n) & 1) === 1) {
|
|
97
|
-
pcs.push((root + n) % 12);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return pcs.sort((a, b) => a - b);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* List the pitch classes of a key's scale in ascending scale-degree order.
|
|
105
|
-
*
|
|
106
|
-
* Degree 0 is the root; degrees follow the mask bits in offset order rather
|
|
107
|
-
* than sorted pitch-class order.
|
|
108
|
-
*
|
|
109
|
-
* @param key The key/scale to enumerate.
|
|
110
|
-
* @returns The member pitch classes ordered by scale degree.
|
|
111
|
-
*
|
|
112
|
-
* @category Scales
|
|
113
|
-
*/
|
|
114
|
-
export function scaleTonesInDegreeOrder(key: KeyScale): number[] {
|
|
115
|
-
const root = pitchClass(key.rootPc);
|
|
116
|
-
const pcs: number[] = [];
|
|
117
|
-
for (let n = 0; n < 12; n += 1) {
|
|
118
|
-
if (((key.modeMask12 >> n) & 1) === 1) {
|
|
119
|
-
pcs.push((root + n) % 12);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return pcs;
|
|
123
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
diatonicPitchClasses,
|
|
3
|
-
isScaleTone,
|
|
4
|
-
nearestScaleTone,
|
|
5
|
-
pitchToScaleDegree,
|
|
6
|
-
scaleTonesInDegreeOrder,
|
|
7
|
-
} from './degrees.js';
|
|
8
|
-
export { majorKey, minorKey, scaleByName } from './key.js';
|
|
9
|
-
export {
|
|
10
|
-
BLUES_MASK,
|
|
11
|
-
CHROMATIC_MASK,
|
|
12
|
-
DORIAN_MASK,
|
|
13
|
-
HARMONIC_MINOR_MASK,
|
|
14
|
-
LOCRIAN_MASK,
|
|
15
|
-
LYDIAN_MASK,
|
|
16
|
-
MAJOR_MASK,
|
|
17
|
-
MAJOR_PENTATONIC_MASK,
|
|
18
|
-
MELODIC_MINOR_MASK,
|
|
19
|
-
MINOR_PENTATONIC_MASK,
|
|
20
|
-
MIXOLYDIAN_MASK,
|
|
21
|
-
maskFromOffsets,
|
|
22
|
-
NAMED_SCALES,
|
|
23
|
-
NATURAL_MINOR_MASK,
|
|
24
|
-
OCTATONIC_HALF_WHOLE_MASK,
|
|
25
|
-
OCTATONIC_WHOLE_HALF_MASK,
|
|
26
|
-
PHRYGIAN_MASK,
|
|
27
|
-
WHOLE_TONE_MASK,
|
|
28
|
-
} from './masks.js';
|