supersonic-scsynth 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2098,6 +2098,12 @@ var SuperSonic = class _SuperSonic {
2098
2098
  this._handleBufferFreed(msg.args);
2099
2099
  } else if (msg.address === "/buffer/allocated") {
2100
2100
  this._handleBufferAllocated(msg.args);
2101
+ } else if (msg.address === "/synced" && msg.args.length > 0) {
2102
+ const syncId = msg.args[0];
2103
+ if (this._syncListeners && this._syncListeners.has(syncId)) {
2104
+ const listener = this._syncListeners.get(syncId);
2105
+ listener(msg);
2106
+ }
2101
2107
  }
2102
2108
  if (this.onMessage) {
2103
2109
  this.stats.messagesReceived++;
@@ -2512,7 +2518,7 @@ var SuperSonic = class _SuperSonic {
2512
2518
  if (synthName) {
2513
2519
  this.loadedSynthDefs.add(synthName);
2514
2520
  }
2515
- console.log(`[SuperSonic] Loaded synthdef from ${path} (${synthdefData.length} bytes)`);
2521
+ console.log(`[SuperSonic] Sent synthdef from ${path} (${synthdefData.length} bytes)`);
2516
2522
  } catch (error) {
2517
2523
  console.error("[SuperSonic] Failed to load synthdef:", error);
2518
2524
  throw error;
@@ -2548,9 +2554,45 @@ var SuperSonic = class _SuperSonic {
2548
2554
  })
2549
2555
  );
2550
2556
  const successCount = Object.values(results).filter((r) => r.success).length;
2551
- console.log(`[SuperSonic] Loaded ${successCount}/${names.length} synthdefs`);
2557
+ console.log(`[SuperSonic] Sent ${successCount}/${names.length} synthdef loads`);
2552
2558
  return results;
2553
2559
  }
