-
Notifications
You must be signed in to change notification settings - Fork 149
/
Copy pathsoftcut.lua
551 lines (468 loc) · 22.9 KB
/
softcut.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
--- Softcut module.
--
-- API for controlling the "softcut" buffer processor
-- includes low-level setters and mid-level utilities
--
-- IMPORTANT: all indices are 1-based, per lua convention!
-- this applies to indices for selecting voice, ADC/DAC channel, buffer, &c.
-- however, _quantities_ (including units of time) can still start at zero.
--
-- @module softcut
-- @alias SC
local SC = {}
local controlspec = require 'core/controlspec'
-------------------------------
-- @section constants
-- @field number of voices
SC.VOICE_COUNT = 6
-- @field length of buffer in seconds
SC.BUFFER_SIZE = 16777216 / 48000
-------------------------------
-- @section setters
--- set output level of each voice
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.level = function(voice, amp)
_norns.level_cut(voice, amp)
end
--- set pan position of each voice.
-- -1 == full left, +1 == full right, 0 == centered
-- @tparam int voice : voice index
-- @tparam number pos : position in [-1, 1]
SC.pan = function(voice, pos)
_norns.pan_cut(voice, pos)
end
--- set input level to each voice/channel.
-- @tparam int ch : ADC channel index
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.level_input_cut = function(ch, voice, amp)
_norns.level_input_cut(ch, voice, amp)
end
--- set mix matrix, voice output to voice input.
-- @tparam number src : source voice index
-- @tparam number dst : destination voice index
-- @tparam number amp : linear amplitude
SC.level_cut_cut = function(src, dst, amp)
_norns.level_cut_cut(src, dst, amp)
end
--- set play status.
-- @tparam int voice : voice index
-- @tparam int state : off/on (0,1)
SC.play = function(voice,state) _norns.cut_param("play_flag",voice,state) end
--- set playback rate.
-- @tparam int voice : voice index
-- @tparam number rate : speed of read/write head (unitless; 1 == normal)
SC.rate = function(voice,rate) _norns.cut_param("rate",voice,rate) end
--- set loop start.
-- @tparam int voice : voice index
-- @tparam number pos : loop start position in seconds
SC.loop_start = function(voice,pos) _norns.cut_param("loop_start",voice,pos) end
--- set loop end.
-- @tparam int voice : voice index
-- @tparam number pos : loop end position in seconds
SC.loop_end = function(voice,pos) _norns.cut_param("loop_end",voice,pos) end
--- set loop mode.
-- "0" indicates one-shot mode: voice will play to the loop endpoint, fadeout and stop.
-- "1" indicates crossfaded looping mode.
-- @tparam int voice : voice index
-- @tparam int state : off/on (0,1)
SC.loop = function(voice,state) _norns.cut_param("loop_flag",voice,state) end
--- set fade time.
-- @tparam int voice : voice index
-- @tparam number fade_time : crossfade time in seconds
SC.fade_time = function(voice,fade_time) _norns.cut_param("fade_time",voice,fade_time) end
--- set record level.
-- this sets the realtime-modulated record level,
-- by which incoming signal is scaled before writing to the buffer
-- `recpre` slew level applies
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude of new signal
SC.rec_level = function(voice,amp) _norns.cut_param("rec_level",voice,amp) end
--- set pre level (overdub preserve.)
-- this sets the realtime-modulated "preserve" level,
-- by which existing material is scaled on each pass of the write head.
-- `recpre` slew level applies
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude of preserved signal
SC.pre_level = function(voice,amp) _norns.cut_param("pre_level",voice,amp) end
--- set record state.
-- @tparam int voice : voice number (1-?)
-- @tparam int state : off/on (0,1)
SC.rec = function(voice,state) _norns.cut_param("rec_flag",voice,state) end
--- set record head offset
SC.rec_offset = function(voice,value) _norns.cut_param("rec_offset",voice,value) end
--- set play position
SC.position = function(voice,value) _norns.cut_param("position",voice,value) end
--- specify buffer used by voice.
-- @tparam int i : voice number
-- @tparam int b : buffer number (1,2)
SC.buffer = function(i,b) _norns.cut_param_ii("buffer",i,b) end
--- synchronize two voices.
--- position of "dst" will be immediately set to that of "source"
-- @tparam int dst : destination voice index
-- @tparam int src : source voice index
-- @tparam number offset : additional offset in seconds
SC.voice_sync = function(dst, src, offset) _norns.cut_param_iif("voice_sync",dst,src,offset) end
--- set pre_filter cutoff frequency.
--- @tparam int voice : voice index
--- @tparam number fc : cutoff frequency in Hz
SC.pre_filter_fc = function(voice,fc) _norns.cut_param("pre_filter_fc",voice,fc) end
--- set pre_filter amount of rate modulation.
-- this parameter controls the amount by which the current rate affects filter cutoff frequency
-- (always in a negative direction, towards zero.)
-- with mod == 1, setting rate = 0 will also fully close the filter.
-- this can be useful as a crude anti-aliasing method...
--- @tparam int voice : voice index
--- @tparam number amount : modulation amount in [0, 1]
SC.pre_filter_fc_mod = function(voice,amount) _norns.cut_param("pre_filter_fc_mod",voice,amount) end
--- set pre_filter reciprocal of Q-factor.
-- the reciprocal of the filter's Q-factor is a measure of bandwidth,
-- that is independent of center frequency.
-- RQ ~= 0 will result in self-oscillation;
-- RQ == 4 gives a bandwidth of 2 octaves.
-- @tparam int voice : voice index
-- @tparam number rq : reciprocal of filter Q-factor for voice
SC.pre_filter_rq = function(voice,rq) _norns.cut_param("pre_filter_rq",voice,rq) end
--- set pre_filter lowpass output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.pre_filter_lp = function(voice,amp) _norns.cut_param("pre_filter_lp",voice,amp) end
--- set pre-filter highpass output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.pre_filter_hp = function(voice,amp) _norns.cut_param("pre_filter_hp",voice,amp) end
--- set pre-filter bandpass output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.pre_filter_bp = function(voice,amp) _norns.cut_param("pre_filter_bp",voice,amp) end
--- set pre_filter band-reject output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.pre_filter_br = function(voice,amp) _norns.cut_param("pre_filter_br",voice,amp) end
--- set pre_filter dry output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.pre_filter_dry = function(voice,amp) _norns.cut_param("pre_filter_dry",voice,amp) end
---------------------
-- wrappers around pre_filter, for backwards compatibility
SC.filter_fc = function(voice,value) SC.pre_filter_fc(voice, value) end
SC.filter_fc_mod = function(voice,value) SC.pre_filter_fc_mod(voice, value) end
SC.filter_rq = function(voice,value) SC.pre_filter_rq(voice, value) end
SC.filter_lp = function(voice,value) SC.pre_filter_lp(voice, value) end
SC.filter_hp = function(voice,value) SC.pre_filter_hp(voice, value) end
SC.filter_bp = function(voice,value) SC.pre_filter_bp(voice, value) end
SC.filter_br = function(voice,value) SC.pre_filter_br(voice, value) end
SC.filter_dry = function(voice,value) SC.pre_filter_dry(voice, value) end
--- set post-filter cutoff
-- @tparam int voice : voice index
-- @tparam number value : cutoff frequency in Hz
SC.post_filter_fc = function(voice,value) _norns.cut_param("post_filter_fc",voice,value) end
--- set post-filter reciprocal of Q
-- the reciprocal of the filter's Q factor is a measure of bandwidth,
-- that is independent of center frequency.
-- RQ ~= 0 will result in self oscillation;
-- RQ == 4 gives a bandwidth of 2 octaves.
-- @tparam int voice : voice index
-- @tparam number rq : reciprocal of filter Q-factor for voice
SC.post_filter_rq = function(voice,rq) _norns.cut_param("post_filter_rq",voice,rq) end
--- set post_filter lowpass output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.post_filter_lp = function(voice,amp) _norns.cut_param("post_filter_lp",voice,amp) end
--- set post-filter highpass output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.post_filter_hp = function(voice,amp) _norns.cut_param("post_filter_hp",voice,amp) end
--- set post-filter bandpass output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.post_filter_bp = function(voice,amp) _norns.cut_param("post_filter_bp",voice,amp) end
--- set post_filter band-reject output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.post_filter_br = function(voice,amp) _norns.cut_param("post_filter_br",voice,amp) end
--- set post_filter dry output level.
-- @tparam int voice : voice index
-- @tparam number amp : linear amplitude
SC.post_filter_dry = function(voice,amp) _norns.cut_param("post_filter_dry",voice,amp) end
--- set level-slew time
-- this slew time applies to level at all mix points: ADC->voice, voice->voice, &c.
-- @tparam int voice : voice index
-- @tparam number time : exponential slew time in seconds (-60db convergence)
SC.level_slew_time = function(voice,time) _norns.cut_param("level_slew_time",voice,time) end
--- set pan slew time
-- @tparam int voice : voice index
-- @tparam number time : exponential slew time in seconds (-60db convergence)
SC.pan_slew_time = function(voice,time) _norns.cut_param("pan_slew_time",voice,time) end
--- set recpre slew time
-- affects slew time for record and pre levels
-- @tparam int voice : voice index
-- @tparam number time : exponential slew time in seconds (-60db convergence)
SC.recpre_slew_time = function(voice,time) _norns.cut_param("recpre_slew_time",voice,time) end
--- set rate slew time
-- affects slew time for rate
-- @tparam int voice : voice index
-- @tparam number time : exponential slew time in seconds (-60db convergence)
SC.rate_slew_time = function(voice,time) _norns.cut_param("rate_slew_time",voice,time) end
--- set phase poll quantum
-- e.g. 0.25 will produce 4 updates per second with rate=1
-- judicious use of this parameter is preferable to using a very fast poll (for performance,)
-- or polling at arbitrary rate (for accuracy when rate is slewed.)
-- @tparam int voice : voice index
-- @tparam number quantum : phase reporting interval, in seconds
SC.phase_quant = function(voice,quantum) _norns.cut_param("phase_quant",voice,quantum) end
--- set phase poll offset in frames
-- @tparam int voice : voice index
-- @tparam number offset : phase poll offset in seconds
SC.phase_offset = function(voice,offset) _norns.cut_param("phase_offset",voice,offset) end
--- start phase poll
SC.poll_start_phase = function() _norns.poll_start_cut_phase() end
--- stop phase poll
SC.poll_stop_phase = function() _norns.poll_stop_cut_phase() end
--- set voice enable
-- disabled voices have no effect and consume basically zero CPU
-- @tparam int voice : voice number (1-?)
-- @tparam int state : off/on (0,1)
SC.enable = function(voice, state) _norns.cut_enable(voice, state) end
-- - TODO: complete function doc comments below here!
--- clear all buffers completely
SC.buffer_clear = function() _norns.cut_buffer_clear() end
--- clear one buffer completely
-- @tparam int channel : buffer channel index (1-based)
SC.buffer_clear_channel = function(channel) _norns.cut_buffer_clear_channel(channel) end
--- clear region (both channels)
-- @tparam number start : start point in seconds
-- @tparam number dur : duration in seconds
-- @tparam number fade_time : crossfade time in seconds
-- @tparam number preserve : level of existing material
SC.buffer_clear_region = function(start, dur, fade_time, preserve)
_norns.cut_buffer_clear_region(start, dur or -1, fade_time or 0, preserve or 0)
end
--- clear region of single channel
-- @tparam int ch : buffer channel index (1-based)
-- @tparam number start : start point in seconds
-- @tparam number dur : duration in seconds
-- @tparam number fade_time : crossfade time in seconds
-- @tparam number preserve : level of existing material
SC.buffer_clear_region_channel = function(ch, start, dur, fade_time, preserve)
_norns.cut_buffer_clear_region_channel(ch, start, dur or -1, fade_time or 0, preserve or 0)
end
--- copy region from one point in a buffer to another
-- @tparam int src_ch : source buffer index (1-based)
-- @tparam int dst_ch : destination buffer index (1-based)
-- @tparam number start_src : start point in source, in seconds
-- @tparam number start_dst : start point in destination, in seconds
-- @tparam number dur : duration in seconds. if -1, copy as much as possible.
-- @tparam number fade_time : crossfade time in seconds
-- @tparam number preserve : level of existing material
-- @tparam int reverse : nonzero to reverse while copying. when reversing, overlap between source and destination regions is not handled.
SC.buffer_copy_mono = function(src_ch, dst_ch, start_src, start_dst, dur, fade_time, preserve, reverse)
_norns.cut_buffer_copy_mono(src_ch, dst_ch, start_src, start_dst, dur or -1, fade_time or 0, preserve or 0, reverse or 0)
end
--- copy region of both buffers to another point
-- @tparam number start_src : start point in source, in seconds
-- @tparam number start_dst : start point in destination, in seconds
-- @tparam number dur : duration in seconds. if -1, copy as much as possible.
-- @tparam number fade_time : crossfade time in seconds
-- @tparam number preserve : level of existing material
-- @tparam int reverse : nonzero to reverse while copying.
SC.buffer_copy_stereo = function(start_src, start_dst, dur, fade_time, preserve, reverse)
_norns.cut_buffer_copy_stereo(start_src, start_dst, dur or -1, fade_time or 0, preserve or 0, reverse or 0)
end
--- read mono soundfile to arbitrary region of single buffer
-- @tparam string file : input file path
-- @tparam number start_src : start point in source, in seconds
-- @tparam number start_dst : start point in destination, in seconds
-- @tparam number dur : duration in seconds. if -1, read as much as possible.
-- @tparam int ch_src : soundfile channel to read
-- @tparam int ch_dst : buffer channel to write
-- @tparam number preserve : level of existing material
-- @tparam number mix : level of new material
SC.buffer_read_mono = function(file, start_src, start_dst, dur, ch_src, ch_dst, preserve, mix)
_norns.cut_buffer_read_mono(file, start_src or 0, start_dst or 0, dur or -1, ch_src or 1, ch_dst or 1, preserve or 0, mix or 1)
end
--- read stereo soundfile to an arbitrary region in both buffers
-- @tparam string file : input file path
-- @tparam number start_src : start point in source, in seconds
-- @tparam number start_dst : start point in destination, in seconds
-- @tparam number dur : duration in seconds. if -1, read as much as possible
-- @tparam number preserve : level of existing material
-- @tparam number mix : level of new material
SC.buffer_read_stereo = function(file, start_src, start_dst, dur, preserve, mix)
_norns.cut_buffer_read_stereo(file, start_src or 0, start_dst or 0, dur or -1, preserve or 0, mix or 1)
end
--- write an arbitrary buffer region to soundfile (mono)
-- @tparam string file : output file path
-- @tparam number start : start point in seconds
-- @tparam number dur : duration in seconds. if -1, read as much as possible
-- @tparam int ch : buffer channel index (1-based)
SC.buffer_write_mono = function(file, start, dur, ch)
_norns.cut_buffer_write_mono(file, start or 0, dur or -1, ch or 1)
end
--- write an arbitrary region from both buffers to stereo soundfile
-- @tparam string file : output file path
-- @tparam number start : start point in seconds
-- @tparam number dur : duration in seconds. if -1, read as much as possible
SC.buffer_write_stereo = function(file, start, dur)
_norns.cut_buffer_write_stereo(file, start or 0, dur or -1)
end
--- set function for phase poll
-- @tparam function func : callback function. this function should take two parameters (voice, phase)
SC.event_phase = function(func) _norns.softcut_phase = func end
--- request snapshot of buffer content for region.
-- @tparam integer ch : buffer channel index (1-based)
-- @tparam number start : beginning of region in seconds
-- @tparam number dur : length of region in seconds
-- @tparam integer samples : max number of samples to retrieve. if less than the number of frames in the region, content will be downsampled
SC.render_buffer = function(ch, start, dur, samples)
_norns.cut_buffer_render(ch, start, dur, samples)
end
--- set function for render callback. use render_buffer to request contents.
-- @tparam function func : called when buffer content is ready. args: (ch, start, sec_per_sample, samples)
SC.event_render = function(func)
_norns.softcut_render = func
end
--- query playback position
-- @tparam integer i : which softcut voice
SC.query_position = function(i) _norns.cut_query_position(i) end
--- set function for query callback. use query_position to request contents.
-- @tparam function func : called when index and position is returned
SC.event_position = function(func)
_norns.softcut_position = func
end
-------------------------------
-- @section utilities
--- reset state of softcut process on backend.
-- this should correspond to the values returned by the `defaults()` function above.
function SC.reset()
_norns.cut_reset()
SC.event_phase(norns.none)
end
--- get the default state of the softcut system
-- this returns a table for each voice,
-- in which each key corresponds to the name of one of the setter functions defined above.
-- for parameters with one value per voice, the corresponding entry is also a single value.
-- for parameters with multiple values (e.g. matrix indices), the entry is a table.
-- NB: these values are synchronized by hand with those specified in the softcut cpp sources
-- @treturn table table of parameter states for each voice
function SC.defaults()
zeros = {}
for i=1,SC.VOICE_COUNT do
zeros[i] = 0
end
local state = {}
for i=1,SC.VOICE_COUNT do
state[i] = {}
state[i].enable = 0
state[i].play = 0
state[i].record = 0
state[i].buffer = (i%2 + 1)
state[i].level =0
state[i].pan = 0
state[i].level_input_cut = {0,0}
state[i].level_cut_cut = zeros
state[i].rate = 1
state[i].loop_start = (i-1)*2
state[i].loop_end = (i-1)*2+1
state[i].loop = 1
state[i].fade_time = 0.0005
state[i].rec_level = 0
state[i].pre_level = 0
state[i].rec = 0
state[i].rec_offset = -0.00015
state[i].position = 0
state[i].pre_filter_fc = 16000
state[i].pre_filter_dry = 0
state[i].pre_filter_lp = 1
state[i].pre_filter_hp = 0
state[i].pre_filter_bp = 0
state[i].pre_filter_br = 0
state[i].pre_filter_fc_mod = 1
state[i].post_filter_fc = 12000
state[i].post_filter_dry = 0
state[i].post_filter_lp = 0
state[i].post_filter_hp = 0
state[i].post_filter_bp = 0
state[i].post_filter_br = 0
state[i].level_slew_time = 0.001
state[i].rate_slew_time = 0.001
state[i].phase_quant = 1
state[i].phase_offset = 0
end
return state
end
--- controlspec factory
-- each table contains an entry for each softcut parameter.
-- each entry is a parameter argument list configured for that voice+param
-- @return an array of tables, one per voice.
function SC.params()
-- @fixme should memoize
local specs = {}
local voice=1
while voice <= SC.VOICE_COUNT do
local spec = {
-- voice enable
enable = { type="number", min=0, max=1, default=0 },
-- levels
-- @fixme: use dB / taper?
level = { type="control", controlspec=controlspec.new(0, 0, 'lin', 0, 0.25, "") },
pan = { type="control", controlspec=controlspec.new(-1, 1, 'lin', 0, 0, "") },
level_input_cut = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0.5, "") },
level_cut_cut = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
-- timing
rate = { type="control", controlspec=controlspec.new(-8, 8, 'lin', 0, 0, "") },
loop_start = { type="control", controlspec=controlspec.new(0, SC.BUFFER_SIZE, 'lin', 0, voice*2.5, "sec") },
loop_end = { type="control", controlspec=controlspec.new(0, SC.BUFFER_SIZE, 'lin', 0, voice*2.5 + 2, "sec") },
loop = { type="number", min=0, max=1, default=1},
fade_time = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
-- recording parameters
rec_level = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
pre_level = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
play = { type="number", min=0, max=1, default=1},
rec = { type="number", min=0, max=1, default=1},
rec_offset = { type="number", min=-100, max=100, default=-8},
-- jump to position
position = { type="control", controlspec=controlspec.new(0, SC.BUFFER_SIZE, 'lin', 0, voice*2.5, "sec") },
-- pre filter
pre_filter_fc = { type="control", controlspec=controlspec.new(10, 12000, 'exp', 1, 12000, "Hz") },
pre_filter_fc_mod = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 1, "") },
pre_filter_rq = { type="control", controlspec=controlspec.new(0.0005, 8.0, 'exp', 0, 2.0, "") },
-- @fixme use dB / taper?
pre_filter_lp = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 1, "") },
pre_filter_hp = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
pre_filter_bp = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
pre_filter_br = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
pre_filter_dry = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
-- post filter
post_filter_fc = { type="control", controlspec=controlspec.new(10, 12000, 'exp', 1, 12000, "Hz") },
post_filter_rq = { type="control", controlspec=controlspec.new(0.0005, 8.0, 'exp', 0, 2.0, "") },
-- @fixme use dB / taper?
post_filter_lp = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 1, "") },
post_filter_hp = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
post_filter_bp = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
post_filter_br = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
post_filter_dry = { type="control", controlspec=controlspec.new(0, 1, 'lin', 0, 0, "") },
-- slew times
level_slew_time = { type="control", controlspec=controlspec.new(0, 8, 'lin', 0, 0, "") },
rate_slew_time = { type="control", controlspec=controlspec.new(0, 8, 'lin', 0, 0, "") },
-- poll quantization unit
phase_quant = { type="control", controlspec=controlspec.new(0, 8, 'lin', 0, 0.125, "") },
}
-- assign name, id and action
for k,v in pairs(spec) do
local z = voice
spec[k].id = k
spec[k].name = "cut"..z..k
local act = SC[k]
if act == nil then
print("warning: didn't find SoftCut voice method: "..k)
end
spec[k].action = function(x) act(z, x) end
end
specs[voice] = spec
voice = voice + 1
end
return specs
end
return SC