@swiftbrowser/native 0.23.0 → 0.26.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftbrowser/native",
3
- "version": "0.23.0",
3
+ "version": "0.26.0",
4
4
  "type": "module",
5
5
  "description": "Run a @swiftbrowser/react app natively: the runtime a JavaScriptCore-hosted app needs (timers, console, fetch and the other web globals over the SwiftBrowser Native bridge), the `run` entry that renders into the native SwiftUI renderer, and the bundler that builds app.js for the Xcode target.",
6
6
  "repository": {
@@ -63,8 +63,8 @@
63
63
  },
64
64
  "dependencies": {
65
65
  "@babel/core": "^7.29.7",
66
- "@swiftbrowser/react": "0.23.0",
67
- "@swiftbrowser/web": "0.23.0",
66
+ "@swiftbrowser/react": "0.26.0",
67
+ "@swiftbrowser/web": "0.26.0",
68
68
  "esbuild": "^0.28.2",
69
69
  "react-refresh": "^0.17.0"
70
70
  },
@@ -42,7 +42,9 @@ import Foundation
42
42
  /// Block processing: a block runs in chunks of 16 frames, each instruction over every frame of the chunk for every
43
43
  /// sounding lane, but for a stream's serial part (feedback through state, which it schedules when it loads the
44
44
  /// program), which runs frame by frame within the chunk. Unlike the JavaScript machine's, a register holds a frame's lanes side by side,
