@libraz/libsonare 1.3.2 → 1.3.3
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 +45 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +236 -68
- package/dist/index.js.map +1 -1
- package/dist/sonare-rt-module.js +1 -1
- package/dist/sonare-rt.js +1 -1
- package/dist/sonare-rt.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +347 -9
- package/dist/worklet.js +1184 -100
- package/dist/worklet.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +13 -1
- package/src/module_state.ts +82 -17
- package/src/opfs_clip_pages.ts +43 -9
- package/src/realtime_engine.ts +174 -109
- package/src/sonare.js.d.ts +59 -0
- package/src/web_midi.ts +15 -11
- package/src/worklet.ts +1402 -66
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -401,6 +401,7 @@ export type {
|
|
|
401
401
|
EngineAutomationPoint,
|
|
402
402
|
EngineBounceOptions,
|
|
403
403
|
EngineBounceResult,
|
|
404
|
+
EngineBus,
|
|
404
405
|
EngineCapabilities,
|
|
405
406
|
EngineCaptureStatus,
|
|
406
407
|
EngineClip,
|
|
@@ -410,8 +411,14 @@ export type {
|
|
|
410
411
|
EngineMarker,
|
|
411
412
|
EngineMeterTelemetry,
|
|
412
413
|
EngineMetronomeConfig,
|
|
414
|
+
EngineMidiClipSchedule,
|
|
415
|
+
EngineMidiEvent,
|
|
413
416
|
EngineParameterInfo,
|
|
414
417
|
EngineTelemetry,
|
|
418
|
+
EngineTempoSegment,
|
|
419
|
+
EngineTimeSignatureSegment,
|
|
420
|
+
EngineTrackLane,
|
|
421
|
+
EngineTrackSend,
|
|
415
422
|
EngineTransportState,
|
|
416
423
|
MidiCcBindOptions,
|
|
417
424
|
} from './realtime_engine';
|
|
@@ -484,6 +491,11 @@ let initPromise: Promise<void> | null = null;
|
|
|
484
491
|
*/
|
|
485
492
|
export async function init(options?: {
|
|
486
493
|
locateFile?: (path: string, prefix: string) => string;
|
|
494
|
+
wasmBinary?: ArrayBuffer | Uint8Array;
|
|
495
|
+
moduleFactory?: (options?: {
|
|
496
|
+
locateFile?: (path: string, prefix: string) => string;
|
|
497
|
+
wasmBinary?: ArrayBuffer | Uint8Array;
|
|
498
|
+
}) => Promise<SonareModule>;
|
|
487
499
|
}): Promise<void> {
|
|
488
500
|
if (module) {
|
|
489
501
|
return;
|
|
@@ -495,7 +507,7 @@ export async function init(options?: {
|
|
|
495
507
|
|
|
496
508
|
initPromise = (async () => {
|
|
497
509
|
try {
|
|
498
|
-
const createModule = (await import('./sonare.js')).default;
|
|
510
|
+
const createModule = options?.moduleFactory ?? (await import('./sonare.js')).default;
|
|
499
511
|
module = await createModule(options);
|
|
500
512
|
setSonareModule(module);
|
|
501
513
|
} catch (error) {
|
package/src/module_state.ts
CHANGED
|
@@ -70,6 +70,7 @@ function makeSonareError(raw: SonareModule, thrown: number): SonareError {
|
|
|
70
70
|
*/
|
|
71
71
|
function wrapModuleErrors(raw: SonareModule): SonareModule {
|
|
72
72
|
const cache = new Map<PropertyKey, unknown>();
|
|
73
|
+
const objectCache = new WeakMap<object, unknown>();
|
|
73
74
|
const convert = (error: unknown): never => {
|
|
74
75
|
const ptr = nativeExceptionPtr(error);
|
|
75
76
|
if (ptr !== null) {
|
|
@@ -77,6 +78,86 @@ function wrapModuleErrors(raw: SonareModule): SonareModule {
|
|
|
77
78
|
}
|
|
78
79
|
throw error;
|
|
79
80
|
};
|
|
81
|
+
|
|
82
|
+
const wrapNativeObject = (value: unknown): unknown => {
|
|
83
|
+
if (value === null || typeof value !== 'object') {
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer || value instanceof Promise) {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
const objectValue = value as object;
|
|
90
|
+
const cached = objectCache.get(objectValue);
|
|
91
|
+
if (cached) {
|
|
92
|
+
return cached;
|
|
93
|
+
}
|
|
94
|
+
const methodCache = new Map<PropertyKey, unknown>();
|
|
95
|
+
const wrapped = new Proxy(objectValue, {
|
|
96
|
+
get(target, prop, receiver) {
|
|
97
|
+
const member = Reflect.get(target, prop, receiver);
|
|
98
|
+
if (typeof member !== 'function') {
|
|
99
|
+
return member;
|
|
100
|
+
}
|
|
101
|
+
const cachedMethod = methodCache.get(prop);
|
|
102
|
+
if (cachedMethod) {
|
|
103
|
+
return cachedMethod;
|
|
104
|
+
}
|
|
105
|
+
const method = member as (...a: unknown[]) => unknown;
|
|
106
|
+
const wrappedMethod = (...args: unknown[]) => {
|
|
107
|
+
try {
|
|
108
|
+
return wrapNativeObject(Reflect.apply(method, target, args));
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return convert(error);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
methodCache.set(prop, wrappedMethod);
|
|
114
|
+
return wrappedMethod;
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
objectCache.set(objectValue, wrapped);
|
|
118
|
+
return wrapped;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const wrapFunction = (value: (...a: unknown[]) => unknown): unknown => {
|
|
122
|
+
const fnCache = new Map<PropertyKey, unknown>();
|
|
123
|
+
return new Proxy(value, {
|
|
124
|
+
get(target, prop, receiver) {
|
|
125
|
+
const member = Reflect.get(target, prop, receiver);
|
|
126
|
+
if (typeof member !== 'function') {
|
|
127
|
+
return member;
|
|
128
|
+
}
|
|
129
|
+
const cachedMember = fnCache.get(prop);
|
|
130
|
+
if (cachedMember) {
|
|
131
|
+
return cachedMember;
|
|
132
|
+
}
|
|
133
|
+
const fn = member as (...a: unknown[]) => unknown;
|
|
134
|
+
const wrappedMember = (...args: unknown[]) => {
|
|
135
|
+
try {
|
|
136
|
+
return wrapNativeObject(Reflect.apply(fn, target, args));
|
|
137
|
+
} catch (error) {
|
|
138
|
+
return convert(error);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
fnCache.set(prop, wrappedMember);
|
|
142
|
+
return wrappedMember;
|
|
143
|
+
},
|
|
144
|
+
apply(t, thisArg, args) {
|
|
145
|
+
try {
|
|
146
|
+
return wrapNativeObject(Reflect.apply(t, thisArg, args as unknown[]));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
return convert(error);
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
construct(t, args, newTarget) {
|
|
152
|
+
try {
|
|
153
|
+
return wrapNativeObject(Reflect.construct(t, args as unknown[], newTarget));
|
|
154
|
+
} catch (error) {
|
|
155
|
+
return convert(error) as object;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
80
161
|
return new Proxy(raw, {
|
|
81
162
|
get(target, prop, receiver) {
|
|
82
163
|
const value = Reflect.get(target, prop, receiver);
|
|
@@ -90,23 +171,7 @@ function wrapModuleErrors(raw: SonareModule): SonareModule {
|
|
|
90
171
|
// Wrap as a Proxy (not a plain function) so embind class constructors
|
|
91
172
|
// invoked via `new module.Foo(...)` keep their `[[Construct]]` behaviour
|
|
92
173
|
// and prototype while still converting thrown native pointers.
|
|
93
|
-
const
|
|
94
|
-
const wrapped = new Proxy(fn, {
|
|
95
|
-
apply(t, thisArg, args) {
|
|
96
|
-
try {
|
|
97
|
-
return Reflect.apply(t, thisArg, args as unknown[]);
|
|
98
|
-
} catch (error) {
|
|
99
|
-
return convert(error);
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
construct(t, args, newTarget) {
|
|
103
|
-
try {
|
|
104
|
-
return Reflect.construct(t, args as unknown[], newTarget) as object;
|
|
105
|
-
} catch (error) {
|
|
106
|
-
return convert(error) as object;
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
});
|
|
174
|
+
const wrapped = wrapFunction(value as (...a: unknown[]) => unknown);
|
|
110
175
|
cache.set(prop, wrapped);
|
|
111
176
|
return wrapped;
|
|
112
177
|
},
|
package/src/opfs_clip_pages.ts
CHANGED
|
@@ -29,10 +29,25 @@ interface PageResponse {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export const opfsClipPageWorkerSource = `
|
|
32
|
+
const sonareClipPageReadQueues = new Map();
|
|
33
|
+
|
|
34
|
+
function sonareEnqueueClipPageRead(key, task) {
|
|
35
|
+
const previous = sonareClipPageReadQueues.get(key) || Promise.resolve();
|
|
36
|
+
const next = previous.catch(() => undefined).then(task);
|
|
37
|
+
const queued = next.finally(() => {
|
|
38
|
+
if (sonareClipPageReadQueues.get(key) === queued) {
|
|
39
|
+
sonareClipPageReadQueues.delete(key);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
sonareClipPageReadQueues.set(key, queued);
|
|
43
|
+
return next;
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
self.onmessage = async (event) => {
|
|
33
47
|
const message = event.data;
|
|
34
48
|
if (!message || message.type !== 'sonare:read-clip-page') return;
|
|
35
49
|
const { requestId, path, pageIndex, numChannels, numSamples, pageFrames, dataOffsetBytes = 0 } = message;
|
|
50
|
+
await sonareEnqueueClipPageRead(String(path), async () => {
|
|
36
51
|
try {
|
|
37
52
|
if (pageIndex < 0) {
|
|
38
53
|
self.postMessage({ type: 'sonare:clip-page', requestId, pageIndex, ok: false });
|
|
@@ -95,6 +110,7 @@ self.onmessage = async (event) => {
|
|
|
95
110
|
error: error instanceof Error ? error.message : String(error),
|
|
96
111
|
});
|
|
97
112
|
}
|
|
113
|
+
});
|
|
98
114
|
};
|
|
99
115
|
`;
|
|
100
116
|
|
|
@@ -119,6 +135,7 @@ export function createOpfsClipPageProvider(
|
|
|
119
135
|
const ownsWorker = options.worker === undefined || options.terminateWorkerOnClose === true;
|
|
120
136
|
let nextRequestId = 1;
|
|
121
137
|
let closed = false;
|
|
138
|
+
let readQueue: Promise<void> = Promise.resolve();
|
|
122
139
|
const pending = new Map<
|
|
123
140
|
number,
|
|
124
141
|
{ resolve: (value: boolean) => void; reject: (reason: unknown) => void }
|
|
@@ -165,15 +182,32 @@ export function createOpfsClipPageProvider(
|
|
|
165
182
|
const promise = new Promise<boolean>((resolve, reject) => {
|
|
166
183
|
pending.set(requestId, { resolve, reject });
|
|
167
184
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
185
|
+
readQueue = readQueue
|
|
186
|
+
.catch(() => undefined)
|
|
187
|
+
.then(() => {
|
|
188
|
+
if (closed) {
|
|
189
|
+
const entry = pending.get(requestId);
|
|
190
|
+
pending.delete(requestId);
|
|
191
|
+
entry?.reject(new Error('OpfsClipPageProvider is closed'));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
worker.postMessage({
|
|
195
|
+
type: 'sonare:read-clip-page',
|
|
196
|
+
requestId,
|
|
197
|
+
path: options.path,
|
|
198
|
+
pageIndex,
|
|
199
|
+
numChannels: options.numChannels,
|
|
200
|
+
numSamples: options.numSamples,
|
|
201
|
+
pageFrames: options.pageFrames,
|
|
202
|
+
dataOffsetBytes: options.dataOffsetBytes ?? 0,
|
|
203
|
+
});
|
|
204
|
+
return promise.then(
|
|
205
|
+
() => undefined,
|
|
206
|
+
() => undefined,
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
readQueue.catch(() => {
|
|
210
|
+
// The per-request promise carries the user-visible failure.
|
|
177
211
|
});
|
|
178
212
|
return promise;
|
|
179
213
|
};
|