@swiftbrowser/native 0.21.0 → 0.23.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 +3 -3
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSP/ProgramNode.swift +63 -5
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSPCore/Machine.swift +950 -126
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSPCore/Program.swift +137 -41
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Controls.swift +28 -6
- package/swift/Sources/SwiftBrowserProtocol/TextChange.swift +17 -0
- package/wasm/SwiftBrowserNative.wasm +2 -2
|
@@ -40,8 +40,8 @@ import Foundation
|
|
|
40
40
|
/// reset only in `init` and on `reset`.
|
|
41
41
|
///
|
|
42
42
|
/// Block processing: a block runs in chunks of 16 frames, each instruction over every frame of the chunk for every
|
|
43
|
-
/// sounding lane, but for a stream's serial part (feedback through state, which
|
|
44
|
-
/// frame by frame within the chunk. Unlike the JavaScript machine's, a register holds a frame's lanes side by side,
|
|
43
|
+
/// sounding lane, but for a stream's serial part (feedback through state, which it schedules when it loads the
|
|
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
45
|
/// then the next frame's, so the loops over lanes stay vector loops; the results are the same.
|
|
46
46
|
public final class DSPMachine: @unchecked Sendable {
|
|
47
47
|
public let program: DSPProgram
|
|
@@ -59,15 +59,77 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
59
59
|
private let stateCount: Int
|
|
60
60
|
private let noise: UnsafeMutablePointer<UInt32>
|
|
61
61
|
private let noiseCount: Int
|
|
62
|
+
/// The streams as they run: what reads a gated region's result reading what the region gives it, `sample` and
|
|
63
|
+
/// `master` without the regions that are off, and scheduled (their parts that run frame by frame, in instructions).
|
|
62
64
|
private let blockCode: UnsafeMutablePointer<Int32>
|
|
63
65
|
private let blockCount: Int
|
|
66
|
+
/// How many of `blockCode`'s instructions run (those the streams were last made with).
|
|
67
|
+
private var blockRunning: Int
|
|
64
68
|
private let sampleCode: UnsafeMutablePointer<Int32>
|
|
65
|
-
private
|
|
69
|
+
private var sampleCount: Int
|
|
66
70
|
private let masterCode: UnsafeMutablePointer<Int32>
|
|
67
|
-
private
|
|
68
|
-
|
|
69
|
-
private
|
|
70
|
-
|
|
71
|
+
private var masterCount: Int
|
|
72
|
+
private var sampleSerial: Range<Int> = 0..<0
|
|
73
|
+
private var masterSerial: Range<Int> = 0..<0
|
|
74
|
+
/// The streams as the program has them, each instruction's region, each region's parameter, parent and results
|
|
75
|
+
/// (register, then what it reads as on and off, as offsets), whether each is on (2 before the program loads), and
|
|
76
|
+
/// per register what a result reads as now (-1 for a register that is none).
|
|
77
|
+
private let fullBlock: UnsafeMutablePointer<Int32>
|
|
78
|
+
private let fullSample: UnsafeMutablePointer<Int32>
|
|
79
|
+
private let fullSampleCount: Int
|
|
80
|
+
private let fullMaster: UnsafeMutablePointer<Int32>
|
|
81
|
+
private let fullMasterCount: Int
|
|
82
|
+
private let sampleGates: [Int32]?
|
|
83
|
+
private let masterGates: [Int32]?
|
|
84
|
+
private let groupParameters: [Int]
|
|
85
|
+
private let groupParents: [Int]
|
|
86
|
+
private let groupOutputs: [[(register: Int, on: Int32, off: Int32)]]
|
|
87
|
+
private var groupOn: [UInt8]
|
|
88
|
+
/// What runs: per instruction of the block, sample and master streams as the program has them, whether something
|
|
89
|
+
/// the outputs need reads it, now and when the streams were last made; per register, whether it is read, now and
|
|
90
|
+
/// then; whether the streams have been made; and each instruction's builtin (-1 for none). An instruction that comes
|
|
91
|
+
/// to run again starts over (its builtin, the state it writes), as one in a region coming on does.
|
|
92
|
+
private let aliveNow: [UnsafeMutablePointer<UInt8>]
|
|
93
|
+
private let aliveBefore: [UnsafeMutablePointer<UInt8>]
|
|
94
|
+
private let readNow: UnsafeMutablePointer<UInt8>
|
|
95
|
+
private let readBefore: UnsafeMutablePointer<UInt8>
|
|
96
|
+
private var made = false
|
|
97
|
+
private let streamInstances: [[Int32]]
|
|
98
|
+
private let streamCounts: [Int]
|
|
99
|
+
private let renameTable: UnsafeMutablePointer<Int32>
|
|
100
|
+
/// Scratch for making a stream again: its instructions before they are scheduled, a byte an instruction (touches
|
|
101
|
+
/// state, computed from it, needed by it) and a byte a register (tainted, needed).
|
|
102
|
+
private let staging: UnsafeMutablePointer<Int32>
|
|
103
|
+
private let instructionMarks: UnsafeMutablePointer<UInt8>
|
|
104
|
+
private let registerMarks: UnsafeMutablePointer<UInt8>
|
|
105
|
+
/// Choices: each one's parameter, register and options (as offsets); the option each reads as now (the last
|
|
106
|
+
/// choices with no cycle), the ones last asked for (so choices refused for a cycle are not tried every block),
|
|
107
|
+
/// and the ones asked for this block.
|
|
108
|
+
private let choiceParameters: [Int]
|
|
109
|
+
private let choiceRegisters: [Int]
|
|
110
|
+
private let choiceOptions: [[Int32]]
|
|
111
|
+
private let choiceScales: [Double]
|
|
112
|
+
private var choiceUsed: [Int]
|
|
113
|
+
private var choiceAsked: [Int]
|
|
114
|
+
private var choiceNow: [Int]
|
|
115
|
+
/// The voicing's parameters (nil: the program's own), and the voices and mode last played with (-1 before the
|
|
116
|
+
/// first block), so a turn lets go of what no longer plays.
|
|
117
|
+
private let voicesParameter: Int?
|
|
118
|
+
private let modeParameter: Int?
|
|
119
|
+
private var voicedLimit = -1
|
|
120
|
+
private var voicedMode = 0
|
|
121
|
+
/// Scratch for ordering a stream when choices change (made only when there are choices): the ordered sample and
|
|
122
|
+
/// master streams; per register its writer (not state) or the first of its writers (state, then linked by
|
|
123
|
+
/// instruction) and where its readers start in `readers`; per instruction how many it waits on; and a heap.
|
|
124
|
+
private let orderedSample: UnsafeMutablePointer<Int32>
|
|
125
|
+
private let orderedMaster: UnsafeMutablePointer<Int32>
|
|
126
|
+
private let writerOf: UnsafeMutablePointer<Int32>
|
|
127
|
+
private let stateWriterFirst: UnsafeMutablePointer<Int32>
|
|
128
|
+
private let stateWriterNext: UnsafeMutablePointer<Int32>
|
|
129
|
+
private let readerStarts: UnsafeMutablePointer<Int32>
|
|
130
|
+
private let readers: UnsafeMutablePointer<Int32>
|
|
131
|
+
private let waiting: UnsafeMutablePointer<Int32>
|
|
132
|
+
private let heap: UnsafeMutablePointer<Int32>
|
|
71
133
|
/// Per instance: whether the master stage runs it.
|
|
72
134
|
private let instanceMaster: [Bool]
|
|
73
135
|
/// Each instance's builtin and where its state starts (in floats, lanes included; a noise instance's in `noise`).
|
|
@@ -80,36 +142,60 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
80
142
|
private let parameterIndices: [String: Int]
|
|
81
143
|
private let targets: UnsafeMutablePointer<Float>
|
|
82
144
|
private var snapped = true
|
|
83
|
-
///
|
|
84
|
-
|
|
85
|
-
|
|
145
|
+
/// Whether a parameter was set since the last block: only then can a region, a choice or the voicing turn.
|
|
146
|
+
private var retargeted = false
|
|
147
|
+
/// Each wavetable's samples (level, 2048 samples, each its frames side by side), how many floats, and its frames;
|
|
148
|
+
/// each its own, so one is replaced (`replaceTable`) by swapping it whole. (`Slots`, not arrays replaced in place,
|
|
149
|
+
/// which Swift would check for exclusive access at every read as it renders.)
|
|
150
|
+
private let tablePointers: Slots<UnsafeMutablePointer<Float>>
|
|
151
|
+
private let tableSizes: Slots<Int>
|
|
152
|
+
private let tableFrames: Slots<Int>
|
|
86
153
|
private let tableCount: Int
|
|
87
|
-
/// Envelopes' curves
|
|
88
|
-
|
|
89
|
-
private let
|
|
90
|
-
private let tableFrames: [Int]
|
|
154
|
+
/// Envelopes' curves: the registers (as offsets) their attack, decay and release exponents are read from, three a
|
|
155
|
+
/// curve; an `adsr` names its curve in slot 11.
|
|
156
|
+
private let curveRegisters: [Int]
|
|
91
157
|
/// The delays' samples: each instance's buffer per lane, a power of two long; by instance, where it starts and its length.
|
|
92
158
|
private let delaySamples: UnsafeMutablePointer<Float>
|
|
93
159
|
private let delaySampleCount: Int
|
|
94
160
|
private let delayBases: [Int]
|
|
95
161
|
private let delayLengths: [Int]
|
|
96
|
-
///
|
|
97
|
-
|
|
98
|
-
private let
|
|
99
|
-
private let shapeLengths:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
private let
|
|
103
|
-
private let
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
/// `modsum`'s route lists one after another, as register offsets (× `width`): a source's, then its amount's, pair
|
|
108
|
-
/// after pair; list i is `routeStarts[i] ..< routeStarts[i + 1]`.
|
|
162
|
+
/// Each shape's points and how many; each sample's audio, its length, its frames a frame at this rate, and its
|
|
163
|
+
/// spray (20 ms) in frames. Each its own, so one is replaced by swapping it whole.
|
|
164
|
+
private let shapePointers: Slots<UnsafeMutablePointer<Float>>
|
|
165
|
+
private let shapeLengths: Slots<Int>
|
|
166
|
+
private let samplePointers: Slots<UnsafeMutablePointer<Float>>
|
|
167
|
+
private let sampleLengths: Slots<Int>
|
|
168
|
+
private let sampleRatios: Slots<Float>
|
|
169
|
+
private let sampleSprays: Slots<UInt32>
|
|
170
|
+
/// `modsum`'s route lists, each with room for `DSPProgram.routesMax` registers (so one is replaced in place), as
|
|
171
|
+
/// register offsets (× `width`): a source's, then its amount's, pair after pair; list i starts at
|
|
172
|
+
/// `i × routesMax` and is `routeCounts[i]` long.
|
|
109
173
|
private let routeRegisters: UnsafeMutablePointer<Int>
|
|
110
174
|
/// Whether each route's register is state (one value a lane, not one a frame).
|
|
111
175
|
private let routeState: UnsafeMutablePointer<Bool>
|
|
112
|
-
private let
|
|
176
|
+
private let routeCounts: UnsafeMutablePointer<Int>
|
|
177
|
+
private let routeCount: Int
|
|
178
|
+
/// Whether a route list was replaced since the last block: the streams are made again.
|
|
179
|
+
private var rerouted = false
|
|
180
|
+
/// Per register: whether it is a parameter, state, or one a machine renames (a choice's, a region's result).
|
|
181
|
+
private let parameterMask: UnsafeMutablePointer<UInt8>
|
|
182
|
+
private let stateMask: UnsafeMutablePointer<UInt8>
|
|
183
|
+
private let renamedMask: UnsafeMutablePointer<UInt8>
|
|
184
|
+
/// Folding: each register's value where it is a constant (NaN where not), an offset holding 0 (-1 for none), the
|
|
185
|
+
/// registers nothing may fold away (outputs, state, curves, the master stage's inputs) and those a route list names
|
|
186
|
+
/// now, and the registers folded when the streams were last made (their renaming undone before the next).
|
|
187
|
+
private let constantValues: UnsafeMutablePointer<Float>
|
|
188
|
+
/// Each parameter's range, by its register (NaN elsewhere): a clamp it is inside folds away.
|
|
189
|
+
private let parameterLowest: UnsafeMutablePointer<Float>
|
|
190
|
+
private let parameterHighest: UnsafeMutablePointer<Float>
|
|
191
|
+
private let zeroOffset: Int32
|
|
192
|
+
private let pinnedMask: UnsafeMutablePointer<UInt8>
|
|
193
|
+
private let routedMask: UnsafeMutablePointer<UInt8>
|
|
194
|
+
private let folded: UnsafeMutablePointer<Int32>
|
|
195
|
+
private var foldedCount = 0
|
|
196
|
+
/// Re-rating: per register whether it may change from frame to frame, and the sample stream's instructions kept.
|
|
197
|
+
private let changing: UnsafeMutablePointer<UInt8>
|
|
198
|
+
private let keptSample: UnsafeMutablePointer<Int32>
|
|
113
199
|
|
|
114
200
|
private let laneNote: UnsafeMutablePointer<Int32>
|
|
115
201
|
private let laneGate: UnsafeMutablePointer<Float>
|
|
@@ -185,22 +271,68 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
185
271
|
noiseCount = max(1, noises) * L
|
|
186
272
|
noise = .allocate(capacity: noiseCount)
|
|
187
273
|
noise.initialize(repeating: 1, count: noiseCount)
|
|
188
|
-
(
|
|
274
|
+
let tables = program.tables.isEmpty ? [(Self.zeros(DSPProgram.tableLevels * DSPProgram.tableFrame), DSPProgram.tableLevels * DSPProgram.tableFrame)] : program.tables.map { Self.levels(frames: $0.frames, cosines: $0.cosines) }
|
|
275
|
+
tablePointers = Slots(tables.map(\.0))
|
|
276
|
+
tableSizes = Slots(tables.map(\.1))
|
|
277
|
+
tableFrames = Slots(program.tables.isEmpty ? [1] : program.tables.map(\.frames.count))
|
|
189
278
|
tableCount = program.tables.count
|
|
190
|
-
|
|
191
|
-
(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
279
|
+
shapePointers = Slots(program.shapes.isEmpty ? [Self.zeros(2)] : program.shapes.map { Self.copy($0.points) })
|
|
280
|
+
shapeLengths = Slots(program.shapes.isEmpty ? [2] : program.shapes.map(\.points.count))
|
|
281
|
+
samplePointers = Slots(program.samples.isEmpty ? [Self.zeros(2)] : program.samples.map { Self.copy($0.data) })
|
|
282
|
+
sampleLengths = Slots(program.samples.isEmpty ? [2] : program.samples.map(\.data.count))
|
|
283
|
+
sampleRatios = Slots(program.samples.isEmpty ? [1] : program.samples.map { Float($0.rate / Double(Float(sampleRate))) })
|
|
284
|
+
let routesMax = DSPProgram.routesMax
|
|
285
|
+
routeCount = program.routes.count
|
|
286
|
+
routeCounts = .allocate(capacity: Swift.max(1, routeCount))
|
|
287
|
+
routeCounts.initialize(repeating: 0, count: Swift.max(1, routeCount))
|
|
288
|
+
for (i, list) in program.routes.enumerated() { routeCounts[i] = list.count }
|
|
289
|
+
routeRegisters = .allocate(capacity: Swift.max(1, routeCount * routesMax))
|
|
290
|
+
routeRegisters.initialize(repeating: 0, count: Swift.max(1, routeCount * routesMax))
|
|
291
|
+
routeState = .allocate(capacity: Swift.max(1, routeCount * routesMax))
|
|
292
|
+
routeState.initialize(repeating: false, count: Swift.max(1, routeCount * routesMax))
|
|
197
293
|
for (i, list) in program.routes.enumerated() {
|
|
198
294
|
for (k, r) in list.enumerated() {
|
|
199
|
-
routeRegisters[
|
|
200
|
-
routeState[
|
|
295
|
+
routeRegisters[i * routesMax + k] = r * W
|
|
296
|
+
routeState[i * routesMax + k] = stateRegisters.contains(r)
|
|
201
297
|
}
|
|
202
298
|
}
|
|
203
|
-
|
|
299
|
+
let registerCount = program.registers
|
|
300
|
+
func mask() -> UnsafeMutablePointer<UInt8> {
|
|
301
|
+
let pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: registerCount)
|
|
302
|
+
pointer.initialize(repeating: 0, count: registerCount)
|
|
303
|
+
return pointer
|
|
304
|
+
}
|
|
305
|
+
parameterMask = mask()
|
|
306
|
+
stateMask = mask()
|
|
307
|
+
renamedMask = mask()
|
|
308
|
+
pinnedMask = mask()
|
|
309
|
+
routedMask = mask()
|
|
310
|
+
changing = mask()
|
|
311
|
+
for p in program.parameters { parameterMask[p.register] = 1 }
|
|
312
|
+
for r in stateRegisters { stateMask[r] = 1 }
|
|
313
|
+
for c in program.choices { renamedMask[c.register] = 1 }
|
|
314
|
+
for g in program.groups { for o in g.outputs { renamedMask[o.register] = 1 } }
|
|
315
|
+
for r in [program.output, program.outputRight ?? program.output] + (program.master?.outputs ?? []) + (program.master?.inputs ?? []) { pinnedMask[r] = 1 }
|
|
316
|
+
for r in stateRegisters { pinnedMask[r] = 1 }
|
|
317
|
+
for c in program.curves { for r in [c.attack, c.decay, c.release] { pinnedMask[r] = 1 } }
|
|
318
|
+
for list in program.routes { for r in list { routedMask[r] = 1 } }
|
|
319
|
+
constantValues = .allocate(capacity: registerCount)
|
|
320
|
+
constantValues.initialize(repeating: .nan, count: registerCount)
|
|
321
|
+
parameterLowest = .allocate(capacity: registerCount)
|
|
322
|
+
parameterLowest.initialize(repeating: .nan, count: registerCount)
|
|
323
|
+
parameterHighest = .allocate(capacity: registerCount)
|
|
324
|
+
parameterHighest.initialize(repeating: .nan, count: registerCount)
|
|
325
|
+
for p in program.parameters {
|
|
326
|
+
parameterLowest[p.register] = p.min
|
|
327
|
+
parameterHighest[p.register] = p.max
|
|
328
|
+
}
|
|
329
|
+
var zero: Int32 = -1
|
|
330
|
+
for constant in program.constants {
|
|
331
|
+
constantValues[constant.register] = constant.value
|
|
332
|
+
if constant.value == 0 && zero < 0 { zero = Int32(constant.register * W) }
|
|
333
|
+
}
|
|
334
|
+
zeroOffset = zero
|
|
335
|
+
sampleSprays = Slots(program.samples.isEmpty ? [1] : program.samples.map { UInt32(Swift.max(1, ($0.rate * 0.02).rounded(.down))) })
|
|
204
336
|
// Each delay's buffer: its longest time (a constant register) at this sample rate, rounded up to a power of
|
|
205
337
|
// two, per voice (the master stage's, once).
|
|
206
338
|
let constantValues = Dictionary(program.constants.map { ($0.register, $0.value) }, uniquingKeysWith: { _, last in last })
|
|
@@ -228,13 +360,95 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
228
360
|
delaySampleCount = Swift.max(1, delayTotal)
|
|
229
361
|
delaySamples = .allocate(capacity: delaySampleCount)
|
|
230
362
|
delaySamples.initialize(repeating: 0, count: delaySampleCount)
|
|
231
|
-
|
|
363
|
+
curveRegisters = program.curves.flatMap { [$0.attack * W, $0.decay * W, $0.release * W] }
|
|
232
364
|
let curves = Dictionary(program.curves.enumerated().map { ($1.instance, $0 + 1) }, uniquingKeysWith: { _, last in last })
|
|
233
|
-
(
|
|
234
|
-
(
|
|
235
|
-
|
|
236
|
-
(
|
|
237
|
-
|
|
365
|
+
(fullBlock, blockCount) = Self.decode(program.block, width: W, state: stateRegisters, offsets: offsets, delays: (bases, lengths), curves: curves)
|
|
366
|
+
(fullSample, fullSampleCount) = Self.decode(program.sample, width: W, state: stateRegisters, offsets: offsets, delays: (bases, lengths), curves: curves)
|
|
367
|
+
(fullMaster, fullMasterCount) = Self.decode(program.master?.code ?? [], width: W, state: stateRegisters, offsets: offsets, delays: (bases, lengths), curves: curves)
|
|
368
|
+
// Each made again (renamed, compacted, scheduled) when the program has loaded, and when a region turns.
|
|
369
|
+
// The block stream's room takes the sample stream's too: what no longer changes frame by frame moves there.
|
|
370
|
+
blockCode = .allocate(capacity: max(12, (blockCount + fullSampleCount) * 12))
|
|
371
|
+
blockCode.initialize(repeating: 0, count: max(12, (blockCount + fullSampleCount) * 12))
|
|
372
|
+
blockCode.update(from: fullBlock, count: blockCount * 12)
|
|
373
|
+
keptSample = .allocate(capacity: max(12, fullSampleCount * 12))
|
|
374
|
+
folded = .allocate(capacity: max(1, blockCount + fullSampleCount + fullMasterCount))
|
|
375
|
+
blockRunning = blockCount
|
|
376
|
+
sampleCode = .allocate(capacity: max(12, fullSampleCount * 12))
|
|
377
|
+
sampleCode.update(from: fullSample, count: max(12, fullSampleCount * 12))
|
|
378
|
+
masterCode = .allocate(capacity: max(12, fullMasterCount * 12))
|
|
379
|
+
masterCode.update(from: fullMaster, count: max(12, fullMasterCount * 12))
|
|
380
|
+
(sampleCount, masterCount) = (fullSampleCount, fullMasterCount)
|
|
381
|
+
sampleGates = program.sampleGates
|
|
382
|
+
masterGates = program.masterGates
|
|
383
|
+
groupParameters = program.groups.map(\.parameter)
|
|
384
|
+
groupParents = program.groups.map(\.parent)
|
|
385
|
+
groupOutputs = program.groups.map { $0.outputs.map { (register: $0.register, on: Int32($0.on * W), off: Int32($0.off * W)) } }
|
|
386
|
+
groupOn = [UInt8](repeating: 2, count: program.groups.count)
|
|
387
|
+
streamInstances = [program.block, program.sample, program.master?.code ?? []].map { code in
|
|
388
|
+
var instances: [Int32] = []
|
|
389
|
+
var pc = 0
|
|
390
|
+
while pc < code.count, let op = DSPOpcode(rawValue: code[pc]) {
|
|
391
|
+
instances.append(op.builtin != nil ? code[pc + 2 + op.arity] : -1)
|
|
392
|
+
pc += 2 + op.arity + (op.builtin == nil ? 0 : 1)
|
|
393
|
+
}
|
|
394
|
+
return instances
|
|
395
|
+
}
|
|
396
|
+
streamCounts = [blockCount, fullSampleCount, fullMasterCount]
|
|
397
|
+
aliveNow = [blockCount, fullSampleCount, fullMasterCount].map { count in
|
|
398
|
+
let marks = UnsafeMutablePointer<UInt8>.allocate(capacity: Swift.max(1, count))
|
|
399
|
+
marks.initialize(repeating: 0, count: Swift.max(1, count))
|
|
400
|
+
return marks
|
|
401
|
+
}
|
|
402
|
+
aliveBefore = [blockCount, fullSampleCount, fullMasterCount].map { count in
|
|
403
|
+
let marks = UnsafeMutablePointer<UInt8>.allocate(capacity: Swift.max(1, count))
|
|
404
|
+
marks.initialize(repeating: 0, count: Swift.max(1, count))
|
|
405
|
+
return marks
|
|
406
|
+
}
|
|
407
|
+
readNow = .allocate(capacity: program.registers)
|
|
408
|
+
readNow.initialize(repeating: 0, count: program.registers)
|
|
409
|
+
readBefore = .allocate(capacity: program.registers)
|
|
410
|
+
readBefore.initialize(repeating: 0, count: program.registers)
|
|
411
|
+
renameTable = .allocate(capacity: program.registers)
|
|
412
|
+
renameTable.initialize(repeating: -1, count: program.registers)
|
|
413
|
+
let longest = max(1, blockCount, fullSampleCount, fullMasterCount)
|
|
414
|
+
staging = .allocate(capacity: longest * 12)
|
|
415
|
+
staging.initialize(repeating: 0, count: longest * 12)
|
|
416
|
+
instructionMarks = .allocate(capacity: longest)
|
|
417
|
+
instructionMarks.initialize(repeating: 0, count: longest)
|
|
418
|
+
registerMarks = .allocate(capacity: program.registers)
|
|
419
|
+
registerMarks.initialize(repeating: 0, count: program.registers)
|
|
420
|
+
choiceParameters = program.choices.map(\.parameter)
|
|
421
|
+
choiceRegisters = program.choices.map(\.register)
|
|
422
|
+
choiceOptions = program.choices.map { $0.options.map { Int32($0 * W) } }
|
|
423
|
+
choiceScales = program.choices.map(\.scale)
|
|
424
|
+
choiceUsed = [Int](repeating: -1, count: program.choices.count)
|
|
425
|
+
choiceAsked = choiceUsed
|
|
426
|
+
choiceNow = choiceUsed
|
|
427
|
+
voicesParameter = program.voicingVoices
|
|
428
|
+
modeParameter = program.voicingMode
|
|
429
|
+
// The most registers an instruction of the sample or master stream reads: its operands, and a modsum's routes.
|
|
430
|
+
let ordering = !program.choices.isEmpty || !program.routes.isEmpty
|
|
431
|
+
// A route list may be replaced by one as long as any may be.
|
|
432
|
+
let longestRoute = program.routes.isEmpty ? 0 : routesMax
|
|
433
|
+
func readsIn(_ code: UnsafeMutablePointer<Int32>, _ count: Int) -> Int {
|
|
434
|
+
(0..<count).reduce(0) { sum, i in
|
|
435
|
+
let op = DSPOpcode(rawValue: code[i * 12])!
|
|
436
|
+
return sum + op.arity + (op == .modsum ? longestRoute : 0)
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
let readCount = ordering ? Swift.max(readsIn(fullSample, fullSampleCount), readsIn(fullMaster, fullMasterCount)) : 0
|
|
440
|
+
let orderable = ordering ? longest : 1
|
|
441
|
+
orderedSample = .allocate(capacity: Swift.max(12, fullSampleCount * 12))
|
|
442
|
+
orderedMaster = .allocate(capacity: Swift.max(12, fullMasterCount * 12))
|
|
443
|
+
writerOf = .allocate(capacity: ordering ? program.registers : 1)
|
|
444
|
+
writerOf.initialize(repeating: -1, count: ordering ? program.registers : 1)
|
|
445
|
+
stateWriterFirst = .allocate(capacity: ordering ? program.registers : 1)
|
|
446
|
+
stateWriterFirst.initialize(repeating: -1, count: ordering ? program.registers : 1)
|
|
447
|
+
stateWriterNext = .allocate(capacity: orderable)
|
|
448
|
+
readerStarts = .allocate(capacity: ordering ? program.registers + 1 : 1)
|
|
449
|
+
readers = .allocate(capacity: Swift.max(1, readCount))
|
|
450
|
+
waiting = .allocate(capacity: orderable)
|
|
451
|
+
heap = .allocate(capacity: orderable)
|
|
238
452
|
for constant in program.constants {
|
|
239
453
|
(registers + constant.register * W).update(repeating: constant.value, count: W)
|
|
240
454
|
}
|
|
@@ -271,18 +485,43 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
271
485
|
queueEvent.initialize(repeating: 0, count: Self.queueCapacity * 3)
|
|
272
486
|
for lane in 0..<L { resetLane(lane) }
|
|
273
487
|
resetMaster()
|
|
488
|
+
// The streams as they run: the regions its parameters turn on, scheduled.
|
|
489
|
+
turnRegions(loading: true)
|
|
490
|
+
revoice()
|
|
274
491
|
}
|
|
275
492
|
|
|
276
493
|
deinit {
|
|
277
|
-
for pointer in [registers, state, targets, laneGate, laneVelocity, laneActive, trigger, masterTrigger
|
|
494
|
+
for pointer in [registers, state, targets, laneGate, laneVelocity, laneActive, trigger, masterTrigger] { pointer.deallocate() }
|
|
495
|
+
for slots in [shapePointers, samplePointers, tablePointers] {
|
|
496
|
+
for i in 0..<slots.count { slots[i].deallocate() }
|
|
497
|
+
slots.deallocate()
|
|
498
|
+
}
|
|
499
|
+
for slots in [tableSizes, tableFrames, shapeLengths, sampleLengths] { slots.deallocate() }
|
|
500
|
+
sampleRatios.deallocate()
|
|
501
|
+
sampleSprays.deallocate()
|
|
278
502
|
noise.deallocate()
|
|
279
503
|
routeRegisters.deallocate()
|
|
504
|
+
routeCounts.deallocate()
|
|
280
505
|
routeState.deallocate()
|
|
281
|
-
|
|
506
|
+
for pointer in [parameterMask, stateMask, renamedMask, pinnedMask, routedMask, changing] { pointer.deallocate() }
|
|
507
|
+
constantValues.deallocate()
|
|
508
|
+
parameterLowest.deallocate()
|
|
509
|
+
parameterHighest.deallocate()
|
|
510
|
+
keptSample.deallocate()
|
|
511
|
+
folded.deallocate()
|
|
282
512
|
delaySamples.deallocate()
|
|
283
513
|
blockCode.deallocate()
|
|
284
514
|
sampleCode.deallocate()
|
|
285
515
|
masterCode.deallocate()
|
|
516
|
+
fullBlock.deallocate()
|
|
517
|
+
fullSample.deallocate()
|
|
518
|
+
fullMaster.deallocate()
|
|
519
|
+
renameTable.deallocate()
|
|
520
|
+
staging.deallocate()
|
|
521
|
+
instructionMarks.deallocate()
|
|
522
|
+
registerMarks.deallocate()
|
|
523
|
+
for pointer in aliveNow + aliveBefore + [readNow, readBefore] { pointer.deallocate() }
|
|
524
|
+
for pointer in [orderedSample, orderedMaster, writerOf, stateWriterFirst, stateWriterNext, readerStarts, readers, waiting, heap] { pointer.deallocate() }
|
|
286
525
|
laneNote.deallocate()
|
|
287
526
|
stack.deallocate()
|
|
288
527
|
queueBeat.deallocate()
|
|
@@ -293,18 +532,458 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
293
532
|
|
|
294
533
|
/// Instructions at a stride of 12 (op, destination, five operands, state offset, which of slots 1 … 6 are state
|
|
295
534
|
/// registers as bits, then a delay line's buffer: where it starts and its length, then an envelope's curve, 1 up,
|
|
296
|
-
/// 0 for none), registers as offsets into the file. With
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
535
|
+
/// 0 for none), registers as offsets into the file. With how many there are.
|
|
536
|
+
/// Gated regions and choices: a region is on while its parameter is set above 0.5 and the region around it is on;
|
|
537
|
+
/// a choice reads as the option its parameter names. When either changes (or the program loads), the streams are
|
|
538
|
+
/// made again: without the regions that are off, with what reads a region's result or a choice reading what it
|
|
539
|
+
/// gives, without what nothing the outputs need reads (an option not chosen), ordered so what a choice reads comes
|
|
540
|
+
/// first, and scheduled. Into buffers made when the program loaded: nothing is allocated.
|
|
541
|
+
private func turnRegions(loading: Bool, force: Bool = false) {
|
|
542
|
+
var changed = loading || force
|
|
543
|
+
for g in 0..<groupParameters.count {
|
|
544
|
+
let parent = groupParents[g]
|
|
545
|
+
let on: UInt8 = (parent < 0 || groupOn[parent] == 1) && targets[groupParameters[g]] > 0.5 ? 1 : 0
|
|
546
|
+
if on != groupOn[g] {
|
|
547
|
+
groupOn[g] = on
|
|
548
|
+
changed = true
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
for c in 0..<choiceParameters.count {
|
|
552
|
+
let value = Float((Double(targets[choiceParameters[c]]) * choiceScales[c]).rounded())
|
|
553
|
+
let last = choiceOptions[c].count - 1
|
|
554
|
+
choiceNow[c] = value.isNaN ? 0 : value <= 0 ? 0 : value >= Float(last) ? last : Int(value)
|
|
555
|
+
if choiceNow[c] != choiceAsked[c] { changed = true }
|
|
556
|
+
}
|
|
557
|
+
guard changed else { return }
|
|
558
|
+
for c in 0..<choiceNow.count { choiceAsked[c] = choiceNow[c] }
|
|
559
|
+
// The choices asked for; if they would make a cycle, the last that did not (with the regions as they are now);
|
|
560
|
+
// and on loading, if even those would, the program's own order.
|
|
561
|
+
if make(choiceNow, sort: true) {
|
|
562
|
+
for c in 0..<choiceNow.count { choiceUsed[c] = choiceNow[c] }
|
|
563
|
+
} else if loading || !make(choiceUsed, sort: true) {
|
|
564
|
+
_ = make(loading ? choiceNow : choiceUsed, sort: false)
|
|
565
|
+
}
|
|
566
|
+
if loading { for c in 0..<choiceNow.count { choiceUsed[c] = choiceNow[c] } }
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/// The streams made again for the regions as they are and the choices `chosen`; false (nothing made) on a cycle.
|
|
570
|
+
private func make(_ chosen: [Int], sort: Bool) -> Bool {
|
|
571
|
+
// What was folded last time reads as itself again, until folded again below.
|
|
572
|
+
for i in 0..<foldedCount { renameTable[Int(folded[i])] = -1 }
|
|
573
|
+
foldedCount = 0
|
|
574
|
+
var results = choiceRegisters.count
|
|
575
|
+
for g in groupOutputs.indices {
|
|
576
|
+
for output in groupOutputs[g] { renameTable[output.register] = groupOn[g] == 1 ? output.on : output.off }
|
|
577
|
+
results += groupOutputs[g].count
|
|
578
|
+
}
|
|
579
|
+
for c in 0..<choiceRegisters.count { renameTable[choiceRegisters[c]] = choiceOptions[c][chosen[c]] }
|
|
580
|
+
// A result or a choice may read as another (nested regions, a choice of results): followed, at most once each.
|
|
581
|
+
let W = Int32(width)
|
|
582
|
+
func resolve(_ register: Int) {
|
|
583
|
+
var target = renameTable[register]
|
|
584
|
+
var steps = 0
|
|
585
|
+
while steps < results, renameTable[Int(target / W)] >= 0 {
|
|
586
|
+
target = renameTable[Int(target / W)]
|
|
587
|
+
steps += 1
|
|
588
|
+
}
|
|
589
|
+
renameTable[register] = target
|
|
590
|
+
}
|
|
591
|
+
for g in groupOutputs.indices { for output in groupOutputs[g] { resolve(output.register) } }
|
|
592
|
+
for register in choiceRegisters { resolve(register) }
|
|
593
|
+
fold()
|
|
594
|
+
liveness()
|
|
595
|
+
// Ordered where something may read what comes after it (a choice of a value made later, a route from a source
|
|
596
|
+
// made later); kept apart from `staging`, which the next stream is compacted into.
|
|
597
|
+
let ordering = sort && (!choiceRegisters.isEmpty || routeCount > 0)
|
|
598
|
+
let samples = compact(fullSample, fullSampleCount, aliveNow[1])
|
|
599
|
+
if ordering {
|
|
600
|
+
guard order(samples, into: orderedSample) else { return false }
|
|
601
|
+
} else {
|
|
602
|
+
orderedSample.update(from: staging, count: samples * 12)
|
|
603
|
+
}
|
|
604
|
+
let masters = compact(fullMaster, fullMasterCount, aliveNow[2])
|
|
605
|
+
if ordering {
|
|
606
|
+
guard order(masters, into: orderedMaster) else { return false }
|
|
607
|
+
} else {
|
|
608
|
+
orderedMaster.update(from: staging, count: masters * 12)
|
|
609
|
+
}
|
|
610
|
+
revive()
|
|
611
|
+
blockRunning = compact(fullBlock, blockCount, aliveNow[0])
|
|
612
|
+
blockCode.update(from: staging, count: blockRunning * 12)
|
|
613
|
+
// What in the sample stream no longer changes frame by frame runs once a block (as machine.ts moves it).
|
|
614
|
+
sampleCount = rerate(samples, from: orderedSample)
|
|
615
|
+
sampleSerial = reschedule(sampleCount, from: keptSample, into: sampleCode)
|
|
616
|
+
masterCount = masters
|
|
617
|
+
masterSerial = reschedule(masters, from: orderedMaster, into: masterCode)
|
|
618
|
+
return true
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/// Folds what turned constant, before what runs is found (as machine.ts folds it): an instruction that comes to
|
|
622
|
+
/// one of its operands (x + 0, x × 1, a mix at 0 or 1, a select of a constant, a mov) or to 0 (x × 0, an empty
|
|
623
|
+
/// route list) is passed over, what reads it reading that instead. Never one whose register is an output, state, a
|
|
624
|
+
/// curve or in a route list.
|
|
625
|
+
private func fold() {
|
|
626
|
+
let W = Int32(width)
|
|
627
|
+
let zero = zeroOffset
|
|
628
|
+
func pass(_ code: UnsafeMutablePointer<Int32>, _ count: Int) {
|
|
629
|
+
for i in 0..<count {
|
|
630
|
+
let slot = code + i * 12
|
|
631
|
+
let d = Int(slot[1] / W)
|
|
632
|
+
if pinnedMask[d] != 0 || routedMask[d] != 0 { continue }
|
|
633
|
+
let op = slot[0]
|
|
634
|
+
let arity = DSPOpcode(rawValue: op)!.arity
|
|
635
|
+
func operand(_ k: Int) -> Int32 {
|
|
636
|
+
guard k < arity else { return -1 }
|
|
637
|
+
let t = renameTable[Int(slot[2 + k] / W)]
|
|
638
|
+
return t >= 0 ? t : slot[2 + k]
|
|
639
|
+
}
|
|
640
|
+
let a = operand(0)
|
|
641
|
+
let b = operand(1)
|
|
642
|
+
let c = operand(2)
|
|
643
|
+
func value(_ offset: Int32) -> Float { offset >= 0 ? constantValues[Int(offset / W)] : .nan }
|
|
644
|
+
var to: Int32 = -1
|
|
645
|
+
switch op {
|
|
646
|
+
case 0: to = a
|
|
647
|
+
case 1: to = value(a) == 0 ? b : value(b) == 0 ? a : -1
|
|
648
|
+
case 2: to = value(b) == 0 ? a : -1
|
|
649
|
+
case 3: to = value(a) == 1 ? b : value(b) == 1 ? a : value(a) == 0 || value(b) == 0 ? zero : -1
|
|
650
|
+
case 58: to = value(a) == 0 || value(b) == 0 ? c : value(c) == 0 && value(a) == 1 ? b : value(c) == 0 && value(b) == 1 ? a : -1
|
|
651
|
+
case 59: to = value(c) == 0 ? zero : value(c) == 1 && value(a) == 0 ? b : value(c) == 1 && value(b) == 0 ? a : -1
|
|
652
|
+
case 30: to = a == b ? b : !value(a).isNaN ? (value(a) != 0 ? b : c) : -1
|
|
653
|
+
// A parameter clamped to a range its own is inside.
|
|
654
|
+
case 31: to = a >= 0 && value(b) <= parameterLowest[Int(a / W)] && parameterHighest[Int(a / W)] <= value(c) ? a : -1
|
|
655
|
+
case 32: to = value(c) == 0 || a == b ? a : value(c) == 1 ? b : -1
|
|
656
|
+
case 57:
|
|
657
|
+
let index = Self.index(registers[Int(slot[2])], routeCount)
|
|
658
|
+
to = index >= routeCount || routeCounts[index] == 0 ? zero : -1
|
|
659
|
+
default: break
|
|
660
|
+
}
|
|
661
|
+
if to < 0 || Int(to / W) == d { continue }
|
|
662
|
+
renameTable[d] = to
|
|
663
|
+
folded[foldedCount] = Int32(d)
|
|
664
|
+
foldedCount += 1
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
pass(fullBlock, blockCount)
|
|
668
|
+
pass(fullSample, fullSampleCount)
|
|
669
|
+
pass(fullMaster, fullMasterCount)
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/// The `count` instructions of the sample stream at `source`, split (as machine.ts splits them): those that read
|
|
673
|
+
/// only what is set once a block (no state, nothing worked out frame by frame, not the beat) and are not builtins,
|
|
674
|
+
/// onto the end of `blockCode`; the rest into `keptSample`, in their order. How many stayed.
|
|
675
|
+
private func rerate(_ count: Int, from source: UnsafeMutablePointer<Int32>) -> Int {
|
|
676
|
+
let W = Int32(width)
|
|
677
|
+
changing[6] = 1
|
|
678
|
+
var kept = 0
|
|
679
|
+
for i in 0..<count {
|
|
680
|
+
let slot = source + i * 12
|
|
681
|
+
let d = Int(slot[1] / W)
|
|
682
|
+
var still = DSPOpcode(rawValue: slot[0])!.builtin == nil && stateMask[d] == 0
|
|
683
|
+
if still { forEachRead(slot) { r, state in if state || changing[r] != 0 { still = false } } }
|
|
684
|
+
if still {
|
|
685
|
+
(blockCode + blockRunning * 12).update(from: slot, count: 12)
|
|
686
|
+
blockRunning += 1
|
|
687
|
+
} else {
|
|
688
|
+
changing[d] = 1
|
|
689
|
+
(keptSample + kept * 12).update(from: slot, count: 12)
|
|
690
|
+
kept += 1
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// The marks back to none, for the next time.
|
|
694
|
+
for i in 0..<kept { changing[Int(keptSample[i * 12 + 1] / W)] = 0 }
|
|
695
|
+
changing[6] = 0
|
|
696
|
+
return kept
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/// Replaces route list `index` from the next block with `list`'s registers (a source's, then its amount's, pair
|
|
700
|
+
/// after pair, as floats), copied in place (as machine.ts `replaceRoutes`): the streams are made again. False where
|
|
701
|
+
/// there is no such list, or an amount is not a parameter, a register is past the program's or one renamed, or the
|
|
702
|
+
/// list is past `DSPProgram.routesMax`.
|
|
703
|
+
public func replaceRoutes(_ index: Int, registers list: DSPBuffer) -> Bool {
|
|
704
|
+
guard index >= 0, index < routeCount, list.count % 2 == 0, list.count <= DSPProgram.routesMax else { return false }
|
|
705
|
+
for k in 0..<list.count {
|
|
706
|
+
let x = list.pointer[k]
|
|
707
|
+
guard x >= 0, x < Float(program.registers), x == x.rounded(.down) else { return false }
|
|
708
|
+
let r = Int(x)
|
|
709
|
+
guard renamedMask[r] == 0, k % 2 == 0 || parameterMask[r] != 0 else { return false }
|
|
710
|
+
}
|
|
711
|
+
let start = index * DSPProgram.routesMax
|
|
712
|
+
for k in 0..<list.count {
|
|
713
|
+
let r = Int(list.pointer[k])
|
|
714
|
+
routeRegisters[start + k] = r * width
|
|
715
|
+
routeState[start + k] = stateMask[r] != 0
|
|
716
|
+
}
|
|
717
|
+
routeCounts[index] = list.count
|
|
718
|
+
routedMask.update(repeating: 0, count: program.registers)
|
|
719
|
+
for i in 0..<routeCount {
|
|
720
|
+
for k in 0..<routeCounts[i] { routedMask[routeRegisters[i * DSPProgram.routesMax + k] / width] = 1 }
|
|
721
|
+
}
|
|
722
|
+
rerouted = true
|
|
723
|
+
return true
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
/// The `count` instructions in `staging` into `out`, ordered so each comes after what writes what it reads (not
|
|
728
|
+
/// state), and a state register's readers before what writes it (they read the frame before's); program order
|
|
729
|
+
/// where neither says (as machine.ts orders). False on a cycle.
|
|
730
|
+
private func order(_ count: Int, into out: UnsafeMutablePointer<Int32>) -> Bool {
|
|
731
|
+
let W = Int32(width)
|
|
732
|
+
let registerCount = program.registers
|
|
733
|
+
for i in 0..<count {
|
|
734
|
+
let slot = staging + i * 12
|
|
735
|
+
let d = Int(slot[1] / W)
|
|
736
|
+
if (slot[8] >> 1) & 1 == 1 {
|
|
737
|
+
stateWriterNext[i] = stateWriterFirst[d]
|
|
738
|
+
stateWriterFirst[d] = Int32(i)
|
|
739
|
+
} else {
|
|
740
|
+
writerOf[d] = Int32(i)
|
|
741
|
+
}
|
|
742
|
+
waiting[i] = 0
|
|
743
|
+
}
|
|
744
|
+
// Each register's readers (reading it not as state), in program order: counted, summed to where each ends,
|
|
745
|
+
// then placed back to front so each ends where it starts.
|
|
746
|
+
readerStarts.update(repeating: 0, count: registerCount + 1)
|
|
747
|
+
for i in 0..<count { forEachRead(staging + i * 12) { r, state in if !state { readerStarts[r] += 1 } } }
|
|
748
|
+
var total: Int32 = 0
|
|
749
|
+
for r in 0..<registerCount {
|
|
750
|
+
total += readerStarts[r]
|
|
751
|
+
readerStarts[r] = total
|
|
752
|
+
}
|
|
753
|
+
readerStarts[registerCount] = total
|
|
754
|
+
for i in stride(from: count - 1, through: 0, by: -1) {
|
|
755
|
+
forEachRead(staging + i * 12) { r, state in
|
|
756
|
+
guard !state else { return }
|
|
757
|
+
readerStarts[r] -= 1
|
|
758
|
+
readers[Int(readerStarts[r])] = Int32(i)
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
for i in 0..<count {
|
|
762
|
+
forEachRead(staging + i * 12) { r, state in
|
|
763
|
+
if state {
|
|
764
|
+
var w = stateWriterFirst[r]
|
|
765
|
+
while w >= 0 {
|
|
766
|
+
if Int(w) != i { waiting[Int(w)] += 1 }
|
|
767
|
+
w = stateWriterNext[Int(w)]
|
|
768
|
+
}
|
|
769
|
+
} else if writerOf[r] >= 0, Int(writerOf[r]) != i {
|
|
770
|
+
waiting[i] += 1
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
// The ready instructions, least first (a binary heap), so program order holds where nothing else decides.
|
|
775
|
+
var size = 0
|
|
776
|
+
func push(_ i: Int32) {
|
|
777
|
+
var k = size
|
|
778
|
+
heap[k] = i
|
|
779
|
+
size += 1
|
|
780
|
+
while k > 0, heap[(k - 1) / 2] > heap[k] {
|
|
781
|
+
(heap[k], heap[(k - 1) / 2]) = (heap[(k - 1) / 2], heap[k])
|
|
782
|
+
k = (k - 1) / 2
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
func pop() -> Int {
|
|
786
|
+
let top = heap[0]
|
|
787
|
+
size -= 1
|
|
788
|
+
heap[0] = heap[size]
|
|
789
|
+
var k = 0
|
|
790
|
+
while true {
|
|
791
|
+
let l = 2 * k + 1
|
|
792
|
+
var m = k
|
|
793
|
+
if l < size, heap[l] < heap[m] { m = l }
|
|
794
|
+
if l + 1 < size, heap[l + 1] < heap[m] { m = l + 1 }
|
|
795
|
+
if m == k { break }
|
|
796
|
+
(heap[k], heap[m]) = (heap[m], heap[k])
|
|
797
|
+
k = m
|
|
798
|
+
}
|
|
799
|
+
return Int(top)
|
|
800
|
+
}
|
|
801
|
+
func done(_ j: Int) {
|
|
802
|
+
waiting[j] -= 1
|
|
803
|
+
if waiting[j] == 0 { push(Int32(j)) }
|
|
804
|
+
}
|
|
805
|
+
for i in 0..<count where waiting[i] == 0 { push(Int32(i)) }
|
|
806
|
+
var placed = 0
|
|
807
|
+
while size > 0 {
|
|
808
|
+
let i = pop()
|
|
809
|
+
let slot = staging + i * 12
|
|
810
|
+
(out + placed * 12).update(from: slot, count: 12)
|
|
811
|
+
placed += 1
|
|
812
|
+
forEachRead(slot) { r, state in
|
|
813
|
+
guard state else { return }
|
|
814
|
+
var w = stateWriterFirst[r]
|
|
815
|
+
while w >= 0 {
|
|
816
|
+
if Int(w) != i { done(Int(w)) }
|
|
817
|
+
w = stateWriterNext[Int(w)]
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
let d = Int(slot[1] / W)
|
|
821
|
+
if (slot[8] >> 1) & 1 == 0, Int(writerOf[d]) == i {
|
|
822
|
+
for k in Int(readerStarts[d])..<Int(readerStarts[d + 1]) where Int(readers[k]) != i { done(Int(readers[k])) }
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
// The tables back to none, for the next time.
|
|
826
|
+
for i in 0..<count {
|
|
827
|
+
let d = Int(staging[i * 12 + 1] / W)
|
|
828
|
+
writerOf[d] = -1
|
|
829
|
+
stateWriterFirst[d] = -1
|
|
830
|
+
}
|
|
831
|
+
return placed == count
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/// An envelope's curve exponent as read: held to 1/16 … 16, 1 for NaN (as machine.ts reads it).
|
|
835
|
+
@inline(__always) private static func exponent(_ x: Float) -> Double {
|
|
836
|
+
x >= 0.0625 ? Double(x <= 16 ? x : 16) : x < 0.0625 ? 0.0625 : 1
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/// Each register the instruction at `slot` reads (its operands, an envelope's curves, and a modsum's routes), and
|
|
840
|
+
/// whether it is state.
|
|
841
|
+
private func forEachRead(_ slot: UnsafeMutablePointer<Int32>, _ body: (Int, Bool) -> Void) {
|
|
842
|
+
let W = Int32(width)
|
|
843
|
+
let op = DSPOpcode(rawValue: slot[0])!
|
|
844
|
+
for k in 0..<op.arity { body(Int(slot[2 + k] / W), (slot[8] >> (2 + k)) & 1 == 1) }
|
|
845
|
+
if op == .adsr, slot[11] > 0 {
|
|
846
|
+
let curve = Int(slot[11]) - 1
|
|
847
|
+
for k in 0..<3 { body(curveRegisters[curve * 3 + k] / width, false) }
|
|
848
|
+
}
|
|
849
|
+
if op == .modsum {
|
|
850
|
+
let index = Self.index(registers[Int(slot[2])], routeCount)
|
|
851
|
+
if index < routeCount {
|
|
852
|
+
let start = index * DSPProgram.routesMax
|
|
853
|
+
for k in start..<(start + routeCounts[index]) { body(routeRegisters[k] / width, routeState[k]) }
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/// What runs, into `aliveNow` and `readNow`, with what reads a result or a choice reading what `renameTable` says:
|
|
859
|
+
/// from the outputs back, each instruction outside a region that is off whose register something running reads,
|
|
860
|
+
/// and what it reads (a state register read, what writes it). Again until nothing more runs, since a choice or
|
|
861
|
+
/// state may read what comes after (as machine.ts finds it).
|
|
862
|
+
private func liveness() {
|
|
863
|
+
let W = Int32(width)
|
|
864
|
+
readNow.update(repeating: 0, count: program.registers)
|
|
865
|
+
readNow[program.output] = 1
|
|
866
|
+
if let right = program.outputRight { readNow[right] = 1 }
|
|
867
|
+
for r in program.master?.outputs ?? [] { readNow[r] = 1 }
|
|
868
|
+
for s in 0..<3 { aliveNow[s].update(repeating: 0, count: streamCounts[s]) }
|
|
869
|
+
/// One pass over a stream, back to front: whether anything more runs.
|
|
870
|
+
func sweep(_ code: UnsafeMutablePointer<Int32>, _ count: Int, _ gates: [Int32]?, _ marks: UnsafeMutablePointer<UInt8>) -> Bool {
|
|
871
|
+
var more = false
|
|
872
|
+
for i in stride(from: count - 1, through: 0, by: -1) {
|
|
873
|
+
let slot = code + i * 12
|
|
874
|
+
guard marks[i] == 0, readNow[Int(slot[1] / W)] != 0 else { continue }
|
|
875
|
+
if let gates, gates[i] >= 0, groupOn[Int(gates[i])] == 0 { continue }
|
|
876
|
+
marks[i] = 1
|
|
877
|
+
more = true
|
|
878
|
+
forEachRead(slot) { r, _ in
|
|
879
|
+
let renamed = renameTable[r]
|
|
880
|
+
readNow[renamed >= 0 ? Int(renamed / W) : r] = 1
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
return more
|
|
884
|
+
}
|
|
885
|
+
var more = true
|
|
886
|
+
while more {
|
|
887
|
+
more = sweep(fullMaster, fullMasterCount, masterGates, aliveNow[2])
|
|
888
|
+
more = sweep(fullSample, fullSampleCount, sampleGates, aliveNow[1]) || more
|
|
889
|
+
more = sweep(fullBlock, blockCount, nil, aliveNow[0]) || more
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/// What comes to run again (it did not when the streams were last made) starts over: a builtin as when a voice
|
|
894
|
+
/// starts, a state register at its first value. Nothing does when the program loads. Then what runs now is what ran.
|
|
895
|
+
private func revive() {
|
|
896
|
+
let counts = streamCounts
|
|
897
|
+
if made {
|
|
898
|
+
for s in 0..<3 {
|
|
899
|
+
for i in 0..<counts[s] where aliveNow[s][i] != 0 && aliveBefore[s][i] == 0 && streamInstances[s][i] >= 0 {
|
|
900
|
+
let instance = Int(streamInstances[s][i])
|
|
901
|
+
if instanceMaster[instance] { resetInstance(instance, 0) } else { for l in 0..<program.voices { resetInstance(instance, l) } }
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
for entry in program.state where readNow[entry.register] != 0 && readBefore[entry.register] == 0 {
|
|
905
|
+
if entry.master { registers[entry.register * width] = entry.initial } else { for l in 0..<program.voices { registers[entry.register * width + l] = entry.initial } }
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
for s in 0..<3 { aliveBefore[s].update(from: aliveNow[s], count: counts[s]) }
|
|
909
|
+
readBefore.update(from: readNow, count: program.registers)
|
|
910
|
+
made = true
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/// A stream's instructions that run (`alive`), their operands renamed, into `staging`: how many instructions.
|
|
914
|
+
private func compact(_ code: UnsafeMutablePointer<Int32>, _ count: Int, _ alive: UnsafeMutablePointer<UInt8>) -> Int {
|
|
915
|
+
let W = Int32(width)
|
|
916
|
+
var kept = 0
|
|
917
|
+
for i in 0..<count {
|
|
918
|
+
if alive[i] == 0 { continue }
|
|
919
|
+
let at = code + i * 12
|
|
920
|
+
let slot = staging + kept * 12
|
|
921
|
+
slot.update(from: at, count: 12)
|
|
922
|
+
for k in 0..<DSPOpcode(rawValue: at[0])!.arity {
|
|
923
|
+
let r = renameTable[Int(at[2 + k] / W)]
|
|
924
|
+
if r >= 0 { slot[2 + k] = r }
|
|
925
|
+
}
|
|
926
|
+
kept += 1
|
|
927
|
+
}
|
|
928
|
+
return kept
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/// A stream's schedule over the `count` instructions at `source`, into `out`: those reading or writing state, and
|
|
932
|
+
/// those both computed from state and needed by it this frame, run frame by frame (the serial part, returned); what
|
|
933
|
+
/// they need comes before, what needs them after; program order kept within each part (as machine.ts schedules).
|
|
934
|
+
private func reschedule(_ count: Int, from source: UnsafeMutablePointer<Int32>, into out: UnsafeMutablePointer<Int32>) -> Range<Int> {
|
|
935
|
+
let W = Int32(width)
|
|
936
|
+
let touches: UInt8 = 1, downstream: UInt8 = 2, upstream: UInt8 = 4
|
|
937
|
+
let tainted: UInt8 = 1, needed: UInt8 = 2
|
|
938
|
+
for i in 0..<count {
|
|
939
|
+
let slot = source + i * 12
|
|
940
|
+
var marks: UInt8 = (slot[8] >> 1) & 1 == 1 ? touches : 0
|
|
941
|
+
var fromState = false
|
|
942
|
+
forEachRead(slot) { r, state in
|
|
943
|
+
if state { marks |= touches }
|
|
944
|
+
if registerMarks[r] & tainted != 0 { fromState = true }
|
|
945
|
+
}
|
|
946
|
+
if marks & touches != 0 || fromState {
|
|
947
|
+
marks |= downstream
|
|
948
|
+
if (slot[8] >> 1) & 1 == 0 { registerMarks[Int(slot[1] / W)] |= tainted }
|
|
949
|
+
}
|
|
950
|
+
instructionMarks[i] = marks
|
|
951
|
+
}
|
|
952
|
+
for i in stride(from: count - 1, through: 0, by: -1) {
|
|
953
|
+
let slot = source + i * 12
|
|
954
|
+
guard instructionMarks[i] & touches != 0 || registerMarks[Int(slot[1] / W)] & needed != 0 else { continue }
|
|
955
|
+
instructionMarks[i] |= upstream
|
|
956
|
+
forEachRead(slot) { r, state in if !state { registerMarks[r] |= needed } }
|
|
957
|
+
}
|
|
958
|
+
var kept = 0
|
|
302
959
|
var from = 0
|
|
303
960
|
var to = 0
|
|
961
|
+
for part in 0..<3 {
|
|
962
|
+
if part == 1 { from = kept }
|
|
963
|
+
if part == 2 { to = kept }
|
|
964
|
+
for i in 0..<count {
|
|
965
|
+
let marks = instructionMarks[i]
|
|
966
|
+
let serial = marks & touches != 0 || (marks & downstream != 0 && marks & upstream != 0)
|
|
967
|
+
guard (part == 0 && !serial && marks & downstream == 0) || (part == 1 && serial) || (part == 2 && !serial && marks & downstream != 0) else { continue }
|
|
968
|
+
(out + kept * 12).update(from: source + i * 12, count: 12)
|
|
969
|
+
kept += 1
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
// The marks back to none, for the next time.
|
|
973
|
+
for i in 0..<count {
|
|
974
|
+
let slot = source + i * 12
|
|
975
|
+
registerMarks[Int(slot[1] / W)] = 0
|
|
976
|
+
forEachRead(slot) { r, _ in registerMarks[r] = 0 }
|
|
977
|
+
}
|
|
978
|
+
return from..<to
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
private static func decode(
|
|
982
|
+
_ code: [Int32], width: Int, state: Set<Int>, offsets: [Int], delays: (bases: [Int], lengths: [Int]), curves: [Int: Int]
|
|
983
|
+
) -> (UnsafeMutablePointer<Int32>, Int) {
|
|
984
|
+
var out: [Int32] = []
|
|
304
985
|
var pc = 0
|
|
305
986
|
while pc < code.count {
|
|
306
|
-
if pc == serial?.lowerBound { from = out.count / 12 }
|
|
307
|
-
if pc == serial?.upperBound { to = out.count / 12 }
|
|
308
987
|
let op = DSPOpcode(rawValue: code[pc])!
|
|
309
988
|
var slot = [Int32](repeating: 0, count: 12)
|
|
310
989
|
slot[0] = op.rawValue
|
|
@@ -323,41 +1002,77 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
323
1002
|
out += slot
|
|
324
1003
|
pc += 2 + op.arity + (op.builtin == nil ? 0 : 1)
|
|
325
1004
|
}
|
|
326
|
-
if pc == serial?.lowerBound { from = out.count / 12 }
|
|
327
|
-
if pc == serial?.upperBound { to = out.count / 12 }
|
|
328
1005
|
let pointer = UnsafeMutablePointer<Int32>.allocate(capacity: max(12, out.count))
|
|
329
1006
|
pointer.initialize(repeating: 0, count: max(12, out.count))
|
|
330
1007
|
out.withUnsafeBufferPointer { pointer.update(from: $0.baseAddress!, count: out.count) }
|
|
331
|
-
return (pointer, out.count / 12
|
|
1008
|
+
return (pointer, out.count / 12)
|
|
332
1009
|
}
|
|
333
1010
|
|
|
334
1011
|
/// Lists one after another: where each starts and its length.
|
|
335
|
-
private static func
|
|
336
|
-
let
|
|
337
|
-
|
|
338
|
-
pointer
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
1012
|
+
private static func zeros(_ count: Int) -> UnsafeMutablePointer<Float> {
|
|
1013
|
+
let pointer = UnsafeMutablePointer<Float>.allocate(capacity: Swift.max(1, count))
|
|
1014
|
+
pointer.initialize(repeating: 0, count: Swift.max(1, count))
|
|
1015
|
+
return pointer
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
private static func copy(_ values: [Float]) -> UnsafeMutablePointer<Float> {
|
|
1019
|
+
let pointer = zeros(values.count)
|
|
1020
|
+
values.withUnsafeBufferPointer { pointer.update(from: $0.baseAddress!, count: values.count) }
|
|
1021
|
+
return pointer
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/// A wavetable's samples as the machine reads them (as machine.ts `tableLevels` makes them), off the audio thread,
|
|
1025
|
+
/// for `replaceTable`.
|
|
1026
|
+
public static func tableLevels(frames: [[Float]], cosines: [[Float]]? = nil) -> DSPBuffer {
|
|
1027
|
+
let (pointer, count) = levels(frames: frames, cosines: cosines)
|
|
1028
|
+
return DSPBuffer(taking: pointer, count: count)
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/// Replaces table `index`'s samples (`tableLevels`) from the next block, swapping them with `levels`, which then
|
|
1032
|
+
/// holds what the machine let go of, to free off the audio thread. False (nothing swapped) where they do not fit.
|
|
1033
|
+
public func replaceTable(_ index: Int, levels: DSPBuffer, frames: Int) -> Bool {
|
|
1034
|
+
guard index >= 0, index < tableCount, frames >= 1, levels.count == frames * DSPProgram.tableLevels * DSPProgram.tableFrame else { return false }
|
|
1035
|
+
var total = frames
|
|
1036
|
+
for i in 0..<tableFrames.count where i != index { total += tableFrames[i] }
|
|
1037
|
+
guard total <= DSPProgram.tableFramesMax else { return false }
|
|
1038
|
+
swap(&tablePointers[index], &levels.pointer)
|
|
1039
|
+
swap(&tableSizes[index], &levels.count)
|
|
1040
|
+
tableFrames[index] = frames
|
|
1041
|
+
return true
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/// Replaces shape `index`'s points from the next block, swapping them with `points` (as `replaceTable`).
|
|
1045
|
+
public func replaceShape(_ index: Int, points: DSPBuffer) -> Bool {
|
|
1046
|
+
guard index >= 0, index < program.shapes.count, (2...DSPProgram.shapePointsMax).contains(points.count) else { return false }
|
|
1047
|
+
var total = points.count
|
|
1048
|
+
for i in 0..<shapeLengths.count where i != index { total += shapeLengths[i] }
|
|
1049
|
+
guard total <= DSPProgram.shapesPointsMax else { return false }
|
|
1050
|
+
swap(&shapePointers[index], &points.pointer)
|
|
1051
|
+
swap(&shapeLengths[index], &points.count)
|
|
1052
|
+
return true
|
|
347
1053
|
}
|
|
348
1054
|
|
|
349
|
-
///
|
|
350
|
-
///
|
|
351
|
-
|
|
352
|
-
|
|
1055
|
+
/// Replaces sample `index`'s audio, at `rate` frames a second, from the next block, swapping it with `audio` (as
|
|
1056
|
+
/// `replaceTable`); a sound playing it goes on from where it is, inside the new length.
|
|
1057
|
+
public func replaceSample(_ index: Int, audio: DSPBuffer, rate: Double) -> Bool {
|
|
1058
|
+
guard index >= 0, index < program.samples.count, rate >= 1000, rate <= 384_000, audio.count >= 2 else { return false }
|
|
1059
|
+
var total = audio.count
|
|
1060
|
+
for i in 0..<sampleLengths.count where i != index { total += sampleLengths[i] }
|
|
1061
|
+
guard total <= DSPProgram.samplesMax else { return false }
|
|
1062
|
+
swap(&samplePointers[index], &audio.pointer)
|
|
1063
|
+
swap(&sampleLengths[index], &audio.count)
|
|
1064
|
+
sampleRatios[index] = Float(rate / Double(Float(sampleRate)))
|
|
1065
|
+
sampleSprays[index] = UInt32(Swift.max(1, (rate * 0.02).rounded(.down)))
|
|
1066
|
+
return true
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/// A table's samples and how many floats: each level one with the harmonics past 512, 256 … dropped, 2048 samples,
|
|
1070
|
+
/// each its frames side by side.
|
|
1071
|
+
private static func levels(frames tableFrames: [[Float]], cosines tableCosines: [[Float]]?) -> (UnsafeMutablePointer<Float>, Int) {
|
|
353
1072
|
let N = DSPProgram.tableFrame
|
|
354
1073
|
let levels = DSPProgram.tableLevels
|
|
355
|
-
let
|
|
356
|
-
let
|
|
357
|
-
let samples = UnsafeMutablePointer<Float>.allocate(capacity: count)
|
|
358
|
-
samples.initialize(repeating: 0, count: count)
|
|
359
|
-
var bases: [Int] = []
|
|
360
|
-
var frames: [Int] = []
|
|
1074
|
+
let count = Swift.max(1, tableFrames.count) * levels * N
|
|
1075
|
+
let samples = zeros(count)
|
|
361
1076
|
var cosT = [Double](repeating: 0, count: N / 2)
|
|
362
1077
|
var sinT = [Double](repeating: 0, count: N / 2)
|
|
363
1078
|
for j in 0..<(N / 2) {
|
|
@@ -366,15 +1081,12 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
366
1081
|
}
|
|
367
1082
|
var re = [Double](repeating: 0, count: N)
|
|
368
1083
|
var im = [Double](repeating: 0, count: N)
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
bases.append(at)
|
|
372
|
-
let count = table.frames.count
|
|
373
|
-
frames.append(count)
|
|
1084
|
+
do {
|
|
1085
|
+
let count = tableFrames.count
|
|
374
1086
|
// A level's samples one after another, each its frames side by side: a read between two samples and two
|
|
375
1087
|
// frames is one span of memory.
|
|
376
|
-
func store(_ level: Int, _ n: Int, _ f: Int) -> Int {
|
|
377
|
-
for (f, amplitudes) in
|
|
1088
|
+
func store(_ level: Int, _ n: Int, _ f: Int) -> Int { (level * N + n) * count + f }
|
|
1089
|
+
for (f, amplitudes) in tableFrames.enumerated() {
|
|
378
1090
|
for level in 0..<levels {
|
|
379
1091
|
let limit = Swift.min(512 >> level, amplitudes.count)
|
|
380
1092
|
if level > 0 && limit == Swift.min(512 >> (level - 1), amplitudes.count) {
|
|
@@ -394,7 +1106,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
394
1106
|
im[N - k] = a / 2
|
|
395
1107
|
}
|
|
396
1108
|
// cos(kθ) = (e^{ikθ} + e^{−ikθ}) / 2
|
|
397
|
-
if let cosines =
|
|
1109
|
+
if let cosines = tableCosines?[f] {
|
|
398
1110
|
for k in 1...limit {
|
|
399
1111
|
let c = Double(cosines[k - 1])
|
|
400
1112
|
re[k] = c / 2
|
|
@@ -439,9 +1151,8 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
439
1151
|
for n in 0..<N { samples[store(level, n, f)] = Float(re[n]) }
|
|
440
1152
|
}
|
|
441
1153
|
}
|
|
442
|
-
at += count * levels * N
|
|
443
1154
|
}
|
|
444
|
-
return (samples,
|
|
1155
|
+
return (samples, count)
|
|
445
1156
|
}
|
|
446
1157
|
|
|
447
1158
|
// MARK: Events
|
|
@@ -455,6 +1166,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
455
1166
|
public func setParameter(at index: Int, _ value: Float) {
|
|
456
1167
|
guard index >= 0, index < parameterRegisters.count, value.isFinite else { return }
|
|
457
1168
|
targets[index] = Swift.min(Swift.max(value, parameterRanges[index].min), parameterRanges[index].max)
|
|
1169
|
+
retargeted = true
|
|
458
1170
|
}
|
|
459
1171
|
|
|
460
1172
|
/// The transport's tempo, in beats a minute (1 … 999), from the next block.
|
|
@@ -544,12 +1256,48 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
544
1256
|
return frames >= Double(DSPProgram.blockFrames) ? DSPProgram.blockFrames : Swift.max(1, Int(frames))
|
|
545
1257
|
}
|
|
546
1258
|
|
|
1259
|
+
/// The mode notes play in now: the voicing's parameter's, else the program's.
|
|
1260
|
+
private func currentMode() -> DSPProgram.Mode? {
|
|
1261
|
+
guard let p = modeParameter else { return program.mode }
|
|
1262
|
+
let m = targets[p].rounded()
|
|
1263
|
+
return m == 1 ? .mono : m == 2 ? .legato : nil
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
/// How many voices play now: the voicing's parameter's (1 … the program's), else the program's.
|
|
1267
|
+
private func voiceLimit() -> Int {
|
|
1268
|
+
guard let p = voicesParameter else { return program.voices }
|
|
1269
|
+
let n = targets[p].rounded()
|
|
1270
|
+
return n.isNaN || n < 1 ? 1 : n >= Float(program.voices) ? program.voices : Int(n)
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/// At a block's start, where the voicing turned: the voices past those that play now let go, and the held notes a
|
|
1274
|
+
/// mode keeps start again from the one voice that sounds (as machine.ts does).
|
|
1275
|
+
private func revoice() {
|
|
1276
|
+
let mode = currentMode()
|
|
1277
|
+
let modeIndex = mode == .mono ? 1 : mode == .legato ? 2 : 0
|
|
1278
|
+
let limit = mode != nil ? 1 : voiceLimit()
|
|
1279
|
+
guard limit != voicedLimit || modeIndex != voicedMode else { return }
|
|
1280
|
+
let first = voicedLimit < 0
|
|
1281
|
+
voicedLimit = limit
|
|
1282
|
+
voicedMode = modeIndex
|
|
1283
|
+
if first { return }
|
|
1284
|
+
for l in limit..<Self.lanes {
|
|
1285
|
+
laneGate[l] = 0
|
|
1286
|
+
laneHeld[l] = false
|
|
1287
|
+
}
|
|
1288
|
+
stackCount = 0
|
|
1289
|
+
if mode != nil && laneGate[0] > 0.5 {
|
|
1290
|
+
stack[0] = laneNote[0]
|
|
1291
|
+
stackCount = 1
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
547
1295
|
private func start(_ note: Int32, velocity: Int32) {
|
|
548
1296
|
if velocity <= 0 {
|
|
549
1297
|
release(note)
|
|
550
1298
|
return
|
|
551
1299
|
}
|
|
552
|
-
if let mode =
|
|
1300
|
+
if let mode = currentMode() {
|
|
553
1301
|
// One voice: the newest note, legato while another is held.
|
|
554
1302
|
unstack(note)
|
|
555
1303
|
if stackCount == Self.lanes {
|
|
@@ -572,7 +1320,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
572
1320
|
}
|
|
573
1321
|
return
|
|
574
1322
|
}
|
|
575
|
-
let voices =
|
|
1323
|
+
let voices = voiceLimit()
|
|
576
1324
|
var lane = -1
|
|
577
1325
|
for l in 0..<voices where lane < 0 && laneNote[l] == note && laneGate[l] > 0.5 { lane = l }
|
|
578
1326
|
var fresh = false
|
|
@@ -602,7 +1350,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
602
1350
|
}
|
|
603
1351
|
|
|
604
1352
|
private func release(_ note: Int32) {
|
|
605
|
-
if let mode =
|
|
1353
|
+
if let mode = currentMode() {
|
|
606
1354
|
unstack(note)
|
|
607
1355
|
guard laneNote[0] == note && laneGate[0] > 0.5 else { return }
|
|
608
1356
|
if stackCount > 0 {
|
|
@@ -747,12 +1495,24 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
747
1495
|
let W = width
|
|
748
1496
|
let R = registers
|
|
749
1497
|
let sr = sampleRate
|
|
750
|
-
// Parameters glide toward their targets, about 20 ms.
|
|
1498
|
+
// Parameters glide toward their targets, about 20 ms, and come to rest on them: one at rest is left as it is.
|
|
751
1499
|
let alpha = snapped ? Float(1) : 1 - exp(-Float(n) / (0.02 * sr))
|
|
752
1500
|
snapped = false
|
|
753
1501
|
for (i, register) in parameterRegisters.enumerated() {
|
|
754
1502
|
let at = R + register * W
|
|
755
|
-
|
|
1503
|
+
let now = at[0]
|
|
1504
|
+
let target = targets[i]
|
|
1505
|
+
if now == target { continue }
|
|
1506
|
+
var value = now + (target - now) * alpha
|
|
1507
|
+
if abs(target - value) <= 1e-6 * (1 + abs(target)) { value = target }
|
|
1508
|
+
at.update(repeating: value, count: W)
|
|
1509
|
+
}
|
|
1510
|
+
if retargeted || rerouted {
|
|
1511
|
+
let force = rerouted
|
|
1512
|
+
retargeted = false
|
|
1513
|
+
rerouted = false
|
|
1514
|
+
if !groupParameters.isEmpty || !choiceParameters.isEmpty || force { turnRegions(loading: false, force: force) }
|
|
1515
|
+
if voicesParameter != nil || modeParameter != nil { revoice() }
|
|
756
1516
|
}
|
|
757
1517
|
(R + 5 * W).update(repeating: Float(tempo), count: W)
|
|
758
1518
|
let beatStep = tempo / 60 / Double(sr)
|
|
@@ -780,7 +1540,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
780
1540
|
R[4 * W + k * V + l] = note >= 0 ? Float(note) : 0
|
|
781
1541
|
}
|
|
782
1542
|
}
|
|
783
|
-
run(blockCode, 0..<
|
|
1543
|
+
run(blockCode, 0..<blockRunning, lanes: lanes, trigger: trigger, frames: F)
|
|
784
1544
|
}
|
|
785
1545
|
if master != nil {
|
|
786
1546
|
// The newest note, the master stage's inputs (registers 7 … 10), in lane 0.
|
|
@@ -930,9 +1690,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
930
1690
|
}
|
|
931
1691
|
}
|
|
932
1692
|
}
|
|
933
|
-
var
|
|
1693
|
+
var next = range.lowerBound * 12
|
|
934
1694
|
let end = range.upperBound * 12
|
|
935
|
-
while
|
|
1695
|
+
while next < end {
|
|
1696
|
+
// A constant for the instruction: a variable the kernels' closures read would be checked for exclusive
|
|
1697
|
+
// access at every read.
|
|
1698
|
+
let at = next
|
|
1699
|
+
next += 12
|
|
936
1700
|
let op = code[at]
|
|
937
1701
|
let states = code[at + 8]
|
|
938
1702
|
// A register at this frame, or a state register's one value.
|
|
@@ -1121,11 +1885,18 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1121
1885
|
let releaseIn = register(6)
|
|
1122
1886
|
let curve = Int(code[at + 11]) - 1
|
|
1123
1887
|
let curved = curve >= 0
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
let
|
|
1127
|
-
|
|
1128
|
-
|
|
1888
|
+
// The curves' registers (a knob's, or modulated), read at the chunk's first frame a voice (as machine.ts
|
|
1889
|
+
// reads them), 1/16 … 16, 1 for NaN.
|
|
1890
|
+
let ra = curved ? R + curveRegisters[curve * 3] + shift : R
|
|
1891
|
+
let rd = curved ? R + curveRegisters[curve * 3 + 1] + shift : R
|
|
1892
|
+
let rr = curved ? R + curveRegisters[curve * 3 + 2] + shift : R
|
|
1893
|
+
for l in 0..<lanes {
|
|
1894
|
+
let ea = curved ? Self.exponent(ra[l]) : 1
|
|
1895
|
+
let ed = curved ? Self.exponent(rd[l]) : 1
|
|
1896
|
+
let er = curved ? Self.exponent(rr[l]) : 1
|
|
1897
|
+
// All three 1: no stage bent (what it put out kept all the same, for a curve turned later).
|
|
1898
|
+
let bent = curved && (ea != 1 || ed != 1 || er != 1)
|
|
1899
|
+
for fk in 0..<fc {
|
|
1129
1900
|
let el = fk * V + l
|
|
1130
1901
|
let gate = a[el]
|
|
1131
1902
|
var stage = s[l]
|
|
@@ -1142,7 +1913,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1142
1913
|
if curved && stage != before {
|
|
1143
1914
|
let last = s[8 * L + l]
|
|
1144
1915
|
if stage == 1 {
|
|
1145
|
-
level = last > 0 ? Float(Foundation.pow(Double(last), 1 / ea)) : 0
|
|
1916
|
+
level = last > 0 ? (ea == 1 ? last : Float(Foundation.pow(Double(last), 1 / ea))) : 0
|
|
1146
1917
|
} else if stage == 4 {
|
|
1147
1918
|
level = last
|
|
1148
1919
|
s[9 * L + l] = last
|
|
@@ -1182,18 +1953,21 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1182
1953
|
s[l] = stage
|
|
1183
1954
|
s[L + l] = level
|
|
1184
1955
|
var out = level
|
|
1185
|
-
if
|
|
1956
|
+
if bent {
|
|
1957
|
+
// An exponent of 1 is no bend: the stage as it is, with no power worked out.
|
|
1186
1958
|
if stage == 1 {
|
|
1187
|
-
out = Float(Foundation.pow(Double(level), ea))
|
|
1959
|
+
out = ea == 1 ? level : Float(Foundation.pow(Double(level), ea))
|
|
1188
1960
|
} else if stage == 2 {
|
|
1189
1961
|
let span = 1 - sustain
|
|
1190
|
-
|
|
1962
|
+
let along = Swift.max(0, (level - sustain) / span)
|
|
1963
|
+
out = span > 1e-6 ? sustain + span * (ed == 1 ? along : Float(Foundation.pow(Double(along), ed))) : level
|
|
1191
1964
|
} else if stage == 4 {
|
|
1192
1965
|
let from = s[9 * L + l]
|
|
1193
|
-
|
|
1966
|
+
let along = Swift.min(1, level / from)
|
|
1967
|
+
out = from > 0 ? from * (er == 1 ? along : Float(Foundation.pow(Double(along), er))) : 0
|
|
1194
1968
|
}
|
|
1195
|
-
s[8 * L + l] = out
|
|
1196
1969
|
}
|
|
1970
|
+
if curved { s[8 * L + l] = out }
|
|
1197
1971
|
d[el] = out
|
|
1198
1972
|
}
|
|
1199
1973
|
}
|
|
@@ -1234,12 +2008,12 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1234
2008
|
// wavetable(table, freq, position): the level that cannot alias at freq (worked out again only when freq
|
|
1235
2009
|
// changes), read between samples and between frames.
|
|
1236
2010
|
let s = S + Int(code[at + 7])
|
|
1237
|
-
let T = tableSamples
|
|
1238
2011
|
// The table is a constant register's, the same in every lane.
|
|
1239
2012
|
let index = a[0]
|
|
1240
2013
|
let t = index >= 0 && Int(exactly: index.rounded(.down)).map { $0 < tableCount } == true ? Int(index) : 0
|
|
1241
2014
|
let frames = tableFrames.isEmpty ? 1 : tableFrames[t]
|
|
1242
|
-
let
|
|
2015
|
+
let T = tablePointers[t]
|
|
2016
|
+
let base = 0
|
|
1243
2017
|
for fk in 0..<fc {
|
|
1244
2018
|
for l in 0..<lanes {
|
|
1245
2019
|
let el = fk * V + l
|
|
@@ -1306,13 +2080,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1306
2080
|
}
|
|
1307
2081
|
case 52:
|
|
1308
2082
|
// shape(shape, x): x 0 … 1 along it, between points.
|
|
1309
|
-
let P = shapePoints
|
|
1310
2083
|
for fk in 0..<fc {
|
|
1311
2084
|
for l in 0..<lanes {
|
|
1312
2085
|
let el = fk * V + l
|
|
1313
2086
|
let t = Self.index(a[el], shapeLengths.count)
|
|
1314
2087
|
let points = shapeLengths.isEmpty ? 1 : shapeLengths[t]
|
|
1315
|
-
let
|
|
2088
|
+
let P = shapePointers[t]
|
|
2089
|
+
let base = 0
|
|
1316
2090
|
let x = b[el]
|
|
1317
2091
|
let position = (x > 0 ? (x < 1 ? x : 1) : 0) * Float(points - 1)
|
|
1318
2092
|
let i0 = Int(position.rounded(.down))
|
|
@@ -1326,13 +2100,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1326
2100
|
// between samples; silent past either end. Where it plays is a whole sample and the fraction past it,
|
|
1327
2101
|
// so it keeps time.
|
|
1328
2102
|
let s = S + Int(code[at + 7])
|
|
1329
|
-
let A = sampleAudio
|
|
1330
2103
|
for fk in 0..<fc {
|
|
1331
2104
|
for l in 0..<lanes {
|
|
1332
2105
|
let el = fk * V + l
|
|
1333
2106
|
let t = Self.index(a[el], sampleLengths.count)
|
|
1334
2107
|
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
1335
|
-
let
|
|
2108
|
+
let A = samplePointers[t]
|
|
2109
|
+
let base = 0
|
|
1336
2110
|
if s[2 * L + l] != trigger[l] {
|
|
1337
2111
|
s[2 * L + l] = trigger[l]
|
|
1338
2112
|
let start = c[el]
|
|
@@ -1363,13 +2137,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1363
2137
|
// tableread(table, phase, freq, position): wavetable's read, at a phase the program keeps (wrapped to
|
|
1364
2138
|
// 0 … 1), at the level that cannot alias at freq (worked out again only when freq changes).
|
|
1365
2139
|
let s = S + Int(code[at + 7])
|
|
1366
|
-
let T = tableSamples
|
|
1367
2140
|
let e = register(5)
|
|
1368
2141
|
// The table is a constant register's, the same in every lane.
|
|
1369
2142
|
let index = a[0]
|
|
1370
2143
|
let t = index >= 0 && Int(exactly: index.rounded(.down)).map { $0 < tableCount } == true ? Int(index) : 0
|
|
1371
2144
|
let frames = tableFrames.isEmpty ? 1 : tableFrames[t]
|
|
1372
|
-
let
|
|
2145
|
+
let T = tablePointers[t]
|
|
2146
|
+
let base = 0
|
|
1373
2147
|
for fk in 0..<fc {
|
|
1374
2148
|
for l in 0..<lanes {
|
|
1375
2149
|
let el = fk * V + l
|
|
@@ -1408,13 +2182,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1408
2182
|
case 79:
|
|
1409
2183
|
// loop(sample, rate, start): play's, but round and round: past either end it comes in at the other.
|
|
1410
2184
|
let s = S + Int(code[at + 7])
|
|
1411
|
-
let A = sampleAudio
|
|
1412
2185
|
for fk in 0..<fc {
|
|
1413
2186
|
for l in 0..<lanes {
|
|
1414
2187
|
let el = fk * V + l
|
|
1415
2188
|
let t = Self.index(a[el], sampleLengths.count)
|
|
1416
2189
|
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
1417
|
-
let
|
|
2190
|
+
let A = samplePointers[t]
|
|
2191
|
+
let base = 0
|
|
1418
2192
|
if s[2 * L + l] != trigger[l] {
|
|
1419
2193
|
s[2 * L + l] = trigger[l]
|
|
1420
2194
|
let start = c[el]
|
|
@@ -1443,13 +2217,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1443
2217
|
let s = S + Int(code[at + 7])
|
|
1444
2218
|
let lengthIn = register(5)
|
|
1445
2219
|
let pitchIn = register(6)
|
|
1446
|
-
let A = sampleAudio
|
|
1447
2220
|
for fk in 0..<fc {
|
|
1448
2221
|
for l in 0..<lanes {
|
|
1449
2222
|
let el = fk * V + l
|
|
1450
2223
|
let t = Self.index(a[el], sampleLengths.count)
|
|
1451
2224
|
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
1452
|
-
let
|
|
2225
|
+
let A = samplePointers[t]
|
|
2226
|
+
let base = 0
|
|
1453
2227
|
let ratio = sampleRatios.isEmpty ? 1 : sampleRatios[t]
|
|
1454
2228
|
let density = c[el]
|
|
1455
2229
|
var countdown = s[l]
|
|
@@ -1508,10 +2282,10 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1508
2282
|
// modsum(routes): Σ amount × source over the list, route by route. An amount is a parameter, the same
|
|
1509
2283
|
// in every lane, so a route at 0 is passed over once for all of them and costs nothing.
|
|
1510
2284
|
each { d[$0] = 0 }
|
|
1511
|
-
let index = Self.index(a[0],
|
|
1512
|
-
guard index <
|
|
1513
|
-
var k =
|
|
1514
|
-
let end =
|
|
2285
|
+
let index = Self.index(a[0], routeCount)
|
|
2286
|
+
guard index < routeCount else { break }
|
|
2287
|
+
var k = index * DSPProgram.routesMax
|
|
2288
|
+
let end = k + routeCounts[index]
|
|
1515
2289
|
while k < end {
|
|
1516
2290
|
let amount = R + routeRegisters[k + 1]
|
|
1517
2291
|
let source = R + routeRegisters[k] + (routeState[k] ? 0 : shift)
|
|
@@ -1555,7 +2329,57 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1555
2329
|
default:
|
|
1556
2330
|
break
|
|
1557
2331
|
}
|
|
1558
|
-
at += 12
|
|
1559
2332
|
}
|
|
1560
2333
|
}
|
|
1561
2334
|
}
|
|
2335
|
+
|
|
2336
|
+
/// Memory a machine reads (a table's samples, a shape's points, a sample's audio), made off the audio thread and
|
|
2337
|
+
/// swapped into a machine whole (`replaceTable` …). After a swap it holds what the machine let go of: freed with it,
|
|
2338
|
+
/// off the audio thread.
|
|
2339
|
+
/// A fixed number of values in memory of their own, each changed in place: what the machine reads as it renders and
|
|
2340
|
+
/// swaps when data is replaced, with no exclusivity checks (as an array property changed in place would need).
|
|
2341
|
+
struct Slots<Element> {
|
|
2342
|
+
let base: UnsafeMutablePointer<Element>
|
|
2343
|
+
let count: Int
|
|
2344
|
+
|
|
2345
|
+
init(_ values: [Element]) {
|
|
2346
|
+
count = values.count
|
|
2347
|
+
base = .allocate(capacity: Swift.max(1, values.count))
|
|
2348
|
+
base.initialize(from: values, count: values.count)
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
var isEmpty: Bool { count == 0 }
|
|
2352
|
+
|
|
2353
|
+
subscript(i: Int) -> Element {
|
|
2354
|
+
get { base[i] }
|
|
2355
|
+
nonmutating set { base[i] = newValue }
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
func deallocate() {
|
|
2359
|
+
base.deinitialize(count: count)
|
|
2360
|
+
base.deallocate()
|
|
2361
|
+
}
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
public final class DSPBuffer: @unchecked Sendable {
|
|
2365
|
+
public fileprivate(set) var pointer: UnsafeMutablePointer<Float>
|
|
2366
|
+
public fileprivate(set) var count: Int
|
|
2367
|
+
|
|
2368
|
+
/// `values`, copied.
|
|
2369
|
+
public init(_ values: [Float]) {
|
|
2370
|
+
let pointer = UnsafeMutablePointer<Float>.allocate(capacity: Swift.max(1, values.count))
|
|
2371
|
+
pointer.initialize(repeating: 0, count: Swift.max(1, values.count))
|
|
2372
|
+
values.withUnsafeBufferPointer { if let base = $0.baseAddress { pointer.update(from: base, count: values.count) } }
|
|
2373
|
+
self.pointer = pointer
|
|
2374
|
+
count = values.count
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
fileprivate init(taking pointer: UnsafeMutablePointer<Float>, count: Int) {
|
|
2378
|
+
self.pointer = pointer
|
|
2379
|
+
self.count = count
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
deinit {
|
|
2383
|
+
pointer.deallocate()
|
|
2384
|
+
}
|
|
2385
|
+
}
|