@swiftbrowser/native 0.21.0 → 0.25.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 +954 -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,460 @@ 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
|
+
// The same list again: nothing to make again.
|
|
713
|
+
if routeCounts[index] == list.count && (0..<list.count).allSatisfy({ routeRegisters[start + $0] == Int(list.pointer[$0]) * width }) { return true }
|
|
714
|
+
for k in 0..<list.count {
|
|
715
|
+
let r = Int(list.pointer[k])
|
|
716
|
+
routeRegisters[start + k] = r * width
|
|
717
|
+
routeState[start + k] = stateMask[r] != 0
|
|
718
|
+
}
|
|
719
|
+
routeCounts[index] = list.count
|
|
720
|
+
routedMask.update(repeating: 0, count: program.registers)
|
|
721
|
+
for i in 0..<routeCount {
|
|
722
|
+
for k in 0..<routeCounts[i] { routedMask[routeRegisters[i * DSPProgram.routesMax + k] / width] = 1 }
|
|
723
|
+
}
|
|
724
|
+
rerouted = true
|
|
725
|
+
return true
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
/// The `count` instructions in `staging` into `out`, ordered so each comes after what writes what it reads (not
|
|
730
|
+
/// state), and a state register's readers before what writes it (they read the frame before's); program order
|
|
731
|
+
/// where neither says (as machine.ts orders). False on a cycle.
|
|
732
|
+
private func order(_ count: Int, into out: UnsafeMutablePointer<Int32>) -> Bool {
|
|
733
|
+
let W = Int32(width)
|
|
734
|
+
let registerCount = program.registers
|
|
735
|
+
for i in 0..<count {
|
|
736
|
+
let slot = staging + i * 12
|
|
737
|
+
let d = Int(slot[1] / W)
|
|
738
|
+
if (slot[8] >> 1) & 1 == 1 {
|
|
739
|
+
stateWriterNext[i] = stateWriterFirst[d]
|
|
740
|
+
stateWriterFirst[d] = Int32(i)
|
|
741
|
+
} else {
|
|
742
|
+
writerOf[d] = Int32(i)
|
|
743
|
+
}
|
|
744
|
+
waiting[i] = 0
|
|
745
|
+
}
|
|
746
|
+
// Each register's readers (reading it not as state), in program order: counted, summed to where each ends,
|
|
747
|
+
// then placed back to front so each ends where it starts.
|
|
748
|
+
readerStarts.update(repeating: 0, count: registerCount + 1)
|
|
749
|
+
for i in 0..<count { forEachRead(staging + i * 12) { r, state in if !state { readerStarts[r] += 1 } } }
|
|
750
|
+
var total: Int32 = 0
|
|
751
|
+
for r in 0..<registerCount {
|
|
752
|
+
total += readerStarts[r]
|
|
753
|
+
readerStarts[r] = total
|
|
754
|
+
}
|
|
755
|
+
readerStarts[registerCount] = total
|
|
756
|
+
for i in stride(from: count - 1, through: 0, by: -1) {
|
|
757
|
+
forEachRead(staging + i * 12) { r, state in
|
|
758
|
+
guard !state else { return }
|
|
759
|
+
readerStarts[r] -= 1
|
|
760
|
+
readers[Int(readerStarts[r])] = Int32(i)
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
for i in 0..<count {
|
|
764
|
+
forEachRead(staging + i * 12) { r, state in
|
|
765
|
+
if state {
|
|
766
|
+
var w = stateWriterFirst[r]
|
|
767
|
+
while w >= 0 {
|
|
768
|
+
if Int(w) != i { waiting[Int(w)] += 1 }
|
|
769
|
+
w = stateWriterNext[Int(w)]
|
|
770
|
+
}
|
|
771
|
+
} else if writerOf[r] >= 0, Int(writerOf[r]) != i {
|
|
772
|
+
waiting[i] += 1
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
// The ready instructions, least first (a binary heap), so program order holds where nothing else decides.
|
|
777
|
+
var size = 0
|
|
778
|
+
func push(_ i: Int32) {
|
|
779
|
+
var k = size
|
|
780
|
+
heap[k] = i
|
|
781
|
+
size += 1
|
|
782
|
+
while k > 0, heap[(k - 1) / 2] > heap[k] {
|
|
783
|
+
(heap[k], heap[(k - 1) / 2]) = (heap[(k - 1) / 2], heap[k])
|
|
784
|
+
k = (k - 1) / 2
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
func pop() -> Int {
|
|
788
|
+
let top = heap[0]
|
|
789
|
+
size -= 1
|
|
790
|
+
heap[0] = heap[size]
|
|
791
|
+
var k = 0
|
|
792
|
+
while true {
|
|
793
|
+
let l = 2 * k + 1
|
|
794
|
+
var m = k
|
|
795
|
+
if l < size, heap[l] < heap[m] { m = l }
|
|
796
|
+
if l + 1 < size, heap[l + 1] < heap[m] { m = l + 1 }
|
|
797
|
+
if m == k { break }
|
|
798
|
+
(heap[k], heap[m]) = (heap[m], heap[k])
|
|
799
|
+
k = m
|
|
800
|
+
}
|
|
801
|
+
return Int(top)
|
|
802
|
+
}
|
|
803
|
+
func done(_ j: Int) {
|
|
804
|
+
waiting[j] -= 1
|
|
805
|
+
if waiting[j] == 0 { push(Int32(j)) }
|
|
806
|
+
}
|
|
807
|
+
for i in 0..<count where waiting[i] == 0 { push(Int32(i)) }
|
|
808
|
+
var placed = 0
|
|
809
|
+
while size > 0 {
|
|
810
|
+
let i = pop()
|
|
811
|
+
let slot = staging + i * 12
|
|
812
|
+
(out + placed * 12).update(from: slot, count: 12)
|
|
813
|
+
placed += 1
|
|
814
|
+
forEachRead(slot) { r, state in
|
|
815
|
+
guard state else { return }
|
|
816
|
+
var w = stateWriterFirst[r]
|
|
817
|
+
while w >= 0 {
|
|
818
|
+
if Int(w) != i { done(Int(w)) }
|
|
819
|
+
w = stateWriterNext[Int(w)]
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
let d = Int(slot[1] / W)
|
|
823
|
+
if (slot[8] >> 1) & 1 == 0, Int(writerOf[d]) == i {
|
|
824
|
+
for k in Int(readerStarts[d])..<Int(readerStarts[d + 1]) where Int(readers[k]) != i { done(Int(readers[k])) }
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
// The tables back to none, for the next time.
|
|
828
|
+
for i in 0..<count {
|
|
829
|
+
let d = Int(staging[i * 12 + 1] / W)
|
|
830
|
+
writerOf[d] = -1
|
|
831
|
+
stateWriterFirst[d] = -1
|
|
832
|
+
}
|
|
833
|
+
return placed == count
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/// An envelope's curve exponent as read: held to 1/16 … 16, 1 for NaN (as machine.ts reads it).
|
|
837
|
+
@inline(__always) private static func exponent(_ x: Float) -> Double {
|
|
838
|
+
x >= 0.0625 ? Double(x <= 16 ? x : 16) : x < 0.0625 ? 0.0625 : 1
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/// Each register the instruction at `slot` reads (its operands, an envelope's curves, and a modsum's routes), and
|
|
842
|
+
/// whether it is state.
|
|
843
|
+
private func forEachRead(_ slot: UnsafeMutablePointer<Int32>, _ body: (Int, Bool) -> Void) {
|
|
844
|
+
let W = Int32(width)
|
|
845
|
+
let op = DSPOpcode(rawValue: slot[0])!
|
|
846
|
+
for k in 0..<op.arity { body(Int(slot[2 + k] / W), (slot[8] >> (2 + k)) & 1 == 1) }
|
|
847
|
+
if op == .adsr, slot[11] > 0 {
|
|
848
|
+
let curve = Int(slot[11]) - 1
|
|
849
|
+
for k in 0..<3 { body(curveRegisters[curve * 3 + k] / width, false) }
|
|
850
|
+
}
|
|
851
|
+
if op == .modsum {
|
|
852
|
+
let index = Self.index(registers[Int(slot[2])], routeCount)
|
|
853
|
+
if index < routeCount {
|
|
854
|
+
let start = index * DSPProgram.routesMax
|
|
855
|
+
for k in start..<(start + routeCounts[index]) { body(routeRegisters[k] / width, routeState[k]) }
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/// What runs, into `aliveNow` and `readNow`, with what reads a result or a choice reading what `renameTable` says:
|
|
861
|
+
/// from the outputs back, each instruction outside a region that is off whose register something running reads,
|
|
862
|
+
/// and what it reads (a state register read, what writes it). Again until nothing more runs, since a choice or
|
|
863
|
+
/// state may read what comes after (as machine.ts finds it).
|
|
864
|
+
private func liveness() {
|
|
865
|
+
let W = Int32(width)
|
|
866
|
+
readNow.update(repeating: 0, count: program.registers)
|
|
867
|
+
readNow[program.output] = 1
|
|
868
|
+
if let right = program.outputRight { readNow[right] = 1 }
|
|
869
|
+
for r in program.master?.outputs ?? [] { readNow[r] = 1 }
|
|
870
|
+
for s in 0..<3 { aliveNow[s].update(repeating: 0, count: streamCounts[s]) }
|
|
871
|
+
/// One pass over a stream, back to front: whether anything more runs.
|
|
872
|
+
func sweep(_ code: UnsafeMutablePointer<Int32>, _ count: Int, _ gates: [Int32]?, _ marks: UnsafeMutablePointer<UInt8>) -> Bool {
|
|
873
|
+
var more = false
|
|
874
|
+
for i in stride(from: count - 1, through: 0, by: -1) {
|
|
875
|
+
let slot = code + i * 12
|
|
876
|
+
guard marks[i] == 0, readNow[Int(slot[1] / W)] != 0 else { continue }
|
|
877
|
+
if let gates, gates[i] >= 0, groupOn[Int(gates[i])] == 0 { continue }
|
|
878
|
+
marks[i] = 1
|
|
879
|
+
more = true
|
|
880
|
+
forEachRead(slot) { r, _ in
|
|
881
|
+
let renamed = renameTable[r]
|
|
882
|
+
readNow[renamed >= 0 ? Int(renamed / W) : r] = 1
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return more
|
|
886
|
+
}
|
|
887
|
+
var more = true
|
|
888
|
+
while more {
|
|
889
|
+
more = sweep(fullMaster, fullMasterCount, masterGates, aliveNow[2])
|
|
890
|
+
more = sweep(fullSample, fullSampleCount, sampleGates, aliveNow[1]) || more
|
|
891
|
+
more = sweep(fullBlock, blockCount, nil, aliveNow[0]) || more
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/// What comes to run again (it did not when the streams were last made) starts over: a builtin as when a voice
|
|
896
|
+
/// starts, a state register at its first value. Nothing does when the program loads. Then what runs now is what ran.
|
|
897
|
+
private func revive() {
|
|
898
|
+
let counts = streamCounts
|
|
899
|
+
if made {
|
|
900
|
+
for s in 0..<3 {
|
|
901
|
+
for i in 0..<counts[s] where aliveNow[s][i] != 0 && aliveBefore[s][i] == 0 && streamInstances[s][i] >= 0 {
|
|
902
|
+
let instance = Int(streamInstances[s][i])
|
|
903
|
+
if instanceMaster[instance] { resetInstance(instance, 0) } else { for l in 0..<program.voices { resetInstance(instance, l) } }
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
for entry in program.state where readNow[entry.register] != 0 && readBefore[entry.register] == 0 {
|
|
907
|
+
if entry.master { registers[entry.register * width] = entry.initial } else { for l in 0..<program.voices { registers[entry.register * width + l] = entry.initial } }
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
for s in 0..<3 { aliveBefore[s].update(from: aliveNow[s], count: counts[s]) }
|
|
911
|
+
readBefore.update(from: readNow, count: program.registers)
|
|
912
|
+
made = true
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/// A stream's instructions that run (`alive`), their operands renamed, into `staging`: how many instructions.
|
|
916
|
+
private func compact(_ code: UnsafeMutablePointer<Int32>, _ count: Int, _ alive: UnsafeMutablePointer<UInt8>) -> Int {
|
|
917
|
+
let W = Int32(width)
|
|
918
|
+
var kept = 0
|
|
919
|
+
for i in 0..<count {
|
|
920
|
+
if alive[i] == 0 { continue }
|
|
921
|
+
let at = code + i * 12
|
|
922
|
+
let slot = staging + kept * 12
|
|
923
|
+
slot.update(from: at, count: 12)
|
|
924
|
+
for k in 0..<DSPOpcode(rawValue: at[0])!.arity {
|
|
925
|
+
let r = renameTable[Int(at[2 + k] / W)]
|
|
926
|
+
if r >= 0 { slot[2 + k] = r }
|
|
927
|
+
}
|
|
928
|
+
kept += 1
|
|
929
|
+
}
|
|
930
|
+
return kept
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/// A stream's schedule over the `count` instructions at `source`, into `out`: those reading or writing state, and
|
|
934
|
+
/// those both computed from state and needed by it this frame, run frame by frame (the serial part, returned); what
|
|
935
|
+
/// they need comes before, what needs them after; program order kept within each part (as machine.ts schedules).
|
|
936
|
+
private func reschedule(_ count: Int, from source: UnsafeMutablePointer<Int32>, into out: UnsafeMutablePointer<Int32>) -> Range<Int> {
|
|
937
|
+
let W = Int32(width)
|
|
938
|
+
let touches: UInt8 = 1, downstream: UInt8 = 2, upstream: UInt8 = 4
|
|
939
|
+
let tainted: UInt8 = 1, needed: UInt8 = 2
|
|
940
|
+
for i in 0..<count {
|
|
941
|
+
let slot = source + i * 12
|
|
942
|
+
var marks: UInt8 = (slot[8] >> 1) & 1 == 1 ? touches : 0
|
|
943
|
+
var fromState = false
|
|
944
|
+
forEachRead(slot) { r, state in
|
|
945
|
+
if state { marks |= touches }
|
|
946
|
+
if registerMarks[r] & tainted != 0 { fromState = true }
|
|
947
|
+
}
|
|
948
|
+
if marks & touches != 0 || fromState {
|
|
949
|
+
marks |= downstream
|
|
950
|
+
if (slot[8] >> 1) & 1 == 0 { registerMarks[Int(slot[1] / W)] |= tainted }
|
|
951
|
+
}
|
|
952
|
+
instructionMarks[i] = marks
|
|
953
|
+
}
|
|
954
|
+
for i in stride(from: count - 1, through: 0, by: -1) {
|
|
955
|
+
let slot = source + i * 12
|
|
956
|
+
guard instructionMarks[i] & touches != 0 || registerMarks[Int(slot[1] / W)] & needed != 0 else { continue }
|
|
957
|
+
instructionMarks[i] |= upstream
|
|
958
|
+
forEachRead(slot) { r, state in if !state { registerMarks[r] |= needed } }
|
|
959
|
+
}
|
|
960
|
+
var kept = 0
|
|
302
961
|
var from = 0
|
|
303
962
|
var to = 0
|
|
963
|
+
for part in 0..<3 {
|
|
964
|
+
if part == 1 { from = kept }
|
|
965
|
+
if part == 2 { to = kept }
|
|
966
|
+
for i in 0..<count {
|
|
967
|
+
let marks = instructionMarks[i]
|
|
968
|
+
let serial = marks & touches != 0 || (marks & downstream != 0 && marks & upstream != 0)
|
|
969
|
+
guard (part == 0 && !serial && marks & downstream == 0) || (part == 1 && serial) || (part == 2 && !serial && marks & downstream != 0) else { continue }
|
|
970
|
+
(out + kept * 12).update(from: source + i * 12, count: 12)
|
|
971
|
+
kept += 1
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
// The marks back to none, for the next time.
|
|
975
|
+
for i in 0..<count {
|
|
976
|
+
let slot = source + i * 12
|
|
977
|
+
registerMarks[Int(slot[1] / W)] = 0
|
|
978
|
+
forEachRead(slot) { r, _ in registerMarks[r] = 0 }
|
|
979
|
+
}
|
|
980
|
+
return from..<to
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
private static func decode(
|
|
984
|
+
_ code: [Int32], width: Int, state: Set<Int>, offsets: [Int], delays: (bases: [Int], lengths: [Int]), curves: [Int: Int]
|
|
985
|
+
) -> (UnsafeMutablePointer<Int32>, Int) {
|
|
986
|
+
var out: [Int32] = []
|
|
304
987
|
var pc = 0
|
|
305
988
|
while pc < code.count {
|
|
306
|
-
if pc == serial?.lowerBound { from = out.count / 12 }
|
|
307
|
-
if pc == serial?.upperBound { to = out.count / 12 }
|
|
308
989
|
let op = DSPOpcode(rawValue: code[pc])!
|
|
309
990
|
var slot = [Int32](repeating: 0, count: 12)
|
|
310
991
|
slot[0] = op.rawValue
|
|
@@ -323,41 +1004,77 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
323
1004
|
out += slot
|
|
324
1005
|
pc += 2 + op.arity + (op.builtin == nil ? 0 : 1)
|
|
325
1006
|
}
|
|
326
|
-
if pc == serial?.lowerBound { from = out.count / 12 }
|
|
327
|
-
if pc == serial?.upperBound { to = out.count / 12 }
|
|
328
1007
|
let pointer = UnsafeMutablePointer<Int32>.allocate(capacity: max(12, out.count))
|
|
329
1008
|
pointer.initialize(repeating: 0, count: max(12, out.count))
|
|
330
1009
|
out.withUnsafeBufferPointer { pointer.update(from: $0.baseAddress!, count: out.count) }
|
|
331
|
-
return (pointer, out.count / 12
|
|
1010
|
+
return (pointer, out.count / 12)
|
|
332
1011
|
}
|
|
333
1012
|
|
|
334
1013
|
/// 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
|
-
|
|
1014
|
+
private static func zeros(_ count: Int) -> UnsafeMutablePointer<Float> {
|
|
1015
|
+
let pointer = UnsafeMutablePointer<Float>.allocate(capacity: Swift.max(1, count))
|
|
1016
|
+
pointer.initialize(repeating: 0, count: Swift.max(1, count))
|
|
1017
|
+
return pointer
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
private static func copy(_ values: [Float]) -> UnsafeMutablePointer<Float> {
|
|
1021
|
+
let pointer = zeros(values.count)
|
|
1022
|
+
values.withUnsafeBufferPointer { pointer.update(from: $0.baseAddress!, count: values.count) }
|
|
1023
|
+
return pointer
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/// A wavetable's samples as the machine reads them (as machine.ts `tableLevels` makes them), off the audio thread,
|
|
1027
|
+
/// for `replaceTable`.
|
|
1028
|
+
public static func tableLevels(frames: [[Float]], cosines: [[Float]]? = nil) -> DSPBuffer {
|
|
1029
|
+
let (pointer, count) = levels(frames: frames, cosines: cosines)
|
|
1030
|
+
return DSPBuffer(taking: pointer, count: count)
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/// Replaces table `index`'s samples (`tableLevels`) from the next block, swapping them with `levels`, which then
|
|
1034
|
+
/// holds what the machine let go of, to free off the audio thread. False (nothing swapped) where they do not fit.
|
|
1035
|
+
public func replaceTable(_ index: Int, levels: DSPBuffer, frames: Int) -> Bool {
|
|
1036
|
+
guard index >= 0, index < tableCount, frames >= 1, levels.count == frames * DSPProgram.tableLevels * DSPProgram.tableFrame else { return false }
|
|
1037
|
+
var total = frames
|
|
1038
|
+
for i in 0..<tableFrames.count where i != index { total += tableFrames[i] }
|
|
1039
|
+
guard total <= DSPProgram.tableFramesMax else { return false }
|
|
1040
|
+
swap(&tablePointers[index], &levels.pointer)
|
|
1041
|
+
swap(&tableSizes[index], &levels.count)
|
|
1042
|
+
tableFrames[index] = frames
|
|
1043
|
+
return true
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
/// Replaces shape `index`'s points from the next block, swapping them with `points` (as `replaceTable`).
|
|
1047
|
+
public func replaceShape(_ index: Int, points: DSPBuffer) -> Bool {
|
|
1048
|
+
guard index >= 0, index < program.shapes.count, (2...DSPProgram.shapePointsMax).contains(points.count) else { return false }
|
|
1049
|
+
var total = points.count
|
|
1050
|
+
for i in 0..<shapeLengths.count where i != index { total += shapeLengths[i] }
|
|
1051
|
+
guard total <= DSPProgram.shapesPointsMax else { return false }
|
|
1052
|
+
swap(&shapePointers[index], &points.pointer)
|
|
1053
|
+
swap(&shapeLengths[index], &points.count)
|
|
1054
|
+
return true
|
|
347
1055
|
}
|
|
348
1056
|
|
|
349
|
-
///
|
|
350
|
-
///
|
|
351
|
-
|
|
352
|
-
|
|
1057
|
+
/// Replaces sample `index`'s audio, at `rate` frames a second, from the next block, swapping it with `audio` (as
|
|
1058
|
+
/// `replaceTable`); a sound playing it goes on from where it is, inside the new length.
|
|
1059
|
+
public func replaceSample(_ index: Int, audio: DSPBuffer, rate: Double) -> Bool {
|
|
1060
|
+
guard index >= 0, index < program.samples.count, rate >= 1000, rate <= 384_000, audio.count >= 2 else { return false }
|
|
1061
|
+
var total = audio.count
|
|
1062
|
+
for i in 0..<sampleLengths.count where i != index { total += sampleLengths[i] }
|
|
1063
|
+
guard total <= DSPProgram.samplesMax else { return false }
|
|
1064
|
+
swap(&samplePointers[index], &audio.pointer)
|
|
1065
|
+
swap(&sampleLengths[index], &audio.count)
|
|
1066
|
+
sampleRatios[index] = Float(rate / Double(Float(sampleRate)))
|
|
1067
|
+
sampleSprays[index] = UInt32(Swift.max(1, (rate * 0.02).rounded(.down)))
|
|
1068
|
+
return true
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/// A table's samples and how many floats: each level one with the harmonics past 512, 256 … dropped, 2048 samples,
|
|
1072
|
+
/// each its frames side by side.
|
|
1073
|
+
private static func levels(frames tableFrames: [[Float]], cosines tableCosines: [[Float]]?) -> (UnsafeMutablePointer<Float>, Int) {
|
|
353
1074
|
let N = DSPProgram.tableFrame
|
|
354
1075
|
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] = []
|
|
1076
|
+
let count = Swift.max(1, tableFrames.count) * levels * N
|
|
1077
|
+
let samples = zeros(count)
|
|
361
1078
|
var cosT = [Double](repeating: 0, count: N / 2)
|
|
362
1079
|
var sinT = [Double](repeating: 0, count: N / 2)
|
|
363
1080
|
for j in 0..<(N / 2) {
|
|
@@ -366,15 +1083,12 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
366
1083
|
}
|
|
367
1084
|
var re = [Double](repeating: 0, count: N)
|
|
368
1085
|
var im = [Double](repeating: 0, count: N)
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
bases.append(at)
|
|
372
|
-
let count = table.frames.count
|
|
373
|
-
frames.append(count)
|
|
1086
|
+
do {
|
|
1087
|
+
let count = tableFrames.count
|
|
374
1088
|
// A level's samples one after another, each its frames side by side: a read between two samples and two
|
|
375
1089
|
// frames is one span of memory.
|
|
376
|
-
func store(_ level: Int, _ n: Int, _ f: Int) -> Int {
|
|
377
|
-
for (f, amplitudes) in
|
|
1090
|
+
func store(_ level: Int, _ n: Int, _ f: Int) -> Int { (level * N + n) * count + f }
|
|
1091
|
+
for (f, amplitudes) in tableFrames.enumerated() {
|
|
378
1092
|
for level in 0..<levels {
|
|
379
1093
|
let limit = Swift.min(512 >> level, amplitudes.count)
|
|
380
1094
|
if level > 0 && limit == Swift.min(512 >> (level - 1), amplitudes.count) {
|
|
@@ -394,7 +1108,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
394
1108
|
im[N - k] = a / 2
|
|
395
1109
|
}
|
|
396
1110
|
// cos(kθ) = (e^{ikθ} + e^{−ikθ}) / 2
|
|
397
|
-
if let cosines =
|
|
1111
|
+
if let cosines = tableCosines?[f] {
|
|
398
1112
|
for k in 1...limit {
|
|
399
1113
|
let c = Double(cosines[k - 1])
|
|
400
1114
|
re[k] = c / 2
|
|
@@ -439,9 +1153,8 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
439
1153
|
for n in 0..<N { samples[store(level, n, f)] = Float(re[n]) }
|
|
440
1154
|
}
|
|
441
1155
|
}
|
|
442
|
-
at += count * levels * N
|
|
443
1156
|
}
|
|
444
|
-
return (samples,
|
|
1157
|
+
return (samples, count)
|
|
445
1158
|
}
|
|
446
1159
|
|
|
447
1160
|
// MARK: Events
|
|
@@ -455,6 +1168,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
455
1168
|
public func setParameter(at index: Int, _ value: Float) {
|
|
456
1169
|
guard index >= 0, index < parameterRegisters.count, value.isFinite else { return }
|
|
457
1170
|
targets[index] = Swift.min(Swift.max(value, parameterRanges[index].min), parameterRanges[index].max)
|
|
1171
|
+
retargeted = true
|
|
458
1172
|
}
|
|
459
1173
|
|
|
460
1174
|
/// The transport's tempo, in beats a minute (1 … 999), from the next block.
|
|
@@ -544,12 +1258,48 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
544
1258
|
return frames >= Double(DSPProgram.blockFrames) ? DSPProgram.blockFrames : Swift.max(1, Int(frames))
|
|
545
1259
|
}
|
|
546
1260
|
|
|
1261
|
+
/// The mode notes play in now: the voicing's parameter's, else the program's.
|
|
1262
|
+
private func currentMode() -> DSPProgram.Mode? {
|
|
1263
|
+
guard let p = modeParameter else { return program.mode }
|
|
1264
|
+
let m = targets[p].rounded()
|
|
1265
|
+
return m == 1 ? .mono : m == 2 ? .legato : nil
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
/// How many voices play now: the voicing's parameter's (1 … the program's), else the program's.
|
|
1269
|
+
private func voiceLimit() -> Int {
|
|
1270
|
+
guard let p = voicesParameter else { return program.voices }
|
|
1271
|
+
let n = targets[p].rounded()
|
|
1272
|
+
return n.isNaN || n < 1 ? 1 : n >= Float(program.voices) ? program.voices : Int(n)
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/// At a block's start, where the voicing turned: the voices past those that play now let go, and the held notes a
|
|
1276
|
+
/// mode keeps start again from the one voice that sounds (as machine.ts does).
|
|
1277
|
+
private func revoice() {
|
|
1278
|
+
let mode = currentMode()
|
|
1279
|
+
let modeIndex = mode == .mono ? 1 : mode == .legato ? 2 : 0
|
|
1280
|
+
let limit = mode != nil ? 1 : voiceLimit()
|
|
1281
|
+
guard limit != voicedLimit || modeIndex != voicedMode else { return }
|
|
1282
|
+
let first = voicedLimit < 0
|
|
1283
|
+
voicedLimit = limit
|
|
1284
|
+
voicedMode = modeIndex
|
|
1285
|
+
if first { return }
|
|
1286
|
+
for l in limit..<Self.lanes {
|
|
1287
|
+
laneGate[l] = 0
|
|
1288
|
+
laneHeld[l] = false
|
|
1289
|
+
}
|
|
1290
|
+
stackCount = 0
|
|
1291
|
+
if mode != nil && laneGate[0] > 0.5 {
|
|
1292
|
+
stack[0] = laneNote[0]
|
|
1293
|
+
stackCount = 1
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
547
1297
|
private func start(_ note: Int32, velocity: Int32) {
|
|
548
1298
|
if velocity <= 0 {
|
|
549
1299
|
release(note)
|
|
550
1300
|
return
|
|
551
1301
|
}
|
|
552
|
-
if let mode =
|
|
1302
|
+
if let mode = currentMode() {
|
|
553
1303
|
// One voice: the newest note, legato while another is held.
|
|
554
1304
|
unstack(note)
|
|
555
1305
|
if stackCount == Self.lanes {
|
|
@@ -572,7 +1322,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
572
1322
|
}
|
|
573
1323
|
return
|
|
574
1324
|
}
|
|
575
|
-
let voices =
|
|
1325
|
+
let voices = voiceLimit()
|
|
576
1326
|
var lane = -1
|
|
577
1327
|
for l in 0..<voices where lane < 0 && laneNote[l] == note && laneGate[l] > 0.5 { lane = l }
|
|
578
1328
|
var fresh = false
|
|
@@ -602,7 +1352,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
602
1352
|
}
|
|
603
1353
|
|
|
604
1354
|
private func release(_ note: Int32) {
|
|
605
|
-
if let mode =
|
|
1355
|
+
if let mode = currentMode() {
|
|
606
1356
|
unstack(note)
|
|
607
1357
|
guard laneNote[0] == note && laneGate[0] > 0.5 else { return }
|
|
608
1358
|
if stackCount > 0 {
|
|
@@ -645,6 +1395,8 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
645
1395
|
resetLane(l)
|
|
646
1396
|
}
|
|
647
1397
|
resetMaster()
|
|
1398
|
+
// Nothing sounds: what is set before the next block jumps there, with no glide (a preset loaded as data).
|
|
1399
|
+
snapped = true
|
|
648
1400
|
default:
|
|
649
1401
|
break
|
|
650
1402
|
}
|
|
@@ -747,12 +1499,24 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
747
1499
|
let W = width
|
|
748
1500
|
let R = registers
|
|
749
1501
|
let sr = sampleRate
|
|
750
|
-
// Parameters glide toward their targets, about 20 ms.
|
|
1502
|
+
// Parameters glide toward their targets, about 20 ms, and come to rest on them: one at rest is left as it is.
|
|
751
1503
|
let alpha = snapped ? Float(1) : 1 - exp(-Float(n) / (0.02 * sr))
|
|
752
1504
|
snapped = false
|
|
753
1505
|
for (i, register) in parameterRegisters.enumerated() {
|
|
754
1506
|
let at = R + register * W
|
|
755
|
-
|
|
1507
|
+
let now = at[0]
|
|
1508
|
+
let target = targets[i]
|
|
1509
|
+
if now == target { continue }
|
|
1510
|
+
var value = now + (target - now) * alpha
|
|
1511
|
+
if abs(target - value) <= 1e-6 * (1 + abs(target)) { value = target }
|
|
1512
|
+
at.update(repeating: value, count: W)
|
|
1513
|
+
}
|
|
1514
|
+
if retargeted || rerouted {
|
|
1515
|
+
let force = rerouted
|
|
1516
|
+
retargeted = false
|
|
1517
|
+
rerouted = false
|
|
1518
|
+
if !groupParameters.isEmpty || !choiceParameters.isEmpty || force { turnRegions(loading: false, force: force) }
|
|
1519
|
+
if voicesParameter != nil || modeParameter != nil { revoice() }
|
|
756
1520
|
}
|
|
757
1521
|
(R + 5 * W).update(repeating: Float(tempo), count: W)
|
|
758
1522
|
let beatStep = tempo / 60 / Double(sr)
|
|
@@ -780,7 +1544,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
780
1544
|
R[4 * W + k * V + l] = note >= 0 ? Float(note) : 0
|
|
781
1545
|
}
|
|
782
1546
|
}
|
|
783
|
-
run(blockCode, 0..<
|
|
1547
|
+
run(blockCode, 0..<blockRunning, lanes: lanes, trigger: trigger, frames: F)
|
|
784
1548
|
}
|
|
785
1549
|
if master != nil {
|
|
786
1550
|
// The newest note, the master stage's inputs (registers 7 … 10), in lane 0.
|
|
@@ -930,9 +1694,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
930
1694
|
}
|
|
931
1695
|
}
|
|
932
1696
|
}
|
|
933
|
-
var
|
|
1697
|
+
var next = range.lowerBound * 12
|
|
934
1698
|
let end = range.upperBound * 12
|
|
935
|
-
while
|
|
1699
|
+
while next < end {
|
|
1700
|
+
// A constant for the instruction: a variable the kernels' closures read would be checked for exclusive
|
|
1701
|
+
// access at every read.
|
|
1702
|
+
let at = next
|
|
1703
|
+
next += 12
|
|
936
1704
|
let op = code[at]
|
|
937
1705
|
let states = code[at + 8]
|
|
938
1706
|
// A register at this frame, or a state register's one value.
|
|
@@ -1121,11 +1889,18 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1121
1889
|
let releaseIn = register(6)
|
|
1122
1890
|
let curve = Int(code[at + 11]) - 1
|
|
1123
1891
|
let curved = curve >= 0
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
let
|
|
1127
|
-
|
|
1128
|
-
|
|
1892
|
+
// The curves' registers (a knob's, or modulated), read at the chunk's first frame a voice (as machine.ts
|
|
1893
|
+
// reads them), 1/16 … 16, 1 for NaN.
|
|
1894
|
+
let ra = curved ? R + curveRegisters[curve * 3] + shift : R
|
|
1895
|
+
let rd = curved ? R + curveRegisters[curve * 3 + 1] + shift : R
|
|
1896
|
+
let rr = curved ? R + curveRegisters[curve * 3 + 2] + shift : R
|
|
1897
|
+
for l in 0..<lanes {
|
|
1898
|
+
let ea = curved ? Self.exponent(ra[l]) : 1
|
|
1899
|
+
let ed = curved ? Self.exponent(rd[l]) : 1
|
|
1900
|
+
let er = curved ? Self.exponent(rr[l]) : 1
|
|
1901
|
+
// All three 1: no stage bent (what it put out kept all the same, for a curve turned later).
|
|
1902
|
+
let bent = curved && (ea != 1 || ed != 1 || er != 1)
|
|
1903
|
+
for fk in 0..<fc {
|
|
1129
1904
|
let el = fk * V + l
|
|
1130
1905
|
let gate = a[el]
|
|
1131
1906
|
var stage = s[l]
|
|
@@ -1142,7 +1917,7 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1142
1917
|
if curved && stage != before {
|
|
1143
1918
|
let last = s[8 * L + l]
|
|
1144
1919
|
if stage == 1 {
|
|
1145
|
-
level = last > 0 ? Float(Foundation.pow(Double(last), 1 / ea)) : 0
|
|
1920
|
+
level = last > 0 ? (ea == 1 ? last : Float(Foundation.pow(Double(last), 1 / ea))) : 0
|
|
1146
1921
|
} else if stage == 4 {
|
|
1147
1922
|
level = last
|
|
1148
1923
|
s[9 * L + l] = last
|
|
@@ -1182,18 +1957,21 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1182
1957
|
s[l] = stage
|
|
1183
1958
|
s[L + l] = level
|
|
1184
1959
|
var out = level
|
|
1185
|
-
if
|
|
1960
|
+
if bent {
|
|
1961
|
+
// An exponent of 1 is no bend: the stage as it is, with no power worked out.
|
|
1186
1962
|
if stage == 1 {
|
|
1187
|
-
out = Float(Foundation.pow(Double(level), ea))
|
|
1963
|
+
out = ea == 1 ? level : Float(Foundation.pow(Double(level), ea))
|
|
1188
1964
|
} else if stage == 2 {
|
|
1189
1965
|
let span = 1 - sustain
|
|
1190
|
-
|
|
1966
|
+
let along = Swift.max(0, (level - sustain) / span)
|
|
1967
|
+
out = span > 1e-6 ? sustain + span * (ed == 1 ? along : Float(Foundation.pow(Double(along), ed))) : level
|
|
1191
1968
|
} else if stage == 4 {
|
|
1192
1969
|
let from = s[9 * L + l]
|
|
1193
|
-
|
|
1970
|
+
let along = Swift.min(1, level / from)
|
|
1971
|
+
out = from > 0 ? from * (er == 1 ? along : Float(Foundation.pow(Double(along), er))) : 0
|
|
1194
1972
|
}
|
|
1195
|
-
s[8 * L + l] = out
|
|
1196
1973
|
}
|
|
1974
|
+
if curved { s[8 * L + l] = out }
|
|
1197
1975
|
d[el] = out
|
|
1198
1976
|
}
|
|
1199
1977
|
}
|
|
@@ -1234,12 +2012,12 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1234
2012
|
// wavetable(table, freq, position): the level that cannot alias at freq (worked out again only when freq
|
|
1235
2013
|
// changes), read between samples and between frames.
|
|
1236
2014
|
let s = S + Int(code[at + 7])
|
|
1237
|
-
let T = tableSamples
|
|
1238
2015
|
// The table is a constant register's, the same in every lane.
|
|
1239
2016
|
let index = a[0]
|
|
1240
2017
|
let t = index >= 0 && Int(exactly: index.rounded(.down)).map { $0 < tableCount } == true ? Int(index) : 0
|
|
1241
2018
|
let frames = tableFrames.isEmpty ? 1 : tableFrames[t]
|
|
1242
|
-
let
|
|
2019
|
+
let T = tablePointers[t]
|
|
2020
|
+
let base = 0
|
|
1243
2021
|
for fk in 0..<fc {
|
|
1244
2022
|
for l in 0..<lanes {
|
|
1245
2023
|
let el = fk * V + l
|
|
@@ -1306,13 +2084,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1306
2084
|
}
|
|
1307
2085
|
case 52:
|
|
1308
2086
|
// shape(shape, x): x 0 … 1 along it, between points.
|
|
1309
|
-
let P = shapePoints
|
|
1310
2087
|
for fk in 0..<fc {
|
|
1311
2088
|
for l in 0..<lanes {
|
|
1312
2089
|
let el = fk * V + l
|
|
1313
2090
|
let t = Self.index(a[el], shapeLengths.count)
|
|
1314
2091
|
let points = shapeLengths.isEmpty ? 1 : shapeLengths[t]
|
|
1315
|
-
let
|
|
2092
|
+
let P = shapePointers[t]
|
|
2093
|
+
let base = 0
|
|
1316
2094
|
let x = b[el]
|
|
1317
2095
|
let position = (x > 0 ? (x < 1 ? x : 1) : 0) * Float(points - 1)
|
|
1318
2096
|
let i0 = Int(position.rounded(.down))
|
|
@@ -1326,13 +2104,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1326
2104
|
// between samples; silent past either end. Where it plays is a whole sample and the fraction past it,
|
|
1327
2105
|
// so it keeps time.
|
|
1328
2106
|
let s = S + Int(code[at + 7])
|
|
1329
|
-
let A = sampleAudio
|
|
1330
2107
|
for fk in 0..<fc {
|
|
1331
2108
|
for l in 0..<lanes {
|
|
1332
2109
|
let el = fk * V + l
|
|
1333
2110
|
let t = Self.index(a[el], sampleLengths.count)
|
|
1334
2111
|
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
1335
|
-
let
|
|
2112
|
+
let A = samplePointers[t]
|
|
2113
|
+
let base = 0
|
|
1336
2114
|
if s[2 * L + l] != trigger[l] {
|
|
1337
2115
|
s[2 * L + l] = trigger[l]
|
|
1338
2116
|
let start = c[el]
|
|
@@ -1363,13 +2141,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1363
2141
|
// tableread(table, phase, freq, position): wavetable's read, at a phase the program keeps (wrapped to
|
|
1364
2142
|
// 0 … 1), at the level that cannot alias at freq (worked out again only when freq changes).
|
|
1365
2143
|
let s = S + Int(code[at + 7])
|
|
1366
|
-
let T = tableSamples
|
|
1367
2144
|
let e = register(5)
|
|
1368
2145
|
// The table is a constant register's, the same in every lane.
|
|
1369
2146
|
let index = a[0]
|
|
1370
2147
|
let t = index >= 0 && Int(exactly: index.rounded(.down)).map { $0 < tableCount } == true ? Int(index) : 0
|
|
1371
2148
|
let frames = tableFrames.isEmpty ? 1 : tableFrames[t]
|
|
1372
|
-
let
|
|
2149
|
+
let T = tablePointers[t]
|
|
2150
|
+
let base = 0
|
|
1373
2151
|
for fk in 0..<fc {
|
|
1374
2152
|
for l in 0..<lanes {
|
|
1375
2153
|
let el = fk * V + l
|
|
@@ -1408,13 +2186,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1408
2186
|
case 79:
|
|
1409
2187
|
// loop(sample, rate, start): play's, but round and round: past either end it comes in at the other.
|
|
1410
2188
|
let s = S + Int(code[at + 7])
|
|
1411
|
-
let A = sampleAudio
|
|
1412
2189
|
for fk in 0..<fc {
|
|
1413
2190
|
for l in 0..<lanes {
|
|
1414
2191
|
let el = fk * V + l
|
|
1415
2192
|
let t = Self.index(a[el], sampleLengths.count)
|
|
1416
2193
|
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
1417
|
-
let
|
|
2194
|
+
let A = samplePointers[t]
|
|
2195
|
+
let base = 0
|
|
1418
2196
|
if s[2 * L + l] != trigger[l] {
|
|
1419
2197
|
s[2 * L + l] = trigger[l]
|
|
1420
2198
|
let start = c[el]
|
|
@@ -1443,13 +2221,13 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1443
2221
|
let s = S + Int(code[at + 7])
|
|
1444
2222
|
let lengthIn = register(5)
|
|
1445
2223
|
let pitchIn = register(6)
|
|
1446
|
-
let A = sampleAudio
|
|
1447
2224
|
for fk in 0..<fc {
|
|
1448
2225
|
for l in 0..<lanes {
|
|
1449
2226
|
let el = fk * V + l
|
|
1450
2227
|
let t = Self.index(a[el], sampleLengths.count)
|
|
1451
2228
|
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
1452
|
-
let
|
|
2229
|
+
let A = samplePointers[t]
|
|
2230
|
+
let base = 0
|
|
1453
2231
|
let ratio = sampleRatios.isEmpty ? 1 : sampleRatios[t]
|
|
1454
2232
|
let density = c[el]
|
|
1455
2233
|
var countdown = s[l]
|
|
@@ -1508,10 +2286,10 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1508
2286
|
// modsum(routes): Σ amount × source over the list, route by route. An amount is a parameter, the same
|
|
1509
2287
|
// in every lane, so a route at 0 is passed over once for all of them and costs nothing.
|
|
1510
2288
|
each { d[$0] = 0 }
|
|
1511
|
-
let index = Self.index(a[0],
|
|
1512
|
-
guard index <
|
|
1513
|
-
var k =
|
|
1514
|
-
let end =
|
|
2289
|
+
let index = Self.index(a[0], routeCount)
|
|
2290
|
+
guard index < routeCount else { break }
|
|
2291
|
+
var k = index * DSPProgram.routesMax
|
|
2292
|
+
let end = k + routeCounts[index]
|
|
1515
2293
|
while k < end {
|
|
1516
2294
|
let amount = R + routeRegisters[k + 1]
|
|
1517
2295
|
let source = R + routeRegisters[k] + (routeState[k] ? 0 : shift)
|
|
@@ -1555,7 +2333,57 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
1555
2333
|
default:
|
|
1556
2334
|
break
|
|
1557
2335
|
}
|
|
1558
|
-
at += 12
|
|
1559
2336
|
}
|
|
1560
2337
|
}
|
|
1561
2338
|
}
|
|
2339
|
+
|
|
2340
|
+
/// Memory a machine reads (a table's samples, a shape's points, a sample's audio), made off the audio thread and
|
|
2341
|
+
/// swapped into a machine whole (`replaceTable` …). After a swap it holds what the machine let go of: freed with it,
|
|
2342
|
+
/// off the audio thread.
|
|
2343
|
+
/// A fixed number of values in memory of their own, each changed in place: what the machine reads as it renders and
|
|
2344
|
+
/// swaps when data is replaced, with no exclusivity checks (as an array property changed in place would need).
|
|
2345
|
+
struct Slots<Element> {
|
|
2346
|
+
let base: UnsafeMutablePointer<Element>
|
|
2347
|
+
let count: Int
|
|
2348
|
+
|
|
2349
|
+
init(_ values: [Element]) {
|
|
2350
|
+
count = values.count
|
|
2351
|
+
base = .allocate(capacity: Swift.max(1, values.count))
|
|
2352
|
+
base.initialize(from: values, count: values.count)
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
var isEmpty: Bool { count == 0 }
|
|
2356
|
+
|
|
2357
|
+
subscript(i: Int) -> Element {
|
|
2358
|
+
get { base[i] }
|
|
2359
|
+
nonmutating set { base[i] = newValue }
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
func deallocate() {
|
|
2363
|
+
base.deinitialize(count: count)
|
|
2364
|
+
base.deallocate()
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
public final class DSPBuffer: @unchecked Sendable {
|
|
2369
|
+
public fileprivate(set) var pointer: UnsafeMutablePointer<Float>
|
|
2370
|
+
public fileprivate(set) var count: Int
|
|
2371
|
+
|
|
2372
|
+
/// `values`, copied.
|
|
2373
|
+
public init(_ values: [Float]) {
|
|
2374
|
+
let pointer = UnsafeMutablePointer<Float>.allocate(capacity: Swift.max(1, values.count))
|
|
2375
|
+
pointer.initialize(repeating: 0, count: Swift.max(1, values.count))
|
|
2376
|
+
values.withUnsafeBufferPointer { if let base = $0.baseAddress { pointer.update(from: base, count: values.count) } }
|
|
2377
|
+
self.pointer = pointer
|
|
2378
|
+
count = values.count
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
fileprivate init(taking pointer: UnsafeMutablePointer<Float>, count: Int) {
|
|
2382
|
+
self.pointer = pointer
|
|
2383
|
+
self.count = count
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
deinit {
|
|
2387
|
+
pointer.deallocate()
|
|
2388
|
+
}
|
|
2389
|
+
}
|