@yam8d/m8-sdk 0.3.3 → 0.4.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/README.md +156 -15
- package/dist/index.js +1377 -204
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/types/client.d.ts +38 -5
- package/types/fxCommands.d.ts +47 -0
- package/types/fxInsight.d.ts +136 -0
- package/types/index.d.ts +3 -3
- package/types/parameterViews.d.ts +104 -0
- package/types/scaleInsight.d.ts +47 -0
- package/types/types.d.ts +7 -0
- package/types/viewContext.d.ts +179 -15
package/package.json
CHANGED
package/types/client.d.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
import type { M8KeyName, M8SdkConfig, M8State } from './types';
|
|
2
|
-
import type { M8SemanticContext } from './viewContext';
|
|
3
|
-
export type { CursorPos, CursorRect, M8KeyName, M8State, RGB, SystemInfos } from './types';
|
|
4
|
-
export type { M8SemanticContext } from './viewContext';
|
|
1
|
+
import type { M8KeyName, M8Screen, M8SdkConfig, M8State } from './types';
|
|
2
|
+
import type { M8DescriptionPart, M8SemanticContext } from './viewContext';
|
|
3
|
+
export type { CursorPos, CursorRect, M8KeyName, M8Screen, M8State, RGB, SystemInfos } from './types';
|
|
4
|
+
export type { M8DescriptionPart, M8SemanticContext } from './viewContext';
|
|
5
|
+
/**
|
|
6
|
+
* Everything the SDK knows about the moment: raw state, semantic context, its
|
|
7
|
+
* human-readable description and the whole screen. Plain JSON, safe to persist.
|
|
8
|
+
*/
|
|
9
|
+
export interface M8Snapshot {
|
|
10
|
+
capturedAt: number;
|
|
11
|
+
state: Pick<M8State, 'viewName' | 'viewTitle' | 'cursorPos' | 'selectionMode' | 'textUnderCursor' | 'currentLine' | 'deviceModel'>;
|
|
12
|
+
semantic: M8SemanticContext | null;
|
|
13
|
+
/** describeContext() output, e.g. "Note (N): Note C-4 — Phrase View row 00". */
|
|
14
|
+
description: string | null;
|
|
15
|
+
/** The same description as tagged parts (describeContextParts()). */
|
|
16
|
+
descriptionParts: M8DescriptionPart[] | null;
|
|
17
|
+
screen: M8Screen;
|
|
18
|
+
}
|
|
5
19
|
export interface M8Client {
|
|
6
20
|
readonly state: M8State;
|
|
7
21
|
readonly isConnected: boolean;
|
|
@@ -18,6 +32,13 @@ export interface M8Client {
|
|
|
18
32
|
sendKeyUp(): Promise<void>;
|
|
19
33
|
getState(): M8State;
|
|
20
34
|
fetchState(): Promise<M8State>;
|
|
35
|
+
/**
|
|
36
|
+
* Whole screen as text rows, read directly from the M8 character stream.
|
|
37
|
+
* No cursor movement involved, so it is instant and side-effect free.
|
|
38
|
+
*/
|
|
39
|
+
getScreen(): Promise<M8Screen>;
|
|
40
|
+
/** Fresh state + semantic context + description + whole screen in one call. */
|
|
41
|
+
captureSnapshot(): Promise<M8Snapshot>;
|
|
21
42
|
onStateChange(callback: (state: M8State) => void): () => void;
|
|
22
43
|
onViewChange(callback: (viewName: string | null, viewTitle: string | null) => void): () => void;
|
|
23
44
|
onCursorMove(callback: (pos: M8State['cursorPos'], rect: M8State['cursorRect'], selectionMode: boolean) => void): () => void;
|
|
@@ -32,9 +53,21 @@ export interface M8Client {
|
|
|
32
53
|
getSemanticContext(): M8SemanticContext | null;
|
|
33
54
|
/**
|
|
34
55
|
* Returns a short human-readable description of what the cursor is on,
|
|
35
|
-
* e.g. "Track 3 —
|
|
56
|
+
* e.g. "Track 3: 0A — Track 3 — Song View row 01".
|
|
57
|
+
*
|
|
58
|
+
* It also reads the cursor against its surroundings when that changes the meaning:
|
|
59
|
+
* an FX cell under a running REP says which command is repeating and where it has
|
|
60
|
+
* got to, FX values are explained (ARP 37 → "+3 and +7 semitones"), and the Scale
|
|
61
|
+
* View names the scale the enabled notes spell. The surroundings come from a copy of
|
|
62
|
+
* the screen the client keeps fresh in the background; call getScreen() (or await
|
|
63
|
+
* captureSnapshot()) first if you need it guaranteed current.
|
|
36
64
|
*/
|
|
37
65
|
describeContext(): string | null;
|
|
66
|
+
/**
|
|
67
|
+
* The description as ordered, tagged parts ({ kind: 'field' | 'meaning' | 'location' | …, text }),
|
|
68
|
+
* for consumers that want to keep, drop, reorder or style some of them.
|
|
69
|
+
*/
|
|
70
|
+
describeContextParts(): M8DescriptionPart[] | null;
|
|
38
71
|
}
|
|
39
72
|
export declare function createM8Client(config?: M8SdkConfig): Promise<M8Client>;
|
|
40
73
|
export declare function createM8ClientSync(config?: M8SdkConfig): {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fxCommands.ts
|
|
3
|
+
*
|
|
4
|
+
* Lookup of FX commands (the 3-letter names in the FX columns) with what is known
|
|
5
|
+
* about them: description, whether they are relative, and which instrument types
|
|
6
|
+
* offer them. Data lives in m8-view-context.json (`fxCommands`, `instrumentFx`).
|
|
7
|
+
*/
|
|
8
|
+
export type FxCategory = 'sequencer' | 'instrument' | 'mixer' | 'modulation';
|
|
9
|
+
export interface M8FxCommandInfo {
|
|
10
|
+
description: string;
|
|
11
|
+
category: FxCategory;
|
|
12
|
+
/**
|
|
13
|
+
* Relative instrument commands add to the parameter's current value (01-7F add,
|
|
14
|
+
* 80-FF subtract) and accumulate until a note is triggered with RET or with an
|
|
15
|
+
* instrument number in the I column.
|
|
16
|
+
*/
|
|
17
|
+
relative: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Instrument types that offer the command, when it is not offered by all of them
|
|
20
|
+
* (`null` = every type, or not an instrument command). Only types whose command
|
|
21
|
+
* list was read from a device are considered.
|
|
22
|
+
*/
|
|
23
|
+
instrumentTypes: string[] | null;
|
|
24
|
+
/** Modulator slot 1-4 for envelope/LFO commands (EA1, LF3…). */
|
|
25
|
+
slot: number | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Look up a 3-character FX command name. Modulator commands carry their slot
|
|
29
|
+
* number as the third character (EA1, LF3…). Returns undefined for unknown commands.
|
|
30
|
+
*/
|
|
31
|
+
export declare function lookupFxCommand(cmd: string): M8FxCommandInfo | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* True for relative instrument commands: values 01-7F add to the parameter, 80-FF
|
|
34
|
+
* subtract (see "Relative and Absolute FX Commands" in the manual).
|
|
35
|
+
*/
|
|
36
|
+
export declare function isRelativeFx(cmd: string): boolean;
|
|
37
|
+
export interface M8InstrumentFxList {
|
|
38
|
+
/** Type key as used in the schema: sampler, macrosynth, fmsynth, hypersynth, midiout. */
|
|
39
|
+
type: string;
|
|
40
|
+
/** Read from a real device (false = taken from documentation). */
|
|
41
|
+
verified: boolean;
|
|
42
|
+
commands: string[];
|
|
43
|
+
}
|
|
44
|
+
/** Instrument command names offered for an instrument type, or null when unknown. */
|
|
45
|
+
export declare function getInstrumentFxCommands(type: string): M8InstrumentFxList | null;
|
|
46
|
+
/** What the manual says about relative commands (for tooltips and descriptions). */
|
|
47
|
+
export declare const RELATIVE_FX_RULE: string;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fxInsight.ts
|
|
3
|
+
*
|
|
4
|
+
* Explains what an FX value does and follows FX "lanes" (FX1/FX2/FX3 columns)
|
|
5
|
+
* down a phrase, so a REP command - or an empty cell sitting under one - can be
|
|
6
|
+
* described in terms of the command it is repeating.
|
|
7
|
+
*
|
|
8
|
+
* Purely structural: works on already-parsed cells, no dependency on viewContext.
|
|
9
|
+
*/
|
|
10
|
+
import { isRelativeFx } from './fxCommands';
|
|
11
|
+
export { isRelativeFx };
|
|
12
|
+
/** The subset of a parsed cell this module needs. */
|
|
13
|
+
export interface FxCell {
|
|
14
|
+
rawValue: string;
|
|
15
|
+
hexValue: number | null;
|
|
16
|
+
isEmpty: boolean;
|
|
17
|
+
}
|
|
18
|
+
/** One phrase step, indexed by column key ('note', 'fx1cmd', 'fx1val', …). */
|
|
19
|
+
export interface FxStepRow {
|
|
20
|
+
fields: Record<string, FxCell | undefined>;
|
|
21
|
+
}
|
|
22
|
+
export type FxLaneEvent =
|
|
23
|
+
/** Nothing FX-related happens in this lane at this step. */
|
|
24
|
+
'none'
|
|
25
|
+
/** A regular command starts a new base value; any REP ends. */
|
|
26
|
+
| 'set'
|
|
27
|
+
/** A regular command replaces a REP that was running. */
|
|
28
|
+
| 'replace'
|
|
29
|
+
/** REP XX (XX>0) starts repeating. */
|
|
30
|
+
| 'rep'
|
|
31
|
+
/** REP XX while another REP was already running (new increment). */
|
|
32
|
+
| 'repChange'
|
|
33
|
+
/** REP 00 stops the repeat. */
|
|
34
|
+
| 'repStop'
|
|
35
|
+
/** REP with no earlier command in this lane and none carried over. */
|
|
36
|
+
| 'orphan'
|
|
37
|
+
/** RTO sets the limit for the running REP. */
|
|
38
|
+
| 'rto'
|
|
39
|
+
/** Empty cell while a REP is active: the repeated command fires again. */
|
|
40
|
+
| 'continue';
|
|
41
|
+
export interface M8FxLaneTrace {
|
|
42
|
+
/** FX lane, 1-3. */
|
|
43
|
+
lane: number;
|
|
44
|
+
/** Phrase step this trace was computed for. */
|
|
45
|
+
step: number;
|
|
46
|
+
event: FxLaneEvent;
|
|
47
|
+
/** Last regular command in the lane at or before `step`. */
|
|
48
|
+
source: {
|
|
49
|
+
cmd: string;
|
|
50
|
+
value: number | null;
|
|
51
|
+
step: number;
|
|
52
|
+
} | null;
|
|
53
|
+
/** True when a REP is running after this step. */
|
|
54
|
+
active: boolean;
|
|
55
|
+
/** Per-step increment of the running REP (0x01-0xFF, 0x80+ = negative). */
|
|
56
|
+
increment: number | null;
|
|
57
|
+
/** Value set with RTO, if any. */
|
|
58
|
+
limit: number | null;
|
|
59
|
+
/** Value the repeated command has at this step (null when unknown). */
|
|
60
|
+
value: number | null;
|
|
61
|
+
/** True when `value` was clamped by the RTO limit. */
|
|
62
|
+
atLimit: boolean;
|
|
63
|
+
/** Step where the running REP began. */
|
|
64
|
+
repStartStep: number | null;
|
|
65
|
+
/** True when a REP from a previous phrase is running here (value unknown). */
|
|
66
|
+
carried: boolean;
|
|
67
|
+
}
|
|
68
|
+
export declare const hex2: (n: number) => string;
|
|
69
|
+
/** Hex text of a value, with its signed reading when the command is relative and negative. */
|
|
70
|
+
export declare function formatFxValue(cmd: string, value: number): string;
|
|
71
|
+
export interface FxValueContext {
|
|
72
|
+
/** Note on the same step (used by ARP to spell out the notes). */
|
|
73
|
+
note?: string | null;
|
|
74
|
+
/** Table view: HOP and CHA mean something different there. */
|
|
75
|
+
inTable?: boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Short explanation of what `value` does for `cmd` (phrase view), or null when
|
|
79
|
+
* the command has no interpretation worth adding beyond its name.
|
|
80
|
+
*/
|
|
81
|
+
export declare function describeFxValue(cmd: string, value: number, ctx?: FxValueContext): string | null;
|
|
82
|
+
/**
|
|
83
|
+
* Walks FX lane `lane` (1-3) from step 0 to `step` and reports what is in effect
|
|
84
|
+
* there. `rows` must hold every step of the phrase, index = step.
|
|
85
|
+
*
|
|
86
|
+
* Model (manual: "Repeat"): REP XX repeats the lane's last command with its value
|
|
87
|
+
* bumped by XX on each step, keeps going through empty cells, and stops on a new
|
|
88
|
+
* command or REP00. RTO XX in the same lane sets the value REP stops at.
|
|
89
|
+
* `carriedIn` says a REP from a previous phrase is running (the '^^' marker).
|
|
90
|
+
*/
|
|
91
|
+
export declare function analyzeFxLane(rows: FxStepRow[], lane: number, step: number, carriedIn?: boolean): M8FxLaneTrace;
|
|
92
|
+
/**
|
|
93
|
+
* One-sentence explanation of a lane trace, or null when the lane has nothing to
|
|
94
|
+
* say at this step. Meant to be appended to the cell description.
|
|
95
|
+
*/
|
|
96
|
+
export declare function describeFxLane(t: M8FxLaneTrace): string | null;
|
|
97
|
+
/**
|
|
98
|
+
* What a relative instrument command (VOL, CUT, PIT…) has done to its parameter
|
|
99
|
+
* since the last note trigger.
|
|
100
|
+
*/
|
|
101
|
+
export interface M8RelativeFxState {
|
|
102
|
+
cmd: string;
|
|
103
|
+
/** Net change applied to the parameter (signed; the device clamps to the parameter's range). */
|
|
104
|
+
total: number;
|
|
105
|
+
/** Times the command was applied (REP-driven repeats included). */
|
|
106
|
+
count: number;
|
|
107
|
+
/** Step of the last trigger that reset it, or null when none happened in this phrase. */
|
|
108
|
+
resetStep: number | null;
|
|
109
|
+
/** What reset it: a row with an instrument number in the I column, or a RET command. */
|
|
110
|
+
resetBy: 'instrument' | 'ret' | null;
|
|
111
|
+
/** True when some of the applications came from a running REP. */
|
|
112
|
+
viaRep: boolean;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* What makes step `row` trigger the instrument again, which resets every relative change:
|
|
116
|
+
* an instrument number in the I column, a RET command, or nothing (null).
|
|
117
|
+
*/
|
|
118
|
+
export declare function relativeResetBy(row: FxStepRow): M8RelativeFxState['resetBy'];
|
|
119
|
+
/**
|
|
120
|
+
* Follows every relative instrument command through the phrase up to `step` (inclusive).
|
|
121
|
+
*
|
|
122
|
+
* Model (manual: "Relative and Absolute FX Commands"): a relative command adds its
|
|
123
|
+
* value (01-7F) or subtracts it (80-FF) from the instrument's parameter, and the change
|
|
124
|
+
* stays until a note is triggered with a RET command or with an instrument number in
|
|
125
|
+
* the I column, which puts every parameter back to the instrument's assigned value.
|
|
126
|
+
* A row that triggers resets first, then applies its own commands. State from a
|
|
127
|
+
* previous phrase isn't visible, so totals only cover this phrase.
|
|
128
|
+
*/
|
|
129
|
+
export declare function analyzeRelativeFx(rows: FxStepRow[], step: number, carriedLane?: number | null): Record<string, M8RelativeFxState>;
|
|
130
|
+
/** Sentence about a relative command's accumulated effect, for the cell that applies it. */
|
|
131
|
+
export declare function describeRelativeFx(s: M8RelativeFxState | null | undefined): string;
|
|
132
|
+
/**
|
|
133
|
+
* For an instrument number typed on a step: which relative changes are still in force
|
|
134
|
+
* and will be wiped by the note it triggers. Null when nothing relative is active.
|
|
135
|
+
*/
|
|
136
|
+
export declare function describeRelativeReset(active: Record<string, M8RelativeFxState>): string | null;
|
package/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { CursorPos, CursorRect, M8ClientEvents, M8ClientMethods, M8HostEvents, M8HostMethods, M8KeyName, M8SdkConfig, M8State, RGB, SystemInfos, } from './types';
|
|
2
|
-
export { createM8Client, createM8ClientSync, type M8Client } from './client';
|
|
3
|
-
export { getSemanticContext, describeContext, formatFieldValue, lookupFxCommand, type M8FieldType, type M8ParsedField, type M8ParsedRow, type M8ActiveField, type M8SemanticContext, } from './viewContext';
|
|
1
|
+
export type { CursorPos, CursorRect, M8ClientEvents, M8ClientMethods, M8HostEvents, M8HostMethods, M8KeyName, M8Screen, M8SdkConfig, M8State, RGB, SystemInfos, } from './types';
|
|
2
|
+
export { createM8Client, createM8ClientSync, type M8Client, type M8Snapshot } from './client';
|
|
3
|
+
export { getSemanticContext, describeContext, describeContextParts, formatFieldValue, lookupFxCommand, getInstrumentFxCommands, RELATIVE_FX_RULE, type M8FieldType, type M8ParsedField, type M8ParsedRow, type M8ActiveField, type M8SemanticContext, type M8DescriptionPart, type M8DescriptionPartKind, type M8FileKind, type M8FxCommandInfo, type M8InstrumentFxList, type M8FxLaneTrace, type M8RelativeFxState, type M8ScaleInfo, type M8ScaleNote, } from './viewContext';
|
|
4
4
|
export { default } from './client';
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* parameterViews.ts
|
|
3
|
+
*
|
|
4
|
+
* Maps the non-grid ("parameter") views - instrument, modulation, mixer, project,
|
|
5
|
+
* effects, EQ, scope, system settings, file browsers - onto typed field cells,
|
|
6
|
+
* using m8-parameter-views.json.
|
|
7
|
+
*
|
|
8
|
+
* These views are label/value screens, so a row is recognised from its text (its
|
|
9
|
+
* label), not from a row number, and the cells of that row come from the schema. Views
|
|
10
|
+
* whose labels change with the instrument or modulation type are read through two
|
|
11
|
+
* label/value "zones" and the label is looked up in a vocabulary.
|
|
12
|
+
*
|
|
13
|
+
* Pure text in, cell definitions out; viewContext.ts turns them into parsed fields.
|
|
14
|
+
*/
|
|
15
|
+
import type { ColumnDef } from './viewContext';
|
|
16
|
+
export type ValueFormat = 'hex' | 'hexOption' | 'hexPair' | 'decimal' | 'decimalPair' | 'float' | 'text' | 'auto';
|
|
17
|
+
interface MatchDef {
|
|
18
|
+
x: number;
|
|
19
|
+
text?: string;
|
|
20
|
+
regex?: string;
|
|
21
|
+
width?: number;
|
|
22
|
+
}
|
|
23
|
+
interface RowDef {
|
|
24
|
+
id: string;
|
|
25
|
+
section?: string;
|
|
26
|
+
match: MatchDef[];
|
|
27
|
+
cells: ColumnDef[];
|
|
28
|
+
}
|
|
29
|
+
interface ZoneDef {
|
|
30
|
+
id: string;
|
|
31
|
+
labelX: number;
|
|
32
|
+
labelWidth: number;
|
|
33
|
+
x: number;
|
|
34
|
+
width: number;
|
|
35
|
+
}
|
|
36
|
+
type VocabEntry = Pick<ColumnDef, 'type' | 'format' | 'label' | 'description' | 'parts' | 'options' | 'unit'>;
|
|
37
|
+
interface LayoutDef {
|
|
38
|
+
extends?: string;
|
|
39
|
+
/** Pattern of the heading label that names the block a cell belongs to ('MOD\\d'): read upward in the cell's zone. */
|
|
40
|
+
slotLabel?: string;
|
|
41
|
+
kind?: 'browser' | 'text';
|
|
42
|
+
browserFor?: string;
|
|
43
|
+
fieldLabel?: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
sections?: string[];
|
|
46
|
+
rows?: RowDef[];
|
|
47
|
+
zones?: ZoneDef[];
|
|
48
|
+
vocabulary?: Record<string, VocabEntry>;
|
|
49
|
+
vocabularyFrom?: 'instrumentTypes';
|
|
50
|
+
}
|
|
51
|
+
export type ParameterLayout = Omit<LayoutDef, 'extends'>;
|
|
52
|
+
/** Layout of a parameter view, or null when the view has none. */
|
|
53
|
+
export declare function getParameterLayout(viewName: string): ParameterLayout | null;
|
|
54
|
+
/**
|
|
55
|
+
* Section heading ('MODFX', 'DELAY', 'REVERB') the cursor line belongs to: the
|
|
56
|
+
* nearest heading at column 0 on this line or above it. `linesUpward` starts with the
|
|
57
|
+
* cursor line, then the lines above it.
|
|
58
|
+
*/
|
|
59
|
+
export declare function detectSection(layout: ParameterLayout, linesUpward: string[]): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* Block heading ('MOD1'…'MOD4') a cell in `zoneId` belongs to: the nearest line at or
|
|
62
|
+
* above the cursor with a heading label in that zone.
|
|
63
|
+
*/
|
|
64
|
+
export declare function detectSlot(layout: ParameterLayout, zoneId: string, linesUpward: string[]): string | null;
|
|
65
|
+
/** 'WAVSYNTH' → 'wavsynth', 'MIDI OUT' → 'midiout', unknown text → null. */
|
|
66
|
+
export declare function instrumentKeyFromText(text: string): string | null;
|
|
67
|
+
/** Instrument type shown on the TYPE row of the Instrument View, read off the screen lines. */
|
|
68
|
+
export declare function instrumentTypeFromLines(lines: string[]): {
|
|
69
|
+
text: string;
|
|
70
|
+
key: string | null;
|
|
71
|
+
} | null;
|
|
72
|
+
/** Display name and description of an instrument type key ('wavsynth' → Wavsynth). */
|
|
73
|
+
export declare function getInstrumentType(key: string): {
|
|
74
|
+
name: string;
|
|
75
|
+
description: string;
|
|
76
|
+
} | null;
|
|
77
|
+
export interface ParameterLineOptions {
|
|
78
|
+
section: string | null;
|
|
79
|
+
instrumentKey: string | null;
|
|
80
|
+
/** Text under the cursor; the entry of a file-browser row. */
|
|
81
|
+
entryText?: string | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The cells present on `line`: the schema row whose label text matches, or else the
|
|
85
|
+
* label/value zones. Empty when nothing on the line is recognised.
|
|
86
|
+
*/
|
|
87
|
+
export declare function readParameterCells(layout: ParameterLayout, line: string, opts: ParameterLineOptions): {
|
|
88
|
+
rowId: string | null;
|
|
89
|
+
cells: ColumnDef[];
|
|
90
|
+
};
|
|
91
|
+
export interface ParsedValue {
|
|
92
|
+
hexValue: number | null;
|
|
93
|
+
/** Natural number of the value: the hex value, decimal integer or float. */
|
|
94
|
+
numericValue: number | null;
|
|
95
|
+
/** Both numbers of an 'XX:YY' pair. */
|
|
96
|
+
values: number[] | null;
|
|
97
|
+
/** The non-numeric part: option name, caption, name text. */
|
|
98
|
+
text: string | null;
|
|
99
|
+
}
|
|
100
|
+
/** Reads a cell's text according to its format. Empty text gives all-null. */
|
|
101
|
+
export declare function parseValue(raw: string, format: ValueFormat): ParsedValue;
|
|
102
|
+
/** Description of the option currently selected ('CLIP' → "Values above the maximum level are clipped."). */
|
|
103
|
+
export declare function describeOption(options: Record<string, string> | undefined, text: string | null): string | null;
|
|
104
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scaleInsight.ts
|
|
3
|
+
*
|
|
4
|
+
* Interprets the Scale View: which of the 12 note intervals are enabled, what
|
|
5
|
+
* well-known scale that shape spells, and what a given row means musically.
|
|
6
|
+
*/
|
|
7
|
+
/** Interval row labels in screen order. */
|
|
8
|
+
export declare const SCALE_INTERVALS: readonly ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
9
|
+
export interface M8ScaleNote {
|
|
10
|
+
/** Row label, e.g. 'D#'. */
|
|
11
|
+
interval: string;
|
|
12
|
+
/** Semitones above the first row ('C'), 0-11. */
|
|
13
|
+
semitone: number;
|
|
14
|
+
/** true = ON, false = OFF, null = unset ('--'). */
|
|
15
|
+
enabled: boolean | null;
|
|
16
|
+
/** Detune in semitones (-24..+24), null when unreadable. */
|
|
17
|
+
offset: number | null;
|
|
18
|
+
}
|
|
19
|
+
export interface M8ScaleInfo {
|
|
20
|
+
/** The 12 rows, in screen order. */
|
|
21
|
+
notes: M8ScaleNote[];
|
|
22
|
+
/** Labels of the enabled intervals, e.g. ['C', 'D', 'E', …]. */
|
|
23
|
+
enabled: string[];
|
|
24
|
+
/** Well-known scales this set of notes spells, most natural reading first. */
|
|
25
|
+
shapes: string[];
|
|
26
|
+
/** Intervals carrying a non-zero detune. */
|
|
27
|
+
detuned: {
|
|
28
|
+
interval: string;
|
|
29
|
+
offset: number;
|
|
30
|
+
}[];
|
|
31
|
+
/** One-line summary, e.g. "C D E F G A B — Major (Ionian)". */
|
|
32
|
+
summary: string;
|
|
33
|
+
}
|
|
34
|
+
/** 'C-' → 'C', 'C#' → 'C#'. Returns null when the text is not an interval label. */
|
|
35
|
+
export declare function normalizeInterval(raw: string): string | null;
|
|
36
|
+
/** "a major 3rd (+4 semitones above C)" for a given interval label. */
|
|
37
|
+
export declare function describeInterval(interval: string): string;
|
|
38
|
+
/** Names of well-known scales whose notes equal the enabled set, C-rooted reading first. */
|
|
39
|
+
export declare function matchScaleShapes(enabledSemitones: number[]): string[];
|
|
40
|
+
export declare function analyzeScale(notes: M8ScaleNote[]): M8ScaleInfo;
|
|
41
|
+
/**
|
|
42
|
+
* The two halves of the summary: the enabled notes ("C D E F G A B"), then the scale
|
|
43
|
+
* they spell with any detune ("Major (Ionian); detuned: E −0.50 st"), when notes are enabled.
|
|
44
|
+
*/
|
|
45
|
+
export declare function scaleSummaryParts(info: Pick<M8ScaleInfo, 'enabled' | 'shapes' | 'detuned'>): string[];
|
|
46
|
+
/** '+0.50 st' style detune text. */
|
|
47
|
+
export declare function formatOffset(offset: number): string;
|
package/types/types.d.ts
CHANGED
|
@@ -35,6 +35,12 @@ export interface M8State {
|
|
|
35
35
|
macroCurrentStep?: number;
|
|
36
36
|
macroSequenceLength?: number;
|
|
37
37
|
}
|
|
38
|
+
export interface M8Screen {
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
/** One string per screen row; columns preserved, trailing spaces trimmed. */
|
|
42
|
+
lines: string[];
|
|
43
|
+
}
|
|
38
44
|
export type M8KeyName = 'left' | 'right' | 'up' | 'down' | 'shift' | 'play' | 'opt' | 'edit';
|
|
39
45
|
export interface M8HostMethods {
|
|
40
46
|
navigateToView(viewName: string): Promise<boolean>;
|
|
@@ -49,6 +55,7 @@ export interface M8HostMethods {
|
|
|
49
55
|
sendKeyDown(keys: M8KeyName[]): Promise<void>;
|
|
50
56
|
sendKeyUp(): Promise<void>;
|
|
51
57
|
getState(): Promise<M8State>;
|
|
58
|
+
getScreen(): Promise<M8Screen>;
|
|
52
59
|
}
|
|
53
60
|
export interface M8ClientMethods {
|
|
54
61
|
ping(): Promise<string>;
|