Paul Batcheloradded rephasorx marker3485b025 years agoHistory
401 lines · 9.3 KB · Text
RawDownload
#+TITLE: Rephasor* Overview=rephasor= is an unusual little algorithm that is designedto recreate an incoming phasor signal at a slightlydifferent frequency. This works by examining the incremental amount of changehappening between each phasor sample, then using a scaledversion of that signal to produce a new phasor. Why? Because I'm having a bit of a phasor a renaissance.I am discovering that these phasors are actually quiteuseful control signals, not just for table lookuposcillators. In the spirit of that, I'm looking for ways toexplore ways to modify phasor signals.* Tangled FilesTangles to =rephasor.c= and =rephasor.h=. =SK_REPHASOR_PRIV= exposes structs. #+NAME: rephasor.h#+BEGIN_SRC c :tangle rephasor.h#ifndef SK_REPHASOR_H#define SK_REPHASOR_H#ifndef SKFLT#define SKFLT float#endif<<typedefs>>#ifdef SK_REPHASOR_PRIV<<structs>>#endif<<funcdefs>>#endif#+END_SRC #+NAME: rephasor.c#+BEGIN_SRC c :tangle rephasor.c#include <math.h>#define SK_REPHASOR_PRIV#include "rephasor.h"<<funcs>>#+END_SRC* StructCalled =sk_rephasor=. #+NAME: typedefs#+BEGIN_SRC ctypedef struct sk_rephasor sk_rephasor;#+END_SRC #+NAME: structs#+BEGIN_SRC cstruct sk_rephasor {    <<sk_rephasor>>};#+END_SRC Initialized with =sk_rephasor_init=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_rephasor_init(sk_rephasor *rp);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_rephasor_init(sk_rephasor *rp){    <<init>>}#+END_SRC The struct is needed to store stateful data. The previoussample of the input phasor signal (=extphs=), and theprevious phase of the internal phasor signal is used(=phs=). #+NAME: sk_rephasor#+BEGIN_SRC cSKFLT extphs;SKFLT phs;#+END_SRC Internal and external phasor memory are both marked asbeing initialized but not set by setting them to be -1. #+NAME: init#+BEGIN_SRC crp->extphs = -1;rp->phs = -1;#+END_SRC A value holding the scale amount is also stored here. Itis set to a sensible of default of 1. In theory, this shouldcreate an identical phasor signal as the input with aunit delay. #+NAME: sk_rephasor#+BEGIN_SRC cSKFLT scale;#+END_SRC #+NAME: init#+BEGIN_SRC csk_rephasor_scale(rp, 1.0);#+END_SRC* Setting the scaling valueCalled =sk_rephasor_scale=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_rephasor_scale(sk_rephasor *rp, SKFLT scale);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_rephasor_scale(sk_rephasor *rp, SKFLT scale){    rp->scale = scale;}#+END_SRC* ComputeDone with =sk_rephasor_tick=. Expects an incoming inputphasor signal. #+NAME: funcdefs#+BEGIN_SRC cSKFLT sk_rephasor_tick(sk_rephasor *rp, SKFLT in);#+END_SRC #+NAME: funcs#+BEGIN_SRC cSKFLT sk_rephasor_tick(sk_rephasor *rp, SKFLT in){    SKFLT out;    SKFLT delta;    out = 0;    <<set_output>>    <<calculate_delta>>    <<cache_external_phasor>>    <<update_internal_phasor>>    return out;}#+END_SRC The current phase of the internal phasor is set to bethe output of the current sample. If it is negative, itis assumed to be initalized but not set, and the currentphasor signal is set to be the output, and theinternal phasor value is set to be the current externalphasor value. #+NAME: set_output#+BEGIN_SRC cif (rp->phs < 0) {   rp->phs = in;} out = rp->phs;#+END_SRC The =delta= amount is calculated next. This is the absolutevalue of the difference between the current and previousexternal phasor. The absolute value is used to compensatefor phasor resets. If the previous phasor hasn't beeninitialized, the delta value is zero. #+NAME: calculate_delta#+BEGIN_SRC cif (rp->extphs < 0 || in < rp->extphs) delta = 0;else delta = in - rp->extphs;#+END_SRC At this point, it is safe to cache the external phasorsignal. #+NAME: cache_external_phasor#+BEGIN_SRC crp->extphs = in;#+END_SRC The internal phasor is updated. The increment valueis the =delta= multiplied by the internal =scale= value.Wraparound is applied so that the range is within 0 and 1. #+NAME: update_internal_phasor#+BEGIN_SRC crp->phs += delta * rp->scale;while (rp->phs >= 1.0) rp->phs -= 1.0;while (rp->phs < 0.0) rp->phs += 1.0;#+END_SRC* Rephasorx@!(marker "rephasorx")!@The =rephasorx= is a specialized version of =rephasor= thatis designed to stay synchronized with the external phasorsignal. This allows it to be used in a long-termcontexts without having to worry about phase drift. #+NAME: typedefs#+BEGIN_SRC ctypedef struct sk_rephasorx sk_rephasorx;#+END_SRC Rephasorx is limited to whole-integer divisions to ensurethat they line up with the the external phasor. There are two kinds of operations rephasorx operations: a=divider= will divide a phasor signal into =N= smallersections, while a =multiplier= will stretch out that phasorby a factor of =N=. Both dividers and multipliers share the same base struct=sk_rephasorx=, which contains an instance of =sk_rephasor=,a =counter=, and counter =limit=. Counters are used to keeptrack of phasor resets. Whichphasor resets it keeps track of (external or output)depends on if it is a multiplier or divider. #+NAME: structs#+BEGIN_SRC cstruct sk_rephasorx {   sk_rephasor rp;   int counter;   int limit;   SKFLT N;};#+END_SRC Rephasorx is initialized with the function=sk_rephasorx_init=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_rephasorx_init(sk_rephasorx *rpx);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_rephasorx_init(sk_rephasorx *rpx){    sk_rephasor_init(&rpx->rp);    rpx->counter = -1;    rpx->limit = -1;    rpx->N = 1;}#+END_SRC The N-factor value is set with =sk_rephasorx_factor=. #+NAME: funcdefs#+BEGIN_SRC cvoid sk_rephasorx_factor(sk_rephasorx *rpx, SKFLT N);#+END_SRC #+NAME: funcs#+BEGIN_SRC cvoid sk_rephasorx_factor(sk_rephasorx *rpx, SKFLT N){    rpx->N = N;}#+END_SRC Rephasorx has two tick functions: =sk_rephasorx_tick_div=for the divider behavior, and =sk_rephasorx_tick_mul= forthe multiplier behavior. #+NAME: funcdefs#+BEGIN_SRC cSKFLT sk_rephasorx_tick_div(sk_rephasorx *rpx, SKFLT in);SKFLT sk_rephasorx_tick_mul(sk_rephasorx *rpx, SKFLT in);#+END_SRC A phasor divider works by taking an external phasor signaland shrinking it by a factor of N.To be a divider, the scale of the rephasor is set to be =N=.The counter keeps track of how many times the internalrephasor resets. When it reaches N, the signal holds at 1until the external phasor resets. At this point, theinternal phasor is set to be the external phasor, whichis presumably at or very close to 0. If the external phasorresets before the internal phasor has a chance to hold at 1,it gets reset a bit earlier. The early or late arrival ofthe internal phasor should be negligible. #+NAME: funcs#+BEGIN_SRC cSKFLT sk_rephasorx_tick_div(sk_rephasorx *rpx, SKFLT in){    SKFLT out;    SKFLT delta;    out = 0;     if (rpx->counter < 0) {        /* initialize */        rpx->counter = 0;        rpx->limit = floor(rpx->N);        rpx->rp.scale = rpx->limit;    }     if (rpx->rp.phs < 0) {        rpx->rp.phs = in;    }     out = rpx->rp.phs;     if (rpx->rp.extphs < 0 || in < rpx->rp.extphs) delta = 0;    else delta = in - rpx->rp.extphs;     if (in < rpx->rp.extphs) {        /* hard reset */        rpx->rp.phs = in;         rpx->counter = 0;        rpx->limit = floor(rpx->N);        rpx->rp.scale = rpx->limit;    }     if (rpx->counter < rpx->limit) {        SKFLT phs;        rpx->rp.extphs = in;         phs = rpx->rp.phs;        phs += delta * rpx->rp.scale;        while (phs >= 1.0) phs -= 1.0;        while (phs < 0.0) phs += 1.0;         if (phs < rpx->rp.phs) {            rpx->counter++;        }         rpx->rp.phs = phs;    } else {       /* hang out at 1, wait for external phasor to reset */       rpx->rp.phs = 1;    }     return out;}#+END_SRC A phasor multiplier works by taking an external phasorsignal and shrinking it by a factor of N. To be amultiplier, the scale of the rephasor is set to be =1/N=.The counter keeps track of how many times the externalphasor rests. When it reaches N, it sets the internal phasorto be the external phasor. At this point, the internalphasor was producing a long ramp that was going to beapproaching or already approaching 1 and due to loop back to0 anyways. Similar to the divider, the multiplier will hangat one and wait for the external phasor to reset. Again,this amount of time should be negligible. #+NAME: funcs#+BEGIN_SRC cSKFLT sk_rephasorx_tick_mul(sk_rephasorx *rpx, SKFLT in){    SKFLT out;    SKFLT delta;    out = 0;     if (rpx->counter < 0) {        /* initialize */        rpx->counter = 0;        rpx->limit = floor(rpx->N);        rpx->rp.scale = 1.0 / rpx->limit;    }     if (rpx->rp.phs < 0) {        rpx->rp.phs = in;    }     out = rpx->rp.phs;     if (rpx->rp.extphs < 0 || in < rpx->rp.extphs) delta = 0;    else delta = in - rpx->rp.extphs;     if (in < rpx->rp.extphs) {        rpx->counter++;    }     if (rpx->counter >= rpx->limit) {        /* hard reset */        rpx->rp.phs = in;         rpx->counter = 0;        rpx->limit = floor(rpx->N);        rpx->rp.scale = 1.0 / rpx->limit;    }      if (rpx->rp.extphs < 0 || in < rpx->rp.extphs) delta = 0;    else delta = in - rpx->rp.extphs;    rpx->rp.phs += delta * rpx->rp.scale;    rpx->rp.extphs = in;     /* hang at 1 and wait until external phasor resets */    if (rpx->rp.phs > 1) rpx->rp.phs = 1;     return out;}#+END_SRC

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t