45
- /// then the next frame's, so the loops over lanes stay vector loops; the results are the same.
45
+ /// then the next frame's, so the loops over lanes stay vector loops; the results are the same. The lanes side by side are
46
+ /// those that run: the sounding voices' (up to the highest), or the master stage's one, so every instruction runs over
47
+ /// one span of memory, however many voices sound.
46
48
  public final class DSPMachine: @unchecked Sendable {
47
49
  public let program: DSPProgram
48
50
  public let sampleRate: Float
@@ -50,8 +52,8 @@ public final class DSPMachine: @unchecked Sendable {
50
52
  private static let lanes = 16
51
53
  /// Frames a chunk has: each instruction runs over a chunk's frames together (block processing).
52
54
  private static let frames = 16
53
- /// The registers, each `width` values: a chunk's frames one after another, each its lanes (as many as the
54
- /// program has voices) side by side, so an instruction runs over lanes as a SIMD vector and a builtin's state
55
+ /// The registers, each `width` values: a chunk's frames one after another, each its lanes (as many as run: the
56
+ /// sounding voices', or the master stage's one) side by side, so an instruction runs over lanes as a SIMD vector and a builtin's state
55
57
  /// follows its lanes; a state register's value is its lane's at the first frame.
56
58
  private let registers: UnsafeMutablePointer<Float>
57
59
  private let width: Int
@@ -709,6 +711,8 @@ public final class DSPMachine: @unchecked Sendable {
709
711
  guard renamedMask[r] == 0, k % 2 == 0 || parameterMask[r] != 0 else { return false }
710
712
  }
711
713
  let start = index * DSPProgram.routesMax
714
+ // The same list again: nothing to make again.
715
+ if routeCounts[index] == list.count && (0..<list.count).allSatisfy({ routeRegisters[start + $0] == Int(list.pointer[$0]) * width }) { return true }
712
716
  for k in 0..<list.count {
713
717
  let r = Int(list.pointer[k])
714
718
  routeRegisters[start + k] = r * width
@@ -1393,6 +1397,8 @@ public final class DSPMachine: @unchecked Sendable {
1393
1397
  resetLane(l)
1394
1398
  }
1395
1399
  resetMaster()
1400
+ // Nothing sounds: what is set before the next block jumps there, with no glide (a preset loaded as data).
1401
+ snapped = true
1396
1402
  default:
1397
1403
  break
1398
1404
  }
@@ -1529,7 +1535,8 @@ public final class DSPMachine: @unchecked Sendable {
1529
1535
  return
1530
1536
  }
1531
1537
  if any {
1532
- let V = program.voices
1538
+ // A frame's lanes side by side, as many as sound this block (`run`).
1539
+ let V = lanes
1533
1540
  for l in 0..<V {
1534
1541
  let note = laneNote[l]
1535
1542
  let freq = note >= 0 ? Float(440 * Foundation.pow(2, (Double(note) - 69 + bend) / 12)) : 0
@@ -1552,19 +1559,20 @@ public final class DSPMachine: @unchecked Sendable {
1552
1559
  l >= 0 ? laneVelocity[l] : 0,
1553
1560
  note >= 0 ? Float(note) : 0,
1554
1561
  ]
1555
- let V = program.voices
1556
- for (i, value) in values.enumerated() { for k in 0..<F { R[(7 + i) * W + k * V] = value } }
1562
+ // The master stage's one lane: a frame after another.
1563
+ for (i, value) in values.enumerated() { for k in 0..<F { R[(7 + i) * W + k] = value } }
1557
1564
  }
1558
1565
  let o = R + program.output * W
1559
1566
  let or = R + (program.outputRight ?? program.output) * W
1560
- let V = program.voices
1567
+ let V = Swift.max(lanes, 1)
1561
1568
  var check: Float = 0
1562
1569
  // A chunk of up to 16 frames at a time: each instruction over all of them, the serial part frame by frame.
1563
1570
  var chunk = 0
1564
1571
  while chunk < n {
1565
1572
  let fc = Swift.min(F, n - chunk)
1573
+ let start = beat
1566
1574
  for k in 0..<fc {
1567
- for l in 0..<Swift.max(lanes, 1) { R[6 * W + k * V + l] = Float(beat) }
1575
+ for l in 0..<V { R[6 * W + k * V + l] = Float(beat) }
1568
1576
  beat += beatStep
1569
1577
  }
1570
1578
  if any {
@@ -1585,8 +1593,8 @@ public final class DSPMachine: @unchecked Sendable {
1585
1593
  }
1586
1594
  }
1587
1595
  if let master {
1588
- R[master.inputs[0] * W + k * V] = leftSum
1589
- R[master.inputs[1] * W + k * V] = rightSum
1596
+ R[master.inputs[0] * W + k] = leftSum
1597
+ R[master.inputs[1] * W + k] = rightSum
1590
1598
  } else {
1591
1599
  if let right {
1592
1600
  out[chunk + k] = leftSum
@@ -1598,14 +1606,20 @@ public final class DSPMachine: @unchecked Sendable {
1598
1606
  }
1599
1607
  }
1600
1608
  if let master {
1609
+ // The beat in the master stage's one lane: a frame after another, added up as the voices' was.
1610
+ var at = start
1611
+ for k in 0..<fc {
1612
+ R[6 * W + k] = Float(at)
1613
+ at += beatStep
1614
+ }
1601
1615
  run(masterCode, 0..<masterSerial.lowerBound, lanes: 1, trigger: masterTrigger, frames: fc)
1602
1616
  if !masterSerial.isEmpty {
1603
1617
  for k in 0..<fc { run(masterCode, masterSerial, lanes: 1, trigger: masterTrigger, frames: 1, frame: k) }
1604
1618
  }
1605
1619
  run(masterCode, masterSerial.upperBound..<masterCount, lanes: 1, trigger: masterTrigger, frames: fc)
1606
1620
  for k in 0..<fc {
1607
- let leftSum = R[master.outputs[0] * W + k * V]
1608
- let rightSum = R[master.outputs[1] * W + k * V]
1621
+ let leftSum = R[master.outputs[0] * W + k]
1622
+ let rightSum = R[master.outputs[1] * W + k]
1609
1623
  if let right {
1610
1624
  out[chunk + k] = leftSum
1611
1625
  right[chunk + k] = rightSum
@@ -1675,7 +1689,9 @@ public final class DSPMachine: @unchecked Sendable {
1675
1689
  let sr = sampleRate
1676
1690
  let serial = frame >= 0
1677
1691
  let fc = serial ? 1 : frames
1678
- let V = program.voices
1692
+ // A frame's lanes side by side, as many as run (the sounding voices', or the master stage's one): every
1693
+ // instruction then runs over one span of memory.
1694
+ let V = Swift.max(lanes, 1)
1679
1695
  let shift = serial ? frame * V : 0
1680
1696
  // A stateless instruction: every lane's frames at once (the chunk's last ones, past fc, go unread), the
1681
1697
  // sounding lanes of each frame; in the serial part, the one frame's.