@algo-wasm/algo-wasm 1.1.0 → 1.2.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +6 -4
  3. package/dist/algo-wasm-error.d.ts +13 -0
  4. package/dist/algo-wasm-player.d.ts +169 -0
  5. package/dist/algo_wasm.wasm +0 -0
  6. package/dist/built-in-profiles.d.ts +5 -0
  7. package/dist/engine-core.d.ts +96 -0
  8. package/dist/events.d.ts +5 -1
  9. package/dist/index.d.ts +3 -4
  10. package/dist/index.js +1 -6
  11. package/dist/integrations/phaser.d.ts +9 -2
  12. package/dist/integrations/phaser.js +1 -16
  13. package/dist/integrations/react.d.ts +8 -2
  14. package/dist/integrations/react.js +1 -28
  15. package/dist/integrations/svelte.d.ts +6 -2
  16. package/dist/integrations/svelte.js +1 -17
  17. package/dist/integrations/vue.d.ts +7 -2
  18. package/dist/integrations/vue.js +1 -16
  19. package/dist/load-wasm.d.ts +92 -0
  20. package/dist/profile-schema.json +317 -228
  21. package/dist/profile-spec.d.ts +178 -0
  22. package/dist/profile-wire.d.ts +50 -0
  23. package/dist/profile.d.ts +25 -1
  24. package/dist/types.d.ts +25 -1
  25. package/dist/wire-decode.d.ts +18 -0
  26. package/dist/worklet/algo-worklet.d.ts +1 -83
  27. package/dist/worklet/algo-worklet.js +1 -471
  28. package/package.json +1 -1
  29. package/dist/AlgoWasmError.d.ts +0 -8
  30. package/dist/AlgoWasmError.d.ts.map +0 -1
  31. package/dist/AlgoWasmError.js +0 -9
  32. package/dist/AlgoWasmError.js.map +0 -1
  33. package/dist/AlgoWasmPlayer.d.ts +0 -48
  34. package/dist/AlgoWasmPlayer.d.ts.map +0 -1
  35. package/dist/AlgoWasmPlayer.js +0 -375
  36. package/dist/AlgoWasmPlayer.js.map +0 -1
  37. package/dist/events.d.ts.map +0 -1
  38. package/dist/events.js +0 -25
  39. package/dist/events.js.map +0 -1
  40. package/dist/index.d.ts.map +0 -1
  41. package/dist/index.js.map +0 -1
  42. package/dist/integrations/phaser.d.ts.map +0 -1
  43. package/dist/integrations/phaser.js.map +0 -1
  44. package/dist/integrations/react.d.ts.map +0 -1
  45. package/dist/integrations/react.js.map +0 -1
  46. package/dist/integrations/svelte.d.ts.map +0 -1
  47. package/dist/integrations/svelte.js.map +0 -1
  48. package/dist/integrations/vue.d.ts.map +0 -1
  49. package/dist/integrations/vue.js.map +0 -1
  50. package/dist/loadWasm.d.ts +0 -47
  51. package/dist/loadWasm.d.ts.map +0 -1
  52. package/dist/loadWasm.js +0 -229
  53. package/dist/loadWasm.js.map +0 -1
  54. package/dist/profile.d.ts.map +0 -1
  55. package/dist/profile.js +0 -525
  56. package/dist/profile.js.map +0 -1
  57. package/dist/types.d.ts.map +0 -1
  58. package/dist/types.js +0 -2
  59. package/dist/types.js.map +0 -1
  60. package/dist/worklet/algo-worklet.d.ts.map +0 -1
  61. package/dist/worklet/algo-worklet.js.map +0 -1
