/* *********************************************************************************************** * STEREO_CHORUS_FEEDBACK.ASM - stereo chorusing effect simulating 3 voices/musical instruments * * (SPORT1 Rx ISR count & update method - delay calculation determined by a counter * * incremented in the serial port's audio processing routine) * * * * Chorus Effect as Described by: * * 1. Jon Dattorro in "Effect Design Part 2 - Delay-Line Modulation and Chorus," * * J. Audio Eng. Society, Vol. 45, No. 10, October 1997 * * * * 2. Eq(8.2.20) of Introduction to Signal Processing. * * By Sophocles J. Orfanidis - 1996, Prentice-Hall * * ISBN 0-13-209172-0 * * * * This version uses Linear Interpolation (versus Allpass Interpolation) along with * * integer (not fractional) sample delay. Since the sample rate is 48K, for most * * lower bandwidth signals, Linear Interpolation is probably adequate for most instruments. * * * * I/O equations: * * yL(n) = 1.0 * x(n) + 0.7071*x(n - d1(n)) - 0.7071*x(n - D1/2) * * yR(n) = 1.0 * x(n) + 0.7071*x(n - d2(n)) - 0.7071*x(n - D2/2) * * * * x(n) -----O------------------------------------------|>-------->O--------> yL(n) * * ^ - | ^ 0.7071 ^ * * | | / | * * | | _________/_____ | * * | | | | Rotating Tap | * * | | | Z^(-D1) | Center | * * | |----------->| |-----------|>--| * * | | d1(n) | 0.7071 * * | | | * * | |_______________| * * | / | * * | / | Fixed Tap * * | / | Center * * | feedback1 | D1 / 2 * * |-------------<|---------------| * * | * * O------------------------------------------|>-------->O--------> yR(n) * * | - | ^ 0.7071 ^ * * | | / | * * | | _________/_____ | * * | | | | Rotating Tap | * * | | | Z^(-D2) | Center | * * | |----------->| |-----------|>--| * * | | d2(n) | 0.7071 * * | | | * * | |_______________| * * | / | * * | / | Fixed Tap * * | / | Center * * | feedback2 | D2 / 2 * * |-------------<|---------------| * * * * * * * * What the Chorusing Effect does? * * Chorusing simulates the effect of multiple instruments/voices playing the same musical * * arrangement at the same time. In actual concert situations, musicians are usually * * synchronized together, except for small variations in amplitude and timing. * * This effect is achieved by allowing the time delay (and also amplitude) to vary randomly * * or sinusoidally in time by using a random number generation routine or sine wavetable. * * * * * * For each input sample, the sample processing algorithm does the following: * * store input sample s0 to 2 delay lines * * modify wavetable(when necessary) * * generated variable delay, d = D * (0.5 + randnum(fc*t)) * * s1 = sample1 = tap(D, w1, p1, d) * * s2 = sample2 = tap(D/2) * * y = a0 * s0 + a1 * s1 - af * s2 * * * * Developed for the 21065L EZ-LAB Evaluation Board, Ported to the ADSP-21161 EZ-KIT LITE * * * * * **************************************************************************************************/ /* ADSP-21060 System Register bit definitions */ #include "def21161.h" #include .GLOBAL chorus_effect; .GLOBAL Init_Chorus_Buffers; .EXTERN Left_Channel; .EXTERN Right_Channel; .EXTERN Left_Channel_Out0; .EXTERN Right_Channel_Out0; .EXTERN Left_Channel_In1; .EXTERN Right_Channel_In1; // playing with the feedback, modulation rate, and width size affects // the intensity (or comb filtering) of the chorus'ed sound. // increasing depth size increases chorus pitch variations /* Chorus Control Parameters */ #define a0L 0x7FFFFFFF /* a0 = 0.99999999, left input gain */ #define a0R 0x7FFFFFFF /* a0 = 0.99999999, right input gain */ //#define a1L 0x5A82799A /* a1 = 0.707106781, left output gain of tapped delay line */ //#define a1R 0x5A82799A /* a1 = 0.707106781, left output gain of tapped delay line */ //#define afL 0x5A82799A /* a0 = 0.707106781, negative feedback gain */ //#define afR 0x5A82799A /* a0 = 0.707106781, negative feedback gain */ /* = 0xA57D8666 for positive feedback, - 0.707106781 */ #define a1R 0x4A82799A #define a1L 0x4A82799A #define afL 0x2A82799A #define afR 0x2A82799A //#define D1 870 /* Depth, or TD = D1/fs = 870/48000 = 18 msec */ //#define D2 1120 /* Depth, or TD = D2/fs = 1120/48000 = 23 msec */ #define D1 1356 #define D2 1789 #define DepthL D1 /* DepthL is equivalent to time delay of signal, or d-line 1 size */ #define DepthR D2 /* DepthR is equivalent to time delay of signal, or d-line 2 size */ #define L_Del_TapCenter DepthL/2 /* D1/2, used for add & subtracting delay from nominal tap center */ #define R_Del_TapCenter DepthR/2 /* D2/2, used for add & subtracting delay from nominal tap center */ #define WaveSize 4000 /* semi-random/sinusoidal wavetable size*/ /* chorus control parameters */ #define c 1 /* wavetable signal freq fc1 = c1*(update time)*WaveSize = 4 Hz */ #define chorus_width -2 /* Enter # from 1 to 31. Do not enter 0, to keep #'s +/- 0.5 */ #define modulation_rate 40 /* Update wavetable pointer for delay calc every 40 interrupts */ /* sine wavetable has a rate of modulation of about 0.15 Hz */ /* playing with the width, modulation rate, depth size and feedback affects the intensity of the chorus'ed sound */ .section /dm dmchorus; //.var IRQ2_counter = 0x00000004; //.var DRY_GAIN = 0x7FFFFFFF; //.var WET_GAIN = 0x7FFFFFFF; /* For inverted phase, set to 0x80000000 */ .var Chorus_Left_Ptr, Chorus_Right_Ptr, random1_ptr, random2_ptr; .var inputL, inputR; .var feedback_gainL = afL; .var feedback_gainR = afR; .var sweep_rate = modulation_rate; .var sweep_widthL = chorus_width; /* controls width of left sweep, ranges from -1 to - 15 */ .var sweep_widthR = chorus_width; /* controls width of right sweep, ranges from -1 to - 15 */ .var wL[DepthL + 1]; /* delay-line buffer 1, max delay = D1 */ .var wR[DepthR + 1]; /* delay-line buffer 2, max delay = D2 */ .var excursion_valueL; /* wavetable offset value for delay calculation */ .var excursion_valueR; /* wavetable offset value for delay calculation */ .var random[WaveSize]="sinetbl.dat"; /* store one period of the wavetable */ /* minimum frequency of table = (freq of delay update)/WaveSize = 8/4 = 2 Hz */ .var wavetbl_counter = 0x00000000; .VAR IRQ0_counter = 0x00000004; /* selects preset 1 on first IRQ0 assertion */ .VAR FLAG0IRQ0_counter = 0x00000004; /* selects preset 1 on first IRQ0 with FLAG0 button assertion */ /* -------------PROGRAM MEMORY CODE---------------------------------*/ .section /pm seg_pmco; Init_Chorus_Buffers: B4 = wL; L4 = @wL; /* left delay-line buffer pointer and length */ DM(Chorus_Left_Ptr) = B4; m4 = 1; LCNTR = L4; /* clear left delay line buffer to zero */ DO clrDlineL UNTIL LCE; clrDlineL: dm(i4, m4) = 0; B4 = wR; L4 = @wR; /* right delay-line buffer pointer and length */ DM(Chorus_Right_Ptr) = B4; m4 = 1; LCNTR = L4; /* clear right delay line buffer to zero */ DO clrDlineR UNTIL LCE; clrDlineR: dm(i4, m4) = 0; B4 = random; /* left channel pointer for signal generator */ DM(random1_ptr) = B4; L4 = @random; /* get size of table lookup */ B4 = random; /* right channel pointer for signal generator */ I4 = random + 1000; /* offset 90 degrees so modulators in quadradure phase */ DM(random2_ptr) = I4; RTS; Init_Chorus_Buffers.end: /* //////////////////////////////////////////////////////////////////////////////////////////// */ /* */ /* Digital Chorus Routine - process both channels the left and right channels */ /* */ /* //////////////////////////////////////////////////////////////////////////////////////////// */ chorus_effect: /* combine both left and right input samples together into 1 signal */ r0 = dm(Left_Channel); /* left input sample */ r1 = dm(Right_Channel); /* right input sample */ // r0 = ashift r0 by -1; /* scale signal by 1/2 for equal mix */ // r1 = ashift r1 by -1; /* scale signal by 1/2 for equal mix */ // r12 = r0 + r1; /* 1/2xL(n) + 1/2 xR(n) */ DM(inputL) = r0; DM(inputR) = r1; test_wav_update: /* update sine value from lookup table? Update every 80 SPORT rx interrupts */ /* sweep frequency = 80 * c * 4000 / fs = 96000 /48k = .15 sec */ r1 = DM(sweep_rate); /* count up to 80 interrupts (default) */ r0 = DM(wavetbl_counter); /* get last count from memory */ r0 = r0 + 1; /* increment preset */ comp (r0, r1); /* compare current count to max count */ if ge r0 = r0 - r0; /* if count equals max, reset to zero and start over */ DM(wavetbl_counter) = r0; /* save updated count */ r12 = pass r0; /* test for wave count 0? */ if eq jump update_wavetbl_ptrs; /* if you are here, reuse same random values for now */ jump do_stereo_chorus; /* if necessary, calculate updated pointer to wavetables */ update_wavetbl_ptrs: B4 = random; L4 = @random; I4 = DM(random1_ptr); m4 = c; /* desired increment c - frequency f = c x fs / D */ r1 = DepthL; /* Total Delay Time */ r2 = dm(i4, m4); /* get next value in wavetable */ DM(random1_ptr) = I4; r4 = dm(sweep_widthL); r2 = ashift r2 by r4; /* divide by at least 2 to keep 1.31 #s between 0.5 and -0.5 */ r4 = r1 * r2 (SSFR); /* multiply Delay 1 by a fractional value from 0 to 0.5 */ dm(excursion_valueL) = r4; I4 = DM(random2_ptr); m4 = c; /* desired increment c - frequency f = c x fs / D */ r1 = DepthR; /* Total Delay Time */ r2 = dm(i4, m4); /* get next value in wavetable */ DM(random2_ptr) = I4; r4 = dm(sweep_widthR); r2 = ashift r2 by r4; /* divide by at least 2 to keep 1.31 #s between 0.5 and -0.5 */ r4 = r1 * r2 (SSFR); /* multiply Delay 1 by a fractional value from 0 to 0.5 */ dm(excursion_valueR) = r4; do_stereo_chorus: r4 = dm(excursion_valueL); /* calculate time-varing delay for 2nd voice */ r1 = L_Del_TapCenter; /* center tap for delay line */ r4 = r1 + r4; /* r4 = d(n) = D1/2 + D1 * random(fc*t) */ process_left_ch: B4 = wL; L4 = @wL; I4 = DM(Chorus_Left_Ptr); /* r4 now will be used to set m2 register to fetch time varying delayed sample */ m4 = r4; /* tap outputs of circular delay line */ modify(i4, m4); /* go to delayed sample */ r4 = -r4; /* negate to get back to where we were */ m4 = r4; /* used to post modify back to current sample */ r1 = dm(i4, m4); /* get time-varying delayed sample 1 */ /* r8 will be used to get Nominal Tap Center delayed sample for flange feedback */ m4 = L_Del_TapCenter; /* tap outputs of circular delay line */ modify(i4, m4); /* go to delayed sample */ m4 = -L_Del_TapCenter; /* negate to get back to where we were */ r0 = dm(i4, m4); /* get delayed sample, postmodify back to current sample */ /* crank out difference equation */ r12 = DM(inputL); r8 = a0L; /* left input gain */ mrf = r8 * r12 (SSF); /* mrf = a0L * x-input */ r8 = dm(feedback_gainL); /* left gain for feedback of nominal tap center*/ mrf = mrf - r8 * r0 (SSF); /* mrf = a0L * x - afL * sNominalL */ r12 = mr1f; /* save for input to chorus left delay line */ r8 = a1L; /* left delay line output gain */ mrf = mrf + r8 * r1 (SSFR); /* mrf = a0L * xL + a1L * s1L - afL * sNominalL */ mrf = SAT mrf; /* saturate result if necessary */ r2 = mr1f; /* chorus result in r10 */ /* put 'input minus feedback' sample from r12 into tap-0 of delay line */ /* and backshift circular delay-line buffer pointer */ dm(i4, -1) = r12; DM(Chorus_Left_Ptr) = I4; /* write chorus'ed output sample to left output channel */ dm(Left_Channel) = r2; /* left output sample */ process_right_ch: B4 = wL; L4 = @wL; I4 = DM(Chorus_Right_Ptr); r4 = dm(excursion_valueR); /* calculate time-varing delay for 2nd voice */ r1 = R_Del_TapCenter; /* center tap for delay line */ r4 = r1 + r4; /* r4 = d(n) = D2/2 + D2 * random(fc*t) */ /* r4 now will be used to set m3 register to fetch time varying delayed sample */ m4 = r4; /* tap outputs of circular delay line */ modify(i4, m4); /* go to delayed sample */ r4 = -r4; /* negate to get back to where we were */ m4 = r4; /* used to post modify back to current sample */ r1 = dm(i4, m4); /* get time-varying delayed sample 1 */ /* r8 will be used to get Nominal Tap Center delayed sample for chorus feedback */ m4 = R_Del_TapCenter; /* tap outputs of circular delay line */ modify(i4, m4); /* go to delayed sample */ m4 = -R_Del_TapCenter; /* negate to get back to where we were */ r0 = dm(i4, m4); /* get delayed sample, postmodify back to current sample */ /* crank out difference equation */ r12 = DM(inputR); r8 = a0R; /* right input gain */ mrf = r8 * r12 (SSF); /* mrf = a0R * x-input */ r8 = dm(feedback_gainR); /* gain for feedback of nominal tap center*/ mrf = mrf - r8 * r0 (SSF); /* mrf = a0R * xR - afR * sNominalR */ r12 = mr1f; /* save for input to chorus right delay line */ r8 = a1R; /* right delay line output gain */ mrf = mrf + r8 * r1 (SSFR); /* mrf = a0R * x + a1R * s1R - af * sNominalR */ mrf = SAT mrf; /* saturate result if necessary */ r2 = mr1f; /* chorus result in r10 */ /* put 'input minus feedback' sample from r12 into tap-0 of delay line */ /* and backshift circular delay-line buffer pointer */ dm(i4, -1) = r12; DM(Chorus_Right_Ptr) = I4; /* write chorus'ed output sample to right output channel */ dm(Right_Channel) = r2; /* right output sample */ RTS; chorus_effect.end: