brand-music
Version:
Strongly typed music theory library
1 lines • 11.8 kB
Source Map (JSON)
{"version":3,"sources":["../src/Semitones.ts","../src/internal/utils.ts","../src/internal/refined/Literal.ts","../src/internal/refined/Int.ts","../src/internal/refined/NonNegative.ts","../src/internal/PitchIntervalClass.ts","../src/internal/refined/NonNegativeInt.ts","../src/internal/Semitones.ts","../src/internal/conversion.ts"],"sourcesContent":["export {\n stBetweenMnn as between,\n stFromPi as fromPi,\n picFromSemitones as pic,\n} from \"./internal/conversion.js\";\nexport * from \"./internal/Semitones.js\";\n","// mod(-3, 5) = 2\nexport const mod = (a: number, n: number) => ((a % n) + n) % n;\n","import { mod } from \"../utils.js\";\nimport { Int } from \"./Int.js\";\n\nexport type Negate<N extends number> = N extends 0\n ? 0\n : `${N}` extends `-${infer X extends number}`\n ? X\n : `-${N}` extends `${infer X extends number}`\n ? X\n : number;\n\nexport type RangedNat<\n START extends number,\n END extends number,\n ARR extends unknown[] = [],\n ACC extends number = never,\n> = ARR[\"length\"] extends END\n ? ACC | START | END\n : RangedNat<\n START,\n END,\n [...ARR, 1],\n ARR[START] extends undefined ? ACC : ACC | ARR[\"length\"]\n >;\n\nexport const is =\n <S extends number, E extends number>(start: S, end: E) =>\n <T>(v: T): v is T & RangedNat<S, E> =>\n Number.isSafeInteger(v) &&\n start <= (v as unknown as number) &&\n (v as unknown as number) <= end;\n\nexport const clamp =\n <S extends number, E extends number>(start: S, end: E) =>\n (v: Int): RangedNat<S, E> =>\n v < start ? start : v > end ? end : (v as RangedNat<S, E>);\n\n// range: [s, e]\n// input: -5 -4 -3 -2 -1 0 1 2 3 4 5\n// output: 1 2 3 -2 -1 0 1 2 3 -2 -1\nexport const modded =\n <S extends number, E extends number>(start: S, end: E) =>\n (v: Int): RangedNat<S, E> => {\n const size = end - start + 1;\n const zero = v - start;\n\n const m = mod(zero, size);\n return (m + start) as RangedNat<S, E>;\n };\n\nexport const all = <S extends number, E extends number>(\n start: S,\n end: E,\n): readonly RangedNat<S, E>[] =>\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n [...Array(end - start + 1)].map((_, i) => (start + i) as RangedNat<S, E>);\n\nexport const asInt = (v: RangedNat<number, number>) => v as Int;\n","export interface IntBrand {\n readonly Int: unique symbol;\n}\n\nexport type Int = number & IntBrand;\n\nexport const isInt = <T>(v: T): v is T & Int => Number.isInteger(v);\nexport const fromUnknown = <T>(v: T) => (isInt(v) ? v : undefined);\nexport const makeChecked = <T extends number>(v: T) => fromUnknown(v);\nexport const makeRound = <T extends number>(v: T) => Math.round(v) as T & Int;\nexport const makeTrunc = <T extends number>(v: T) => Math.trunc(v) as T & Int;\n\nexport const add = (a: Int, b: Int) => (a + b) as Int;\nexport const addChecked = (a: number, b: number) => makeChecked(a + b);\n\nexport const sub = (a: Int, b: Int) => (a - b) as Int;\nexport const subChecked = (a: number, b: number) => makeChecked(a - b);\n\nexport const mul = (a: Int, b: Int) => (a * b) as Int;\nexport const mulChecked = (a: number, b: number) => makeChecked(a * b);\nexport const mulRound = (a: number, b: number) => makeRound(a * b);\nexport const mulTrunc = (a: number, b: number) => makeTrunc(a * b);\n\nexport const divChecked = (a: number, b: number) => makeChecked(a * b);\nexport const divRound = (a: number, b: number) => makeRound(a * b);\nexport const divTrunc = (a: number, b: number) => makeTrunc(a * b);\n","export interface NonNegativeBrand {\n readonly NonNegative: unique symbol;\n}\n\nexport type NonNegative = number & NonNegativeBrand;\n\nexport const isNonNegative = <T>(v: T): v is T & NonNegative =>\n typeof v === \"number\" && v >= 0;\n\nexport function makeAbs<T extends number, N extends NonNegative>(\n v: T,\n edgeCases: { NaN: N },\n): (T & NonNegative) | N;\nexport function makeAbs<T extends number>(\n v: T,\n edgeCases?: { NaN?: never },\n): (T & NonNegative) | (0 & NonNegative);\nexport function makeAbs(v: number, edgeCases?: { NaN?: NonNegative }) {\n const caseNaN = edgeCases?.NaN ?? (0 as 0 & NonNegative);\n\n if (Number.isNaN(v)) {\n return caseNaN;\n } else {\n return Math.abs(v);\n }\n}\n","import * as I from \"./refined/Int.js\";\nimport { all, asInt, clamp, is, modded, RangedNat } from \"./refined/Literal.js\";\n\nconst min = 0;\nconst max = 11;\nexport type Shape = RangedNat<typeof min, typeof max>;\nexport const hasShape = is(min, max);\n\nexport interface PitchIntervalClassBrand {\n readonly PitchIntervalClass: unique symbol;\n}\n\n/**\n * Ordered pitch-class interval\n *\n * Number of ascending semitones from one pitch-class to the next, ordered from lowest to highest\n * @see {@link https://en.wikipedia.org/wiki/Pitch_interval#Ordered_pitch-class_intervals_('pitch_interval_class;_PIC')}\n */\nexport type PitchIntervalClass = Shape & PitchIntervalClassBrand;\n\nexport const mark = (_v: Shape): _v is PitchIntervalClass => true;\nexport const markNum = (v: number): v is PitchIntervalClass => hasShape(v);\nexport const markUnknown = (v: unknown): v is PitchIntervalClass => hasShape(v);\n\nexport const from = (v: Shape) => v as PitchIntervalClass;\nexport const fromNum = (v: number) => (markNum(v) ? v : undefined);\nexport const fromIntClamp = (v: I.Int) => from(clamp(min, max)(v));\nexport const fromIntMod = (v: I.Int) => from(modded(min, max)(v));\nexport const fromUnknown = (v: unknown) => (markUnknown(v) ? v : undefined);\n\nconst const_ = <T extends Shape>(value: T) => value as T & PitchIntervalClass;\nexport const MIN = const_(min);\nexport const MAX = const_(max);\nexport const ALL: readonly PitchIntervalClass[] = all(min, max).map(const_);\n\nexport const invert = (v: PitchIntervalClass, index: Shape = 0) =>\n fromIntMod(I.sub(asInt(index), asInt(v)));\n","import { Int, isInt } from \"./Int.js\";\nimport { isNonNegative, NonNegative } from \"./NonNegative.js\";\n\nexport type NonNegativeInt = Int & NonNegative;\n\nexport const isNonNegativeInt = (v: unknown): v is NonNegativeInt =>\n isInt(v) && isNonNegative(v);\n","import * as I from \"./refined/Int.js\";\nimport { RangedNat } from \"./refined/Literal.js\";\nimport { makeAbs } from \"./refined/NonNegative.js\";\nimport { isNonNegativeInt, NonNegativeInt } from \"./refined/NonNegativeInt.js\";\n\ntype Shape = NonNegativeInt;\nexport const hasShape = isNonNegativeInt;\n\nexport interface SemitonesBrand {\n readonly Semitones: unique symbol;\n}\n\n/**\n * Unordered pitch interval\n *\n * Number of semitones that separates one pitch from another.\n * @see {@link https://en.wikipedia.org/wiki/Pitch_interval#Unordered_Pitch_Interval}\n */\nexport type Semitones = NonNegativeInt & SemitonesBrand;\n\nexport const mark = (_v: Shape): _v is Semitones => true;\nexport const markNum = (v: number): v is Semitones => hasShape(v);\nexport const markUnknown = (v: unknown): v is Semitones => hasShape(v);\n\nexport const from = (v: Shape | RangedNat<0, 999>) => v as Semitones;\nexport const fromNum = (v: number) => (markNum(v) ? v : undefined);\nexport const fromIntAbs = (v: I.Int) => from(makeAbs(v) as NonNegativeInt);\nexport const fromUnknown = (v: unknown) => (markUnknown(v) ? v : undefined);\n\nconst const_ = <T>(v: T) => v as T & Semitones;\nexport const MIN = const_(0);\n\nexport const octaves = (st: Semitones) =>\n st === 0 ? (0 as NonNegativeInt) : (I.makeTrunc(st / 12) as NonNegativeInt);\n","import * as IC from \"./IntervalClass.js\";\nimport * as MNN from \"./MidiNoteNumber.js\";\nimport * as PC from \"./PitchClass.js\";\nimport * as PI from \"./PitchInterval.js\";\nimport * as PIC from \"./PitchIntervalClass.js\";\nimport * as I from \"./refined/Int.js\";\nimport { asInt } from \"./refined/Literal.js\";\nimport * as ST from \"./Semitones.js\";\n\nexport const icFromPic = (pic: PIC.PitchIntervalClass) =>\n Math.min(pic, PIC.invert(pic)) as IC.IntervalClass;\nexport const icBetweenPc = (a: PC.PitchClass, b: PC.PitchClass) =>\n icFromPic(picBetweenPc(a, b));\n\nconst mnnAdd = (value: MNN.MidiNoteNumber, interval: PI.PitchInterval) =>\n I.add(asInt(value), asInt(interval));\nexport const mnnTransposeChecked = (\n value: MNN.MidiNoteNumber,\n interval: PI.PitchInterval,\n) => MNN.fromNum(mnnAdd(value, interval));\nexport const mnnTransposeClamped = (\n value: MNN.MidiNoteNumber,\n interval: PI.PitchInterval,\n) => MNN.fromIntClamp(mnnAdd(value, interval));\nexport const mnnTransposeModded = (\n value: MNN.MidiNoteNumber,\n interval: PI.PitchInterval,\n) => MNN.fromIntMod(mnnAdd(value, interval));\n\nexport const pcTranspose = (\n value: PC.PitchClass,\n interval: PIC.PitchIntervalClass,\n) => PC.fromIntMod(I.add(asInt(value), asInt(interval)));\nexport const pcFromMnn = (mnn: MNN.MidiNoteNumber) => PC.fromIntMod(asInt(mnn));\n\nexport const piBetweenMnn = (\n from: MNN.MidiNoteNumber,\n to: MNN.MidiNoteNumber,\n) => PI.from(I.sub(asInt(to), asInt(from)));\n\nexport const picBetweenPc = (from: PC.PitchClass, to: PC.PitchClass) =>\n PIC.fromIntMod(I.sub(asInt(to), asInt(from)));\n\n/**\n * Gets the pitch interval class of the pitch interval.\n *\n * @example\n * ```ts\n * import * as PI from \"brand-music/PitchInterval\";\n * import * as PIC from \"brand-music/PitchIntervalClass\";\n *\n * const picOfPi = PIC.fromPi(PI.from(-2)); // 10\n * const picOfSemitones = PIC.fromSemitones(PI.semitones(PI.from(-2))); // 2\n * ```\n */\nexport const picFromPi = (pi: PI.PitchInterval) => PIC.fromIntMod(pi);\nexport const picFromSemitones = (semitones: ST.Semitones) =>\n PIC.fromIntMod(semitones);\n\nexport const stFromPi = (pi: PI.PitchInterval) => ST.fromIntAbs(pi);\nexport const stBetweenMnn = (a: MNN.MidiNoteNumber, b: MNN.MidiNoteNumber) =>\n ST.fromIntAbs(I.sub(asInt(a), asInt(b)));\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,qBAAA;AAAA,SAAAA,oBAAA;AAAA,aAAAC;AAAA,EAAA;AAAA,cAAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,kBAAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAAAH;;;ACCO,IAAM,MAAM,CAAC,GAAW,OAAgB,IAAI,IAAK,KAAK;;;ACwBtD,IAAM,KACX,CAAqC,OAAU,QAC/C,CAAI,MACF,OAAO,cAAc,CAAC,KACtB,SAAU,KACT,KAA2B;AAUzB,IAAM,SACX,CAAqC,OAAU,QAC/C,CAAC,MAA4B;AAC3B,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,OAAO,IAAI;AAEjB,QAAM,IAAI,IAAI,MAAM,IAAI;AACxB,SAAQ,IAAI;AACd;AAEK,IAAM,MAAM,CACjB,OACA;AAAA;AAAA,EAGA,CAAC,GAAG,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,MAAO,QAAQ,CAAqB;AAAA;AAEnE,IAAM,QAAQ,CAAC,MAAiC;;;ACnDhD,IAAM,QAAQ,CAAI,MAAuB,OAAO,UAAU,CAAC;AAI3D,IAAM,YAAY,CAAmB,MAAS,KAAK,MAAM,CAAC;AAK1D,IAAM,MAAM,CAAC,GAAQ,MAAY,IAAI;;;ACTrC,IAAM,gBAAgB,CAAI,MAC/B,OAAO,MAAM,YAAY,KAAK;AAUzB,SAAS,QAAQ,GAAW,WAAmC;AACpE,QAAM,UAAU,WAAW,OAAQ;AAEnC,MAAI,OAAO,MAAM,CAAC,GAAG;AACnB,WAAO;AAAA,EACT,OAAO;AACL,WAAO,KAAK,IAAI,CAAC;AAAA,EACnB;AACF;;;ACtBA,IAAM,MAAM;AACZ,IAAM,MAAM;AAEL,IAAM,WAAW,GAAG,KAAK,GAAG;AAkB5B,IAAM,OAAO,CAAC,MAAa;AAG3B,IAAM,aAAa,CAAC,MAAa,KAAK,OAAO,KAAK,GAAG,EAAE,CAAC,CAAC;AAGhE,IAAM,SAAS,CAAkB,UAAa;AACvC,IAAM,MAAM,OAAO,GAAG;AACtB,IAAM,MAAM,OAAO,GAAG;AACtB,IAAM,MAAqC,IAAI,KAAK,GAAG,EAAE,IAAI,MAAM;;;AC5BnE,IAAM,mBAAmB,CAAC,MAC/B,MAAM,CAAC,KAAK,cAAc,CAAC;;;ACAtB,IAAMI,YAAW;AAcjB,IAAM,OAAO,CAAC,OAA+B;AAC7C,IAAM,UAAU,CAAC,MAA8BA,UAAS,CAAC;AACzD,IAAM,cAAc,CAAC,MAA+BA,UAAS,CAAC;AAE9D,IAAMC,QAAO,CAAC,MAAiC;AAC/C,IAAM,UAAU,CAAC,MAAe,QAAQ,CAAC,IAAI,IAAI;AACjD,IAAM,aAAa,CAAC,MAAaA,MAAK,QAAQ,CAAC,CAAmB;AAClE,IAAM,cAAc,CAAC,MAAgB,YAAY,CAAC,IAAI,IAAI;AAEjE,IAAMC,UAAS,CAAI,MAAS;AACrB,IAAMC,OAAMD,QAAO,CAAC;AAEpB,IAAM,UAAU,CAAC,OACtB,OAAO,IAAK,IAA0B,UAAU,KAAK,EAAE;;;ACuBlD,IAAM,mBAAmB,CAAC,cAC3B,WAAW,SAAS;AAEnB,IAAM,WAAW,CAAC,OAA4B,WAAW,EAAE;AAC3D,IAAM,eAAe,CAAC,GAAuB,MAC/C,WAAa,IAAI,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;","names":["Semitones_exports","MIN","from","hasShape","hasShape","from","const_","MIN"]}