;***************************************************************
              ; 
              ; Stereo chorus for the DSP56001 signal processor.
              ; Developed by Quinn Jensen
              ; 
              ; This program fragment implements a stereo "chorus" effect
              ; on a DSP56001 processor.  Chorus adds depth and warmth to
              ; sound by creating the illusion that more instruments
              ; are involved in the sound than really are.  It does this by
              ; mixing together a delayed version of the input with the input
              ; itself.  This program uses the following signal flow.
              ;
              ;
              ;  Left in ------+------- "dry" gain -----------> sum -----> Left out
              ;                |                                 ^
              ;                v                                 |
              ;               sum --> delay ---> "wet" gain -----+
              ;                ^                                 |
              ;                |                                 v -
              ;  Right in -----+------- "dry" gain -----------> sum -----> Right out
              ;
              ;
              ; Note that the delay line output is negated before summing with the
              ; right input signal.  This throws in 180 degrees of phase shift
              ; making for interesting results even with mono inputs 
              ; (i.e. Left in == Right in).
              ; 
              ; Chorus uses a delay time of between about 10 and 50ms in some commercial
              ; units.  This program can be configured for longer delays.
              ; In the chorus effect, the delay time is slowly varied, adding a very
              ; subtle pitch shift.  The depth and speed of the delay-time modulation
              ; are adjustable to taste.  The greater the depth or speed, the greater
              ; the coloration of the signal.
              ;
              ; Variations in this algorithm are possible and encouraged.  I came
              ; up with this code after studying the impulse response and characteristics
              ; of a commercial stereo chorus pedal and reading various articles in magazines
              ; and on usenet over the years.  My somewhat dry TX81 synthesizer sounds pretty
              ; good with this and other effects I run on the 56001.
              ;
              ; A recent article with a pretty good not-too-technical overview of chorus 
              ; and other effects:  Gary Hall, "From the Top: Effects, the Essential Musical
              ; Spice," _Electronic Musician_, August 1991, pp. 62-68.
              ;
              ; I would enjoy seeing any improvements to the code.
              ;
              include "tdsg.a56"      ;hardware specific initialization code
              
              ;***************************************************************
              ;
              ;       Data and constants
              ;
              ;***************************************************************
              
P:0076        dot                             ;remember where we were in P-space
X:0020                org     x:$20           ;put runtime variables in on-chip X-space
              
              ; A spreadsheet was used to calculate the following numbers
              
              ; Sample rate              32.5500 kc         
              ; 
              ; Delay time (4-52)        28.0000 ms           delay time knob
              ; Depth (1-10)             10.0000              depth knob (I like it deep)
              ; Speed (1-10)              0.0000              speed knob (I like it slow)
              ; 
              ; max depth +/-            24.0000 ms         
              ; min delay                 4.0000 ms         
              ; max delay                52.0000 ms         
              ; 1/2 cycle period          5.0000 s          
              ; samples per 1/2 cyc  162750.0000
              ; time delta/samp           0.2949 us         
              ; offset samp/samp          0.0096
              ; 
FFFF7E          doff_i equ                              -130  ;initial delay offset (tap)
    0.0096      ddeltaf equ                           0.0096  ;delta-delay, per sample
027BBE          dspeed_i equ                          162750  ;number of samples per
                                                              ;half cycle of triangle wave
                                                              ;delay-time modulator
              ; 
              ; Delay time (ms)              tap                   tap     delay
              ; 
              ;                   1      32.5500                     1      0.03
              ;                   2      65.1000                     2      0.06
              ;                   4     130.2000                     4      0.12
              ;                   5     162.7500                     8      0.25
              ;                   8     260.4000                    16      0.49
              ;                  10     325.5000                    32      0.98
              ;                  20     651.0000                    64      1.97
              ;                  25     813.7500                   128      3.93
              ;                  40    1302.0000                   256      7.86
              ;                  50    1627.5000                   512     15.73
              ;                  60    1953.0000                  1024     31.46
              ;                  70    2278.5000                  2048     62.92
              ;                  80    2604.0000                  4096    125.84
              ;                  90    2929.5000                  8192    251.67
              ;                 100    3255.0000                 16384    503.35
              
              ;
              ;       The delay line is in off-chip X memory
              ;
002000        delay   equ     $2000
001000        dmax    equ     4096            ;125 ms (probably way too long)
              ;
              ; doff and ddelta are 48-bit quantities
              ;
X:0020 FFFF7E doff    dc      doff_i          ;current delay distance
Y:0020                org     y:doff
Y:0020 000000         dc      0
              
X:0021                org     x:doff+1
X:0021 000000 ddelta  dc      0               ;delta delay per sample
Y:0021                org     y:ddelta
Y:0021 013A93         dc      ddeltaf
              
X:0022                org     x:ddelta+1
              
