-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlooping_delay.c
982 lines (760 loc) · 27.3 KB
/
looping_delay.c
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
/*
* looping_delay.c - the heart of the DLD
* Functions for processing audio buffer from the codec, managing audio buffer addresses,
* cross-fades, windowing/scrolling, and reverse
*
* Author: Dan Green (danngreen1@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* See http://creativecommons.org/licenses/MIT/ for more information.
*
* -----------------------------------------------------------------------------
*/
#include "globals.h"
#include "looping_delay.h"
#include "sdram.h"
#include "adc.h"
#include "params.h"
#include "audio_memory.h"
#include "timekeeper.h"
#include "compressor.h"
#include "dig_pins.h"
extern const float epp_lut[4096];
extern float param[NUM_CHAN][NUM_PARAMS];
extern uint8_t mode[NUM_CHAN][NUM_CHAN_MODES];
extern uint8_t global_mode[NUM_GLOBAL_MODES];
extern float global_param[NUM_GLOBAL_PARAMS];
extern uint8_t flag_inf_change[2];
uint8_t SAMPLESIZE=2;
extern uint8_t flag_pot_changed_revdown[NUM_POT_ADCS];
extern int16_t CODEC_DAC_CALIBRATION_DCOFFSET[4];
//extern int16_t CODEC_ADC_CALIBRATION_DCOFFSET[4];
volatile uint32_t ping_time;
uint32_t locked_ping_time[NUM_CHAN];
volatile uint32_t divmult_time[NUM_CHAN];
uint32_t write_addr[NUM_CHAN];
uint32_t read_addr[NUM_CHAN];
uint32_t loop_start[NUM_CHAN];
uint32_t loop_end[NUM_CHAN];
const uint32_t LOOP_RAM_BASE[NUM_CHAN] = {SDRAM_BASE, SDRAM_BASE + LOOP_SIZE};
uint32_t fade_queued_dest_divmult_time[NUM_CHAN];
uint8_t queued_write_fade_state[NUM_CHAN];
uint32_t fade_queued_dest_read_addr[NUM_CHAN];
uint32_t fade_queued_dest_write_addr[NUM_CHAN];
uint32_t fade_dest_read_addr[NUM_CHAN];
uint32_t fade_dest_write_addr[NUM_CHAN];
float read_fade_pos[NUM_CHAN];
float write_fade_pos[NUM_CHAN];
uint8_t doing_reverse_fade[NUM_CHAN] = {0,0};
float lpf_coef;
int32_t min_vol;
float mainin_lpf[2]={0.0,0.0}, auxin_lpf[2]={0.0,0.0}, dcblock_lpf[2]={0.0,0.0};
enum FadeStates{
NOT_FADING,
WRITE_FADE_DOWN,
WRITE_FADE_UP,
WRITE_FADE_WRDOWN_DESTUP
};
uint8_t write_fade_state[NUM_CHAN] = {NOT_FADING,NOT_FADING};
void audio_buffer_init(void)
{
uint32_t i;
if (MODE_24BIT_JUMPER)
SAMPLESIZE=4;
else
SAMPLESIZE=2;
if (!ping_time)
ping_time=0x00002000*SAMPLESIZE;
for(i=0;i<NUM_CHAN;i++){
memory_clear(i);
write_addr[i]=LOOP_RAM_BASE[i] + ping_time;
read_addr[i] = LOOP_RAM_BASE[i];
fade_dest_read_addr[i] = LOOP_RAM_BASE[i];
fade_dest_write_addr[i] = write_addr[i];
divmult_time[i]=ping_time;
set_divmult_time(i);
loop_start[i] = LOOP_RAM_BASE[i];
loop_end[i] = LOOP_RAM_BASE[i] + LOOP_SIZE;
doing_reverse_fade[i]=0;
}
lpf_coef = 0.0002;
if (SAMPLESIZE==2)
{
min_vol = 10;
init_compressor(1<<15, 0.75);
}
else
{
min_vol = 10 << 16;
init_compressor(1<<31, 0.75);
}
}
uint32_t offset_samples(uint8_t channel, uint32_t base_addr, uint32_t offset, uint8_t subtract)
{
uint32_t t_addr;
//convert samples to addresses
offset*=SAMPLESIZE;
if (subtract == 0){
t_addr = base_addr + offset;
while (t_addr >= (LOOP_RAM_BASE[channel] + LOOP_SIZE))
t_addr = t_addr - LOOP_SIZE;
} else {
t_addr = base_addr - offset;
while (t_addr < LOOP_RAM_BASE[channel])
t_addr = t_addr + LOOP_SIZE;
}
if (SAMPLESIZE==2)
t_addr = t_addr & 0xFFFFFFFE; //addresses must be even
else
t_addr = t_addr & 0xFFFFFFFC; //addresses must end in 00
return (t_addr);
}
uint32_t calculate_read_addr(uint8_t channel, uint32_t new_divmult_time){
uint32_t t_read_addr;
t_read_addr = offset_samples(channel, write_addr[channel], new_divmult_time, 1-mode[channel][REV]);
return (t_read_addr);
}
void swap_read_write(uint8_t channel){
fade_dest_read_addr[channel] = fade_dest_write_addr[channel];
fade_dest_write_addr[channel] = read_addr[channel];
write_fade_pos[channel] = global_param[FAST_FADE_INCREMENT];
write_fade_state[channel] = WRITE_FADE_WRDOWN_DESTUP;
read_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
doing_reverse_fade[channel]=1;
fade_queued_dest_divmult_time[channel] = 0;
}
void reverse_loop(uint8_t channel)
{
uint32_t t;
//When reversing in INF mode, swap the loop start/end but offset them by the FADE_SAMPLES so the crossfade stays within already recorded audio
t=loop_start[channel];
loop_start[channel] = offset_samples(channel, loop_end[channel], global_param[SLOW_FADE_SAMPLES], mode[channel][REV]);
loop_end[channel] = offset_samples(channel, t, global_param[SLOW_FADE_SAMPLES], mode[channel][REV]);
//ToDo: Add a crossfade for read head reversing direction here
fade_dest_read_addr[channel] = read_addr[channel];
read_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
doing_reverse_fade[channel]=1;
fade_queued_dest_divmult_time[channel] = 0;
}
uint32_t inc_addr(uint32_t addr, uint8_t channel)
{
if (mode[channel][REV] == 0)
{
addr+=SAMPLESIZE;
if (addr >= (LOOP_RAM_BASE[channel] + LOOP_SIZE))
addr = LOOP_RAM_BASE[channel];
}
else
{
addr-=SAMPLESIZE;
if (addr <= LOOP_RAM_BASE[channel])
addr = LOOP_RAM_BASE[channel] + LOOP_SIZE - SAMPLESIZE;
}
return(addr & 0xFFFFFFFE);
//return (offset_samples(channel, addr, 1, mode[channel][REV]));
}
uint32_t dec_addr(uint32_t addr, uint8_t channel)
{
if (mode[channel][REV] != 0)
{
addr+=SAMPLESIZE;
if (addr >= (LOOP_RAM_BASE[channel] + LOOP_SIZE))
addr = LOOP_RAM_BASE[channel];
}
else
{
addr-=SAMPLESIZE;
if (addr <= LOOP_RAM_BASE[channel])
addr = LOOP_RAM_BASE[channel] + LOOP_SIZE - 2;
}
return(addr & 0xFFFFFFFE);
//return (offset_samples(channel, addr, 1, 1-mode[channel][REV]));
}
/*
* in_between()
*
* Utility function to determine if address mid is in between addresses beg and end in a circular (ring) buffer.
* To Do: draw a truth table and condense this into a few boolean logic functions
*
*/
uint8_t in_between(uint32_t mid, uint32_t beg, uint32_t end, uint8_t reverse)
{
uint32_t t;
if (beg==end) //zero length, trivial case
{
if (mid!=beg) return(0);
else return(1);
}
if (reverse) { //swap beg and end if we're reversed
t=end;
end=beg;
beg=t;
}
if (end>beg) //not wrapped around
{
if ((mid>=beg) && (mid<=end)) return(1);
else return(0);
}
else //end has wrapped around
{
if ((mid<=end) || (mid>=beg)) return(1);
else return(0);
}
}
/*
* set_divmult_time()
*
* Changing divmult (Time knob or jack, or Ping clock speed) results in moving the read addr
* Unless we're in INF mode, then move the loop end
*
* To move the read addr, we have to pay attention to the cross-fading status:
* If we are not cross-fading the read head then
* -See if the new divmult_time is different than the existing one
* -If so, initiate a cross-fade.
* -Set divmult_time to the destination divmult_time
*
* Otherwise, if we are in the middle of a cross-fade, then just queue the new divmult_time
*
*/
uint32_t old_divmult_time[2]={0,0};
void set_divmult_time(uint8_t channel){
uint32_t t_divmult_time;
if (mode[channel][PING_LOCKED])
t_divmult_time = locked_ping_time[channel] * param[channel][TIME];
else
t_divmult_time = ping_time * param[channel][TIME];
//t_divmult_time = t_divmult_time & 0xFFFFFFFC; //force it to be a multiple of 4
// Check for valid divmult time range
if (t_divmult_time > LOOP_SIZE/SAMPLESIZE)
t_divmult_time = LOOP_SIZE/SAMPLESIZE;
if (mode[channel][INF] != INF_OFF)
{
if (old_divmult_time[channel] != t_divmult_time){
old_divmult_time[channel] = t_divmult_time;
divmult_time[channel] = t_divmult_time;
if (flag_pot_changed_revdown[TIME*2+channel])
loop_end[channel] = offset_samples(channel, loop_start[channel], divmult_time[channel], mode[channel][REV]);
else
loop_start[channel] = offset_samples(channel, loop_end[channel], divmult_time[channel], 1-mode[channel][REV]);
// If the read addr is not in between the loop start and end, then fade to the loop start
if (!in_between(read_addr[channel], loop_start[channel], loop_end[channel],mode[channel][REV]))
{
if (read_fade_pos[channel] < global_param[SLOW_FADE_INCREMENT])
{
read_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
fade_queued_dest_divmult_time[channel] = 0;
fade_dest_read_addr[channel] = loop_start[channel];
reset_loopled_tmr(channel);
}
else
{
fade_queued_dest_read_addr[channel]=loop_start[channel];
}
}
}
}
else
{
if (read_fade_pos[channel] < global_param[SLOW_FADE_INCREMENT])
{
divmult_time[channel] = t_divmult_time;
fade_queued_dest_divmult_time[channel] = 0;
fade_dest_read_addr[channel] = calculate_read_addr(channel, divmult_time[channel]);
if (fade_dest_read_addr[channel] != read_addr[channel])
read_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
} else
fade_queued_dest_divmult_time[channel]=t_divmult_time;
}
}
/*
* scroll_loop()
*
* Move loop_start and loop_end the same amount.
*
* scroll_amount specifies the amount to move it, as expressed as a fraction of the loop legnth
* scroll_subtract flag means to subtract from loop_start and loop_end, otherwise add
* Thus, if loop_start is 500 and loop_end is 750, and scroll_amount is 0.4
* then add 0.4 * (750 - 500) = 100 to loop_start and loop_end
*
*/
void scroll_loop(uint8_t channel, float scroll_amount, uint8_t scroll_subtract)
{
uint32_t loop_length;
uint32_t loop_shift;
// Get loop length
if (!mode[channel][REV]){
if (loop_end[channel] > loop_start[channel])
loop_length = loop_end[channel] - loop_start[channel];
else
loop_length = loop_end[channel] + LOOP_SIZE - loop_start[channel];
}
else
{
if (loop_start[channel] > loop_end[channel])
loop_length = loop_start[channel] - loop_end[channel];
else
loop_length = loop_start[channel] + LOOP_SIZE - loop_end[channel];
}
//Calculate amount to shift
loop_shift = (uint32_t)(scroll_amount * (float)loop_length);
//convert the units from addresses to samples
loop_shift = loop_shift / SAMPLESIZE;
//Add (or subtract) to the loop points.
loop_start[channel] = offset_samples(channel, loop_start[channel], loop_shift, scroll_subtract);
loop_end[channel] = offset_samples(channel, loop_end[channel], loop_shift, scroll_subtract);
}
/*
* increment_read_fade()
*
* If we're fading, increment the fade position
* If we've cross-faded 100%:
* -Stop the cross-fade
* -Set read_addr to the destination
* -Load the next queued fade (if it exists)
*
*/
void increment_read_fade(uint8_t channel)
{
if (read_fade_pos[channel]>0.0)
{
read_fade_pos[channel] += global_param[SLOW_FADE_INCREMENT];
if (read_fade_pos[channel] > 1.0)
{
read_fade_pos[channel] = 0.0;
doing_reverse_fade[channel] = 0;
read_addr[channel] = fade_dest_read_addr[channel];
if (fade_queued_dest_divmult_time[channel])
{
divmult_time[channel] = fade_queued_dest_divmult_time[channel];
fade_queued_dest_divmult_time[channel]=0;
fade_dest_read_addr[channel] = calculate_read_addr(channel, divmult_time[channel]);
read_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
}
else if (fade_queued_dest_read_addr[channel])
{
fade_dest_read_addr[channel] = fade_queued_dest_read_addr[channel];
fade_queued_dest_read_addr[channel]=0;
read_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
}
}
}
}
void increment_write_fade(uint8_t channel)
{
if (write_fade_pos[channel]>0.0){
if (write_fade_state[channel]==WRITE_FADE_UP)
write_fade_pos[channel] += global_param[FAST_FADE_INCREMENT];
else if (write_fade_state[channel]==WRITE_FADE_DOWN)
write_fade_pos[channel] += global_param[SLOW_FADE_INCREMENT];
else if (write_fade_state[channel]==WRITE_FADE_WRDOWN_DESTUP)
write_fade_pos[channel] += global_param[FAST_FADE_INCREMENT];
if (write_fade_pos[channel] > 1.0)
{
write_fade_pos[channel] = 0.0;
write_fade_state[channel]=NOT_FADING;
write_addr[channel] = fade_dest_write_addr[channel];
if (mode[channel][INF]==INF_TRANSITIONING_ON)
mode[channel][INF]=INF_ON;
}
}
}
/*
* change_inf_mode()
*
* Do nothing if we are write-fading
*
* Otherwise...
* If INF is on, go to transition-off mode
* Initiate a write fade-up at the read_addr
*
* If INF is off or transitioning off, turn it on
* Initiate a write-fade-down at the present write_addr
*
*
*/
void change_inf_mode(uint8_t channel)
{
if(write_fade_state[channel]==NOT_FADING)
{
flag_inf_change[channel]=0;
if (mode[channel][INF]==INF_ON || mode[channel][INF]==INF_TRANSITIONING_ON)
{
mode[channel][INF] = INF_TRANSITIONING_OFF;
write_fade_pos[channel] = global_param[FAST_FADE_INCREMENT];
write_fade_state[channel]=WRITE_FADE_UP;
fade_dest_write_addr[channel] = read_addr[channel];
}
else
{
//Don't change the loop start/end if we hit INF off recently (recent enough that we're still T_OFF)
//This is because the read and write pointers are in the same spot
if (mode[channel][INF] != INF_TRANSITIONING_OFF)
{
reset_loopled_tmr(channel);
loop_start[channel] = fade_dest_read_addr[channel]; //use the dest because if we happen to be fading the read head when we hit inf (e.g. changing divmult time) then we should loop between the new points since divmult_time (used in the next line) corresponds with the dest
loop_end[channel] = offset_samples(channel, loop_start[channel], divmult_time[channel], mode[channel][REV]);
}
write_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
write_fade_state[channel]=WRITE_FADE_DOWN;
fade_dest_write_addr[channel] = write_addr[channel];
mode[channel][INF] = INF_TRANSITIONING_ON;
}
}
}
/*
* abs_diff()
*
* returns the absolute difference between uint32_t values
*
*/
uint32_t abs_diff(uint32_t a1, uint32_t a2)
{
if (a1>a2) return (a1-a2);
else return (a2-a1);
}
enum AutoMute_States{
MUTED,
FADING_DOWN,
FADING_UP,
UNMUTED
};
/*
* process_audio_block_codec()
*
* Process the audio
* This is called by the RX DMA interrupt for each codec
*
* parameter sz is codec_BUFF_LEN = 8
*
*/
void process_audio_block_codec(int16_t *src, int16_t *dst, int16_t sz, uint8_t channel)
{
static uint32_t mute_on_boot_ctr=96000;
static uint8_t auto_muting_main_state[NUM_CHAN]={0,0};
static float auto_muting_main_fade[NUM_CHAN]={0,0};
static uint8_t auto_muting_aux_state[NUM_CHAN]={0,0};
static float auto_muting_aux_fade[NUM_CHAN]={0,0};
uint32_t last_read_block_addr;
int32_t mainin, mix, dry, wr, rd;
float regen;
float mainin_atten;
int32_t auxin;
int32_t auxout;
uint16_t i,t;
uint16_t topbyte, bottombyte;
int32_t rd_buff[codec_BUFF_LEN/4];
int32_t rd_buff_dest[codec_BUFF_LEN/4];
int32_t wr_buff[codec_BUFF_LEN/4];
uint32_t crossed_start_fade_addr;
uint32_t start_fade_addr;
int32_t dummy;
uint32_t t32;
//Sanity check to made sure the read_addr is inside the loop.
//We shouldn't have to do this. The likely reason the read_addr escapes the loop
//is that it passes the start_fade_addr and triggers the crossed_start_fade_addr block,
//while at the same time already in the middle of a cross fade due to change in divmult_time or reverse
//What to do? If we queue to crossed_start_fade_addr fade then we risk overflowing out of the loop
//We could set start_fade_addr to be much earlier than the loop_end (by a factor of 2?) so that we won't go
//past the loop_end even if we have to do two cross fades. Of course, this means usually our loop will be earlier by
//one crossfade period, maybe 3ms or so. This seems acceptable, but a better solution could be desired.
if ((mode[channel][INF]==INF_ON || mode[channel][INF]==INF_TRANSITIONING_OFF || mode[channel][INF]==INF_TRANSITIONING_ON)
&& (!in_between(read_addr[channel], loop_start[channel], loop_end[channel], mode[channel][REV])))
{
if (read_fade_pos[channel] < global_param[SLOW_FADE_INCREMENT])
{
read_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
fade_queued_dest_divmult_time[channel] = 0;
fade_dest_read_addr[channel] = loop_start[channel];
reset_loopled_tmr(channel);
}
}
//
// Sanity check: make sure read_addr and write_addr are spaced properly
//
if ((mode[channel][INF]==INF_OFF) && (read_fade_pos[channel] < global_param[SLOW_FADE_INCREMENT]) /*&& !mode[channel][CONTINUOUS_REVERSE]*/)
{
t32 = calculate_read_addr(channel, divmult_time[channel]);
if (t32 != read_addr[channel])
{
set_divmult_time(channel);
}
}
//For short periods (audio rate), disble crossfading before the end of the loop
if (divmult_time[channel] < (global_param[SLOW_FADE_SAMPLES]))
start_fade_addr = loop_end[channel];
else
start_fade_addr = offset_samples(channel, loop_end[channel], global_param[SLOW_FADE_SAMPLES] / SAMPLESIZE, 1-mode[channel][REV]);
// Read from memory into the main read buffer:
// This in/decrements the read_addr based on the REV mode,
// and also based on doing_reverse_fade (in which case the read_addr goes the opposite direction as REV mode would indicate)
// crossed_start_fade_addr is true if read addr crosses the end of the loop, in which case we need to reset it to the beginning of the loop.
// If doing_reverse_fade is true, then we should read in the opposite direction as mode[][REV] dictates (this is because we just
// reversed direction, so we should continue reading from rd_buff in the same direction (which is now !REV),
// and cross fade towards dest_rd_buff being read in the direction of REV
crossed_start_fade_addr = memory_read(read_addr, channel, rd_buff, sz/2, start_fade_addr, doing_reverse_fade[channel] /*| mode[channel][CONTINUOUS_REVERSE]*/);
if (mode[channel][INF]!=INF_OFF && crossed_start_fade_addr)
{
reset_loopled_tmr(channel);
if (divmult_time[channel] < (global_param[SLOW_FADE_SAMPLES]))
{
read_addr[channel]=loop_start[channel];
read_fade_pos[channel] = 0.0;
//Issue: is it necessary to set this below?
fade_dest_read_addr[channel] = offset_samples(channel, read_addr[channel], sz/SAMPLESIZE, 1-mode[channel][REV]);
if (mode[channel][INF]==INF_TRANSITIONING_OFF)
{
mode[channel][INF]=INF_OFF;
}
}
else
{
read_fade_pos[channel] = global_param[SLOW_FADE_INCREMENT];
//Issue: clearing a queued divmult time?
fade_queued_dest_divmult_time[channel]=0;
//Start fading from before the loop
//We have to add in sz because read_addr has already been incremented by sz since a block was just read
if (mode[channel][REV])
fade_dest_read_addr[channel] = offset_samples(channel, read_addr[channel], ((loop_start[channel]-loop_end[channel])+sz)/SAMPLESIZE, 0);
else
fade_dest_read_addr[channel] = offset_samples(channel, read_addr[channel], ((loop_end[channel]-loop_start[channel])+sz)/SAMPLESIZE, 1);
if (mode[channel][INF]==INF_TRANSITIONING_OFF)
{
mode[channel][INF]=INF_OFF;
}
}
}
memory_read(fade_dest_read_addr, channel, rd_buff_dest, sz/2, 0, 0 /* + mode[channel][CONTINUOUS_REVERSE]*/);
for (i=0;i<(sz/2);i++){
// Split incoming stereo audio into the two channels: Left=>Main input (clean), Right=>Aux Input
if (SAMPLESIZE==2){
mainin = (*src++) /*+ CODEC_ADC_CALIBRATION_DCOFFSET[channel+0]*/;
dummy=*src++;
auxin = (*src++) /*+ CODEC_ADC_CALIBRATION_DCOFFSET[channel+2]*/;
dummy=*src++;
}
else
{
topbyte = (uint16_t)(*src++);
bottombyte = (uint16_t)(*src++);
mainin = (topbyte << 16) + (uint16_t)bottombyte;
topbyte = (uint16_t)(*src++);
bottombyte = (uint16_t)(*src++);
auxin = (topbyte << 16) + (uint16_t)bottombyte;
}
if (mute_on_boot_ctr)
{
mute_on_boot_ctr--;
mainin=0;
auxin=0;
}
if (global_mode[AUTO_MUTE]){
//minimum 830ns, mean 2us
mainin_lpf[channel] = (mainin_lpf[channel]*(1.0-lpf_coef)) + (((mainin>0)?mainin:(-1*mainin))*lpf_coef);
//Initiate a fade-down
if (mainin_lpf[channel]<min_vol && (auto_muting_main_state[channel] == FADING_UP || auto_muting_main_state[channel]==UNMUTED))
auto_muting_main_state[channel] = FADING_DOWN;
//Initiate a fade-up
if (mainin_lpf[channel]>=min_vol && (auto_muting_main_state[channel] == FADING_DOWN || auto_muting_main_state[channel]==MUTED))
auto_muting_main_state[channel] = FADING_UP;
//Continue fades
if (auto_muting_main_state[channel] == FADING_DOWN)
auto_muting_main_fade[channel] -= AUTO_MUTE_DECAY;
else if (auto_muting_main_state[channel] == FADING_UP)
auto_muting_main_fade[channel] += AUTO_MUTE_ATTACK;
//Terminate fades
if (auto_muting_main_fade[channel] <= 0.0)
{
auto_muting_main_fade[channel] = 0.0;
auto_muting_main_state[channel] = MUTED;
}
else if (auto_muting_main_fade[channel] >= 1.0)
{
auto_muting_main_fade[channel] = 1.0;
auto_muting_main_state[channel] = UNMUTED;
}
//Apply mute or fade
if (auto_muting_main_state[channel] == MUTED)
mainin = 0;
else if (auto_muting_main_state[channel] != UNMUTED)
mainin = (float)mainin * auto_muting_main_fade[channel];
auxin_lpf[channel] = (auxin_lpf[channel]*(1.0-lpf_coef)) + (((auxin>0)?auxin:(-1*auxin))*lpf_coef);
//Initiate a fade-down
if (auxin_lpf[channel]<min_vol && (auto_muting_aux_state[channel] == FADING_UP || auto_muting_aux_state[channel]==UNMUTED))
auto_muting_aux_state[channel] = FADING_DOWN;
//Initiate a fade-up
if (auxin_lpf[channel]>=min_vol && (auto_muting_aux_state[channel] == FADING_DOWN || auto_muting_aux_state[channel]==MUTED))
auto_muting_aux_state[channel] = FADING_UP;
//Continue fades
if (auto_muting_aux_state[channel] == FADING_DOWN)
auto_muting_aux_fade[channel] -= AUTO_MUTE_DECAY;
else if (auto_muting_aux_state[channel] == FADING_UP)
auto_muting_aux_fade[channel] += AUTO_MUTE_ATTACK;
//Terminate fades
if (auto_muting_aux_fade[channel] <= 0.0)
{
auto_muting_aux_fade[channel] = 0.0;
auto_muting_aux_state[channel] = MUTED;
}
else if (auto_muting_aux_fade[channel] >= 1.0)
{
auto_muting_aux_fade[channel] = 1.0;
auto_muting_aux_state[channel] = UNMUTED;
}
//Apply mute or fade
if (auto_muting_aux_state[channel] == MUTED)
auxin = 0;
else if (auto_muting_aux_state[channel] != UNMUTED)
auxin = (float)auxin * auto_muting_aux_fade[channel];
}
// The Dry signal is just the clean signal, without any attenuation from LEVEL
dry = mainin;
// Read from the loop and save this value so we can output it to the Delay Out jack
t = (uint16_t)(4095.0 * read_fade_pos[channel]);
asm("usat %[dst], #12, %[src]" : [dst] "=r" (t) : [src] "r" (t));
rd=((float)rd_buff[i] * epp_lut[t]) + ((float)rd_buff_dest[i] * epp_lut[4095-t]);
if (global_mode[SOFTCLIP])
rd = compress(rd);
if (SAMPLESIZE==2)
asm("ssat %[dst], #16, %[src]" : [dst] "=r" (rd) : [src] "r" (rd));
// Attenuate the delayed signal with REGEN
regen = ((float)rd) * param[channel][REGEN];
// Attenuate the clean signal by the LEVEL parameter
mainin_atten = ((float)mainin) * param[channel][LEVEL];
if (mode[channel][SEND_RETURN_BEFORE_LOOP]) {
// Assign the auxin signal to the write head
wr = auxin;
// Add the loop contents to the input signal, and assign to the auxout signal
auxout = (int32_t)(regen + mainin_atten);
} else {
// Add the loop contents to the input signal, as well as the auxin signal, and assign to the write head
wr = (int32_t)(regen + mainin_atten + (float)auxin);
// Assign the non-attenuated loop contents to the auxout signal
auxout = rd;
}
//High-pass filter on wr
if (global_mode[RUNAWAYDC_BLOCK])
{
dcblock_lpf[channel] = (dcblock_lpf[channel]*(1.0-(1.0/4800.0))) + (wr*(1.0/4800.0));
wr = wr - dcblock_lpf[channel];
}
if (global_mode[SOFTCLIP])
wr = compress(wr);
else if (SAMPLESIZE==2)
asm("ssat %[dst], #16, %[src]" : [dst] "=r" (wr) : [src] "r" (wr));
// Wet/dry mix, as determined by the MIX parameter
mix = ( ((float)dry) * param[channel][MIX_DRY] ) + ( ((float)rd) * param[channel][MIX_WET] );
if (global_mode[SOFTCLIP])
mix = compress(mix);
else if (SAMPLESIZE==2)
asm("ssat %[dst], #16, %[src]" : [dst] "=r" (mix) : [src] "r" (mix));
if (global_mode[CALIBRATE])
{
*dst++ = CODEC_DAC_CALIBRATION_DCOFFSET[0+channel];
*dst++ = 0;
*dst++ = CODEC_DAC_CALIBRATION_DCOFFSET[2+channel];
*dst++ = 0;
}
else
{
#ifdef DEBUG_POTADC_TO_CODEC
*dst++ = potadc_buffer[channel+0]*4;
*dst++ = 0;
if (TIMESW_CH1==SWITCH_CENTER) *dst++ = potadc_buffer[channel+2]*4;
else if (TIMESW_CH1==SWITCH_UP) *dst++ = potadc_buffer[channel+4]*4;
else *dst++ = potadc_buffer[channel+6]*4;
*dst++ = 0;
#else
#ifdef DEBUG_CVADC_TO_CODEC
*dst++ = potadc_buffer[channel+2]*4;
*dst++ = 0;
if (TIMESW_CH1==SWITCH_CENTER) *dst++ = cvadc_buffer[channel+4]*4;
else if (TIMESW_CH1==SWITCH_UP) *dst++ = cvadc_buffer[channel+0]*4;
else *dst++ = cvadc_buffer[channel+2]*4;
*dst++ = 0;
#else
if (SAMPLESIZE==2){
//Main out
*dst++ = mix + CODEC_DAC_CALIBRATION_DCOFFSET[0+channel];
*dst++ = 0;
//Send out
*dst++ = auxout + CODEC_DAC_CALIBRATION_DCOFFSET[2+channel];
*dst++ = 0;
}
else
{
//Main out
*dst++ = (int16_t)(mix>>16) + (int16_t)CODEC_DAC_CALIBRATION_DCOFFSET[0+channel];
*dst++ = (int16_t)(mix & 0x0000FF00);
//Send out
*dst++ = (int16_t)(auxout>>16) + (int16_t)CODEC_DAC_CALIBRATION_DCOFFSET[2+channel];
*dst++ = (int16_t)(auxout & 0x0000FF00);
}
#endif
#endif
}
wr_buff[i]=wr;
}
//Write a block to memory
if (mode[channel][INF] == INF_OFF || mode[channel][INF]==INF_TRANSITIONING_OFF)
{
if (write_fade_state[channel] == WRITE_FADE_WRDOWN_DESTUP)
{
memory_fade_write(fade_dest_write_addr, channel, wr_buff, sz/2, 0, write_fade_pos[channel]);
memory_fade_write(write_addr, channel, wr_buff, sz/2, 1, 1.0-write_fade_pos[channel]); //write in the opposite direction of [REV]
}
else if (write_fade_state[channel] == WRITE_FADE_UP)
{
memory_fade_write(fade_dest_write_addr, channel, wr_buff, sz/2, 0, write_fade_pos[channel]);
write_addr[channel] = fade_dest_write_addr[channel];
}
else/* if (write_fade_pos[channel] < global_param[SLOW_FADE_INCREMENT])*/
{
memory_write(write_addr, channel, wr_buff, sz/2, 0);
fade_dest_write_addr[channel] = write_addr[channel];
}
}
else if (mode[channel][INF]==INF_TRANSITIONING_ON)
{
if (write_fade_state[channel]==WRITE_FADE_DOWN)
{
memory_fade_write(fade_dest_write_addr, channel, wr_buff, sz/2, 0, 1.0-write_fade_pos[channel]);
write_addr[channel] = fade_dest_write_addr[channel];
}
}
#ifdef ALLOW_CONT_REVERSE
if (mode[channel][CONTINUOUS_REVERSE])
{
if (abs_diff(write_addr[channel], read_addr[channel]) < 960) //10ms pulse
{
loop_led_state[channel]=1;
if (channel==0) CLKOUT1_ON;
else CLKOUT2_ON;
}
else
{
loop_led_state[channel]=0;
if (channel==0) CLKOUT1_OFF;
else CLKOUT2_OFF;
}
}
#endif
increment_read_fade(channel);
increment_write_fade(channel);
}