@@ -1,471 +1 @@
1
- "use strict";
2
- class WorkletWasmBinding {
3
- exports;
4
- enginePtr = 0;
5
- renderPtr = 0;
6
- renderLength = 0;
7
- renderView;
8
- constructor(instance) {
9
- this.exports = instance.exports;
10
- this.memory;
11
- }
12
- create(profile, seed, requestedSampleRate, volume) {
13
- if (this.enginePtr !== 0) {
14
- this.destroy();
15
- }
16
- const profileJson = JSON.stringify(profile);
17
- const { high, low } = splitSeed(seed);
18
- const pointer = this.writeString(profileJson);
19
- try {
20
- const result = this.call('algo_wasm_engine_create', requestedSampleRate, high, low, pointer.pointer, pointer.length);
21
- if (result === 0) {
22
- throw new Error(this.lastError('Engine creation failed.'));
23
- }
24
- this.enginePtr = result;
25
- this.setVolume(volume);
26
- }
27
- finally {
28
- this.call('algo_wasm_dealloc', pointer.pointer, pointer.length);
29
- }
30
- }
31
- start() {
32
- this.callCode('algo_wasm_engine_start');
33
- }
34
- pause() {
35
- this.callCode('algo_wasm_engine_pause');
36
- }
37
- resume() {
38
- this.callCode('algo_wasm_engine_resume');
39
- }
40
- stop() {
41
- this.callCode('algo_wasm_engine_stop');
42
- }
43
- setSeed(seed) {
44
- const { high, low } = splitSeed(seed);
45
- this.callCode('algo_wasm_engine_set_seed', high, low);
46
- }
47
- setProfile(profile) {
48
- const pointer = this.writeString(JSON.stringify(profile));
49
- try {
50
- const code = this.call('algo_wasm_engine_set_profile', this.enginePtr, pointer.pointer, pointer.length);
51
- this.assertCode(code, 'Profile update failed.');
52
- }
53
- finally {
54
- this.call('algo_wasm_dealloc', pointer.pointer, pointer.length);
55
- }
56
- }
57
- setVolume(value) {
58
- this.callCode('algo_wasm_engine_set_volume', clampUnit(value));
59
- }
60
- setEnergy(value) {
61
- this.callCode('algo_wasm_engine_set_energy', clampUnit(value));
62
- }
63
- setIntensity(value) {
64
- this.callCode('algo_wasm_engine_set_intensity', clampUnit(value));
65
- }
66
- setMood(mood) {
67
- const pointer = this.writeString(mood);
68
- try {
69
- const code = this.call('algo_wasm_engine_set_mood', this.enginePtr, pointer.pointer, pointer.length);
70
- this.assertCode(code, 'Mood update failed.');
71
- }
72
- finally {
73
- this.call('algo_wasm_dealloc', pointer.pointer, pointer.length);
74
- }
75
- }
76
- setTempoRange(minBpm, maxBpm) {
77
- this.callCode('algo_wasm_engine_set_tempo_range', minBpm, maxBpm);
78
- }
79
- setMuted(trackId, muted) {
80
- const pointer = this.writeString(trackId);
81
- try {
82
- const code = this.call('algo_wasm_engine_set_muted', this.enginePtr, pointer.pointer, pointer.length, muted ? 1 : 0);
83
- this.assertCode(code, 'Mute update failed.');
84
- }
85
- finally {
86
- this.call('algo_wasm_dealloc', pointer.pointer, pointer.length);
87
- }
88
- }
89
- setSolo(trackId, solo) {
90
- const pointer = this.writeString(trackId);
91
- try {
92
- const code = this.call('algo_wasm_engine_set_solo', this.enginePtr, pointer.pointer, pointer.length, solo ? 1 : 0);
93
- this.assertCode(code, 'Solo update failed.');
94
- }
95
- finally {
96
- this.call('algo_wasm_dealloc', pointer.pointer, pointer.length);
97
- }
98
- }
99
- renderTo(output) {
100
- this.ensureRenderBuffer(output.length);
101
- const code = this.call('algo_wasm_engine_render', this.enginePtr, this.renderPtr, output.length);
102
- this.assertCode(code, 'Render failed.');
103
- if (!this.renderView || this.renderView.buffer !== this.memory.buffer) {
104
- this.renderView = new Float32Array(this.memory.buffer, this.renderPtr, this.renderLength);
105
- }
106
- output.set(this.renderView.subarray(0, output.length));
107
- }
108
- snapshot() {
109
- const length = this.call('algo_wasm_engine_snapshot_json', this.enginePtr);
110
- return this.readJson(length);
111
- }
112
- events() {
113
- const length = this.call('algo_wasm_engine_events_json', this.enginePtr);
114
- return this.readJson(length);
115
- }
116
- exportMidi() {
117
- const length = this.call('algo_wasm_engine_export_midi', this.enginePtr);
118
- const pointer = this.call('algo_wasm_bytes_ptr');
119
- return new Uint8Array(this.memory.buffer, pointer, length).slice();
120
- }
121
- destroy() {
122
- if (this.renderPtr !== 0) {
123
- this.call('algo_wasm_dealloc', this.renderPtr, this.renderLength * 4);
124
- this.renderPtr = 0;
125
- }
126
- if (this.enginePtr !== 0) {
127
- this.call('algo_wasm_engine_destroy', this.enginePtr);
128
- this.enginePtr = 0;
129
- }
130
- }
131
- get memory() {
132
- const memory = this.exports.memory;
133
- if (!(memory instanceof WebAssembly.Memory)) {
134
- throw new Error('AlgoWASM did not export WebAssembly memory.');
135
- }
136
- return memory;
137
- }
138
- ensureRenderBuffer(length) {
139
- if (this.renderPtr !== 0 && this.renderLength >= length) {
140
- return;
141
- }
142
- if (this.renderPtr !== 0) {
143
- this.call('algo_wasm_dealloc', this.renderPtr, this.renderLength * 4);
144
- }
145
- this.renderLength = length;
146
- this.renderPtr = this.call('algo_wasm_alloc', length * 4);
147
- this.renderView = new Float32Array(this.memory.buffer, this.renderPtr, length);
148
- }
149
- writeString(value) {
150
- const bytes = encodeUtf8(value);
151
- const pointer = this.call('algo_wasm_alloc', bytes.length);
152
- if (pointer === 0) {
153
- throw new Error('Could not allocate a WASM string buffer.');
154
- }
155
- new Uint8Array(this.memory.buffer, pointer, bytes.length).set(bytes);
156
- return {
157
- pointer,
158
- length: bytes.length
159
- };
160
- }
161
- readJson(length) {
162
- if (length === 0) {
163
- throw new Error(this.lastError('AlgoWASM returned an empty JSON buffer.'));
164
- }
165
- const pointer = this.call('algo_wasm_json_ptr');
166
- const bytes = new Uint8Array(this.memory.buffer, pointer, length);
167
- return JSON.parse(decodeUtf8(bytes));
168
- }
169
- callCode(name, ...args) {
170
- const code = this.call(name, this.enginePtr, ...args);
171
- this.assertCode(code, `${String(name)} failed.`);
172
- }
173
- assertCode(code, fallback) {
174
- if (code !== 0) {
175
- throw new Error(this.lastError(fallback));
176
- }
177
- }
178
- call(name, ...args) {
179
- const value = this.exports[name];
180
- if (typeof value !== 'function') {
181
- throw new Error(`AlgoWASM is missing the ${String(name)} export.`);
182
- }
183
- return value(...args);
184
- }
185
- lastError(fallback) {
186
- const length = this.call('algo_wasm_last_error_len');
187
- if (length === 0) {
188
- return fallback;
189
- }
190
- const pointer = this.call('algo_wasm_last_error_ptr');
191
- const bytes = new Uint8Array(this.memory.buffer, pointer, length);
192
- return decodeUtf8(bytes);
193
- }
194
- }
195
- class AlgoWasmProcessor extends AudioWorkletProcessor {
196
- binding;
197
- playing = false;
198
- scratch = new Float32Array(0);
199
- eventCountdown = 0;
200
- commandQueue = Promise.resolve();
201
- constructor() {
202
- super();
203
- this.port.onmessage = event => {
204
- const command = event.data;
205
- // Chain onto the queue instead of firing handleCommand directly so
206
- // commands are always applied in the order they were received, even
207
- // while an earlier async command (such as 'configure') is still
208
- // pending.
209
- this.commandQueue = this.commandQueue.then(() => this.handleCommand(command));
210
- };
211
- }
212
- process(_inputs, outputs, _parameters) {
213
- const output = outputs[0];
214
- const left = output?.[0];
215
- const right = output?.[1] ?? left;
216
- if (!left || !right) {
217
- return true;
218
- }
219
- if (!this.binding || !this.playing) {
220
- left.fill(0);
221
- right.fill(0);
222
- return true;
223
- }
224
- const length = left.length * 2;
225
- if (this.scratch.length !== length) {
226
- this.scratch = new Float32Array(length);
227
- }
228
- try {
229
- this.binding.renderTo(this.scratch);
230
- for (let index = 0; index < left.length; index += 1) {
231
- left[index] = this.scratch[index * 2] ?? 0;
232
- right[index] = this.scratch[index * 2 + 1] ?? 0;
233
- }
234
- this.eventCountdown -= 1;
235
- if (this.eventCountdown <= 0) {
236
- this.eventCountdown = 16;
237
- const events = this.binding.events();
238
- if (events.length > 0) {
239
- this.port.postMessage({
240
- type: 'events',
241
- events,
242
- snapshot: this.binding.snapshot()
243
- });
244
- }
245
- }
246
- }
247
- catch (error) {
248
- this.playing = false;
249
- left.fill(0);
250
- right.fill(0);
251
- this.port.postMessage({
252
- type: 'error',
253
- error: error instanceof Error ? error.message : String(error)
254
- });
255
- }
256
- return true;
257
- }
258
- async handleCommand(command) {
259
- try {
260
- if (command.type === 'configure') {
261
- await this.configure(command);
262
- return;
263
- }
264
- if (!this.binding) {
265
- throw new Error('AlgoWASM worklet has not been configured.');
266
- }
267
- const result = this.applyCommand(command);
268
- this.reply(command.id, true, result);
269
- }
270
- catch (error) {
271
- this.reply(command.id, false, undefined, error instanceof Error ? error.message : String(error));
272
- }
273
- }
274
- async configure(command) {
275
- if (!command.wasmBytes && !command.wasmUrl) {
276
- throw new Error('WASM bytes or a WASM URL are required.');
277
- }
278
- const instance = await instantiateWorkletWasm(command.wasmUrl, command.wasmBytes);
279
- const binding = new WorkletWasmBinding(instance);
280
- binding.create(command.profile ?? {}, command.seed ?? '1', command.sampleRate ?? sampleRate, command.volume ?? 0.8);
281
- this.binding = binding;
282
- this.port.postMessage({
283
- type: 'ready',
284
- snapshot: binding.snapshot()
285
- });
286
- }
287
- applyCommand(command) {
288
- const binding = this.requireBinding();
289
- switch (command.type) {
290
- case 'start':
291
- binding.start();
292
- this.playing = true;
293
- return binding.snapshot();
294
- case 'pause':
295
- binding.pause();
296
- this.playing = false;
297
- return binding.snapshot();
298
- case 'resume':
299
- binding.resume();
300
- this.playing = true;
301
- return binding.snapshot();
302
- case 'stop':
303
- binding.stop();
304
- this.playing = false;
305
- return binding.snapshot();
306
- case 'set-seed':
307
- binding.setSeed(command.seed ?? '1');
308
- return binding.snapshot();
309
- case 'set-profile':
310
- binding.setProfile(command.profile ?? {});
311
- return binding.snapshot();
312
- case 'set-volume':
313
- binding.setVolume(command.value ?? 0.8);
314
- return binding.snapshot();
315
- case 'set-energy':
316
- binding.setEnergy(command.value ?? 0.5);
317
- return binding.snapshot();
318
- case 'set-intensity':
319
- binding.setIntensity(command.value ?? 0.5);
320
- return binding.snapshot();
321
- case 'set-mood':
322
- binding.setMood(command.mood ?? 'dark');
323
- return binding.snapshot();
324
- case 'set-tempo-range':
325
- binding.setTempoRange(command.minBpm ?? 118, command.maxBpm ?? 138);
326
- return binding.snapshot();
327
- case 'set-muted':
328
- binding.setMuted(command.trackId ?? '', command.muted ?? false);
329
- return binding.snapshot();
330
- case 'set-solo':
331
- binding.setSolo(command.trackId ?? '', command.solo ?? false);
332
- return binding.snapshot();
333
- case 'export-midi': {
334
- const bytes = binding.exportMidi();
335
- return bytes.buffer;
336
- }
337
- case 'destroy':
338
- binding.destroy();
339
- this.binding = undefined;
340
- this.playing = false;
341
- return undefined;
342
- default:
343
- throw new Error(`Unknown AlgoWASM command: ${command.type}.`);
344
- }
345
- }
346
- requireBinding() {
347
- if (!this.binding) {
348
- throw new Error('AlgoWASM worklet has not been configured.');
349
- }
350
- return this.binding;
351
- }
352
- reply(id, ok, result, error) {
353
- if (id === undefined) {
354
- if (!ok && error) {
355
- this.port.postMessage({
356
- type: 'error',
357
- error
358
- });
359
- }
360
- return;
361
- }
362
- this.port.postMessage({
363
- type: 'command-result',
364
- id,
365
- ok,
366
- result,
367
- error
368
- });
369
- }
370
- }
371
- async function instantiateWorkletWasm(url, wasmBytes) {
372
- if (wasmBytes) {
373
- const result = await WebAssembly.instantiate(wasmBytes, {});
374
- return result.instance;
375
- }
376
- if (!url) {
377
- throw new Error('WASM URL is required when bytes are not supplied.');
378
- }
379
- const response = await fetch(url);
380
- if (!response.ok) {
381
- throw new Error(`Could not load AlgoWASM from ${url}. The server returned ${response.status}.`);
382
- }
383
- if (WebAssembly.instantiateStreaming) {
384
- try {
385
- const result = await WebAssembly.instantiateStreaming(response, {});
386
- return result.instance;
387
- }
388
- catch (error) {
389
- if (response.headers.get('content-type') === 'application/wasm') {
390
- throw error;
391
- }
392
- }
393
- }
394
- const fallback = await fetch(url);
395
- const bytes = await fallback.arrayBuffer();
396
- const result = await WebAssembly.instantiate(bytes, {});
397
- return result.instance;
398
- }
399
- function splitSeed(seed) {
400
- const value = BigInt.asUintN(64, BigInt(seed));
401
- return {
402
- high: Number((value >> 32n) & 0xffffffffn),
403
- low: Number(value & 0xffffffffn)
404
- };
405
- }
406
- function clampUnit(value) {
407
- if (!Number.isFinite(value)) {
408
- return 0;
409
- }
410
- return Math.min(1, Math.max(0, value));
411
- }
412
- function encodeUtf8(value) {
413
- if (typeof TextEncoder !== 'undefined') {
414
- return new TextEncoder().encode(value);
415
- }
416
- const bytes = [];
417
- for (const character of value) {
418
- const point = character.codePointAt(0) ?? 0;
419
- if (point <= 0x7f) {
420
- bytes.push(point);
421
- }
422
- else if (point <= 0x7ff) {
423
- bytes.push(0xc0 | (point >> 6), 0x80 | (point & 0x3f));
424
- }
425
- else if (point <= 0xffff) {
426
- bytes.push(0xe0 | (point >> 12), 0x80 | ((point >> 6) & 0x3f), 0x80 | (point & 0x3f));
427
- }
428
- else {
429
- bytes.push(0xf0 | (point >> 18), 0x80 | ((point >> 12) & 0x3f), 0x80 | ((point >> 6) & 0x3f), 0x80 | (point & 0x3f));
430
- }
431
- }
432
- return new Uint8Array(bytes);
433
- }
434
- function decodeUtf8(bytes) {
435
- if (typeof TextDecoder !== 'undefined') {
436
- return new TextDecoder().decode(bytes);
437
- }
438
- let output = '';
439
- let index = 0;
440
- while (index < bytes.length) {
441
- const first = bytes[index] ?? 0;
442
- if (first < 0x80) {
443
- output += String.fromCodePoint(first);
444
- index += 1;
445
- }
446
- else if (first < 0xe0) {
447
- const second = bytes[index + 1] ?? 0;
448
- output += String.fromCodePoint(((first & 0x1f) << 6) | (second & 0x3f));
449
- index += 2;
450
- }
451
- else if (first < 0xf0) {
452
- const second = bytes[index + 1] ?? 0;
453
- const third = bytes[index + 2] ?? 0;
454
- output += String.fromCodePoint(((first & 0x0f) << 12) | ((second & 0x3f) << 6) | (third & 0x3f));
455
- index += 3;
456
- }
457
- else {
458
- const second = bytes[index + 1] ?? 0;
459
- const third = bytes[index + 2] ?? 0;
460
- const fourth = bytes[index + 3] ?? 0;
461
- output += String.fromCodePoint(((first & 0x07) << 18)
462
- | ((second & 0x3f) << 12)
463
- | ((third & 0x3f) << 6)
464
- | (fourth & 0x3f));
465
- index += 4;
466
- }
467
- }
468
- return output;
469
- }
470
- registerProcessor('algo-wasm-processor', AlgoWasmProcessor);
471
- //# sourceMappingURL=algo-worklet.js.map
1
+ var e=class extends Error{code;constructor(e,t,n){super(t,n),this.name=`AlgoWasmError`,this.code=e}},t=[`kick`,`snare`,`closed_hat`,`open_hat`,`percussion`,`bass`,`chords`,`pad`,`lead`,`arp`,`fx`];58+t.length*20;var n=10+t.length*3,r=class{exports;enginePtr=0;renderPtr=0;renderLength=0;renderView;failed=!1;constructor(t){if(this.exports=t.exports,!(this.exports.memory instanceof WebAssembly.Memory))throw new e(`wasm_load_failed`,`AlgoWASM did not export a WebAssembly memory.`)}createFromWire(t,n,r,i){this.destroy();let a=this.writeProfile(i);try{let o=this.call(`algo_wasm_engine_create`,t,n,r,a,i.length);if(o===0)throw new e(`engine_failed`,this.lastError(`AlgoWASM engine creation failed.`));this.enginePtr=o}finally{this.call(`algo_wasm_dealloc`,a,i.byteLength)}}start(){this.callEngine(`algo_wasm_engine_start`)}pause(){this.callEngine(`algo_wasm_engine_pause`)}resume(){this.callEngine(`algo_wasm_engine_resume`)}stop(){this.callEngine(`algo_wasm_engine_stop`)}setSeedParts(e,t){this.callEngine(`algo_wasm_engine_set_seed`,e,t)}setProfileWire(e){this.ensureEngine();let t=this.writeProfile(e);try{this.assertCode(this.call(`algo_wasm_engine_set_profile`,this.enginePtr,t,e.length),`Profile update failed.`)}finally{this.call(`algo_wasm_dealloc`,t,e.byteLength)}}setVolume(e){this.callEngine(`algo_wasm_engine_set_volume`,e)}setEnergy(e){this.callEngine(`algo_wasm_engine_set_energy`,e)}setIntensity(e){this.callEngine(`algo_wasm_engine_set_intensity`,e)}setMoodIndex(e){this.callEngine(`algo_wasm_engine_set_mood`,e)}setTempoRange(e,t){this.callEngine(`algo_wasm_engine_set_tempo_range`,e,t)}setMutedIndex(e,t){this.callEngine(`algo_wasm_engine_set_muted`,e,+!!t)}setSoloIndex(e,t){this.callEngine(`algo_wasm_engine_set_solo`,e,+!!t)}renderTo(e){e.set(this.render(e.length).subarray(0,e.length))}renderPlanar(e,t){let n=this.render(e.length*2);for(let r=0;r<e.length;r+=1)e[r]=n[r*2]??0,t[r]=n[r*2+1]??0}copySnapshot(){this.ensureEngine();let t=this.call(`algo_wasm_engine_snapshot`,this.enginePtr);if(t===0)throw new e(`engine_failed`,this.lastError(`Snapshot failed.`));return new Float64Array(this.memory.buffer,t,n).slice()}copyEvents(){this.ensureEngine();let e=this.call(`algo_wasm_engine_drain_events`,this.enginePtr);if(e===0)return;let t=this.call(`algo_wasm_engine_events_ptr`,this.enginePtr);return new Float64Array(this.memory.buffer,t,e*5).slice()}exportMidi(){this.ensureEngine();let e=this.call(`algo_wasm_engine_export_midi`,this.enginePtr),t=this.call(`algo_wasm_engine_midi_ptr`,this.enginePtr);return new Uint8Array(this.memory.buffer,t,e).slice()}destroy(){this.failed?(this.enginePtr=0,this.renderPtr=0):(this.renderPtr!==0&&(this.call(`algo_wasm_dealloc`,this.renderPtr,this.renderLength*4),this.renderPtr=0,this.renderLength=0,this.renderView=void 0),this.enginePtr!==0&&(this.call(`algo_wasm_engine_destroy`,this.enginePtr),this.enginePtr=0))}get memory(){return this.exports.memory}render(t){if(this.ensureEngine(),this.renderLength<t&&(this.renderPtr!==0&&this.call(`algo_wasm_dealloc`,this.renderPtr,this.renderLength*4),this.renderPtr=this.call(`algo_wasm_alloc`,t*4),this.renderLength=this.renderPtr===0?0:t,this.renderView=void 0,this.renderPtr===0))throw new e(`render_failed`,`Could not allocate the WASM render buffer.`);return this.assertCode(this.call(`algo_wasm_engine_render`,this.enginePtr,this.renderPtr,t),`Render failed.`),this.renderView&&this.renderView.buffer===this.memory.buffer||(this.renderView=new Float32Array(this.memory.buffer,this.renderPtr,this.renderLength)),this.renderView}writeProfile(t){let n=this.call(`algo_wasm_alloc`,t.byteLength);if(n===0)throw new e(`engine_failed`,`Could not allocate a WASM buffer for the profile.`);return new Float32Array(this.memory.buffer,n,t.length).set(t),n}callEngine(e,...t){this.ensureEngine(),this.assertCode(this.call(e,this.enginePtr,...t),`${e} failed.`)}assertCode(t,n){if(t!==0)throw new e(`engine_failed`,this.lastError(n))}ensureEngine(){if(this.enginePtr===0)throw new e(`engine_failed`,`AlgoWASM engine has not been created.`)}call(t,...n){if(this.failed)throw new e(`engine_failed`,`The AlgoWASM engine stopped after an internal error. Create a new player to continue.`);let r=this.exports[t];if(typeof r!=`function`)throw new e(`wasm_load_failed`,`AlgoWASM is missing the ${t} export.`);try{return r(...n)}catch(t){throw this.failed=!0,new e(`engine_failed`,`The AlgoWASM engine stopped after an internal error. Create a new player to continue.`,{cause:t})}}lastError(e){let t=this.call(`algo_wasm_last_error_len`);if(t===0)return e;let n=this.call(`algo_wasm_last_error_ptr`);return String.fromCharCode(...new Uint8Array(this.memory.buffer,n,t))}},i=1024,a=class extends AudioWorkletProcessor{engine;playing=!1;destroyed=!1;framesSincePoll=0;commandQueue=Promise.resolve();constructor(){super(),this.port.onmessage=e=>{let t=e.data;this.commandQueue=this.commandQueue.then(()=>this.handleCommand(t))}}process(e,t,n){if(this.destroyed)return!1;let r=t[0],a=r?.[0],s=r?.[1]??a;if(!a||!s)return!0;if(!this.engine||!this.playing)return a.fill(0),s.fill(0),!0;try{this.engine.renderPlanar(a,s),this.framesSincePoll+=a.length,this.framesSincePoll>=i&&(this.framesSincePoll=0,this.postEvents())}catch(e){this.playing=!1,a.fill(0),s.fill(0),this.port.postMessage({type:`error`,error:o(e)})}return!0}postEvents(){let e=this.engine?.copyEvents();if(!e||!this.engine)return;let t=this.engine.copySnapshot();this.port.postMessage({type:`events`,events:e,snapshot:t},[e.buffer,t.buffer])}async handleCommand(e){try{if(e.type===`configure`){await this.configure(e);return}let t=this.engine;if(!t){if(e.type===`destroy`){this.destroyed=!0,this.reply(e.id,!0);return}throw Error(`AlgoWASM worklet has not been configured.`)}let n=this.applyCommand(t,e),r=this.engine?t.copySnapshot():void 0;this.reply(e.id,!0,r,n)}catch(t){this.reply(e.id,!1,void 0,void 0,o(t))}}async configure(e){if(!e.wasmBytes||!e.profile)throw Error(`WASM bytes and a profile are required to configure the worklet.`);let{instance:t}=await WebAssembly.instantiate(e.wasmBytes,{}),n=new r(t);n.createFromWire(e.sampleRate??sampleRate,e.seedHigh??0,e.seedLow??1,e.profile),n.setVolume(e.value??.8),this.engine=n;let i=n.copySnapshot();this.port.postMessage({type:`ready`,snapshot:i},[i.buffer])}applyCommand(e,t){switch(t.type){case`start`:e.start(),this.playing=!0;return;case`pause`:e.pause(),this.playing=!1;return;case`resume`:e.resume(),this.playing=!0;return;case`stop`:e.stop(),this.playing=!1;return;case`set-seed`:e.setSeedParts(t.seedHigh??0,t.seedLow??1);return;case`set-profile`:if(!t.profile)throw Error(`A profile is required.`);e.setProfileWire(t.profile);return;case`set-volume`:e.setVolume(t.value??.8);return;case`set-energy`:e.setEnergy(t.value??.5);return;case`set-intensity`:e.setIntensity(t.value??.5);return;case`set-mood`:e.setMoodIndex(t.mood??0);return;case`set-tempo-range`:e.setTempoRange(t.minBpm??0,t.maxBpm??0);return;case`set-muted`:e.setMutedIndex(t.track??-1,t.flag??!1);return;case`set-solo`:e.setSoloIndex(t.track??-1,t.flag??!1);return;case`export-midi`:return e.exportMidi().buffer;case`destroy`:e.destroy(),this.engine=void 0,this.playing=!1,this.destroyed=!0;return;default:throw Error(`Unknown AlgoWASM command: ${t.type}.`)}}reply(e,t,n,r,i){e===void 0?!t&&i&&this.port.postMessage({type:`error`,error:i}):this.port.postMessage({type:`command-result`,id:e,ok:t,snapshot:n,result:r,error:i})}};function o(e){return e instanceof Error?e.message:String(e)}registerProcessor(`algo-wasm-processor`,a);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algo-wasm/algo-wasm",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A WebAssembly-first deterministic algorithmic music engine for modern browsers.",
5
5
  "type": "module",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/0xe25f/algowasm.git" },
