Shop OBEX P1 Docs P2 Docs Learn Events
DDS with an envelope scaling — Parallax Forums

DDS with an envelope scaling

dmwilson86dmwilson86 Posts: 27
edited 2011-04-20 04:56 in Propeller 1
I have been up all night working with PASM, writing to main memory, using the serial debugger, and trying to work out what I need to do, however, there are just things that are happening that I really don't understand. So I'll post what I have and what is happening, and also what I'm trying to do, and hopefully one of you geniuses can enlighten my soupy brain. This one might be a challenge but I'll be active on it all day, so here we go.

What I'm trying to do:
I have built a 25-key keyboard with resistive pressure sensors that go into 4 8-channel adcs, and are read into an array in main memory by the mcp3208-driver, that works great. I want to create the frequency of the key at an amplitude proportional to the pressure the key being hit.

Alright, so I started with this code from kuroneko, and tdlivings in order to have a continuously updated sine wave frequency. Got this to work well.
org      0
  entry
  '256 sampled 8-bit sine table. Replace with any sampled waveform you like
  'as long as the data table is of the same dimension and depth. Only longs are
  'used to make the DDS loop as short as possible in terms of clock cycles.
 
  Sine          long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE
                long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8
                long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5
                long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF
                long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7
                long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC
                long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3
                long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83
                long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51
                long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27
                long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A
                long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00
                long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08
                long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23
                long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C
                long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C
                mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
  :loop         rdlong   M_,par                  'Get Freq Control Value
                add      Acc,     M_             'M $1FF max if literal with #$xxx
                ror      Acc,     #16            'Postion index
                movs     :inline, Acc            'Store 9-bit index
                andn     :inline, #%1_00000000   'Clamp to 8-bits
                rol      Acc,     #16            'Restore accumulator
  :inline       mov      outa,    0-0            'Get long value from table
                jmp      #:loop                  'Loop again
  PinMsk        long     $FF                     '8-bit DAC, LSB on PA0, MSB on PA7  
  state         long     $100
  M_            long     0                       'Tuning word var init
  Acc           long     0                       'Accumulator var init
  AddrFreq      long     0                       'Address of Freq Control in Main Spin Code
Then, (trying to take baby steps) instead of movs of the Acc to :inline, I moved it to a local variable x so I could perform scaling operations on the value with:
                movs       :scale, Acc  
  :scale        movs     x, 0-0
             movs      :inline, x