X:0022 027BBE dspeed  dc      dspeed_i        ;samples per half cycle of triangle modulator
X:0023 000000 dtoggle dc      0               ;current sample count
X:0024        delayout
X:0024 000000         dc      0               ;current delay-line output
              
Y:0000                org     y:$0
              
P:0076                org     p:dot           ;go back to P-space
              ;***************************************************************
              ;
              ; Initialization code
              ;
              ;***************************************************************
P:0076        hf_init
P:0076 61F400         move    #delay,r1                       ;delay line input
P:0077 002000
P:0078 05F421         movec   #dmax-1,m1                      ;
P:0079 000FFF
P:007A 71F400         move    #doff_i,n1                      ;distance to output
P:007B FFFF7E
P:007C 00000C         rts
              ;
              ;***************************************************************
              ;
              ;       Sample-rate computations.  Call chorus_compute at
              ;       interrupt time when both left and right inputs are
              ;       ready.
              ;
              ;       fs = 32.552083 kHz
              ;
              ;       x:<in_l         left-channel input
              ;       x:<in_r         right-channel input
              ;       x:<out_l        left-channel output
              ;       x:<out_r        right-channel output
              ;
              ;***************************************************************
              
P:007D        hf_comp
P:007D 0D0066         jsr     <saveregs
              ;
              ;       output and input mix
              ;
P:007E 253813         clr     a       #.4375,x1               ;clr a, get input scale
P:007F 27401B         clr     b       #.5,y1                  ;clr b, get output scaler
              
P:0080 46A400         move            x:<delayout,y0          ;get delay out
              
P:0081 4480BB         macr    y0,y1,b x:<in_l,x0              ;b = .5 * delay, x0=in_l
P:0082 21E6CB         macr    x0,y1,b         b,y0            ;b += .5 * in_l, y0=b
P:0083 5704A3         macr    x0,x1,a b,x:<out_l              ;a = x1 * in_l, L = b
P:0084 20CF00         move                    y0,b            ;b = -y0
P:0085 44813E         neg     b       x:<in_r,x0              ;x0 = in_r
P:0086 2000CB         macr    x0,y1,b                         ;b += .5 * in_r
P:0087 5705A3         macr    x0,x1,a b,x:<out_r              ;R = b, a += x1 * in_r
              ;
              ;       delay line in
              ;
P:0088 565900         move    a,x:(r1)+
              ;
              ;       delay line length modulation.  A simple triangle-wave modulator
              ;       is used.  A sine-wave modulator would be much better, especially
              ;       with deeper and/or faster settings.
              ;
P:0089 48A000         move            l:<doff,a               ;a = current offset
P:008A 42A100         move            l:<ddelta,x             ;x = current delta
P:008B 200020         add     x,a
P:008C 482000         move            a,l:<doff               ;new offset = a + x
P:008D 219900         move            a1,n1
              
P:008E 20AF00         move            x1,b                    ;save delta for later use
              ;
              ; smoothly transition between delay-line offsets by
              ; interpolating the current sample with the previous or next
              ; one depending on whether the delay is currently getting longer or
              ; shorter.  Otherwise, an obnoxious click results when the offset snaps
              ; to the next integral value.  This does have a low-pass effect on the
              ; delayed signal path but it is not objectionable.
              ;
P:008F 5EA000         move                    y:<doff,a       ;compute |frac(doff)|
P:0090 44F423         lsr     a       #$800000,x0
P:0091 800000
P:0092 254042         or      x0,a    #.5,x1
P:0093 218400         move            a1,x0
P:0094 2000A4         mpy     -x0,x1,a
P:0095 21C60B         tst     b               a,y0            ;y0 = 0.5 * |frac(doff)|
P:0096 0AF0A3         jpl     shorter                         ;on positive delta, the
P:0097 0000A1
                                                              ;delay is shortening
              
P:0098 205100         move            (r1)-                   ;get previous sample
P:0099 44E900         move            x:(r1+n1),x0
P:009A 2F40D0         mpy     y0,x0,a #.5,b                   ;scale
P:009B 20595C         sub     y0,b    (r1)+                   ;compute scale for cur sample
P:009C 21E600         move                    b,y0
P:009D 44E900         move            x:(r1+n1),x0            ;get cur sample
P:009E 2000D2         mac     x0,y0,a                         ;scale and sum
P:009F 0AF080         jmp     endpan
P:00A0 0000A7
              
P:00A1        shorter
P:00A1 44E900         move            x:(r1+n1),x0            ;get cur sample
P:00A2 2F40D0         mpy     y0,x0,a #.5,b                   ;scale
P:00A3 20595C         sub     y0,b    (r1)+                   ;compute scale for next sample
P:00A4 21E600         move                    b,y0
P:00A5 44E900         move            x:(r1+n1),x0            ;get next sample
P:00A6 2051D2         mac     x0,y0,a (r1)-                   ;scale and sum
P:00A7        endpan
P:00A7 562400         move            a,x:<delayout           ;store resulting output
              ;
              ; update the triangle wave modulation (sinewave would be better)
              ;
