@audiofab-io/fv1-core 0.4.0 → 0.5.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.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * `.spnbank` — Easy Spin Program Bank file format.
3
+ *
4
+ * A `.spnbank` file is a small JSON manifest describing the eight program
5
+ * slots of an Easy Spin pedal. Each slot points at a workspace-relative
6
+ * `.spn` (FV-1 assembly) or `.spndiagram` (block diagram) file. The manifest
7
+ * is the unit of "program the whole pedal" — tooling resolves each slot to a
8
+ * compiled binary at programming time.
9
+ *
10
+ * Slot indices in the manifest are 1-based (slot 1..8) for human readability.
11
+ * The runtime API uses 0-based indices via `bankSlotIndex0()`.
12
+ *
13
+ * Round-trip:
14
+ * parseSpnBankJson(serializeSpnBankJson(b)) === b // structurally equal
15
+ */
16
+ /** A single entry in a `.spnbank` file. */
17
+ export interface SpnBankSlot {
18
+ /** 1-based slot number (1..8). */
19
+ slot: number;
20
+ /** Workspace-relative path to a `.spn` or `.spndiagram` file. Empty
21
+ * string indicates an unassigned slot. */
22
+ path: string;
23
+ }
24
+ /** Parsed `.spnbank` file. */
25
+ export interface SpnBankFile {
26
+ /** Display name. Defaults to the filename's basename when not set. */
27
+ name?: string;
28
+ /** Always exactly `PROGRAM_SLOT_COUNT` (8) entries, in slot-number order. */
29
+ slots: SpnBankSlot[];
30
+ }
31
+ /** True if a slot has no assigned program. */
32
+ export declare function isSlotEmpty(slot: SpnBankSlot): boolean;
33
+ /** Build an empty bank. All slots unassigned. */
34
+ export declare function createEmptyBank(name?: string): SpnBankFile;
35
+ /**
36
+ * Parse the JSON text of a `.spnbank` file. Always returns a structurally
37
+ * complete bank (8 slots in order); malformed JSON or missing fields fall
38
+ * back to defaults so tooling can render a usable editor over a corrupt or
39
+ * empty file.
40
+ *
41
+ * @param text Raw file contents.
42
+ * @param fallbackName Display name to use when the file's `name` field is
43
+ * missing — typically the file basename without `.spnbank`.
44
+ */
45
+ export declare function parseSpnBankJson(text: string, fallbackName?: string): SpnBankFile;
46
+ /**
47
+ * Serialise a `.spnbank` file to JSON text. Output is pretty-printed with
48
+ * 2-space indentation and a trailing newline.
49
+ */
50
+ export declare function serializeSpnBankJson(bank: SpnBankFile): string;
51
+ /** Convenience: 0-based index for a slot. */
52
+ export declare function bankSlotIndex0(slot: SpnBankSlot): number;
53
+ /** Re-exported for convenience so callers don't have to also import from `/pedal`. */
54
+ export { PROGRAM_SLOT_COUNT } from '../pedal/constants.js';
55
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/spnbank/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ;+CAC2C;IAC3C,IAAI,EAAE,MAAM,CAAA;CACb;AAED,8BAA8B;AAC9B,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,6EAA6E;IAC7E,KAAK,EAAE,WAAW,EAAE,CAAA;CACrB;AAED,8CAA8C;AAC9C,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAEtD;AAED,iDAAiD;AACjD,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAQ1D;AA6BD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,WAAW,CAoBjF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAM9D;AAED,6CAA6C;AAC7C,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAExD;AAED,sFAAsF;AACtF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * `.spnbank` — Easy Spin Program Bank file format.
3
+ *
4
+ * A `.spnbank` file is a small JSON manifest describing the eight program
5
+ * slots of an Easy Spin pedal. Each slot points at a workspace-relative
6
+ * `.spn` (FV-1 assembly) or `.spndiagram` (block diagram) file. The manifest
7
+ * is the unit of "program the whole pedal" — tooling resolves each slot to a
8
+ * compiled binary at programming time.
9
+ *
10
+ * Slot indices in the manifest are 1-based (slot 1..8) for human readability.
11
+ * The runtime API uses 0-based indices via `bankSlotIndex0()`.
12
+ *
13
+ * Round-trip:
14
+ * parseSpnBankJson(serializeSpnBankJson(b)) === b // structurally equal
15
+ */
16
+ import { PROGRAM_SLOT_COUNT } from '../pedal/constants.js';
17
+ /** True if a slot has no assigned program. */
18
+ export function isSlotEmpty(slot) {
19
+ return !slot.path;
20
+ }
21
+ /** Build an empty bank. All slots unassigned. */
22
+ export function createEmptyBank(name) {
23
+ return {
24
+ name,
25
+ slots: Array.from({ length: PROGRAM_SLOT_COUNT }, (_, i) => ({
26
+ slot: i + 1,
27
+ path: '',
28
+ })),
29
+ };
30
+ }
31
+ function normaliseSlots(input) {
32
+ const empty = () => Array.from({ length: PROGRAM_SLOT_COUNT }, (_, i) => ({ slot: i + 1, path: '' }));
33
+ if (!Array.isArray(input))
34
+ return empty();
35
+ // Build a lookup by slot number, tolerant of duplicate / missing entries
36
+ // and out-of-range slot numbers in the input.
37
+ const bySlot = new Map();
38
+ for (const entry of input) {
39
+ if (typeof entry !== 'object' || entry === null)
40
+ continue;
41
+ const e = entry;
42
+ const slot = typeof e.slot === 'number' ? e.slot : NaN;
43
+ const path = typeof e.path === 'string' ? e.path : '';
44
+ if (!Number.isFinite(slot))
45
+ continue;
46
+ if (slot < 1 || slot > PROGRAM_SLOT_COUNT)
47
+ continue;
48
+ bySlot.set(slot, path);
49
+ }
50
+ const result = empty();
51
+ for (const slot of result) {
52
+ const path = bySlot.get(slot.slot);
53
+ if (path !== undefined)
54
+ slot.path = path;
55
+ }
56
+ return result;
57
+ }
58
+ /**
59
+ * Parse the JSON text of a `.spnbank` file. Always returns a structurally
60
+ * complete bank (8 slots in order); malformed JSON or missing fields fall
61
+ * back to defaults so tooling can render a usable editor over a corrupt or
62
+ * empty file.
63
+ *
64
+ * @param text Raw file contents.
65
+ * @param fallbackName Display name to use when the file's `name` field is
66
+ * missing — typically the file basename without `.spnbank`.
67
+ */
68
+ export function parseSpnBankJson(text, fallbackName) {
69
+ if (!text || !text.trim()) {
70
+ return createEmptyBank(fallbackName);
71
+ }
72
+ let raw;
73
+ try {
74
+ raw = JSON.parse(text);
75
+ }
76
+ catch {
77
+ return createEmptyBank(fallbackName);
78
+ }
79
+ if (typeof raw !== 'object' || raw === null) {
80
+ return createEmptyBank(fallbackName);
81
+ }
82
+ const obj = raw;
83
+ const name = typeof obj.name === 'string' && obj.name ? obj.name : fallbackName;
84
+ const slots = normaliseSlots(obj.slots);
85
+ return { name, slots };
86
+ }
87
+ /**
88
+ * Serialise a `.spnbank` file to JSON text. Output is pretty-printed with
89
+ * 2-space indentation and a trailing newline.
90
+ */
91
+ export function serializeSpnBankJson(bank) {
92
+ const json = {
93
+ ...(bank.name ? { name: bank.name } : {}),
94
+ slots: bank.slots,
95
+ };
96
+ return JSON.stringify(json, null, 2) + '\n';
97
+ }
98
+ /** Convenience: 0-based index for a slot. */
99
+ export function bankSlotIndex0(slot) {
100
+ return slot.slot - 1;
101
+ }
102
+ /** Re-exported for convenience so callers don't have to also import from `/pedal`. */
103
+ export { PROGRAM_SLOT_COUNT } from '../pedal/constants.js';
104
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/spnbank/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audiofab-io/fv1-core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "FV-1 DSP toolchain: assembler, simulator, Intel HEX parser, and block-diagram compiler",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -22,7 +22,12 @@
22
22
  "./blockDiagram/node": {
23
23
  "import": "./dist/blockDiagram/blocks/BlockDirectoryLoader.js",
24
24
  "types": "./dist/blockDiagram/blocks/BlockDirectoryLoader.d.ts"
25
- }
25
+ },
26
+ "./spnbank": {
27
+ "import": "./dist/spnbank/index.js",
28
+ "types": "./dist/spnbank/index.d.ts"
29
+ },
30
+ "./package.json": "./package.json"
26
31
  },
27
32
  "files": [
28
33
  "dist",
@@ -32,6 +37,7 @@
32
37
  "build:manifest": "node scripts/build-manifest.mjs",
33
38
  "build": "npm run build:manifest && tsc",
34
39
  "clean": "rm -rf dist src/blockDiagram/builtinBlocks.ts",
40
+ "test": "node test/spnbank-test.mjs",
35
41
  "prepublishOnly": "npm run build"
36
42
  },
37
43
  "dependencies": {