I realize that I will have to do (probably pin) synchronization at the output with a counter since multiplication and division operation take variable amounts of time. I tried so many different ways to do this, including not even using dynamic source addressing and just going: mov outa, x....or using the literal for x in the movs: movs, :inline,#x.......but no acceptable output. Anytime I do get an output waveform, every other period is completely scrambled:
TEK00000.png
. So I don't understand why this is. I have to be able to copy the value from the wavetable, and manipulate it in order to do the scaling operations, and then be able to put out the result. But before I do that I just want to create the sine wave from values copied from the wavetable so that I know I can reliably operate on the waveform. I only need to be able to produce frequencies up to about 2kHz, so I'm pretty sure this is doable. Here is the entire DAT file I'm working with, there is some debugging code commented out.
DAT
                         org      0
  entry
  '256 sampled 8-bit sine table. Replace with any sampled waveform you like
  'as long as the data table is of the same dimension and depth. Only longs are
  'used to make the DDS loop as short as possible in terms of clock cycles.
 
  Sine          long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE
                long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8
                long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5
                long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF
                long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7
                long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC
                long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3
                long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83
                long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51
                long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27
                long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A
                long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00
                long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08
                long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23
                long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C
                long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C
                {{mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
                mov      fPtr, par
                add      t1, fPtr
                add      t1, #48
                mov      envPtr, t1
                
  :loop         rdlong   M1_, fPtr                  'Get Freq Control Value      
                add      Acc1,     M1_  wz           'M $1FF max if literal with #$xxx 
                ror      Acc1,     #16            'Postion index  
                movs     :setMult, Acc1            
                rdlong   x, envPtr   
  :setMult      mov      y, 0-0                                 
                call     #multiply                                        
                mov      y, #0100                  'dividing must be done b/c no floating point
                call     #divide 
                movs     :inline, x   
                andn     :inline, #%1_00000000   'Clamp to 8-bits
                rol      Acc1,     #16            'Restore accumulator  
  :inline       mov      outa,    0-0            'Get long value from table
                jmp      #:loop                  'Loop again                'Loop again }}
   
                
                mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
  :loop         rdlong   M_,par                  'Get Freq Control Value
                add      Acc,     M_             'M $1FF max if literal with #$xxx
                ror      Acc,     #16            'Postion index
              '  movs     :inline, Acc            'Store 9-bit index
                movs       :scale, Acc  
  :scale        movs     x, 0-0
             movs      :inline, x
             '   mov      y,#2
             '   call      #divide  
                mov       t1, par
                add       t1, #96   
                wrlong    x, t1    
                andn     :inline, #%1_00000000   'Clamp to 8-bits  
                rol      Acc,     #16            'Restore accumulator
  :inline       mov      outa,    0-0            'Get long value from table
                djnz     count, #:loop
   jmp      #:loop                  'Loop again

   
                'ret

  '' Multiply x[15..0] by y[15..0] (y[31..16] must be 0)
  ' on exit, product in y[31..0]
  '
  mult         shl x,#16 'get multiplicand into x[31..16]
               mov t1,#16 'ready for 16 multiplier bits
               shr y,#1 wc 'get initial multiplier bit into c
  :loop        if_c add y,x wc 'if c set, add multiplicand to product
               rcr y,#1 wc 'put next multiplier in c, shift prod.
               djnz t1,#:loop 'loop until done
  mult_ret ret 'return with product in y[31..0]       

  ' Divide x[31..0] by y[15..0] (y[16] must be 0)
  ' on exit, quotient is in x[15..0] and remainder is in x[31..16]
  divide       shl y,#15 'get divisor into y[30..15]
               mov t1,#16 'ready for 16 quotient bits
  :loop        cmpsub x,y wc 'y =< x? Subtract it, quotient bit in c
               rcl x,#1 'rotate c into quotient, shift dividend
               djnz t1,#:loop 'loop until done
  divide_ret   ret 'quotient in x[15..0]
       
  PinMsk       long     $FF                     '8-bit DAC, LSB on PA0, MSB on PA7      
  t1           long     0
  x            long     0
  y            long     0   
  M_          long     0                       'Tuning word var init      
  Acc         long     0                       'Accumulator var init   
  fPtr         long     0                       'Address of Freq Control in Main Spin
  envPtr       long     0                                             
  count   long 256
Please help! I can't wait to get this working!
640 x 480 - 34K

Comments

  • AleAle Posts: 2,363
    edited 2011-04-19 07:41
    Remember that between modifying an instruction and its execution you have to have at least one other opcode because of pre-fetch.
                  movs     :scale, Acc  
                  nop
      :scale      movs     x, 0-0
                  movs     :inline, x
    
  • tdlivingstdlivings Posts: 437
    edited 2011-04-19 07:43
    On my first cup of coffee after watching the Wings playoff game last night but one thing I see is the three
    movs instructions in a row. The manual says you need at least one instruction between a movs and the
    use of the target. The :scale sequence for sure looks like it needs a nop added between.

    Tom
  • dmwilson86dmwilson86 Posts: 27
    edited 2011-04-19 08:01
    tdlivings wrote: »
    On my first cup of coffee after watching the Wings playoff game last night but one thing I see is the three
    movs instructions in a row. The manual says you need at least one instruction between a movs and the
    use of the target. The :scale sequence for sure looks like it needs a nop added between.

    Tom

    Thanks for responding guys....that makes sense. Tried it out and I still seem to be having this scrambling issue:
    TEK00001.png
              
                    mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
      :loop         rdlong   M_,par                  'Get Freq Control Value
                    add      Acc,     M_             'M $1FF max if literal with #$xxx
                    ror      Acc,     #16            'Postion index
                  '  movs     :inline, Acc            'Store 9-bit index
                    movs       :scale, Acc
                    nop
      :scale        mov     x, 0-0  
                    movs    :inline, x  
                 '   mov      y,#2
                 '   call      #divide  
                    'mov       t1, par
                    'add       t1, #96   
                    'wrlong    count, t1    
                    andn     :inline, #%1_00000000   'Clamp to 8-bits  
                    rol      Acc,     #16            'Restore accumulator
      :inline       mov      outa,    0-0           'Get long value from table    
                    jmp      #:loop                  'Loop again
    
    I just can't think of what's causing it, it seems like an issue with Acc, since it's happening once every (what would be) 2 periods.
    640 x 480 - 33K
  • dmwilson86dmwilson86 Posts: 27
    edited 2011-04-19 08:04
    Actually it's not exactly once every 2 periods, the scramble is about 3/4 of the period....even more perplexing.
  • AleAle Posts: 2,363
    edited 2011-04-19 08:45
    You are not re-initializing count after the first run :)

    Edit: Wait, no that's not the problem... let me see again... (I'm using a simulator :) )

    Edit2: Nonono, the problem is when you do the second movs because you allow x to be loaded with any value in the 0..511 range instead of 0.255 where your waveform resides... (the andn should be for the instruction at scale and not at inline as the waveform has values in the 0..255 range).
  • dmwilson86dmwilson86 Posts: 27
    edited 2011-04-19 09:24
    Ale wrote: »
    Edit2: Nonono, the problem is when you do the second movs because you allow x to be loaded with any value in the 0..511 range instead of 0.255 where your waveform resides... (the andn should be for the instruction at scale and not at inline as the waveform has values in the 0..255 range).
    Okay, that makes sense.....and works. Here is the new code. I was able to get rid of the: 'mov :inline, x'......which was causing a wierd waveform anyways, and output x directly: 'mov outa, x'...here is the updated code:
    mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
      :loop         rdlong   M_,par                  'Get Freq Control Value
                    add      Acc,     M_             'M $1FF max if literal with #$xxx
                    ror      Acc,     #16            'Postion index         
                    movs       :scale, Acc                         
                    andn     :scale, #%1_00000000   'Clamp to 8-bits
                    nop
      :scale        mov     x, 0-0       
                 '   mov      y,#2
                 '  call      #divide  
                    mov       t1, par
                    add       t1, #96   
                    wrlong    x, t1
                                                                  
                    rol      Acc,     #16            'Restore accumulator
                   ' waitpeq  timerState, state
                    'waitpne  timerState, state
      :inline       mov      outa, x           'Get long value from table    
                    jmp      #:loop                  'Loop again
    
    THANKS ALE. I will keep this thread going throughout the day if I have any other major runins.
  • dmwilson86dmwilson86 Posts: 27
    edited 2011-04-19 12:46
    Alright, I'm back with major issue number 2, the actual scaling of the output amplitude. To me, it seems like it would just entail a simple multiply and divide, but this isn't so true, I guess, when floating point operations are not being used. So then I think....well, I'll just shift the numerator left, then divide, then shift it back right again (I might get drilled for that, but I'm ready for it). I guess the problem is the no floating point math thing.....how do you get around that???

    Here is my code:
    DAT
                             org      0
      entry
      '256 sampled 8-bit sine table. Replace with any sampled waveform you like
      'as long as the data table is of the same dimension and depth. Only longs are
      'used to make the DDS loop as short as possible in terms of clock cycles.
     
      Sine          long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE
                    long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8
                    long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5
                    long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF
                    long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7
                    long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC
                    long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3
                    long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83
                    long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51
                    long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27
                    long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A
                    long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00
                    long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08
                    long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23
                    long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C
                    long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C
    
                    {{mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
                    mov      fPtr, par
                    add      t1, fPtr
                    add      t1, #48
                    mov      envPtr, t1
                    
      :loop         rdlong   M1_, fPtr                  'Get Freq Control Value      
                    add      Acc1,     M1_  wz           'M $1FF max if literal with #$xxx 
                    ror      Acc1,     #16            'Postion index  
                    movs     :setMult, Acc1            
                    rdlong   x, envPtr   
      :setMult      mov      y, 0-0                                 
                    call     #multiply                                        
                    mov      y, #0100                  'dividing must be done b/c no floating point
                    call     #divide 
                    movs     :inline, x   
                    andn     :inline, #%1_00000000   'Clamp to 8-bits
                    rol      Acc1,     #16            'Restore accumulator  
      :inline       mov      outa,    0-0            'Get long value from table
                    jmp      #:loop                  'Loop again                'Loop again }}
       
                    mov      t1,par
                    add      t1, 96
                    mov      envPtr, t1
                    mov      t1,par
                    add      t1, 24
                    mov      pressPtr, t1
                    mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
      :loop         rdlong   M_,par                  'Get Freq Control Value
                    add      Acc,     M_             'M $1FF max if literal with #$xxx 
                    ror      Acc,     #16            'Postion index   
                    rdlong   y, pressPtr            
                    movs       :val, Acc                         
                    andn     :val, #%1_00000000   'Clamp to 8-bits              
                    rol      Acc,     #16            'Restore accumulator
      :val          mov     x, 0-0
                    mov     t2,x              'store sine value for subtraction after factoring
                    shl     x,#2              'shift left for multiplication and division
                    call    #mult            'multiply
                    
                    'wrlong   y,envPtr
                    'ret
                    mov     x,y               'move mult output into x
                    mov     y,pScale          'setup y to divide x by 4095
                    call #divide              'do it
                    shr   x, #2               'shift it back over
                    andn     x, #%1_00000000   'Clamp to 8-bits
                   waitpeq  timerState, state  'to evenly space loops in time
                   waitpne  timerState, state   '  ^^same
                       
      :inline       mov      outa, x           'move value out    
                    jmp      #:loop                  'Loop again
    
       
                    'ret
    
      '' Multiply x[15..0] by y[15..0] (y[31..16] must be 0)
      ' on exit, product in y[31..0]
      '
      mult         shl x,#16 'get multiplicand into x[31..16]
                   mov t1,#16 'ready for 16 multiplier bits
                   shr y,#1 wc 'get initial multiplier bit into c
      :loop        if_c add y,x wc 'if c set, add multiplicand to product
                   rcr y,#1 wc 'put next multiplier in c, shift prod.
                   djnz t1,#:loop 'loop until done
      mult_ret ret 'return with product in y[31..0]       
    
      ' Divide x[31..0] by y[15..0] (y[16] must be 0)
      ' on exit, quotient is in x[15..0] and remainder is in x[31..16]
      divide       shl y,#15 'get divisor into y[30..15]
                   mov t1,#16 'ready for 16 quotient bits
      :loop        cmpsub x,y wc 'y =< x? Subtract it, quotient bit in c
                   rcl x,#1 'rotate c into quotient, shift dividend
                   djnz t1,#:loop 'loop until done
      divide_ret   ret 'quotient in x[15..0]
           
      PinMsk       long     $FF                     '8-bit DAC, LSB on PA0, MSB on PA7
      timerState   long     $100
      state        long     $100
      pScale       long     4095  
      t1           long     0
      t2           long     0
      x            long     0
      y            long     0   
      M_          long     0                       'Tuning word var init      
      Acc         long     0                       'Accumulator var init   
      fPtr         long     0                       'Address of Freq Control in Main Spin
      pressPtr     long     0
      envPtr       long     0
      envState     long     0
    
    I'm only using the envPtr to report values back to the hub for serial debugging.

    I'm trying to do this:
    SineValue = (SineValue - (pressVal/4095)*SineValue)
  • AribaAriba Posts: 2,690
    edited 2011-04-19 17:34
    dmwilson86 wrote: »
    I'm trying to do this:
    SineValue = (SineValue - (pressVal/4095)*SineValue)

    What you do wrong is: You multiply the index into the sine table and not the value read from the sine table.

    There are other issues in your code, so I took the initial Sine DDS code and added the modulation of the amplitude by my own. I think this makes it more clear than modifying your last posted code.

    This is as simple as possible, but not perfect. It calculates:
    SineValue = pressVal * SineValue / 4096
    If you want the loudest tone if presVal is 0 then calculate it like that: presVal := 4095 - ADCvalue

    Instead of a division by 4095 you can divide by 4096 and that is a simple right shift by 12.

    The multiplication is not optimized, you need only a 8bit * 16bit mult which then would be two times faster.
    And the zero point of the output signal is not in the middle (value 128) instead the signal goes to value 0 with the EnvValue=0. To make it right you need to calculate:
    SineValue = (SineValue - 128) * pressVal / 4096 + 128
    with the used sine table, but for that you need a signed multiplication, which makes it more complicated.

    Here is the code:
    VAR
      long       FreqVal, EnvVal
    '            ^par  
    DAT
                  org      0
    entry
    '256 sampled 8-bit sine table. Replace with any sampled waveform you like
    'as long as the data table is of the same dimension and depth. Only longs are
    'used to make the DDS loop as short as possible in terms of clock cycles.
    
    Sine         long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE
                 long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8
                 long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5
                 long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF
                 long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7
                 long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC
                 long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3
                 long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83
                 long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51
                 long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27
                 long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A
                 long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00
                 long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08
                 long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23
                 long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C
                 long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C
    
                 mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
                 mov      EnvPtr,  par
                 add      EnvPtr,  #4             'calc EnvAddr
    :loop        rdlong   frq,par                 'Get Freq Control Value
                 add      Acc,     frq            'DDS accumulate
                 mov      t1,      Acc
                 shr      t1,      #24            'use highest 8 bits as pointer
                 movs     :inline, t1             ' into sine table
                 nop
    :inline      mov      x,       0-0            'Get value from table (8 bit)
    
                 rdlong   y,       EnvPtr         'Get Env value (12 bit)
                 call     #mult                   'sineVal * env
                 shr      y,       #12            'divide by 4096
                 mov      outa,    y              'write to DAC at P0..P7
    
                 jmp      #:loop                  'Loop again
    
    '' Multiply x[15..0] by y[15..0] (y[31..16] must be 0)
    ' on exit, product in y[31..0]
    '
    mult         shl  x,#16          'get multiplicand into x[31..16]
                 mov  t1,#16         'ready for 16 multiplier bits
                 shr  y,#1 wc        'get initial multiplier bit into c
    :loop  if_c  add  y,x  wc        'if c set, add multiplicand to product
                 rcr  y,#1 wc        'put next multiplier in c, shift prod.
                 djnz t1,#:loop      'loop until done
    mult_ret     ret                 'return with product in y[31..0]       
    
    
    PinMsk       long     $FF                     '8-bit DAC, LSB on PA0, MSB on PA7  
    state        long     $100
    frq          long     0                       'Tuning word var init
    Acc          long     0                       'Accumulator var init
    AddrFreq     long     0                       'Address of Freq Control in Main Spin Code
    EnvPtr       long     0                       'Address of EnvValue (12bit)
    x            long     0
    y            long     0
    t1           long     0
    

    Andy
  • dmwilson86dmwilson86 Posts: 27
    edited 2011-04-19 18:16
    Ariba wrote: »
    What you do wrong is: You multiply the index into the sine table and not the value read from the sine table.
    There are other issues in your code, so I took the initial Sine DDS code and added the modulation of the amplitude by my own. I think this makes it more clear than modifying your last posted code.
    Wow! that worked great! I've been rackin my brain over that for days. I really appreciate your help on that...it sounds great. Can you give me an idea of where I was going wrong?
  • AribaAriba Posts: 2,690
    edited 2011-04-19 19:02
    You first calculate the DDS and get the current phase angle in Acc. Then you need to read the current sine value from the table with this phase index and multiply it with the Env value. But you multiply the phase angle with the envelope value, which results in a distorted phase and sine curve. (This can be used to produce very interessting sounds, if you know what you do - it's the principle behind FM and PD synthesis - but you don't are after that, I think).

    I see now that the "other issues" I mentoined are in a commented out section of the code, so I think the above is the main error.

    Andy
  • dmwilson86dmwilson86 Posts: 27
    edited 2011-04-19 22:34
    I tried duplicating the code in order to mix signals on the dac in a 'round robin fasion' (kuroneko). I get some pretty interesting stuff, but not the clean signal I was hoping for. There seem to be lots of frequencies that are produced when I'm trying to do this.

    I have all the frequencies and envelopes in being set in a 6-long array in main memory. For now I'm just trying to mix 2 signals (the rest is commented out). I tried setting z, and checking for that when outputting the signal....and I'm pretty sure that 'should' work for 2 channels, but I don't think it would work properly for any more than that because if more than one, but less than all channels were being played, then the channels being played would have unequal amounts of time at the dac. I'm up for suggestions. Thanks again to Arriba for the envelope solution.

    CON
    
       _clkmode = xtal1 + pll16x                      '5MHz * 16PLL = 80MHz system clock.
      _xinfreq = 5_000_000                          '5MHz external crystal.
    VAR
      long  cog                                      'cog id         
    PUB start(freqPtr) : okay                              'M is the tuning word
      stop                                                                                      
      okay := cog := cognew(@entry, freqPtr)      'Start DDS, uses a cog.
    PUB stop
      if cog                                         'If cog is running
        cogstop(cog~)                                'Stop DDS, frees a cog 
    DAT
                org      0
    entry
    '256 sampled 8-bit sine table. Replace with any sampled waveform you like
    'as long as the data table is of the same dimension and depth. Only longs are
    'used to make the DDS loop as short as possible in terms of clock cycles.
    
    Sine         long $80,$83,$86,$89,$8C,$8F,$92,$95,$98,$9C,$9F,$A2,$A5,$A8,$AB,$AE
                 long $B0,$B3,$B6,$B9,$BC,$BF,$C1,$C4,$C7,$C9,$CC,$CE,$D1,$D3,$D5,$D8
                 long $DA,$DC,$DE,$E0,$E2,$E4,$E6,$E8,$EA,$EC,$ED,$EF,$F0,$F2,$F3,$F5
                 long $F6,$F7,$F8,$F9,$FA,$FB,$FC,$FC,$FD,$FE,$FE,$FF,$FF,$FF,$FF,$FF
                 long $FF,$FF,$FF,$FF,$FF,$FF,$FE,$FE,$FD,$FC,$FC,$FB,$FA,$F9,$F8,$F7
                 long $F6,$F5,$F3,$F2,$F0,$EF,$ED,$EC,$EA,$E8,$E6,$E4,$E2,$E0,$DE,$DC
                 long $DA,$D8,$D5,$D3,$D1,$CE,$CC,$C9,$C7,$C4,$C1,$BF,$BC,$B9,$B6,$B3
                 long $B0,$AE,$AB,$A8,$A5,$A2,$9F,$9C,$98,$95,$92,$8F,$8C,$89,$86,$83
                 long $80,$7C,$79,$76,$73,$70,$6D,$6A,$67,$63,$60,$5D,$5A,$57,$54,$51
                 long $4F,$4C,$49,$46,$43,$40,$3E,$3B,$38,$36,$33,$31,$2E,$2C,$2A,$27
                 long $25,$23,$21,$1F,$1D,$1B,$19,$17,$15,$13,$12,$10,$0F,$0D,$0C,$0A
                 long $09,$08,$07,$06,$05,$04,$03,$03,$02,$01,$01,$00,$00,$00,$00,$00
                 long $00,$00,$00,$00,$00,$00,$01,$01,$02,$03,$03,$04,$05,$06,$07,$08
                 long $09,$0A,$0C,$0D,$0F,$10,$12,$13,$15,$17,$19,$1B,$1D,$1F,$21,$23
                 long $25,$27,$2A,$2C,$2E,$31,$33,$36,$38,$3B,$3E,$40,$43,$46,$49,$4C
                 long $4F,$51,$54,$57,$5A,$5D,$60,$63,$67,$6A,$6D,$70,$73,$76,$79,$7C
    
                 mov      dira,    PinMsk         'LSB to PA0, LSB to PA7
                 mov      EnvPtr1,  par
                 add      EnvPtr1,  #24             'calc EnvAddr           
                 mov      EnvPtr2,  par
                 add      EnvPtr2,  #28             'calc EnvAddr
                 mov      EnvPtr3,  par
                 add      EnvPtr3,  #32             'calc EnvAddr
                 mov      EnvPtr4,  par
                 add      EnvPtr4,  #36             'calc EnvAddr
                 mov      EnvPtr5,  par
                 add      EnvPtr5,  #40             'calc EnvAddr
                 mov      EnvPtr6,  par
                 add      EnvPtr6,  #44             'calc EnvAddr
                 mov      frq2,par
                 add      frq2,#4 
                 mov      frq3,par
                 add      frq3,#8 
                 mov      frq4,par
                 add      frq4,#12
                 mov      frq5,par
                 add      frq5,#16
                 mov      frq6,par
                 add      frq6,#20   
                 
    :loop        rdlong   frq1,par          wz        'Get Freq Control Value
                 add      Acc1,     frq1           'DDS accumulate
                 mov      t1,      Acc1
                 shr      t1,      #24            'use highest 8 bits as pointer
                 movs     :inline1, t1            ' into sine table
                 nop
    :inline1     mov      x,       0-0          'Get value from table (8 bit)           
                 rdlong   y,       EnvPtr1         'Get Env value (12 bit)
                 call     #mult                   'sineVal * env
                 shr      y,       #12            'divide by 4096
          if_nz  mov      outa,    y              'write to DAC at P0..P7   
                 
                 rdlong   TW2,frq2         wz         'Get Freq Control Value
                 add      Acc2,     TW2            'DDS accumulate
                 mov      t1,      Acc2
                 shr      t1,      #24            'use highest 8 bits as pointer
                 movs     :inline2, t1            ' into sine table
                 nop
    :inline2     mov      x,       0-0         'Get value from table (8 bit)           
                 rdlong   y,       EnvPtr2         'Get Env value (12 bit)
                 call     #mult                   'sineVal * env
                 shr      y,       #12            'divide by 4096
          if_nz  mov      outa,    y              'write to DAC at P0..P7  
                   {{
                 rdlong   TW3,frq3                 'Get Freq Control Value
                 add      Acc3,     TW3            'DDS accumulate
                 mov      t1,      Acc3
                 shr      t1,      #24            'use highest 8 bits as pointer
                 movs     :inline3, t1             ' into sine table
                 nop
    :inline3     mov      x,       0-0            'Get value from table (8 bit)           
                 rdlong   y,       EnvPtr3         'Get Env value (12 bit)
                 call     #mult                   'sineVal * env
                 shr      y,       #12            'divide by 4096
                 mov      outa,    y              'write to DAC at P0..P7
                 
                 rdlong   TW4, frq4                 'Get Freq Control Value
                 add      Acc4,     TW4            'DDS accumulate
                 mov      t1,      Acc4
                 shr      t1,      #24            'use highest 8 bits as pointer
                 movs     :inline4, t1             ' into sine table
                 nop
    :inline4     mov      x,       0-0            'Get value from table (8 bit)           
                 rdlong   y,       EnvPtr4         'Get Env value (12 bit)
                 call     #mult                   'sineVal * env
                 shr      y,       #12            'divide by 4096
                 mov      outa,    y              'write to DAC at P0..P7
                 
                 rdlong   TW5, frq5                 'Get Freq Control Value
                 add      Acc5,     TW5            'DDS accumulate
                 mov      t1,      Acc5
                 shr      t1,      #24            'use highest 8 bits as pointer
                 movs     :inline5, t1             ' into sine table
                 nop
    :inline5     mov      x,       0-0            'Get value from table (8 bit)           
                 rdlong   y,       EnvPtr5         'Get Env value (12 bit)
                 call     #mult                   'sineVal * env
                 shr      y,       #12            'divide by 4096
                 mov      outa,    y              'write to DAC at P0..P7
                 
                rdlong    TW6,frq6                 'Get Freq Control Value
                 add      Acc6,     TW6            'DDS accumulate
                 mov      t1,      Acc6
                 shr      t1,      #24            'use highest 8 bits as pointer
                 movs     :inline6, t1             ' into sine table
                 nop
    :inline6     mov      x,       0-0            'Get value from table (8 bit)           
                 rdlong   y,       EnvPtr6         'Get Env value (12 bit)
                 call     #mult                   'sineVal * env
                 shr      y,       #12            'divide by 4096
                 mov      outa,    y              'write to DAC at P0..P7  
                                   }}          
    
                 jmp      #:loop                  'Loop again
                                                        
    
    '' Multiply x[15..0] by y[15..0] (y[31..16] must be 0)
    ' on exit, product in y[31..0]
    mult         shl  x,#16          'get multiplicand into x[31..16]
                 mov  t1,#16         'ready for 16 multiplier bits
                 shr  y,#1 wc        'get initial multiplier bit into c
    :loop  if_c  add  y,x  wc        'if c set, add multiplicand to product
                 rcr  y,#1 wc        'put next multiplier in c, shift prod.
                 djnz t1,#:loop      'loop until done
    mult_ret     ret                 'return with product in y[31..0]       
    
    
    PinMsk        long     $FF                
    state         long     $100
    frq1          long     0                       'Tuning word var init
    Acc1          long     0                       'Accumulator var init                       
    EnvPtr1       long     0                       'Address of EnvValue (12bit)
    TW2           long     0
    frq2          long     0                       'Tuning word var init
    Acc2          long     0                       'Accumulator var init                       
    EnvPtr2       long     0                       'Address of EnvValue (12bit)
    frq3          long     0                       'Tuning word var init
    Acc3          long     0                       'Accumulator var init
    TW3           long     0            
    EnvPtr3       long     0                       'Address of EnvValue (12bit)
    frq4          long     0                       'Tuning word var init
    Acc4          long     0                       'Accumulator var init
    TW4           long     0            
    EnvPtr4       long     0                       'Address of EnvValue (12bit)
    frq5          long     0                       'Tuning word var init
    Acc5          long     0                       'Accumulator var init 
    EnvPtr5       long     0                       'Address of EnvValue (12bit) 
    TW5           long     0           
    frq6          long     0                       'Tuning word var init
    Acc6          long     0                       'Accumulator var init                       
    EnvPtr6       long     0                       'Address of EnvValue (12bit)
    TW6           long     0
    x             long     0
    y             long     0
    t1            long     0     
    

    And here is a sample picture of the output when 2 keys are pressed simultaneously:
    TEK00004.png
    The waveform is perfect when only one key is pressed.
    640 x 480 - 47K
  • kuronekokuroneko Posts: 3,623
    edited 2011-04-19 23:04
    dmwilson86 wrote: »
    I tried duplicating the code in order to mix signals on the dac in a 'round robin fasion' (kuroneko).
    Not entirely sure about my involvement here. I always wondered about your approach of sending interleaved waveforms. Shouldn't that be mixed properly, i.e. add them all up, scale down and then output a single value?
  • Heater.Heater. Posts: 21,230
    edited 2011-04-19 23:25
    Kuroneko is right. In your :loop you are supposed to be generating a single sample value that is the sum of all your scaled sine wave samples.
    Instead you have a write to outa for each scaled sine wave component.
    The result is that the output is "chopping" from the value of one sine wave frequency to another, twice per loop, we can see this in your scope trace.
    So perhaps instead of "mov outa, y" you should "add output, y" for each frequency component and then "mov outa, output" at the end of the loop.
    Don't forget to zero output afterwards.

    P.S. Don't forget to scale down the totalled output value as it may well have exceeded 8 bits. So for two frequencies shift right by one, for four frequencies shift right by two etc.
  • dmwilson86dmwilson86 Posts: 27
    edited 2011-04-20 04:56
    kuroneko wrote: »
    Not entirely sure about my involvement here. I always wondered about your approach of sending interleaved waveforms. Shouldn't that be mixed properly, i.e. add them all up, scale down and then output a single value?

    Oh, I was just noting that you introduced me to the idea of interleaving.....I just initially thought that's what you meant, I should have dug deaper on that...but that makes much more sense. Thanks, I will try that out asap.
Sign In or Register to comment.