UNPKG

brand-music

Version:

Strongly typed music theory library

1 lines 11.1 kB
{"version":3,"sources":["../src/PitchClass.ts","../src/internal/utils.ts","../src/internal/refined/Literal.ts","../src/internal/refined/Int.ts","../src/internal/PitchClass.ts","../src/internal/PitchIntervalClass.ts","../src/internal/conversion.ts"],"sourcesContent":["export {\n pcFromMnn as fromMnn,\n icBetweenPc as ic,\n picBetweenPc as pic,\n pcTranspose as transpose,\n} from \"./internal/conversion.js\";\nexport * from \"./internal/PitchClass.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","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 PitchClassBrand {\n readonly PitchClass: unique symbol;\n}\n\n/**\n * Integer notation of pitch class\n *\n * @see {@link https://en.wikipedia.org/wiki/Pitch_class}\n */\nexport type PitchClass = Shape & PitchClassBrand;\n\nexport const mark = (_v: Shape): _v is PitchClass => true;\nexport const markNum = (v: number): v is PitchClass => hasShape(v);\nexport const markUnknown = (v: unknown): v is PitchClass => hasShape(v);\n\nexport const from = (v: Shape) => v as PitchClass;\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) =>\n is(min, max)(v) ? from(v) : undefined;\n\nconst const_ = <T extends Shape>(value: T) => value as T & PitchClass;\nexport const MIN = const_(min);\nexport const MAX = const_(max);\nexport const ALL: readonly PitchClass[] = all(min, max).map(const_);\n\nexport const invert = (v: PitchClass, index: Shape = 0) =>\n fromIntMod(I.sub(asInt(index), asInt(v)));\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 * 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,sBAAA;AAAA,SAAAA,qBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAAAA;;;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;AAEzB,IAAM,QACX,CAAqC,OAAU,QAC/C,CAAC,MACC,IAAI,QAAQ,QAAQ,IAAI,MAAM,MAAO;AAKlC,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;;;AC7ChD,IAAM,MAAM,CAAC,GAAQ,MAAY,IAAI;AAGrC,IAAM,MAAM,CAAC,GAAQ,MAAY,IAAI;;;ACZ5C,IAAM,MAAM;AACZ,IAAM,MAAM;AAEL,IAAM,WAAW,GAAG,KAAK,GAAG;AAa5B,IAAM,OAAO,CAAC,OAAgC;AAC9C,IAAM,UAAU,CAAC,MAA+B,SAAS,CAAC;AAC1D,IAAM,cAAc,CAAC,MAAgC,SAAS,CAAC;AAE/D,IAAM,OAAO,CAAC,MAAa;AAC3B,IAAM,UAAU,CAAC,MAAe,QAAQ,CAAC,IAAI,IAAI;AACjD,IAAM,eAAe,CAAC,MAAa,KAAK,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC;AAC1D,IAAM,aAAa,CAAC,MAAa,KAAK,OAAO,KAAK,GAAG,EAAE,CAAC,CAAC;AACzD,IAAM,cAAc,CAAC,MAC1B,GAAG,KAAK,GAAG,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI;AAE9B,IAAM,SAAS,CAAkB,UAAa;AACvC,IAAM,MAAM,OAAO,GAAG;AACtB,IAAM,MAAM,OAAO,GAAG;AACtB,IAAM,MAA6B,IAAI,KAAK,GAAG,EAAE,IAAI,MAAM;AAE3D,IAAM,SAAS,CAAC,GAAe,QAAe,MACnD,WAAa,IAAI,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;;;ACjC1C,IAAMC,OAAM;AACZ,IAAMC,OAAM;AAEL,IAAMC,YAAW,GAAGF,MAAKC,IAAG;AAkB5B,IAAME,QAAO,CAAC,MAAa;AAG3B,IAAMC,cAAa,CAAC,MAAaC,MAAK,OAAOC,MAAKC,IAAG,EAAE,CAAC,CAAC;AAGhE,IAAMC,UAAS,CAAkB,UAAa;AACvC,IAAMC,OAAMD,QAAOE,IAAG;AACtB,IAAMC,OAAMH,QAAOI,IAAG;AACtB,IAAMC,OAAqC,IAAIH,MAAKE,IAAG,EAAE,IAAIJ,OAAM;AAEnE,IAAMM,UAAS,CAAC,GAAuB,QAAe,MAC3DC,YAAa,IAAI,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC;;;AC3BnC,IAAM,YAAY,CAAC,QACxB,KAAK,IAAI,KAASC,QAAO,GAAG,CAAC;AACxB,IAAM,cAAc,CAAC,GAAkB,MAC5C,UAAU,aAAa,GAAG,CAAC,CAAC;AAiBvB,IAAM,cAAc,CACzB,OACA,aACM,WAAa,IAAI,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,CAAC;AAChD,IAAM,YAAY,CAAC,QAA+B,WAAW,MAAM,GAAG,CAAC;AAOvE,IAAM,eAAe,CAACC,OAAqB,OAC5CC,YAAa,IAAI,MAAM,EAAE,GAAG,MAAMD,KAAI,CAAC,CAAC;","names":["PitchClass_exports","min","max","hasShape","from","fromIntMod","from","min","max","const_","MIN","min","MAX","max","ALL","invert","fromIntMod","invert","from","fromIntMod"]}