-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patha_granularlooper.instr
More file actions
1706 lines (1551 loc) · 59.9 KB
/
a_granularlooper.instr
File metadata and controls
1706 lines (1551 loc) · 59.9 KB
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
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
nebconfigbegin
ksmps,64
-B,2048
-b,512
sr,48000
freeze,momentary,state
reset,triggered,rising
source,latching,falling
file,incremental,falling
record,latching,rising
blend_alt,0.5
window_alt,0
overlap_alt,0
loopsize_alt,0
density_alt,0
nebconfigend
;previous settings:
;ksmps,128
;-B,2048
;-b,128
;- Granular File/Buffer Looping Granular Instrument
; Qu-bit Electronix
; Author: Stephen Hensley
; San Clemente, CA
; 2017
;
; Global Data from External Software
; All of the following globals are set from external software.
; Controls are named after their hardware control name.
; gilen[] - Array containing all file lengths
; gkpitch - percentage of original pitch. (Negative values allowed)
; gkspeed - percentage of original speed. (Negative values allowed)
; gkloopstart - percentage of file to start at.
; gkloopsize - percentage of post-loopstart size file to play
; gkdensity -
; gkoverlap -
; gkblend - percentage of granular sound (inverse percentage of dry audio file)
; gkwindow - percentage of degradation of audio output signal
; gkfilesel - index of table containing audio file data.
; gkfreeze - binary freeze state value (0 or 1)
; gknext - trigger input for advancing files
; gkreset - trigger input for restarting phase to loopstart point.
; gksource - toggles between live input source/and usb file sources
; gkrecord - toggles record behavior
; gkfilestate - state of the record button, independent of latching state and gate input.
; gksourcegate - separate state for the source gate input. used to repurpose separately from source control.
; gkeol - end of sample 1 or 0 essentially high while the sample is resetting to 0.
; gkbufferlength - size of buffer
;;;;;;;;;;;;;;;;;
;;;; UDOs ;;;;;;;
;;;;;;;;;;;;;;;;;
; The Following UDOs are based on those in the Csound FLOSS Manual on recording and playing buffers
giMaxRecordBuffer = 300; Max record time in seconds
;giftEmpty ftgen 0, 0, giMaxRecordBuffer * sr, 7, 0
giftEmpty ftgen 0, 0, 16, 7, 0
giftLA ftgen 0, 0, giMaxRecordBuffer * sr, 7, 0
giftRA ftgen 0, 0, giMaxRecordBuffer * sr, 7, 0
giftLB ftgen 200, 0, giMaxRecordBuffer * sr, 7, 0
giftRB ftgen 201, 0, giMaxRecordBuffer * sr, 7, 0
opcode CopyPartialBuffer, k, kkk
krecstart, kbuffsize, ktrig xin
kchannel init 0
knew changed ktrig
if knew == 1 then
if ktrig == 1 then
kchannel = 0
kcopying = 1
endif
if kcopying == 1 then
if kchannel == 0 then
;tablemix giftLB, krecstart, kbuffsize, giftLA, krecstart, 1.0, giftEmpty, 0, 0.0
;tablecopy giftLB, giftLA
kchannel += 1
else
;tablemix giftRB, krecstart, kbuffsize, giftRA, krecstart, 1.0, giftEmpty, 0, 0.0
;tablecopy giftRB, giftRA
kcopying = 0
endif
endif
endif
xout kcopying
endop
opcode CplxBufRec1, kkk, aaiikkkkkk
ain1, ain2, ifta, iftb, krec, kstart, kend, kpos, kwrap, kmode xin
setksmps 1
kdisablecount init 0
kcopying init 0
ktmp init 0
kndx init 0
kcopying init 0
koverlapping init 0
kactivebuffer init 0
kmaxdelay = 12000; maybe a bit excessive.
kmaxfade = 1200
knew changed krec ; 1 if record just started
if knew == 1 then
if krec == 1 then
krec_delayed = 1
kstartsmps = kstart * sr - 1
kwrapsmps = kpos * sr - 1
kendsmps = kend * sr
kfinished = 0
ktmp = 0
kcoyping = 0
koverlap = 0
else
kfinished = 1
kreclength = kndx
kdisablecount = kmaxdelay
koverlapping = 1
endif
endif
if kwrap == 1 then
kwrapsmps = kpos * sr - 1
kendsmps = kend * sr
kstartsmps = kstart * sr - 1
endif
kendsmps = (kendsmps == 0 || kendsmps > ftlen(ifta) ? ftlen(ifta) : kendsmps)
if krec == 1 then
if knew == 1 && krec == 1 then
kndx = kstartsmps
kfade = 0
kcpndx = -1
endif
if kndx >= kendsmps - 1 && kwrap == 1 then
kndx = kwrapsmps
kreclength = kndx
event "i", 2, 0, -1, kwrapsmps + 1, kendsmps
koverlap = 1
;kstate CopyPartialBuffer, kwrapsmps + 1, kendsmps, 1
;tablemix giftLB, kwrapsmps + 1, kendsmps, giftLA, kwrapsmps + 1, 1.0, giftEmpty, 0, 0.0
;tablemix giftRB, kwrapsmps + 1, kendsmps, giftRA, kwrapsmps + 1, 1.0, giftEmpty, 0, 0.0
endif
if kndx < kendsmps-1 then
if kndx > kwrapsmps + (ksmps * 4) then
koverlap = 0
endif
kndx = kndx + 1
andx = kndx
asig = ain1 + ain2
tabw asig, andx, ifta
;aprevidx = kndx > 0 ? kndx - 1 : kendsmps
;tabw asig, andx, iftb
;aprev tab aprevidx, ifta
;tabw asig, aprevidx, iftb
elseif kndx >= kendsmps -1 then
kfinished = 1
koverlapping = 1
kreclength = kndx
endif
endif
if koverlapping == 1 then
if kreclength + ktmp < (giMaxRecordBuffer * sr) - 1 then
if ktmp < kmaxdelay then
ktmp = ktmp + 1
atmp = a(ktmp)
tabw ain1 + ain2, kreclength + atmp, iftb
tabw ain1 + ain2, kreclength + atmp, ifta
endif
if ktmp >= kmaxdelay then
koverlap = 1
koverlapping = 0
ktmp = 0
kcopying = 0
kcpoff = 0
endif
else
;koverlap = 1
koverlapping = 0
ktmp = 0
endif
endif
xout kfinished, kreclength, koverlap
endop
opcode CplxBufRec2, kkk, aaaaiiiikkkkkk
kcp init 0
ain1L, ain1R, ain2L, ain2R, iftLA, iftRA, iftLB, iftRB, krec, kstart, kend, kpos, kwrap, kmode xin
kfin, kreclen, koverlap CplxBufRec1 ain1L, ain2L, iftLA, iftLB, krec, kstart, kend, kpos, kwrap, kmode
kfin, kreclen, koverlap CplxBufRec1 ain1R, ain2R, iftRA, iftRB, krec, kstart, kend, kpos, kwrap, kmode
xout kfin, kreclen, koverlap
endop
;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Global Tables ;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;
;giwindowtablesize = 1024
giwindowtablesize = 512
giHamming ftgen 0, 0, giwindowtablesize, 20, 9, 1
giBartlett ftgen 0, 0, giwindowtablesize, 20, 3
giBlkHarris ftgen 0, 0, giwindowtablesize, 20, 5
giGaussian ftgen 0, 0, giwindowtablesize, 20, 6
;giRectangle ftgen 0, 0, giwindowtablesize, 7, 0, 886, 0, 138, 1, 6144, 1, 138, 0
;giRectangle ftgen 0, 0, giwindowtablesize, 7, 0, 1024, 0, 8, 1, 4095, 1, 8, 0, 1024, 0
;giRectangle ftgen 0, 0, giwindowtablesize, 7, 0, 1024, 0, 1, 1, 6143, 1, 1, 0, 1024, 0
;giRectangle ftgen 0, 0, giwindowtablesize, 16, 1, giwindowtablesize, 0, 1
giRectangle ftgen 0, 0, giwindowtablesize, 20, 8, 1
;giRampUp ftgen 0, 0, giwindowtablesize, 7, 0, 7992, 1, 200, 0
;giRampDown ftgen 0, 0, giwindowtablesize, 7, 0, 200, 1, 7992, 0
giRampUp ftgen 0, 0, giwindowtablesize, 7, 0, 15.0 * (giwindowtablesize / 16.0), 1, (giwindowtablesize / 16.0), 0
giRampDown ftgen 0, 0, giwindowtablesize, 7, 0,(giwindowtablesize / 16.0),1 , 15.0 * (giwindowtablesize / 16.0), 0
giWin ftgen 0, 0, giwindowtablesize, 20, 9, 1 ; Hamming Window
giWinB ftgen 0, 0, giwindowtablesize, 20, 9, 1 ; Hamming Window
giWinMix ftgen 0, 0, giwindowtablesize, 20, 9, 1 ; Hamming Window
giCosine ftgen 0, 0, 8193, 9, 1, 1, 90 ; Cosine Table
giSine ftgen 0, 0, 8193, 10, 1 ; Sine Table (for freq shifter)
giSquare ftgen 0, 0, 2048, 7, 1.0, 1023, 1.0, 1, 0.0
giLine10 ftgen 0, 0, 2048, 7, 0, 2048, 10.0
giLine1 ftgen 0, 0, 2048, 7, 0, 2048, 1
giPanL ftgen 0, 0, 256, -21, 1
giPanR ftgen 0, 0, 256, -21, 1
giPanNone ftgen 0, 0, 256, -24, giPanL, 0.0, 0.0 ; Edited from 0.5 for non-panned partikkel
giPanAllL ftgen 0, 0, 256, -24, giPanL, 0.0, 1.0
giPanAllR ftgen 0, 0, 256, -24, giPanR, 0.0, 1.0
giPanMixL ftgen 0, 0, 256, -24, giPanL, 0.0, 0.0
giPanMixR ftgen 0, 0, 256, -24, giPanR, 0.0, 0.0
; =====================================================================
; SUPERNOVA: Formant filter vowel frequency tables (Hillenbrand male)
; 5 vowels: a(730) e(660) i(570) o(300) u(640) — F1 frequencies
; tablei interpolation gives smooth morph between adjacent vowels.
; =====================================================================
giFormF1 ftgen 0, 0, 8, -2, 730, 660, 570, 300, 640
giFormF2 ftgen 0, 0, 8, -2, 1090, 1720, 840, 870, 1190
giFormF3 ftgen 0, 0, 8, -2, 2440, 2410, 2410, 2240, 2390
; =====================================================================
; SUPERNOVA: Strum distribution tables (Source+Density grain timing)
; StrumDown: exponential ramp 0→10 — grains cluster toward END of period
; (slow start, fast end — like picking down from low to high string)
; StrumUp: exponential ramp 10→0 — grains cluster toward START of period
; (fast start, slow end — like picking up from high to low string)
; StrumDist: live-blended table used by partikkel as idisttab
; =====================================================================
giStrumDown ftgen 0, 0, 2048, 7, 0, 1024, 0.5, 512, 2.0, 256, 5.0, 256, 10.0
giStrumUp ftgen 0, 0, 2048, 7, 10.0, 256, 5.0, 256, 2.0, 512, 0.5, 1024, 0
giStrumDist ftgen 0, 0, 2048, 7, 0, 2048, 10.0
giChordMix ftgen 0, 0, 8, -2, 0.25, 0.25, 0.25, 0.25, 0 ; Chord voice mix (updated k-rate)
; =====================================================================
; SUPERNOVA: HilbertIIR UDO — Manual Hilbert transform via biquad
; biquad confirmed available (used in factory World of Echo instrument).
; Niemitalo coefficients: 2x4 second-order allpass sections.
; Allpass: H(z) = (c - z^-2) / (1 - c*z^-2) → biquad ain, c, 0, -1, 1, 0, -c
; 1-sample delay on Chain 1 via biquad ain, 0, 1, 0, 1, 0, 0
; =====================================================================
opcode HilbertIIR, aa, a
ain xin
; Chain 1 (I channel): 4 cascaded second-order allpass
a1_1 biquad ain, 0.47940086558884, 0, -1, 1, 0, -0.47940086558884
a1_2 biquad a1_1, 0.87621849353931, 0, -1, 1, 0, -0.87621849353931
a1_3 biquad a1_2, 0.97659758950819, 0, -1, 1, 0, -0.97659758950819
a1_4 biquad a1_3, 0.99749925593555, 0, -1, 1, 0, -0.99749925593555
; 1-sample delay on Chain 1 (critical for quadrature accuracy)
aI biquad a1_4, 0, 1, 0, 1, 0, 0
; Chain 2 (Q channel): 4 cascaded second-order allpass
a2_1 biquad ain, 0.16175849836770, 0, -1, 1, 0, -0.16175849836770
a2_2 biquad a2_1, 0.73302893234149, 0, -1, 1, 0, -0.73302893234149
a2_3 biquad a2_2, 0.94534970032911, 0, -1, 1, 0, -0.94534970032911
aQ biquad a2_3, 0.99059915668453, 0, -1, 1, 0, -0.99059915668453
xout aI, aQ
endop
instr 1
;;;;;;;;;;;;;;;;;;
;;;;; inits ;;;;;;
;;;;;;;;;;;;;;;;;;
tableiw 254, 1, giPanNone
tableiw 0, 0, giPanAllL
tableiw 254, 1, giPanAllL
tableiw 0, 0, giPanAllR
tableiw 254, 1, giPanAllR
tableiw 0, 0, giPanMixL
tableiw 254, 1, giPanMixL
tableiw 0, 0, giPanMixR
tableiw 254, 1, giPanMixR
krecordedbuff init giMaxRecordBuffer
kBufferEmpty init 1
kcopyingbuffer init 0
kprevsize init 0
aoutl init 0
aoutr init 0
arecL init 0
arecR init 0
amixl init 0
amixr init 0
aprevmixl init 0
aprevmixr init 0
arecordsync init 0
kActiveBuffer init 0
; =====================================================================
; SUPERNOVA MOD: Play Mode state (Source+Freeze 2-way toggle)
; kPlayMode: 0=Normal looping, 1=One-shot
; Each press of Source+Freeze toggles: 0→1→0
; One-shot: plays once on trigger, then mutes until next trigger
; =====================================================================
kPlayMode init 0
kOneShotPlaying init 0
kOneShotDelay init 0
kOneShotSrcTrig init 0
kLPGEnv init 0 ; Buchla 292-style LPG envelope (0=closed, 1=open)
kPrevFrzAlt init 0
; =====================================================================
; SUPERNOVA MOD: Multi-Type Filter state
; 5 types: LP(0), HP(1), Dual Peak(2), Bitcrusher(3), Comb±(4)
; Cycled via Source+Reset tap (forward only, wraps at 4→0)
; =====================================================================
kFilterType init 0
; generates right channel ftables for stereo files
gifile_right_offset = 599
itempidx = 0
loop:
if gichn[itempidx] == 2 then
giwoffset = itempidx + gifile_right_offset
giwoffset ftgen (itempidx+gifile_right_offset), 0, 0, 1, gSname[itempidx], 0, 0, 2
endif
itempidx += 1
if (itempidx < 100) igoto loop
gkfilesel_offset = 399
kfirsttime init 1
if kfirsttime == 1 then ; Reset!
giftLA ftgen 0, 0, giMaxRecordBuffer * sr, 7, 0
giftRA ftgen 0, 0, giMaxRecordBuffer * sr, 7, 0
giftLB ftgen 200, 0, giMaxRecordBuffer * sr, 7, 0
giftRB ftgen 201, 0, giMaxRecordBuffer * sr, 7, 0
klen = giMaxRecordBuffer
kBufferEmpty = 1
kActiveBuffer = 0
;printf "bam! Zeroed Buffer is empty: %d\n", kfirsttime, kBufferEmpty
kmaintainsize = 0
endif
;;;;;;;;;;;;;;;;;;;;;;
;; control clipping ;;
;;;;;;;;;;;;;;;;;;;;;;
if gkloopstart < 0.0015 then
gkloopstart = 0
endif
if gkloopstart > 0.995 then
gkloopstart = 1
endif
if gkloopsize < 0.0015 then
gkloopsize = 0
endif
if gkloopsize > 0.995 then
gkloopsize = 1
endif
if gkblend < 0.01 then
gkblend = 0.0
elseif gkblend > 0.99 then
gkblend = 1.0
endif
;;;;;;;;;;;;;;;;;;;
;; phasor config ;;
;;;;;;;;;;;;;;;;;;;
; Set Start and Size
if gksource == 1 then
kfilesr = sr
klen = krecordedbuff
kfilelen = klen
kglen = giMaxRecordBuffer
kpeakamp = 1.0
if kBufferEmpty == 1 then
gkbufferlength = 0
else
gkbufferlength = klen * sr
endif
else
if kBufferEmpty == 1 then
gkbufferlength = 0
else
gkbufferlength = krecordedbuff * sr
endif
kfilesr = gisr[gkfilesel]
kfilelen = gilen[gkfilesel]
kfilesamps = (kfilelen * kfilesr) * (sr / kfilesr)
kpeakamp = gipeak[gkfilesel]
;klen = kfilesamps / sr
klen = kfilelen; * (kfilesr / sr); Length of Current File in seconds
kglen = kfilesamps / sr
endif
if klen == 0 then
klen = giMaxRecordBuffer
endif
kconvertedlen = (klen * (kfilesr / sr))
kloopstart = (gkloopstart * kconvertedlen)
kloopscalar = gkloopsize
kloopsize = ((kloopscalar * kloopscalar) * (kconvertedlen-kloopstart))
if (kloopsize <= 0.000035) then
kloopsize = 0.000035
endif
; Set Speed
if gkrecord == 0 || gkrecord_alt == 1 then
kspeed = (gkspeed * 8.0) - 4.0
endif
if abs(kspeed) <= 1.025 && abs(kspeed) >= 0.975 then
if kspeed > 0 then
kspeed = 1.0
else
kspeed = -1.0
endif
endif
; SUPERNOVA MOD: Spectral freeze removed — gkfreeze repurposed as killswitch
; Freeze button is now momentary mute (Buckethead-style killswitch).
; Original behavior: if (gkfreeze == 1) then kspeed = 0.0
ainl, ainr inch 1, 2
; gkblend_alt now used for frequency shifter (Source+Blend)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; mincer/partikkel primary controls ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; pitch setting
; SUPERNOVA MOD: 2ms portk rejects single-k-period glitches from Python control layer.
; Without this, v/oct CV + encoder summing in Python can cause brief pitch spikes
; at loop boundaries when gkeol triggers a control update cycle.
ilogmax = log(32.00)
ilogmin = log(1.0)
kpitchSmooth portk gkpitch, 0.002
kfactor = exp(kpitchSmooth * (ilogmax - ilogmin) + ilogmin)
kpitch = 0.125 * kfactor
if kpitch <= 1.005 && kpitch >= 0.995 then
kpitch = 1.0
endif
; SUPERNOVA MOD: Grain window locked to Gaussian.
; gkwindow repurposed as DJ filter cutoff (see filter block near output).
; Original code morphed between 7 window shapes — now permanently Gaussian.
if kfirsttime == 1 then
tablecopy giWin, giGaussian
tablecopy giWinB, giGaussian
tablecopy giWinMix, giGaussian
kfirsttime = 0
endif
; Density and Overlap
ilogmaxgfreq = log(2500.0)
;ilogmingfreq = log(0.1667)
ilogmingfreq = log(0.12)
kdensityscalartan = ((tanh((gkdensity * 2.0) - 1.0)) + 0.8) * 0.4
kdensityscalarcube = ((((gkdensity * 2.0) - 1.0) ^ 3) + 1.0) * 0.5
kdensityscalar = (kdensityscalartan * 0.25) + (kdensityscalarcube * 0.75)
; SUPERNOVA FIX: Original code used gkfreeze_alt to gate grain generation
; for external grain triggering. Since we repurposed Source+Freeze for
; play mode cycling, gkfreeze_alt alternates 0/1 independently of kPlayMode.
; After 3 presses (return to normal), gkfreeze_alt=1 would kill grains at
; low density. Now density < 5% → no grains regardless of mode.
if gkdensity > 0.05 then
kgrainfreq = exp((kdensityscalar) * (ilogmaxgfreq - ilogmingfreq) + ilogmingfreq)
else
kgrainfreq = 0
endif
if (kgrainfreq < 4) then
kmaxgrainpw = 4.0
else
kmaxgrainpw = 6.0
endif
; SUPERNOVA MOD: gkoverlap_alt repurposed as Tail Dampener (Source+Overlap).
; Original code: random grain size deviation from gkoverlap_alt.
; krandsizel/krandsizer permanently 0 (no random grain size).
krandsizel = 0.0
krandsizer = 0.0
kgsizescalarL = krandsizel + (gkoverlap * gkoverlap)
kgsizescalarR = krandsizer + (gkoverlap * gkoverlap)
if kgsizescalarL > 1.0 then
kgsizescalarL = 1.0
elseif kgsizescalarL < 0.0 then
kgsizescalarL = 0.0
endif
if kgsizescalarR > 1.0 then
kgsizescalarR = 1.0
elseif kgsizescalarR < 0.0 then
kgsizescalarR = 0.0
endif
kgrainsync init 1
; =====================================================================
; SUPERNOVA MOD: 3-mode play cycle (Source+Freeze)
; ORIGINAL CODE:
; if gkfreeze_alt == 1 then
; kgrainsync trigger gksourcegate, 0.5, 0
; else
; kgrainsync = 0
; endif
;
; NEW: Source+Freeze toggles between 2 modes:
; kPlayMode 0 = Normal looping (default)
; kPlayMode 1 = One-shot playthrough (trigger to play once)
; Each toggle of gkfreeze_alt flips the mode.
; =====================================================================
kFrzAltChanged changed gkfreeze_alt
if kFrzAltChanged == 1 then
if kPlayMode == 0 then
kPlayMode = 1
else
kPlayMode = 0
endif
; Reset state on mode change — prevents stale kOneShotPlaying
kOneShotPlaying = 0
endif
; One-shot triggers (mode 1)
if kPlayMode == 1 then
kOneShotSrcTrig trigger gksourcegate, 0.5, 0
kOneShotRstTrig trigger gkreset, 0.5, 0
if kOneShotSrcTrig == 1 || kOneShotRstTrig == 1 then
kOneShotPlaying = 1
kOneShotDelay = 4
endif
kgrainsync = 0
else
kgrainsync = 0
kOneShotPlaying = 0
endif
;* If we decide we need to handle grain size based on incoming grain rate.
ktickssincelastclock init 0
if kgrainsync == 1 then
kincomingclockrate = ktickssincelastclock * (1 / kr)
ktickssincelastclock = 0
endif
ksmoothclockrate portk kincomingclockrate, 0.5
ktickssincelastclock += 1
if kgrainfreq > 0 then
kgrainsizel = (kgsizescalarL) * (((1 / kgrainfreq) * kmaxgrainpw) * 1000) + 1
kgrainsizer = (kgsizescalarR) * (((1 / kgrainfreq) * kmaxgrainpw) * 1000) + 1
else
;kgrainsizel = (kgsizescalarL * kgsizescalarL * 2000) + 0.5
;kgrainsizer = (kgsizescalarR * kgsizescalarR * 2000) + 0.5
kgrainsizel = (kgsizescalarL * kgsizescalarL * 1000) + 0.5
kgrainsizer = (kgsizescalarR * kgsizescalarR * 1000) + 0.5
;if ksmoothclockrate < 0.03 && gkfreeze_alt == 1 then
if kgrainsizel > 6 *ksmoothclockrate * 1000.0 then
kgrainsizel = (6 * ksmoothclockrate * 1000.0)
endif
if kgrainsizer > 6 * ksmoothclockrate * 1000.0 then
kgrainsizer = (6 * ksmoothclockrate * 1000.0)
endif
if kgrainsizel == 0 then
kgrainsizel = 0.5
endif
if kgrainsizer == 0 then
kgrainsizer = 0.5
endif
endif
if kgrainsizel > 8000.0 then
kgrainsizel = 8000.0
endif
if kgrainsizer > 8000.0 then
kgrainsizer = 8000.0
endif
;printks "clock rate: %f\t grainsize: %f\n", 0.25, ksmoothclockrate, kgrainsizel
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; mincer/partikkel secondary controls ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Prep Pan table
; SUPERNOVA: gkloopsize_alt repurposed as freq shifter stereo width (Source+Size).
; Grain panning disabled (fixed at center/no pan).
kpanamount = 0
kpanchanged changed kpanamount
if kpanchanged == 1 then
tablemix giPanMixL, 0, 256, giPanNone, 0, 1.0-kpanamount, giPanAllL, 0, kpanamount
tablemix giPanMixR, 0, 256, giPanNone, 0, 1.0-kpanamount, giPanAllR, 0, kpanamount
tablew 0, 0, giPanMixL
tablew 254, 1, giPanMixL
tablew 0, 0, giPanMixR
tablew 254, 1, giPanMixR
endif
; =====================================================================
; SUPERNOVA MOD: Structured Chord Grains (replaces random pitch)
; Source+Pitch (gkpitch_alt) = progressive chord stacking:
; CCW (0.0): root only — all 4 partikkel voices at unison
; Noon (0.5): root + perfect 5th — voice 2 spreads to interval
; CW (1.0): root + 5th + 3rd + 7th — full chord, all voices spread
; Chord quality fixed at Major 7th: P5(7st), M3(4st), M7(11st)
; Implementation: kwavekey1-4 crossfade from unison(1.0) to chord ratios.
; At gkpitch_alt=0, all keys=1 → identical to original unison behavior.
; No amp table changes, no CPU increase, no level discontinuities.
; =====================================================================
krndpitchmod = 0 ; Random pitch disabled — chord grains replace it
kChordAmt = gkpitch_alt
; Progressive stacking amounts: 5th first, then 3rd, then 7th
; Voice 2 (5th): fades in 0.0→0.5
kAmt5th = kChordAmt * 2
if kAmt5th > 1 then
kAmt5th = 1
endif
; Voice 3 (3rd): fades in 0.5→0.75
kAmt3rd = (kChordAmt - 0.5) * 4
if kAmt3rd < 0 then
kAmt3rd = 0
endif
if kAmt3rd > 1 then
kAmt3rd = 1
endif
; Voice 4 (7th): fades in 0.75→1.0
kAmt7th = (kChordAmt - 0.75) * 4
if kAmt7th < 0 then
kAmt7th = 0
endif
if kAmt7th > 1 then
kAmt7th = 1
endif
; Fixed chord quality: Major 7th — P5(7st), M3(4st), M7(11st)
kInt5th = 7
kInt3rd = 4
kInt7th = 11
; Convert semitone intervals to pitch ratios
; ratio = 2^(semitones/12)
kRatio5th = 2 ^ (kInt5th / 12.0)
kRatio3rd = 2 ^ (kInt3rd / 12.0)
kRatio7th = 2 ^ (kInt7th / 12.0)
; Dynamic chord voice amplitudes via giChordMix table
; All 4 voices always at equal 0.25 amplitude (matches old iwaveamptab=-1)
; Volume is CONSTANT — chord effect is purely pitch-based via kwavekey1-4.
; When chord off: all 4 voices at pitch 1.0 = unison (same as before)
; When chord on: voices spread to intervals, amplitude unchanged = no volume dip
tablew 0.25, 0, giChordMix ; voice 1 (root)
tablew 0.25, 1, giChordMix ; voice 2 (→5th)
tablew 0.25, 2, giChordMix ; voice 3 (→3rd)
tablew 0.25, 3, giChordMix ; voice 4 (→7th)
tablew 0, 4, giChordMix ; trainlet always off
; =====================================================================
; SUPERNOVA MOD: Voice Detune (Source+Density = per-voice pitch spread)
; gkdensity_alt controls random detune spread per chord voice:
; CCW(0) = tight unison, no detune (clean, original behavior)
; CW(1) = wide detune ±100 cents per voice (super-saw, thick, obvious)
; Each voice gets slow, independent random pitch drift.
; Stacks with chord intervals from Source+Pitch — detuned chords = thicc.
; When chord is off (all unison), detune alone = supersaw unison texture.
; =====================================================================
kDetuneCents = gkdensity_alt * 100 ; 0→100 cents max spread (doubled from 50)
; Independent slow drift per voice (different rates = organic movement)
kDet2 randi kDetuneCents, 2.3
kDet3 randi kDetuneCents, 3.1
kDet4 randi kDetuneCents, 1.7
; Grain distribution: periodic (no strum)
kdistribution = 0
idisttab = giLine10 ; flat distribution (default)
;async = 0 ; no sync input
async upsamp kgrainsync
async2 init 0
kenv2amt = 1 ; entirely secondary enveloping
ienv2tab = giWinMix ; default secondary envelope (flat)
ienv_attack = -1 ; ; default attack envelope (flat)
ienv_decay = -1 ; ; default decay envelope (flat)
ksustain_amount = .0 ; time (in fraction of grain dur) at sustain level for each grain
ka_d_ratio = 0.5 ; balance between attack and decay time
iamp = 0.45 ; amp
igainmasks = -1 ; (default) no gain masking
ksweepshape = 0 ; shape of frequency sweep (0=no sweep)
iwavfreqstarttab = -1 ; frequency sweep start table
iwavfreqendtab = -1 ; frequency sweep end table
ifmamptab = -1 ; default FM scaling (=1)
kfmenv = giWin ; default FM envelope (flat)
icosine = giCosine ; cosine ftable
kTrainCps = 1 ; grain rate for single-cycle trainlet in each grain
knumpartials = 1 ; number of partials in trainlet
kchroma = 1 ; balance of partials in trainlet
krandommask = 0 ; no random grain masking
; SUPERNOVA MOD: gkwindow_alt repurposed as filter resonance.
; Original code: grain muting probability from gkwindow_alt.
; krandommask permanently 0 (no grain muting).
iwaveamptab = giChordMix ; chord voice mix table (dynamic, updated at k-rate)
krandposscalar = (gkloopstart_alt * gkloopstart_alt)
krandpos = birnd(krandposscalar)
; Chord grain pitch ratios + per-voice detune
; At kChordAmt=0 + kDetune=0: all keys=1.0 (original unison behavior)
; Chord spreads voices to intervals, detune adds organic drift on top
kwavekey1 = 1 ; root — always unison, no detune
kwavekey2 = (1 + ((kRatio5th - 1) * kAmt5th)) * cent(kDet2)
kwavekey3 = (1 + ((kRatio3rd - 1) * kAmt3rd)) * cent(kDet3)
kwavekey4 = (1 + ((kRatio7th - 1) * kAmt7th)) * cent(kDet4)
;imax_grains = 25 ; max grains per k period
imax_grains = 20 ; bumped from 10 for super grain / chord density
; Set a few parameters for mincer
kphaselock = 1
;ifftlivesize = 1024
ifftlivesize = 2048
ifftfilesize = 2048
idecim = 4
idecimlive = 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Recording Setup ;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Detect Reset
ksynctrig trigger gkreset, 0.5, 0
kfreezetrig trigger gkfreeze, 0.5, 0
krecordtrig trigger gkrecord, 0.5, 1
atime init 0
if gkfilestate == 0 then
asynctrig = a(ksynctrig)
endif
if kBufferEmpty == 1 then
kdetectrecordend trigger k(arecordsync), 0.5, 0
;printf "BAM Record Sync Detected! %d \n", kdetectrecordend, kdetectrecordend
endif
; Alt Recording Features (Record + Control)
kmaintainsize init 0
if gkfilestate == 1 then
; clear buffer
if ksynctrig == 1 then ; Reset!
;tablecopy giftLA, giftEmpty
;tablecopy giftRA, giftEmpty
;tablecopy giftLB, giftEmpty
;tablecopy giftRB, giftEmpty
giftLA ftgen 0, 0, giMaxRecordBuffer * sr, 7, 0
giftRA ftgen 0, 0, giMaxRecordBuffer * sr, 7, 0
giftLB ftgen 200, 0, giMaxRecordBuffer * sr, 7, 0
giftRB ftgen 201, 0, giMaxRecordBuffer * sr, 7, 0
klen = giMaxRecordBuffer
kBufferEmpty = 1
kActiveBuffer = 0
;printf "bam! Zeroed Buffer is empty: %d\n", ksynctrig, kBufferEmpty
kmaintainsize = 0
endif
endif
;printks "BufferEmpty: %d\tklen: %f\tPlayback phase: %f\trecstart: %f\trecend: %f\trecwrap: %f\n", 0.1, kBufferEmpty, klen, k(aphs), krecstart, krecend, krecwrap
;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Phasor Operation ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;
; Run Phasor
arecordtrig = a(krecordtrig)
if kBufferEmpty == 1 && gksource == 1 then
asynctrig += arecordtrig
endif
if gksource == 1 then
asynctrig += arecordsync
endif
;asynctrig += arecordtrig
; =====================================================================
; SUPERNOVA MOD: Play mode phasor routing
; Mode 0 (Normal): standard syncphasor looping
; Mode 1 (One-shot): source gate resets phasor, plays once
; =====================================================================
if kPlayMode == 1 then
asynctrig += a(kOneShotSrcTrig)
endif
kphasorfreq = ((1 / kloopsize) * kspeed) * (kfilesr / sr)
if kphasorfreq > sr then
kphasorfreq = sr
endif
; =====================================================================
; SUPERNOVA MOD: One-shot freeze — phasor stops when idle
; =====================================================================
if kPlayMode == 1 && kOneShotPlaying == 0 then
kphasorfreq = 0
endif
; Normal and One-shot: use syncphasor
aphs, aphssync syncphasor kphasorfreq, asynctrig, 0.0000
atime = kloopstart + (kloopsize * aphs)
agphs = atime / (kglen * (kfilesr / sr))
;printks "startctrl: %f\tscalarctrl: %f\tstarttime: %f\tsize: %f\tconvertedlen: %f\tlength: %f\tphase: %f\ttime: %f\tbuff size: %f\n", 0.25, gkloopstart, gkloopsize, kloopstart, kloopsize, kconvertedlen, klen, k(aphs), k(atime), kbuffsize
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Recording Process ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;
if kBufferEmpty == 1 then
krecstart = 0
krecend = giMaxRecordBuffer
krecwrap = 0
else
if gkrecord == 0 || gkrecord_alt == 1 then
if kmaintainsize == 1 then
krecwrap = kloopstart
if kspeed >= 0 then
krecstart = k(atime) - (ksmps * 2)
else
krecstart = klen - k(atime) + (ksmps * 2)
endif
if krecstart < 0 then
krecstart = 0
elseif krecstart > klen then
krecstart = 0
endif
if kspeed != 0 || gkrecord_alt == 1 then
if kspeed == 0 then
krecend = giMaxRecordBuffer
else
krecend = (kloopsize + kloopstart) / abs(kspeed)
endif
else
krecend = giMaxRecordBuffer
endif
else
krecwrap = kloopstart
krecstart = 0
krecend = giMaxRecordBuffer
endif
endif
endif
koverlapFlag init 0
krecordA init 0
krecordB init 0
if krecordtrig == 1 then
krecordB = 0
endif
kbuffsize init giMaxRecordBuffer
krecordrise trigger gkrecord, 0.5, 0
if krecordrise == 1 then
krecordB = 1
endif
if krecordA == 1 then
krecordA = 0
krecordB = 1
gkrecordstatus = 1
endif
if gksource == 0 then
ablenddry = 0
endif
if krecstart > klen - 0.25 && krecordB == 1 then
krecstart = kloopstart
krecordA = 1
endif
;arecL = aprevmixl
;arecR = aprevmixr
arecL = amixl
arecR = amixr
kfin, kbuffsize, koverlapFlagA CplxBufRec2 arecL, arecR, ainl * ablenddry, ainr * ablenddry, giftLA, giftRA, giftLB, giftRB, krecordB, krecstart, krecend, krecwrap, gkrecord_alt, kActiveBuffer
kfinchanged trigger kfin, 0.5, 0 ; Done Recording
kfinstarted trigger kfin, 0.5, 1 ; Started Recording
koverlaptrig trigger koverlapFlagA, 0.5, 0 ; overwrite of buffer complete
;kstate CopyPartialBuffer, krecstart , kbuffsize, kfinchanged
if kfinchanged == 1 then
if kActiveBuffer == 1 then
kActiveBuffer = 0
else
kActiveBuffer = 1
endif
if kBufferEmpty == 1 then
arecordsync = 1
kActiveBuffer = 1
endif
;tablemix giftLB, krecstart, kbuffsize, giftLA, krecstart, 1.0, giftEmpty, 0, 0.0
;tablemix giftRB, krecstart, kbuffsize, giftRA, krecstart, 1.0, giftEmpty, 0, 0.0
event "i", 2, 0, -1, krecstart, kbuffsize
;kcopyingbuffer = 1
if gkrecord_alt == 0 then
krecordB = 0
endif
if kmaintainsize == 1 then
kprevsize = krecordedbuff
knewsize = kbuffsize/ (sr * 1.0)
if knewsize < kprevsize || gkrecord_alt == 1 then
krecordedbuff = kprevsize
else
krecordedbuff = knewsize
endif
else
kprevsize = (kbuffsize) / (sr * 1.0)
krecordedbuff = (kbuffsize) / (sr * 1.0)
endif
kmaintainsize = 1
kBufferEmpty = 0
else
if kfinstarted == 1 && kBufferEmpty == 1 then
arecordsync = 1
else
arecordsync = 0
endif
endif
if koverlaptrig == 1 then
;tablemix giftLB, krecstart, kbuffsize + (sr/4), giftLA, krecstart, 1.0, giftEmpty, 0, 0.0
;tablemix giftRB, krecstart, kbuffsize + (sr/4), giftRA, krecstart, 1.0, giftEmpty, 0, 0.0
;event "i", 2, 0, 2, krecstart, kbuffsize
;kcopyingbuffer = 1
endif
;printks "kbuffsize: %f\t krecordedbuff: %f\tkglen: %f\tklen: %f\tkloopscalar: %f\tkloopstart: %f\n",0.1, kbuffsize, krecordedbuff, kglen, klen, kloopscalar, kloopstart
gkrecordstatus = krecordB
;;;;;;;;;;;;;;;;;;;;;;
;;;;; processing ;;;;;
;;;;;;;;;;;;;;;;;;;;;;
; =====================================================================
; Processing chain: mincer (phase vocoder) + partikkel (granular)
; =====================================================================
if gkloopstart_alt < 0.01 then
asamplepos1 = agphs
asamplepos2 = agphs
asamplepos3 = agphs
asamplepos4 = agphs ;random klow, khigh
else
if gksource == 1 then
krandposlivescalar = ((krecordedbuff) / giMaxRecordBuffer)
krandpos *= krandposlivescalar
asamplepos1 = abs(agphs + krandpos)
kgrainscalar = kloopscalar * krandposlivescalar
if k(asamplepos1) > (krandposlivescalar) then
asamplepos1 -= krandposlivescalar
endif
else
asamplepos1 = abs(agphs + krandpos)
if k(asamplepos1) > (gkloopstart + kloopscalar) then
asamplepos1 -= kloopscalar
endif
endif
asamplepos2 = asamplepos1
asamplepos3 = asamplepos1
asamplepos4 = asamplepos1
endif
if gksource == 1 then
kwavfreq = ((1/kglen)*kpitch) * cent(krndpitchmod); fundamental frequency of source waveform
awavfm = 0
asigL mincer atime, 1.0, kpitch, giftLB, kphaselock, ifftlivesize, idecimlive
asigR mincer atime, 1.0, kpitch, giftRB, kphaselock, ifftlivesize, idecimlive
agrainLL, agrainLR partikkel kgrainfreq, kdistribution, idisttab, async, kenv2amt, ienv2tab, \
ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, kgrainsizel, iamp, igainmasks, \
kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfm, \
ifmamptab, kfmenv, icosine, kTrainCps, knumpartials, \
kchroma, giPanMixL, krandommask, giftLB, giftLB, giftLB, giftLB, \
iwaveamptab, asamplepos1, asamplepos2, asamplepos3, asamplepos4, \
kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains,1
async2 partikkelsync, 1
agrainRL, agrainRR partikkel kgrainfreq, kdistribution, idisttab, async2, kenv2amt, ienv2tab, \
ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, kgrainsizer, iamp, igainmasks, \
kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfm, \
ifmamptab, kfmenv, icosine, kTrainCps, knumpartials, \
kchroma, giPanMixR, krandommask, giftRB, giftRB, giftRB, giftRB, \
iwaveamptab, asamplepos1, asamplepos2, asamplepos3, asamplepos4, \
kwavekey1, kwavekey2, kwavekey3, kwavekey4, imax_grains
agrainL = agrainLL + agrainRR
agrainR = agrainRL + agrainLR
if kBufferEmpty == 1 then
agrainL = 0
agrainR = 0
asigL = 0
asigR = 0
endif
else
kwavfreq = ((1/klen)*kpitch) * cent(krndpitchmod);
knumchn = gichn[gkfilesel]
kwaveformL = gkfilesel_offset + gkfilesel + 1
asigL mincer atime, 0.8, kpitch * (kfilesr / sr), kwaveformL, kphaselock, ifftfilesize, idecim
knumchn = gichn[gkfilesel]
if knumchn == 2 then
kwaveformR = gifile_right_offset + gkfilesel
asigR mincer atime, 0.8, kpitch * (kfilesr / sr), kwaveformR, kphaselock, ifftfilesize, idecim
else
kwaveformR = kwaveformL
asigR = asigL
endif
; SUPERNOVA: Grain FM disabled — gkblend_alt repurposed as frequency shifter.
awavfml = 0
awavfmr = 0
agrainLL, agrainLR partikkel kgrainfreq, kdistribution, idisttab, async, kenv2amt, ienv2tab, \
ienv_attack, ienv_decay, ksustain_amount, ka_d_ratio, kgrainsizel, iamp, igainmasks, \
kwavfreq, ksweepshape, iwavfreqstarttab, iwavfreqendtab, awavfml, \