1. IMPORTANT:
    We launched a new online community and this space is now closed. This community will be available as a read-only resources until further notice.
    JOIN US HERE

Syntax for exponentiation? (KSP Math Library)

Discussion in 'Scripting Workshop' started by Jeremy Z, Apr 22, 2015.

  1. Jeremy Z

    Jeremy Z Member

    Messages:
    38
    Hi, Bob.

    Sorry for the confusion. You're original impression is right. It's just one sound file played back at different rates.

    Thanks!
    -j
     
  2. Big Bob

    Big Bob Forum Member

    Messages:
    606
    HI Jeremy,

    Thanks for confirming that I don't have to change my thinking on this, whew! :D

    I'm getting my pile of other things to do under better control so I may be able to start typing this up tomorrow sometime. But, just to whet your appetite, here's a few highlights:

    1. I was able to confirm the long wait-time problem with K4/K5 but I have also devised a nice work-around that we can use.

    2. I have confirmed that K4/5 also handle SS offset quite accurately at least out to 100 seconds (that's as high as I tested).

    3. The offset method works just as well as the delay method (I've implemented both). The two methods have a sort of mirror symmetry. In fact, you could view an offset as a negative delay or vice versa.

    4. Each method has some advantages and disadvantages depending on how min, max, and root are positioned on the keyboard and whether you want longer or shorter common playback times.

    5. It turns out we can use a hybrid method that employs both delay and offset for the ultimate configuration flexibility. I've also implemented this.

    To be continued ...:)

    Bob
     
  3. Jeremy Z

    Jeremy Z Member

    Messages:
    38
    I can't wait to see how you're implementing this stuff! Just to be clear, each instance will play the entire sample, yes? So the duration of the entire event will be the duration of the lowest instance.

    Many thanks!!
    -j
     
  4. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    Sounds like multisamples are in question here (chromatic sampling of a particular sound).
     
  5. Big Bob

    Big Bob Forum Member

    Messages:
    606
    HI Jeremy,

    Yes, for the Delay algorithm, the 'common' playback time, P, (to the sync event) for any played key is that of the lowest playback key (without delay). For the Offset algorithm, the 'common' playback time (to the sync event) for any played key is that of the highest playback key (without offset). For the hybrid algorithm, the 'common' playback time (to the sync event) for any played key can be any time you want to set it for!

    If the root key is between the low and high keys, the pure Delay method always results in P >= S whereas the pure Offset method results in P <= S. But, for the hybrid method, P can be set to any desired time.

    As I said previously, each method has it's strengths and weaknesses but now you will have 3 algorithms to choose from:).

    To be continued ...

    Bob

    BTW I think I should be able to start writing this up later today sometime. However, It may facilitate the scaling issue if you can put some limits on things.

    Going by your previous posted code snippets, you want SyncTime, S, to have msec resolution and you have cited values as high as 25 seconds. Will 100 sec max be sufficient and do you still need msec resolution for these long S values? Or, will 3 or 4 significant digits suffice? You are currently computing frequency ratios, Fxy, scaled by 10^4 and I presume you want to retain that precision? However, presuming that the root key will be somewhere between the lowest and highest keys, do we need to allow for covering the entire midi range with the lo and hi keys? If so, Fxy can range from almost 0 to over 1500 and when scaled by 10^4 it can exceed 1.5*10^7. On the other hand, if you restrict the max difference between the lo and hi keys to something like say 5 octaves, then Fxy will not exceed 3.2*10^5.

    Currently I've allowed for the worst case (full midi keyboard range and a max S = 100000 msec). But the arithmetic can be further simplified if these limits can be lowered somewhat. So, give me your thoughts on these limits.
     
    Last edited: Apr 30, 2015
    • Like Like x 1
  6. Big Bob

    Big Bob Forum Member

    Messages:
    606
    Hi Jeremy,

    Well it looks like I finally have a nice block of free time ahead of me so I should be able to start posting all this stuff tomorrow right after breakfast. It's now 4:00PM (Apr 30) in my neck of the woods. I will probably have to spread it over several posts and possibly several days depending on how it unfolds. So, hang in there, everything looking good. :)

    I'm about ready to quit for the day but I shall return in the AM, the Good Lord willing.

    Rejoice,

    Bob
     
    • Like Like x 1
  7. Jeremy Z

    Jeremy Z Member

    Messages:
    38
    Bob, I can't thank you enough for putting all this effort into this project (but I'll keep trying). I don't know what to say... It's extremely generous of you.

    To answer you're questions re range, precision and sync time limits:
    Range: I think 7 octaves should be enough to leave room for experimentation, with 2 octaves max above the root and 5 below.

    Sync time precision: ms are probably best to account for short transient peaks. A 10ms precision could work though it would be a bit of a compromise.

    Max sync time: I think we should go with at least 100 seconds, possibly a bit more if possible. I'm trying to leave things open enough to avoid potential creative limitations in the future. Although, I could always use SuperCollider for extreme values. So now that I think of it, 100 seconds should be fine for this implementation!

    Thank you again, my friend!
    JZ
     
  8. Jeremy Z

    Jeremy Z Member

    Messages:
    38
    Hi! For this idea, there is only a single sample mapped across the range.

    Cheers!
    JZ
     
  9. EvilDragon

    EvilDragon Well-Known Member

    Messages:
    19,938
    Sounded to me like it wasn't. Sorry for confusion :)
     
  10. Big Bob

    Big Bob Forum Member

    Messages:
    606
    You're very welcome my friend, I'm always glad to pitch in when I can be of some help. Besides, this has been a very interesting project. OK, here is the first in a series of posts.

    Installment #1
    But, before we begin discussing this stuff, I want to present the following set of definitions and notation conventions that can be used throughout these posts and any subsequent discussions we might have about this project. .

    S = Sync Time
    The time to reach a specified sync event when playing the sample file with the root_key (with no delay or offset).

    P = Play Time
    The 'common' time to reach the sync event when the sample is played with any key in the allowed range (using one of three algorithms to be described).

    F.X
    The frequency of any played key, X.
    Examples: F.R, F.L, F.H, denote the frequencies of the root key, lowest key, and highest key respectively.

    N.X The midi note number for key X.

    F.XY
    The frequency ratio of any keys X and Y; specifically F.XY = F.X / F.Y Also note that F.YX = F.Y / F.X = 1/F.XY
    Examples: F.RL, F.RX, denote the frequency ratios of the root key to the lowest key and the root key to any arbitrary key X respectively.

    Also, please note that frequency ratio is related to midi note number difference as follows:

    (1) F.XY = 2^[(N.X - N.Y)/12]

    Now for a discussion of the three methods.

    The Delay Method
    This method attempts to make P some constant value for any played key (above the lowest key). This is accomplished by delaying the start of the sample more and more as the played key gets higher and higher. The delay time is calculated to make up for the shorter playback time of the higher keys such that the actual playback time plus the delay time is always equal to P. Since delay time cannot be negative, the 'common' playback time, P, will be that of the lowest key with zero delay.

    (2) P = F.RL*S

    And, the formula for calculating the needed delay time is given by:

    (3) D = (F.RL - F.RX)*S as long as F.X >= F.L

    The Offset Method
    This method attempts to make P some constant value for any played key (below the higest key). This is accomplished by starting sample playback with some offset that gets larger as the playback key gets lower. The sample start offset or Bias is calculated to make up for the longer playback time of the lower keys such that the actual playback time (for the remainder of the sample) is always equal to P. Since the offset bias cannot be negative, the 'common' playback time, P, will be that of the highest key with zero offset bias.

    (4) P = F.RH*S

    And, the formula for calculating the needed offset time is given by:

    (5) B = (1 - F.XH)*S as long as F.X <= F.H

    Comparing the two methods
    Note that the delay method always plays the full sample for any key, but the front of the playback time, P, contains silence for the higher notes. For the offset method, the sample plays through the entire playback time, P, but the front of the sample is not heard for the lower notes. Also, note that in the normal situation where F.R is between F.L and F.H, the following two relationships are true.

    P >= S with the delay method and
    P <= S with the offset method.

    This suggests that we can combine the two methods into a hybrid and be able to choose P independently of what F.L, F.R and F.H are chosen as.

    The Hybrid Method
    This method uses both delays and offsets as needed to obtain some desired 'common' playback time, P. without dependence on the lowest and/or highest keys relative to the root key. When the natural playback time would be less than P, a delay is used and when the natural playback time would be more than P, an offset is used. Thus the 'common' P value can be as chosen independently, rather than depending on either the lowest or highest notes used.

    For the hybrid method, the 'common' playback time P is given by:

    (6) P = K*P where K is any desired constant (K is limited to less than 1000.0000 for the demo script)

    And, the formulae for calculating delay or offset are:

    (7) D = (K - F.RX)*S to be used when F.RX <= K

    (8) B = (1 - K*F.XR)*S to be used when F.RX >= K

    Next, I'll post some demo scripts to illustrate these 3 methods and then, at the tail end of this series of posts, I'll post a discussion about scaling and arithmetic overflow issues.

    To be continued ...

    Bob
     
    Last edited: May 3, 2015
  11. Big Bob

    Big Bob Forum Member

    Messages:
    606
    Hi Jeremy,

    Installment #2
    This is a demo script for the Delay method. In addition to the following source listing, I'm attaching the KSE source code file in case the copy and paste thing causes any problems.

    import "KSPMathV700.ksp"
    on init
    ``message('')
    ``set_script_title('Delay Method')
    ``SetMathMode(0,DG)
    ``declare ui_value_edit SyncTime(0,100000,1000) { 0 .. 100.000 sec }
    ````SyncTime->width := 130
    ````move_control_px(SyncTime,100,0)
    ````set_text(SyncTime,'Sync Time (secs)')
    ````SyncTime := 10000
    ``declare lo_key := 36
    ``declare root_key := 60
    ``declare polyphonic delay { delay time in msec }
    ``declare F.RL``{ F.R/F.L }
    ``declare F.RX``{ F.R/F.X }
    ``make_persistent(SyncTime)
    end on

    on_post_init
    ``F.RL := F(root_key,lo_key)
    end_on

    on note
    ``ignore_event(EVENT_ID)
    ``F.RX := F(root_key,EVENT_NOTE)
    ``delay := MulDiv64((F.RL - F.RX),SyncTime,10000) { to avoid arithmetic overflow }
    { message(delay) }
    ``long_wait(delay)
    ``play_note(EVENT_NOTE,EVENT_VELOCITY,0,-1)
    end on

    {-------------------- support functions ---------------------}
    function F(n1,n2)->result``{ ratio of F.1/F.2 for notes n1 and n2 }
    ``result := Exp2(250000*(n1-n2)/3 + 13287712)``{ scaled by 10^4 }
    end function

    function long_wait(t) { time in msec }
    ``declare polyphonic t0
    ``t0 := ENGINE_UPTIME
    ``while (ENGINE_UPTIME - t0) < t
    ````wait(500)
    ``end while
    end function


    This script uses the math that you already 'know and love'. I used -1 for the last parameter of the play_note function since that's what you seemed to be using. So, I guess you like to hold the keys down until the sync event occurs. For my testing, I usually set this parameter to 0 so the sample would be triggered in the one-shot mode. That way I didn't have to hold the keys down so long for high S values.

    I solved Kontakt's long wait problem by creating a new function which I named long_wait. This function provides a fairly stable wait time in msec (which I presume will be sufficiently fine resolution for you). It should be accurate to about 0.5 msec and I tested it out to just over 100 secs. However, if you are going to have S values that might be even higher than 100 secs and you plan to have a lo_key value 5 octaves below the root_key, you could need delay times greater than 3200 seconds. So, maybe you should test the long_wait function for these longer delay times to be sure some new Kontakt funny doesn't occur out there ;).

    Also, please note that not only the delay variable but long_wait's local variable named t0 must both be polyphonic. This of course is because the long_wait routine could be re-entered multiple times (for instance when playing a chord).

    This script tested fine here using my test files but please let me know If you have any problems with it. It could be that I'm still not in perfect alignment with you as to your requirement.:)

    I'll post the Offset Method demo next.

    To be continued ...

    Bob
     

    Attached Files:

    Last edited: May 3, 2015
  12. Big Bob

    Big Bob Forum Member

    Messages:
    606
    Hi Jeremy,

    Installment #3.
    This is a demo script for the Offset method. In addition to the following source listing, I'm also attaching the KSE source code file in case the copy and paste thing causes any problems.

    import "KSPMathV700.ksp"

    on init
    ``message('')
    ``set_script_title('Offset Method')
    ``SetMathMode(0,DG)
    ``declare ui_value_edit SyncTime(0,100000,1000) { 0 .. 100.000 sec }
    ````SyncTime->width := 130
    ````move_control_px(SyncTime,100,0)
    ````set_text(SyncTime,'Sync Time (secs)')
    ````SyncTime := 10000
    ``declare hi_key := 72
    ``declare bias { SS offset time in usec }
    ``declare F.XH { F.X / F.H }
    ``make_persistent(SyncTime)
    end on

    on note
    ``ignore_event(EVENT_ID)
    ``F.XH := F(EVENT_NOTE,hi_key)
    ``bias := (10000 - F.XH)*SyncTime/10``{ offset = (1 - F.XH)*S }
    { message(bias) }
    ``play_note(EVENT_NOTE,EVENT_VELOCITY,bias,-1)
    end on

    {-------------------- support functions ---------------------}
    function F(n1,n2)->result``{ ratio of F.1/F.2 for notes n1 and n2 }
    ``result := Exp2(250000*(n1-n2)/3 + 13287712)``{ scaled by 10^4 }
    end function


    Using your former numbers, I set hi_key = 72 (1-octave above the root_key) so the 'common' playback time, P, for this script will be S / 2 (see equation 4 in my first post).

    This script also tested fine here but please let me know If you have any problems with it.

    I'll post the Hybrid Method demo next.

    To be continued ...

    Bob
     

    Attached Files:

    Last edited: May 4, 2015
  13. Big Bob

    Big Bob Forum Member

    Messages:
    606
    Hi Jeremy,

    Installment #4.
    This is a demo script for the Hybrid method. In addition to the following source listing, I'm also attaching the KSE source code file in case the copy and paste thing causes any problems.

    import "KSPMathV700.ksp"

    on init
    ``message('')
    ``set_script_title('General Hybrid')
    ``SetMathMode(0,DG)
    ``declare ui_value_edit SyncTime(0,100000,1000)``{ 0 .. 100.000 sec }
    ````SyncTime->width := 140
    ````move_control_px(SyncTime,100,0)
    ````set_text(SyncTime,'Sync Time (secs)')
    ````SyncTime := 10000
    ``declare ui_value_edit PlayTime(0,1000000,1000) { 0..1000.000 sec }
    ````PlayTime->width := 140
    ````move_control_px(PlayTime,100,20)
    ````set_text(PlayTime,'Play Time (secs)')
    ````PlayTime := 10000
    ``declare ui_switch Lock
    ````move_control_px(Lock,240,20)
    ````set_text(Lock,'Lock to Sync Time')
    ````Lock->text_alignment := 1
    ````Lock->width := 105
    ``declare root_key := 60
    ``declare bias`````````{ SS offset in usec }
    ``declare polyphonic delay``{ delay time in msec }
    ``declare K := 10000```{ K = 1.0000 }
    ``declare F.RX`````````{ F.R/F.X }
    ``make_persistent(SyncTime)
    ``make_persistent(PlayTime)
    ``make_persistent(Lock)
    ``make_persistent(K)``
    end on

    on note
    ``ignore_event(EVENT_ID)
    ``F.RX := F(root_key,EVENT_NOTE)
    ``if F.RX < K``{ use delay method }
    ```````{ delay = (K - F.RX)*S where K = P/S }
    ````delay := MulDiv64((K - F.RX),SyncTime,10000)``{ msec }
    { message('delay = ' & delay & ' msec') }
    ````long_wait(delay)
    ````play_note(EVENT_NOTE,EVENT_VELOCITY,0,-1)
    ``else { use offset method }
    ```````{ bias = (1 - K/F.RX)*S where K + P/S }
    ````bias := MulDiv64(10000,K,F.RX)
    ````bias := (10000 - bias)*SyncTime/10
    { message('offset = ' & bias & ' usec') }
    ````play_note(EVENT_NOTE,EVENT_VELOCITY,bias,-1)
    ``end if
    end on

    on ui_control(Lock)
    ``call set_K
    end on

    on ui_control(SyncTime)
    ``call set_K
    end on

    on ui_control(PlayTime)
    ``call set_K
    end on

    {-------------------- support functions ---------------------}
    function F(n1,n2)->result``{ ratio of F.1/F.2 for notes n1 and n2 }
    ``result := Exp2(250000*(n1-n2)/3 + 13287712)``{ scaled by 10^4 }
    end function

    function long_wait(t) { time in msec }
    ``declare polyphonic t0
    ``t0 := ENGINE_UPTIME
    ``while (ENGINE_UPTIME - t0) < t
    ````wait(500)
    ``end while
    end function

    function set_K``{ K = P/S, 0 <= K <= 1000.0000 }
    ``if Lock # 0 or SyncTime = 0
    ````PlayTime := SyncTime
    ````K := 10000``{ K = 1.0000 }
    ``else
    ````if PlayTime > 1000*SyncTime
    ``````K := 10000000``{ clamp K <= 1000.0000 }
    ``````PlayTime := 1000*SyncTime
    ````else
    ``````K := MulDiv64(PlayTime,10000,SyncTime)``{ P/S scaled by 10^4 }
    ````end if
    ``end if
    end function


    This script is the most flexible of the three. If you set P (the Play Time edit box) to 4*S, the script will behave identically with the Delay Method script. If you set P = S / 2, the sript will behave identically with the Offset Method script.

    However, if you set P = S for example, the sync event will always occur at SyncTime after hitting any key. You can even set P = 0 and it will cause the sync event to occur as soon as you hit any key (even though S > 0).

    This script also tested fine here but again, please let me know If you have any problems with it.

    For the next installment, I'll post a discussion on scaling and such but I probably won't have time to do that until tomorrow.

    To be continued ...

    Bob
     

    Attached Files:

    Last edited: May 4, 2015
  14. Jeremy Z

    Jeremy Z Member

    Messages:
    38
    Brilliant stuff. Wow... You really opened this idea up.

    However I'm not getting the Offset method working properly. No matter the values, the file will start playback from the beginning of the file. It seems like the sample start offset isn't working for me. However, the Delay method and what I assume to be the Delay 'side' of the Hybrid method seems to be working very well and doing exactly what I had want it to do.

    I've got a couple questions / issues to report:
    - Why will P = 4*S behave identically to the Delay method? Where does the constant 4 come from? (Same question for P = S/2 being identical to the Offset method). Is that assuming the values of 32 for F.L and 60 for F.R?
    - I tried setting P to 0 to start at S but the file just starts at the beginning with no offset regardless of the key. (In fact, the value of S has no effect at all when using the Offset method.)

    Many thanks!!!
    -j

    PS Hope I'm using your notation properly...!
     
  15. Jeremy Z

    Jeremy Z Member

    Messages:
    38
    no worries!
     
  16. Big Bob

    Big Bob Forum Member

    Messages:
    606
    Hi Jeremy,

    Regarding your issues with the offset function, are you running in Sampler mode? If you are running in DFD mode you will have to open up the S.Mod thing (see below) far enough to cover the entire sample length. That of course will obviate the advantage of the DFD mode. If for some reason you can't run in Sampler mode, and you don't want to extend S.Mod, you won't be able to use the offset or hybrid method.

    Yes, the 4*S and S/2 values are only for these specific demo scripts where F.RL = 4 and F.RH = 0.5 (ie lo_note=36 and hi_note=72). Generally, P would be F.RL*S and F.RH*S for the Delay and Offset methods respectively.

    Again, this is probably because you are running in DFD mode. When you run in DFD mode, Kontakt ignores the offset value in play_note unless you have set it up properly. You have to go into the wave editor and set S.Mod just past the longest offset you intend to use. In your case, you'd essentially have to set it to the end of the sample. So it would be easier to simply switch to Sampler mode.

    Rejoice,

    Bob
     
  17. Jeremy Z

    Jeremy Z Member

    Messages:
    38
    Ah. Sorry, I should've realized that. All methods are working beautifully now. Very impressive, Bob. This is going to be great to have in the DAW. And the other methods will really open things up creatively!

    I need to mention that the 'stacker' idea is something I got from Trevor Wishart (who IIRC got from Dennis Smalley??) Such a simple and effective technique.

    I've got some more ideas for this to take it 'off the grid' so to speak (non-equal tempered ratios). I'll share here when I get something happening...

    HUGE THANKS, Bob. It's been a great learning experience for me.

    More soon,
    Jeremy
     
  18. Big Bob

    Big Bob Forum Member

    Messages:
    606
    Hi Jeremy,

    Well, very few of us have truly original ideas, we all learn from each other.

    Again you're very welcome but I haven't posted the last three installments for you yet. Or have you had enough? ;)

    Even if you have, I just finished Installment #5 for this post so you're too late to stop me:)

    Installment #5
    This post will start discussing scaling and other math issues. Since each of the 3 methods has somewhat different math requirements, I'll discuss them individually one at a time. For this installment, I'll discuss the Delay Method.

    We need to focus on the formula for the delay time that was presented in equation (1) and repeated here for convenience.

    (9) D = (F.RL - F.RX)*S

    The first limit that must be considered is that the calculated delay time (in whatever unit the wait routine requires) must fit in a signed integer, thus:

    (10) D <= MaxInt

    Since F.RX <= F.RL, the parenthetical expression in (9) has a max value of F.RL so the max value of D will be F.RX *Smax and thus:

    (11) Smax <= MaxInt / F.RLmax

    For the full midi keyboard range, it would be possible for F.RLmax to be as high as 1534.27 so the limit for Smax would be about 1.4 million units. If D and S are both in msec, then Smax must be just under 1400 secs (1,400,000 units). Of course if the value of F.RL is constrained to some lower value (for example if the user is not allowed to use a key below lo_key and lo_key is no more than 5 octaves below the root_key), then F.RLmax <= 32 and Smax could be as high as 67000 seconds.

    Of course the above Dmax limitation on S is not the only thing that limits the max value of S. There is another consideration that must be examined.

    Currently we are scaling frequency ratios by 10^4. This means that F.RL could be higher than 1.5*10^7 for the full midi range. To retain precision, it will be required that we multply by S before we remove the scaling (divide by 10^4). Therefore, the interim product of the scaled F.RL and S has to be reckoned with.

    If we want to use only the native multiply and divide operators, it will be necessary to constrain the interim product to MaxInt and thus:

    (12) Smax <= MaxInt / (10^4*F.RLmax)

    By equation (12), Smax would be limited to 140 units or a paltry 0.14 seconds (if we use msec units). Of course with F.RLmax constrained to say 32, this could be opened up to about 7 seconds but this also would be totally inadequate for your application. For this reason I coded multiply by S and divide by 10^4 using the library routine MulDiv64. This routine computes the product with an interim register size of 64 bits.

    By using the MulDiv64 routine, equation (12) doesn't apply and we're back to the limitation of equation (11). So, as long as we use the MulDiv64 routine, it looks like you can increase Smax to just under 1400 seconds (using msec units) for the full midi range and maybe as high as 67000 seconds for your narrower 5-octave limit.

    MulDiv64 naturally isn't as fast as the native multiply and divide operations but, in order to eliminate its use, both range and precision will have to be severely curtailed. For example, if you use 10 msec units for Smax and restrict F.RL to 5 octaves, Smax could only be about 70 seconds.

    Of course if you use finer units for S and/or want to compute delay time in usec for example, you will have to lower Smax accordingly. And, speaking of delay time, the long_wait function should be accurate to around 1 msec (since we poll the UPTIME twice every msec). However, if you think you need finer resolution, we can consider replacing UPTIME with the KSP_TIMER. How much better accuracy this would achieve would have to be investigated. I just assumed, for now, that msec accuracy would be sufficient.

    Installment #6 will discuss the math for the Offset Method.

    To be continued ?

    Rejoice,

    Bob
     
    Last edited: May 2, 2015
    • Like Like x 1
  19. Big Bob

    Big Bob Forum Member

    Messages:
    606
    Hi Jeremy,

    installment #6.
    This post will discuss the Offset algorithm math.

    We need to focus on the formula for the offset bias time equation (5) repeated here for convenience.

    (13) B = (1 - F.XH)*S {BTW there was a typo in eq (5 initially which I corrected today}

    Since F.X is restricted to being less than F.H, F.XH <= 1 so the max value for B will be S.

    (14) Bmax <= Smax

    So, for the offset algorithm, the unscaled situation is simpler than for the Delay method in that as long as Smax fits in a signed integer, so will Bmax.

    Regarding the scaled calculation of B, as for the Delay method, F.XH is scaled by 10^4 and we will need to multiply that scaled F.XH by S before dividing by 10^4. But, in this case, the scaled product of F.XH and S is only 10^4*F.XH*S which is at most 10^4*S. Therefore, for the iterim product to fit in a 32-bit integer, we have:

    (15) Smax <= MaxInt / 10^4

    This limits Smax to about 2*10^5 units or 214 secs. So if Smax needs to be higher than this, you will need to use MulDiv64.

    For the demo script, Smax was restricted to 100 sec, so a native multiply/divide was used to compute B.

    Installment #7 will discuss the math for the Hybrid Algorithm.

    To be continued ...

    Bob
     
    Last edited: May 4, 2015
    • Like Like x 1
  20. Big Bob

    Big Bob Forum Member

    Messages:
    606
    Hi Jeremy,

    This should finish the series that I promised

    Installment #7.
    This post will discuss the Hybrid algorithm math.

    We need to focus on the formulae for the delay and offset bias previously given in equations (7) and (8) and repeated here as (16) and (17) for convenience.

    (16) D = (K - F.RX)*S
    (17) B = (1 - K*F.XR)*S


    where:

    (18) K = P / S

    And, since we should probably avoid calculating both F.RX and F.XR, we can re-write (17) as:

    (19) B = (1 - K / F.RX)*S

    The 'new kid on the block' is K and we have a wide latitude in how we allow K to be specified. For the demo script, instead of specifying K directly, I thought it would be more convenient to allow it to be specified with the desired, 'common' play time, P. Since the current Smax = 100.000 secs, I arbitrarily chose a Pmax value of 1000.000 secs (which seemed about as long a play time as one might want to 'wait through' :)

    However, when using smaller S values, one might want to use K values greater than 10 so I decided to make Kmax = 1000). Therefore, the demo script limits P to either K*S or 10*Smax secs (whichever is smaller). For the special case of S = 0, the only meaningful value for P is zero, so the set_K routine sets P = 0 and K = 1 if S is set to zero. This also neatly avoids any illegal division by zero that might otherwise result when computing K.

    It also seemed sensible to chose the same units for P as for S. Further, inspection of (16) and (19) reveals that K should be scaled by the same factor as the frequency ratios are scaled (10^4 for the demos).

    The calculation for K then becomes:

    (20) K = 10^4*P/S

    And, of course, to retain precision, we need to multiply by 10^4 before we divide by S. Since 10^4*Pmax is 10^10 (which exceeds MaxInt), we must use MulDiv64 to compute K. However, if you are willing to lower Pmax to 200.000 seconds, you can replace MulDiv64 with native multiply/divide.

    Now, we need to look at how we calculate (16) and (19) when F.RX and K are scaled by 10^4. Let's first deal with (16).

    Since F.RX <= K when (16) is used, the max value for the parenthetical expression (K - F.RX) will be Kmax = 1000.0000.

    Now when we compute (16), we need to multiply by S before we divide by 10^4 to remove the scale. So, the max interim product can reach Kmax*Smax = 10^13 which greatly exceeds MaxInt. So again we resort to using MulDiv64 to calculate delay.

    When F.RX >= K, we use (19) for which the parenthetical expression has a max of 10^4 with scaling. Therfore, the iterim product with S (before removing the 10^4 scale) will be 10^4*Smax = 10^9 which is less than MaxInt so we can perform this multiply and then the divide by 10^4 with native multiply/divide instructions. However, to evaluate the parenthetical expression itself, we also have to compute 10^4*K / F.RX. Again we need to do the mulitply first to retain precision so here, the max interim product will be 10^4*Kmax = 10^11. And, since this is greater than MaxInt, again we must use MulDiv64.

    I think that just about covers everything of note about the demo scripts but, please don't hesitate to tell me if something isn't clear.

    Rejoice,

    Bob
     
    • Like Like x 1