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
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
|
<html>
<head>
<script type="text/javascript" src="https://apps.csound.1bpm.net/code/jquery.js"></script>
<style type="text/css">
body {
font-family: Arial, sans-serif;
}
table {
border-collapse: collapse;
}
td {
border: 1px solid black;
}
pre {
font-family: Monospace, Courier, sans-serif;
background-color: #ccffff;
}
#container_overview {
background-color: #ddddff;
}
#container_json {
background-color: #ddffdd;
}
#container_csound {
background-color: #ddffff;
}
#container_examples {
background-color: #ffffdd;
}
</style>
<script type="text/javascript">
var documentation = {};
documentation.opcodes = [
{
name: "twst_param",
ins: [
["Sname", "Name of the parameter to obtain"]
],
outs: [
["kvalue", "Value of the parameter"]
],
description: "Obtain a parameter at k-rate. The name should correspond to the {json(channel)} in the definition for the given transform",
exampleJson: {
name: "Oscillator",
instr: "twst_tf_example_osc",
parameters: [
{name: "Frequency", min: 20, max: 8000, dfault: 440}
]
},
exampleCsound: 'aL, aR, ileft, iright twst_getinput\nkfreq twst_param "frequency"\nif (ileft == 1) then\n\t aL oscil 1, kfreq\nendif\nif (iright == 1) then\n\taR oscil 1, kfreq\nendif\nouts aL, aR'
},
{
name: "twst_parami",
ins: [
["Sname", "Name of the parameter to obtain"]
],
outs: [
["ivalue", "Value of the parameter"]
],
description: "Obtain a parameter at init time. The name should correspond to the {json(channel)} in the definition for the given transform",
exampleJson: {
name: "Oscillator",
instr: "twst_tf_example_osc",
parameters: [
{name: "Frequency", min: 20, max: 8000, dfault: 440}
]
},
exampleCsound: 'ifreq twst_parami "frequency"\naout oscil 1, ifreq\nouts aout, aout'
},
{
name: "twst_tf_isoffline",
outs: [
["ioffline", "Whether processing is offline"]
],
description: "Get a boolean signifying whether the current operation is a commit (1) or audition (0)",
exampleJson: {
name: "Reverse",
instr: "twst_tfi_example_reverse",
parameters: []
},
exampleCsound: 'ileft, iright, istartsamp, iendsamp, idocut, ilength twst_tf_getstate\nifnL, ifnR twst_tfi_getfn\nioffline twst_tf_isoffline\napos linseg (iendsamp - istartsamp) - 1, ilength, 0\nif (ileft == 1) then\n\tif (ioffline == 1) then\n\t\tifntempL ftgentmp 0, 0, -ftlen(ifnL), -2, 0\n\t\ttableicopy ifntempL, ifnL\n\t\taL table3 apos, ifntempL\n\telse\n\t\taL table3 apos, ifnL\n\tendif\nendif\nif (iright == 1) then\n\tif (ioffline == 1) then\n\t\tifntempR ftgentmp 0, 0, -ftlen(ifnR), -2, 0\n\t\ttableicopy ifntempR, ifnR\n\t\taR table3 apos, ifntempR\n\telse\n\t\taR table3 apos, ifnR\n\tendif\nendif\nouts aL, aR'
},
{
name: "twst_getinput",
outs: [
["aL", "Left channel"],
["aR", "Right channel"],
["ileft", "Whether the left channel is to be processed"],
["iright", "Whether the right channel is to be processed"]
],
description: "Get input audio and channel flags for the current transform process. The transform should utilise ileft and iright, which are set to either 0 or 1, to process the inputs accordingly. If the instrument does not process audio (ie, generates new audio), then this opcode can still be used to obtain ileft and iright. This can also be obtained from {csound(twst_tf_getstate)}",
exampleJson: {
name: "Gain",
instr: "twst_tf_example_gain",
parameters: [
{name: "Gain"}
]
},
exampleCsound: 'aL, aR, ileft, iright twst_getinput\nkgain twst_param "gain"\nif (ileft == 1) then\n\taL *= kgain\nendif\nif (iright == 1) then\n\taR *= kgain\nendif\nouts aL, aR'
},
{
name: "twst_getfinput",
ins: [
["fftsize", "FFT size", "{preset(fftsize)}"]
],
outs: [
["fL", "Left channel"],
["fR", "Right channel"],
["aL", "Left channel"],
["aR", "Right channel"],
["ileft", "Whether the left channel is to be processed"],
["iright", "Whether the right channel is to be processed"]
],
description: "Get input audio as a PVS stream along with channel flags for the current transform process. The transform should utilise ileft and iright, which are set to either 0 or 1, to process the inputs accordingly. FFT size is optional and obtains the value from {preset(fftsize)} if not specified. Frequency/phase lock is also applied according to the parameter in the preset group {presetgroup(pvsynth)}",
exampleJson: {
name: "Repitcher",
instr: "twst_tf_example_pvscale",
parameters: [
{name: "Pitch scaling", channel: "scale", min: 0.5, max: 2, dfault: 1},
{presetgroup: "pvanal"},
{presetgroup: "pvsynth"}
]
},
exampleCsound: 'fL, fR, aL, aR, ileft, iright twst_getfinput\nkscale twst_param "scale"\nif (ileft == 1) then\n\tfoutL pvscale fL, kscale\n\taL twst_tf_fresynth foutL\nendif\nif (iright == 1) then\n\tfoutL pvscale fL, kscale\n\taR twst_tf_fresynth foutR\nendif\nouts aL, aR'
},
{
name: "twst_tf_fresynth",
ins: [
["fsig", "Input PVS stream"]
],
outs: [
["aout", "Resynthesised audio"]
],
description: "Resynthesise the PVS stream provided using the values of parameter preset group {presetgroup(pvsynth)}",
exampleJson: {
name: "Repitcher",
instr: "twst_tf_example_pvscale",
parameters: [
{name: "Pitch scaling", channel: "scale", min: 0.5, max: 2, dfault: 1},
{presetgroup: "pvanal"},
{presetgroup: "pvsynth"},
{preset: "applymode"}
]
},
exampleCsound: 'fL, fR, aL, aR, ileft, iright twst_getfinput\nkscale twst_param "scale"\nif (ileft == 1) then\n\tfoutL pvscale fL, kscale\n\taL twst_tf_fresynth foutL\nendif\nif (iright == 1) then\n\tfoutL pvscale fL, kscale\n\taR twst_tf_fresynth foutR\nendif\nouts aL, aR'
},
{
name: "twst_tf_pitchscale",
outs: [
["kpitchscale", "Pitch scaling ratio (1 = normal; 2 = one octave above)"]
],
description: "Obtain the pitch scaling value from the parameter preset group {presetgroup(pitchscale)}",
exampleJson: {
name: "Repitcher",
instr: "twst_tf_example_pvscale",
parameters: [
{presetgroup: "pvanal"},
{presetgroup: "pvsynth"},
{presetgroup: "pitchscale"}
]
},
exampleCsound: 'fL, fR, aL, aR, ileft, iright twst_getfinput\nkscale twst_tf_pitchscale\nif (ileft == 1) then\n\tfoutL pvscale fL, kscale\n\taL twst_tf_fresynth foutL\nendif\nif (iright == 1) then\n\tfoutL pvscale fL, kscale\n\taR twst_tf_fresynth foutR\nendif\nouts aL, aR'
},
{
name: "twst_tf_pitchscale_custom",
ins: [
["SchanPrepend", "String to prepend the preset channel name with"]
],
outs: [
["kpitchscale", "Pitch scaling ratio (1 = normal; 2 = one octave above)"]
],
description: "Obtain the pitch scaling value from the parameter preset group {presetgroup(pitchscale)} but with channel names prepended with the specified string, to be used with the {json(channelprepend)} attribute",
exampleJson: {
name: "Repitcher",
instr: "twst_tf_example_pvscale",
parameters: [
{presetgroup: "pvanal"},
{presetgroup: "pvsynth"},
{presetgroup: "pitchscale", channelprepend: "custom"}
]
},
exampleCsound: 'fL, fR, aL, aR, ileft, iright twst_getfinput\nkscale twst_tf_pitchscale \"custom\"\nif (ileft == 1) then\n\tfoutL pvscale fL, kscale\n\taL twst_tf_fresynth foutL\nendif\nif (iright == 1) then\n\tfoutL pvscale fL, kscale\n\taR twst_tf_fresynth foutR\nendif\nouts aL, aR'
},
{
name: "twst_tf_freq",
outs: [
["kfreq", "Frequency in Hz"]
],
description: "Obtain the frequency value at k-rate from the parameter preset group {presetgroup(notefreq)}",
exampleJson: {
name: "Oscillator",
instr: "twst_tf_example_osc",
parameters: [
{presetgroup: "notefreq"}
]
},
exampleCsound: 'kfreq twst_tf_freq\naout oscil 1, kfreq\nouts aout, aout'
},
{
name: "twst_tf_freqi",
outs: [
["ifreq", "Frequency in Hz"]
],
description: "Obtain the frequency value at init time from the parameter preset group {presetgroup(notefreq)}, which should also have the {json(automatable)} attribute set to false",
exampleJson: {
name: "Oscillator",
instr: "twst_tf_example_osc",
parameters: [
{presetgroup: "notefreq", automatable: false},
{preset: "applymode"}
]
},
exampleCsound: 'ifreq twst_tf_freqi\naout oscil 1, ifreq\nouts aout, aout'
},
{
name: "twst_tf_freq_custom",
ins: [
["SchanPrepend", "String to prepend the preset channel name with"]
],
outs: [
["kfreq", "Frequency in Hz"]
],
description: "Obtain the frequency value at k-rate from the parameter preset group {presetgroup(notefreq)} but with channel names prepended with the specified string, to be used with the {json(channelprepend)} attribute",
exampleJson: {
name: "Oscillator",
instr: "twst_tf_example_osc",
parameters: [
{presetgroup: "notefreq", channelprepend: "custom"}
]
},
exampleCsound: 'kfreq twst_tf_freq_custom "custom"\naout oscil 1, kfreq\nouts aout, aout'
},
{
name: "twst_tf_freqi_custom",
ins: [
["SchanPrepend", "String to prepend the preset channel name with"]
],
outs: [
["ifreq", "Frequency in Hz"]
],
description: "Obtain the frequency value at init time from the parameter preset group {presetgroup(notefreq)} but with channel names prepended with the specified string, to be used with the {json(channelprepend)} attribute",
exampleJson: {
name: "Oscillator",
instr: "twst_tf_example_osc",
parameters: [
{presetgroup: "notefreq", channelprepend: "custom", automatable: false}
]
},
exampleCsound: 'ifreq twst_tf_freqi_custom "custom"\naout oscil 1, ifreq\nouts aout, aout'
},
{
name: "twst_tf_getwaveform",
ins: [
["inumber", "Reference number of the wave", "{preset(wave)}"]
],
outs: [
["ifn", "f-table number"]
],
description: "Obtain an f-table at init time from the parameter preset {preset(wave)}. This can be overridden with {p(inumber)}, a value which corresponds to the array of available waveforms which is [Sine, Saw, Pulse, Triangle]",
exampleJson: {
name: "Oscillator",
instr: "twst_tf_example_osc",
parameters: [
{preset: "wave", automatable: false}
]
},
exampleCsound: 'ifn twst_tf_getwaveform\naout oscil 1, 440, ifn\outs aout, aout'
},
{
name: "twst_tf_getwaveformk",
ins: [
["inumber", "Reference number of the wave", "{preset(wave)}"]
],
outs: [
["kfn", "f-table number"]
],
description: "Obtain an f-table at k-rate from the parameter preset group {preset(wave)}. This can be overridden with inumber, a value which corresponds to the array of available waveforms which is [Sine, Saw, Pulse, Triangle]",
exampleJson: {
name: "Oscillator",
instr: "twst_tf_example_osc",
parameters: [
{preset: "wave"},
{preset: "applymode"}
]
},
exampleCsound: 'kfn twst_tf_getwaveformk\naout oscilikt 1, 440, kfn\outs aout, aout'
},
{
name: "twst_tf_getwintype",
outs: [
["ifn", "f-table number"]
],
description: "Obtain an f-table at init time from the parameter preset group {preset(wintype)}. This may be Hanning, Hamming or Half sine as selected in the UI",
exampleJson: {
name: "Sndwarp",
instr: "twst_tfi_exsndwarp",
parameters: [
{name: "Time scale", channel: "timescale", min: 0.1, max: 10, dfault: 1, automatable: false},
{preset: "wintype", automatable: false}
]
},
exampleCsound: 'ileft, iright, istartsamp, iendsamp, idocut, ilength twst_tf_getstate\nitimescale = twst_parami("timescale")\nifnWindow = twst_tf_getwintype()\np3 = ilength * itimescale\natime linseg 0, p3, ilength\nifnL, ifnR twst_tfi_getfn\nif (ileft == 1) then\n\taL sndwarp 1, atime, 1, ifnL, 0, 4410, 441, 4, ifnWindow, 1\nendif\nif (iright == 1) then\n\taR sndwarp 1, atime, apitchscale, ifnR, 0, 4410, 441, 4, ifnWindow, 1\nendif\nouts aL, aR'
},
{
name: "twst_tf_getwintypek",
outs: [
["kfn", "f-table number"]
],
description: "Obtain an f-table at k-rate from the parameter preset group {preset(wintype)}. This may be Hanning, Hamming or Half sine as selected in the UI",
exampleJson: {
name: "Wintype",
instr: "twst_tf_wintype",
parameters: [
{preset: "wintype"}
]
},
exampleCsound: 'kfnWindow = twst_tf_getwintypek()\naout oscil 1, 440\naenv oscilikt 1, 10, kfnWindow\naout *= aenv\nouts aout, aout'
},
{
name: "twst_getcrossinput",
outs: [
["aL", "Left channel from instance"],
["aR", "Right channel from instance"],
["ileft", "whether the left channel is selected"],
["iright", "whether the right channel is selected"]
],
description: "Obtain an audio stream from the file instance selected in a parameter preset {preset(instance)}. ileft and iright correspond to the selected channels, and the audio is played according to the parameter preset {preset(looptype)}",
exampleJson: {
name: "Multiplier",
instr: "twst_tf_multiplier",
inputs: 2,
parameters: [
{preset: "instance"},
{preset: "instanceloop"}
]
},
exampleCsound: 'aL, aR, ileft, iright twst_getinput\naxL, axR, ixleft, ixright twst_getcrossinput\nif (ileft == 1 && ixleft == 1) then\n\taL *= axL\nendif\nif (iright == 1 && ixright == 1) then\n\taR *= axR\nendif\nouts aL, aR'
},
{
name: "twst_getfcrossinput",
outs: [
["fL", "Left channel from instance"],
["fR", "Right channel from instance"],
["ileft", "whether the left channel is selected"],
["iright", "whether the right channel is selected"]
],
description: "Obtain a PVS stream from the file instance selected in a parameter preset {preset(instance)}. ileft and iright correspond to the selected channels, and the audio is played according to the parameter preset {preset(looptype)}. The FFT size is taken from {preset(fftsize)}",
exampleJson: {
name: "Morpher",
instr: "twst_tf_exmorph",
inputs: 2,
parameters: [
{preset: "instance", name: "Cross instance"},
{preset: "instanceloop"},
{name: "Amplitude amount", channel: "amp", description: "Amplitude interpolation", dfault: 1, min: 0, max: 1},
{name: "Frequency amount", channel: "freq", description: "Frequency interpolation", dfault: 1, min: 0, max: 1},
{presetgroup: "pvanal"},
{presetgroup: "pvsynth"},
{preset: "applymode"}
]
},
exampleCsound: 'fL, fR, aL, aR, ileft, iright twst_getfinput\nfLo, fRo, ilefto, irighto twst_getfcrossinput\nkamp = twst_param:k("amp")\nkfreq = twst_param:k("freq")\n\nif (ileft == 1 && ilefto == 1) then\n\tfoutL pvsmorph fL, fLo, kamp, kfreq\n\taL twst_tf_fresynth foutL\nendif\n\nif (iright == 1 && irighto == 1) then\n\tfoutR pvsmorph fR, fRo, kamp, kfreq\n\taR twst_tf_fresynth foutR\nendif\nouts aL, aR'
},
{
name: "twst_getcrossdata",
outs: [
["ifnL", "f-table containing left channel audio"],
["ifnR", "f-table containing right channel audio"],
["istart", "selection start in samples"],
["ilen", "selection length in samples"],
["ileft", "whether the left channel is selected"],
["iright", "whether the right channel is selected"]
],
description: "Obtain data from the file instance selected in a parameter preset {preset(instance)}. The f-tables are the complete file, and the start and length correspond to the selected area. ileft and iright also correspond to the selected channels",
exampleJson: {
name: "Multiplier",
instr: "twst_tf_multiplier",
inputs: 2,
parameters: [
{preset: "instance"},
{preset: "instanceloop"}
]
},
exampleCsound: 'ixfnL, ixfnR, ixstart, ixlen, ixleft, ixright twst_getcrossdata\nprint ixlen\naL, aR, ileft, iright twst_getinput\naxL, axR, ixleft, ixright twst_getcrossinput\nif (ileft == 1 && ixleft == 1) then\n\taL *= axL\nendif\nif (iright == 1 && ixright == 1) then\n\taR *= axR\nendif\nouts aL, aR'
},
{
name: "twst_tfi_getcrossfn",
outs: [
["ifnL", "f-table containing left channel audio"],
["ifnR", "f-table containing right channel audio"],
],
description: "Obtain an f-table from the file instance selected in a parameter preset {preset(instance)}, which has been sliced from the original file, based on the selection area. This may be necessary for some opcodes that read from a f-table, such as those that have no control over the read point, when used for convolution or cross synthesis",
exampleJson: {
name: "Convolve",
instr: "twst_tfi_exdconv",
inputs: 2,
parameters: [
{preset: "instance"},
{name: "Size ratio", min: 0.00001, max: 1, dfault: 0.1, lagHint: -1, channel: "sizeratio", automatable: false},
{presetgroup: "applymode"}
]
},
exampleCsound: 'aL, aR, ileft, iright twst_getinput \nifnLo, ifnRo twst_tfi_getcrossfn\nisizeratio = twst_parami("sizeratio")\nif (ileft == 1) then\n\taL dconv aL, isizeratio * ftlen(ifnLo), ifnLo\nendif\nif (iright == 1) then\n\taR dconv aR, isizeratio * ftlen(ifnRo), ifnRo\nendif\nouts aL, aR'
},
{
name: "twst_tfi_getfn",
outs: [
["ifnL", "f-table containing left channel audio"],
["ifnR", "f-table containing right channel audio"],
],
description: "Obtain f-tables which have been sliced from the original file, based on the selection area. This may be necessary for some opcodes that read from a f-table, such as those that have no control over the read point",
exampleJson: {
name: "Sndwarp",
instr: "twst_tfi_exsndwarp",
parameters: [
{name: "Time scale", channel: "timescale", min: 0.1, max: 10, dfault: 1, automatable: false},
{preset: "wintype", automatable: false}
]
},
exampleCsound: 'ileft, iright, istartsamp, iendsamp, idocut, ilength twst_tf_getstate\nitimescale = twst_parami("timescale")\nifnWindow = twst_tf_getwintype()\np3 = ilength * itimescale\natime linseg 0, p3, ilength\nifnL, ifnR twst_tfi_getfn\nif (ileft == 1) then\n\taL sndwarp 1, atime, 1, ifnL, 0, 4410, 441, 4, ifnWindow, 1\nendif\nif (iright == 1) then\n\taR sndwarp 1, atime, apitchscale, ifnR, 0, 4410, 441, 4, ifnWindow, 1\nendif\nouts aL, aR'
},
{
name: "twst_setlatencysamples",
ins: [
["isamples", "Number of samples latency"]
],
description: "Set the expected processing latency in samples. This is then used to offset any writing and output accordingly. This should ideally be used for any buffered or windowed operations. However, {csound(twst_getfinput)} already sets this based on the FFT size",
exampleJson: {
name: "Hilbert pitch scale",
instr: "twst_tf_exhilbertpitchscale",
parameters: [
{presetgroup: "pitchscale"},
{preset: "fftsize"},
{preset: "applymode"}
]
},
exampleCsound: 'aL, aR, ileft, iright twst_getinput\nifftsize = twst_parami("fftsize")\nkscale = twst_tf_pitchscale()\ntwst_setlatencysamples(ifftsize)\n\nif (ileft == 1) then\n\tahL1, ahL2 hilbert2 aL, ifftsize, ifftsize / 4\n\tamL, afmL fmanal ahL1, ahL2\n\taLx oscil amL, afmL * kscale\nendif\nif (iright == 1) then\n\tahR1, ahR2 hilbert2 aR, ifftsize, ifftsize / 4\n\tamR, afmR fmanal ahR1, ahR2\n\taRx oscil amR, afmR * kscale\nendif\nouts aLx, aRx'
},
{
name: "twst_setlatencyseconds",
ins: [
["iseconds", "Number of seconds latency"]
],
description: "Set the expected processing latency in seconds. This is then used to offset any writing and output accordingly. This should ideally be used for any buffered or windowed operations. However, {csound(twst_getfinput)} already sets this based on the FFT size",
exampleJson: {
name: "Hilbert pitch scale",
instr: "twst_tf_exhilbertpitchscale",
parameters: [
{presetgroup: "pitchscale"},
{preset: "fftsize"},
{preset: "applymode"}
]
},
exampleCsound: 'aL, aR, ileft, iright twst_getinput\nifftsize = twst_parami("fftsize")\nkscale = twst_tf_pitchscale()\ntwst_setlatencyseconds(1 / ifftsize)\n\nif (ileft == 1) then\n\tahL1, ahL2 hilbert2 aL, ifftsize, ifftsize / 4\n\tamL, afmL fmanal ahL1, ahL2\n\taLx oscil amL, afmL * kscale\nendif\nif (iright == 1) then\n\tahR1, ahR2 hilbert2 aR, ifftsize, ifftsize / 4\n\tamR, afmR fmanal ahR1, ahR2\n\taRx oscil amR, afmR * kscale\nendif\nouts aLx, aRx'
},
{
name: "twst_getlatencyseconds",
outs: [
["iseconds", "Number of seconds latency"]
],
description: "Obtain the expected processing latency in seconds, as previously set by {csound(twst_setlatencysamples)}, {csound(twst_setlatencyseconds)} or {csound(twst_getfinput)}",
exampleJson: {
name: "Hilbert pitch scale",
instr: "twst_tf_exhilbertpitchscale",
parameters: [
{presetgroup: "pitchscale"},
{preset: "fftsize"},
{preset: "applymode"}
]
},
exampleCsound: 'aL, aR, ileft, iright twst_getinput\nifftsize = twst_parami("fftsize")\nkscale = twst_tf_pitchscale()\ntwst_setlatencysamples(ifftsize)\nprint twst_getlatencyseconds()\nif (ileft == 1) then\n\tahL1, ahL2 hilbert2 aL, ifftsize, ifftsize / 4\n\tamL, afmL fmanal ahL1, ahL2\n\taLx oscil amL, afmL * kscale\nendif\nif (iright == 1) then\n\tahR1, ahR2 hilbert2 aR, ifftsize, ifftsize / 4\n\tamR, afmR fmanal ahR1, ahR2\n\taRx oscil amR, afmR * kscale\nendif\nouts aLx, aRx'
},
{
name: "twst_tf_setplayposition",
ins: [
["kposition", "Playback position ratio"]
],
description: "Override the displayed playback position. The supplied kposition must be between 0 and 1, and corresponds to the selected area being played or auditioned. Normal operation would be equivalent to line(0, p3, 1)",
exampleJson: {
name: "Direct time sndwarp",
instr: "twst_tfi_directsndwarp",
parameters: [
{name: "Read time", channel: "readtime"},
{preset: "wintype", automatable: false}
]
},
exampleCsound: 'ileft, iright, istartsamp, iendsamp, idocut, ilength twst_tf_getstate\nktime = twst_param:k("readtime")\ntwst_tf_setplayposition ktime\natime = a(ktime * ilength)\nifnWindow = twst_tf_getwintype()\nifnL, ifnR twst_tfi_getfn\nif (ileft == 1) then\n\taL sndwarp 1, atime, 1, ifnL, 0, 4410, 441, 4, ifnWindow, 1\nendif\nif (iright == 1) then\n\taR sndwarp 1, atime, apitchscale, ifnR, 0, 4410, 441, 4, ifnWindow, 1\nendif\nouts aL, aR'
},
{
name: "twst_tf_getstate",
outs: [
["ileft", "Whether the left channel is selected"],
["iright", "Whether the right channel is selected"],
["istart", "Start sample number of selection"],
["iend", "End sample number of selection"],
["ilens", "Length of selection in seconds"]
],
description: "Obtain details about the current selection. If the source is mono, only ileft will be equal to 1",
exampleJson: {
name: "Direct time sndwarp",
instr: "twst_tfi_directsndwarp",
parameters: [
{name: "Read time", channel: "readtime"},
{preset: "wintype", automatable: false}
]
},
exampleCsound: 'ileft, iright, istartsamp, iendsamp, idocut, ilength twst_tf_getstate\nktime = twst_param:k("readtime")\ntwst_tf_setplayposition ktime\natime = a(ktime * ilength)\nifnWindow = twst_tf_getwintype()\nifnL, ifnR twst_tfi_getfn\nif (ileft == 1) then\n\taL sndwarp 1, atime, 1, ifnL, 0, 4410, 441, 4, ifnWindow, 1\nendif\nif (iright == 1) then\n\taR sndwarp 1, atime, apitchscale, ifnR, 0, 4410, 441, 4, ifnWindow, 1\nendif\nouts aL, aR'
}
];
function linkage(text) {
var re = /{(preset|presetgroup|json|csound)\((.*?)\)}/g;
do {
match = re.exec(text);
if (match) {
text = text.replaceAll(match[0], "<a href=\"#" + match[1] + "_" + match[2] + "\">" + match[2] + "</a>");
}
} while (match);
return text;
}
function buildOpcodes() {
var elTarget = $("#opcodes");
for (let index in documentation.opcodes) {
let o = documentation.opcodes[index];
var html = ""
if (o.outs) {
var names = [];
for (var x of o.outs) names.push(x[0]);
html += "<i>" + names.join(", ") + "</i> "
}
html += "<b>" + o.name + "</b>";
if (o.ins) {
var names = [];
for (var x of o.ins) {
if (x[2]) { // has default
names.push("[" + x[0] + "=" + x[2] + "]");
} else {
names.push(x[0]);
}
}
html += " <i>" + names.join(", ") + "</i>"
}
$("<p />").html(linkage(html)).attr("id", "csound_" + o.name).appendTo(elTarget);
if (o.ins || o.outs) {
var ul = $("<ul />").appendTo(elTarget);
if (o.outs) {
for (var x of o.outs) {
var html = "<i>" + x[0] + "</i> " + x[1];
$("<li />").html(linkage(html)).appendTo(ul);
}
}
if (o.ins) {
for (var x of o.ins) {
var html = "<i>" + x[0] + "</i> " + x[1];
if (x[2]) html += " <i>[default: " + x[2] + "</i>]"
$("<li />").html(linkage(html)).appendTo(ul);
}
}
}
elTarget.append($("<p />").html(linkage(o.description)));
if (o.exampleCsound && o.exampleJson) {
var cs = "instr " + o.exampleJson.instr + "\n\t$TWST_TRANSFORM\n"
cs += o.exampleCsound.replace(/^/gm, "\t");
cs += "\nendin"
var exampleCsound = $("<pre />").hide().text(cs).attr("id", "csex_" + index);
let exampleCsoundShown = false;
$("<a />").attr("href", "#").text("Csound example").click(function(e) {
e.preventDefault();
if (!exampleCsoundShown) {
$("#csex_" + index).show();
exampleCsoundShown = true;
} else {
$("#csex_" + index).hide();
exampleCsoundShown = false;
}
}).appendTo($("<p />").appendTo(elTarget));
exampleCsound.appendTo(elTarget);
var exampleJson = $("<pre />").hide().text(JSON.stringify(o.exampleJson, null, "\t")).attr("id", "jsex_" + index);
let exampleJsonShown = false;
$("<a />").attr("href", "#").text("Corresponding definition example").click(function(e) {
e.preventDefault();
if (!exampleJsonShown) {
$("#jsex_" + index).show();
exampleJsonShown = true;
} else {
$("#jsex_" + index).hide();
exampleJsonShown = false;
}
}).appendTo($("<p />").appendTo(elTarget));
exampleJson.appendTo(elTarget);
}
elTarget.append($("<hr />"));
}
}
$(function() {
buildOpcodes();
});
</script>
</head>
<body>
<h1>Extending twist</h1>
<div id="container_overview">
<h2>Overview</h2>
Twist can quite easily be extended to feature additional transforms. These can be tested and used on the fly with the <i>developer console</i> within twist, and are encouraged to be submitted for inclusion in the live application which can be done <a href="https://csound.1bpm.net/contact/?type=twist_submit">here</a>, via the <i>Help > Submit transform code</i> menu option, or via the link in the <i>developer console</i><br /><br />
In order to write new transforms for twist, familiarity with JSON and Csound is required. The UI components including parameters available to the end user are defined with JSON (detailed in the <a href="#json">transform definition</a> section, and the actual audio processing is defined with Csound code (detailed in the <a href="#csound">audio processing with Csound</a> section. Additional twist opcodes are provided to the developer in order to ease integration.<br />
Each transform requires a transform definition as a JSON object, and at least one Csound instrument. While each section describes the API to be used, full examples are provided in the <a href="#opcodes">Csound opcode</a> subsection.
</div><div id="container_json">
<h2 id="json">Transform definition with JSON</h2>
The transform definition is a JSON object, which should at the very least have the keys <a href="#json_name">name</a> and <a href="#json_instr">instr</a> defined. The possible keys for the top-level transform definition are as follows, and if a default value is applicable it is shown to the right of the equals sign in the name column:
<table><tbody>
<tr><td><h5 id="json_name">name</h5></td><td>Name as seen in the twist user interface</td></tr>
<tr><td><h5 id="json_instr">instr</h5></td><td>Csound named instrument, which is to be called to carry out the processing</td></tr>
<tr><td><h5>inputs = 1</h5></td><td>Number of input files the transform requires. This defaults to 1 but may be set to 2 for cross-processing transforms and such</td></tr>
<tr><td><h5>description = ""</h5></td><td>Description of the transform</td></tr>
<tr><td><h5>author = ""</h5></td><td>Author name and other relevant details</td></tr>
<tr><td><h5>parameters = []</h5></td><td><a href="#parameter">Parameter definitions</a> in an array</td></tr>
<tr><td><h5>twine = false</h5></td><td>Whether the transform will be available as a twine insert. Only <a href="#csound_rule_tfi">transforms using live input</a> can be used for this purpose</td></tr>
<tr><td><h5>unstable = false</h5></td><td>Should be set to true if the transform is expected to be unstable and may cause crashes. This will result in a warning displayed when the transform is loaded</td></tr>
</tbody></table>
<h4 id="parameter">Parameter definition</h4>
The parameter definition is a JSON object, which may have any of the following keys. If min == 0, max == 1 and step == 1, the parameter appears as a checkbox which provides the value 1 when checked and 0 when unchecked. If <a href="#parameter_options">options</a> is supplied, then the parameter appears as a drop-down select box. In other cases the parameter is displayed as a range slider with an adjacent number input box.<br /><br />
The only required key for a parameter definition is <a href="#parameter_name">name</a>. In this case, a parameter would be with a range of 0 to 1, with the default step amount and sending on the channel with the lowercase equivalent of the name.<br /><br />
The possible keys for a parameter definition are as follows, and if a default value is applicable it is shown to the right of the equals sign in the name column:
<table><tbody>
<tr><td><h5 id="parameter_name">name</h5></td><td>Name of the parameter to be shown in the interface</td></tr>
<tr><td><h5>description = ""</h5></td><td>Description of the parameter</td></tr>
<tr><td><h5 id="parameter_channel">channel = name.toLowerCase()</h5></td><td>Channel name which should correspond to that which is requested by <a href="#csound_twst_param">twst_param</a> in the <a href="#json_instr">transform instrument</a>. Defaults to the lowercase parameter name</td></tr>
<tr><td><h5>min = 0</h5></td><td>Numeric minimum accepted value</td></tr>
<tr><td><h5>max = 1</h5></td><td>Numeric maximum accepted value</td></tr>
<tr><td><h5>step = 0.0000001</h5></td><td>Incremental allowance of the value, should numeric</td></tr>
<tr><td><h5 id="parameter_dfault">dfault = 1</h5></td><td>Numeric default value</td></tr>
<tr><td><h5 id="parameter_options">options = null</h5></td><td>Array containing options to be displayed in a drop-down select box. If supplied, the minimum, maximum and step values are redundant. <a href="#parameter_dfault">dfault</a> corresponds to the index of the array to be the default value. If <a href="#parameter_asvalue">asvalue</a> is set, then the value supplied to Csound will be the value provided in the options array; otherwise it will be the index of the value</td></tr>
<tr><td><h5 id="parameter_asvalue">asvalue = false</h5></td><td>Whether the selected item from <a href="#parameter_options">options</a> should be provided to Csound as the actual value rather than the array index</td></tr>
<tr><td><h5>hidden = false</h5></td><td>Whether the parameter should be hidden. May be useful passing static data from the interface to Csound</td></tr>
<tr><td><h5>conditions = null</h5></td><td>An array of <a href="#condition">Condition</a> objects which are all to be met for the parameter to be shown</td></tr>
<tr><td><h5>hostrange = false</h5></td><td>For child parameters (namely those in modulations), whether the min, max, step, dfault, options and asvalue attributes should be inherited from the parent</td></tr>
<tr><td><h5 id="parameter_preset">preset = null</h5></td><td>The name of a <a href="#preset">preset</a> to be used. Any definition attributes provided by the preset may be overriden</td></tr>
<tr><td><h5 id="parameter_presetgroup">presetgroup = null</h5></td><td>The name of a <a href="#presetgroup">presetgroup</a> to be used, which will provide a number of parameters in place of the current definition
<tr><td><h5>nameprepend = null</h5></td><td>The string which will be prepended to parameter names, if presetgroup is specified
<tr><td><h5>channelprepend = null</h5></td><td>The string which will be prepended to parameter channels, if presetgroup is specified
</tbody></table>
<hr />
<h4 id="condition">Condition</h4>
The condition definition is a JSON object, which should include all of the following keys:
<table><tbody>
<tr><td><h5>channel</h5></td><td>Parameter <a href="#parameter_channel">channel</a> to evaluate</td></tr>
<tr><td><h5>operator</h5></td><td>Operator type, which may be eq (equal), neq (not equal), lt (less than), gt (greater than), le (less than or equal to) or ge (greater than or equal to)</td></tr>
<tr><td><h5>value</h5></td><td>Static value to check against the above</td></tr>
</tbody></table>
<hr />
<h4 id="preset">Presets</h4>
These are available as values to specify in the <a href="#parameter_presetgroup">presetgroup</a> parameter attribute and alter set the parameter up as follows
<table><tbody>
<tr><td><h5>amp</h5></td><td>Amplitude slider with min: 0 and max: 1, channel: "amp"</td></tr>
<tr><td><h5 id="preset_fftsize">fftsize</h5></td><td>FFT size drop down which may be transparently utilised by <a href="#csound_twst_getfinput">twst_getfinput</a> and <a href="#csound_twst_getfcrossinput">twst_getfcrossinput</a>, or accessed directly via the channel "fftsize" or the specified channel name with <a href="#csound_twst_param">twst_param</a></td></tr>
<tr><td><h5 id="preset_wave">wave</h5></td><td>f-table selector which may be transparently utilised by <a href="#csound_twst_tf_getwaveform">twst_tf_getwaveform</a>, <a href="#csound_twst_tf_getwaveformk">twst_tf_getwaveformk</a>, or accessed directly via the channel "wave" or the specified channel name with <a href="#csound_twst_param">twst_param</a> or <a href="#csound_twst_paramk">twst_paramk</a></td></tr>
<tr><td><h5>applymode</h5></td><td>Apply mode drop down, which may be Replace, Mix, Modulate or Demodulate. Used internally by twist at the rendering stage</td></tr>
<tr><td><h5>note</h5></td><td>MIDI note number drop-down, displaying note names between MIDI note number 21 (A0) and 127 (G#9) and returning the MIDI note number to the channel</td></tr>
<tr><td><h5 id="preset_wintype">wintype</h5></td><td>Window type drop-down which may be utilised by <a href="#csound_twst_tf_getwintype">twst_tf_getwintype</a>, <a href="#csound_twst_tf_getwintypek">twst_tf_getwintypek</a> or accessed directly via the channel "wintype" or the specified channel name with <a href="#csound_twst_param">twst_param</a> or <a href="#csound_twst_paramk">twst_paramk</a></td></tr>
<tr><td><h5 id="preset_instance">instance</h5></td><td>Drop down selecting a file open in twist, other than that which is currently open. Utilised interally by <a href="#csound_twst_getcrossinput">twst_getcrossinput</a> and <a href="#csound_twst_getfcrossinput">twst_getfcrossinput</a></td></tr>
<tr><td><h5 id="preset_instanceloop">instanceloop</h5></td><td>Drop down selecting either None, Forward, Backward or Ping-pong to denote the loop type of the other selected instance, used internally for cross-processing transforms within <a href="#csound_twst_getcrossinput">twst_getcrossinput</a>, <a href="#csound_twst_getfcrossinput">twst_getfcrossinput</a> and <a href="#csound_twst_getfcrossdata">twst_getfcrossdata</a></td></tr>
</tbody></table>
<hr />
<h4 id="presetgroup">Preset groups</h4>
These are available as values to specify in the <a href="#parameter_presetgroup">presetgroup</a> parameter attribute.
<table><tbody>
<tr><td><h5>pvanal</h5></td><td>Provides <a href="#preset_fftsize">FFT size</a> and a frequency/phase locking checkbox, used internally in the provision of PVS stream data within <a href="#csound_twst_getfinput">twst_getfinput</a> and <a href="#csound_twst_getfcrossinput">twst_getfcrossinput</a></td></tr>
<tr><td><h5>pvresmode</h5></td><td>Provides parameters which control the resynthesis approach as used by <a href="#csound_twst_tf_fresynth">twst_tf_fresynth</a>. A drop down permits selection between overlap-add and additive approaches, with the latter showing several further parameters when selected</td></tr>
<tr><td><h5 id="presetgroup_pitchscale">pitchscale</h5></td><td>Provides a scaling mode drop down with semitones or ratio as options. The selected scaling is presented via <a href="#csound_twst_tf_pitchscale">twst_tf_pitchscale</a> as a ratio</td></tr>
<tr><td><h5 id="presetgroup_notefreq">notefreq</h5></td><td>Shows an option of selecting a note name from a drop down, or specifying the frequency in Hz. The computed frequency is provided to Csound via <a href="#csound_twst_tf_freq">twst_tf_freq</a> and <a href="#csound_twst_tf_freqi">twst_tf_freqi</a></td></tr>
</tbody></table>
</div><div id="container_csound">
<h2 id="csound">Audio processing with Csound</h2>
Audio processing is carried out for the corresponding JSON transform definition by invoking the Csound instrument specified in the <a href="#json_instr">instr</a> key. Multiple Csound instruments and opcodes may be utilised, however it should be noted that the Csound instrument is called using <i>subinstr</i>, and offline/commit processing is carried out using audio rate processing within a k-rate loop. The only known limitation this imposes is that additional/auxilliary instruments may not usually be called from the initial instrument in a way that would affect synchronisation of the offline processing aspect - ie, opcodes such as <i>schedule</i> and <i>event</i> should not be used except in careful circumstances where the synchronisation is respected - for example where the scheduled instrument only complete init time processing, or completes in a single k-cycle. However, auxilliary instruments may be called using <i>subinstr</i>.<br /><br />
Instruments can generate audio, utilise direct feed of audio, or access table data directly. The latter is useful if the output should be a different duration to the input. <br />
A number of opcodes to ease integration with the UI and transform definition are provided by twist, detailed below with examples in the <a href="#opcodes">Opcode reference</a> subsection.
<h3>Rules and style guide</h3>
The rules and style guide should be adhered to where appropriate, especially if opcodes are to be submitted for inclusion in the live application.
<ul>
<li id="csound_rule_tfi">Instruments referenced by the transform definition should be named prepended with <i>twst_tf_</i> if they generate audio or use direct input (obtained with <a href="#csound_twst_getinput">twst_getinput</a> or <a href="#csound_twst_getfinput">twst_getfinput</a>) - or prepended with <i>twst_tfi_</i> if table access is to be used (with <a href="#csound_twst_tfi_getfn">twst_tfi_getfn</a></li>
<li>The first line of the instrument must be <i>$TWST_TRANSFORM</i> to mark it as a twist transform</li>
<li>Auxilliary instruments and user-defined opcodes should be named prepended with the name of the initial instrument</li>
<li>Instruments may generate audio or process audio input obtained from calls to <a href="#csound_twst_getinput">twst_getinput</a>, <a href="#csound_twst_getfinput">twst_getfinput</a> or <a href="#csound_twst_tfi_getfn">twst_tfi_getfn</a></li>
<li>Instruments must emit stereo audio signals using the <i>outs</i> opcode. Depending on the processing action, either of the outputs may be a silent signal or the same as the input</li>
<li>Instruments should be prepared to process or generate left and right channels according to the <i>ileft</i> and <i>iright</i> values from the call to <a href="#csound_twst_getinput">twst_getinput</a>, <a href="#csound_twst_getfinput">twst_getfinput</a> or <a href="#csound_twst_tf_getstate">twst_tf_getstate</a>. A channel not applicable to the request must still be emitted, but may be a silent signal or the same as the input - the audition/commit process will only use the output for the channel requested by the user in the UI</li>
<li>Any global objects created must not persist after the instrument has finished. For example, <i>ftgentmp</i> must be used rather than <i>ftgen</i> - unless <i>ftfree</i> is used on the f-table accordingly</li>
<li><i>print</i> opcodes and other console output opcodes should not be used except for debugging purposes</li>
<li>0dbfs is set at 1, so anything reliant on this should adhere accordingly</li>
<li>Opcodes are limited to those available in the Csound WASM build - this is generally everything, but one noted example of an exclusion is <i>fractalnoise</i>. If otherwise unexplainable errors are encountered, this may be due to an unavailable opcode</li>
</ul>
<h3>Opcode reference</h3>
<div id="opcodes"></div>
</div><div id="container_examples">
<h2>Examples from the live application</h2>
All of the JSON transform definitions <a href="../twirl/appdata.js">can be seen here</a>, under the <i>transforms</i> key.<br />
Csound code in the live application is split across several files which are as follows. Each file generally corresponds to the section in the JSON.
<ul>
<li><a href="/udo/twist/transforms/amplitude.udo">amplitude</a></li>
<li><a href="/udo/twist/transforms/cross_processing.udo">cross_processing</a></li>
<li><a href="/udo/twist/transforms/delay.udo">delay</a></li>
<li><a href="/udo/twist/transforms/filter.udo">filter</a></li>
<li><a href="/udo/twist/transforms/frequency.udo">frequency</a></li>
<li><a href="/udo/twist/transforms/amplitude.udo">amplitude</a></li>
<li><a href="/udo/twist/transforms/general.udo">general</a></li>
<li><a href="/udo/twist/transforms/generate.udo">generate</a></li>
<li><a href="/udo/twist/transforms/granular.udo">granular</a></li>
<li><a href="/udo/twist/transforms/harmonic.udo">harmonic</a></li>
<li><a href="/udo/twist/transforms/reverb.udo">reverb</a></li>
<li><a href="/udo/twist/transforms/spectral.udo">spectral</a></li>
<li><a href="/udo/twist/transforms/warping.udo">warping</a></li>
</ul>
</body>
</html>
|