@libraz/libcantus 0.9.3 → 0.9.5

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.
Files changed (201) hide show
  1. package/README.md +29 -20
  2. package/README_ja.md +268 -0
  3. package/dist/analyze/index.cjs +39 -1947
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +161 -91
  6. package/dist/analyze/index.d.ts +161 -91
  7. package/dist/analyze/index.js +19 -8
  8. package/dist/chunk-2A2QMA74.cjs +261 -0
  9. package/dist/chunk-2A2QMA74.cjs.map +1 -0
  10. package/dist/chunk-2PTFGVD7.js +261 -0
  11. package/dist/chunk-2PTFGVD7.js.map +1 -0
  12. package/dist/chunk-357ZDPVU.cjs +162 -0
  13. package/dist/chunk-357ZDPVU.cjs.map +1 -0
  14. package/dist/chunk-3KX76RLG.js +1548 -0
  15. package/dist/chunk-3KX76RLG.js.map +1 -0
  16. package/dist/chunk-4VZ5MZAM.cjs +889 -0
  17. package/dist/chunk-4VZ5MZAM.cjs.map +1 -0
  18. package/dist/{chunk-C5XYKUVG.js → chunk-7EONYVI5.js} +471 -171
  19. package/dist/chunk-7EONYVI5.js.map +1 -0
  20. package/dist/{chunk-IT2O4XFE.js → chunk-7JEP3MFY.js} +42 -34
  21. package/dist/chunk-7JEP3MFY.js.map +1 -0
  22. package/dist/{chunk-4MHA432G.js → chunk-AID75RN3.js} +572 -250
  23. package/dist/chunk-AID75RN3.js.map +1 -0
  24. package/dist/chunk-BSLTLGM3.cjs +497 -0
  25. package/dist/chunk-BSLTLGM3.cjs.map +1 -0
  26. package/dist/chunk-CU6VG2YM.cjs +109 -0
  27. package/dist/chunk-CU6VG2YM.cjs.map +1 -0
  28. package/dist/{chunk-EH6EM37U.js → chunk-DZMHIGI4.js} +81 -59
  29. package/dist/chunk-DZMHIGI4.js.map +1 -0
  30. package/dist/{chunk-AKLYPWIN.js → chunk-EDOTEABK.js} +45 -19
  31. package/dist/chunk-EDOTEABK.js.map +1 -0
  32. package/dist/{chunk-2ZWMSJH6.js → chunk-HN2OEUN2.js} +7 -4
  33. package/dist/chunk-HN2OEUN2.js.map +1 -0
  34. package/dist/{chunk-MIWO425M.js → chunk-KBTTEX7Q.js} +128 -43
  35. package/dist/chunk-KBTTEX7Q.js.map +1 -0
  36. package/dist/chunk-KQN2ZYND.cjs +724 -0
  37. package/dist/chunk-KQN2ZYND.cjs.map +1 -0
  38. package/dist/chunk-MPTVJIVG.cjs +3176 -0
  39. package/dist/chunk-MPTVJIVG.cjs.map +1 -0
  40. package/dist/chunk-MTMZTMWM.cjs +53 -0
  41. package/dist/chunk-MTMZTMWM.cjs.map +1 -0
  42. package/dist/{chunk-CHMXELDS.js → chunk-NNB67XA2.js} +372 -197
  43. package/dist/chunk-NNB67XA2.js.map +1 -0
  44. package/dist/chunk-OL3IHHSJ.cjs +1204 -0
  45. package/dist/chunk-OL3IHHSJ.cjs.map +1 -0
  46. package/dist/chunk-PI3UXRWK.cjs +528 -0
  47. package/dist/chunk-PI3UXRWK.cjs.map +1 -0
  48. package/dist/chunk-R2VVFAIN.js +528 -0
  49. package/dist/chunk-R2VVFAIN.js.map +1 -0
  50. package/dist/chunk-T64C7ZD7.cjs +1 -0
  51. package/dist/chunk-T64C7ZD7.cjs.map +1 -0
  52. package/dist/chunk-WFDXOLUB.cjs +337 -0
  53. package/dist/chunk-WFDXOLUB.cjs.map +1 -0
  54. package/dist/chunk-WTAN4ACG.cjs +1548 -0
  55. package/dist/chunk-WTAN4ACG.cjs.map +1 -0
  56. package/dist/chunk-YFJXS3AU.cjs +33 -0
  57. package/dist/chunk-YFJXS3AU.cjs.map +1 -0
  58. package/dist/{chunk-GOAW4VYL.js → chunk-YUE6TJ4M.js} +315 -145
  59. package/dist/chunk-YUE6TJ4M.js.map +1 -0
  60. package/dist/{chunk-62QZKCJQ.js → chunk-ZGLJM4A4.js} +10 -6
  61. package/dist/chunk-ZGLJM4A4.js.map +1 -0
  62. package/dist/core/index.cjs +140 -647
  63. package/dist/core/index.cjs.map +1 -1
  64. package/dist/core/index.d.cts +313 -41
  65. package/dist/core/index.d.ts +313 -41
  66. package/dist/core/index.js +64 -16
  67. package/dist/{function-DVH4Eusr.d.ts → function-CxixTB02.d.cts} +122 -10
  68. package/dist/{function-DYt4iKsn.d.cts → function-DTCt675z.d.ts} +122 -10
  69. package/dist/generate/index.cjs +28 -4085
  70. package/dist/generate/index.cjs.map +1 -1
  71. package/dist/generate/index.d.cts +374 -66
  72. package/dist/generate/index.d.ts +374 -66
  73. package/dist/generate/index.js +23 -9
  74. package/dist/{index-Jc0Ryi79.d.ts → index-B2Ulxxpo.d.cts} +21 -5
  75. package/dist/{index-CpOsP1y9.d.cts → index-BkYJ0xMq.d.cts} +18 -17
  76. package/dist/{index-CxqBQtH1.d.cts → index-BuaB-ypt.d.ts} +21 -5
  77. package/dist/index-CAN7v-kw.d.ts +199 -0
  78. package/dist/{index-Bt4tOUD9.d.ts → index-COw1Ky9g.d.ts} +93 -8
  79. package/dist/{index-Dikst44v.d.cts → index-Cfx8xgH0.d.ts} +14 -11
  80. package/dist/{index-DxvARIP2.d.ts → index-DRtIhlJj.d.ts} +18 -17
  81. package/dist/{index-Cl7mhOFM.d.cts → index-DXd9JikN.d.cts} +92 -10
  82. package/dist/{index-Cl7mhOFM.d.ts → index-DXd9JikN.d.ts} +92 -10
  83. package/dist/index-alrRninU.d.cts +199 -0
  84. package/dist/{index-BNlGP86A.d.cts → index-oTQbYYIs.d.cts} +93 -8
  85. package/dist/index-tR9jTn26.d.cts +275 -0
  86. package/dist/index-tR9jTn26.d.ts +275 -0
  87. package/dist/{index-Bg5dYMmZ.d.ts → index-x2vcsduE.d.cts} +14 -11
  88. package/dist/index.cjs +429 -7383
  89. package/dist/index.cjs.map +1 -1
  90. package/dist/index.d.cts +15 -15
  91. package/dist/index.d.ts +15 -15
  92. package/dist/index.js +121 -27
  93. package/dist/model/index.cjs +8 -2562
  94. package/dist/model/index.cjs.map +1 -1
  95. package/dist/model/index.d.cts +471 -81
  96. package/dist/model/index.d.ts +471 -81
  97. package/dist/model/index.js +6 -7
  98. package/dist/styled-Bl0ZK28h.d.ts +543 -0
  99. package/dist/styled-Crv6SA4S.d.cts +543 -0
  100. package/dist/theory/index.cjs +172 -1822
  101. package/dist/theory/index.cjs.map +1 -1
  102. package/dist/theory/index.d.cts +151 -233
  103. package/dist/theory/index.d.ts +151 -233
  104. package/dist/theory/index.js +30 -7
  105. package/dist/{types-MGP29r3t.d.cts → types-BN6_Q9Kl.d.cts} +11 -2
  106. package/dist/{types-MGP29r3t.d.ts → types-BN6_Q9Kl.d.ts} +11 -2
  107. package/package.json +34 -13
  108. package/dist/chunk-2ZWMSJH6.js.map +0 -1
  109. package/dist/chunk-4MHA432G.js.map +0 -1
  110. package/dist/chunk-62QZKCJQ.js.map +0 -1
  111. package/dist/chunk-AKLYPWIN.js.map +0 -1
  112. package/dist/chunk-C5XYKUVG.js.map +0 -1
  113. package/dist/chunk-CHMXELDS.js.map +0 -1
  114. package/dist/chunk-DYWFNC4A.js +0 -152
  115. package/dist/chunk-DYWFNC4A.js.map +0 -1
  116. package/dist/chunk-EH6EM37U.js.map +0 -1
  117. package/dist/chunk-FBQK3ZQN.js +0 -131
  118. package/dist/chunk-FBQK3ZQN.js.map +0 -1
  119. package/dist/chunk-GOAW4VYL.js.map +0 -1
  120. package/dist/chunk-I5R7ZBJS.js +0 -202
  121. package/dist/chunk-I5R7ZBJS.js.map +0 -1
  122. package/dist/chunk-IT2O4XFE.js.map +0 -1
  123. package/dist/chunk-MIWO425M.js.map +0 -1
  124. package/dist/chunk-SGU7SPLK.js +0 -924
  125. package/dist/chunk-SGU7SPLK.js.map +0 -1
  126. package/dist/index-CGMZNCx6.d.cts +0 -140
  127. package/dist/index-CGMZNCx6.d.ts +0 -140
  128. package/dist/index-CnZCo29u.d.cts +0 -108
  129. package/dist/index-JnF4v9bx.d.ts +0 -108
  130. package/dist/styled-DZ7GjShb.d.ts +0 -282
  131. package/dist/styled-ZTbdaVFM.d.cts +0 -282
  132. package/src/analyze/arrange/index.ts +0 -21
  133. package/src/analyze/arrange/internal.ts +0 -127
  134. package/src/analyze/arrange/tension.ts +0 -199
  135. package/src/analyze/arrange/tracks.ts +0 -312
  136. package/src/analyze/detect/index.ts +0 -272
  137. package/src/analyze/functional/borrowed.ts +0 -84
  138. package/src/analyze/functional/cadence.ts +0 -66
  139. package/src/analyze/functional/function.ts +0 -184
  140. package/src/analyze/functional/index.ts +0 -23
  141. package/src/analyze/functional/internal.ts +0 -29
  142. package/src/analyze/functional/roman.ts +0 -402
  143. package/src/analyze/index.ts +0 -49
  144. package/src/analyze/timeline/index.ts +0 -451
  145. package/src/analyze/voice/index.ts +0 -339
  146. package/src/core/event-index/index.ts +0 -104
  147. package/src/core/index.ts +0 -61
  148. package/src/core/interval/index.ts +0 -81
  149. package/src/core/meter/index.ts +0 -302
  150. package/src/core/pitch/index.ts +0 -311
  151. package/src/core/random/index.ts +0 -76
  152. package/src/core/tuning/index.ts +0 -186
  153. package/src/core/types.ts +0 -29
  154. package/src/core/validation/index.ts +0 -119
  155. package/src/generate/bass/index.ts +0 -373
  156. package/src/generate/countermelody/index.ts +0 -470
  157. package/src/generate/drums/beat.ts +0 -266
  158. package/src/generate/drums/euclid.ts +0 -65
  159. package/src/generate/drums/fills.ts +0 -356
  160. package/src/generate/drums/ghost.ts +0 -119
  161. package/src/generate/drums/hihat.ts +0 -285
  162. package/src/generate/drums/hit.ts +0 -31
  163. package/src/generate/drums/index.ts +0 -361
  164. package/src/generate/drums/internal.ts +0 -257
  165. package/src/generate/drums/kick.ts +0 -160
  166. package/src/generate/drums/percussion.ts +0 -121
  167. package/src/generate/drums/rng.ts +0 -7
  168. package/src/generate/drums/swing.ts +0 -71
  169. package/src/generate/groove/index.ts +0 -351
  170. package/src/generate/harmonize/index.ts +0 -451
  171. package/src/generate/index.ts +0 -58
  172. package/src/generate/motif/index.ts +0 -422
  173. package/src/generate/progression/index.ts +0 -362
  174. package/src/generate/reharmony/index.ts +0 -323
  175. package/src/generate/rhythm/index.ts +0 -175
  176. package/src/index.ts +0 -13
  177. package/src/model/chord.ts +0 -442
  178. package/src/model/index.ts +0 -15
  179. package/src/model/interval.ts +0 -82
  180. package/src/model/key.ts +0 -266
  181. package/src/model/note.ts +0 -176
  182. package/src/model/progression.ts +0 -161
  183. package/src/model/shared.ts +0 -12
  184. package/src/theory/chord/index.ts +0 -375
  185. package/src/theory/chordscale/index.ts +0 -374
  186. package/src/theory/counterpoint/index.ts +0 -230
  187. package/src/theory/harmony/index.ts +0 -77
  188. package/src/theory/index.ts +0 -112
  189. package/src/theory/safety/index.ts +0 -488
  190. package/src/theory/scale/degrees.ts +0 -123
  191. package/src/theory/scale/index.ts +0 -28
  192. package/src/theory/scale/key.ts +0 -56
  193. package/src/theory/scale/masks.ts +0 -153
  194. package/src/theory/spelling/index.ts +0 -249
  195. package/src/theory/symbol/index.ts +0 -268
  196. package/src/theory/voicing/index.ts +0 -5
  197. package/src/theory/voicing/internal.ts +0 -168
  198. package/src/theory/voicing/leading.ts +0 -112
  199. package/src/theory/voicing/satb.ts +0 -216
  200. package/src/theory/voicing/styled.ts +0 -199
  201. package/tsconfig.json +0 -23
