@swiftbrowser/native 0.19.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/README.md +34 -11
- package/dist/blob.d.ts +85 -11
- package/dist/blob.d.ts.map +1 -1
- package/dist/blob.js +184 -34
- package/dist/blob.js.map +1 -1
- package/dist/bridge.d.ts +33 -2
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js.map +1 -1
- package/dist/fetch.d.ts +19 -11
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +105 -41
- package/dist/fetch.js.map +1 -1
- package/dist/hostRequests.d.ts +3 -1
- package/dist/hostRequests.d.ts.map +1 -1
- package/dist/hostRequests.js +47 -6
- package/dist/hostRequests.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/runtime.js +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/webSocket.d.ts.map +1 -1
- package/dist/webSocket.js +13 -1
- package/dist/webSocket.js.map +1 -1
- package/package.json +3 -3
- package/src/blob.ts +215 -34
- package/src/bridge.ts +27 -2
- package/src/fetch.ts +99 -41
- package/src/hostRequests.ts +62 -8
- package/src/index.ts +1 -2
- package/src/runtime.ts +1 -1
- package/src/webSocket.ts +10 -1
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSP/ProgramNode.swift +185 -24
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSP/ScopeView.swift +79 -0
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSPCore/Machine.swift +2075 -213
- package/swift/Packages/SwiftBrowserDSP/Sources/SwiftBrowserDSPCore/Program.swift +407 -16
- package/swift/Packages/SwiftBrowserShader/Package.swift +31 -0
- package/swift/Packages/SwiftBrowserShader/README.md +45 -0
- package/swift/Packages/SwiftBrowserShader/Sources/SwiftBrowserShader/ShaderRenderer.swift +103 -0
- package/swift/Packages/SwiftBrowserShader/Sources/SwiftBrowserShader/ShaderView.swift +168 -0
- package/swift/Packages/SwiftBrowserShader/Sources/SwiftBrowserShaderCore/ShaderProps.swift +120 -0
- package/swift/Sources/SwiftBrowserNative/Host/BodyStream.swift +166 -0
- package/swift/Sources/SwiftBrowserNative/Host/FilesBridge.swift +141 -7
- package/swift/Sources/SwiftBrowserNative/Host/HostBridge.swift +47 -1
- package/swift/Sources/SwiftBrowserNative/Host/HostRequestBridge.swift +98 -29
- package/swift/Sources/SwiftBrowserNative/Runtime/NativeFunctions.swift +1 -1
- package/swift/Sources/SwiftBrowserNative/Runtime/ViewRegistry.swift +57 -0
- package/swift/Sources/SwiftBrowserNative/Views/ElementView.swift +11 -7
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Controls.swift +28 -6
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Drawing.swift +378 -0
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/Navigation.swift +15 -10
- package/swift/Sources/SwiftBrowserNative/Views/Kinds/PopTap.swift +31 -28
- package/swift/Sources/SwiftBrowserNative/Views/StyleModifier.swift +142 -0
- package/swift/Sources/SwiftBrowserProtocol/Drawing.swift +306 -0
- package/swift/Sources/SwiftBrowserProtocol/Props.swift +5 -2
- package/swift/Sources/SwiftBrowserProtocol/RendererEvent.swift +24 -3
- package/swift/Sources/SwiftBrowserProtocol/Style.swift +87 -0
- package/swift/Sources/SwiftBrowserProtocol/TextChange.swift +17 -0
- package/wasm/SwiftBrowserNative.wasm +2 -2
- package/dist/native.d.ts +0 -19
- package/dist/native.d.ts.map +0 -1
- package/dist/native.js +0 -17
- package/dist/native.js.map +0 -1
- package/src/native.ts +0 -35
- package/swift/Sources/SwiftBrowserNative/Runtime/NativeRegistry.swift +0 -53
|
@@ -17,21 +17,121 @@ import Foundation
|
|
|
17
17
|
/// else a free one, else the oldest released, else the oldest; a lane's gate
|
|
18
18
|
/// is up from its note on to its note off, or under the sustain pedal until
|
|
19
19
|
/// the pedal lifts, and it sounds while the gate is up and then while any
|
|
20
|
-
/// `adsr` in the program has not finished its release.
|
|
20
|
+
/// `adsr` in the program has not finished its release. A `mono` or `legato`
|
|
21
|
+
/// program is one voice playing the newest of the notes held (up to 16
|
|
22
|
+
/// remembered): releasing it goes back to the one before, and `mono` starts
|
|
23
|
+
/// its envelopes over at every change of note, `legato` only when no note
|
|
24
|
+
/// was held.
|
|
25
|
+
///
|
|
26
|
+
/// The transport: `tempo` beats a minute (120 until set) and `beat`, which
|
|
27
|
+
/// advances every frame from 0 (or where `seek` puts it), sounding or not.
|
|
28
|
+
/// An event given a beat waits for it (up to 1024 at once) and applies on
|
|
29
|
+
/// the frame nearest it, the block split there, so a sequencer that
|
|
30
|
+
/// schedules ahead plays in time; one whose beat has passed applies at the
|
|
31
|
+
/// next block. All notes off and all sound off drop what waits.
|
|
32
|
+
///
|
|
33
|
+
/// Memory is sized in `init` and never after: the wavetables (built from
|
|
34
|
+
/// their harmonics with the JavaScript machine's inverse FFT, 2048 samples a
|
|
35
|
+
/// frame at ten band-limited levels), a ring buffer per voice for each
|
|
36
|
+
/// `delay`, the shapes' points and the samples' audio.
|
|
37
|
+
///
|
|
38
|
+
/// With a `master` stage, the voices' sum goes through it once a frame, in
|
|
39
|
+
/// lane 0, whether or not a voice sounds; its state and its builtins' are
|
|
40
|
+
/// reset only in `init` and on `reset`.
|
|
41
|
+
///
|
|
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 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
|
+
/// then the next frame's, so the loops over lanes stay vector loops; the results are the same.
|
|
21
46
|
public final class DSPMachine: @unchecked Sendable {
|
|
22
47
|
public let program: DSPProgram
|
|
23
48
|
public let sampleRate: Float
|
|
24
49
|
|
|
25
50
|
private static let lanes = 16
|
|
51
|
+
/// Frames a chunk has: each instruction runs over a chunk's frames together (block processing).
|
|
52
|
+
private static let frames = 16
|
|
53
|
+
/// The registers, each `width` values: a chunk's frames one after another, each its lanes (as many as the
|
|
54
|
+
/// program has voices) side by side, so an instruction runs over lanes as a SIMD vector and a builtin's state
|
|
55
|
+
/// follows its lanes; a state register's value is its lane's at the first frame.
|
|
26
56
|
private let registers: UnsafeMutablePointer<Float>
|
|
57
|
+
private let width: Int
|
|
27
58
|
private let state: UnsafeMutablePointer<Float>
|
|
28
59
|
private let stateCount: Int
|
|
29
60
|
private let noise: UnsafeMutablePointer<UInt32>
|
|
30
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).
|
|
31
64
|
private let blockCode: UnsafeMutablePointer<Int32>
|
|
32
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
|
|
33
68
|
private let sampleCode: UnsafeMutablePointer<Int32>
|
|
34
|
-
private
|
|
69
|
+
private var sampleCount: Int
|
|
70
|
+
private let masterCode: UnsafeMutablePointer<Int32>
|
|
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>
|
|
133
|
+
/// Per instance: whether the master stage runs it.
|
|
134
|
+
private let instanceMaster: [Bool]
|
|
35
135
|
/// Each instance's builtin and where its state starts (in floats, lanes included; a noise instance's in `noise`).
|
|
36
136
|
private let instanceKinds: [DSPBuiltin]
|
|
37
137
|
private let instanceOffsets: [Int]
|
|
@@ -42,6 +142,60 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
42
142
|
private let parameterIndices: [String: Int]
|
|
43
143
|
private let targets: UnsafeMutablePointer<Float>
|
|
44
144
|
private var snapped = true
|
|
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>
|
|
153
|
+
private let tableCount: 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]
|
|
157
|
+
/// The delays' samples: each instance's buffer per lane, a power of two long; by instance, where it starts and its length.
|
|
158
|
+
private let delaySamples: UnsafeMutablePointer<Float>
|
|
159
|
+
private let delaySampleCount: Int
|
|
160
|
+
private let delayBases: [Int]
|
|
161
|
+
private let delayLengths: [Int]
|
|
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.
|
|
173
|
+
private let routeRegisters: UnsafeMutablePointer<Int>
|
|
174
|
+
/// Whether each route's register is state (one value a lane, not one a frame).
|
|
175
|
+
private let routeState: UnsafeMutablePointer<Bool>
|
|
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>
|
|
45
199
|
|
|
46
200
|
private let laneNote: UnsafeMutablePointer<Int32>
|
|
47
201
|
private let laneGate: UnsafeMutablePointer<Float>
|
|
@@ -50,31 +204,60 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
50
204
|
private let laneActive: UnsafeMutablePointer<Float>
|
|
51
205
|
private let laneAge: UnsafeMutablePointer<Double>
|
|
52
206
|
private let trigger: UnsafeMutablePointer<Float>
|
|
207
|
+
/// The master stage's trigger: counts the notes (as the voice a mono program's starts over at).
|
|
208
|
+
private let masterTrigger: UnsafeMutablePointer<Float>
|
|
209
|
+
/// The lane of the newest note, for a master stage that reads it (-1 before the first).
|
|
210
|
+
private var newestLane = -1
|
|
53
211
|
private var age: Double = 0
|
|
54
212
|
private var pedal = false
|
|
55
213
|
private var bend: Double = 0
|
|
214
|
+
private var tempo: Double = 120
|
|
215
|
+
private var beat: Double = 0
|
|
216
|
+
/// A mono program's held notes, oldest first.
|
|
217
|
+
private let stack: UnsafeMutablePointer<Int32>
|
|
218
|
+
private var stackCount = 0
|
|
219
|
+
/// Events waiting for their beat, soonest first: the beat, then the kind (note on, note off, controller,
|
|
220
|
+
/// bend) and two values.
|
|
221
|
+
private static let queueCapacity = 1024
|
|
222
|
+
private let queueBeat: UnsafeMutablePointer<Double>
|
|
223
|
+
private let queueEvent: UnsafeMutablePointer<Int32>
|
|
224
|
+
private var queueCount = 0
|
|
56
225
|
|
|
57
226
|
public init(program: DSPProgram, sampleRate: Double) {
|
|
58
227
|
let L = Self.lanes
|
|
59
228
|
self.program = program
|
|
60
229
|
self.sampleRate = Float(sampleRate)
|
|
61
|
-
|
|
62
|
-
|
|
230
|
+
let W = program.voices * Self.frames
|
|
231
|
+
width = W
|
|
232
|
+
registers = .allocate(capacity: program.registers * W)
|
|
233
|
+
registers.initialize(repeating: 0, count: program.registers * W)
|
|
234
|
+
let stateRegisters = Set(program.state.map(\.register))
|
|
235
|
+
var isMaster = [Bool](repeating: false, count: program.instances.count)
|
|
236
|
+
if let master = program.master {
|
|
237
|
+
var pc = 0
|
|
238
|
+
while pc < master.code.count {
|
|
239
|
+
let op = DSPOpcode(rawValue: master.code[pc])!
|
|
240
|
+
if op.builtin != nil { isMaster[Int(master.code[pc + 2 + op.arity])] = true }
|
|
241
|
+
pc += 2 + op.arity + (op.builtin == nil ? 0 : 1)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
instanceMaster = isMaster
|
|
63
245
|
var kinds: [DSPBuiltin] = []
|
|
64
246
|
var offsets: [Int] = []
|
|
65
247
|
var envelopes: [Int] = []
|
|
66
248
|
var filters: [Int] = []
|
|
67
249
|
var words = 0
|
|
68
250
|
var noises = 0
|
|
69
|
-
for builtin in program.instances {
|
|
251
|
+
for (i, builtin) in program.instances.enumerated() {
|
|
70
252
|
kinds.append(builtin)
|
|
71
253
|
if builtin == .noise {
|
|
72
254
|
offsets.append(noises * L)
|
|
73
255
|
noises += 1
|
|
74
256
|
} else {
|
|
75
257
|
offsets.append(words * L)
|
|
76
|
-
if builtin == .adsr { envelopes.append(words * L) }
|
|
77
|
-
|
|
258
|
+
if builtin == .adsr && !isMaster[i] { envelopes.append(words * L) }
|
|
259
|
+
// The filters' integrators, a one-pole's, a DC blocker's and a comb's loop.
|
|
260
|
+
if [.lowpass, .highpass, .bandpass, .onepole, .dcblock, .comb].contains(builtin) { filters.append(words * L) }
|
|
78
261
|
words += builtin.words
|
|
79
262
|
}
|
|
80
263
|
}
|
|
@@ -88,19 +271,195 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
88
271
|
noiseCount = max(1, noises) * L
|
|
89
272
|
noise = .allocate(capacity: noiseCount)
|
|
90
273
|
noise.initialize(repeating: 1, count: noiseCount)
|
|
91
|
-
(
|
|
92
|
-
|
|
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))
|
|
278
|
+
tableCount = program.tables.count
|
|
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))
|
|
293
|
+
for (i, list) in program.routes.enumerated() {
|
|
294
|
+
for (k, r) in list.enumerated() {
|
|
295
|
+
routeRegisters[i * routesMax + k] = r * W
|
|
296
|
+
routeState[i * routesMax + k] = stateRegisters.contains(r)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
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
|
|
93
330
|
for constant in program.constants {
|
|
94
|
-
|
|
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))) })
|
|
336
|
+
// Each delay's buffer: its longest time (a constant register) at this sample rate, rounded up to a power of
|
|
337
|
+
// two, per voice (the master stage's, once).
|
|
338
|
+
let constantValues = Dictionary(program.constants.map { ($0.register, $0.value) }, uniquingKeysWith: { _, last in last })
|
|
339
|
+
var bases = [Int](repeating: 0, count: program.instances.count)
|
|
340
|
+
var lengths = [Int](repeating: 0, count: program.instances.count)
|
|
341
|
+
var delayTotal = 0
|
|
342
|
+
for code in [program.sample, program.master?.code ?? []] {
|
|
343
|
+
var pc = 0
|
|
344
|
+
while pc < code.count {
|
|
345
|
+
let op = DSPOpcode(rawValue: code[pc])!
|
|
346
|
+
if let longestAt = op.longest {
|
|
347
|
+
let instance = Int(code[pc + 2 + op.arity])
|
|
348
|
+
let longest = Double(Swift.min(Swift.max(constantValues[Int(code[pc + 1 + longestAt])] ?? 1, 0), 2))
|
|
349
|
+
var length = 4
|
|
350
|
+
while Double(length) < (longest * Double(Float(sampleRate))).rounded(.up) + 2 { length *= 2 }
|
|
351
|
+
bases[instance] = delayTotal
|
|
352
|
+
lengths[instance] = length
|
|
353
|
+
delayTotal += length * (isMaster[instance] ? 1 : L)
|
|
354
|
+
}
|
|
355
|
+
pc += 2 + op.arity + (op.builtin == nil ? 0 : 1)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
delayBases = bases
|
|
359
|
+
delayLengths = lengths
|
|
360
|
+
delaySampleCount = Swift.max(1, delayTotal)
|
|
361
|
+
delaySamples = .allocate(capacity: delaySampleCount)
|
|
362
|
+
delaySamples.initialize(repeating: 0, count: delaySampleCount)
|
|
363
|
+
curveRegisters = program.curves.flatMap { [$0.attack * W, $0.decay * W, $0.release * W] }
|
|
364
|
+
let curves = Dictionary(program.curves.enumerated().map { ($1.instance, $0 + 1) }, uniquingKeysWith: { _, last in last })
|
|
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
|
+
}
|
|
95
438
|
}
|
|
96
|
-
|
|
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)
|
|
452
|
+
for constant in program.constants {
|
|
453
|
+
(registers + constant.register * W).update(repeating: constant.value, count: W)
|
|
454
|
+
}
|
|
455
|
+
registers.update(repeating: Float(sampleRate), count: W)
|
|
97
456
|
parameterRegisters = program.parameters.map(\.register)
|
|
98
457
|
parameterRanges = program.parameters.map { ($0.min, $0.max) }
|
|
99
458
|
parameterIndices = Dictionary(program.parameters.enumerated().map { ($1.name, $0) }, uniquingKeysWith: { first, _ in first })
|
|
100
459
|
targets = .allocate(capacity: max(1, program.parameters.count))
|
|
101
460
|
for (i, parameter) in program.parameters.enumerated() {
|
|
102
461
|
targets[i] = parameter.defaultValue
|
|
103
|
-
(registers + parameter.register *
|
|
462
|
+
(registers + parameter.register * W).update(repeating: parameter.defaultValue, count: W)
|
|
104
463
|
}
|
|
105
464
|
laneNote = .allocate(capacity: L)
|
|
106
465
|
laneNote.initialize(repeating: -1, count: L)
|
|
@@ -116,37 +475,684 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
116
475
|
laneAge.initialize(repeating: 0, count: L)
|
|
117
476
|
trigger = .allocate(capacity: L)
|
|
118
477
|
trigger.initialize(repeating: 0, count: L)
|
|
478
|
+
masterTrigger = .allocate(capacity: 1)
|
|
479
|
+
masterTrigger.initialize(to: 1)
|
|
480
|
+
stack = .allocate(capacity: L)
|
|
481
|
+
stack.initialize(repeating: 0, count: L)
|
|
482
|
+
queueBeat = .allocate(capacity: Self.queueCapacity)
|
|
483
|
+
queueBeat.initialize(repeating: 0, count: Self.queueCapacity)
|
|
484
|
+
queueEvent = .allocate(capacity: Self.queueCapacity * 3)
|
|
485
|
+
queueEvent.initialize(repeating: 0, count: Self.queueCapacity * 3)
|
|
119
486
|
for lane in 0..<L { resetLane(lane) }
|
|
487
|
+
resetMaster()
|
|
488
|
+
// The streams as they run: the regions its parameters turn on, scheduled.
|
|
489
|
+
turnRegions(loading: true)
|
|
490
|
+
revoice()
|
|
120
491
|
}
|
|
121
492
|
|
|
122
493
|
deinit {
|
|
123
|
-
for pointer in [registers, state, targets, laneGate, laneVelocity, laneActive, trigger] { pointer.deallocate() }
|
|
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()
|
|
124
502
|
noise.deallocate()
|
|
503
|
+
routeRegisters.deallocate()
|
|
504
|
+
routeCounts.deallocate()
|
|
505
|
+
routeState.deallocate()
|
|
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()
|
|
512
|
+
delaySamples.deallocate()
|
|
125
513
|
blockCode.deallocate()
|
|
126
514
|
sampleCode.deallocate()
|
|
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() }
|
|
127
525
|
laneNote.deallocate()
|
|
526
|
+
stack.deallocate()
|
|
527
|
+
queueBeat.deallocate()
|
|
528
|
+
queueEvent.deallocate()
|
|
128
529
|
laneHeld.deallocate()
|
|
129
530
|
laneAge.deallocate()
|
|
130
531
|
}
|
|
131
532
|
|
|
132
|
-
/// Instructions at a stride of
|
|
133
|
-
|
|
533
|
+
/// Instructions at a stride of 12 (op, destination, five operands, state offset, which of slots 1 … 6 are state
|
|
534
|
+
/// registers as bits, then a delay line's buffer: where it starts and its length, then an envelope's curve, 1 up,
|
|
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
|
|
959
|
+
var from = 0
|
|
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) {
|
|
134
984
|
var out: [Int32] = []
|
|
135
985
|
var pc = 0
|
|
136
986
|
while pc < code.count {
|
|
137
987
|
let op = DSPOpcode(rawValue: code[pc])!
|
|
138
|
-
var slot = [Int32](repeating: 0, count:
|
|
988
|
+
var slot = [Int32](repeating: 0, count: 12)
|
|
139
989
|
slot[0] = op.rawValue
|
|
140
|
-
|
|
141
|
-
|
|
990
|
+
for k in 0..<(1 + op.arity) {
|
|
991
|
+
let register = Int(code[pc + 1 + k])
|
|
992
|
+
slot[1 + k] = Int32(register * width)
|
|
993
|
+
if state.contains(register) { slot[8] |= 1 << (1 + k) }
|
|
994
|
+
}
|
|
142
995
|
if op.builtin != nil { slot[7] = Int32(offsets[Int(code[pc + 2 + op.arity])]) }
|
|
996
|
+
if op == .adsr { slot[11] = Int32(curves[Int(code[pc + 2 + op.arity])] ?? 0) }
|
|
997
|
+
if op.longest != nil {
|
|
998
|
+
let instance = Int(code[pc + 2 + op.arity])
|
|
999
|
+
slot[9] = Int32(delays.bases[instance])
|
|
1000
|
+
slot[10] = Int32(delays.lengths[instance])
|
|
1001
|
+
}
|
|
143
1002
|
out += slot
|
|
144
1003
|
pc += 2 + op.arity + (op.builtin == nil ? 0 : 1)
|
|
145
1004
|
}
|
|
146
|
-
let pointer = UnsafeMutablePointer<Int32>.allocate(capacity: max(
|
|
147
|
-
pointer.initialize(repeating: 0, count: max(
|
|
1005
|
+
let pointer = UnsafeMutablePointer<Int32>.allocate(capacity: max(12, out.count))
|
|
1006
|
+
pointer.initialize(repeating: 0, count: max(12, out.count))
|
|
148
1007
|
out.withUnsafeBufferPointer { pointer.update(from: $0.baseAddress!, count: out.count) }
|
|
149
|
-
return (pointer, out.count /
|
|
1008
|
+
return (pointer, out.count / 12)
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/// Lists one after another: where each starts and its length.
|
|
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
|
|
1053
|
+
}
|
|
1054
|
+
|
|
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) {
|
|
1072
|
+
let N = DSPProgram.tableFrame
|
|
1073
|
+
let levels = DSPProgram.tableLevels
|
|
1074
|
+
let count = Swift.max(1, tableFrames.count) * levels * N
|
|
1075
|
+
let samples = zeros(count)
|
|
1076
|
+
var cosT = [Double](repeating: 0, count: N / 2)
|
|
1077
|
+
var sinT = [Double](repeating: 0, count: N / 2)
|
|
1078
|
+
for j in 0..<(N / 2) {
|
|
1079
|
+
cosT[j] = Foundation.cos(2 * Double.pi * Double(j) / Double(N))
|
|
1080
|
+
sinT[j] = Foundation.sin(2 * Double.pi * Double(j) / Double(N))
|
|
1081
|
+
}
|
|
1082
|
+
var re = [Double](repeating: 0, count: N)
|
|
1083
|
+
var im = [Double](repeating: 0, count: N)
|
|
1084
|
+
do {
|
|
1085
|
+
let count = tableFrames.count
|
|
1086
|
+
// A level's samples one after another, each its frames side by side: a read between two samples and two
|
|
1087
|
+
// frames is one span of memory.
|
|
1088
|
+
func store(_ level: Int, _ n: Int, _ f: Int) -> Int { (level * N + n) * count + f }
|
|
1089
|
+
for (f, amplitudes) in tableFrames.enumerated() {
|
|
1090
|
+
for level in 0..<levels {
|
|
1091
|
+
let limit = Swift.min(512 >> level, amplitudes.count)
|
|
1092
|
+
if level > 0 && limit == Swift.min(512 >> (level - 1), amplitudes.count) {
|
|
1093
|
+
// No harmonic dropped: the level above's samples.
|
|
1094
|
+
for n in 0..<N { samples[store(level, n, f)] = samples[store(level - 1, n, f)] }
|
|
1095
|
+
continue
|
|
1096
|
+
}
|
|
1097
|
+
for i in 0..<N {
|
|
1098
|
+
re[i] = 0
|
|
1099
|
+
im[i] = 0
|
|
1100
|
+
}
|
|
1101
|
+
// sin(kθ) = (e^{ikθ} − e^{−ikθ}) / 2i
|
|
1102
|
+
if limit >= 1 {
|
|
1103
|
+
for k in 1...limit {
|
|
1104
|
+
let a = Double(amplitudes[k - 1])
|
|
1105
|
+
im[k] = -a / 2
|
|
1106
|
+
im[N - k] = a / 2
|
|
1107
|
+
}
|
|
1108
|
+
// cos(kθ) = (e^{ikθ} + e^{−ikθ}) / 2
|
|
1109
|
+
if let cosines = tableCosines?[f] {
|
|
1110
|
+
for k in 1...limit {
|
|
1111
|
+
let c = Double(cosines[k - 1])
|
|
1112
|
+
re[k] = c / 2
|
|
1113
|
+
re[N - k] = c / 2
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
// Bit reversal, then radix-2 butterflies with e^{+2πij/len}.
|
|
1118
|
+
var j = 0
|
|
1119
|
+
for i in 1..<N {
|
|
1120
|
+
var bit = N >> 1
|
|
1121
|
+
while j & bit != 0 {
|
|
1122
|
+
j ^= bit
|
|
1123
|
+
bit >>= 1
|
|
1124
|
+
}
|
|
1125
|
+
j ^= bit
|
|
1126
|
+
if i < j {
|
|
1127
|
+
re.swapAt(i, j)
|
|
1128
|
+
im.swapAt(i, j)
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
var len = 2
|
|
1132
|
+
while len <= N {
|
|
1133
|
+
let half = len >> 1
|
|
1134
|
+
let step = N / len
|
|
1135
|
+
var i = 0
|
|
1136
|
+
while i < N {
|
|
1137
|
+
for k in 0..<half {
|
|
1138
|
+
let wr = cosT[k * step]
|
|
1139
|
+
let wi = sinT[k * step]
|
|
1140
|
+
let xr = re[i + k + half] * wr - im[i + k + half] * wi
|
|
1141
|
+
let xi = re[i + k + half] * wi + im[i + k + half] * wr
|
|
1142
|
+
re[i + k + half] = re[i + k] - xr
|
|
1143
|
+
im[i + k + half] = im[i + k] - xi
|
|
1144
|
+
re[i + k] = re[i + k] + xr
|
|
1145
|
+
im[i + k] = im[i + k] + xi
|
|
1146
|
+
}
|
|
1147
|
+
i += len
|
|
1148
|
+
}
|
|
1149
|
+
len <<= 1
|
|
1150
|
+
}
|
|
1151
|
+
for n in 0..<N { samples[store(level, n, f)] = Float(re[n]) }
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
return (samples, count)
|
|
150
1156
|
}
|
|
151
1157
|
|
|
152
1158
|
// MARK: Events
|
|
@@ -160,15 +1166,161 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
160
1166
|
public func setParameter(at index: Int, _ value: Float) {
|
|
161
1167
|
guard index >= 0, index < parameterRegisters.count, value.isFinite else { return }
|
|
162
1168
|
targets[index] = Swift.min(Swift.max(value, parameterRanges[index].min), parameterRanges[index].max)
|
|
1169
|
+
retargeted = true
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/// The transport's tempo, in beats a minute (1 … 999), from the next block.
|
|
1173
|
+
public func setTempo(_ bpm: Double) {
|
|
1174
|
+
if bpm.isFinite { tempo = Swift.min(Swift.max(bpm, 1), 999) }
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/// Moves the transport to `beat`.
|
|
1178
|
+
public func seek(toBeat beat: Double) {
|
|
1179
|
+
if beat.isFinite { self.beat = beat }
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
/// Where the transport is, in beats.
|
|
1183
|
+
public var position: Double { beat }
|
|
1184
|
+
|
|
1185
|
+
/// MIDI note on: `velocity` 1 … 127 (0 is a note off); at `atBeat` on the transport, or at the next block. False
|
|
1186
|
+
/// when too many wait.
|
|
1187
|
+
@discardableResult
|
|
1188
|
+
public func noteOn(_ note: Int32, velocity: Int32, atBeat: Double? = nil) -> Bool {
|
|
1189
|
+
if let atBeat { return schedule(atBeat, 0, note, velocity) }
|
|
1190
|
+
start(note, velocity: velocity)
|
|
1191
|
+
return true
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
/// MIDI note off; at `atBeat` on the transport, or at the next block.
|
|
1195
|
+
@discardableResult
|
|
1196
|
+
public func noteOff(_ note: Int32, atBeat: Double? = nil) -> Bool {
|
|
1197
|
+
if let atBeat { return schedule(atBeat, 1, note, 0) }
|
|
1198
|
+
release(note)
|
|
1199
|
+
return true
|
|
163
1200
|
}
|
|
164
1201
|
|
|
165
|
-
///
|
|
166
|
-
|
|
1202
|
+
/// 64 the sustain pedal, 120 all sound off, 123 all notes off (both dropping what waits); at `atBeat`, or at the
|
|
1203
|
+
/// next block.
|
|
1204
|
+
@discardableResult
|
|
1205
|
+
public func controller(_ number: Int32, value: Int32, atBeat: Double? = nil) -> Bool {
|
|
1206
|
+
if let atBeat { return schedule(atBeat, 2, number, value) }
|
|
1207
|
+
if number == 120 || number == 123 { queueCount = 0 }
|
|
1208
|
+
control(number, value: value)
|
|
1209
|
+
return true
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/// The pitch wheel, 0 … 16383, 8192 centered: ±2 semitones; at `atBeat`, or at the next block.
|
|
1213
|
+
@discardableResult
|
|
1214
|
+
public func pitchBend(_ value: Int32, atBeat: Double? = nil) -> Bool {
|
|
1215
|
+
if let atBeat { return schedule(atBeat, 3, value, 0) }
|
|
1216
|
+
bend = Double(Swift.min(16383, Swift.max(0, value)) - 8192) / 8192 * 2
|
|
1217
|
+
return true
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/// Puts an event in the queue after those at or before its beat.
|
|
1221
|
+
private func schedule(_ beat: Double, _ kind: Int32, _ a: Int32, _ b: Int32) -> Bool {
|
|
1222
|
+
guard beat.isFinite, queueCount < Self.queueCapacity else { return false }
|
|
1223
|
+
var at = queueCount
|
|
1224
|
+
while at > 0 && queueBeat[at - 1] > beat { at -= 1 }
|
|
1225
|
+
(queueBeat + at + 1).update(from: queueBeat + at, count: queueCount - at)
|
|
1226
|
+
(queueEvent + (at + 1) * 3).update(from: queueEvent + at * 3, count: (queueCount - at) * 3)
|
|
1227
|
+
queueBeat[at] = beat
|
|
1228
|
+
queueEvent[at * 3] = kind
|
|
1229
|
+
queueEvent[at * 3 + 1] = a
|
|
1230
|
+
queueEvent[at * 3 + 2] = b
|
|
1231
|
+
queueCount += 1
|
|
1232
|
+
return true
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/// Applies what is due: each event whose beat is nearer this frame than the next.
|
|
1236
|
+
private func applyDue(_ step: Double) {
|
|
1237
|
+
while queueCount > 0 && queueBeat[0] < beat + step / 2 {
|
|
1238
|
+
let kind = queueEvent[0]
|
|
1239
|
+
let a = queueEvent[1]
|
|
1240
|
+
let b = queueEvent[2]
|
|
1241
|
+
queueBeat.update(from: queueBeat + 1, count: queueCount - 1)
|
|
1242
|
+
queueEvent.update(from: queueEvent + 3, count: (queueCount - 1) * 3)
|
|
1243
|
+
queueCount -= 1
|
|
1244
|
+
switch kind {
|
|
1245
|
+
case 0: start(a, velocity: b)
|
|
1246
|
+
case 1: release(a)
|
|
1247
|
+
case 2: control(a, value: b)
|
|
1248
|
+
default: pitchBend(a)
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/// Frames to the next waiting event, at most a block's.
|
|
1254
|
+
private func framesToNext(_ step: Double) -> Int {
|
|
1255
|
+
let frames = ((queueBeat[0] - beat) / step).rounded()
|
|
1256
|
+
return frames >= Double(DSPProgram.blockFrames) ? DSPProgram.blockFrames : Swift.max(1, Int(frames))
|
|
1257
|
+
}
|
|
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
|
+
|
|
1295
|
+
private func start(_ note: Int32, velocity: Int32) {
|
|
167
1296
|
if velocity <= 0 {
|
|
168
|
-
|
|
1297
|
+
release(note)
|
|
169
1298
|
return
|
|
170
1299
|
}
|
|
171
|
-
let
|
|
1300
|
+
if let mode = currentMode() {
|
|
1301
|
+
// One voice: the newest note, legato while another is held.
|
|
1302
|
+
unstack(note)
|
|
1303
|
+
if stackCount == Self.lanes {
|
|
1304
|
+
(stack).update(from: stack + 1, count: stackCount - 1)
|
|
1305
|
+
stackCount -= 1
|
|
1306
|
+
}
|
|
1307
|
+
stack[stackCount] = note
|
|
1308
|
+
stackCount += 1
|
|
1309
|
+
let held = laneGate[0] > 0.5
|
|
1310
|
+
if laneActive[0] == 0 { resetLane(0) }
|
|
1311
|
+
laneNote[0] = note
|
|
1312
|
+
laneGate[0] = 1
|
|
1313
|
+
laneHeld[0] = false
|
|
1314
|
+
laneVelocity[0] = Float(Double(Swift.min(127, velocity)) / 127)
|
|
1315
|
+
laneActive[0] = 1
|
|
1316
|
+
newestLane = 0
|
|
1317
|
+
if mode == .mono || !held {
|
|
1318
|
+
trigger[0] += 1
|
|
1319
|
+
masterTrigger[0] += 1
|
|
1320
|
+
}
|
|
1321
|
+
return
|
|
1322
|
+
}
|
|
1323
|
+
let voices = voiceLimit()
|
|
172
1324
|
var lane = -1
|
|
173
1325
|
for l in 0..<voices where lane < 0 && laneNote[l] == note && laneGate[l] > 0.5 { lane = l }
|
|
174
1326
|
var fresh = false
|
|
@@ -191,19 +1343,35 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
191
1343
|
laneVelocity[lane] = Float(Double(Swift.min(127, velocity)) / 127)
|
|
192
1344
|
laneActive[lane] = 1
|
|
193
1345
|
trigger[lane] += 1
|
|
1346
|
+
masterTrigger[0] += 1
|
|
1347
|
+
newestLane = lane
|
|
194
1348
|
age += 1
|
|
195
1349
|
laneAge[lane] = age
|
|
196
1350
|
}
|
|
197
1351
|
|
|
198
|
-
|
|
1352
|
+
private func release(_ note: Int32) {
|
|
1353
|
+
if let mode = currentMode() {
|
|
1354
|
+
unstack(note)
|
|
1355
|
+
guard laneNote[0] == note && laneGate[0] > 0.5 else { return }
|
|
1356
|
+
if stackCount > 0 {
|
|
1357
|
+
// Back to the note held before it.
|
|
1358
|
+
laneNote[0] = stack[stackCount - 1]
|
|
1359
|
+
laneHeld[0] = false
|
|
1360
|
+
if mode == .mono { trigger[0] += 1 }
|
|
1361
|
+
} else if pedal {
|
|
1362
|
+
laneHeld[0] = true
|
|
1363
|
+
} else {
|
|
1364
|
+
laneGate[0] = 0
|
|
1365
|
+
}
|
|
1366
|
+
return
|
|
1367
|
+
}
|
|
199
1368
|
for l in 0..<program.voices where laneNote[l] == note && laneGate[l] > 0.5 {
|
|
200
1369
|
// Under the pedal the gate stays up until it lifts.
|
|
201
1370
|
if pedal { laneHeld[l] = true } else { laneGate[l] = 0 }
|
|
202
1371
|
}
|
|
203
1372
|
}
|
|
204
1373
|
|
|
205
|
-
|
|
206
|
-
public func controller(_ number: Int32, value: Int32) {
|
|
1374
|
+
private func control(_ number: Int32, value: Int32) {
|
|
207
1375
|
let L = Self.lanes
|
|
208
1376
|
switch number {
|
|
209
1377
|
case 64:
|
|
@@ -215,24 +1383,32 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
215
1383
|
case 123:
|
|
216
1384
|
laneGate.update(repeating: 0, count: L)
|
|
217
1385
|
laneHeld.update(repeating: false, count: L)
|
|
1386
|
+
stackCount = 0
|
|
218
1387
|
case 120:
|
|
1388
|
+
stackCount = 0
|
|
219
1389
|
for l in 0..<L {
|
|
220
1390
|
laneGate[l] = 0
|
|
221
1391
|
laneHeld[l] = false
|
|
222
1392
|
laneActive[l] = 0
|
|
223
1393
|
resetLane(l)
|
|
224
1394
|
}
|
|
1395
|
+
resetMaster()
|
|
225
1396
|
default:
|
|
226
1397
|
break
|
|
227
1398
|
}
|
|
228
1399
|
}
|
|
229
1400
|
|
|
230
|
-
///
|
|
231
|
-
|
|
232
|
-
|
|
1401
|
+
/// Drops `note` from a mono program's held notes.
|
|
1402
|
+
private func unstack(_ note: Int32) {
|
|
1403
|
+
var kept = 0
|
|
1404
|
+
for i in 0..<stackCount where stack[i] != note {
|
|
1405
|
+
stack[kept] = stack[i]
|
|
1406
|
+
kept += 1
|
|
1407
|
+
}
|
|
1408
|
+
stackCount = kept
|
|
233
1409
|
}
|
|
234
1410
|
|
|
235
|
-
/// Silences every voice at once.
|
|
1411
|
+
/// Silences every voice, and the master stage, at once, and drops what waits.
|
|
236
1412
|
public func reset() {
|
|
237
1413
|
controller(120, value: 0)
|
|
238
1414
|
}
|
|
@@ -244,79 +1420,214 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
244
1420
|
|
|
245
1421
|
// MARK: Rendering
|
|
246
1422
|
|
|
247
|
-
/// Writes `frames`
|
|
1423
|
+
/// Writes `frames` frames to `out`, in blocks of up to 128: a program's two sides averaged (a mono program's
|
|
1424
|
+
/// samples either way).
|
|
248
1425
|
public func render(into out: UnsafeMutablePointer<Float>, frames: Int) {
|
|
249
1426
|
var done = 0
|
|
250
1427
|
while done < frames {
|
|
251
|
-
let
|
|
252
|
-
|
|
1428
|
+
let step = tempo / 60 / Double(sampleRate)
|
|
1429
|
+
applyDue(step)
|
|
1430
|
+
// A block ends where the next event is due.
|
|
1431
|
+
let n = Swift.min(DSPProgram.blockFrames, frames - done, queueCount > 0 ? framesToNext(step) : DSPProgram.blockFrames)
|
|
1432
|
+
block(out + done, nil, n)
|
|
1433
|
+
done += n
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
/// Writes `frames` frames, the left side to `left` and the right to `right`, in blocks of up to 128.
|
|
1438
|
+
public func render(left: UnsafeMutablePointer<Float>, right: UnsafeMutablePointer<Float>, frames: Int) {
|
|
1439
|
+
var done = 0
|
|
1440
|
+
while done < frames {
|
|
1441
|
+
let step = tempo / 60 / Double(sampleRate)
|
|
1442
|
+
applyDue(step)
|
|
1443
|
+
let n = Swift.min(DSPProgram.blockFrames, frames - done, queueCount > 0 ? framesToNext(step) : DSPProgram.blockFrames)
|
|
1444
|
+
block(left + done, right + done, n)
|
|
253
1445
|
done += n
|
|
254
1446
|
}
|
|
255
1447
|
}
|
|
256
1448
|
|
|
1449
|
+
/// A voice starts over: its state and its builtins', not the master stage's.
|
|
257
1450
|
private func resetLane(_ l: Int) {
|
|
1451
|
+
// A register holds the program's voices' lanes (a state register's value its lane's first frame).
|
|
1452
|
+
if l < program.voices {
|
|
1453
|
+
for entry in program.state where !entry.master { registers[entry.register * width + l] = entry.initial }
|
|
1454
|
+
}
|
|
1455
|
+
for i in instanceKinds.indices where !instanceMaster[i] { resetInstance(i, l) }
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/// The master stage starts over, in lane 0.
|
|
1459
|
+
private func resetMaster() {
|
|
1460
|
+
for entry in program.state where entry.master { registers[entry.register * width] = entry.initial }
|
|
1461
|
+
for i in instanceKinds.indices where instanceMaster[i] { resetInstance(i, 0) }
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
private func resetInstance(_ i: Int, _ l: Int) {
|
|
258
1465
|
let L = Self.lanes
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
1466
|
+
let kind = instanceKinds[i]
|
|
1467
|
+
let base = instanceOffsets[i] + l
|
|
1468
|
+
let seed = UInt32(truncatingIfNeeded: i + 1) &* 2_654_435_761 &+ UInt32(truncatingIfNeeded: l + 1) &* 2_246_822_519
|
|
1469
|
+
if kind == .noise {
|
|
1470
|
+
noise[base] = seed == 0 ? 1 : seed
|
|
1471
|
+
return
|
|
1472
|
+
}
|
|
1473
|
+
for w in 0..<kind.words { state[base + w * L] = 0 }
|
|
1474
|
+
if kind == .delay || kind == .comb || kind == .allpass {
|
|
1475
|
+
(delaySamples + delayBases[i] + l * delayLengths[i]).update(repeating: 0, count: delayLengths[i])
|
|
1476
|
+
}
|
|
1477
|
+
if kind == .grains {
|
|
1478
|
+
// Its random state, a word of raw bits.
|
|
1479
|
+
UnsafeMutableRawPointer(state + base + L).storeBytes(of: seed == 0 ? 1 : seed, as: UInt32.self)
|
|
1480
|
+
}
|
|
1481
|
+
if kind == .adsr {
|
|
1482
|
+
state[base + 4 * L] = .nan
|
|
1483
|
+
state[base + 6 * L] = .nan
|
|
1484
|
+
} else if kind.words == 8 {
|
|
1485
|
+
state[base + 2 * L] = .nan
|
|
1486
|
+
} else if kind == .smooth {
|
|
1487
|
+
state[base] = .nan
|
|
1488
|
+
state[base + L] = .nan
|
|
274
1489
|
}
|
|
275
1490
|
}
|
|
276
1491
|
|
|
277
|
-
private func block(_ out: UnsafeMutablePointer<Float>, _ n: Int) {
|
|
1492
|
+
private func block(_ out: UnsafeMutablePointer<Float>, _ right: UnsafeMutablePointer<Float>?, _ n: Int) {
|
|
278
1493
|
let L = Self.lanes
|
|
1494
|
+
let F = Self.frames
|
|
1495
|
+
let W = width
|
|
279
1496
|
let R = registers
|
|
280
1497
|
let sr = sampleRate
|
|
281
|
-
// 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.
|
|
282
1499
|
let alpha = snapped ? Float(1) : 1 - exp(-Float(n) / (0.02 * sr))
|
|
283
1500
|
snapped = false
|
|
284
1501
|
for (i, register) in parameterRegisters.enumerated() {
|
|
285
|
-
let at = R + register *
|
|
286
|
-
|
|
1502
|
+
let at = R + register * W
|
|
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() }
|
|
287
1516
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
1517
|
+
(R + 5 * W).update(repeating: Float(tempo), count: W)
|
|
1518
|
+
let beatStep = tempo / 60 / Double(sr)
|
|
1519
|
+
// The voices run up to the highest that sounds: a note takes the lowest free lane, and an idle one starts
|
|
1520
|
+
// over before it sounds again, so what the ones above would compute is never heard.
|
|
1521
|
+
var lanes = 0
|
|
1522
|
+
for l in 0..<L where laneActive[l] != 0 { lanes = l + 1 }
|
|
1523
|
+
let any = lanes > 0
|
|
1524
|
+
let master = program.master
|
|
1525
|
+
guard any || master != nil else {
|
|
1526
|
+
beat += Double(n) * beatStep
|
|
291
1527
|
out.update(repeating: 0, count: n)
|
|
1528
|
+
right?.update(repeating: 0, count: n)
|
|
292
1529
|
return
|
|
293
1530
|
}
|
|
294
|
-
|
|
295
|
-
let
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
1531
|
+
if any {
|
|
1532
|
+
let V = program.voices
|
|
1533
|
+
for l in 0..<V {
|
|
1534
|
+
let note = laneNote[l]
|
|
1535
|
+
let freq = note >= 0 ? Float(440 * Foundation.pow(2, (Double(note) - 69 + bend) / 12)) : 0
|
|
1536
|
+
for k in 0..<F {
|
|
1537
|
+
R[1 * W + k * V + l] = freq
|
|
1538
|
+
R[2 * W + k * V + l] = laneGate[l]
|
|
1539
|
+
R[3 * W + k * V + l] = laneVelocity[l]
|
|
1540
|
+
R[4 * W + k * V + l] = note >= 0 ? Float(note) : 0
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
run(blockCode, 0..<blockRunning, lanes: lanes, trigger: trigger, frames: F)
|
|
300
1544
|
}
|
|
301
|
-
|
|
302
|
-
|
|
1545
|
+
if master != nil {
|
|
1546
|
+
// The newest note, the master stage's inputs (registers 7 … 10), in lane 0.
|
|
1547
|
+
let l = newestLane
|
|
1548
|
+
let note = l >= 0 ? laneNote[l] : -1
|
|
1549
|
+
let values: [Float] = [
|
|
1550
|
+
note >= 0 ? Float(440 * Foundation.pow(2, (Double(note) - 69 + bend) / 12)) : 0,
|
|
1551
|
+
l >= 0 ? laneGate[l] : 0,
|
|
1552
|
+
l >= 0 ? laneVelocity[l] : 0,
|
|
1553
|
+
note >= 0 ? Float(note) : 0,
|
|
1554
|
+
]
|
|
1555
|
+
let V = program.voices
|
|
1556
|
+
for (i, value) in values.enumerated() { for k in 0..<F { R[(7 + i) * W + k * V] = value } }
|
|
1557
|
+
}
|
|
1558
|
+
let o = R + program.output * W
|
|
1559
|
+
let or = R + (program.outputRight ?? program.output) * W
|
|
1560
|
+
let V = program.voices
|
|
303
1561
|
var check: Float = 0
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
1562
|
+
// A chunk of up to 16 frames at a time: each instruction over all of them, the serial part frame by frame.
|
|
1563
|
+
var chunk = 0
|
|
1564
|
+
while chunk < n {
|
|
1565
|
+
let fc = Swift.min(F, n - chunk)
|
|
1566
|
+
for k in 0..<fc {
|
|
1567
|
+
for l in 0..<Swift.max(lanes, 1) { R[6 * W + k * V + l] = Float(beat) }
|
|
1568
|
+
beat += beatStep
|
|
1569
|
+
}
|
|
1570
|
+
if any {
|
|
1571
|
+
run(sampleCode, 0..<sampleSerial.lowerBound, lanes: lanes, trigger: trigger, frames: fc)
|
|
1572
|
+
if !sampleSerial.isEmpty {
|
|
1573
|
+
for k in 0..<fc { run(sampleCode, sampleSerial, lanes: lanes, trigger: trigger, frames: 1, frame: k) }
|
|
1574
|
+
}
|
|
1575
|
+
run(sampleCode, sampleSerial.upperBound..<sampleCount, lanes: lanes, trigger: trigger, frames: fc)
|
|
1576
|
+
}
|
|
1577
|
+
for k in 0..<fc {
|
|
1578
|
+
var leftSum: Float = 0
|
|
1579
|
+
var rightSum: Float = 0
|
|
1580
|
+
// The sounding voices only: an idle one may hold anything (log(0) of its freq, say), and NaN × 0 is NaN.
|
|
1581
|
+
if any {
|
|
1582
|
+
for l in 0..<lanes where laneActive[l] != 0 {
|
|
1583
|
+
leftSum += o[k * V + l]
|
|
1584
|
+
rightSum += or[k * V + l]
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
if let master {
|
|
1588
|
+
R[master.inputs[0] * W + k * V] = leftSum
|
|
1589
|
+
R[master.inputs[1] * W + k * V] = rightSum
|
|
1590
|
+
} else {
|
|
1591
|
+
if let right {
|
|
1592
|
+
out[chunk + k] = leftSum
|
|
1593
|
+
right[chunk + k] = rightSum
|
|
1594
|
+
} else {
|
|
1595
|
+
out[chunk + k] = (leftSum + rightSum) * 0.5
|
|
1596
|
+
}
|
|
1597
|
+
check += leftSum + rightSum
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
if let master {
|
|
1601
|
+
run(masterCode, 0..<masterSerial.lowerBound, lanes: 1, trigger: masterTrigger, frames: fc)
|
|
1602
|
+
if !masterSerial.isEmpty {
|
|
1603
|
+
for k in 0..<fc { run(masterCode, masterSerial, lanes: 1, trigger: masterTrigger, frames: 1, frame: k) }
|
|
1604
|
+
}
|
|
1605
|
+
run(masterCode, masterSerial.upperBound..<masterCount, lanes: 1, trigger: masterTrigger, frames: fc)
|
|
1606
|
+
for k in 0..<fc {
|
|
1607
|
+
let leftSum = R[master.outputs[0] * W + k * V]
|
|
1608
|
+
let rightSum = R[master.outputs[1] * W + k * V]
|
|
1609
|
+
if let right {
|
|
1610
|
+
out[chunk + k] = leftSum
|
|
1611
|
+
right[chunk + k] = rightSum
|
|
1612
|
+
} else {
|
|
1613
|
+
out[chunk + k] = (leftSum + rightSum) * 0.5
|
|
1614
|
+
}
|
|
1615
|
+
check += leftSum + rightSum
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
chunk += fc
|
|
310
1619
|
}
|
|
311
1620
|
guard check.isFinite else {
|
|
312
|
-
// A voice went to infinity or NaN: silence the block and start
|
|
1621
|
+
// A voice or the master stage went to infinity or NaN: silence the block and start everything over.
|
|
313
1622
|
out.update(repeating: 0, count: n)
|
|
1623
|
+
right?.update(repeating: 0, count: n)
|
|
314
1624
|
for l in 0..<L {
|
|
315
1625
|
laneActive[l] = 0
|
|
316
1626
|
laneGate[l] = 0
|
|
317
1627
|
laneHeld[l] = false
|
|
318
1628
|
resetLane(l)
|
|
319
1629
|
}
|
|
1630
|
+
resetMaster()
|
|
320
1631
|
return
|
|
321
1632
|
}
|
|
322
1633
|
// Denormals in the filters' integrators would slow every frame after.
|
|
@@ -344,180 +1655,731 @@ public final class DSPMachine: @unchecked Sendable {
|
|
|
344
1655
|
return 0
|
|
345
1656
|
}
|
|
346
1657
|
|
|
347
|
-
|
|
348
|
-
|
|
1658
|
+
/// A table's, shape's or sample's index from its register: in range, or the first.
|
|
1659
|
+
@inline(__always)
|
|
1660
|
+
private static func index(_ value: Float, _ count: Int) -> Int {
|
|
1661
|
+
value >= 0 && value < Float(count) ? Int(value) : 0
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
// The interpreter: instructions `range` of `code` in the first `lanes` lanes (every voice's, or the master
|
|
1665
|
+
// stage's one), each lane's note on counted in `trigger`, over the chunk's first `frames` frames; or, given a
|
|
1666
|
+
// `frame`, at that frame alone, as the serial part runs (a state register's one value a lane, its first).
|
|
1667
|
+
private func run(
|
|
1668
|
+
_ code: UnsafeMutablePointer<Int32>, _ range: Range<Int>, lanes: Int, trigger: UnsafeMutablePointer<Float>, frames: Int, frame: Int = -1
|
|
1669
|
+
) {
|
|
349
1670
|
let L = Self.lanes
|
|
1671
|
+
let F = Self.frames
|
|
350
1672
|
let R = registers
|
|
351
1673
|
let S = state
|
|
352
1674
|
let N = noise
|
|
353
1675
|
let sr = sampleRate
|
|
354
|
-
|
|
355
|
-
let
|
|
356
|
-
|
|
1676
|
+
let serial = frame >= 0
|
|
1677
|
+
let fc = serial ? 1 : frames
|
|
1678
|
+
let V = program.voices
|
|
1679
|
+
let shift = serial ? frame * V : 0
|
|
1680
|
+
// A stateless instruction: every lane's frames at once (the chunk's last ones, past fc, go unread), the
|
|
1681
|
+
// sounding lanes of each frame; in the serial part, the one frame's.
|
|
1682
|
+
@inline(__always) func each(_ body: (Int) -> Void) {
|
|
1683
|
+
if serial {
|
|
1684
|
+
for l in 0..<lanes { body(l) }
|
|
1685
|
+
} else if lanes == V {
|
|
1686
|
+
for i in 0..<(F * V) { body(i) }
|
|
1687
|
+
} else {
|
|
1688
|
+
for k in 0..<F {
|
|
1689
|
+
for l in 0..<lanes { body(k * V + l) }
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
var next = range.lowerBound * 12
|
|
1694
|
+
let end = range.upperBound * 12
|
|
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
|
|
357
1700
|
let op = code[at]
|
|
358
|
-
let
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
1701
|
+
let states = code[at + 8]
|
|
1702
|
+
// A register at this frame, or a state register's one value.
|
|
1703
|
+
@inline(__always) func register(_ slot: Int) -> UnsafeMutablePointer<Float> {
|
|
1704
|
+
R + Int(code[at + slot]) + (states & (1 << slot) != 0 ? 0 : shift)
|
|
1705
|
+
}
|
|
1706
|
+
let d = register(1)
|
|
1707
|
+
let a = register(2)
|
|
1708
|
+
let b = register(3)
|
|
1709
|
+
let c = register(4)
|
|
362
1710
|
switch op {
|
|
363
|
-
case 0:
|
|
364
|
-
case 1:
|
|
365
|
-
case 2:
|
|
366
|
-
case 3:
|
|
367
|
-
case 4:
|
|
368
|
-
case 5:
|
|
369
|
-
case 6:
|
|
370
|
-
case 7:
|
|
371
|
-
case 8:
|
|
372
|
-
case 9:
|
|
373
|
-
case 10:
|
|
374
|
-
case 11:
|
|
375
|
-
case 12:
|
|
376
|
-
case 13:
|
|
377
|
-
case 14:
|
|
378
|
-
case 15:
|
|
379
|
-
case 16:
|
|
380
|
-
case 17:
|
|
381
|
-
case 18:
|
|
382
|
-
case 19:
|
|
383
|
-
case 20:
|
|
384
|
-
case 21:
|
|
385
|
-
case 22:
|
|
386
|
-
case 23:
|
|
387
|
-
case 24:
|
|
388
|
-
case 25:
|
|
389
|
-
case 26:
|
|
390
|
-
case 27:
|
|
391
|
-
case 28:
|
|
392
|
-
case 29:
|
|
393
|
-
case 30:
|
|
394
|
-
case 31:
|
|
395
|
-
case 32:
|
|
396
|
-
case 33:
|
|
397
|
-
case 34:
|
|
398
|
-
case 35:
|
|
1711
|
+
case 0: each { d[$0] = a[$0] }
|
|
1712
|
+
case 1: each { d[$0] = a[$0] + b[$0] }
|
|
1713
|
+
case 2: each { d[$0] = a[$0] - b[$0] }
|
|
1714
|
+
case 3: each { d[$0] = a[$0] * b[$0] }
|
|
1715
|
+
case 4: each { d[$0] = a[$0] / b[$0] }
|
|
1716
|
+
case 5: each { d[$0] = a[$0].truncatingRemainder(dividingBy: b[$0]) }
|
|
1717
|
+
case 6: each { d[$0] = -a[$0] }
|
|
1718
|
+
case 7: each { d[$0] = Swift.min(a[$0], b[$0]) }
|
|
1719
|
+
case 8: each { d[$0] = Swift.max(a[$0], b[$0]) }
|
|
1720
|
+
case 9: each { d[$0] = Swift.abs(a[$0]) }
|
|
1721
|
+
case 10: each { d[$0] = a[$0].rounded(.down) }
|
|
1722
|
+
case 11: each { d[$0] = a[$0].rounded(.up) }
|
|
1723
|
+
case 12: each { d[$0] = (a[$0] + 0.5).rounded(.down) }
|
|
1724
|
+
case 13: each { d[$0] = a[$0].squareRoot() }
|
|
1725
|
+
case 14: each { d[$0] = Foundation.sin(a[$0]) }
|
|
1726
|
+
case 15: each { d[$0] = Foundation.cos(a[$0]) }
|
|
1727
|
+
case 16: each { d[$0] = Foundation.tan(a[$0]) }
|
|
1728
|
+
case 17: each { d[$0] = Foundation.exp(a[$0]) }
|
|
1729
|
+
case 18: each { d[$0] = Foundation.log(a[$0]) }
|
|
1730
|
+
case 19: each { d[$0] = Foundation.pow(a[$0], b[$0]) }
|
|
1731
|
+
case 20: each { d[$0] = Foundation.tanh(a[$0]) }
|
|
1732
|
+
case 21: each { d[$0] = a[$0] < b[$0] ? 1 : 0 }
|
|
1733
|
+
case 22: each { d[$0] = a[$0] <= b[$0] ? 1 : 0 }
|
|
1734
|
+
case 23: each { d[$0] = a[$0] > b[$0] ? 1 : 0 }
|
|
1735
|
+
case 24: each { d[$0] = a[$0] >= b[$0] ? 1 : 0 }
|
|
1736
|
+
case 25: each { d[$0] = a[$0] == b[$0] ? 1 : 0 }
|
|
1737
|
+
case 26: each { d[$0] = a[$0] != b[$0] ? 1 : 0 }
|
|
1738
|
+
case 27: each { d[$0] = a[$0] != 0 && b[$0] != 0 ? 1 : 0 }
|
|
1739
|
+
case 28: each { d[$0] = a[$0] != 0 || b[$0] != 0 ? 1 : 0 }
|
|
1740
|
+
case 29: each { d[$0] = a[$0] == 0 ? 1 : 0 }
|
|
1741
|
+
case 30: each { d[$0] = a[$0] != 0 ? b[$0] : c[$0] }
|
|
1742
|
+
case 31: each { d[$0] = Swift.min(Swift.max(a[$0], b[$0]), c[$0]) }
|
|
1743
|
+
case 32: each { d[$0] = a[$0] + (b[$0] - a[$0]) * c[$0] }
|
|
1744
|
+
case 33: each { d[$0] = a[$0] - a[$0].rounded(.down) }
|
|
1745
|
+
case 34: each { d[$0] = a[$0] > 0 ? 1 : a[$0] < 0 ? -1 : 0 }
|
|
1746
|
+
case 35: each { d[$0] = Foundation.atan(a[$0]) }
|
|
1747
|
+
// The superinstructions: the product or the sum a float, as its own instruction would have stored it.
|
|
1748
|
+
case 58: each { d[$0] = a[$0] * b[$0] + c[$0] }
|
|
1749
|
+
case 59: each { d[$0] = (a[$0] + b[$0]) * c[$0] }
|
|
1750
|
+
case 60: each { d[$0] = Foundation.log2(a[$0]) }
|
|
1751
|
+
case 61: each { d[$0] = Foundation.log10(a[$0]) }
|
|
1752
|
+
case 62: each { d[$0] = Foundation.exp2(a[$0]) }
|
|
1753
|
+
case 63: each { d[$0] = a[$0].rounded(.towardZero) }
|
|
1754
|
+
case 64: each { d[$0] = Foundation.asin(a[$0]) }
|
|
1755
|
+
case 65: each { d[$0] = Foundation.acos(a[$0]) }
|
|
1756
|
+
case 66: each { d[$0] = Foundation.atan2(a[$0], b[$0]) }
|
|
1757
|
+
case 67: each { d[$0] = Foundation.sinh(a[$0]) }
|
|
1758
|
+
case 68: each { d[$0] = Foundation.cosh(a[$0]) }
|
|
1759
|
+
case 69: each { d[$0] = Foundation.cbrt(a[$0]) }
|
|
1760
|
+
case 70: each { d[$0] = Foundation.hypot(a[$0], b[$0]) }
|
|
1761
|
+
case 71: each { d[$0] = Foundation.expm1(a[$0]) }
|
|
1762
|
+
case 72: each { d[$0] = Foundation.log1p(a[$0]) }
|
|
1763
|
+
case 73:
|
|
1764
|
+
// softclip(x): x (27 + x²) / (27 + 9x²) within ±3.
|
|
1765
|
+
each {
|
|
1766
|
+
let x = a[$0] > -3 ? (a[$0] < 3 ? a[$0] : 3) : -3
|
|
1767
|
+
let x2 = x * x
|
|
1768
|
+
d[$0] = x * (27 + x2) / (27 + 9 * x2)
|
|
1769
|
+
}
|
|
1770
|
+
case 74:
|
|
1771
|
+
// onepole(input, cutoff): toward the input by 1 − e^(−2π cutoff / sampleRate) a frame.
|
|
1772
|
+
let s = S + Int(code[at + 7])
|
|
1773
|
+
for fk in 0..<fc {
|
|
1774
|
+
for l in 0..<lanes {
|
|
1775
|
+
let el = fk * V + l
|
|
1776
|
+
let cutoff = Swift.min(Swift.max(b[el], 0), 0.49 * sr)
|
|
1777
|
+
if !(s[L + l] == cutoff) {
|
|
1778
|
+
s[L + l] = cutoff
|
|
1779
|
+
s[2 * L + l] = 1 - Foundation.exp(-2 * Float.pi * cutoff / sr)
|
|
1780
|
+
}
|
|
1781
|
+
let y = s[l]
|
|
1782
|
+
s[l] = y + (a[el] - y) * s[2 * L + l]
|
|
1783
|
+
d[el] = s[l]
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
case 75:
|
|
1787
|
+
// dcblock(input): y = x − x before + r × y before, r = 1 − 2π 20 / sampleRate.
|
|
1788
|
+
let s = S + Int(code[at + 7])
|
|
1789
|
+
let r = 1 - Float(125.66370614359172) / sr
|
|
1790
|
+
for fk in 0..<fc {
|
|
1791
|
+
for l in 0..<lanes {
|
|
1792
|
+
let el = fk * V + l
|
|
1793
|
+
let x = a[el]
|
|
1794
|
+
let y = (x - s[l]) + r * s[L + l]
|
|
1795
|
+
s[l] = x
|
|
1796
|
+
s[L + l] = y
|
|
1797
|
+
d[el] = y
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
case 76, 77:
|
|
1801
|
+
// comb(input, seconds, feedback, damp, longest): its line read seconds back, between samples, through a
|
|
1802
|
+
// one-pole (damp the share it keeps) back in with the input times feedback. allpass(input, seconds,
|
|
1803
|
+
// gain, longest): w = input + gain × w seconds back, out = w seconds back − gain × w.
|
|
1804
|
+
let s = S + Int(code[at + 7])
|
|
1805
|
+
let length = Int(code[at + 10])
|
|
1806
|
+
let mask = length - 1
|
|
1807
|
+
let memory = delaySamples + Int(code[at + 9])
|
|
1808
|
+
let damping = register(5)
|
|
1809
|
+
for fk in 0..<fc {
|
|
1810
|
+
for l in 0..<lanes {
|
|
1811
|
+
let el = fk * V + l
|
|
1812
|
+
let w = Int(s[l])
|
|
1813
|
+
var ds = b[el] * sr
|
|
1814
|
+
ds = ds >= 1 ? (ds <= Float(length - 2) ? ds : Float(length - 2)) : 1
|
|
1815
|
+
let position = Float(w) - ds
|
|
1816
|
+
let i0 = Int(position.rounded(.down))
|
|
1817
|
+
let fr = position - Float(i0)
|
|
1818
|
+
let line = memory + l * length
|
|
1819
|
+
let v0 = line[i0 & mask]
|
|
1820
|
+
let v = v0 + (line[(i0 + 1) & mask] - v0) * fr
|
|
1821
|
+
let x = a[el]
|
|
1822
|
+
let g = c[el]
|
|
1823
|
+
if op == 76 {
|
|
1824
|
+
let damp = damping[el] > 0 ? (damping[el] < 1 ? damping[el] : 1) : 0
|
|
1825
|
+
let store = v * (1 - damp) + s[L + l] * damp
|
|
1826
|
+
s[L + l] = store
|
|
1827
|
+
line[w & mask] = x + store * g
|
|
1828
|
+
d[el] = v
|
|
1829
|
+
} else {
|
|
1830
|
+
let into = x + g * v
|
|
1831
|
+
line[w & mask] = into
|
|
1832
|
+
d[el] = v - g * into
|
|
1833
|
+
}
|
|
1834
|
+
s[l] = Float((w + 1) & mask)
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
399
1837
|
case 40, 41, 42, 43, 44:
|
|
400
1838
|
// Oscillators: the output from the phase now, then the phase steps by freq / sampleRate.
|
|
401
1839
|
let s = S + Int(code[at + 7])
|
|
402
|
-
for
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
v = 2 *
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
1840
|
+
for fk in 0..<fc {
|
|
1841
|
+
for l in 0..<lanes {
|
|
1842
|
+
let el = fk * V + l
|
|
1843
|
+
let p = s[l]
|
|
1844
|
+
let dt = a[el] / sr
|
|
1845
|
+
var v: Float
|
|
1846
|
+
switch op {
|
|
1847
|
+
case 40: v = p
|
|
1848
|
+
case 41: v = Foundation.sin(2 * Float.pi * p)
|
|
1849
|
+
case 42:
|
|
1850
|
+
v = 2 * p - 1
|
|
1851
|
+
if dt > 0 { v -= Self.blep(p, dt) }
|
|
1852
|
+
case 43:
|
|
1853
|
+
let w = Swift.min(Swift.max(b[el], 0), 1)
|
|
1854
|
+
v = p < w ? 1 : -1
|
|
1855
|
+
if dt > 0 {
|
|
1856
|
+
let q = p + 1 - w
|
|
1857
|
+
v += Self.blep(p, dt) - Self.blep(q - q.rounded(.down), dt)
|
|
1858
|
+
}
|
|
1859
|
+
default: v = 1 - 4 * Swift.abs(p - 0.5)
|
|
418
1860
|
}
|
|
419
|
-
|
|
1861
|
+
d[el] = v
|
|
1862
|
+
let next = p + dt
|
|
1863
|
+
s[l] = next >= 0 && next < 1 ? next : next - next.rounded(.down)
|
|
420
1864
|
}
|
|
421
|
-
d[l] = v
|
|
422
|
-
let next = p + dt
|
|
423
|
-
s[l] = next - next.rounded(.down)
|
|
424
1865
|
}
|
|
425
1866
|
case 45:
|
|
426
1867
|
let s = N + Int(code[at + 7])
|
|
427
|
-
for
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
1868
|
+
for fk in 0..<fc {
|
|
1869
|
+
for l in 0..<lanes {
|
|
1870
|
+
let el = fk * V + l
|
|
1871
|
+
var x = s[l]
|
|
1872
|
+
x ^= x << 13
|
|
1873
|
+
x ^= x >> 17
|
|
1874
|
+
x ^= x << 5
|
|
1875
|
+
s[l] = x
|
|
1876
|
+
d[el] = Float(Double(x) / 4_294_967_296 * 2 - 1)
|
|
1877
|
+
}
|
|
434
1878
|
}
|
|
435
1879
|
case 46:
|
|
436
1880
|
// adsr(gate, attack, decay, sustain, release): stage, level, trigger, gate before, decay time and coefficient, release time and coefficient.
|
|
1881
|
+
// With curves, what it puts out is each stage bent by its exponent, and a stage that starts takes up
|
|
1882
|
+
// where the last left off: its last output, and where a release started.
|
|
437
1883
|
let s = S + Int(code[at + 7])
|
|
438
|
-
let sustainIn =
|
|
439
|
-
let releaseIn =
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
let
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
1884
|
+
let sustainIn = register(5)
|
|
1885
|
+
let releaseIn = register(6)
|
|
1886
|
+
let curve = Int(code[at + 11]) - 1
|
|
1887
|
+
let curved = curve >= 0
|
|
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 {
|
|
1900
|
+
let el = fk * V + l
|
|
1901
|
+
let gate = a[el]
|
|
1902
|
+
var stage = s[l]
|
|
1903
|
+
var level = s[L + l]
|
|
1904
|
+
let before = stage
|
|
1905
|
+
if s[2 * L + l] != trigger[l] {
|
|
1906
|
+
s[2 * L + l] = trigger[l]
|
|
1907
|
+
if gate > 0.5 { stage = 1 }
|
|
1908
|
+
} else if gate > 0.5 && s[3 * L + l] <= 0.5 {
|
|
1909
|
+
stage = 1
|
|
464
1910
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
1911
|
+
if gate <= 0.5 && stage >= 1 && stage <= 3 { stage = 4 }
|
|
1912
|
+
s[3 * L + l] = gate
|
|
1913
|
+
if curved && stage != before {
|
|
1914
|
+
let last = s[8 * L + l]
|
|
1915
|
+
if stage == 1 {
|
|
1916
|
+
level = last > 0 ? (ea == 1 ? last : Float(Foundation.pow(Double(last), 1 / ea))) : 0
|
|
1917
|
+
} else if stage == 4 {
|
|
1918
|
+
level = last
|
|
1919
|
+
s[9 * L + l] = last
|
|
1920
|
+
}
|
|
474
1921
|
}
|
|
475
|
-
|
|
476
|
-
if
|
|
1922
|
+
let sustain = Swift.min(Swift.max(sustainIn[el], 0), 1)
|
|
1923
|
+
if stage == 1 {
|
|
1924
|
+
level += 1 / Swift.max(1, b[el] * sr)
|
|
1925
|
+
if level >= 1 {
|
|
1926
|
+
level = 1
|
|
1927
|
+
stage = 2
|
|
1928
|
+
}
|
|
1929
|
+
} else if stage == 2 {
|
|
1930
|
+
let time = c[el]
|
|
1931
|
+
if !(s[4 * L + l] == time) {
|
|
1932
|
+
s[4 * L + l] = time
|
|
1933
|
+
s[5 * L + l] = Foundation.exp(-9.2 / Swift.max(1, time * sr))
|
|
1934
|
+
}
|
|
1935
|
+
level = sustain + (level - sustain) * s[5 * L + l]
|
|
1936
|
+
if level - sustain < 0.0001 { stage = 3 }
|
|
1937
|
+
} else if stage == 3 {
|
|
1938
|
+
level = sustain
|
|
1939
|
+
} else if stage == 4 {
|
|
1940
|
+
let time = releaseIn[el]
|
|
1941
|
+
if !(s[6 * L + l] == time) {
|
|
1942
|
+
s[6 * L + l] = time
|
|
1943
|
+
s[7 * L + l] = Foundation.exp(-9.2 / Swift.max(1, time * sr))
|
|
1944
|
+
}
|
|
1945
|
+
level *= s[7 * L + l]
|
|
1946
|
+
if level < 0.0001 {
|
|
1947
|
+
level = 0
|
|
1948
|
+
stage = 0
|
|
1949
|
+
}
|
|
1950
|
+
} else {
|
|
477
1951
|
level = 0
|
|
478
|
-
stage = 0
|
|
479
1952
|
}
|
|
480
|
-
|
|
481
|
-
|
|
1953
|
+
s[l] = stage
|
|
1954
|
+
s[L + l] = level
|
|
1955
|
+
var out = level
|
|
1956
|
+
if bent {
|
|
1957
|
+
// An exponent of 1 is no bend: the stage as it is, with no power worked out.
|
|
1958
|
+
if stage == 1 {
|
|
1959
|
+
out = ea == 1 ? level : Float(Foundation.pow(Double(level), ea))
|
|
1960
|
+
} else if stage == 2 {
|
|
1961
|
+
let span = 1 - sustain
|
|
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
|
|
1964
|
+
} else if stage == 4 {
|
|
1965
|
+
let from = s[9 * L + l]
|
|
1966
|
+
let along = Swift.min(1, level / from)
|
|
1967
|
+
out = from > 0 ? from * (er == 1 ? along : Float(Foundation.pow(Double(along), er))) : 0
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
if curved { s[8 * L + l] = out }
|
|
1971
|
+
d[el] = out
|
|
482
1972
|
}
|
|
483
|
-
s[l] = stage
|
|
484
|
-
s[L + l] = level
|
|
485
|
-
d[l] = level
|
|
486
1973
|
}
|
|
487
1974
|
case 47, 48, 49:
|
|
488
1975
|
// A state-variable filter (Simper's form): resonance 0 … 1.
|
|
489
1976
|
let s = S + Int(code[at + 7])
|
|
490
|
-
for
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
s[3 * L + l]
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
1977
|
+
for fk in 0..<fc {
|
|
1978
|
+
for l in 0..<lanes {
|
|
1979
|
+
let el = fk * V + l
|
|
1980
|
+
let cutoff = Swift.min(Swift.max(b[el], 10), 0.49 * sr)
|
|
1981
|
+
let resonance = Swift.min(Swift.max(c[el], 0), 0.98)
|
|
1982
|
+
if !(s[2 * L + l] == cutoff && s[3 * L + l] == resonance) {
|
|
1983
|
+
s[2 * L + l] = cutoff
|
|
1984
|
+
s[3 * L + l] = resonance
|
|
1985
|
+
let g = Foundation.tan(Float.pi * cutoff / sr)
|
|
1986
|
+
let k = 2 - 2 * resonance
|
|
1987
|
+
let a1 = 1 / (1 + g * (g + k))
|
|
1988
|
+
s[4 * L + l] = a1
|
|
1989
|
+
s[5 * L + l] = g * a1
|
|
1990
|
+
s[6 * L + l] = g * g * a1
|
|
1991
|
+
s[7 * L + l] = k
|
|
1992
|
+
}
|
|
1993
|
+
let a1 = s[4 * L + l]
|
|
1994
|
+
let a2 = s[5 * L + l]
|
|
1995
|
+
let a3 = s[6 * L + l]
|
|
1996
|
+
let ic1 = s[l]
|
|
1997
|
+
let ic2 = s[L + l]
|
|
1998
|
+
let x = a[el]
|
|
1999
|
+
let v3 = x - ic2
|
|
2000
|
+
let v1 = a1 * ic1 + a2 * v3
|
|
2001
|
+
let v2 = ic2 + a2 * ic1 + a3 * v3
|
|
2002
|
+
s[l] = 2 * v1 - ic1
|
|
2003
|
+
s[L + l] = 2 * v2 - ic2
|
|
2004
|
+
d[el] = op == 47 ? v2 : op == 49 ? v1 : x - s[7 * L + l] * v1 - v2
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
case 50:
|
|
2008
|
+
// wavetable(table, freq, position): the level that cannot alias at freq (worked out again only when freq
|
|
2009
|
+
// changes), read between samples and between frames.
|
|
2010
|
+
let s = S + Int(code[at + 7])
|
|
2011
|
+
// The table is a constant register's, the same in every lane.
|
|
2012
|
+
let index = a[0]
|
|
2013
|
+
let t = index >= 0 && Int(exactly: index.rounded(.down)).map { $0 < tableCount } == true ? Int(index) : 0
|
|
2014
|
+
let frames = tableFrames.isEmpty ? 1 : tableFrames[t]
|
|
2015
|
+
let T = tablePointers[t]
|
|
2016
|
+
let base = 0
|
|
2017
|
+
for fk in 0..<fc {
|
|
2018
|
+
for l in 0..<lanes {
|
|
2019
|
+
let el = fk * V + l
|
|
2020
|
+
let p = s[l]
|
|
2021
|
+
let f = b[el]
|
|
2022
|
+
let dt = f / sr
|
|
2023
|
+
if s[L + l] != f {
|
|
2024
|
+
s[L + l] = f
|
|
2025
|
+
var level = 0
|
|
2026
|
+
let allowed = 0.5 * Double(sr) / Double(Swift.abs(f))
|
|
2027
|
+
if allowed < 512 {
|
|
2028
|
+
let octaves = Foundation.log2(512 / allowed)
|
|
2029
|
+
level = octaves > 8 ? 9 : Int(octaves.rounded(.up))
|
|
2030
|
+
}
|
|
2031
|
+
s[2 * L + l] = Float(level)
|
|
2032
|
+
}
|
|
2033
|
+
let level = Int(s[2 * L + l])
|
|
2034
|
+
// NaN reads as 0 here, as in the JavaScript machine.
|
|
2035
|
+
let morph = c[el] > 0 ? (c[el] < 1 ? c[el] : 1) : 0
|
|
2036
|
+
let position = morph * Float(frames - 1)
|
|
2037
|
+
// Never below 0: truncating is rounding down.
|
|
2038
|
+
let f0 = Int(position)
|
|
2039
|
+
let f1 = Swift.min(f0 + 1, frames - 1)
|
|
2040
|
+
let ff = position - Float(f0)
|
|
2041
|
+
let x = p * 2048
|
|
2042
|
+
// A phase gone to NaN reads NaN, which the block's check silences.
|
|
2043
|
+
let i0 = x >= 0 && x < 2048 ? Int(x) : x.isFinite ? Int(x.rounded(.down)) : 0
|
|
2044
|
+
let fr = x - Float(i0)
|
|
2045
|
+
let j0 = i0 & 2047
|
|
2046
|
+
let j1 = (i0 + 1) & 2047
|
|
2047
|
+
// Samples j0 and j1, each its frames side by side.
|
|
2048
|
+
let row0 = base + (level * 2048 + j0) * frames
|
|
2049
|
+
let row1 = base + (level * 2048 + j1) * frames
|
|
2050
|
+
let v0 = T[row0 + f0] + (T[row1 + f0] - T[row0 + f0]) * fr
|
|
2051
|
+
let v1 = T[row0 + f1] + (T[row1 + f1] - T[row0 + f1]) * fr
|
|
2052
|
+
d[el] = v0 + (v1 - v0) * ff
|
|
2053
|
+
let next = p + dt
|
|
2054
|
+
s[l] = next >= 0 && next < 1 ? next : next - next.rounded(.down)
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
case 51:
|
|
2058
|
+
// delay(input, seconds, longest): read at least a sample back, between samples, then write.
|
|
2059
|
+
let s = S + Int(code[at + 7])
|
|
2060
|
+
let length = Int(code[at + 10])
|
|
2061
|
+
let mask = length - 1
|
|
2062
|
+
let memory = delaySamples + Int(code[at + 9])
|
|
2063
|
+
for fk in 0..<fc {
|
|
2064
|
+
for l in 0..<lanes {
|
|
2065
|
+
let el = fk * V + l
|
|
2066
|
+
let w = Int(s[l])
|
|
2067
|
+
let x = a[el]
|
|
2068
|
+
var ds = b[el] * sr
|
|
2069
|
+
ds = ds >= 1 ? (ds <= Float(length - 2) ? ds : Float(length - 2)) : 1
|
|
2070
|
+
let position = Float(w) - ds
|
|
2071
|
+
let i0 = Int(position.rounded(.down))
|
|
2072
|
+
let fr = position - Float(i0)
|
|
2073
|
+
let line = memory + l * length
|
|
2074
|
+
let v0 = line[i0 & mask]
|
|
2075
|
+
let v1 = line[(i0 + 1) & mask]
|
|
2076
|
+
d[el] = v0 + (v1 - v0) * fr
|
|
2077
|
+
line[w & mask] = x
|
|
2078
|
+
s[l] = Float((w + 1) & mask)
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
case 52:
|
|
2082
|
+
// shape(shape, x): x 0 … 1 along it, between points.
|
|
2083
|
+
for fk in 0..<fc {
|
|
2084
|
+
for l in 0..<lanes {
|
|
2085
|
+
let el = fk * V + l
|
|
2086
|
+
let t = Self.index(a[el], shapeLengths.count)
|
|
2087
|
+
let points = shapeLengths.isEmpty ? 1 : shapeLengths[t]
|
|
2088
|
+
let P = shapePointers[t]
|
|
2089
|
+
let base = 0
|
|
2090
|
+
let x = b[el]
|
|
2091
|
+
let position = (x > 0 ? (x < 1 ? x : 1) : 0) * Float(points - 1)
|
|
2092
|
+
let i0 = Int(position.rounded(.down))
|
|
2093
|
+
let v0 = P[base + i0]
|
|
2094
|
+
let v1 = P[base + Swift.min(i0 + 1, points - 1)]
|
|
2095
|
+
d[el] = v0 + (v1 - v0) * (position - Float(i0))
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
case 53:
|
|
2099
|
+
// play(sample, rate, start): from start (0 … 1 along it) at each note on, at rate (1 its own pitch),
|
|
2100
|
+
// between samples; silent past either end. Where it plays is a whole sample and the fraction past it,
|
|
2101
|
+
// so it keeps time.
|
|
2102
|
+
let s = S + Int(code[at + 7])
|
|
2103
|
+
for fk in 0..<fc {
|
|
2104
|
+
for l in 0..<lanes {
|
|
2105
|
+
let el = fk * V + l
|
|
2106
|
+
let t = Self.index(a[el], sampleLengths.count)
|
|
2107
|
+
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
2108
|
+
let A = samplePointers[t]
|
|
2109
|
+
let base = 0
|
|
2110
|
+
if s[2 * L + l] != trigger[l] {
|
|
2111
|
+
s[2 * L + l] = trigger[l]
|
|
2112
|
+
let start = c[el]
|
|
2113
|
+
let p = (start > 0 ? (start < 1 ? start : 1) : 0) * Float(length - 1)
|
|
2114
|
+
s[l] = p.rounded(.down)
|
|
2115
|
+
s[L + l] = p - p.rounded(.down)
|
|
2116
|
+
}
|
|
2117
|
+
let index = s[l]
|
|
2118
|
+
let fraction = s[L + l]
|
|
2119
|
+
var v: Float = 0
|
|
2120
|
+
if index >= 0 && index < Float(length) {
|
|
2121
|
+
// Past the last sample, silence.
|
|
2122
|
+
let k = base + Int(index)
|
|
2123
|
+
let after = index < Float(length - 1) ? A[k + 1] : 0
|
|
2124
|
+
v = A[k] + (after - A[k]) * fraction
|
|
2125
|
+
}
|
|
2126
|
+
d[el] = v
|
|
2127
|
+
var step = b[el] * (sampleRatios.isEmpty ? 1 : sampleRatios[t])
|
|
2128
|
+
step = step > -65536 ? (step < 65536 ? step : 65536) : -65536
|
|
2129
|
+
let next = fraction + step
|
|
2130
|
+
let whole = next.rounded(.down)
|
|
2131
|
+
let moved = index + whole
|
|
2132
|
+
s[l] = moved > -1 ? (moved < Float(length) ? moved : Float(length)) : -1
|
|
2133
|
+
s[L + l] = next - whole
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
case 78:
|
|
2137
|
+
// tableread(table, phase, freq, position): wavetable's read, at a phase the program keeps (wrapped to
|
|
2138
|
+
// 0 … 1), at the level that cannot alias at freq (worked out again only when freq changes).
|
|
2139
|
+
let s = S + Int(code[at + 7])
|
|
2140
|
+
let e = register(5)
|
|
2141
|
+
// The table is a constant register's, the same in every lane.
|
|
2142
|
+
let index = a[0]
|
|
2143
|
+
let t = index >= 0 && Int(exactly: index.rounded(.down)).map { $0 < tableCount } == true ? Int(index) : 0
|
|
2144
|
+
let frames = tableFrames.isEmpty ? 1 : tableFrames[t]
|
|
2145
|
+
let T = tablePointers[t]
|
|
2146
|
+
let base = 0
|
|
2147
|
+
for fk in 0..<fc {
|
|
2148
|
+
for l in 0..<lanes {
|
|
2149
|
+
let el = fk * V + l
|
|
2150
|
+
let f = c[el]
|
|
2151
|
+
if s[l] != f {
|
|
2152
|
+
s[l] = f
|
|
2153
|
+
var level = 0
|
|
2154
|
+
let allowed = 0.5 * Double(sr) / Double(Swift.abs(f))
|
|
2155
|
+
if allowed < 512 {
|
|
2156
|
+
let octaves = Foundation.log2(512 / allowed)
|
|
2157
|
+
level = octaves > 8 ? 9 : Int(octaves.rounded(.up))
|
|
2158
|
+
}
|
|
2159
|
+
s[L + l] = Float(level)
|
|
2160
|
+
}
|
|
2161
|
+
let level = Int(s[L + l])
|
|
2162
|
+
let morph = e[el] > 0 ? (e[el] < 1 ? e[el] : 1) : 0
|
|
2163
|
+
let position = morph * Float(frames - 1)
|
|
2164
|
+
let f0 = Int(position)
|
|
2165
|
+
let f1 = Swift.min(f0 + 1, frames - 1)
|
|
2166
|
+
let ff = position - Float(f0)
|
|
2167
|
+
let phase = b[el]
|
|
2168
|
+
// 0 … 1 (1 itself, as a phase just below 0 rounds to); NaN past infinities.
|
|
2169
|
+
let p = phase - phase.rounded(.down)
|
|
2170
|
+
let x = p * 2048
|
|
2171
|
+
let i0 = x >= 0 && x <= 2048 ? Int(x) : 0
|
|
2172
|
+
let fr = x - Float(i0)
|
|
2173
|
+
let j0 = i0 & 2047
|
|
2174
|
+
let j1 = (i0 + 1) & 2047
|
|
2175
|
+
let row0 = base + (level * 2048 + j0) * frames
|
|
2176
|
+
let row1 = base + (level * 2048 + j1) * frames
|
|
2177
|
+
let v0 = T[row0 + f0] + (T[row1 + f0] - T[row0 + f0]) * fr
|
|
2178
|
+
let v1 = T[row0 + f1] + (T[row1 + f1] - T[row0 + f1]) * fr
|
|
2179
|
+
d[el] = v0 + (v1 - v0) * ff
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
case 79:
|
|
2183
|
+
// loop(sample, rate, start): play's, but round and round: past either end it comes in at the other.
|
|
2184
|
+
let s = S + Int(code[at + 7])
|
|
2185
|
+
for fk in 0..<fc {
|
|
2186
|
+
for l in 0..<lanes {
|
|
2187
|
+
let el = fk * V + l
|
|
2188
|
+
let t = Self.index(a[el], sampleLengths.count)
|
|
2189
|
+
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
2190
|
+
let A = samplePointers[t]
|
|
2191
|
+
let base = 0
|
|
2192
|
+
if s[2 * L + l] != trigger[l] {
|
|
2193
|
+
s[2 * L + l] = trigger[l]
|
|
2194
|
+
let start = c[el]
|
|
2195
|
+
let p = (start > 0 ? (start < 1 ? start : 1) : 0) * Float(length - 1)
|
|
2196
|
+
s[l] = p.rounded(.down)
|
|
2197
|
+
s[L + l] = p - p.rounded(.down)
|
|
2198
|
+
}
|
|
2199
|
+
// Always a sample of this one, though a lane's index may change samples under it.
|
|
2200
|
+
let index = ((Int(s[l]) % length) + length) % length
|
|
2201
|
+
let fraction = s[L + l]
|
|
2202
|
+
let k = base + index
|
|
2203
|
+
let after = index < length - 1 ? A[k + 1] : A[base]
|
|
2204
|
+
d[el] = A[k] + (after - A[k]) * fraction
|
|
2205
|
+
var step = b[el] * (sampleRatios.isEmpty ? 1 : sampleRatios[t])
|
|
2206
|
+
step = step > -65536 ? (step < 65536 ? step : 65536) : -65536
|
|
2207
|
+
let next = fraction + step
|
|
2208
|
+
let whole = next.rounded(.down)
|
|
2209
|
+
s[l] = Float((((index + Int(whole)) % length) + length) % length)
|
|
2210
|
+
s[L + l] = next - whole
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
case 54:
|
|
2214
|
+
// grains(sample, position, density, length, pitch): a grain every 1 / density seconds from position
|
|
2215
|
+
// (0 … 1 along the sample, and up to 20 ms on), length seconds (0.001 … 1) long, at pitch (1/16 …
|
|
2216
|
+
// 16, 1 its own), under a parabola; up to 8 at once (with none free, one is skipped).
|
|
2217
|
+
let s = S + Int(code[at + 7])
|
|
2218
|
+
let lengthIn = register(5)
|
|
2219
|
+
let pitchIn = register(6)
|
|
2220
|
+
for fk in 0..<fc {
|
|
2221
|
+
for l in 0..<lanes {
|
|
2222
|
+
let el = fk * V + l
|
|
2223
|
+
let t = Self.index(a[el], sampleLengths.count)
|
|
2224
|
+
let length = sampleLengths.isEmpty ? 2 : sampleLengths[t]
|
|
2225
|
+
let A = samplePointers[t]
|
|
2226
|
+
let base = 0
|
|
2227
|
+
let ratio = sampleRatios.isEmpty ? 1 : sampleRatios[t]
|
|
2228
|
+
let density = c[el]
|
|
2229
|
+
var countdown = s[l]
|
|
2230
|
+
if density > 0 {
|
|
2231
|
+
if countdown <= 0 {
|
|
2232
|
+
var g = 0
|
|
2233
|
+
while g < 8 && s[(3 + 4 * g) * L + l] < s[(4 + 4 * g) * L + l] { g += 1 }
|
|
2234
|
+
if g < 8 {
|
|
2235
|
+
let random = UnsafeMutableRawPointer(s + L + l)
|
|
2236
|
+
var x = random.load(as: UInt32.self)
|
|
2237
|
+
x ^= x << 13
|
|
2238
|
+
x ^= x >> 17
|
|
2239
|
+
x ^= x << 5
|
|
2240
|
+
random.storeBytes(of: x, as: UInt32.self)
|
|
2241
|
+
let position = b[el]
|
|
2242
|
+
let spray = sampleSprays.isEmpty ? 1 : sampleSprays[t]
|
|
2243
|
+
let start = ((position > 0 ? (position < 1 ? position : 1) : 0) * Float(length - 1)).rounded(.down) + Float(x % spray)
|
|
2244
|
+
let seconds = lengthIn[el]
|
|
2245
|
+
let frames = ((seconds > 0.001 ? (seconds < 1 ? seconds : 1) : 0.001) * sr).rounded(.down)
|
|
2246
|
+
let pitch = pitchIn[el]
|
|
2247
|
+
let w = s + (2 + 4 * g) * L + l
|
|
2248
|
+
w[0] = start < Float(length - 1) ? start : Float(length - 1)
|
|
2249
|
+
w[L] = 0
|
|
2250
|
+
w[2 * L] = frames > 1 ? frames : 1
|
|
2251
|
+
w[3 * L] = (pitch > 0.0625 ? (pitch < 16 ? pitch : 16) : 0.0625) * ratio
|
|
2252
|
+
}
|
|
2253
|
+
let interval = sr / density
|
|
2254
|
+
countdown += interval > 1 ? interval : 1
|
|
2255
|
+
}
|
|
2256
|
+
countdown -= 1
|
|
2257
|
+
} else {
|
|
2258
|
+
countdown = 0
|
|
2259
|
+
}
|
|
2260
|
+
s[l] = countdown
|
|
2261
|
+
var sum: Float = 0
|
|
2262
|
+
for g in 0..<8 {
|
|
2263
|
+
let w = s + (2 + 4 * g) * L + l
|
|
2264
|
+
let age = w[L]
|
|
2265
|
+
let frames = w[2 * L]
|
|
2266
|
+
guard age < frames else { continue }
|
|
2267
|
+
let offset = age * w[3 * L]
|
|
2268
|
+
let whole = offset.rounded(.down)
|
|
2269
|
+
let index = w[0] + whole
|
|
2270
|
+
if index >= 0 && index < Float(length - 1) {
|
|
2271
|
+
let k = base + Int(index)
|
|
2272
|
+
let v = A[k] + (A[k + 1] - A[k]) * (offset - whole)
|
|
2273
|
+
let phase = age / frames
|
|
2274
|
+
sum += v * (4 * phase * (1 - phase))
|
|
2275
|
+
}
|
|
2276
|
+
w[L] = age + 1
|
|
2277
|
+
}
|
|
2278
|
+
d[el] = sum
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
case 57:
|
|
2282
|
+
// modsum(routes): Σ amount × source over the list, route by route. An amount is a parameter, the same
|
|
2283
|
+
// in every lane, so a route at 0 is passed over once for all of them and costs nothing.
|
|
2284
|
+
each { d[$0] = 0 }
|
|
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]
|
|
2289
|
+
while k < end {
|
|
2290
|
+
let amount = R + routeRegisters[k + 1]
|
|
2291
|
+
let source = R + routeRegisters[k] + (routeState[k] ? 0 : shift)
|
|
2292
|
+
k += 2
|
|
2293
|
+
if amount[0] == 0 { continue }
|
|
2294
|
+
each { d[$0] += amount[$0] * source[$0] }
|
|
2295
|
+
}
|
|
2296
|
+
case 55:
|
|
2297
|
+
// smooth(input, seconds): toward the input by 1 − e^(−1 / (seconds × sampleRate)) a frame, from where
|
|
2298
|
+
// the input is on its first.
|
|
2299
|
+
let s = S + Int(code[at + 7])
|
|
2300
|
+
for fk in 0..<fc {
|
|
2301
|
+
for l in 0..<lanes {
|
|
2302
|
+
let el = fk * V + l
|
|
2303
|
+
let x = a[el]
|
|
2304
|
+
var y = s[l]
|
|
2305
|
+
if y.isNaN { y = x }
|
|
2306
|
+
let time = b[el]
|
|
2307
|
+
if !(s[L + l] == time) {
|
|
2308
|
+
s[L + l] = time
|
|
2309
|
+
let frames = time * sr
|
|
2310
|
+
s[2 * L + l] = frames > 1 ? -Foundation.expm1(-1 / frames) : 1
|
|
2311
|
+
}
|
|
2312
|
+
y += (x - y) * s[2 * L + l]
|
|
2313
|
+
s[l] = y
|
|
2314
|
+
d[el] = y
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
case 56:
|
|
2318
|
+
// hold(input, trigger): takes the input as the trigger rises above 0.5, and keeps it.
|
|
2319
|
+
let s = S + Int(code[at + 7])
|
|
2320
|
+
for fk in 0..<fc {
|
|
2321
|
+
for l in 0..<lanes {
|
|
2322
|
+
let el = fk * V + l
|
|
2323
|
+
let t = b[el]
|
|
2324
|
+
if t > 0.5 && !(s[L + l] > 0.5) { s[l] = a[el] }
|
|
2325
|
+
s[L + l] = t
|
|
2326
|
+
d[el] = s[l]
|
|
503
2327
|
}
|
|
504
|
-
let a1 = s[4 * L + l]
|
|
505
|
-
let a2 = s[5 * L + l]
|
|
506
|
-
let a3 = s[6 * L + l]
|
|
507
|
-
let ic1 = s[l]
|
|
508
|
-
let ic2 = s[L + l]
|
|
509
|
-
let x = a[l]
|
|
510
|
-
let v3 = x - ic2
|
|
511
|
-
let v1 = a1 * ic1 + a2 * v3
|
|
512
|
-
let v2 = ic2 + a2 * ic1 + a3 * v3
|
|
513
|
-
s[l] = 2 * v1 - ic1
|
|
514
|
-
s[L + l] = 2 * v2 - ic2
|
|
515
|
-
d[l] = op == 47 ? v2 : op == 49 ? v1 : x - s[7 * L + l] * v1 - v2
|
|
516
2328
|
}
|
|
517
2329
|
default:
|
|
518
2330
|
break
|
|
519
2331
|
}
|
|
520
|
-
at += 8
|
|
521
2332
|
}
|
|
522
2333
|
}
|
|
523
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
|
+
}
|