@yam8d/m8-sdk 0.3.3 → 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.
- package/README.md +898 -655
- package/dist/index.js +4470 -249
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/types/client.d.ts +94 -5
- package/types/engine/astar.d.ts +15 -0
- package/types/engine/audio.d.ts +29 -0
- package/types/engine/bus.d.ts +13 -0
- package/types/engine/connectWithRetry.d.ts +9 -0
- package/types/engine/connection.d.ts +56 -0
- package/types/engine/cursorNavigator.d.ts +15 -0
- package/types/engine/engine.d.ts +30 -0
- package/types/engine/eventBus.d.ts +5 -0
- package/types/engine/host/fileBrowserHelpers.d.ts +59 -0
- package/types/engine/host/fileBrowserSearch.d.ts +13 -0
- package/types/engine/host/floatEditor.d.ts +2 -0
- package/types/engine/host/hostIO.d.ts +30 -0
- package/types/engine/host/hostMethods.d.ts +22 -0
- package/types/engine/host/hostState.d.ts +7 -0
- package/types/engine/host/keySequences.d.ts +2 -0
- package/types/engine/host/midiTransport.d.ts +10 -0
- package/types/engine/host/valueEditors.d.ts +7 -0
- package/types/engine/host/valueParsers.d.ts +24 -0
- package/types/engine/index.d.ts +14 -0
- package/types/engine/keys.d.ts +28 -0
- package/types/engine/macroRunner.d.ts +15 -0
- package/types/engine/protocol.d.ts +138 -0
- package/types/engine/queuedReaders.d.ts +18 -0
- package/types/engine/store.d.ts +65 -0
- package/types/engine/viewExtractor.d.ts +27 -0
- package/types/engine/viewGraph.d.ts +9 -0
- package/types/engine/viewNavigation.d.ts +16 -0
- package/types/fxCommands.d.ts +47 -0
- package/types/fxInsight.d.ts +136 -0
- package/types/index.d.ts +9 -3
- package/types/parameterViews.d.ts +104 -0
- package/types/scaleInsight.d.ts +47 -0
- package/types/theme.d.ts +42 -0
- package/types/types.d.ts +131 -0
- package/types/viewContext.d.ts +216 -15
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export type RectCommand = ReturnType<typeof parseRectCommand>;
|
|
2
|
+
declare const parseRectCommand: (data: DataView, length: number) => {
|
|
3
|
+
readonly type: 1;
|
|
4
|
+
readonly pos: {
|
|
5
|
+
readonly x: number;
|
|
6
|
+
readonly y: number;
|
|
7
|
+
};
|
|
8
|
+
readonly size: {
|
|
9
|
+
readonly width: 1;
|
|
10
|
+
readonly height: 1;
|
|
11
|
+
};
|
|
12
|
+
readonly color?: undefined;
|
|
13
|
+
} | {
|
|
14
|
+
readonly type: 2;
|
|
15
|
+
readonly pos: {
|
|
16
|
+
readonly x: number;
|
|
17
|
+
readonly y: number;
|
|
18
|
+
};
|
|
19
|
+
readonly size: {
|
|
20
|
+
readonly width: 1;
|
|
21
|
+
readonly height: 1;
|
|
22
|
+
};
|
|
23
|
+
readonly color: {
|
|
24
|
+
readonly r: number;
|
|
25
|
+
readonly g: number;
|
|
26
|
+
readonly b: number;
|
|
27
|
+
};
|
|
28
|
+
} | {
|
|
29
|
+
readonly type: 3;
|
|
30
|
+
readonly pos: {
|
|
31
|
+
readonly x: number;
|
|
32
|
+
readonly y: number;
|
|
33
|
+
};
|
|
34
|
+
readonly size: {
|
|
35
|
+
readonly width: number;
|
|
36
|
+
readonly height: number;
|
|
37
|
+
};
|
|
38
|
+
readonly color?: undefined;
|
|
39
|
+
} | {
|
|
40
|
+
readonly type: 4;
|
|
41
|
+
readonly pos: {
|
|
42
|
+
readonly x: number;
|
|
43
|
+
readonly y: number;
|
|
44
|
+
};
|
|
45
|
+
readonly size: {
|
|
46
|
+
readonly width: number;
|
|
47
|
+
readonly height: number;
|
|
48
|
+
};
|
|
49
|
+
readonly color: {
|
|
50
|
+
readonly r: number;
|
|
51
|
+
readonly g: number;
|
|
52
|
+
readonly b: number;
|
|
53
|
+
};
|
|
54
|
+
} | {
|
|
55
|
+
readonly type: 0;
|
|
56
|
+
readonly pos: {
|
|
57
|
+
readonly x: number;
|
|
58
|
+
readonly y: number;
|
|
59
|
+
};
|
|
60
|
+
readonly size: {
|
|
61
|
+
readonly width: 0;
|
|
62
|
+
readonly height: 0;
|
|
63
|
+
};
|
|
64
|
+
readonly color?: undefined;
|
|
65
|
+
};
|
|
66
|
+
export type CharacterCommand = ReturnType<typeof parseCharacterCommand>;
|
|
67
|
+
declare const parseCharacterCommand: (data: DataView, length: number) => {
|
|
68
|
+
character: string;
|
|
69
|
+
pos: {
|
|
70
|
+
x: number;
|
|
71
|
+
y: number;
|
|
72
|
+
};
|
|
73
|
+
foreground: {
|
|
74
|
+
r: number;
|
|
75
|
+
g: number;
|
|
76
|
+
b: number;
|
|
77
|
+
};
|
|
78
|
+
background: {
|
|
79
|
+
r: number;
|
|
80
|
+
g: number;
|
|
81
|
+
b: number;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
export type WaveCommand = ReturnType<typeof parseWaveCommand>;
|
|
85
|
+
declare const parseWaveCommand: (data: DataView, length: number) => {
|
|
86
|
+
color: {
|
|
87
|
+
r: number;
|
|
88
|
+
g: number;
|
|
89
|
+
b: number;
|
|
90
|
+
};
|
|
91
|
+
wave: number[];
|
|
92
|
+
};
|
|
93
|
+
export type KeyCommand = ReturnType<typeof parseKeyCommand>;
|
|
94
|
+
declare const parseKeyCommand: (data: DataView, length: number) => {
|
|
95
|
+
keys: number;
|
|
96
|
+
};
|
|
97
|
+
export type SystemCommand = ReturnType<typeof parseSystemCommand>;
|
|
98
|
+
declare const parseSystemCommand: (data: DataView, length: number) => {
|
|
99
|
+
model: "Headless" | "Beta M8" | "M8 Model:01" | "M8 Model:02";
|
|
100
|
+
version: string;
|
|
101
|
+
fontMode: 0 | 1 | 2;
|
|
102
|
+
};
|
|
103
|
+
/** System command plus the layout metrics derived from the model and font mode. */
|
|
104
|
+
export type SystemInfos = {
|
|
105
|
+
model: SystemCommand['model'];
|
|
106
|
+
fontMode: SystemCommand['fontMode'];
|
|
107
|
+
spacingX: number;
|
|
108
|
+
spacingY: number;
|
|
109
|
+
offX: number;
|
|
110
|
+
offY: number;
|
|
111
|
+
screenWidth: number;
|
|
112
|
+
screenHeight: number;
|
|
113
|
+
rectOffset: number;
|
|
114
|
+
};
|
|
115
|
+
export type ProtocolEvents = {
|
|
116
|
+
rect: (data: RectCommand) => void;
|
|
117
|
+
text: (data: CharacterCommand) => void;
|
|
118
|
+
wave: (data: WaveCommand) => void;
|
|
119
|
+
key: (data: KeyCommand) => void;
|
|
120
|
+
system: (data: SystemCommand) => void;
|
|
121
|
+
systemInfo: (data: SystemInfos) => void;
|
|
122
|
+
any: (data: Uint8Array<ArrayBufferLike>) => void;
|
|
123
|
+
};
|
|
124
|
+
export declare const protocol: () => {
|
|
125
|
+
eventBus: {
|
|
126
|
+
emit: <K extends keyof ProtocolEvents>(event: K, ...args: Parameters<ProtocolEvents[K]>) => void;
|
|
127
|
+
on: <K extends keyof ProtocolEvents>(event: K, callback: ProtocolEvents[K]) => void;
|
|
128
|
+
off: <K extends keyof ProtocolEvents>(event: K, callback: ProtocolEvents[K]) => void;
|
|
129
|
+
};
|
|
130
|
+
dispatch: (data: DataView, dataLength: number) => void;
|
|
131
|
+
getSystemInfo: () => {
|
|
132
|
+
model: "Headless" | "Beta M8" | "M8 Model:01" | "M8 Model:02";
|
|
133
|
+
version: string;
|
|
134
|
+
fontMode: 0 | 1 | 2;
|
|
135
|
+
} | undefined;
|
|
136
|
+
getSystemInfos: () => SystemInfos;
|
|
137
|
+
};
|
|
138
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type SlipState = 'read' | 'escape' | 'error';
|
|
2
|
+
export declare const createSlipParser: () => {
|
|
3
|
+
feed: (byte: number) => DataView | undefined;
|
|
4
|
+
};
|
|
5
|
+
export declare const queuedUsbReader: (device: USBDevice) => () => Promise<{
|
|
6
|
+
terminated: true;
|
|
7
|
+
data?: undefined;
|
|
8
|
+
} | {
|
|
9
|
+
terminated: false;
|
|
10
|
+
data: DataView<ArrayBufferLike>;
|
|
11
|
+
}>;
|
|
12
|
+
export declare const queuedSerialReader: (reader: ReadableStreamDefaultReader<Uint8Array<ArrayBufferLike>>) => () => Promise<{
|
|
13
|
+
terminated: true;
|
|
14
|
+
data?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
terminated: false;
|
|
17
|
+
data: DataView<ArrayBufferLike>;
|
|
18
|
+
}>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { CursorPos, CursorRect, RGB } from '../types';
|
|
2
|
+
/** Model, font mode and the layout metrics derived from them (from the M8's system command). */
|
|
3
|
+
export interface M8SystemInfo {
|
|
4
|
+
model: string;
|
|
5
|
+
fontMode: number;
|
|
6
|
+
spacingX: number;
|
|
7
|
+
spacingY: number;
|
|
8
|
+
offX: number;
|
|
9
|
+
offY: number;
|
|
10
|
+
screenWidth: number;
|
|
11
|
+
screenHeight: number;
|
|
12
|
+
rectOffset: number;
|
|
13
|
+
}
|
|
14
|
+
export interface M8CellMetrics {
|
|
15
|
+
cellW: number;
|
|
16
|
+
cellH: number;
|
|
17
|
+
offX: number;
|
|
18
|
+
offY: number;
|
|
19
|
+
}
|
|
20
|
+
export interface M8MacroStatus {
|
|
21
|
+
running: boolean;
|
|
22
|
+
currentStep?: number;
|
|
23
|
+
sequenceLength?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface M8StoreState {
|
|
26
|
+
/** Current view name (lowercased, cleaned). */
|
|
27
|
+
viewName: string | null;
|
|
28
|
+
/** Raw title row text. */
|
|
29
|
+
viewTitle: string | null;
|
|
30
|
+
minimapKey: string | null;
|
|
31
|
+
/** Cursor position in character grid coordinates (0..39, 0..23). */
|
|
32
|
+
cursorPos: CursorPos | null;
|
|
33
|
+
/** Cursor rectangle in M8 pixel space. */
|
|
34
|
+
cursorRect: CursorRect | null;
|
|
35
|
+
/** Whether the cursor is drawn as a 1px selection rectangle. */
|
|
36
|
+
selectionMode: boolean;
|
|
37
|
+
/** Foreground colour of the text under the cursor. */
|
|
38
|
+
highlightColor: RGB | null;
|
|
39
|
+
/** Colour of the cursor rectangle corners as last drawn. */
|
|
40
|
+
cursorColor: RGB | null;
|
|
41
|
+
textUnderCursor: string | null;
|
|
42
|
+
/** The whole screen row the cursor is on. */
|
|
43
|
+
currentLine: string | null;
|
|
44
|
+
titleColor: RGB | null;
|
|
45
|
+
backgroundColor: RGB | null;
|
|
46
|
+
deviceModel: string | null;
|
|
47
|
+
fontMode: number | null;
|
|
48
|
+
/** Pixel size and offset of one character cell (model and font dependent). */
|
|
49
|
+
cellMetrics: M8CellMetrics;
|
|
50
|
+
systemInfo: M8SystemInfo | null;
|
|
51
|
+
macroStatus: M8MacroStatus;
|
|
52
|
+
}
|
|
53
|
+
export type M8StoreKey = keyof M8StoreState;
|
|
54
|
+
export interface M8Store {
|
|
55
|
+
get<K extends M8StoreKey>(key: K): M8StoreState[K];
|
|
56
|
+
/** Sets a value; listeners run synchronously, and only when the value changed (by identity). */
|
|
57
|
+
set<K extends M8StoreKey>(key: K, value: M8StoreState[K]): void;
|
|
58
|
+
/** Listens to one key. Returns the unsubscribe function. */
|
|
59
|
+
sub(key: M8StoreKey, listener: () => void): () => void;
|
|
60
|
+
/** Listens to every key. Returns the unsubscribe function. */
|
|
61
|
+
subAll(listener: (key: M8StoreKey) => void): () => void;
|
|
62
|
+
snapshot(): M8StoreState;
|
|
63
|
+
}
|
|
64
|
+
export declare const initialStoreState: () => M8StoreState;
|
|
65
|
+
export declare const createM8Store: (initial?: Partial<M8StoreState>) => M8Store;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CharacterCommand, RectCommand, SystemInfos } from './protocol';
|
|
2
|
+
import type { M8Store } from './store';
|
|
3
|
+
export declare const SCREEN_ROWS = 24;
|
|
4
|
+
export declare const SCREEN_COLUMNS = 40;
|
|
5
|
+
/** Colour of every character cell, row by row ('#rrggbb', null where nothing was drawn). */
|
|
6
|
+
export type ScreenCellColors = {
|
|
7
|
+
fg: (string | null)[][];
|
|
8
|
+
bg: (string | null)[][];
|
|
9
|
+
};
|
|
10
|
+
/** The protocol events the extractor listens to (a ConnectedBus's protocol.eventBus fits). */
|
|
11
|
+
export interface DrawEventSource {
|
|
12
|
+
on(event: 'text', callback: (data: CharacterCommand) => void): void;
|
|
13
|
+
on(event: 'rect', callback: (data: RectCommand) => void): void;
|
|
14
|
+
on(event: 'systemInfo', callback: (data: SystemInfos) => void): void;
|
|
15
|
+
off(event: 'text', callback: (data: CharacterCommand) => void): void;
|
|
16
|
+
off(event: 'rect', callback: (data: RectCommand) => void): void;
|
|
17
|
+
off(event: 'systemInfo', callback: (data: SystemInfos) => void): void;
|
|
18
|
+
}
|
|
19
|
+
export interface ViewExtractor {
|
|
20
|
+
/** Whole M8 screen as text rows, one string per row (columns preserved, trailing spaces trimmed). */
|
|
21
|
+
getScreenLines(): string[];
|
|
22
|
+
/** Colours of the whole M8 screen, same grid as getScreenLines. */
|
|
23
|
+
getScreenCellColors(): ScreenCellColors;
|
|
24
|
+
dispose(): void;
|
|
25
|
+
}
|
|
26
|
+
/** Listens to the draw stream and keeps `store` up to date. */
|
|
27
|
+
export declare function registerViewExtractor(events: DrawEventSource, store: M8Store): ViewExtractor;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Graph } from './astar';
|
|
2
|
+
export interface M8ViewGraph {
|
|
3
|
+
graph: Graph;
|
|
4
|
+
/** Explicit key frame sequences per edge: from -> to -> frames. */
|
|
5
|
+
edgeKeys: Map<string, Map<string, number[]>>;
|
|
6
|
+
}
|
|
7
|
+
export declare const getViewGraph: () => M8ViewGraph;
|
|
8
|
+
/** Names of the M8 views, lowercased (as viewName reports them). */
|
|
9
|
+
export declare const getViewNames: () => Set<string>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Direction } from './astar';
|
|
2
|
+
import type { MacroRunner } from './macroRunner';
|
|
3
|
+
import type { M8Store } from './store';
|
|
4
|
+
/** Shift+direction frames, used for edges without an explicit key sequence. */
|
|
5
|
+
export declare const toKeyMasks: (directions: Direction[], opts?: {
|
|
6
|
+
shift?: boolean;
|
|
7
|
+
}) => number[];
|
|
8
|
+
export interface ViewNavigation {
|
|
9
|
+
/** Resolves when the view is reached, no path is found, or the step limit is hit. */
|
|
10
|
+
navigateToView(viewName: string): Promise<void>;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
13
|
+
export declare const createViewNavigation: ({ store, runner }: {
|
|
14
|
+
store: M8Store;
|
|
15
|
+
runner: MacroRunner;
|
|
16
|
+
}) => ViewNavigation;
|
|
@@ -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,10 @@
|
|
|
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 {
|
|
1
|
+
export type { CursorPos, CursorRect, M8ClientEvents, M8ClientMethods, M8ClientMode, M8DeviceBus, M8DeviceConnectOptions, M8DeviceEvents, M8DeviceSupport, M8DeviceTransport, M8HostEvents, M8HostMethods, M8KeyName, M8Screen, M8ScreenCells, M8SdkConfig, M8State, M8TransportEvent, RGB, SystemInfos, } from './types';
|
|
2
|
+
export { createM8Client, createM8ClientSync, getDeviceSupport, type M8Client, type M8Snapshot, YAM8D_FRAME_NAME } from './client';
|
|
3
|
+
export { connection as createM8Connection, type ConnectedBus as M8ConnectedBus, type M8ConnectOptions } from './engine/connection';
|
|
4
|
+
export { createM8Audio, getM8InputStream, type M8Audio, M8_AUDIO_CAPTURE_CONSTRAINTS } from './engine/audio';
|
|
5
|
+
export { createM8Engine, type M8Engine, type M8EngineOptions } from './engine/engine';
|
|
6
|
+
export { M8KeyMask, pressKeys } from './engine/keys';
|
|
7
|
+
export type { M8MacroStatus, M8Store, M8StoreKey, M8StoreState } from './engine/store';
|
|
8
|
+
export { getSemanticContext, parseGridScreen, readPlayPositions, parseViewTitle, SCREEN_MARGIN, describeContext, describeContextParts, formatFieldValue, lookupFxCommand, getInstrumentFxCommands, RELATIVE_FX_RULE, type M8FieldType, type M8ParsedField, type M8ParsedRow, type M8ScreenGridRow, type M8PlayPositions, type M8ActiveField, type M8SemanticContext, type M8DescriptionPart, type M8DescriptionPartKind, type M8FileKind, type M8FxCommandInfo, type M8InstrumentFxList, type M8FxLaneTrace, type M8RelativeFxState, type M8ScaleInfo, type M8ScaleNote, } from './viewContext';
|
|
9
|
+
export { parseThemeScreen, observeThemeColors, M8_THEME_ROLES, M8_THEME_VIEW, type M8Theme, type M8ThemeRole, } from './theme';
|
|
4
10
|
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 = out of the scale ('--' on the device, 'OFF' if shown), null = not read. */
|
|
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/theme.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* theme.ts
|
|
3
|
+
*
|
|
4
|
+
* The device theme: the 13 colours of the Theme View (System Settings > EDIT THEME),
|
|
5
|
+
* read off its screen, and the few colours any screen shows on its own.
|
|
6
|
+
*/
|
|
7
|
+
import type { M8Screen, M8State } from './types';
|
|
8
|
+
export type M8ThemeRole = 'background' | 'textEmpty' | 'textInfo' | 'textDefault' | 'textValue' | 'textTitles' | 'playMarkers' | 'cursor' | 'selection' | 'scopeSlider' | 'meterLow' | 'meterMid' | 'meterPeak';
|
|
9
|
+
/** The Theme View rows, top to bottom, with the label the device shows. */
|
|
10
|
+
export declare const M8_THEME_ROLES: readonly {
|
|
11
|
+
role: M8ThemeRole;
|
|
12
|
+
label: string;
|
|
13
|
+
}[];
|
|
14
|
+
/** View name the SDK reports on the Theme View (title "THEME SETTINGS"). */
|
|
15
|
+
export declare const M8_THEME_VIEW = "themesettings";
|
|
16
|
+
export interface M8Theme {
|
|
17
|
+
/** Every theme colour as '#rrggbb'. */
|
|
18
|
+
colors: Record<M8ThemeRole, string>;
|
|
19
|
+
/** Colour model the Theme View was showing. HSV values are converted to RGB (H, S and V read as 00-FF). */
|
|
20
|
+
mode: 'rgb' | 'hsv';
|
|
21
|
+
/** THEME NAME, or null when unnamed. */
|
|
22
|
+
name: string | null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Reads the theme off the Theme View screen (from getScreen()). Returns null unless
|
|
26
|
+
* all 13 colour rows are found, so a half-drawn screen is never taken for a theme.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* if (m8.state.viewName === M8_THEME_VIEW) {
|
|
31
|
+
* const theme = parseThemeScreen(await m8.getScreen())
|
|
32
|
+
* theme?.colors.textValue // '#ffffff'
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseThemeScreen(screen: M8Screen): M8Theme | null;
|
|
37
|
+
/**
|
|
38
|
+
* The theme colours any screen gives away without opening the Theme View: the
|
|
39
|
+
* background and the title colour. (The cursor rectangle is animated on the device, so
|
|
40
|
+
* `state.cursorColor` is not the theme's CURSOR colour.)
|
|
41
|
+
*/
|
|
42
|
+
export declare function observeThemeColors(state: M8State): Partial<Record<M8ThemeRole, string>>;
|