package/README.md CHANGED
@@ -1,9 +1,16 @@
1
1
  # @libraz/libcantus
2
2
 
3
- Pure-TypeScript toolkit for working with music: name notes and chords, analyze
4
- harmony, recognize what you're hearing, voice and reharmonize progressions, and
5
- generate parts (bass, countermelody, drums, rhythm) — all with no runtime
6
- dependencies.
3
+ Pure-TypeScript music theory engine. It takes MIDI note numbers and chord
4
+ symbols and gives back harmonic meaning (Roman numerals, function, cadence,
5
+ key), then generates new parts against that meaning. Built for the tools that
6
+ sit next to a DAW: analyzers, generators, and composition assistants.
7
+
8
+ It is a theory layer, not an I/O or audio layer. There is no MIDI file reader or
9
+ writer, no audio analysis, and no playback — bring your own parser and hand it
10
+ note events. No runtime dependencies. If you need that other layer as well,
11
+ [libsonare](https://github.com/libraz/libsonare) covers audio analysis,
12
+ mastering, synthesis, and SMF I/O, and its npm binding sits alongside this one;
13
+ the two share no code and neither requires the other.
7
14
 
8
15
  [![CI](https://img.shields.io/github/actions/workflow/status/libraz/libcantus/ci.yml?branch=main&label=CI)](https://github.com/libraz/libcantus/actions)
9
16
  [![npm](https://img.shields.io/npm/v/@libraz/libcantus)](https://www.npmjs.com/package/@libraz/libcantus)
@@ -21,7 +28,7 @@ dependencies.
21
28
  (`V7/V`), or lead-sheet symbols (`Cmaj7`, `F#m7b5`, `C/G`).
22
29
  - **Analyze harmony** — Roman-numeral analysis, harmonic function, cadence
23
30
  detection, and borrowed / modal-interchange chords.
24
- - **Recognize what you hear** — notes in, chord or key out (the inverse of the
31
+ - **Recognize chords and keys** — notes in, chord or key out (the inverse of the
25
32
  builders).
26
33
  - **Reharmonize** — tritone subs, chromatic mediants, negative harmony, modal
27
34
  interchange palettes.
@@ -29,17 +36,18 @@ dependencies.
29
36
  rootless comping styles.
30
37
  - **Generate parts** — bass lines, countermelodies, drums, rhythms, motifs, and
31
38
  whole progressions; deterministic for a given seed.
32
- - **Arrange from a DAW / MIDI file** — feed raw multi-track notes, recover the
39
+ - **Analyze a whole arrangement** — feed raw multi-track note events, recover the
33
40
  harmony, get a whole-piece analysis (with per-note conflicts), and generate the
34
41
  missing parts against it.
35
42
  - **Work with pitch as sound** — frequencies, cents, EDO, and just intonation.
36
43
 
37
- The musical **values** — notes, chords, keys, progressions — come in two
38
- interchangeable styles: a **fluent, immutable class API** (`Note`, `Chord`,
39
- `Key`, `Progression`) that reads like music theory, and the underlying
40
- **tree-shakeable pure functions**. Operations over **collections and timelines**
41
- (part generation and whole-arrangement analysis) work on arrays of note events,
42
- so — like `rhythm`, `drums`, and `detectKey` — they are exposed as functions.
44
+ The theory itself lives in **tree-shakeable pure functions**. On top of them sits
45
+ a **fluent, immutable class API** (`Note`, `Interval`, `Chord`, `Key`,
46
+ `Progression`) that reads like music theory. Which one you get follows a single
47
+ rule: the musical **values** — notes, chords, keys, progressions — have both
48
+ forms and interoperate freely, while operations over **collections and
49
+ timelines** (part generation, whole-arrangement analysis, key ranking) take
50
+ arrays of note events and are exposed as functions only.
43
51
 
44
52
  ## Install
45
53
 
@@ -88,11 +96,11 @@ pure functions, so the two styles interoperate freely. The same task with the
88
96
  functional API:
89
97
 
90
98
  ```ts
91
- import { chordFromDegree, chordPitchClasses, classifyInterval, majorKey } from '@libraz/libcantus';
99
+ import { chordFromDegree, chordPitchClasses, classifyInterval, ConsonanceClass, majorKey } from '@libraz/libcantus';
92
100
 
93
101
  const cMajor = majorKey(0);
94
102
 
95
- classifyInterval(7); // IntervalQuality.PerfectConsonance
103
+ classifyInterval(7); // ConsonanceClass.PerfectConsonance
96
104
  chordPitchClasses(chordFromDegree(4, 'dom7', cMajor)); // [2, 5, 7, 11] (G7)
97
105
  ```
98
106
 
@@ -157,10 +165,10 @@ import { Chord, Key } from '@libraz/libcantus';
157
165
 
158
166
  // A minor iv in a major key reads as a borrowed subdominant:
159
167
  Chord.of('F', 'min').analyze(Key.major('C'));
160
- // { function: 'subdominant', borrowed: true, source: 'parallel-minor', roman: 'iv' }
168
+ // { function: 'subdominant', borrowed: true, source: 'parallelMinor', roman: 'iv' }
161
169
  ```
162
170
 
163
- ## Recognize what you hear
171
+ ## Recognize chords and keys
164
172
 
165
173
  Notes in, chord or key out — the inverse of the builders:
166
174
 
@@ -256,10 +264,11 @@ motif.notes.length > 0; // true — a deterministic short melodic cell
256
264
  `humanize`, `extractGrooveTemplate`, and `applyGrooveTemplate` add a meter-aware
257
265
  feel — or capture the feel of one performance and transplant it onto another.
258
266
 
259
- ## Arrange from a DAW or MIDI file
267
+ ## Analyze and extend an arrangement
260
268
 
261
- The arrangement layer takes raw multi-track `NoteEvent`s, recovers the harmony,
262
- then analyses and generates against it:
269
+ The arrangement layer takes raw multi-track `NoteEvent`s — whatever your DAW or
270
+ MIDI-file parser produced — recovers the harmony, then analyses and generates
271
+ against it:
263
272
 
264
273
  ```ts
265
274
  import {
@@ -279,7 +288,7 @@ report.conflicts; // [{ beat, trackName, pitch, safety, reasons, rationale }, ..
279
288
 
280
289
  // Generate the missing parts from the recovered timeline:
281
290
  generateBassLine({ segments: timeline.segments, key, style: 'walking', seed: 1 });
282
- generateCounterMelody({ melody: melodyNotes, chordAt: timeline.at, key, register: 'below' });
291
+ generateCounterMelody({ melody: melodyNotes, timeline, key, register: 'below' });
283
292
  ```
284
293
 
285
294
  `harmonizeMelody` goes the other way — given a bare melody, it searches for the
package/README_ja.md ADDED
@@ -0,0 +1,268 @@
1
+ # @libraz/libcantus
2
+
3
+ TypeScript だけで書かれた音楽理論エンジンです。MIDI ノート番号とコードネームを受け取り、和声的な意味(ローマ数字、和声機能、終止、キー)を返します。さらにその解析結果を土台にして新しいパートを生成します。DAW の隣に置くツール(解析器、ジェネレーター、作曲アシスタント)で使うことを想定しています。
4
+
5
+ 扱うのは理論の層だけで、I/O やオーディオには踏み込みません。MIDI ファイルの読み書き、オーディオ解析、再生のいずれも持たないので、パースは呼び出し側で行い、ノートイベントとして渡してください。実行時依存はありません。その層も必要であれば、[libsonare](https://github.com/libraz/libsonare) が音声解析・マスタリング・音源合成・SMF の読み書きを担当していて、npm パッケージなので本ライブラリと並べて置けます。両者はコードを共有しておらず、どちらも単独で使えます。
6
+
7
+ [![CI](https://img.shields.io/github/actions/workflow/status/libraz/libcantus/ci.yml?branch=main&label=CI)](https://github.com/libraz/libcantus/actions)
8
+ [![npm](https://img.shields.io/npm/v/@libraz/libcantus)](https://www.npmjs.com/package/@libraz/libcantus)
9
+ [![codecov](https://codecov.io/gh/libraz/libcantus/branch/main/graph/badge.svg)](https://codecov.io/gh/libraz/libcantus)
10
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/libraz/libcantus/blob/main/LICENSE)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
12
+ [![Node.js](https://img.shields.io/badge/Node.js-22.x-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
13
+ [![docs](https://img.shields.io/badge/docs-API%20reference-b5892e)](https://libraz.github.io/libcantus/)
14
+
15
+ ## 何ができるか
16
+
17
+ - **音名を扱う** — `C4` のパース、移調、MIDI との相互変換、異名同音を区別した音程の綴り(`F#` と `Gb`)。
18
+ - **コードと進行を組み立てる** — スケール度数、ローマ数字(`V7/V`)、リードシート記号(`Cmaj7`, `F#m7b5`, `C/G`)のいずれからでも。
19
+ - **和声を解析する** — ローマ数字解析、和声機能、終止形の検出、借用和音とモーダルインターチェンジ。
20
+ - **コードとキーを判定する** — ノートを入れるとコードやキーが返る、組み立ての逆方向の処理。
21
+ - **リハーモナイズする** — 裏コード、クロマチックメディアント、ネガティブハーモニー、モーダルインターチェンジのパレット。
22
+ - **コードをボイシングする** — 声部の動きを最小化した 4 声(SATB)に加えて、drop-2/3、シェル、ルートレスのコンピングスタイル。
23
+ - **パートを生成する** — ベースライン、対旋律、ドラム、リズム、モチーフ、進行全体。シードを与えれば結果は決定的になります。
24
+ - **アレンジ全体を解析する** — マルチトラックのノートイベントを渡すと和声を復元し、曲全体の解析(ノート単位の衝突検出つき)を返します。そこから足りないパートを生成できます。
25
+ - **音高を音響として扱う** — 周波数、セント、EDO、純正律。
26
+
27
+ 理論の本体は tree-shaking の効く純粋関数として実装してあります。その上に、音楽理論の記述に近い形で書ける不変(immutable)なクラス API(`Note`, `Interval`, `Chord`, `Key`, `Progression`)が乗ります。どちらの形で提供されるかは一つの規則で決まります。音符・コード・キー・進行といった値は両方の形を持ち、相互に行き来できます。一方でコレクションやタイムラインを対象とする操作(パート生成、アレンジ全体の解析、キーのランキング)はノートイベントの配列を受け取るため、関数としてのみ公開されます。
28
+
29
+ ## インストール
30
+
31
+ ```sh
32
+ yarn add @libraz/libcantus
33
+ ```
34
+
35
+ ## サブパスインポート
36
+
37
+ パッケージのルートからはすべてがエクスポートされます。特定の層だけを取り込みたい場合は、その層のサブパスからインポートしてください。
38
+
39
+ ```ts
40
+ import { Chord, Key, Note } from '@libraz/libcantus/model'; // クラス API
41
+ import { majorKey, makeChord } from '@libraz/libcantus/theory'; // スケール、コード
42
+ import { generateDrums, generateProgression } from '@libraz/libcantus/generate';
43
+ import { analyzeArrangement, detectKey } from '@libraz/libcantus/analyze';
44
+ import { parseNote, edo } from '@libraz/libcantus/core'; // 音高、拍子、音律
45
+ ```
46
+
47
+ 層は `core`, `theory`, `analyze`, `generate`, `model` の 5 つです。
48
+
49
+ ## クイックスタート
50
+
51
+ クラス API は不変のままメソッドチェーンでき、キーの文脈を持ち回るので、解析のたびにキーを渡し直す必要はありません。
52
+
53
+ ```ts
54
+ import { Chord, Key, Note } from '@libraz/libcantus';
55
+
56
+ const c = Key.major('C');
57
+
58
+ c.chord(4, 'dom7').pitchClasses(); // [2, 5, 7, 11] (G7)
59
+ c.roman('V7/V').voice(); // [ ...SATB の MIDI ノート ] (セカンダリードミナントをボイシング)
60
+ Note.of('C4').transpose(7).name; // 'G4'
61
+
62
+ // ii–V–I を組み立てて、そのまま 1 行で解析する:
63
+ c.chord(1, 'min').progressionTo(c.chord(4, 'dom7'), c.chord(0, 'maj')).analyze();
64
+ // { chords: [...機能和声の解析...], cadence: 'authentic' }
65
+
66
+ Chord.detect([60, 64, 67])[0].quality; // 'maj'
67
+ ```
68
+
69
+ どのクラスもプレーンなオブジェクト(`Chord.data`, `Note.data`)を包んでいるだけで、処理は純粋関数に委譲されます。そのため二つのスタイルは自由に混ぜられます。同じ処理を関数 API で書くとこうなります。
70
+
71
+ ```ts
72
+ import { chordFromDegree, chordPitchClasses, classifyInterval, ConsonanceClass, majorKey } from '@libraz/libcantus';
73
+
74
+ const cMajor = majorKey(0);
75
+
76
+ classifyInterval(7); // ConsonanceClass.PerfectConsonance
77
+ chordPitchClasses(chordFromDegree(4, 'dom7', cMajor)); // [2, 5, 7, 11] (G7)
78
+ ```
79
+
80
+ ## 音名を扱う
81
+
82
+ 音名のパースと整形、MIDI への変換、そしてピッチクラスの層では区別できない異名同音を正しく綴り分ける音程計算ができます。
83
+
84
+ ```ts
85
+ import { Interval, Note } from '@libraz/libcantus';
86
+
87
+ Note.of('C4').transpose(7).name; // 'G4'
88
+ Note.of('C4').midi; // 60
89
+ Interval.between(Note.of('C4'), Note.of('F#4')).name; // 'A4' (増四度)
90
+ Interval.between(Note.of('C4'), Note.of('Gb4')).name; // 'd5' (減五度)
91
+ ```
92
+
93
+ ## キーとスケール
94
+
95
+ `KeyScale` はルートのピッチクラスと 12 ビットの `modeMask12` の組です(ビット `n` が立っていれば、ピッチクラス `(rootPc + n) % 12` がスケールに含まれます)。`majorKey`, `minorKey`, `scaleByName` が教会旋法・ペンタトニック・ブルース・ホールトーン・オクタトニックをカバーし、`MAJOR_MASK` と `NATURAL_MINOR_MASK` を使えば独自のキーも定義できます。
96
+
97
+ `nearestScaleTone` は与えた音高を最も近いスケール構成音の MIDI ノートにスナップします(同距離なら低い方を優先)。生成した音をキーに収める場面で使えます。
98
+
99
+ ## コードと進行を組み立てる
100
+
101
+ スケール度数、ローマ数字、リードシート記号のいずれからでも組み立てられ、逆方向にも戻せます。
102
+
103
+ ```ts
104
+ import { Chord, Key } from '@libraz/libcantus';
105
+
106
+ const c = Key.major('C');
107
+
108
+ c.roman('V7/V').symbol(); // 'D7' (セカンダリードミナント)
109
+ c.chord(4, 'dom7').roman(); // 'V7'
110
+ Chord.parse('F#m7b5').pitchClasses(); // [0, 4, 6, 9]
111
+ ```
112
+
113
+ コードの語彙は三和音から 13th までを網羅していて、`dim7`, `m7b5`, `minMaj7`, `aug7`、6th 系、オルタードドミナントも含みます。
114
+
115
+ スタイルのプリセットから進行そのものを生成することもできます(コレクション単位の操作なので関数として提供されます)。
116
+
117
+ ```ts
118
+ import { generateProgression, majorKey } from '@libraz/libcantus';
119
+
120
+ // 1 小節につき 1 コード。収まる箇所にはセカンダリードミナントを挿入する:
121
+ generateProgression({ key: majorKey(0), style: 'idol', bars: 8, reharmonize: true, seed: 1 });
122
+ ```
123
+
124
+ ## 和声を解析する
125
+
126
+ コードを、和声機能・終止・モーダルインターチェンジつきのローマ数字解析に変換します。長調と短調の双方に対応し、転回形も反映されます。
127
+
128
+ ```ts
129
+ import { Chord, Key } from '@libraz/libcantus';
130
+
131
+ // 長調に現れる短調の iv は、借用されたサブドミナントとして解釈される:
132
+ Chord.of('F', 'min').analyze(Key.major('C'));
133
+ // { function: 'subdominant', borrowed: true, source: 'parallelMinor', roman: 'iv' }
134
+ ```
135
+
136
+ ## コードとキーを判定する
137
+
138
+ ノートを入れるとコードやキーが返ります。組み立てとちょうど逆方向の処理です。
139
+
140
+ ```ts
141
+ import { Chord, detectKey } from '@libraz/libcantus';
142
+
143
+ Chord.detect([60, 64, 67])[0].symbol(); // 'C'
144
+ Chord.detectBest([60, 63, 67, 70])?.symbol(); // 'Cm7'
145
+ detectKey([0, 0, 0, 4, 7])[0]; // 最も適合する C メジャー (キーのランキングは関数として提供)
146
+ ```
147
+
148
+ ## コードとスケールを綴る
149
+
150
+ `Key` は綴りを持ったトニックを保持しているので、ピッチクラスで動くコア部分からでも音名の文字列が得られます。長調でも短調でも綴りは正しくなります。
151
+
152
+ ```ts
153
+ import { Chord, Key } from '@libraz/libcantus';
154
+
155
+ Key.named('harmonicMinor', 'A').noteNames(); // ['A', 'B', 'C', 'D', 'E', 'F', 'G#']
156
+ Key.minor('E').noteNames(); // ['E', 'F#', 'G', 'A', 'B', 'C', 'D']
157
+
158
+ // コードもキーの文脈に沿って綴られる:
159
+ Key.major('C').chord(4, 'dom7').spell().map((n) => n.name); // ['G', 'B', 'D', 'F']
160
+ ```
161
+
162
+ ## リハーモナイズする
163
+
164
+ クラス側ではネガティブハーモニーによる反転を、関数側では代理コードの候補(裏コード、平行調、借用、クロマチックメディアント、およびモーダルインターチェンジのパレット)の列挙を扱います。
165
+
166
+ ```ts
167
+ import { Chord, Key, majorKey, parseChordSymbol, substituteChord } from '@libraz/libcantus';
168
+
169
+ Key.major('C').chord(4, 'dom7').negativeHarmony().symbol(); // 'Dm7b5'
170
+
171
+ // 代理コードやパレットの検索はリストを返すので関数として提供される:
172
+ substituteChord(parseChordSymbol('G7'), majorKey(0));
173
+ // [{ chord: Db7, type: 'tritone', ... }, ...]
174
+ ```
175
+
176
+ ## スケールとテンションを選ぶ
177
+
178
+ クラス側で、あるコードに使えるスケールと、そこで利用できるテンションおよびアボイドノートを求められます。`scalesForChanges` を使えば、コード進行全体を通して繋がりが最適になるスケール選択が得られます。
179
+
180
+ ```ts
181
+ import { Chord } from '@libraz/libcantus';
182
+
183
+ Chord.of('C', 'dom7').scales()[0]; // { name: 'mixolydian', rootPc: 0 }
184
+ Chord.of('C', 'maj7').tensions('ionian'); // [2, 9] (9th と 13th。11th はアボイドノート)
185
+ ```
186
+
187
+ ## コードをボイシングする
188
+
189
+ 進行全体を、声部の動きが最小になる 4 声(SATB)の MIDI ボイシングに展開できます。単体のコードをコンピングスタイル(drop-2/3、シェル、ルートレス)でボイシングすることもできます。
190
+
191
+ ```ts
192
+ import { Chord, Key } from '@libraz/libcantus';
193
+
194
+ const c = Key.major('C');
195
+
196
+ // 進行全体を、動きが最小になるようボイシングする:
197
+ c.chord(0, 'maj').progressionTo(c.chord(5, 'maj'), c.chord(4, 'dom7'), c.chord(0, 'maj')).voice();
198
+ // [[...], [...], [...], [...]] (各声部の音高が昇順に並ぶ)
199
+
200
+ // 単体のコードをシェルボイシングで:
201
+ Chord.of('C', 'maj7').styledVoicing({ style: 'shell' }); // ルート、3rd、7th
202
+ ```
203
+
204
+ 声部の進行を細かく制御したい場合は `voiceLeadingCost` と `nextVoicing` を、結果を検証したい場合は `counterpoint` の述語群(並行・隠伏の完全音程、音域の間隔、声部の交差、導音の解決など)を使います。
205
+
206
+ ## パートを生成する
207
+
208
+ 旋律と伴奏のジェネレーターは、いずれもシードを取り決定的に動きます。
209
+
210
+ ```ts
211
+ import {
212
+ generateBassLine, generateCounterMelody, generateDrums, generateRhythm,
213
+ generateMotif, majorKey, parseTimeSignature,
214
+ } from '@libraz/libcantus';
215
+
216
+ generateRhythm(parseTimeSignature('4/4'), { seed: 1, density: 0.5 }); // 強拍に重みを置いた発音位置
217
+ const motif = generateMotif({ key: majorKey(0), bars: 2, contour: 'arch', seed: 1 });
218
+ motif.notes.length > 0; // true — 決定的に生成された短い旋律の断片
219
+ ```
220
+
221
+ `humanize`, `extractGrooveTemplate`, `applyGrooveTemplate` は拍子を踏まえたノリを付け足します。ある演奏のノリを取り出して別の演奏に移植する使い方もできます。
222
+
223
+ ## アレンジを解析して足す
224
+
225
+ アレンジ層は、DAW や MIDI ファイルのパーサーが吐いたマルチトラックの `NoteEvent` をそのまま受け取り、和声を復元してから解析と生成を行います。
226
+
227
+ ```ts
228
+ import {
229
+ analyzeArrangement, chordTimelineFromNotes, generateBassLine, generateCounterMelody,
230
+ } from '@libraz/libcantus';
231
+
232
+ // 演奏されたノートからコード進行(とキー)を推定する:
233
+ const { timeline, key } = chordTimelineFromNotes(melodyAndChordNotes);
234
+
235
+ // 曲全体の解析。推定されたコード、終止、ノート単位の理論ラベル、そして
236
+ // 鳴っている和声とぶつかるノート(理由と修正候補つき)が得られる:
237
+ const report = analyzeArrangement([
238
+ { role: 'melody', notes: melodyNotes },
239
+ { role: 'harmony', notes: chordNotes },
240
+ ]);
241
+ report.conflicts; // [{ beat, trackName, pitch, safety, reasons, rationale }, ...]
242
+
243
+ // 復元されたタイムラインから、足りないパートを生成する:
244
+ generateBassLine({ segments: timeline.segments, key, style: 'walking', seed: 1 });
245
+ generateCounterMelody({ melody: melodyNotes, timeline, key, register: 'below' });
246
+ ```
247
+
248
+ `harmonizeMelody` は逆方向を担当します。旋律だけを渡すと、それを和声づけするのに最適なキー・移調量・コードの経路を(必要ならリハーモナイズも含めて)探索します。
249
+
250
+ アレンジ層と生成層のあいだでやり取りするノートイベントは `NoteEvent`(`{ pitch, startBeat, durationBeat, velocity? }`。音高は MIDI ノート番号、時間は 4 分音符を 1 とする拍数)の一種類に統一されています。シードを取るジェネレーター(`bass`, `groove`, `countermelody`, `rhythm`, `drums`, `motif`, `progression`)は、同じシードに対して常に同じ結果を返します。
251
+
252
+ ## 音高を音響として扱う
253
+
254
+ 周波数、セント、EDO、純正律を扱えます。音律の設計、微分音、そして解析に使えます。
255
+
256
+ ```ts
257
+ import { frequencyOf, edo, justDeviationCents } from '@libraz/libcantus';
258
+ ```
259
+
260
+ 拍子まわりのヘルパー(`TimeSignature`, `parseTimeSignature`, `beatsPerBar`, `metricWeight`, `isStrongBeat`, `tuplet`)は単純拍子と複合拍子の両方を扱い、アクセントを考慮するジェネレーター群の土台にもなっています。
261
+
262
+ ## ドキュメント
263
+
264
+ すべてのエクスポートをシグネチャつきで、領域ごとに分類し、動かせる例を添えた API リファレンスをソースから生成して **[libraz.github.io/libcantus](https://libraz.github.io/libcantus/)** に公開しています。
265
+
266
+ ## ライセンス
267
+
268
+ Apache-2.0