@collabhut/plugin-sdk 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.md +1046 -0
- package/dist/helpers/note-utils.d.ts +128 -0
- package/dist/helpers/note-utils.d.ts.map +1 -0
- package/dist/helpers/note-utils.js +155 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/types/audio-effect.d.ts +70 -0
- package/dist/types/audio-effect.d.ts.map +1 -0
- package/dist/types/audio-effect.js +1 -0
- package/dist/types/context.d.ts +39 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +1 -0
- package/dist/types/events.d.ts +119 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +19 -0
- package/dist/types/instrument.d.ts +83 -0
- package/dist/types/instrument.d.ts.map +1 -0
- package/dist/types/instrument.js +1 -0
- package/dist/types/licensing.d.ts +118 -0
- package/dist/types/licensing.d.ts.map +1 -0
- package/dist/types/licensing.js +27 -0
- package/dist/types/manifest.d.ts +90 -0
- package/dist/types/manifest.d.ts.map +1 -0
- package/dist/types/manifest.js +1 -0
- package/dist/types/midi-effect.d.ts +101 -0
- package/dist/types/midi-effect.d.ts.map +1 -0
- package/dist/types/midi-effect.js +1 -0
- package/dist/types/parameters.d.ts +76 -0
- package/dist/types/parameters.d.ts.map +1 -0
- package/dist/types/parameters.js +1 -0
- package/dist/types/shader.d.ts +110 -0
- package/dist/types/shader.d.ts.map +1 -0
- package/dist/types/shader.js +1 -0
- package/dist/types/vocal-preset.d.ts +149 -0
- package/dist/types/vocal-preset.d.ts.map +1 -0
- package/dist/types/vocal-preset.js +54 -0
- package/package.json +50 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Note utility helpers for @collabhut/plugin-sdk.
|
|
3
|
+
*
|
|
4
|
+
* All functions are pure and zero-dependency. Safe to call from a Worker.
|
|
5
|
+
*
|
|
6
|
+
* ## MIDI note numbers
|
|
7
|
+
* Middle C (C4) = MIDI 60. A4 (concert pitch) = MIDI 69 = 440 Hz.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Convert a MIDI note number to its frequency in Hz.
|
|
11
|
+
*
|
|
12
|
+
* Uses equal temperament at A4 = 440 Hz.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* noteToHz(69) // 440
|
|
16
|
+
* noteToHz(60) // 261.63 (middle C)
|
|
17
|
+
* noteToHz(57) // 220 (A3)
|
|
18
|
+
*/
|
|
19
|
+
export declare function noteToHz(midi: number): number;
|
|
20
|
+
/**
|
|
21
|
+
* Convert a frequency in Hz to the nearest MIDI note number.
|
|
22
|
+
*
|
|
23
|
+
* The result is rounded to the nearest integer.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* hzToNote(440) // 69
|
|
27
|
+
* hzToNote(261.63)// 60
|
|
28
|
+
*/
|
|
29
|
+
export declare function hzToNote(hz: number): number;
|
|
30
|
+
/**
|
|
31
|
+
* Convert a frequency in Hz to a precise (fractional) MIDI note number.
|
|
32
|
+
*
|
|
33
|
+
* Useful for pitch detection where you want the exact pitch, not just the
|
|
34
|
+
* nearest semitone.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* hzToNoteFractional(450) // ~69.39
|
|
38
|
+
*/
|
|
39
|
+
export declare function hzToNoteFractional(hz: number): number;
|
|
40
|
+
/** Note names in order (chromatic, starting from C) */
|
|
41
|
+
declare const NOTE_NAMES: readonly ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
42
|
+
export type NoteName = (typeof NOTE_NAMES)[number];
|
|
43
|
+
/**
|
|
44
|
+
* Convert a MIDI note number to a human-readable note name with octave.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* midiToName(60) // "C4"
|
|
48
|
+
* midiToName(69) // "A4"
|
|
49
|
+
* midiToName(57) // "A3"
|
|
50
|
+
*/
|
|
51
|
+
export declare function midiToName(midi: number): string;
|
|
52
|
+
/**
|
|
53
|
+
* Convert a note name + octave to a MIDI note number.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* nameToMidi("C", 4) // 60
|
|
57
|
+
* nameToMidi("A", 4) // 69
|
|
58
|
+
* nameToMidi("A#", 3) // 58
|
|
59
|
+
*/
|
|
60
|
+
export declare function nameToMidi(note: NoteName, octave: number): number;
|
|
61
|
+
/**
|
|
62
|
+
* Return the number of semitones between two MIDI note numbers.
|
|
63
|
+
* Result is signed — positive if `b > a`.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* semitonesBetween(60, 67) // 7 (perfect fifth up)
|
|
67
|
+
* semitonesBetween(69, 60) // -9
|
|
68
|
+
*/
|
|
69
|
+
export declare function semitonesBetween(a: number, b: number): number;
|
|
70
|
+
/**
|
|
71
|
+
* Transpose a MIDI note by a number of semitones, clamped to 0–127.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* transpose(60, 7) // 67 (C4 → G4)
|
|
75
|
+
* transpose(60, -12) // 48 (C4 → C3)
|
|
76
|
+
*/
|
|
77
|
+
export declare function transpose(midi: number, semitones: number): number;
|
|
78
|
+
/**
|
|
79
|
+
* Convert beats to seconds given a BPM.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* beatsToSeconds(1, 120) // 0.5
|
|
83
|
+
* beatsToSeconds(4, 90) // 2.666...
|
|
84
|
+
*/
|
|
85
|
+
export declare function beatsToSeconds(beats: number, bpm: number): number;
|
|
86
|
+
/**
|
|
87
|
+
* Convert seconds to beats given a BPM.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* secondsToBeats(0.5, 120) // 1
|
|
91
|
+
* secondsToBeats(2, 90) // 3
|
|
92
|
+
*/
|
|
93
|
+
export declare function secondsToBeats(seconds: number, bpm: number): number;
|
|
94
|
+
/**
|
|
95
|
+
* Convert a linear gain value (0–∞) to decibels.
|
|
96
|
+
* Returns `-Infinity` for gain = 0.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* gainToDb(1) // 0
|
|
100
|
+
* gainToDb(0.5) // -6.02
|
|
101
|
+
*/
|
|
102
|
+
export declare function gainToDb(gain: number): number;
|
|
103
|
+
/**
|
|
104
|
+
* Convert decibels to a linear gain value.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* dbToGain(0) // 1
|
|
108
|
+
* dbToGain(-6) // ~0.5012
|
|
109
|
+
*/
|
|
110
|
+
export declare function dbToGain(db: number): number;
|
|
111
|
+
/**
|
|
112
|
+
* Clamp a value between a minimum and maximum.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* clamp(1.5, 0, 1) // 1
|
|
116
|
+
* clamp(-0.5, 0, 1) // 0
|
|
117
|
+
*/
|
|
118
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
119
|
+
/**
|
|
120
|
+
* Linear interpolation between two values.
|
|
121
|
+
*
|
|
122
|
+
* @param t Blend factor, 0–1
|
|
123
|
+
* @example
|
|
124
|
+
* lerp(0, 100, 0.5) // 50
|
|
125
|
+
*/
|
|
126
|
+
export declare function lerp(a: number, b: number, t: number): number;
|
|
127
|
+
export {};
|
|
128
|
+
//# sourceMappingURL=note-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"note-utils.d.ts","sourceRoot":"","sources":["../../src/helpers/note-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,uDAAuD;AACvD,QAAA,MAAM,UAAU,4EAA6E,CAAA;AAE7F,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;AAElD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5D"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Note utility helpers for @collabhut/plugin-sdk.
|
|
3
|
+
*
|
|
4
|
+
* All functions are pure and zero-dependency. Safe to call from a Worker.
|
|
5
|
+
*
|
|
6
|
+
* ## MIDI note numbers
|
|
7
|
+
* Middle C (C4) = MIDI 60. A4 (concert pitch) = MIDI 69 = 440 Hz.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Convert a MIDI note number to its frequency in Hz.
|
|
11
|
+
*
|
|
12
|
+
* Uses equal temperament at A4 = 440 Hz.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* noteToHz(69) // 440
|
|
16
|
+
* noteToHz(60) // 261.63 (middle C)
|
|
17
|
+
* noteToHz(57) // 220 (A3)
|
|
18
|
+
*/
|
|
19
|
+
export function noteToHz(midi) {
|
|
20
|
+
return 440 * 2 ** ((midi - 69) / 12);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Convert a frequency in Hz to the nearest MIDI note number.
|
|
24
|
+
*
|
|
25
|
+
* The result is rounded to the nearest integer.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* hzToNote(440) // 69
|
|
29
|
+
* hzToNote(261.63)// 60
|
|
30
|
+
*/
|
|
31
|
+
export function hzToNote(hz) {
|
|
32
|
+
return Math.round(69 + 12 * Math.log2(hz / 440));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Convert a frequency in Hz to a precise (fractional) MIDI note number.
|
|
36
|
+
*
|
|
37
|
+
* Useful for pitch detection where you want the exact pitch, not just the
|
|
38
|
+
* nearest semitone.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* hzToNoteFractional(450) // ~69.39
|
|
42
|
+
*/
|
|
43
|
+
export function hzToNoteFractional(hz) {
|
|
44
|
+
return 69 + 12 * Math.log2(hz / 440);
|
|
45
|
+
}
|
|
46
|
+
/** Note names in order (chromatic, starting from C) */
|
|
47
|
+
const NOTE_NAMES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
48
|
+
/**
|
|
49
|
+
* Convert a MIDI note number to a human-readable note name with octave.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* midiToName(60) // "C4"
|
|
53
|
+
* midiToName(69) // "A4"
|
|
54
|
+
* midiToName(57) // "A3"
|
|
55
|
+
*/
|
|
56
|
+
export function midiToName(midi) {
|
|
57
|
+
const octave = Math.floor(midi / 12) - 1;
|
|
58
|
+
const name = NOTE_NAMES[midi % 12];
|
|
59
|
+
return `${name}${octave}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Convert a note name + octave to a MIDI note number.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* nameToMidi("C", 4) // 60
|
|
66
|
+
* nameToMidi("A", 4) // 69
|
|
67
|
+
* nameToMidi("A#", 3) // 58
|
|
68
|
+
*/
|
|
69
|
+
export function nameToMidi(note, octave) {
|
|
70
|
+
return NOTE_NAMES.indexOf(note) + (octave + 1) * 12;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Return the number of semitones between two MIDI note numbers.
|
|
74
|
+
* Result is signed — positive if `b > a`.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* semitonesBetween(60, 67) // 7 (perfect fifth up)
|
|
78
|
+
* semitonesBetween(69, 60) // -9
|
|
79
|
+
*/
|
|
80
|
+
export function semitonesBetween(a, b) {
|
|
81
|
+
return b - a;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Transpose a MIDI note by a number of semitones, clamped to 0–127.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* transpose(60, 7) // 67 (C4 → G4)
|
|
88
|
+
* transpose(60, -12) // 48 (C4 → C3)
|
|
89
|
+
*/
|
|
90
|
+
export function transpose(midi, semitones) {
|
|
91
|
+
return Math.max(0, Math.min(127, midi + semitones));
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Convert beats to seconds given a BPM.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* beatsToSeconds(1, 120) // 0.5
|
|
98
|
+
* beatsToSeconds(4, 90) // 2.666...
|
|
99
|
+
*/
|
|
100
|
+
export function beatsToSeconds(beats, bpm) {
|
|
101
|
+
return (beats / bpm) * 60;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Convert seconds to beats given a BPM.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* secondsToBeats(0.5, 120) // 1
|
|
108
|
+
* secondsToBeats(2, 90) // 3
|
|
109
|
+
*/
|
|
110
|
+
export function secondsToBeats(seconds, bpm) {
|
|
111
|
+
return (seconds * bpm) / 60;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Convert a linear gain value (0–∞) to decibels.
|
|
115
|
+
* Returns `-Infinity` for gain = 0.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* gainToDb(1) // 0
|
|
119
|
+
* gainToDb(0.5) // -6.02
|
|
120
|
+
*/
|
|
121
|
+
export function gainToDb(gain) {
|
|
122
|
+
if (gain <= 0)
|
|
123
|
+
return -Infinity;
|
|
124
|
+
return 20 * Math.log10(gain);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Convert decibels to a linear gain value.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* dbToGain(0) // 1
|
|
131
|
+
* dbToGain(-6) // ~0.5012
|
|
132
|
+
*/
|
|
133
|
+
export function dbToGain(db) {
|
|
134
|
+
return 10 ** (db / 20);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Clamp a value between a minimum and maximum.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* clamp(1.5, 0, 1) // 1
|
|
141
|
+
* clamp(-0.5, 0, 1) // 0
|
|
142
|
+
*/
|
|
143
|
+
export function clamp(value, min, max) {
|
|
144
|
+
return Math.max(min, Math.min(max, value));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Linear interpolation between two values.
|
|
148
|
+
*
|
|
149
|
+
* @param t Blend factor, 0–1
|
|
150
|
+
* @example
|
|
151
|
+
* lerp(0, 100, 0.5) // 50
|
|
152
|
+
*/
|
|
153
|
+
export function lerp(a, b, t) {
|
|
154
|
+
return a + (b - a) * t;
|
|
155
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type { PluginManifest, PluginType, PluginPricing, SemVer, PluginAuthor } from "./types/manifest";
|
|
2
|
+
export type { PluginParam, RangeParam, BoolParam, ChoiceParam, ParamCurve, ParamValues } from "./types/parameters";
|
|
3
|
+
export type { PluginContext, InstrumentContext } from "./types/context";
|
|
4
|
+
export type { AudioEffectIO, AudioEffectFactory, AudioEffectModule, } from "./types/audio-effect";
|
|
5
|
+
export type { MidiEvent, MidiEventType, MidiNoteOnEvent, MidiNoteOffEvent, MidiControlChangeEvent, MidiPitchBendEvent, MidiAftertouchEvent, MidiPolyAftertouchEvent, MidiProgramChangeEvent, MidiEffectPlugin, MidiEffectFactory, MidiEffectModule, } from "./types/midi-effect";
|
|
6
|
+
export type { InstrumentVoice, InstrumentPlugin, InstrumentFactory, InstrumentModule, } from "./types/instrument";
|
|
7
|
+
export type { EqBand, EqBandType, VocalEqSettings, VocalCompressorSettings, VocalDeEsserSettings, VocalSaturationSettings, VocalPitchCorrectionSettings, VocalChorusSettings, VocalReverbSettings, VocalDelaySettings, VocalPreset, VocalPresetModule, } from "./types/vocal-preset";
|
|
8
|
+
export type { UniformType, ShaderUniform, ShaderDefinition, ShaderModule, } from "./types/shader";
|
|
9
|
+
export type { CollaborationAccess, LicenseToken, LicenseGranted, LicenseDenied, LicenseCheckResult, LicenseChecker, } from "./types/licensing";
|
|
10
|
+
export type { HostToPluginMessage, HostToPluginExtended, PluginToHostEvent, PluginInitMessage, PluginParamsUpdateMessage, PluginProcessMessage, PluginMidiMessage, PluginNoteOnMessage, PluginNoteOffMessage, PluginDisposeMessage, PluginReadyEvent, PluginAudioOutputEvent, PluginMidiOutputEvent, PluginErrorEvent, PluginFetchRequestEvent, PluginFetchResponseMessage, } from "./types/events";
|
|
11
|
+
export { noteToHz, hzToNote, hzToNoteFractional, midiToName, nameToMidi, semitonesBetween, transpose, beatsToSeconds, secondsToBeats, gainToDb, dbToGain, clamp, lerp, } from "./helpers/note-utils";
|
|
12
|
+
export type { NoteName } from "./helpers/note-utils";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACvG,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAClH,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAGvE,YAAY,EACR,aAAa,EACb,kBAAkB,EAClB,iBAAiB,GACpB,MAAM,sBAAsB,CAAA;AAG7B,YAAY,EACR,SAAS,EACT,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACnB,MAAM,qBAAqB,CAAA;AAG5B,YAAY,EACR,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACnB,MAAM,oBAAoB,CAAA;AAG3B,YAAY,EACR,MAAM,EACN,UAAU,EACV,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,4BAA4B,EAC5B,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,iBAAiB,GACpB,MAAM,sBAAsB,CAAA;AAG7B,YAAY,EACR,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,YAAY,GACf,MAAM,gBAAgB,CAAA;AAGvB,YAAY,EACR,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,cAAc,GACjB,MAAM,mBAAmB,CAAA;AAG1B,YAAY,EACR,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,gBAAgB,EAChB,uBAAuB,EACvB,0BAA0B,GAC7B,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EACH,QAAQ,EACR,QAAQ,EACR,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,cAAc,EACd,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,KAAK,EACL,IAAI,GACP,MAAM,sBAAsB,CAAA;AAC7B,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { PluginContext } from "./context";
|
|
2
|
+
import type { PluginParam } from "./parameters";
|
|
3
|
+
import type { PluginManifest } from "./manifest";
|
|
4
|
+
/** The stereo or mono AudioNode pair exposed by an audio-effect plugin */
|
|
5
|
+
export interface AudioEffectIO {
|
|
6
|
+
/** Plugin receives audio here — connect the host's source to this node */
|
|
7
|
+
readonly input: AudioNode;
|
|
8
|
+
/** Plugin outputs processed audio from here — host connects this to the chain */
|
|
9
|
+
readonly output: AudioNode;
|
|
10
|
+
/**
|
|
11
|
+
* Optional side-chain input for compressors, gates, etc.
|
|
12
|
+
* Declare `sidechain: true` in the manifest to activate this input.
|
|
13
|
+
*/
|
|
14
|
+
readonly sidechain?: AudioNode;
|
|
15
|
+
/**
|
|
16
|
+
* Named AudioParams made available for automation.
|
|
17
|
+
* Keys must match the `id` fields of `manifest.params` entries.
|
|
18
|
+
* If omitted, the DAW uses param-polling via `getParamValue()` instead.
|
|
19
|
+
*/
|
|
20
|
+
readonly automationTargets?: Readonly<Record<string, AudioParam>>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Audio effect plugin interface.
|
|
24
|
+
*
|
|
25
|
+
* Return an `AudioEffectIO` from your factory function. The host wires
|
|
26
|
+
* `input` and `output` into the track processing chain automatically.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* import type { AudioEffectFactory, PluginContext } from "@collabhut/plugin-sdk"
|
|
31
|
+
*
|
|
32
|
+
* export const manifest = {
|
|
33
|
+
* id: "com.myorg.warm-compressor",
|
|
34
|
+
* // ...
|
|
35
|
+
* params: [
|
|
36
|
+
* { type: "range", id: "threshold", label: "Threshold",
|
|
37
|
+
* min: -60, max: 0, default: -18, unit: "dB" },
|
|
38
|
+
* { type: "range", id: "ratio", label: "Ratio",
|
|
39
|
+
* min: 1, max: 20, default: 4, decimals: 1 },
|
|
40
|
+
* ],
|
|
41
|
+
* } satisfies PluginManifest
|
|
42
|
+
*
|
|
43
|
+
* export const factory: AudioEffectFactory<typeof manifest.params> =
|
|
44
|
+
* (context, params) => {
|
|
45
|
+
* const compressor = context.audioContext.createDynamicsCompressor()
|
|
46
|
+
* compressor.threshold.value = params.threshold
|
|
47
|
+
* compressor.ratio.value = params.ratio
|
|
48
|
+
* return {
|
|
49
|
+
* input: compressor,
|
|
50
|
+
* output: compressor,
|
|
51
|
+
* automationTargets: {
|
|
52
|
+
* threshold: compressor.threshold,
|
|
53
|
+
* ratio: compressor.ratio,
|
|
54
|
+
* },
|
|
55
|
+
* }
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export type AudioEffectFactory<TParams extends ReadonlyArray<PluginParam> = ReadonlyArray<PluginParam>> = (context: PluginContext, params: Readonly<Record<string, number | boolean | string>>, manifest: PluginManifest) => AudioEffectIO;
|
|
60
|
+
/**
|
|
61
|
+
* Full audio-effect module shape.
|
|
62
|
+
* Your plugin file must export both `manifest` and `factory`.
|
|
63
|
+
*/
|
|
64
|
+
export interface AudioEffectModule {
|
|
65
|
+
readonly manifest: PluginManifest & {
|
|
66
|
+
readonly type: "audio-effect";
|
|
67
|
+
};
|
|
68
|
+
readonly factory: AudioEffectFactory;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=audio-effect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-effect.d.ts","sourceRoot":"","sources":["../../src/types/audio-effect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;IAC1B;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAA;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAA;CACpE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,MAAM,kBAAkB,CAC1B,OAAO,SAAS,aAAa,CAAC,WAAW,CAAC,GAAG,aAAa,CAAC,WAAW,CAAC,IACvE,CACA,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC,EAC3D,QAAQ,EAAE,cAAc,KACvB,aAAa,CAAA;AAElB;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;KAAE,CAAA;IACrE,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;CACvC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime context provided to every plugin factory.
|
|
3
|
+
*
|
|
4
|
+
* The context object is the plugin's only gateway to the host environment.
|
|
5
|
+
* All properties are read-only — plugins cannot modify host state directly.
|
|
6
|
+
*/
|
|
7
|
+
export interface PluginContext {
|
|
8
|
+
/** Web Audio API context. Use this to create nodes and schedule events. */
|
|
9
|
+
readonly audioContext: AudioContext;
|
|
10
|
+
/**
|
|
11
|
+
* Current sample rate in Hz.
|
|
12
|
+
* Always equal to `audioContext.sampleRate`.
|
|
13
|
+
*/
|
|
14
|
+
readonly sampleRate: number;
|
|
15
|
+
/**
|
|
16
|
+
* Current tempo in beats per minute.
|
|
17
|
+
* Reactive: you should re-read this in every `process()` call for DAW sync.
|
|
18
|
+
*/
|
|
19
|
+
readonly bpm: number;
|
|
20
|
+
/** Current transport position in beats (fractions of a beat allowed) */
|
|
21
|
+
readonly positionBeats: number;
|
|
22
|
+
/** `true` when the DAW transport is rolling */
|
|
23
|
+
readonly isPlaying: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Fetch a resource from CollabHut CDN.
|
|
26
|
+
* Only HTTPS URLs on `cdn.collabhut.com` are allowed.
|
|
27
|
+
* Arbitrary network access is blocked inside the plugin sandbox.
|
|
28
|
+
*/
|
|
29
|
+
fetchAsset(url: string): Promise<ArrayBuffer>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Runtime context provided exclusively to instrument plugins.
|
|
33
|
+
* Extends PluginContext with polyphonic voice management.
|
|
34
|
+
*/
|
|
35
|
+
export interface InstrumentContext extends PluginContext {
|
|
36
|
+
/** Maximum number of simultaneous voices the host will attempt */
|
|
37
|
+
readonly maxPolyphony: number;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/types/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC1B,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAA;IACnC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B;;;;OAIG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CAChD;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACpD,kEAAkE;IAClE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAChC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed message protocol for plugin ↔ host communication.
|
|
3
|
+
*
|
|
4
|
+
* Plugins execute inside a sandboxed Web Worker. All communication with the
|
|
5
|
+
* host DAW happens via `postMessage` with these discriminated-union types.
|
|
6
|
+
*
|
|
7
|
+
* ## Architecture
|
|
8
|
+
* ```
|
|
9
|
+
* Host (main thread) Plugin (Worker)
|
|
10
|
+
* │ │
|
|
11
|
+
* │──── HostToPluginMessage ────────►│ host → plugin commands
|
|
12
|
+
* │◄─── PluginToHostMessage ─────────│ plugin → host events
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Plugin authors **do not** import or use these types directly — they are
|
|
16
|
+
* consumed by the DAW runtime. They are documented here for those building
|
|
17
|
+
* plugin host environments or tooling.
|
|
18
|
+
*/
|
|
19
|
+
/** Initialize the plugin. Sent once immediately after the Worker starts. */
|
|
20
|
+
export interface PluginInitMessage {
|
|
21
|
+
readonly type: "init";
|
|
22
|
+
/** Transferable SharedArrayBuffer for lock-free transport state reads */
|
|
23
|
+
readonly transportBuffer: SharedArrayBuffer;
|
|
24
|
+
/** Plugin parameter initial values */
|
|
25
|
+
readonly params: Readonly<Record<string, number | boolean | string>>;
|
|
26
|
+
readonly sampleRate: number;
|
|
27
|
+
readonly blockSize: number;
|
|
28
|
+
}
|
|
29
|
+
/** Update one or more parameter values */
|
|
30
|
+
export interface PluginParamsUpdateMessage {
|
|
31
|
+
readonly type: "params-update";
|
|
32
|
+
readonly params: Readonly<Record<string, number | boolean | string>>;
|
|
33
|
+
}
|
|
34
|
+
/** Process one audio block (audio-effect plugins only) */
|
|
35
|
+
export interface PluginProcessMessage {
|
|
36
|
+
readonly type: "process";
|
|
37
|
+
/** Interleaved float32 samples, transferred (zero-copy) */
|
|
38
|
+
readonly inputBuffer: Float32Array;
|
|
39
|
+
readonly numChannels: number;
|
|
40
|
+
readonly numFrames: number;
|
|
41
|
+
}
|
|
42
|
+
/** Send MIDI events for this block (midi-effect / instrument plugins) */
|
|
43
|
+
export interface PluginMidiMessage {
|
|
44
|
+
readonly type: "midi";
|
|
45
|
+
readonly events: ReadonlyArray<{
|
|
46
|
+
readonly channel: number;
|
|
47
|
+
readonly beatTime: number;
|
|
48
|
+
readonly data: readonly number[];
|
|
49
|
+
}>;
|
|
50
|
+
}
|
|
51
|
+
/** Trigger note on (instrument plugins only) */
|
|
52
|
+
export interface PluginNoteOnMessage {
|
|
53
|
+
readonly type: "note-on";
|
|
54
|
+
readonly note: number;
|
|
55
|
+
readonly velocity: number;
|
|
56
|
+
readonly time: number;
|
|
57
|
+
}
|
|
58
|
+
/** Trigger note off (instrument plugins only) */
|
|
59
|
+
export interface PluginNoteOffMessage {
|
|
60
|
+
readonly type: "note-off";
|
|
61
|
+
readonly note: number;
|
|
62
|
+
readonly velocity: number;
|
|
63
|
+
readonly time: number;
|
|
64
|
+
}
|
|
65
|
+
/** Dispose and terminate the plugin */
|
|
66
|
+
export interface PluginDisposeMessage {
|
|
67
|
+
readonly type: "dispose";
|
|
68
|
+
}
|
|
69
|
+
export type HostToPluginMessage = PluginInitMessage | PluginParamsUpdateMessage | PluginProcessMessage | PluginMidiMessage | PluginNoteOnMessage | PluginNoteOffMessage | PluginDisposeMessage;
|
|
70
|
+
/** Plugin is ready after init */
|
|
71
|
+
export interface PluginReadyEvent {
|
|
72
|
+
readonly type: "ready";
|
|
73
|
+
readonly manifestId: string;
|
|
74
|
+
readonly apiVersion: string;
|
|
75
|
+
}
|
|
76
|
+
/** Processed audio output (audio-effect plugins) */
|
|
77
|
+
export interface PluginAudioOutputEvent {
|
|
78
|
+
readonly type: "audio-output";
|
|
79
|
+
/** Interleaved float32 samples, transferred (zero-copy) */
|
|
80
|
+
readonly outputBuffer: Float32Array;
|
|
81
|
+
readonly numChannels: number;
|
|
82
|
+
readonly numFrames: number;
|
|
83
|
+
}
|
|
84
|
+
/** MIDI output events (midi-effect plugins) */
|
|
85
|
+
export interface PluginMidiOutputEvent {
|
|
86
|
+
readonly type: "midi-output";
|
|
87
|
+
readonly events: ReadonlyArray<{
|
|
88
|
+
readonly channel: number;
|
|
89
|
+
readonly beatTime: number;
|
|
90
|
+
readonly data: readonly number[];
|
|
91
|
+
}>;
|
|
92
|
+
}
|
|
93
|
+
/** Plugin encountered an error */
|
|
94
|
+
export interface PluginErrorEvent {
|
|
95
|
+
readonly type: "error";
|
|
96
|
+
/** Human-readable description of the error */
|
|
97
|
+
readonly message: string;
|
|
98
|
+
/** Optional stack trace (stripped in production builds) */
|
|
99
|
+
readonly stack?: string;
|
|
100
|
+
}
|
|
101
|
+
/** Plugin requests to fetch a CDN asset (host performs the fetch and replies) */
|
|
102
|
+
export interface PluginFetchRequestEvent {
|
|
103
|
+
readonly type: "fetch-request";
|
|
104
|
+
readonly requestId: string;
|
|
105
|
+
/** Must be a URL on cdn.collabhut.com — rejected otherwise */
|
|
106
|
+
readonly url: string;
|
|
107
|
+
}
|
|
108
|
+
/** Host response to a `fetch-request` */
|
|
109
|
+
export interface PluginFetchResponseMessage {
|
|
110
|
+
readonly type: "fetch-response";
|
|
111
|
+
readonly requestId: string;
|
|
112
|
+
readonly ok: boolean;
|
|
113
|
+
/** Transferred ArrayBuffer on success */
|
|
114
|
+
readonly data?: ArrayBuffer;
|
|
115
|
+
readonly error?: string;
|
|
116
|
+
}
|
|
117
|
+
export type PluginToHostEvent = PluginReadyEvent | PluginAudioOutputEvent | PluginMidiOutputEvent | PluginErrorEvent | PluginFetchRequestEvent;
|
|
118
|
+
export type HostToPluginExtended = HostToPluginMessage | PluginFetchResponseMessage;
|
|
119
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/types/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed message protocol for plugin ↔ host communication.
|
|
3
|
+
*
|
|
4
|
+
* Plugins execute inside a sandboxed Web Worker. All communication with the
|
|
5
|
+
* host DAW happens via `postMessage` with these discriminated-union types.
|
|
6
|
+
*
|
|
7
|
+
* ## Architecture
|
|
8
|
+
* ```
|
|
9
|
+
* Host (main thread) Plugin (Worker)
|
|
10
|
+
* │ │
|
|
11
|
+
* │──── HostToPluginMessage ────────►│ host → plugin commands
|
|
12
|
+
* │◄─── PluginToHostMessage ─────────│ plugin → host events
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Plugin authors **do not** import or use these types directly — they are
|
|
16
|
+
* consumed by the DAW runtime. They are documented here for those building
|
|
17
|
+
* plugin host environments or tooling.
|
|
18
|
+
*/
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { InstrumentContext } from "./context";
|
|
2
|
+
import type { MidiEvent } from "./midi-effect";
|
|
3
|
+
import type { PluginManifest } from "./manifest";
|
|
4
|
+
/**
|
|
5
|
+
* An individual voice instance returned by `noteOn`.
|
|
6
|
+
*
|
|
7
|
+
* The DAW keeps a reference per active voice and calls `stop` when the
|
|
8
|
+
* corresponding note-off arrives (or when voices are stolen for polyphony).
|
|
9
|
+
*/
|
|
10
|
+
export interface InstrumentVoice {
|
|
11
|
+
/** AudioNode carrying this voice's audio signal — host connects it to the track bus */
|
|
12
|
+
readonly output: AudioNode;
|
|
13
|
+
/**
|
|
14
|
+
* Stop (release) the voice.
|
|
15
|
+
* @param velocity Release velocity, 0–127
|
|
16
|
+
* @param time AudioContext time at which the release should begin
|
|
17
|
+
*/
|
|
18
|
+
stop(velocity: number, time: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Kill the voice immediately with no release tail.
|
|
21
|
+
* Used for voice stealing or when the project stops.
|
|
22
|
+
*/
|
|
23
|
+
kill(): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Polyphonic instrument plugin.
|
|
27
|
+
*
|
|
28
|
+
* The DAW calls `noteOn` for every note and stores the returned voice.
|
|
29
|
+
* When the note ends it calls `voice.stop()`. If polyphony is exceeded it
|
|
30
|
+
* calls `voice.kill()` on the oldest voice before starting a new one.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* import type { InstrumentFactory, InstrumentVoice } from "@collabhut/plugin-sdk"
|
|
35
|
+
*
|
|
36
|
+
* export const factory: InstrumentFactory = (context) => ({
|
|
37
|
+
* noteOn(note, velocity, time) {
|
|
38
|
+
* const osc = context.audioContext.createOscillator()
|
|
39
|
+
* const gain = context.audioContext.createGain()
|
|
40
|
+
* osc.frequency.value = 440 * 2 ** ((note - 69) / 12)
|
|
41
|
+
* gain.gain.setValueAtTime(velocity / 127, time)
|
|
42
|
+
* osc.connect(gain)
|
|
43
|
+
* osc.start(time)
|
|
44
|
+
* return {
|
|
45
|
+
* output: gain,
|
|
46
|
+
* stop(_vel, releaseTime) {
|
|
47
|
+
* gain.gain.setTargetAtTime(0, releaseTime, 0.1)
|
|
48
|
+
* osc.stop(releaseTime + 0.5)
|
|
49
|
+
* },
|
|
50
|
+
* kill() { osc.stop(); gain.disconnect() },
|
|
51
|
+
* }
|
|
52
|
+
* },
|
|
53
|
+
* onMidi(_event) {},
|
|
54
|
+
* dispose() {},
|
|
55
|
+
* })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export interface InstrumentPlugin {
|
|
59
|
+
/**
|
|
60
|
+
* Start a new voice for the given MIDI note.
|
|
61
|
+
* @param note MIDI note number (0–127)
|
|
62
|
+
* @param velocity Attack velocity (0–127)
|
|
63
|
+
* @param time AudioContext time for the note start
|
|
64
|
+
* @returns A voice object the DAW will manage
|
|
65
|
+
*/
|
|
66
|
+
noteOn(note: number, velocity: number, time: number): InstrumentVoice;
|
|
67
|
+
/**
|
|
68
|
+
* Handle non-note MIDI events (CC, pitch bend, aftertouch, etc.).
|
|
69
|
+
* Called alongside the voice lifecycle — useful for mod-wheel, portamento, etc.
|
|
70
|
+
*/
|
|
71
|
+
onMidi(event: MidiEvent): void;
|
|
72
|
+
/** Called when the plugin is removed or the project closes */
|
|
73
|
+
dispose(): void;
|
|
74
|
+
}
|
|
75
|
+
export type InstrumentFactory = (context: InstrumentContext, params: Readonly<Record<string, number | boolean | string>>, manifest: PluginManifest) => InstrumentPlugin;
|
|
76
|
+
/** Full instrument module shape */
|
|
77
|
+
export interface InstrumentModule {
|
|
78
|
+
readonly manifest: PluginManifest & {
|
|
79
|
+
readonly type: "instrument";
|
|
80
|
+
};
|
|
81
|
+
readonly factory: InstrumentFactory;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=instrument.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrument.d.ts","sourceRoot":"","sources":["../../src/types/instrument.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;IAC1B;;;;OAIG;IACH,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1C;;;OAGG;IACH,IAAI,IAAI,IAAI,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,WAAW,gBAAgB;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,eAAe,CAAA;IACrE;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IAC9B,8DAA8D;IAC9D,OAAO,IAAI,IAAI,CAAA;CAClB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAC5B,OAAO,EAAE,iBAAiB,EAC1B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC,EAC3D,QAAQ,EAAE,cAAc,KACvB,gBAAgB,CAAA;AAErB,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;KAAE,CAAA;IACnE,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAA;CACtC"}
|