New! 4ch delay plug-in... Delay 4D!

Discussion in 'Effects and the DSP' started by Maddogg6, Jan 17, 2006.

  1. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    Its a SURROUND panning delay.

    Ok - so its not all mine - I TOTALLY RIPPED this off of Max M -

    I just added 2 more delay taps and 2 more outputs.

    (Im MUCH more efficient 'hacking' than writing.. hehe - sorry max, I hope you dont mind :S )

    Anyway, I commented out the changes I made..
    Hell, I'm VERY suprised this works lol
    Not really sure why it works, but I seen a pattern and just replicated the logical parts.. ??

    Or? - did I screw it up...

    Code:
     ; Generated by kX DSP Editor - microcode dump
    name "Delay 4D";
    copyright "(c) Mark Davis 2006 - Based on work by: (c)Max Mikhailov, 2003-2005";
    ; NOTE The present DSP microcode dump is protected by the 
    ; license agreement bundled with the appropriate software 
    ; package containing this microcode,
    ; regardless the particular copyright notice is present in the dump.
    
    comment "mono->4 ch panning delay";
    	guid "e7e71636-0fb8-4a02-98d3-af3940cf36dc";
    	; -- generated GUID
    
    
    ; itramsize 0
    xtramsize 64002
    ; Registers
    	input in1, in2;
    	output out1, out2, out3, out4;
    	control dry=0x0, wet=0x7fffffff, feedback=0x547ae147;
    	control time=0x7fffffff, panning=0x2a3d70a3;
    	static maxdlysize=0x3e80000;
    	temp q, z;
    ; External TRAM delay line (64002 samples; ~1.333375 sec)
    	xdelay write d1 at 0x0;
    	xdelay read d1r at 0x3e80; added/ changed 1st tap point
    	xdelay read d2r at 0x7d00; original 1st tap point
    	xdelay read d3r at 0xbb80; added / 3rd/intermediate tap point
    	xdelay read d4r at 0xfa00; original 2nd tap point.
    
    ; Code
    	 ;macsn 	 z,  0x0,  d2r,  feedback;
    	 macsn 	  z, 0x0, d4r, feedback;  changed this to get 4th instead of 2nd point
    	 
    	 ;interp 	 q,  d1r,  panning,  z;
    	 interp 	 q,  d3r,  panning,  z; 
    	 macs 	 0x0,  0x0,  q,  wet;
    	 macs 	 out1,  accum,  in1,  dry;
    	 
    	 ;interp 	 q,  z,  panning,  d1r;
    	 interp 	 q,  z,  panning,  d3r;
    	 macs 	 0x0,  0x0,  q,  wet;
    	 macs 	 out2,  accum,  in2,  dry;
    	 
    	 ;interp 	 q,  z,  panning,  d1r;
    	 interp 	 q,  z,  panning,  d2r;
    	 macs 	 0x0,  0x0,  q,  wet;
    	 macs 	 out3,  accum,  in1,  dry;	 
    	 
    		  ;interp 	 q,  z,  panning,  d1r;
    	 interp 	 q,  z,  panning,  d1r;
    	 macs 	 0x0,  0x0,  q,  wet;
    	 macs 	 out4,  accum,  in2,  dry;
    	 
    	 
    	 interp 	 0x0,  in1,  0x40000000,  in2;
    	 macs 	 d1,  accum,  z,  feedback;
    	 macs 	 &d4r,  &d3r,  maxdlysize,  time; added 4th taps 'difusion filter '??    
    	 macs 	 &d3r,  &d2r,  maxdlysize,  time; added 3rd taps 'diffusion filter' ??
    	 macs 	 &d2r,  &d1r,  maxdlysize,  time; existing
    	 macs 	 &d1r,  &d1,  maxdlysize,  time;
    
    end
     
    Last edited: Jan 17, 2006
  2. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    After just a quick lookover:

    I am not sure why you changed the 1st instruction from a MACSN to a MACS...

    You cut the original delays in half, but did not change the maxdlysize to reflect that, so your read points are going to be all wrong. You should either double the size of the tram (and set your initial read points to match), or adjust the maxdlysize to match the new delay sizes.
    maxdlysize = delay_size * 0x800 (32000 * 0x800 = 0x3E80000 for the orginal delays)

    You do not have seperate controls for both stereo channels, so I would guess that the 2 stereo channels will be the same, in which case you could have just copied the front to the back. You might try adding a seperate control for the rear panning/balance, and/or a front/rear fader control (and I am not sure you need the extra taps in the first place, without having a seperate delay time control for the rear channels).
     
    Last edited: Jan 17, 2006
  3. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    The macsn -> macs - was a typo - hehe, it still 'functioned' as surround delay.
    Didnt this mistake 'just invert the 4th tap'?

    Ok I admit , Im still completely lost...

    But,
    Id think... if I kept the xtram size and re-used same tap points, but add intermediate tap points (0x3e80 & 0xbb80) eliminated the pointer problems you described.. ??
    Whats the 'effect' of the pointer error, I mean, dane doent give error as is - would the delays be inconsistant if pointers were off... ? Cuz so far its sounds consistant.

    Ill look at more delays and see what I can learn... LOL

    Im just happy it sounded like surround delay.. hehe
     
  4. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    Well basically you made a delay, with 4 read pointers (every 16000 samples), but the code to dynamically change the read pointers is using a size of 32000 samples.

    Now lets say that you set the TIME to full.
    Your 1st read pointer will be a 32000 (32000 past the write pointer (0)).
    Your 2nd read pointer will be a 64000 (32000 past the 1st read pointer).
    Now your 3rd and 4th read pointer will be beyond the size of the TRAM.

    Basically any time setting > 50% will have problems.

    This is not the type of error Dane would catch because it is happening dynamically.

    BTW: With the actual code you are using, it may be even worse, because the read pointers are changed in reverse order.

    Again with a TIME setting of full.
    Your 4th and 3rd read pointers are again beyond the size of the TRAM.
    Your 2nd read pointer is 48000 (32000 past the initial 1st read pointer (16000)).
    Your 1st read pointer is at 32000 (32000 past the write pointer (0).
    That makes the delay times different for the 1st and 2nd read pointer. (the first one is 32000 samples, but the second is only 16000 samples later).
     
    Last edited: Jan 17, 2006
  5. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    Ok - I see the panning control doesnt act as expected...

    So I set that as static and removed the control

    It does sound cool when set to 50%
     
    Last edited: Jan 17, 2006
  6. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    Max's code isn't always the best to try and learn from for people just starting out. He knows the instructions very well, and is very efficient with his coding. This can make it difficult to figure out exactly what the code is doing.
     
  7. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    ahh - so

    Optimized code = confusion...

    hmmm this notion sux... lol
     
  8. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    Well it is not just the the optimized code, I mean you could understand exactly what some code is doing and still not understand why it does what it does.

    In any case, what did you want it to do?
    i.e.
    Did you want the front to be a copy of the back?
    Did you want it to ping-pong around the room?
     
  9. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    Well, I didnt have any real expectations of the out come...
    I read 'Jump in and do stuff' is best way to learn - so, I find the highest diving board, on the windiest day, at the smallest pool I can find, on the most crowded day.... and just dive in...

    But thats just my M.O. -lol

    I guess... I did assume that the pattern would be 'Z' shaped - based on how the originals behavior was - its how it sounds too -

    But - the rears sound first - then the fronts more delayed.

    Sounds cool with midi solo instruments.

    So yes, I understand - the algo disctates the 'pattern' of delays...

    I just figured -'Hey, theres no surround delays'

    And started toying around ... its kinda fun and frustrating at the same time -

    Just was playing around to get the 'rears' in on all the action.
     
  10. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    Thats ok, you learned something, right?

    I did some of the same things when I was figuring this stuff out.
    i.e.
    I figured out how to modulate the delay lines by reading the ask10k1 manual, and looking at code for the 'Delay A' plugin IIRC. If you haven't looked at the as10k1 manual as of yet, you should, as it does contain some information that is not available elsewhere.
     
    Last edited: Jan 18, 2006
  11. radiocolonel.it

    radiocolonel.it New Member

    Joined:
    Jan 16, 2005
    Messages:
    192
    Likes Received:
    0
    Trophy Points:
    0
    That's right Max is kinda "artist" of dane, and as he says it depends on knowledge and a bit of magician...
     
    Last edited: Jan 18, 2006
  12. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    @Russ:
    Yeah.. Im learning alot - that formula>dane thing that tril explained was like THE most enlightening thing thus far tho. I think it was my first brickwall - understanding how formulas translate into dane.

    Im still looking at the examples and trying to understand how delays work.

    I did print out the 10K1 manual - I also got the fx8010 one too.
    They start making more sense, going back to them now...


    @Radiocolonel:
    Its true - Max seems like quite the magician .. hehe - definitly something to aspire too.

    Also - Ive been using your stereo widener for my 'TV watching' dsp config.
    Er - I think its yours anyway - soo many plugins its hard to keep track - [*looking at plugin*] no credits but name 'ColHQ WIDENER' makes me think its yours hehe..

    anyway - I like it... But was wondering what 'formula' you used - is it an 'interleaving comb filter' ?
     
  13. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    Delay lines are actually pretty easy (once you know the specifics), but you should start with simple delay lines (i.e. a simple delay or a simple echo, without the extras).

    The only real trick (which is not in the guide, but is explained in the as10k1 manual), is updating the read/write pointers to change the delay time on the fly (from a slider or whatever).

    Delay time (number of seconds) = (size of delay line (in samples) -1 ) / 48000).
    The TRAM starting index is at 0, so the read pointer would always be 1 less than the delay size.
    i.e.
    A 1 sec delay line, would have a size of 48001 samples, and the read pointer (the write pointer is at 0) would be at 48000.

    For 2 delay lines (for stereo or whatever) of 1 sec each, you would have a size of 96002. The first read and write pointer would be the same as above. The second write pointer would be at 48001, and the second read pointer would be at 96001.

    The '&' symbol you see in Max's code means "address of", and that is what you use to change the address of the read pointer.
    i.e.
    macs &d1r, &d1, maxdlysize, time;

    That means: the address of the first read pointer will be changed to: (the address of the write pointer) + (( the size of the delay line * 0x800) * (percentage of the full delay))

    The 0x800 thing is explained in the as10k1 manual.
    For both examples above, the delay lines are 48000 samples, so the maxdlysize would equal 0x5DC0000 (48000 * 0x800).

    If you search the forum the are some examples of simple delays that you could use for reference.
     
    Last edited: Jan 18, 2006
  14. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    Ok - I'll need to time to ingest this... :S


    BUT the '&d1l' - so this is like 'indirect addressing' type of thing in other assemblers then?

    in other words

    I have a variable; its Called 'APPLES'; Its contents is '27'; This is stored in 0x0XXXXX

    So using the '&APPLES = 0x0YYYYYY' - Im changing the LOCATION this variable is stored in. Not the CONTENTS of the variable - APPLES still = 27.

    right? - if so it makes alot of sense..
     
  15. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    You are close, but not exactly right.
    It is indirect addressing, but it is more like this:
    You have an array of apples each with its own value and own address (in contiguous memory).
    Now lets say we have an APPLE variable whose address is the address of one of the apples in the APPLES array, whose value is 27.
    Using '&APPLE = 0x0YYYYYY' would change the address of the APPLE variable, such that it points to a different apple in the APPLES array. It does not change the value of the first apple, but rather allows us to use the same variable to get the value of a different apple in the array.

    Does that make sense?

    <edit>
    BTW: They key here is that it is contiguous memory. This means that each apple is stored one after the other in memory, so you can change the pointer/address using math. i.e. You can move the pointer to the next apple in the array by adding (1 * the size of an APPLE) to the current address, or by adding (10 * the size of an APPLE) you could move ahead 10 apples, or whatever. If starting from the beginning of the array, you can move to any particulair apple you want, based on its position in the array, or by using the size of the array, you can pick any apple based on some percentage of that size, etc. (i.e. from a slider setting or whatever).
    </edit>
     
    Last edited: Jan 18, 2006
  16. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    Dang - for some reason this forum makes it easy to miss posts - while still showing ones that I HAVE read... Maybe it how FF processes cookies??

    Sorry Russ.

    Yes it does sorta make sense, IIRC thats the nature of indirect addressing - weather we read or write to it also makes the difference if APPLES contents are change.

    For instance:
    if APPLE contains 27 and is stored in 0x01
    and ORANGE contains 99 and is stored in 0x02

    We execute the following in DANE:

    &APPLE = 0x02

    The result I would expect would be:

    APPLE = 99

    And the Mem dump:
    0x01 = 27
    ox02 = 99

    So the memory hasnt changed EXCEPT the pointer to APPLES variable contents location.
    Which should ALSO equal the pointer for ORANGES variable contents location.

    we ONLY change what apple contains (now 99) - but mem location 0x01 STILL equal 27 - is this correct?

    - I understand the 'ADDRESS' is located in a 'RING' which makes it contigious.
    I understand Im not offestting (the +1) like I should be.

    Im still gonna need to digest this tho. - I moved on to a new idea - I know, Im ALL over the place. - but I wanted to 'lower' the difficulty for me in a new direction.

    I'll come back to more unique delays when I get a better grasp on all this.
     
  17. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    Lets not confuse TRAM usage in Dane, with pointers in general. I was really only responding to your example, but this is not really stuff you need to know to use the TRAM or Dane.

    It (pointers in general) is a little more complicated than that. You should not really be mixing apples and oranges here, because we do not know the size of these objects, and the size makes a difference when you are talking about array indices (and objects in memory), etc.

    For TRAM, all we are really talking about is an 'X' number of delayed samples from some starting point (the write pointer or another read pointer, etc), or, some percentage of the size of the delay line. All you really need to know for this is the magic number 0x800.

    i.e.
    To change the read pointer to some specific number of samples beyond some starting point (the write pointer here, but it could be another read pointer, or whatever):
    READ_PTR_ADDR = WRITE_PTR_ADDR + (NUMBER_OF_SAMPLES * 0x800)

    Or to change the read pointer to some percentage of the delay size (i.e. based on a slider value):
    READ_PTR_ADDR = WRITE_PTR_ADDR + ((DELAY_SIZE * 0x800) * PERCENTAGE)
     
  18. Maddogg6

    Maddogg6 Tail Razer

    Joined:
    Jun 21, 2005
    Messages:
    4,027
    Likes Received:
    26
    Trophy Points:
    0
    hehe - this is making my brain hurt - I seem to be slow at grasping all this.

    Ill continue to try - cuz I DO want to make some new delay algos. I DO want to understand it and NOT 'accidentally' stumble on something. lol
     
  19. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    I think maybe you are just over thinking it. Forget about the addressing and just think of what the delay line is. You put data in at some point (the write pointer), and every sample cycle, that data moves forward to the next position, and you read the data back out somewhere else.

    i.e.
    You put the data in at position 0, and you have a read pointer at 5.
    After one cycle, that data is at position 1.
    After two cycles, that data is at position 2.
    After three cycles, that data is at position 3.
    After four cycles, that data is at position 4.
    After five cycles, that data is at position 5, where you read pointer is, so what you are reading is a sample from 5 cycles ago, thus it is delayed 5 cycles.
    If you fed a sample into position 0, every cycle, then you will get those same samples back out 5 cycles later, so they are all delayed by 5 samples. Now the samples are not removed from the delay line, they keep moving until they reach the end, so if you had another read pointer further down the line, then you would get the same samples again but a more delayed version of it.

    If you wanted to change the read pointer at run time, to creater a longer or shorter delay, then you use the math from my previous post to do it.
    i.e. To move the first read pointer to position 10:
    [ READ_PTR_ADDR = WRITE_PTR_ADDR + (10 * 0x800) ] (10 after the write pointer. So, the 0x800 is irrelevant for your calculation, it is used simply to calculate the address).

    Now if you were using a slider (ignoring the 0x800 thing for the moment):
    Say your delay line is 100 samples (MAXDELAYSIZE = 100).
    Say your slider is at 50%, so you want the delay time to be half of the total time (MAXDELAYSIZE * 50%):
    [ 100 * .50 = 50 ], which is half the delay line. Now, if you had the wrong size for the total delay, then you would get the wrong answer.

    Now lets double the size of the delay line, and add a second read pointer, but keep the first read pointer within the same range as it was previously (i.e. 50% still equals 50 samples). We will move the first read pointer from the write position, and we will move the second read pointer from the first read position (so the delay time of the 2nd read ptr will be double that of the first).
    Total delay is 200, but we want each read pointer to have a max delay of 100 (to keep the same range as before), so MAXDELAYSIZE = 100.
    [ 1st_READ_PTR = WRITE_PTR + (100 * .50) ] or 1st_READ_PTR = 50 (0 + 50)
    [ 2nd_READ_PTR = 1ST_READ_PTR + (100 * .50) ] or 2nd_READ_PTR = 100 (50+50)

    Now if we decided to just add a couple more read points in between, like in your example code, without changing the MAXDELAYSIZE:
    [ 1st_READ_PTR = WRITE_PTR + (100 * .50) ] or 1st_READ_PTR = 50 (0 + 50)
    [ 2nd_READ_PTR = 1ST_READ_PTR + (100 * .50) ] or 2nd_READ_PTR = 100 (50 + 50)
    [ 3rd_READ_PTR = 2nd_READ_PTR + (100 * .50) ] or 3rd_READ_PTR = 150 (100 + 50)
    [ 4th_READ_PTR = 3rd_READ_PTR + (100 * .50) ] or 4th_READ_PTR = 200 (150 + 50)

    From the above, you can see that with the slider at only 50%, the 4th read pointer is at exactly the size of the total delay line, so anything above 50% will result in a values beyond the size of the delay line. The math is pretty straight forward there, and is what you would use to design your delays. The 0x800 is only for converting those numbers to the correct addresses, so in your code you would mutiply the MAXDELAYSIZE by 0x800, but you do not have to think about that when you are designing your delay lines, you should only be concerned with how much delay you want, and if your delay line is the correct size for the max delay you want (or if you split it up into multiple parts, that the max delay size of each part, when added together does not exceed the size of the complete delay line).

    BTW: If you add additional write pointers, then you are basically splitting the delay line into multiple delay lines. The end of the first delay line would be the position right before the second write pointer, etc.
     
    Last edited: Jan 30, 2006
  20. radiocolonel.it

    radiocolonel.it New Member

    Joined:
    Jan 16, 2005
    Messages:
    192
    Likes Received:
    0
    Trophy Points:
    0
    Yeah the widener is an idea of mine, max helped me to optimize the code and teaching me how to reuse some registers. Yeah the name ColHq would stand for Colonel High Quality...:lol::lol::lol:

    the formula i used is simple
    i subtract from the left channel the right one... what comes out from this operation i add to the left channel.... i do the same for the right! Simple, very simple but effective! U make me happy saying that u use that plugin! I don't know if u so my others anyway here they are (dane.zip):

    http://xoomer.virgilio.it/ars_acustica/kx/


    they are simple, so good to learn, some of them don't have optimized code... so it's even easier. I hope i helped you a little bit.
     

Share This Page

visited