@@ -1,8 +0,0 @@
1
- import type { AlgoWasmErrorCode } from './types.js';
2
- export declare class AlgoWasmError extends Error {
3
- readonly code: AlgoWasmErrorCode;
4
- constructor(code: AlgoWasmErrorCode, message: string, options?: {
5
- cause?: unknown;
6
- });
7
- }
8
- //# sourceMappingURL=AlgoWasmError.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AlgoWasmError.d.ts","sourceRoot":"","sources":["../src/AlgoWasmError.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAEjC,YAAY,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,EAIlF;CACF"}
@@ -1,9 +0,0 @@
1
- export class AlgoWasmError extends Error {
2
- code;
3
- constructor(code, message, options) {
4
- super(message, options);
5
- this.name = 'AlgoWasmError';
6
- this.code = code;
7
- }
8
- }
9
- //# sourceMappingURL=AlgoWasmError.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AlgoWasmError.js","sourceRoot":"","sources":["../src/AlgoWasmError.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,IAAI,CAAoB;IAEjC,YAAY,IAAuB,EAAE,OAAe,EAAE,OAA6B;QACjF,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -1,48 +0,0 @@
1
- import type { AlgoWasmEventHandler, AlgoWasmEventName, AlgoWasmGeneratedEvent, AlgoWasmMidiExportOptions, AlgoWasmMood, AlgoWasmOfflineRenderOptions, AlgoWasmPlayerOptions, AlgoWasmProfile, AlgoWasmSnapshot, AlgoWasmTrackId } from './types.js';
2
- export declare class AlgoWasmPlayer {
3
- private readonly emitter;
4
- private readonly audioContext;
5
- private readonly ownsAudioContext;
6
- private readonly node;
7
- private readonly wasmUrl;
8
- private readonly workletUrl;
9
- private readonly pending;
10
- private commandId;
11
- private destroyed;
12
- private destroyPromise?;
13
- private profile;
14
- private seed;
15
- private lastSnapshot;
16
- private lastEvents;
17
- private constructor();
18
- static create(options: AlgoWasmPlayerOptions): Promise<AlgoWasmPlayer>;
19
- start(): Promise<void>;
20
- pause(): Promise<void>;
21
- resume(): Promise<void>;
22
- stop(): Promise<void>;
23
- destroy(): Promise<void>;
24
- private performDestroy;
25
- setSeed(seed: bigint | number | string): void;
26
- setProfile(profile: AlgoWasmProfile): void;
27
- setVolume(volume: number): void;
28
- setMuted(trackId: AlgoWasmTrackId, muted: boolean): void;
29
- setSolo(trackId: AlgoWasmTrackId, solo: boolean): void;
30
- setEnergy(value: number): void;
31
- setIntensity(value: number): void;
32
- setMood(mood: AlgoWasmMood): void;
33
- setTempoRange(minBpm: number, maxBpm: number): void;
34
- getSnapshot(): AlgoWasmSnapshot;
35
- getCurrentEvents(): AlgoWasmGeneratedEvent[];
36
- exportMidi(_options?: AlgoWasmMidiExportOptions): Promise<Uint8Array>;
37
- renderOffline(options: AlgoWasmOfflineRenderOptions): Promise<Float32Array>;
38
- on<Name extends AlgoWasmEventName>(eventName: Name, handler: AlgoWasmEventHandler<Name>): () => void;
39
- off<Name extends AlgoWasmEventName>(eventName: Name, handler: AlgoWasmEventHandler<Name>): void;
40
- private configure;
41
- private postLiveCommand;
42
- private sendBestEffort;
43
- private sendCommand;
44
- private handleMessage;
45
- private updateSnapshot;
46
- private assertAlive;
47
- }
48
- //# sourceMappingURL=AlgoWasmPlayer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AlgoWasmPlayer.d.ts","sourceRoot":"","sources":["../src/AlgoWasmPlayer.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,sBAAsB,EAEtB,yBAAyB,EACzB,YAAY,EACZ,4BAA4B,EAC5B,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,eAAe,EAChB,MAAM,YAAY,CAAC;AA+BpB,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAC7D,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAC,CAAgB;IACvC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,UAAU,CAAgC;IAElD,OAAO,eAmBN;IAED,OAAa,MAAM,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC,CA8C3E;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAuB3B;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3B;IAEK,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAc5B;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAK1B;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAU7B;YAEa,cAAc;IA2B5B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5C;IAED,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAGzC;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,QAAQ,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAEvD;IAED,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAErD;IAED,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEhC;IAED,OAAO,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAEhC;IAED,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAElD;IAED,WAAW,IAAI,gBAAgB,CAE9B;IAED,gBAAgB,IAAI,sBAAsB,EAAE,CAE3C;IAEK,UAAU,CAAC,QAAQ,GAAE,yBAA8B,GAAG,OAAO,CAAC,UAAU,CAAC,CAI9E;IAEK,aAAa,CAAC,OAAO,EAAE,4BAA4B,GAAG,OAAO,CAAC,YAAY,CAAC,CA4BhF;IAED,EAAE,CAAC,IAAI,SAAS,iBAAiB,EAC/B,SAAS,EAAE,IAAI,EACf,OAAO,EAAE,oBAAoB,CAAC,IAAI,CAAC,GAClC,MAAM,IAAI,CAEZ;IAED,GAAG,CAAC,IAAI,SAAS,iBAAiB,EAChC,SAAS,EAAE,IAAI,EACf,OAAO,EAAE,oBAAoB,CAAC,IAAI,CAAC,GAClC,IAAI,CAEN;YAEa,SAAS;IA8CvB,OAAO,CAAC,eAAe;YAUT,cAAc;IAQ5B,OAAO,CAAC,WAAW;IAuBnB,OAAO,CAAC,aAAa;IAqDrB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,WAAW;CAKpB"}