P:00A8 56A300         move            x:<dtoggle,a            ;decrement toggle count
P:00A9 44F400         move            #>1,x0
P:00AA 000001
P:00AB 200044         sub     x0,a
P:00AC 562300         move            a,x:<dtoggle
P:00AD 0AF0A7         jgt     notogg                          ;time to toggle?
P:00AE 0000B3
P:00AF 44A200         move            x:<dspeed,x0            ;yes, negate delta and reset
P:00B0 48A100         move            l:<ddelta,a
P:00B1 442336         neg     a       x0,x:<dtoggle
P:00B2 482100         move            a,l:<ddelta
P:00B3        notogg
P:00B3        bypass
P:00B3 0D006F         jsr     <restregs
P:00B4 00000C         rts
              
                      end

Summary of psect usage

                 section seg base last top      used       avail    total
-------------------------------------------------------------------------


Symbol Table
-------------------------------------
          ddelta 000021
         ddeltaf 0.0096000000
         ssi_int 000061
            ssix 000010
          notogg 0000B3
        delayout 000024
           delay 002000
     init_stereo 000059
           start 000040
           in_rs 000003
           in_ls 000002
            in_r 000001
            in_l 000000
            dmax 001000
          endpan 0000A7
            doff 000020
          doff_i FFFF7E
             dot 000076
        mainloop 000060
         hf_comp 00007D
         hf_init 000076
         dtoggle 000023
        saveregs 000066
           savey 00000B
           savex 00000A
          saveb2 000009
         saveb10 000008
          savea2 000007
         savea10 000006
          dspeed 000022
        dspeed_i 027BBE
        restregs 00006F
           out_r 000005
           out_l 000004
          bypass 0000B3
         shorter 0000A1
          m_scl1 00000F
          m_scl0 00000E
           m_scl 00C000
          m_ssl1 00000D
          m_ssl0 00000C
           m_ssl 003000
          m_hpl1 00000B
          m_hpl0 00000A
           m_hpl 000C00
          m_ibl2 000005
          m_ibl1 000004
          m_ibl0 000003
           m_ibl 000038
          m_ial2 000002
          m_ial1 000001
          m_ial0 000000
           m_ial 000007
           m_ipr 00FFFF
           m_rdf 000007
           m_tde 000006
           m_roe 000005
           m_tue 000004
           m_rfs 000003
           m_tfs 000002
           m_if1 000001
           m_if0 000000
            m_if 000002
          m_srie 00000F
          m_stie 00000E
           m_sre 00000D
           m_ste 00000C
           m_mod 00000B
           m_gck 00000A
           m_syn 000009
           m_fsl 000008
          m_sckd 000005
          m_scd2 000004
          m_scd1 000003
          m_scd0 000002
           m_scd 00001C
           m_of1 000001
           m_of0 000000
            m_of 000003
           m_psr 00000F
           m_wl1 00000E
           m_wl0 00000D
            m_wl 006000
            m_dc 001F00
            m_pm 0000FF
           m_tsr 00FFEE
            m_sr 00FFEE
           m_crb 00FFED
           m_cra 00FFEC
            m_tx 00FFEF
            m_rx 00FFEF
           m_tcm 00000F
           m_rcm 00000E
           m_scp 00000D
           m_cod 00000C
            m_cd 000FFF
            m_r8 000007
            m_fe 000006
            m_pe 000005
            m_or 000004
          m_idle 000003
          m_rdrf 000002
          m_tdre 000001
          m_trne 000000
          m_tmie 00000D
           m_tie 00000C
           m_rie 00000B
          m_ilie 00000A
            m_te 000009
            m_re 000008
          m_woms 000007
           m_rwi 000006
          m_wake 000005
           m_sbk 000004
          m_wds2 000002
          m_wds1 000001
          m_wds0 000000
           m_wds 000003
          m_sccr 00FFF2
           m_ssr 00FFF1
           m_scr 00FFF0
          m_stxa 00FFF3
          m_stxh 00FFF6
          m_stxm 00FFF5
          m_stxl 00FFF4
          m_srxh 00FFF6
          m_srxm 00FFF5
          m_srxl 00FFF4
           m_dma 000007
           m_hf1 000004
           m_hf0 000003
            m_hf 000018
           m_hcp 000002
          m_htde 000001
          m_hrdf 000000
           m_hf3 000004
           m_hf2 000003
          m_hcie 000002
          m_htie 000001
          m_hrie 000000
           m_htx 00FFEB
           m_hrx 00FFEB
           m_hsr 00FFE9
           m_hcr 00FFE8
           m_pcd 00FFE5
         m_pcddr 00FFE3
           m_pcc 00FFE1
           m_pbd 00FFE4
         m_pbddr 00FFE2
           m_pbc 00FFE0
           m_bcr 00FFFE
errors=0