@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.
- package/LICENSE +21 -0
- package/README.es.md +212 -0
- package/README.fr.md +212 -0
- package/README.hi.md +212 -0
- package/README.it.md +212 -0
- package/README.ja.md +214 -0
- package/README.md +214 -0
- package/README.pt-BR.md +212 -0
- package/dist/audio-engine.d.ts +9 -0
- package/dist/audio-engine.d.ts.map +1 -0
- package/dist/audio-engine.js +422 -0
- package/dist/audio-engine.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +551 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +903 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/midi/parser.d.ts +16 -0
- package/dist/midi/parser.d.ts.map +1 -0
- package/dist/midi/parser.js +192 -0
- package/dist/midi/parser.js.map +1 -0
- package/dist/midi/types.d.ts +44 -0
- package/dist/midi/types.d.ts.map +1 -0
- package/dist/midi/types.js +8 -0
- package/dist/midi/types.js.map +1 -0
- package/dist/note-parser.d.ts +105 -0
- package/dist/note-parser.d.ts.map +1 -0
- package/dist/note-parser.js +278 -0
- package/dist/note-parser.js.map +1 -0
- package/dist/playback/controls.d.ts +124 -0
- package/dist/playback/controls.d.ts.map +1 -0
- package/dist/playback/controls.js +252 -0
- package/dist/playback/controls.js.map +1 -0
- package/dist/playback/midi-engine.d.ts +68 -0
- package/dist/playback/midi-engine.d.ts.map +1 -0
- package/dist/playback/midi-engine.js +227 -0
- package/dist/playback/midi-engine.js.map +1 -0
- package/dist/playback/position.d.ts +95 -0
- package/dist/playback/position.d.ts.map +1 -0
- package/dist/playback/position.js +223 -0
- package/dist/playback/position.js.map +1 -0
- package/dist/playback/timing.d.ts +31 -0
- package/dist/playback/timing.d.ts.map +1 -0
- package/dist/playback/timing.js +57 -0
- package/dist/playback/timing.js.map +1 -0
- package/dist/sample-engine.d.ts +17 -0
- package/dist/sample-engine.d.ts.map +1 -0
- package/dist/sample-engine.js +428 -0
- package/dist/sample-engine.js.map +1 -0
- package/dist/schemas.d.ts +40 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +42 -0
- package/dist/schemas.js.map +1 -0
- package/dist/session.d.ts +106 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +361 -0
- package/dist/session.js.map +1 -0
- package/dist/sfz-parser.d.ts +36 -0
- package/dist/sfz-parser.d.ts.map +1 -0
- package/dist/sfz-parser.js +95 -0
- package/dist/sfz-parser.js.map +1 -0
- package/dist/smoke.d.ts +2 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +512 -0
- package/dist/smoke.js.map +1 -0
- package/dist/songs/config/loader.d.ts +14 -0
- package/dist/songs/config/loader.d.ts.map +1 -0
- package/dist/songs/config/loader.js +53 -0
- package/dist/songs/config/loader.js.map +1 -0
- package/dist/songs/config/schema.d.ts +70 -0
- package/dist/songs/config/schema.d.ts.map +1 -0
- package/dist/songs/config/schema.js +53 -0
- package/dist/songs/config/schema.js.map +1 -0
- package/dist/songs/index.d.ts +16 -0
- package/dist/songs/index.d.ts.map +1 -0
- package/dist/songs/index.js +20 -0
- package/dist/songs/index.js.map +1 -0
- package/dist/songs/jam.d.ts +48 -0
- package/dist/songs/jam.d.ts.map +1 -0
- package/dist/songs/jam.js +324 -0
- package/dist/songs/jam.js.map +1 -0
- package/dist/songs/loader.d.ts +27 -0
- package/dist/songs/loader.d.ts.map +1 -0
- package/dist/songs/loader.js +90 -0
- package/dist/songs/loader.js.map +1 -0
- package/dist/songs/midi/hands.d.ts +46 -0
- package/dist/songs/midi/hands.d.ts.map +1 -0
- package/dist/songs/midi/hands.js +134 -0
- package/dist/songs/midi/hands.js.map +1 -0
- package/dist/songs/midi/ingest.d.ts +8 -0
- package/dist/songs/midi/ingest.d.ts.map +1 -0
- package/dist/songs/midi/ingest.js +191 -0
- package/dist/songs/midi/ingest.js.map +1 -0
- package/dist/songs/midi/measures.d.ts +41 -0
- package/dist/songs/midi/measures.d.ts.map +1 -0
- package/dist/songs/midi/measures.js +64 -0
- package/dist/songs/midi/measures.js.map +1 -0
- package/dist/songs/midi/types.d.ts +25 -0
- package/dist/songs/midi/types.d.ts.map +1 -0
- package/dist/songs/midi/types.js +8 -0
- package/dist/songs/midi/types.js.map +1 -0
- package/dist/songs/registry.d.ts +37 -0
- package/dist/songs/registry.d.ts.map +1 -0
- package/dist/songs/registry.js +197 -0
- package/dist/songs/registry.js.map +1 -0
- package/dist/songs/types.d.ts +99 -0
- package/dist/songs/types.d.ts.map +1 -0
- package/dist/songs/types.js +27 -0
- package/dist/songs/types.js.map +1 -0
- package/dist/teaching/live-midi-feedback.d.ts +36 -0
- package/dist/teaching/live-midi-feedback.d.ts.map +1 -0
- package/dist/teaching/live-midi-feedback.js +259 -0
- package/dist/teaching/live-midi-feedback.js.map +1 -0
- package/dist/teaching/midi-feedback.d.ts +33 -0
- package/dist/teaching/midi-feedback.d.ts.map +1 -0
- package/dist/teaching/midi-feedback.js +208 -0
- package/dist/teaching/midi-feedback.js.map +1 -0
- package/dist/teaching/sing-on-midi.d.ts +77 -0
- package/dist/teaching/sing-on-midi.d.ts.map +1 -0
- package/dist/teaching/sing-on-midi.js +186 -0
- package/dist/teaching/sing-on-midi.js.map +1 -0
- package/dist/teaching.d.ts +148 -0
- package/dist/teaching.d.ts.map +1 -0
- package/dist/teaching.js +453 -0
- package/dist/teaching.js.map +1 -0
- package/dist/test-sound.d.ts +3 -0
- package/dist/test-sound.d.ts.map +1 -0
- package/dist/test-sound.js +41 -0
- package/dist/test-sound.js.map +1 -0
- package/dist/types.d.ts +229 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +22 -0
- package/dist/types.js.map +1 -0
- package/dist/vmpk.d.ts +23 -0
- package/dist/vmpk.d.ts.map +1 -0
- package/dist/vmpk.js +236 -0
- package/dist/vmpk.js.map +1 -0
- package/logo.png +0 -0
- package/package.json +70 -0
- package/songs/builtin/a-change-is-gonna-come.json +95 -0
- package/songs/builtin/a-thousand-years.json +93 -0
- package/songs/builtin/aint-no-sunshine.json +98 -0
- package/songs/builtin/all-blues.json +92 -0
- package/songs/builtin/autumn-leaves.json +100 -0
- package/songs/builtin/baba-oriley.json +91 -0
- package/songs/builtin/bach-invention-no1.json +70 -0
- package/songs/builtin/bach-prelude-c-major-bwv846.json +1282 -0
- package/songs/builtin/basic-12-bar-blues.json +119 -0
- package/songs/builtin/beethoven-waldstein-mvt1.json +7766 -0
- package/songs/builtin/bennie-and-the-jets.json +92 -0
- package/songs/builtin/besame-mucho.json +93 -0
- package/songs/builtin/black-orpheus.json +92 -0
- package/songs/builtin/blue-bossa.json +94 -0
- package/songs/builtin/blues-in-g.json +92 -0
- package/songs/builtin/bohemian-rhapsody-intro.json +94 -0
- package/songs/builtin/boogie-woogie-basics.json +93 -0
- package/songs/builtin/bossa-nova-basic.json +95 -0
- package/songs/builtin/chopin-nocturne-op9-no2.json +70 -0
- package/songs/builtin/cinema-paradiso.json +94 -0
- package/songs/builtin/clair-de-lune.json +11511 -0
- package/songs/builtin/clocks.json +91 -0
- package/songs/builtin/crystal-stream.json +70 -0
- package/songs/builtin/desafinado.json +93 -0
- package/songs/builtin/dont-stop-believin.json +91 -0
- package/songs/builtin/dream-on.json +100 -0
- package/songs/builtin/easy-winners.json +70 -0
- package/songs/builtin/el-condor-pasa.json +93 -0
- package/songs/builtin/elite-syncopations.json +70 -0
- package/songs/builtin/evening-calm.json +70 -0
- package/songs/builtin/everyday-blues.json +93 -0
- package/songs/builtin/fly-me-to-the-moon.json +91 -0
- package/songs/builtin/forrest-gump-suite.json +93 -0
- package/songs/builtin/fur-elise.json +20094 -0
- package/songs/builtin/georgia-on-my-mind.json +93 -0
- package/songs/builtin/girl-from-ipanema.json +92 -0
- package/songs/builtin/gladiolus-rag.json +70 -0
- package/songs/builtin/great-balls-of-fire.json +92 -0
- package/songs/builtin/guantanamera.json +92 -0
- package/songs/builtin/hallelujah.json +92 -0
- package/songs/builtin/hedwigs-theme.json +93 -0
- package/songs/builtin/hotel-california.json +92 -0
- package/songs/builtin/imagine.json +92 -0
- package/songs/builtin/just-the-two-of-us.json +92 -0
- package/songs/builtin/la-bamba.json +92 -0
- package/songs/builtin/layla-piano-coda.json +93 -0
- package/songs/builtin/lean-on-me.json +91 -0
- package/songs/builtin/let-it-be.json +101 -0
- package/songs/builtin/lets-stay-together.json +93 -0
- package/songs/builtin/magnetic-rag.json +70 -0
- package/songs/builtin/maple-leaf-rag.json +99 -0
- package/songs/builtin/mia-and-sebastians-theme.json +93 -0
- package/songs/builtin/minor-blues-in-a.json +94 -0
- package/songs/builtin/misty.json +94 -0
- package/songs/builtin/moon-river.json +93 -0
- package/songs/builtin/moonlight-sonata-mvt1.json +101 -0
- package/songs/builtin/morning-light.json +70 -0
- package/songs/builtin/mountain-dawn.json +70 -0
- package/songs/builtin/mozart-k545-mvt1.json +2956 -0
- package/songs/builtin/my-girl.json +92 -0
- package/songs/builtin/night-train.json +92 -0
- package/songs/builtin/november-rain.json +93 -0
- package/songs/builtin/ocean-waves.json +70 -0
- package/songs/builtin/over-the-rainbow.json +93 -0
- package/songs/builtin/oye-como-va.json +93 -0
- package/songs/builtin/peacherine-rag.json +70 -0
- package/songs/builtin/piano-man.json +92 -0
- package/songs/builtin/pineapple-rag.json +70 -0
- package/songs/builtin/pink-panther.json +94 -0
- package/songs/builtin/ragtime-dance.json +70 -0
- package/songs/builtin/river-flows-in-you.json +102 -0
- package/songs/builtin/rocket-man.json +92 -0
- package/songs/builtin/satie-gymnopedie-no1.json +70 -0
- package/songs/builtin/satin-doll.json +93 -0
- package/songs/builtin/schindlers-list.json +96 -0
- package/songs/builtin/schumann-traumerei.json +70 -0
- package/songs/builtin/sitting-on-the-dock.json +91 -0
- package/songs/builtin/slow-blues-in-bb.json +98 -0
- package/songs/builtin/snowfall.json +70 -0
- package/songs/builtin/so-what.json +92 -0
- package/songs/builtin/solace.json +70 -0
- package/songs/builtin/someone-like-you.json +92 -0
- package/songs/builtin/spirited-away.json +94 -0
- package/songs/builtin/st-louis-blues.json +93 -0
- package/songs/builtin/stairway-intro.json +93 -0
- package/songs/builtin/starlight-waltz.json +70 -0
- package/songs/builtin/stay-with-me.json +93 -0
- package/songs/builtin/stormy-monday.json +94 -0
- package/songs/builtin/superstition.json +93 -0
- package/songs/builtin/sweet-home-chicago.json +93 -0
- package/songs/builtin/take-five.json +92 -0
- package/songs/builtin/take-the-a-train.json +93 -0
- package/songs/builtin/the-entertainer.json +98 -0
- package/songs/builtin/the-godfather-waltz.json +93 -0
- package/songs/builtin/thrill-is-gone.json +94 -0
- package/songs/builtin/twilight-garden.json +70 -0
- package/songs/builtin/watermark.json +70 -0
- package/songs/builtin/wave.json +93 -0
- package/songs/builtin/whats-going-on.json +93 -0
- package/songs/builtin/yesterday.json +92 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const MeasureOverrideSchema: z.ZodObject<{
|
|
3
|
+
measure: z.ZodNumber;
|
|
4
|
+
fingering: z.ZodOptional<z.ZodString>;
|
|
5
|
+
teachingNote: z.ZodOptional<z.ZodString>;
|
|
6
|
+
dynamics: z.ZodOptional<z.ZodString>;
|
|
7
|
+
tempoOverride: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export declare const MusicalLanguageSchema: z.ZodObject<{
|
|
10
|
+
description: z.ZodString;
|
|
11
|
+
structure: z.ZodString;
|
|
12
|
+
keyMoments: z.ZodArray<z.ZodString>;
|
|
13
|
+
teachingGoals: z.ZodArray<z.ZodString>;
|
|
14
|
+
styleTips: z.ZodArray<z.ZodString>;
|
|
15
|
+
}, z.core.$strip>;
|
|
16
|
+
export declare const SongConfigSchema: z.ZodObject<{
|
|
17
|
+
id: z.ZodString;
|
|
18
|
+
title: z.ZodString;
|
|
19
|
+
genre: z.ZodEnum<{
|
|
20
|
+
classical: "classical";
|
|
21
|
+
jazz: "jazz";
|
|
22
|
+
pop: "pop";
|
|
23
|
+
blues: "blues";
|
|
24
|
+
rock: "rock";
|
|
25
|
+
rnb: "rnb";
|
|
26
|
+
latin: "latin";
|
|
27
|
+
film: "film";
|
|
28
|
+
ragtime: "ragtime";
|
|
29
|
+
"new-age": "new-age";
|
|
30
|
+
}>;
|
|
31
|
+
composer: z.ZodOptional<z.ZodString>;
|
|
32
|
+
arranger: z.ZodOptional<z.ZodString>;
|
|
33
|
+
difficulty: z.ZodEnum<{
|
|
34
|
+
beginner: "beginner";
|
|
35
|
+
intermediate: "intermediate";
|
|
36
|
+
advanced: "advanced";
|
|
37
|
+
}>;
|
|
38
|
+
key: z.ZodString;
|
|
39
|
+
tempo: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
timeSignature: z.ZodOptional<z.ZodString>;
|
|
41
|
+
tags: z.ZodArray<z.ZodString>;
|
|
42
|
+
source: z.ZodOptional<z.ZodString>;
|
|
43
|
+
musicalLanguage: z.ZodObject<{
|
|
44
|
+
description: z.ZodString;
|
|
45
|
+
structure: z.ZodString;
|
|
46
|
+
keyMoments: z.ZodArray<z.ZodString>;
|
|
47
|
+
teachingGoals: z.ZodArray<z.ZodString>;
|
|
48
|
+
styleTips: z.ZodArray<z.ZodString>;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
measureOverrides: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
51
|
+
measure: z.ZodNumber;
|
|
52
|
+
fingering: z.ZodOptional<z.ZodString>;
|
|
53
|
+
teachingNote: z.ZodOptional<z.ZodString>;
|
|
54
|
+
dynamics: z.ZodOptional<z.ZodString>;
|
|
55
|
+
tempoOverride: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
}, z.core.$strip>>>;
|
|
57
|
+
splitPoint: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
}, z.core.$strip>;
|
|
59
|
+
export type SongConfig = z.infer<typeof SongConfigSchema>;
|
|
60
|
+
export type MeasureOverride = z.infer<typeof MeasureOverrideSchema>;
|
|
61
|
+
export interface ConfigError {
|
|
62
|
+
field: string;
|
|
63
|
+
message: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Validate a SongConfig object using the zod schema.
|
|
67
|
+
* Returns an empty array if valid.
|
|
68
|
+
*/
|
|
69
|
+
export declare function validateConfig(config: unknown): ConfigError[];
|
|
70
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/songs/config/schema.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,qBAAqB;;;;;;iBAMhC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;iBAMhC,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAe3B,CAAC;AAIH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAIpE,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,WAAW,EAAE,CAQ7D"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// ─── Song Config Schema ──────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Human-authored config that accompanies each .mid file.
|
|
4
|
+
// The MIDI ingest pipeline merges this config with extracted note data
|
|
5
|
+
// to produce a complete SongEntry.
|
|
6
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { GENRES, DIFFICULTIES } from "../types.js";
|
|
9
|
+
// ─── Zod Schemas ─────────────────────────────────────────────────────────────
|
|
10
|
+
export const MeasureOverrideSchema = z.object({
|
|
11
|
+
measure: z.number().int().min(1),
|
|
12
|
+
fingering: z.string().optional(),
|
|
13
|
+
teachingNote: z.string().optional(),
|
|
14
|
+
dynamics: z.string().optional(),
|
|
15
|
+
tempoOverride: z.number().min(10).max(400).optional(),
|
|
16
|
+
});
|
|
17
|
+
export const MusicalLanguageSchema = z.object({
|
|
18
|
+
description: z.string().min(1),
|
|
19
|
+
structure: z.string().min(1),
|
|
20
|
+
keyMoments: z.array(z.string()),
|
|
21
|
+
teachingGoals: z.array(z.string()),
|
|
22
|
+
styleTips: z.array(z.string()),
|
|
23
|
+
});
|
|
24
|
+
export const SongConfigSchema = z.object({
|
|
25
|
+
id: z.string().regex(/^[a-z0-9]+(-[a-z0-9]+)*$/, "id must be kebab-case"),
|
|
26
|
+
title: z.string().min(1),
|
|
27
|
+
genre: z.enum(GENRES),
|
|
28
|
+
composer: z.string().optional(),
|
|
29
|
+
arranger: z.string().optional(),
|
|
30
|
+
difficulty: z.enum(DIFFICULTIES),
|
|
31
|
+
key: z.string().min(1),
|
|
32
|
+
tempo: z.number().min(10).max(400).optional(),
|
|
33
|
+
timeSignature: z.string().optional(),
|
|
34
|
+
tags: z.array(z.string()),
|
|
35
|
+
source: z.string().optional(),
|
|
36
|
+
musicalLanguage: MusicalLanguageSchema,
|
|
37
|
+
measureOverrides: z.array(MeasureOverrideSchema).optional(),
|
|
38
|
+
splitPoint: z.number().int().min(0).max(127).optional(),
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Validate a SongConfig object using the zod schema.
|
|
42
|
+
* Returns an empty array if valid.
|
|
43
|
+
*/
|
|
44
|
+
export function validateConfig(config) {
|
|
45
|
+
const result = SongConfigSchema.safeParse(config);
|
|
46
|
+
if (result.success)
|
|
47
|
+
return [];
|
|
48
|
+
return result.error.issues.map((issue) => ({
|
|
49
|
+
field: issue.path.join(".") || "root",
|
|
50
|
+
message: issue.message,
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/songs/config/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type { SongEntry, Measure, MusicalLanguage, Genre, Difficulty, NoteFormat, RegistryStats, SearchOptions, } from "./types.js";
|
|
2
|
+
export { GENRES, DIFFICULTIES } from "./types.js";
|
|
3
|
+
export { registerSong, registerSongs, validateSong, validateRegistry, getSong, getAllSongs, getSongsByGenre, getSongsByDifficulty, searchSongs, getStats, clearRegistry, } from "./registry.js";
|
|
4
|
+
export { loadSongsFromDir, loadSongFile, saveSong, initializeRegistry, } from "./loader.js";
|
|
5
|
+
export { midiToSongEntry, midiNoteToScientific } from "./midi/ingest.js";
|
|
6
|
+
export { separateHands, groupIntoChords, isChord, formatNote, formatHand, chordToString, ticksToDuration, } from "./midi/hands.js";
|
|
7
|
+
export type { HandSplit } from "./midi/hands.js";
|
|
8
|
+
export { ticksPerMeasure, computeTotalMeasures, sliceIntoMeasures, parseTimeSignature, resolveTimeSignature, } from "./midi/measures.js";
|
|
9
|
+
export type { MeasureBucket } from "./midi/measures.js";
|
|
10
|
+
export type { ResolvedNote, IngestTempoEvent, TimeSigEvent } from "./midi/types.js";
|
|
11
|
+
export { SongConfigSchema, MusicalLanguageSchema, MeasureOverrideSchema, validateConfig, } from "./config/schema.js";
|
|
12
|
+
export type { SongConfig, MeasureOverride, ConfigError } from "./config/schema.js";
|
|
13
|
+
export { loadSongConfigs, loadSongConfig, listConfigIds } from "./config/loader.js";
|
|
14
|
+
export { generateJamBrief, formatJamBrief, inferChord, computeContour, getStyleGuidance } from "./jam.js";
|
|
15
|
+
export type { JamBrief, JamBriefOptions, ChordMeasure, MelodyMeasure } from "./jam.js";
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/songs/index.ts"],"names":[],"mappings":"AAMA,YAAY,EACV,SAAS,EACT,OAAO,EACP,eAAe,EACf,KAAK,EACL,UAAU,EACV,UAAU,EACV,aAAa,EACb,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAGlD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,aAAa,GACd,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EACL,aAAa,EACb,eAAe,EACf,OAAO,EACP,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpF,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAGnF,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGpF,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC1G,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ─── Songs Subsystem ────────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Everything for the song library: types, registry, loader, MIDI ingest, config.
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
export { GENRES, DIFFICULTIES } from "./types.js";
|
|
6
|
+
// Registry
|
|
7
|
+
export { registerSong, registerSongs, validateSong, validateRegistry, getSong, getAllSongs, getSongsByGenre, getSongsByDifficulty, searchSongs, getStats, clearRegistry, } from "./registry.js";
|
|
8
|
+
// Song loader (JSON files → registry)
|
|
9
|
+
export { loadSongsFromDir, loadSongFile, saveSong, initializeRegistry, } from "./loader.js";
|
|
10
|
+
// MIDI ingest pipeline
|
|
11
|
+
export { midiToSongEntry, midiNoteToScientific } from "./midi/ingest.js";
|
|
12
|
+
export { separateHands, groupIntoChords, isChord, formatNote, formatHand, chordToString, ticksToDuration, } from "./midi/hands.js";
|
|
13
|
+
export { ticksPerMeasure, computeTotalMeasures, sliceIntoMeasures, parseTimeSignature, resolveTimeSignature, } from "./midi/measures.js";
|
|
14
|
+
// Config schemas
|
|
15
|
+
export { SongConfigSchema, MusicalLanguageSchema, MeasureOverrideSchema, validateConfig, } from "./config/schema.js";
|
|
16
|
+
// Config loader
|
|
17
|
+
export { loadSongConfigs, loadSongConfig, listConfigIds } from "./config/loader.js";
|
|
18
|
+
// Jam session
|
|
19
|
+
export { generateJamBrief, formatJamBrief, inferChord, computeContour, getStyleGuidance } from "./jam.js";
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/songs/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAchF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAElD,WAAW;AACX,OAAO,EACL,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,aAAa,GACd,MAAM,eAAe,CAAC;AAEvB,sCAAsC;AACtC,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,uBAAuB;AACvB,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EACL,aAAa,EACb,eAAe,EACf,OAAO,EACP,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAM5B,iBAAiB;AACjB,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAG5B,gBAAgB;AAChB,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEpF,cAAc;AACd,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SongEntry, Genre, Difficulty } from "./types.js";
|
|
2
|
+
export interface JamBriefOptions {
|
|
3
|
+
/** Target genre for reinterpretation. */
|
|
4
|
+
style?: Genre;
|
|
5
|
+
/** Target mood (e.g., "upbeat", "melancholic", "dreamy"). */
|
|
6
|
+
mood?: string;
|
|
7
|
+
/** Target difficulty level. */
|
|
8
|
+
difficulty?: Difficulty;
|
|
9
|
+
/** Measure range to focus on, e.g., "1-8". */
|
|
10
|
+
measures?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ChordMeasure {
|
|
13
|
+
measure: number;
|
|
14
|
+
leftHand: string;
|
|
15
|
+
impliedChord: string;
|
|
16
|
+
}
|
|
17
|
+
export interface MelodyMeasure {
|
|
18
|
+
measure: number;
|
|
19
|
+
rightHand: string;
|
|
20
|
+
contour: "ascending" | "descending" | "static" | "arc";
|
|
21
|
+
}
|
|
22
|
+
export interface JamBrief {
|
|
23
|
+
source: {
|
|
24
|
+
id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
composer?: string;
|
|
27
|
+
genre: Genre;
|
|
28
|
+
key: string;
|
|
29
|
+
tempo: number;
|
|
30
|
+
timeSignature: string;
|
|
31
|
+
structure: string;
|
|
32
|
+
};
|
|
33
|
+
chordProgression: ChordMeasure[];
|
|
34
|
+
melodyOutline: MelodyMeasure[];
|
|
35
|
+
styleGuidance: string[];
|
|
36
|
+
instructions: string[];
|
|
37
|
+
}
|
|
38
|
+
/** Infer a chord symbol from a left-hand notation string. */
|
|
39
|
+
export declare function inferChord(leftHand: string): string;
|
|
40
|
+
/** Classify the melodic contour of a right-hand notation string. */
|
|
41
|
+
export declare function computeContour(rightHand: string): "ascending" | "descending" | "static" | "arc";
|
|
42
|
+
/** Get style guidance strings for a target genre and optional mood. */
|
|
43
|
+
export declare function getStyleGuidance(genre?: Genre, mood?: string): string[];
|
|
44
|
+
/** Generate a jam brief from a source song. */
|
|
45
|
+
export declare function generateJamBrief(song: SongEntry, options?: JamBriefOptions): JamBrief;
|
|
46
|
+
/** Format a JamBrief as readable markdown text for the LLM. */
|
|
47
|
+
export declare function formatJamBrief(brief: JamBrief, options?: JamBriefOptions): string;
|
|
48
|
+
//# sourceMappingURL=jam.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jam.d.ts","sourceRoot":"","sources":["../../src/songs/jam.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAW,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAIxE,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,KAAK,CAAC;CACxD;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,KAAK,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,gBAAgB,EAAE,YAAY,EAAE,CAAC;IACjC,aAAa,EAAE,aAAa,EAAE,CAAC;IAC/B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AA+FD,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUnD;AAID,oEAAoE;AACpE,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,QAAQ,GAAG,KAAK,CAyB/F;AAsFD,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAcvE;AAYD,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAsDzF;AAID,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CAsDrF"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// ─── Jam Session ────────────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Extracts a structured "jam brief" from a source song so an LLM can create
|
|
4
|
+
// its own interpretation. The brief includes chord analysis, melody outline,
|
|
5
|
+
// structure, and genre-specific style guidance.
|
|
6
|
+
//
|
|
7
|
+
// This is a read-only analysis tool — the LLM uses the brief as source
|
|
8
|
+
// material, then creates its own SongEntry via add_song and plays it.
|
|
9
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
10
|
+
// ─── Pitch Utilities ────────────────────────────────────────────────────────
|
|
11
|
+
const NOTE_NAMES = {
|
|
12
|
+
C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11,
|
|
13
|
+
};
|
|
14
|
+
/** Parse a note token (e.g., "C4:q", "F#5", "Bb3:h") into a MIDI number. Returns -1 for rests. */
|
|
15
|
+
function tokenToMidi(token) {
|
|
16
|
+
const clean = token.split(":")[0].trim();
|
|
17
|
+
if (clean === "R" || clean === "r")
|
|
18
|
+
return -1;
|
|
19
|
+
const match = clean.match(/^([A-Ga-g])(#|b)?(\d)$/);
|
|
20
|
+
if (!match)
|
|
21
|
+
return -1;
|
|
22
|
+
const [, letter, accidental, octStr] = match;
|
|
23
|
+
let midi = (parseInt(octStr, 10) + 1) * 12 + NOTE_NAMES[letter.toUpperCase()];
|
|
24
|
+
if (accidental === "#")
|
|
25
|
+
midi += 1;
|
|
26
|
+
if (accidental === "b")
|
|
27
|
+
midi -= 1;
|
|
28
|
+
return midi;
|
|
29
|
+
}
|
|
30
|
+
/** Extract just the note name (e.g., "C", "F#", "Bb") from a token. */
|
|
31
|
+
function tokenToNoteName(token) {
|
|
32
|
+
const clean = token.split(":")[0].trim();
|
|
33
|
+
if (clean === "R" || clean === "r")
|
|
34
|
+
return null;
|
|
35
|
+
const match = clean.match(/^([A-Ga-g])(#|b)?(\d)$/);
|
|
36
|
+
if (!match)
|
|
37
|
+
return null;
|
|
38
|
+
const [, letter, accidental] = match;
|
|
39
|
+
return letter.toUpperCase() + (accidental ?? "");
|
|
40
|
+
}
|
|
41
|
+
/** Convert a note name to pitch class (0-11). */
|
|
42
|
+
function nameToPitchClass(name) {
|
|
43
|
+
const letter = name.charAt(0);
|
|
44
|
+
let pc = NOTE_NAMES[letter] ?? 0;
|
|
45
|
+
if (name.length > 1) {
|
|
46
|
+
if (name.charAt(1) === "#")
|
|
47
|
+
pc = (pc + 1) % 12;
|
|
48
|
+
if (name.charAt(1) === "b")
|
|
49
|
+
pc = (pc + 11) % 12;
|
|
50
|
+
}
|
|
51
|
+
return pc;
|
|
52
|
+
}
|
|
53
|
+
// ─── Chord Inference ────────────────────────────────────────────────────────
|
|
54
|
+
const PC_NAMES = ["C", "C#", "D", "Eb", "E", "F", "F#", "G", "Ab", "A", "Bb", "B"];
|
|
55
|
+
const CHORD_TEMPLATES = [
|
|
56
|
+
{ name: "maj", intervals: [0, 4, 7] },
|
|
57
|
+
{ name: "m", intervals: [0, 3, 7] },
|
|
58
|
+
{ name: "7", intervals: [0, 4, 7, 10] },
|
|
59
|
+
{ name: "maj7", intervals: [0, 4, 7, 11] },
|
|
60
|
+
{ name: "m7", intervals: [0, 3, 7, 10] },
|
|
61
|
+
{ name: "dim", intervals: [0, 3, 6] },
|
|
62
|
+
{ name: "m7b5", intervals: [0, 3, 6, 10] },
|
|
63
|
+
{ name: "aug", intervals: [0, 4, 8] },
|
|
64
|
+
{ name: "sus4", intervals: [0, 5, 7] },
|
|
65
|
+
{ name: "sus2", intervals: [0, 2, 7] },
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Best-effort chord inference from a set of pitch classes.
|
|
69
|
+
* Tries each pitch class as a potential root and matches against templates.
|
|
70
|
+
*/
|
|
71
|
+
function inferChordFromPitchClasses(pitchClasses) {
|
|
72
|
+
if (pitchClasses.length === 0)
|
|
73
|
+
return "N/A";
|
|
74
|
+
if (pitchClasses.length === 1)
|
|
75
|
+
return PC_NAMES[pitchClasses[0]];
|
|
76
|
+
const unique = [...new Set(pitchClasses)];
|
|
77
|
+
let bestMatch = "";
|
|
78
|
+
let bestScore = 0;
|
|
79
|
+
for (const root of unique) {
|
|
80
|
+
const intervals = unique.map(pc => (pc - root + 12) % 12).sort((a, b) => a - b);
|
|
81
|
+
for (const tmpl of CHORD_TEMPLATES) {
|
|
82
|
+
// Count how many template intervals are present
|
|
83
|
+
const matched = tmpl.intervals.filter(iv => intervals.includes(iv)).length;
|
|
84
|
+
const score = matched / tmpl.intervals.length;
|
|
85
|
+
if (score > bestScore || (score === bestScore && tmpl.intervals.length > 3)) {
|
|
86
|
+
bestScore = score;
|
|
87
|
+
const suffix = tmpl.name === "maj" ? "" : tmpl.name;
|
|
88
|
+
bestMatch = PC_NAMES[root] + suffix;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return bestScore >= 0.66 ? bestMatch : PC_NAMES[unique[0]]; // fallback to bass note
|
|
93
|
+
}
|
|
94
|
+
/** Infer a chord symbol from a left-hand notation string. */
|
|
95
|
+
export function inferChord(leftHand) {
|
|
96
|
+
const tokens = leftHand.split(/\s+/).filter(Boolean);
|
|
97
|
+
const pitchClasses = [];
|
|
98
|
+
for (const tok of tokens) {
|
|
99
|
+
const name = tokenToNoteName(tok);
|
|
100
|
+
if (name)
|
|
101
|
+
pitchClasses.push(nameToPitchClass(name));
|
|
102
|
+
}
|
|
103
|
+
return inferChordFromPitchClasses(pitchClasses);
|
|
104
|
+
}
|
|
105
|
+
// ─── Contour Analysis ───────────────────────────────────────────────────────
|
|
106
|
+
/** Classify the melodic contour of a right-hand notation string. */
|
|
107
|
+
export function computeContour(rightHand) {
|
|
108
|
+
const tokens = rightHand.split(/\s+/).filter(Boolean);
|
|
109
|
+
const midis = [];
|
|
110
|
+
for (const tok of tokens) {
|
|
111
|
+
const m = tokenToMidi(tok);
|
|
112
|
+
if (m >= 0)
|
|
113
|
+
midis.push(m);
|
|
114
|
+
}
|
|
115
|
+
if (midis.length <= 1)
|
|
116
|
+
return "static";
|
|
117
|
+
const first = midis[0];
|
|
118
|
+
const last = midis[midis.length - 1];
|
|
119
|
+
const mid = midis[Math.floor(midis.length / 2)];
|
|
120
|
+
const diff = last - first;
|
|
121
|
+
// Check for arc: goes up then down, or down then up
|
|
122
|
+
if (midis.length >= 3) {
|
|
123
|
+
const goesUpThenDown = mid > first && mid > last;
|
|
124
|
+
const goesDownThenUp = mid < first && mid < last;
|
|
125
|
+
if (goesUpThenDown || goesDownThenUp)
|
|
126
|
+
return "arc";
|
|
127
|
+
}
|
|
128
|
+
if (Math.abs(diff) <= 1)
|
|
129
|
+
return "static";
|
|
130
|
+
return diff > 0 ? "ascending" : "descending";
|
|
131
|
+
}
|
|
132
|
+
// ─── Style Guidance ─────────────────────────────────────────────────────────
|
|
133
|
+
const STYLE_HINTS = {
|
|
134
|
+
classical: [
|
|
135
|
+
"Maintain strict tempo and dynamic contrasts",
|
|
136
|
+
"Use rubato sparingly at phrase endings",
|
|
137
|
+
"Voice the melody above accompaniment figures",
|
|
138
|
+
"Pedal changes on harmonic shifts",
|
|
139
|
+
"Observe all articulation markings (legato, staccato)",
|
|
140
|
+
],
|
|
141
|
+
jazz: [
|
|
142
|
+
"Swing eighths: long-short feel, emphasis on beats 2 and 4",
|
|
143
|
+
"Add 7th, 9th, and 13th extensions to chords",
|
|
144
|
+
"Shell voicings in left hand (root + 7th or 3rd + 7th)",
|
|
145
|
+
"Chromatic approach notes and passing tones in melody",
|
|
146
|
+
"Walking bass line: stepwise motion connecting chord roots",
|
|
147
|
+
],
|
|
148
|
+
pop: [
|
|
149
|
+
"Steady eighth-note pulse in left hand (broken chords or arpeggios)",
|
|
150
|
+
"Keep melody simple and singable",
|
|
151
|
+
"Build intensity through verse → chorus dynamics",
|
|
152
|
+
"Use sustain pedal freely for full sound",
|
|
153
|
+
"Rhythmic consistency matters more than harmonic complexity",
|
|
154
|
+
],
|
|
155
|
+
blues: [
|
|
156
|
+
"Shuffle feel: dotted-eighth + sixteenth pattern",
|
|
157
|
+
"Minor pentatonic scale for melody embellishment",
|
|
158
|
+
"Call-and-response between hands",
|
|
159
|
+
"12-bar blues form if restructuring",
|
|
160
|
+
"Blue notes: b3, b5, b7 for authentic color",
|
|
161
|
+
],
|
|
162
|
+
rock: [
|
|
163
|
+
"Strong downbeats with octave bass",
|
|
164
|
+
"Power chord voicings (root + fifth) in left hand",
|
|
165
|
+
"Driving eighth-note rhythm, slight accent on backbeats",
|
|
166
|
+
"Build tension with dynamics, release on chorus",
|
|
167
|
+
"Keep it raw — precision matters less than energy",
|
|
168
|
+
],
|
|
169
|
+
rnb: [
|
|
170
|
+
"Smooth voice leading between chords",
|
|
171
|
+
"Extended harmonies: 9ths, 11ths, 13ths",
|
|
172
|
+
"Syncopated rhythm — anticipate the beat",
|
|
173
|
+
"Gospel-influenced passing chords and suspensions",
|
|
174
|
+
"Gentle touch, let notes breathe with pedal",
|
|
175
|
+
],
|
|
176
|
+
latin: [
|
|
177
|
+
"Bossa nova: bass note on 1, chord on the 'and' of 2",
|
|
178
|
+
"Anticipation: place chords just before the downbeat",
|
|
179
|
+
"Clave rhythm awareness (3-2 or 2-3 pattern)",
|
|
180
|
+
"Light touch, minimal sustain pedal",
|
|
181
|
+
"Melody should float above a steady rhythmic foundation",
|
|
182
|
+
],
|
|
183
|
+
film: [
|
|
184
|
+
"Cinematic dynamics: start sparse, build to full texture",
|
|
185
|
+
"Arpeggiated patterns create motion and atmosphere",
|
|
186
|
+
"Wide voicings spanning 2+ octaves for orchestral feel",
|
|
187
|
+
"Rubato and tempo flexibility for emotional expression",
|
|
188
|
+
"Let silence and sustain do the storytelling",
|
|
189
|
+
],
|
|
190
|
+
ragtime: [
|
|
191
|
+
"Strict tempo — ragtime swings from rhythm, not rubato",
|
|
192
|
+
"Left hand: oom-pah bass (low note on 1 & 3, chord on 2 & 4)",
|
|
193
|
+
"Right hand: syncopated melody against steady left hand",
|
|
194
|
+
"Crisp articulation, minimal pedal",
|
|
195
|
+
"Accent the off-beat syncopations in the melody",
|
|
196
|
+
],
|
|
197
|
+
"new-age": [
|
|
198
|
+
"Gentle arpeggiated patterns, let notes ring with pedal",
|
|
199
|
+
"Slow harmonic rhythm — let each chord breathe",
|
|
200
|
+
"Use open voicings (root-fifth-octave) for spaciousness",
|
|
201
|
+
"Dynamics should be subtle, mostly pp to mp",
|
|
202
|
+
"Create a meditative, flowing atmosphere",
|
|
203
|
+
],
|
|
204
|
+
};
|
|
205
|
+
const MOOD_HINTS = {
|
|
206
|
+
upbeat: ["Increase tempo 10-20%", "Accent rhythmic drive", "Use brighter register (higher octave)"],
|
|
207
|
+
melancholic: ["Slow down 10-15%", "Minor key reharmonization", "Use lower register, softer dynamics"],
|
|
208
|
+
dreamy: ["Generous pedal, let harmonies blend", "Rubato tempo", "Arpeggiate chords instead of blocking"],
|
|
209
|
+
energetic: ["Strong accents on downbeats", "Full dynamic range", "Use octave doublings for power"],
|
|
210
|
+
gentle: ["Soft dynamics (pp-mp)", "Legato touch throughout", "Sparse left hand, let melody lead"],
|
|
211
|
+
playful: ["Staccato articulation", "Syncopated accents", "Contrast registers for surprise"],
|
|
212
|
+
};
|
|
213
|
+
/** Get style guidance strings for a target genre and optional mood. */
|
|
214
|
+
export function getStyleGuidance(genre, mood) {
|
|
215
|
+
const hints = [];
|
|
216
|
+
if (genre)
|
|
217
|
+
hints.push(...STYLE_HINTS[genre]);
|
|
218
|
+
if (mood) {
|
|
219
|
+
const key = mood.toLowerCase();
|
|
220
|
+
if (MOOD_HINTS[key]) {
|
|
221
|
+
hints.push(...MOOD_HINTS[key]);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
hints.push(`Interpret with a "${mood}" feel — adjust dynamics, tempo, and articulation accordingly`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return hints;
|
|
228
|
+
}
|
|
229
|
+
// ─── Jam Brief Generation ───────────────────────────────────────────────────
|
|
230
|
+
/** Parse a measure range string like "1-8" into [start, end] (0-based indices). */
|
|
231
|
+
function parseMeasureRange(rangeStr, total) {
|
|
232
|
+
const parts = rangeStr.split("-").map(s => parseInt(s.trim(), 10));
|
|
233
|
+
const start = Math.max(0, (parts[0] ?? 1) - 1);
|
|
234
|
+
const end = Math.min(total - 1, (parts[1] ?? parts[0] ?? total) - 1);
|
|
235
|
+
return [start, end];
|
|
236
|
+
}
|
|
237
|
+
/** Generate a jam brief from a source song. */
|
|
238
|
+
export function generateJamBrief(song, options = {}) {
|
|
239
|
+
// Determine measure range
|
|
240
|
+
let measures = song.measures;
|
|
241
|
+
if (options.measures) {
|
|
242
|
+
const [start, end] = parseMeasureRange(options.measures, song.measures.length);
|
|
243
|
+
measures = song.measures.slice(start, end + 1);
|
|
244
|
+
}
|
|
245
|
+
// Analyze chords
|
|
246
|
+
const chordProgression = measures.map(m => ({
|
|
247
|
+
measure: m.number,
|
|
248
|
+
leftHand: m.leftHand,
|
|
249
|
+
impliedChord: inferChord(m.leftHand),
|
|
250
|
+
}));
|
|
251
|
+
// Analyze melody
|
|
252
|
+
const melodyOutline = measures.map(m => ({
|
|
253
|
+
measure: m.number,
|
|
254
|
+
rightHand: m.rightHand,
|
|
255
|
+
contour: computeContour(m.rightHand),
|
|
256
|
+
}));
|
|
257
|
+
// Style guidance
|
|
258
|
+
const styleGuidance = getStyleGuidance(options.style ?? song.genre, options.mood);
|
|
259
|
+
// Build instructions
|
|
260
|
+
const targetStyle = options.style ?? song.genre;
|
|
261
|
+
const targetMood = options.mood ? ` with a ${options.mood} feel` : "";
|
|
262
|
+
const targetDiff = options.difficulty ? ` at ${options.difficulty} level` : "";
|
|
263
|
+
const instructions = [
|
|
264
|
+
`Create your own ${targetStyle} interpretation of "${song.title}"${targetMood}${targetDiff}.`,
|
|
265
|
+
`Use the chord progression and melody outline as your starting point.`,
|
|
266
|
+
`Reharmonize, embellish, or simplify as you see fit — this is YOUR version.`,
|
|
267
|
+
`Write a new SongEntry JSON with id "jam-${song.id}-${targetStyle}" and save with add_song.`,
|
|
268
|
+
`Then play it with play_song to hear your creation.`,
|
|
269
|
+
];
|
|
270
|
+
return {
|
|
271
|
+
source: {
|
|
272
|
+
id: song.id,
|
|
273
|
+
title: song.title,
|
|
274
|
+
composer: song.composer,
|
|
275
|
+
genre: song.genre,
|
|
276
|
+
key: song.key,
|
|
277
|
+
tempo: song.tempo,
|
|
278
|
+
timeSignature: song.timeSignature,
|
|
279
|
+
structure: song.musicalLanguage.structure,
|
|
280
|
+
},
|
|
281
|
+
chordProgression,
|
|
282
|
+
melodyOutline,
|
|
283
|
+
styleGuidance,
|
|
284
|
+
instructions,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
// ─── Brief Formatting ───────────────────────────────────────────────────────
|
|
288
|
+
/** Format a JamBrief as readable markdown text for the LLM. */
|
|
289
|
+
export function formatJamBrief(brief, options = {}) {
|
|
290
|
+
const s = brief.source;
|
|
291
|
+
const targetStyle = options.style ?? s.genre;
|
|
292
|
+
const moodLabel = options.mood ? `, ${options.mood}` : "";
|
|
293
|
+
const rangeLabel = options.measures ? ` (measures ${options.measures})` : "";
|
|
294
|
+
const lines = [
|
|
295
|
+
`# Jam Brief: ${s.title} → ${targetStyle}${moodLabel}`,
|
|
296
|
+
``,
|
|
297
|
+
`## Source Material`,
|
|
298
|
+
`- **Original:** ${s.title}${s.composer ? ` by ${s.composer}` : ""}`,
|
|
299
|
+
`- **Genre:** ${s.genre} → ${targetStyle}${s.genre === targetStyle ? " (same genre reinterpretation)" : ""}`,
|
|
300
|
+
`- **Key:** ${s.key} | **Tempo:** ${s.tempo} BPM | **Time:** ${s.timeSignature}`,
|
|
301
|
+
`- **Structure:** ${s.structure}`,
|
|
302
|
+
``,
|
|
303
|
+
`## Chord Progression${rangeLabel}`,
|
|
304
|
+
`| Measure | Left Hand | Implied Chord |`,
|
|
305
|
+
`|---------|-----------|---------------|`,
|
|
306
|
+
];
|
|
307
|
+
for (const cm of brief.chordProgression) {
|
|
308
|
+
lines.push(`| ${cm.measure} | ${cm.leftHand} | ${cm.impliedChord} |`);
|
|
309
|
+
}
|
|
310
|
+
lines.push(``, `## Melody Outline${rangeLabel}`, `| Measure | Right Hand | Contour |`, `|---------|-----------|---------|`);
|
|
311
|
+
for (const mm of brief.melodyOutline) {
|
|
312
|
+
lines.push(`| ${mm.measure} | ${mm.rightHand} | ${mm.contour} |`);
|
|
313
|
+
}
|
|
314
|
+
lines.push(``, `## Style Guidance (target: ${targetStyle}${moodLabel})`);
|
|
315
|
+
for (const hint of brief.styleGuidance) {
|
|
316
|
+
lines.push(`- ${hint}`);
|
|
317
|
+
}
|
|
318
|
+
lines.push(``, `## Your Mission`);
|
|
319
|
+
for (let i = 0; i < brief.instructions.length; i++) {
|
|
320
|
+
lines.push(`${i + 1}. ${brief.instructions[i]}`);
|
|
321
|
+
}
|
|
322
|
+
return lines.join("\n");
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=jam.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jam.js","sourceRoot":"","sources":["../../src/songs/jam.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SongEntry } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Load all .json song files from a directory.
|
|
4
|
+
* Validates each file and returns valid SongEntry objects.
|
|
5
|
+
* Skips (with warning) any files that fail validation.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadSongsFromDir(dir: string): SongEntry[];
|
|
8
|
+
/**
|
|
9
|
+
* Load a single song from a JSON file.
|
|
10
|
+
* Validates the parsed object and returns a typed SongEntry.
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadSongFile(filePath: string): SongEntry;
|
|
13
|
+
/**
|
|
14
|
+
* Save a SongEntry as a JSON file in the given directory.
|
|
15
|
+
* Creates the directory if it doesn't exist.
|
|
16
|
+
* Returns the full path to the saved file.
|
|
17
|
+
*/
|
|
18
|
+
export declare function saveSong(song: SongEntry, dir: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Initialize the song registry from builtin and optional user directories.
|
|
21
|
+
* Clears any existing registry entries first.
|
|
22
|
+
*
|
|
23
|
+
* @param builtinDir - Path to the builtin songs directory (ships with package)
|
|
24
|
+
* @param userDir - Optional path to user songs directory (default: ~/.pianoai/songs/)
|
|
25
|
+
*/
|
|
26
|
+
export declare function initializeRegistry(builtinDir: string, userDir?: string): void;
|
|
27
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/songs/loader.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAgBzD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAOxD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAO7D;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CA2B7E"}
|