@mcptoolshop/ai_jam_session 0.1.0

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 (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.es.md +212 -0
  3. package/README.fr.md +212 -0
  4. package/README.hi.md +212 -0
  5. package/README.it.md +212 -0
  6. package/README.ja.md +214 -0
  7. package/README.md +214 -0
  8. package/README.pt-BR.md +212 -0
  9. package/dist/audio-engine.d.ts +9 -0
  10. package/dist/audio-engine.d.ts.map +1 -0
  11. package/dist/audio-engine.js +422 -0
  12. package/dist/audio-engine.js.map +1 -0
  13. package/dist/cli.d.ts +3 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +551 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/index.d.ts +32 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +41 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/mcp-server.d.ts +3 -0
  22. package/dist/mcp-server.d.ts.map +1 -0
  23. package/dist/mcp-server.js +903 -0
  24. package/dist/mcp-server.js.map +1 -0
  25. package/dist/midi/parser.d.ts +16 -0
  26. package/dist/midi/parser.d.ts.map +1 -0
  27. package/dist/midi/parser.js +192 -0
  28. package/dist/midi/parser.js.map +1 -0
  29. package/dist/midi/types.d.ts +44 -0
  30. package/dist/midi/types.d.ts.map +1 -0
  31. package/dist/midi/types.js +8 -0
  32. package/dist/midi/types.js.map +1 -0
  33. package/dist/note-parser.d.ts +105 -0
  34. package/dist/note-parser.d.ts.map +1 -0
  35. package/dist/note-parser.js +278 -0
  36. package/dist/note-parser.js.map +1 -0
  37. package/dist/playback/controls.d.ts +124 -0
  38. package/dist/playback/controls.d.ts.map +1 -0
  39. package/dist/playback/controls.js +252 -0
  40. package/dist/playback/controls.js.map +1 -0
  41. package/dist/playback/midi-engine.d.ts +68 -0
  42. package/dist/playback/midi-engine.d.ts.map +1 -0
  43. package/dist/playback/midi-engine.js +227 -0
  44. package/dist/playback/midi-engine.js.map +1 -0
  45. package/dist/playback/position.d.ts +95 -0
  46. package/dist/playback/position.d.ts.map +1 -0
  47. package/dist/playback/position.js +223 -0
  48. package/dist/playback/position.js.map +1 -0
  49. package/dist/playback/timing.d.ts +31 -0
  50. package/dist/playback/timing.d.ts.map +1 -0
  51. package/dist/playback/timing.js +57 -0
  52. package/dist/playback/timing.js.map +1 -0
  53. package/dist/sample-engine.d.ts +17 -0
  54. package/dist/sample-engine.d.ts.map +1 -0
  55. package/dist/sample-engine.js +428 -0
  56. package/dist/sample-engine.js.map +1 -0
  57. package/dist/schemas.d.ts +40 -0
  58. package/dist/schemas.d.ts.map +1 -0
  59. package/dist/schemas.js +42 -0
  60. package/dist/schemas.js.map +1 -0
  61. package/dist/session.d.ts +106 -0
  62. package/dist/session.d.ts.map +1 -0
  63. package/dist/session.js +361 -0
  64. package/dist/session.js.map +1 -0
  65. package/dist/sfz-parser.d.ts +36 -0
  66. package/dist/sfz-parser.d.ts.map +1 -0
  67. package/dist/sfz-parser.js +95 -0
  68. package/dist/sfz-parser.js.map +1 -0
  69. package/dist/smoke.d.ts +2 -0
  70. package/dist/smoke.d.ts.map +1 -0
  71. package/dist/smoke.js +512 -0
  72. package/dist/smoke.js.map +1 -0
  73. package/dist/songs/config/loader.d.ts +14 -0
  74. package/dist/songs/config/loader.d.ts.map +1 -0
  75. package/dist/songs/config/loader.js +53 -0
  76. package/dist/songs/config/loader.js.map +1 -0
  77. package/dist/songs/config/schema.d.ts +70 -0
  78. package/dist/songs/config/schema.d.ts.map +1 -0
  79. package/dist/songs/config/schema.js +53 -0
  80. package/dist/songs/config/schema.js.map +1 -0
  81. package/dist/songs/index.d.ts +16 -0
  82. package/dist/songs/index.d.ts.map +1 -0
  83. package/dist/songs/index.js +20 -0
  84. package/dist/songs/index.js.map +1 -0
  85. package/dist/songs/jam.d.ts +48 -0
  86. package/dist/songs/jam.d.ts.map +1 -0
  87. package/dist/songs/jam.js +324 -0
  88. package/dist/songs/jam.js.map +1 -0
  89. package/dist/songs/loader.d.ts +27 -0
  90. package/dist/songs/loader.d.ts.map +1 -0
  91. package/dist/songs/loader.js +90 -0
  92. package/dist/songs/loader.js.map +1 -0
  93. package/dist/songs/midi/hands.d.ts +46 -0
  94. package/dist/songs/midi/hands.d.ts.map +1 -0
  95. package/dist/songs/midi/hands.js +134 -0
  96. package/dist/songs/midi/hands.js.map +1 -0
  97. package/dist/songs/midi/ingest.d.ts +8 -0
  98. package/dist/songs/midi/ingest.d.ts.map +1 -0
  99. package/dist/songs/midi/ingest.js +191 -0
  100. package/dist/songs/midi/ingest.js.map +1 -0
  101. package/dist/songs/midi/measures.d.ts +41 -0
  102. package/dist/songs/midi/measures.d.ts.map +1 -0
  103. package/dist/songs/midi/measures.js +64 -0
  104. package/dist/songs/midi/measures.js.map +1 -0
  105. package/dist/songs/midi/types.d.ts +25 -0
  106. package/dist/songs/midi/types.d.ts.map +1 -0
  107. package/dist/songs/midi/types.js +8 -0
  108. package/dist/songs/midi/types.js.map +1 -0
  109. package/dist/songs/registry.d.ts +37 -0
  110. package/dist/songs/registry.d.ts.map +1 -0
  111. package/dist/songs/registry.js +197 -0
  112. package/dist/songs/registry.js.map +1 -0
  113. package/dist/songs/types.d.ts +99 -0
  114. package/dist/songs/types.d.ts.map +1 -0
  115. package/dist/songs/types.js +27 -0
  116. package/dist/songs/types.js.map +1 -0
  117. package/dist/teaching/live-midi-feedback.d.ts +36 -0
  118. package/dist/teaching/live-midi-feedback.d.ts.map +1 -0
  119. package/dist/teaching/live-midi-feedback.js +259 -0
  120. package/dist/teaching/live-midi-feedback.js.map +1 -0
  121. package/dist/teaching/midi-feedback.d.ts +33 -0
  122. package/dist/teaching/midi-feedback.d.ts.map +1 -0
  123. package/dist/teaching/midi-feedback.js +208 -0
  124. package/dist/teaching/midi-feedback.js.map +1 -0
  125. package/dist/teaching/sing-on-midi.d.ts +77 -0
  126. package/dist/teaching/sing-on-midi.d.ts.map +1 -0
  127. package/dist/teaching/sing-on-midi.js +186 -0
  128. package/dist/teaching/sing-on-midi.js.map +1 -0
  129. package/dist/teaching.d.ts +148 -0
  130. package/dist/teaching.d.ts.map +1 -0
  131. package/dist/teaching.js +453 -0
  132. package/dist/teaching.js.map +1 -0
  133. package/dist/test-sound.d.ts +3 -0
  134. package/dist/test-sound.d.ts.map +1 -0
  135. package/dist/test-sound.js +41 -0
  136. package/dist/test-sound.js.map +1 -0
  137. package/dist/types.d.ts +229 -0
  138. package/dist/types.d.ts.map +1 -0
  139. package/dist/types.js +22 -0
  140. package/dist/types.js.map +1 -0
  141. package/dist/vmpk.d.ts +23 -0
  142. package/dist/vmpk.d.ts.map +1 -0
  143. package/dist/vmpk.js +236 -0
  144. package/dist/vmpk.js.map +1 -0
  145. package/logo.png +0 -0
  146. package/package.json +70 -0
  147. package/songs/builtin/a-change-is-gonna-come.json +95 -0
  148. package/songs/builtin/a-thousand-years.json +93 -0
  149. package/songs/builtin/aint-no-sunshine.json +98 -0
  150. package/songs/builtin/all-blues.json +92 -0
  151. package/songs/builtin/autumn-leaves.json +100 -0
  152. package/songs/builtin/baba-oriley.json +91 -0
  153. package/songs/builtin/bach-invention-no1.json +70 -0
  154. package/songs/builtin/bach-prelude-c-major-bwv846.json +1282 -0
  155. package/songs/builtin/basic-12-bar-blues.json +119 -0
  156. package/songs/builtin/beethoven-waldstein-mvt1.json +7766 -0
  157. package/songs/builtin/bennie-and-the-jets.json +92 -0
  158. package/songs/builtin/besame-mucho.json +93 -0
  159. package/songs/builtin/black-orpheus.json +92 -0
  160. package/songs/builtin/blue-bossa.json +94 -0
  161. package/songs/builtin/blues-in-g.json +92 -0
  162. package/songs/builtin/bohemian-rhapsody-intro.json +94 -0
  163. package/songs/builtin/boogie-woogie-basics.json +93 -0
  164. package/songs/builtin/bossa-nova-basic.json +95 -0
  165. package/songs/builtin/chopin-nocturne-op9-no2.json +70 -0
  166. package/songs/builtin/cinema-paradiso.json +94 -0
  167. package/songs/builtin/clair-de-lune.json +11511 -0
  168. package/songs/builtin/clocks.json +91 -0
  169. package/songs/builtin/crystal-stream.json +70 -0
  170. package/songs/builtin/desafinado.json +93 -0
  171. package/songs/builtin/dont-stop-believin.json +91 -0
  172. package/songs/builtin/dream-on.json +100 -0
  173. package/songs/builtin/easy-winners.json +70 -0
  174. package/songs/builtin/el-condor-pasa.json +93 -0
  175. package/songs/builtin/elite-syncopations.json +70 -0
  176. package/songs/builtin/evening-calm.json +70 -0
  177. package/songs/builtin/everyday-blues.json +93 -0
  178. package/songs/builtin/fly-me-to-the-moon.json +91 -0
  179. package/songs/builtin/forrest-gump-suite.json +93 -0
  180. package/songs/builtin/fur-elise.json +20094 -0
  181. package/songs/builtin/georgia-on-my-mind.json +93 -0
  182. package/songs/builtin/girl-from-ipanema.json +92 -0
  183. package/songs/builtin/gladiolus-rag.json +70 -0
  184. package/songs/builtin/great-balls-of-fire.json +92 -0
  185. package/songs/builtin/guantanamera.json +92 -0
  186. package/songs/builtin/hallelujah.json +92 -0
  187. package/songs/builtin/hedwigs-theme.json +93 -0
  188. package/songs/builtin/hotel-california.json +92 -0
  189. package/songs/builtin/imagine.json +92 -0
  190. package/songs/builtin/just-the-two-of-us.json +92 -0
  191. package/songs/builtin/la-bamba.json +92 -0
  192. package/songs/builtin/layla-piano-coda.json +93 -0
  193. package/songs/builtin/lean-on-me.json +91 -0
  194. package/songs/builtin/let-it-be.json +101 -0
  195. package/songs/builtin/lets-stay-together.json +93 -0
  196. package/songs/builtin/magnetic-rag.json +70 -0
  197. package/songs/builtin/maple-leaf-rag.json +99 -0
  198. package/songs/builtin/mia-and-sebastians-theme.json +93 -0
  199. package/songs/builtin/minor-blues-in-a.json +94 -0
  200. package/songs/builtin/misty.json +94 -0
  201. package/songs/builtin/moon-river.json +93 -0
  202. package/songs/builtin/moonlight-sonata-mvt1.json +101 -0
  203. package/songs/builtin/morning-light.json +70 -0
  204. package/songs/builtin/mountain-dawn.json +70 -0
  205. package/songs/builtin/mozart-k545-mvt1.json +2956 -0
  206. package/songs/builtin/my-girl.json +92 -0
  207. package/songs/builtin/night-train.json +92 -0
  208. package/songs/builtin/november-rain.json +93 -0
  209. package/songs/builtin/ocean-waves.json +70 -0
  210. package/songs/builtin/over-the-rainbow.json +93 -0
  211. package/songs/builtin/oye-como-va.json +93 -0
  212. package/songs/builtin/peacherine-rag.json +70 -0
  213. package/songs/builtin/piano-man.json +92 -0
  214. package/songs/builtin/pineapple-rag.json +70 -0
  215. package/songs/builtin/pink-panther.json +94 -0
  216. package/songs/builtin/ragtime-dance.json +70 -0
  217. package/songs/builtin/river-flows-in-you.json +102 -0
  218. package/songs/builtin/rocket-man.json +92 -0
  219. package/songs/builtin/satie-gymnopedie-no1.json +70 -0
  220. package/songs/builtin/satin-doll.json +93 -0
  221. package/songs/builtin/schindlers-list.json +96 -0
  222. package/songs/builtin/schumann-traumerei.json +70 -0
  223. package/songs/builtin/sitting-on-the-dock.json +91 -0
  224. package/songs/builtin/slow-blues-in-bb.json +98 -0
  225. package/songs/builtin/snowfall.json +70 -0
  226. package/songs/builtin/so-what.json +92 -0
  227. package/songs/builtin/solace.json +70 -0
  228. package/songs/builtin/someone-like-you.json +92 -0
  229. package/songs/builtin/spirited-away.json +94 -0
  230. package/songs/builtin/st-louis-blues.json +93 -0
  231. package/songs/builtin/stairway-intro.json +93 -0
  232. package/songs/builtin/starlight-waltz.json +70 -0
  233. package/songs/builtin/stay-with-me.json +93 -0
  234. package/songs/builtin/stormy-monday.json +94 -0
  235. package/songs/builtin/superstition.json +93 -0
  236. package/songs/builtin/sweet-home-chicago.json +93 -0
  237. package/songs/builtin/take-five.json +92 -0
  238. package/songs/builtin/take-the-a-train.json +93 -0
  239. package/songs/builtin/the-entertainer.json +98 -0
  240. package/songs/builtin/the-godfather-waltz.json +93 -0
  241. package/songs/builtin/thrill-is-gone.json +94 -0
  242. package/songs/builtin/twilight-garden.json +70 -0
  243. package/songs/builtin/watermark.json +70 -0
  244. package/songs/builtin/wave.json +93 -0
  245. package/songs/builtin/whats-going-on.json +93 -0
  246. package/songs/builtin/yesterday.json +92 -0
@@ -0,0 +1,186 @@
1
+ // ─── Sing-Along for MIDI Files ──────────────────────────────────────────────
2
+ //
3
+ // Teaching hook that generates singable text from raw MIDI note events.
4
+ // Unlike the library sing-along hook (which reads measure data from SongEntry),
5
+ // this hook reads the actual MidiNoteEvent stream and converts MIDI note numbers
6
+ // to singable syllables (note-names, solfege, contour, or syllables).
7
+ //
8
+ // Supports voice filters:
9
+ // - "all" — sing every note/chord (default)
10
+ // - "melody-only" — pick the highest note per cluster (typical melody line)
11
+ // - "harmony" — pick the lowest note per cluster (bass/harmony line)
12
+ //
13
+ // Designed to work with PlaybackController — listens to note events in real time.
14
+ // ─────────────────────────────────────────────────────────────────────────────
15
+ import { midiToNoteName } from "../note-parser.js";
16
+ import { clusterEvents } from "../playback/timing.js";
17
+ // ─── MIDI Note → Singable Conversion ────────────────────────────────────────
18
+ /** Solfege mapping from pitch class (0–11) to syllable. */
19
+ const SOLFEGE_BY_PITCH_CLASS = [
20
+ "Do", "Di", "Re", "Me", "Mi", "Fa",
21
+ "Fi", "Sol", "Le", "La", "Te", "Ti",
22
+ ];
23
+ /**
24
+ * Convert a MIDI note number to a singable syllable.
25
+ *
26
+ * - "note-names": "C4", "F#5", "Bb3"
27
+ * - "solfege": "Do", "Re", "Mi" (based on pitch class, not key)
28
+ * - "syllables": always "da"
29
+ * - "contour": not applicable for individual notes (returns note name)
30
+ */
31
+ export function midiNoteToSingable(note, mode) {
32
+ switch (mode) {
33
+ case "note-names":
34
+ return midiToNoteName(note);
35
+ case "solfege":
36
+ return SOLFEGE_BY_PITCH_CLASS[note % 12];
37
+ case "syllables":
38
+ return "da";
39
+ case "contour":
40
+ return midiToNoteName(note);
41
+ }
42
+ }
43
+ /**
44
+ * Convert a cluster (chord or single note) to singable text.
45
+ *
46
+ * - Single note: just the syllable
47
+ * - Chord: syllables joined with " and " (e.g. "Do and Mi and Sol")
48
+ * - Contour mode between clusters: "up", "down", "same"
49
+ */
50
+ export function clusterToSingable(events, mode) {
51
+ if (events.length === 0)
52
+ return "";
53
+ if (mode === "syllables") {
54
+ return events.length === 1 ? "da" : "da".repeat(1); // single syllable per cluster
55
+ }
56
+ const syllables = events.map((e) => midiNoteToSingable(e.note, mode));
57
+ // Remove duplicates (same note in a chord)
58
+ const unique = [...new Set(syllables)];
59
+ return unique.join(" and ");
60
+ }
61
+ /**
62
+ * Generate contour direction between two clusters.
63
+ * Compares the highest note of each cluster.
64
+ */
65
+ export function contourDirection(prev, curr) {
66
+ if (prev.length === 0 || curr.length === 0)
67
+ return "";
68
+ const prevMax = Math.max(...prev.map((e) => e.note));
69
+ const currMax = Math.max(...curr.map((e) => e.note));
70
+ if (currMax > prevMax)
71
+ return "up";
72
+ if (currMax < prevMax)
73
+ return "down";
74
+ return "same";
75
+ }
76
+ /**
77
+ * Filter a cluster of events based on the voice filter.
78
+ * Returns a subset of events to sing.
79
+ */
80
+ export function filterClusterForVoice(events, filter) {
81
+ if (events.length === 0)
82
+ return [];
83
+ if (filter === "all")
84
+ return [...events];
85
+ if (filter === "melody-only") {
86
+ // Pick the highest note (typical melody line is on top)
87
+ let highest = events[0];
88
+ for (const e of events) {
89
+ if (e.note > highest.note)
90
+ highest = e;
91
+ }
92
+ return [highest];
93
+ }
94
+ // "harmony" — pick the lowest note
95
+ let lowest = events[0];
96
+ for (const e of events) {
97
+ if (e.note < lowest.note)
98
+ lowest = e;
99
+ }
100
+ return [lowest];
101
+ }
102
+ // ─── Sing-On-MIDI Hook ──────────────────────────────────────────────────────
103
+ /**
104
+ * Create a teaching hook that sings along with MIDI file playback.
105
+ *
106
+ * Pre-processes the ParsedMidi events into time clusters (chords/single notes),
107
+ * then emits singable text as each cluster is reached during playback.
108
+ *
109
+ * The hook implements TeachingHook so it can be composed with other hooks
110
+ * via composeTeachingHooks.
111
+ *
112
+ * @param sink - Voice sink to emit speech directives.
113
+ * @param midi - Parsed MIDI file data.
114
+ * @param options - Configuration.
115
+ * @returns Teaching hook with `.directives` for inspection.
116
+ */
117
+ export function createSingOnMidiHook(sink, midi, options = {}) {
118
+ const { mode = "note-names", voiceFilter = "all", voice, speechSpeed = 1.0, announcePosition = false, batchSize = 8, chordThresholdMs = 10, blocking = true, } = options;
119
+ const directives = [];
120
+ // Pre-cluster all events by time proximity
121
+ const clusters = clusterEvents(midi.events, chordThresholdMs);
122
+ // Track which cluster we've announced (for onMeasureStart which fires per-note)
123
+ let lastAnnouncedCluster = -1;
124
+ let prevCluster = [];
125
+ // Build a lookup: event index → cluster index
126
+ const eventToCluster = new Map();
127
+ let eventOffset = 0;
128
+ for (let ci = 0; ci < clusters.length; ci++) {
129
+ for (let ei = 0; ei < clusters[ci].length; ei++) {
130
+ eventToCluster.set(eventOffset + ei, ci);
131
+ }
132
+ eventOffset += clusters[ci].length;
133
+ }
134
+ async function emit(directive) {
135
+ directives.push(directive);
136
+ await sink(directive);
137
+ }
138
+ return {
139
+ directives,
140
+ clusters,
141
+ async onMeasureStart(eventIndex, _teachingNote, _dynamics) {
142
+ // eventIndex from PlaybackController is the note index (1-based)
143
+ const clusterIndex = eventToCluster.get(eventIndex - 1);
144
+ if (clusterIndex === undefined || clusterIndex === lastAnnouncedCluster)
145
+ return;
146
+ lastAnnouncedCluster = clusterIndex;
147
+ const rawCluster = clusters[clusterIndex];
148
+ // Apply voice filter (melody-only picks highest, harmony picks lowest)
149
+ const cluster = filterClusterForVoice(rawCluster, voiceFilter);
150
+ // Build singable text
151
+ let text;
152
+ if (mode === "contour" && prevCluster.length > 0) {
153
+ text = contourDirection(prevCluster, cluster);
154
+ }
155
+ else {
156
+ text = clusterToSingable(cluster, mode);
157
+ }
158
+ if (!text)
159
+ return;
160
+ // Optional position prefix
161
+ const prefix = announcePosition ? `${clusterIndex + 1}: ` : "";
162
+ await emit({
163
+ text: `${prefix}${text}`,
164
+ voice,
165
+ speed: speechSpeed,
166
+ blocking,
167
+ });
168
+ prevCluster = cluster;
169
+ },
170
+ async onKeyMoment(_moment) {
171
+ // Sing-on-MIDI does not speak key moments (MIDI files don't have them)
172
+ },
173
+ async onSongComplete(eventsPlayed, songTitle) {
174
+ await emit({
175
+ text: `Finished: ${songTitle}. ${eventsPlayed} notes.`,
176
+ voice,
177
+ speed: speechSpeed,
178
+ blocking: false,
179
+ });
180
+ },
181
+ async push(_interjection) {
182
+ // Sing-on-MIDI does not handle push interjections
183
+ },
184
+ };
185
+ }
186
+ //# sourceMappingURL=sing-on-midi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sing-on-midi.js","sourceRoot":"","sources":["../../src/teaching/sing-on-midi.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,148 @@
1
+ import type { TeachingHook, TeachingInterjection, VoiceDirective, VoiceSink, AsideDirective, AsideSink, LiveFeedbackHookOptions } from "./types.js";
2
+ import type { SongEntry } from "./songs/types.js";
3
+ /**
4
+ * Logs teaching interjections to the console.
5
+ * Good for CLI mode and development.
6
+ */
7
+ export declare function createConsoleTeachingHook(): TeachingHook;
8
+ /**
9
+ * No-op hook — swallows all interjections.
10
+ * Use in tests where you don't want console noise.
11
+ */
12
+ export declare function createSilentTeachingHook(): TeachingHook;
13
+ /** A recorded teaching event for assertions. */
14
+ export interface TeachingEvent {
15
+ type: "measure-start" | "key-moment" | "song-complete" | "push";
16
+ measureNumber?: number;
17
+ teachingNote?: string;
18
+ dynamics?: string;
19
+ moment?: string;
20
+ measuresPlayed?: number;
21
+ songTitle?: string;
22
+ interjection?: TeachingInterjection;
23
+ }
24
+ /**
25
+ * Records all teaching events for test assertions.
26
+ * Use: `const hook = createRecordingTeachingHook(); ... hook.events`
27
+ */
28
+ export declare function createRecordingTeachingHook(): TeachingHook & {
29
+ events: TeachingEvent[];
30
+ };
31
+ /** Callbacks for a custom teaching hook. All optional — unset = no-op. */
32
+ export interface TeachingCallbacks {
33
+ onMeasureStart?: (measureNumber: number, teachingNote?: string, dynamics?: string) => Promise<void>;
34
+ onKeyMoment?: (moment: string) => Promise<void>;
35
+ onSongComplete?: (measuresPlayed: number, songTitle: string) => Promise<void>;
36
+ onPush?: (interjection: TeachingInterjection) => Promise<void>;
37
+ }
38
+ /**
39
+ * Routes teaching events to custom callbacks.
40
+ * Use this to wire to mcp-voice-soundboard, mcp-aside, or any other sink.
41
+ */
42
+ export declare function createCallbackTeachingHook(callbacks: TeachingCallbacks): TeachingHook;
43
+ /** Options for the voice teaching hook. */
44
+ export interface VoiceHookOptions {
45
+ /** Voice preset name for teaching (default: undefined = server default). */
46
+ voice?: string;
47
+ /** Speech speed (default: 1.0). */
48
+ speechSpeed?: number;
49
+ /** Speak teaching notes at measure start (default: true). */
50
+ speakTeachingNotes?: boolean;
51
+ /** Speak key moments (default: true). */
52
+ speakKeyMoments?: boolean;
53
+ /** Speak completion message (default: true). */
54
+ speakCompletion?: boolean;
55
+ /** Block playback while speaking (default: false for notes, true for key moments). */
56
+ blockOnKeyMoments?: boolean;
57
+ }
58
+ /**
59
+ * Voice teaching hook — produces VoiceDirective objects routed to a VoiceSink.
60
+ *
61
+ * The sink can be:
62
+ * - A real mcp-voice-soundboard call (via LLM tool routing)
63
+ * - A console.log wrapper (for CLI testing)
64
+ * - A recording array (for unit tests)
65
+ *
66
+ * Also records all directives for inspection.
67
+ */
68
+ export declare function createVoiceTeachingHook(sink: VoiceSink, options?: VoiceHookOptions): TeachingHook & {
69
+ directives: VoiceDirective[];
70
+ };
71
+ /** Options for the aside teaching hook. */
72
+ export interface AsideHookOptions {
73
+ /** Push teaching notes to aside (default: true). */
74
+ pushTeachingNotes?: boolean;
75
+ /** Push key moments to aside (default: true). */
76
+ pushKeyMoments?: boolean;
77
+ /** Push completion to aside (default: true). */
78
+ pushCompletion?: boolean;
79
+ /** Base tags added to all directives. */
80
+ baseTags?: string[];
81
+ }
82
+ /**
83
+ * Aside teaching hook — produces AsideDirective objects routed to an AsideSink.
84
+ *
85
+ * The sink can be:
86
+ * - A real mcp-aside push (via aside_push tool)
87
+ * - A recording array (for tests)
88
+ *
89
+ * Records all directives for inspection.
90
+ */
91
+ export declare function createAsideTeachingHook(sink: AsideSink, options?: AsideHookOptions): TeachingHook & {
92
+ directives: AsideDirective[];
93
+ };
94
+ import { type SingAlongMode } from "./note-parser.js";
95
+ /** Options for the sing-along teaching hook. */
96
+ export interface SingAlongHookOptions {
97
+ /** Sing-along mode (default: "note-names"). */
98
+ mode?: SingAlongMode;
99
+ /** Which hand(s) to narrate (default: "right"). */
100
+ hand?: "right" | "left" | "both";
101
+ /** Voice preset (default: undefined = server default). */
102
+ voice?: string;
103
+ /** Speech speed (default: 1.0). */
104
+ speechSpeed?: number;
105
+ /** Announce the measure number before notes (default: true). */
106
+ announceMeasureNumber?: boolean;
107
+ /** Speak completion message (default: true). */
108
+ speakCompletion?: boolean;
109
+ }
110
+ /**
111
+ * Sing-along teaching hook — narrates note names/solfege/contour/syllables
112
+ * from the actual measure data, synchronized with playback.
113
+ *
114
+ * Requires the full SongEntry to look up measure note data by measure number.
115
+ * Emits blocking VoiceDirectives on onMeasureStart so the voice speaks BEFORE
116
+ * the piano plays each measure.
117
+ *
118
+ * Composable with other hooks via composeTeachingHooks.
119
+ */
120
+ export declare function createSingAlongHook(sink: VoiceSink, song: SongEntry, options?: SingAlongHookOptions): TeachingHook & {
121
+ directives: VoiceDirective[];
122
+ };
123
+ /**
124
+ * Live feedback teaching hook — pushes context-aware encouragement and tips
125
+ * during singing/playback sessions via both voice and aside sinks.
126
+ *
127
+ * - Voice: periodic encouragement every N measures + key moment reactions
128
+ * - Aside: dynamics tips, fingering warnings, deeper contextual notes
129
+ *
130
+ * Composable with other hooks via composeTeachingHooks.
131
+ */
132
+ export declare function createLiveFeedbackHook(voiceSink: VoiceSink, asideSink: AsideSink, song: SongEntry, options?: LiveFeedbackHookOptions): TeachingHook & {
133
+ voiceDirectives: VoiceDirective[];
134
+ asideDirectives: AsideDirective[];
135
+ };
136
+ /**
137
+ * Compose multiple teaching hooks into one.
138
+ * Events are dispatched to all hooks in order (serially, not parallel).
139
+ *
140
+ * Example: combine a voice hook + aside hook + recording hook for full coverage.
141
+ */
142
+ export declare function composeTeachingHooks(...hooks: TeachingHook[]): TeachingHook;
143
+ /**
144
+ * Check if the current measure matches any key moments in the song.
145
+ * Key moments in ai-music-sheets reference bars like "Bar 1:", "Bars 3-4:", etc.
146
+ */
147
+ export declare function detectKeyMoments(song: SongEntry, measureNumber: number): string[];
148
+ //# sourceMappingURL=teaching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teaching.d.ts","sourceRoot":"","sources":["../src/teaching.ts"],"names":[],"mappings":"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"}