[Fpga-synth] Delay Line Theory
Scott Gravenhorst
music.maker at gte.net
Tue Aug 14 20:57:03 CEST 2007
Magnus Danielson <magnus at rubidium.dyndns.org> wrote:
>From: Scott Gravenhorst <music.maker at gte.net>
>Subject: [Fpga-synth] Delay Line Theory
>Date: Tue, 14 Aug 2007 10:27:40 -0700
>Message-ID: <200708141727.l7EHRbf5015596 at linux7.lan>
>
>> I've looked on the net for examples of delay line implementation using a
>> RAM, but there doesn't seem to be anything. I manufactured my own method
>> based on "clock math". The void of information leads me to one of two
>> conclusions - 1) it's as simple as I think it is - or 2) it's IP that
>> nobody wants to divulge because it's so cool.
>>
>> I just did a little experiment that I thought would show the error of my
>> ways, but it does not (I suspected my wrapping algorithm).
>>
>> This new little project doesn't use an arithmetic wrap algorithm. It makes
>> the length of the delay line the entire RAM address space. This allows me
>> to let the system simply wrap back to zero after the maximum address
>> instead of using arithmetic to compute when to wrap to address zero.
>>
>> I set up a pointer (ptr) that is the end point (for read) and start point
>> (for write) of the delay line. I also have a pickup offset value (PU) that
>> is added to ptr to access the location of the pickup.
>>
>> In a nutshell, the state machine first accesses and stores in a register
>> the pickup using ptr+PU as the RAM address (where binary wrapping will
>> happen). Next, the data pointed to by ptr is accessed to feed the input
>> register of the filter. At the same clock edge, the filter's output is
>> written to the location in RAM pointed to by ptr, storing the previous
>> cycle's IIR output in the RAM. Lastly, in it's own state, ptr is
>> incremented by one and allowed to simply overflow.
>>
>> To my understanding, this should result in a fixed length waveguide that
>> resonates when impulsed and where PU should access a location that is a
>> constant number of locations away from ptr (given that PU is not changing).
>> The output should then be the same frequency, regardless of the value of
>> PU as long as it is constant. I'd expect doppler _while_ PU is changing,
>> but after it's stopped changing the frequency should return to the natural
>> resonant frequency.
>>
>> Do I have this right? Because it surely isn't working like that. When I
>> increase the value of PU, the pitch rises and remains higher even after PU
>> stops changing.
>
>For each sample you want to do these operations:
>
>mem[wptr] = in
>rptr = wptr + PU
>out = mem[rptr]
>wptr = wptr + 1
As far as I understand Verilog, that is almost what I'm doing. There are actually two reads
and one write.
state1: rptr = wptr + PU // set address for reading pickup data
state2: out_for_pickup = mem[rptr] // get data for DAC
state3: out_for_IIR_in = mem[wrptr] // out_to_IIR_in is a register
state4: mem[wptr] = IIR_data // save IIR_data into RAM
state5: wptr = wptr + 1 // increment pointer
>This will give you correct delays as long as your delays are even integer
>multiples of the sample period and you are not chaning the delay (PU).
They are.
>Shifts in PU will naturally give rise to doppler-like effects.
So I understood.
>Your problem seems to be that you do rptr = rptr + PU or something.
Yeah, I know, but it's not there in the code. I've also simulated the state machine, the
arithmetic looks correct, everything wrapping to the correct value at the correct time and
no addresses being skipped.
>> Please someone tell me that my understanding of this is muddled...
>
>Yes, obviously! :-D
>
>No, it *IS* simple as long as you stick to delays being integer multiples of
>samples. If you attempt to break that, then you are in trouble.
Yes, I chose to avoid this by using a larger RAM and using only the musically accurate
portion. I'm getting 3.5 octaves right now, 4.9 cents worst case.
>Smooth shifts
>in delay require interpolation in delay, but interpolation in samples does not
>make a good sound, so there you need to work on it.
Right, and I know this is not supposed to be happening (which is why I tried to update ISE
to 9.2i thinking that 8.2i was messing up). I know that the sample rate is not changing, I
even made a test point to scope it. It seems like the design is not using a constant PU
value when it *is* constant. And the whole keyboard goes out of tune, each semitone higher
is more sharp than the preceding one. Octave keys no longer play octaves.
>If you use a delay in a feedback loop and then shift the delay, you are on your
>own!!!
I've looked for that in the design and I don't see how it could happen. All I'm doing is
using a pickup that is a constant number of delay times away from the read/write end point
of the delay line (represented by the "main" pointer). The offsetted pointer is used only
to read the pickup data to send to the DAC. Reading and writing for the filter is done with
the same "main" pointer to which a simple increment is applied.
-- ScottG
-------------------------------------------------------------
-- Scott Gravenhorst
-- GateMan I - Xilinx Spartan-3E Based MIDI Synthesizer
-- FatMan: home1.gte.net/res0658s/fatman/
-- NonFatMan: home1.gte.net/res0658s/electronics/
-- When the going gets tough, the tough use the command line.
More information about the Fpga-synth
mailing list