2560
+ /**
2561
+ * Send /sync command and wait for /synced response
2562
+ * Use this to ensure all previous asynchronous commands have completed
2563
+ * @param {number} syncId - Unique integer identifier for this sync operation
2564
+ * @returns {Promise<void>}
2565
+ * @example
2566
+ * await sonic.loadSynthDefs(['synth1', 'synth2']);
2567
+ * await sonic.sync(12345); // Wait for all synthdefs to be processed
2568
+ */
2569
+ async sync(syncId) {
2570
+ if (!this.initialized) {
2571
+ throw new Error("SuperSonic not initialized. Call init() first.");
2572
+ }
2573
+ if (!Number.isInteger(syncId)) {
2574
+ throw new Error("sync() requires an integer syncId parameter");
2575
+ }
2576
+ const syncPromise = new Promise((resolve, reject) => {
2577
+ const timeout = setTimeout(() => {
2578
+ if (this._syncListeners) {
2579
+ this._syncListeners.delete(syncId);
2580
+ }
2581
+ reject(new Error("Timeout waiting for /synced response"));
2582
+ }, 1e4);
2583
+ const messageHandler = (msg) => {
2584
+ clearTimeout(timeout);
2585
+ this._syncListeners.delete(syncId);
2586
+ resolve();
2587
+ };
2588
+ if (!this._syncListeners) {
2589
+ this._syncListeners = /* @__PURE__ */ new Map();
2590
+ }
2591
+ this._syncListeners.set(syncId, messageHandler);
2592
+ });
2593
+ await this.send("/sync", syncId);
2594
+ await syncPromise;
2595
+ }
2554
2596
  /**
2555
2597
  * Allocate memory for an audio buffer (includes guard samples)
2556
2598
  * @param {number} numSamples - Number of Float32 samples to allocate
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "wasmFile": "scsynth-nrt.wasm",
3
- "buildId": "20251117-205541",
4
- "buildTime": "2025-11-17T20:55:41Z",
5
- "gitHash": "264e2ae"
3
+ "buildId": "20251118-093603",
4
+ "buildTime": "2025-11-18T09:36:03Z",
5
+ "gitHash": "d43c4be"
6
6
  }
Binary file
@@ -171,40 +171,55 @@ function writeToRingBuffer(oscMessage, isRetry) {
171
171
  return false;
172
172
  }
173
173
 
174
- // Check if message fits contiguously
174
+ // ringbuf.js approach: split writes across wrap boundary
175
+ // No padding markers - just split the write into two parts if it wraps
176
+
175
177
  var spaceToEnd = bufferConstants.IN_BUFFER_SIZE - head;
176
178
 
177
179
  if (totalSize > spaceToEnd) {
180
+ // Message will wrap - write in two parts
181
+ // Create header as byte array to simplify split writes
182
+ var headerBytes = new Uint8Array(bufferConstants.MESSAGE_HEADER_SIZE);
183
+ var headerView = new DataView(headerBytes.buffer);
184
+ headerView.setUint32(0, bufferConstants.MESSAGE_MAGIC, true);
185
+ headerView.setUint32(4, totalSize, true);
186
+ headerView.setUint32(8, stats.bundlesWritten, true);
187
+ headerView.setUint32(12, 0, true);
188
+
189
+ var writePos1 = ringBufferBase + bufferConstants.IN_BUFFER_START + head;
190
+ var writePos2 = ringBufferBase + bufferConstants.IN_BUFFER_START;
191
+
192
+ // Write header (may be split)
178
193
  if (spaceToEnd >= bufferConstants.MESSAGE_HEADER_SIZE) {
179
- // Write padding marker and wrap
180
- var paddingPos = ringBufferBase + bufferConstants.IN_BUFFER_START + head;
181
- dataView.setUint32(paddingPos, bufferConstants.PADDING_MAGIC, true);
182
- dataView.setUint32(paddingPos + 4, 0, true);
183
- dataView.setUint32(paddingPos + 8, 0, true);
184
- dataView.setUint32(paddingPos + 12, 0, true);
185
- } else if (spaceToEnd > 0) {
186
- // Not enough room for a padding header - clear remaining bytes
187
- var padStart = ringBufferBase + bufferConstants.IN_BUFFER_START + head;
188
- for (var i = 0; i < spaceToEnd; i++) {
189
- uint8View[padStart + i] = 0;
190
- }
191
- }
194
+ // Header fits contiguously
195
+ uint8View.set(headerBytes, writePos1);
192
196
 
193
- // Wrap to beginning
194
- head = 0;
195
- }
197
+ // Write payload (split across boundary)
198
+ var payloadBytesInFirstPart = spaceToEnd - bufferConstants.MESSAGE_HEADER_SIZE;
199
+ uint8View.set(oscMessage.subarray(0, payloadBytesInFirstPart), writePos1 + bufferConstants.MESSAGE_HEADER_SIZE);
200
+ uint8View.set(oscMessage.subarray(payloadBytesInFirstPart), writePos2);
201
+ } else {
202
+ // Header is split across boundary
203
+ uint8View.set(headerBytes.subarray(0, spaceToEnd), writePos1);
204
+ uint8View.set(headerBytes.subarray(spaceToEnd), writePos2);
196
205
 
197
- // Write message
198
- var writePos = ringBufferBase + bufferConstants.IN_BUFFER_START + head;
206
+ // All payload goes at beginning
207
+ var payloadOffset = bufferConstants.MESSAGE_HEADER_SIZE - spaceToEnd;
208
+ uint8View.set(oscMessage, writePos2 + payloadOffset);
209
+ }
210
+ } else {
211
+ // Message fits contiguously - write normally
212
+ var writePos = ringBufferBase + bufferConstants.IN_BUFFER_START + head;
199
213
 
200
- // Write header
201
- dataView.setUint32(writePos, bufferConstants.MESSAGE_MAGIC, true);
202
- dataView.setUint32(writePos + 4, totalSize, true);
203
- dataView.setUint32(writePos + 8, stats.bundlesWritten, true); // sequence
204
- dataView.setUint32(writePos + 12, 0, true); // padding
214
+ // Write header
215
+ dataView.setUint32(writePos, bufferConstants.MESSAGE_MAGIC, true);
216
+ dataView.setUint32(writePos + 4, totalSize, true);
217
+ dataView.setUint32(writePos + 8, stats.bundlesWritten, true);
218
+ dataView.setUint32(writePos + 12, 0, true);
205
219
 
206
- // Write payload
207
- uint8View.set(oscMessage, writePos + bufferConstants.MESSAGE_HEADER_SIZE);
220
+ // Write payload
221
+ uint8View.set(oscMessage, writePos + bufferConstants.MESSAGE_HEADER_SIZE);
222
+ }
208
223
 
209
224
  // Diagnostic: Log first few writes
210
225
  if (stats.bundlesWritten < 5) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supersonic-scsynth",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "SuperCollider scsynth WebAssembly port for AudioWorklet - Run SuperCollider synthesis in the browser",
5
5
  "main": "dist/supersonic.js",
6
6
  "unpkg": "dist/supersonic.js",