| @@ -0,0 +1,151 @@ |
| | 1 | #+TITLE: Phasor |
| | 2 | * Overview |
| | 3 | The =phasor= (not to be confused with the phaser audio |
| | 4 | effect) is a name used to describe a classic signal |
| | 5 | generator that produces a periodic ramp signal normalized |
| | 6 | to be in range 0-1, set to move at a specified frequency. |
| | 7 | Phasors are typically used in the context of table-lookup |
| | 8 | oscillators such as @!(ref "oscf")!@. |
| | 9 | |
| | 10 | The term "phasor" comes from the engineering and physics |
| | 11 | world, used to describe the complex value of a sinusoid |
| | 12 | (phase, and amplitude). While not a complex value, the |
| | 13 | phasor in this computer music context could be the phase |
| | 14 | component of the phasor. |
| | 15 | |
| | 16 | The adoption of the phasor term in the computer music world |
| | 17 | can be traced back to one of the oldest opcodes in Csound, |
| | 18 | but the author believes it may go back even further to the |
| | 19 | MUSICN family of computer music languages. It is noteworthy |
| | 20 | that the word phasor is six letters, which was the |
| | 21 | character limit for opcodes in both MUSICN languages and |
| | 22 | the older versions of Csound. In this context, it is a |
| | 23 | really nice name to describe this. |
| | 24 | * Tangled Files |
| | 25 | Tangles to =phasor.h= and =phasor.c=. |
| | 26 | |
| | 27 | If =SK_PHASOR_PRIV= is defined, it exposes the struct |
| | 28 | =sk_phasor=, otherwise it is opaque. |
| | 29 | |
| | 30 | #+NAME: phasor.h |
| | 31 | #+BEGIN_SRC c :tangle phasor.h |
| | 32 | #ifndef SK_PHASOR_H |
| | 33 | #define SK_PHASOR_H |
| | 34 | |
| | 35 | #ifndef SKFLT |
| | 36 | #define SKFLT float |
| | 37 | #endif |
| | 38 | |
| | 39 | <<typedefs>> |
| | 40 | <<funcdefs>> |
| | 41 | #ifdef SK_PHASOR_PRIV |
| | 42 | <<structs>> |
| | 43 | #endif |
| | 44 | #endif |
| | 45 | #+END_SRC |
| | 46 | |
| | 47 | #+NAME: phasor.c |
| | 48 | #+BEGIN_SRC c :tangle phasor.c |
| | 49 | #define SK_PHASOR_PRIV |
| | 50 | #include "phasor.h" |
| | 51 | <<funcs>> |
| | 52 | #+END_SRC |
| | 53 | * Struct and initialization |
| | 54 | The state of phasor is wrapped in a struct |
| | 55 | called =sk_phasor=, and initialized with =sk_phasor_init=. |
| | 56 | You'll need the sampling rate, and initial phase. |
| | 57 | |
| | 58 | #+NAME: typedefs |
| | 59 | #+BEGIN_SRC c |
| | 60 | typedef struct sk_phasor sk_phasor; |
| | 61 | #+END_SRC |
| | 62 | |
| | 63 | In =sk_phasor=, =freq= stores the frequency, =phs= stores |
| | 64 | the previous phasor value, and =onedsr= is the inverse value |
| | 65 | of the sampling rate, which is used in order to shave off |
| | 66 | a traditionally more expensive divide operation. |
| | 67 | |
| | 68 | #+NAME: structs |
| | 69 | #+BEGIN_SRC c |
| | 70 | struct sk_phasor { |
| | 71 | SKFLT freq; |
| | 72 | SKFLT phs; |
| | 73 | SKFLT onedsr; |
| | 74 | }; |
| | 75 | #+END_SRC |
| | 76 | |
| | 77 | #+NAME: funcdefs |
| | 78 | #+BEGIN_SRC c |
| | 79 | void sk_phasor_init(sk_phasor *ph, int sr, SKFLT iphs); |
| | 80 | #+END_SRC |
| | 81 | |
| | 82 | #+NAME: funcs |
| | 83 | #+BEGIN_SRC c |
| | 84 | void sk_phasor_init(sk_phasor *ph, int sr, SKFLT iphs) |
| | 85 | { |
| | 86 | ph->phs = iphs; |
| | 87 | ph->onedsr = 1.0 / sr; |
| | 88 | sk_phasor_freq(ph, 440); |
| | 89 | } |
| | 90 | #+END_SRC |
| | 91 | * Setting the Frequency |
| | 92 | The frequency of the phasor can be set with |
| | 93 | =sk_phasor_freq=. |
| | 94 | |
| | 95 | #+NAME: funcdefs |
| | 96 | #+BEGIN_SRC c |
| | 97 | void sk_phasor_freq(sk_phasor *ph, SKFLT freq); |
| | 98 | #+END_SRC |
| | 99 | |
| | 100 | #+NAME: funcs |
| | 101 | #+BEGIN_SRC c |
| | 102 | void sk_phasor_freq(sk_phasor *ph, SKFLT freq) |
| | 103 | { |
| | 104 | ph->freq = freq; |
| | 105 | } |
| | 106 | #+END_SRC |
| | 107 | * Computation |
| | 108 | A single sample of audio is computed with |
| | 109 | =sk_phasor_tick=. |
| | 110 | |
| | 111 | #+NAME: funcdefs |
| | 112 | #+BEGIN_SRC c |
| | 113 | SKFLT sk_phasor_tick(sk_phasor *ph); |
| | 114 | #+END_SRC |
| | 115 | |
| | 116 | The output of the phasor is the current internal phase |
| | 117 | state before being incremented. |
| | 118 | |
| | 119 | The behavior of the phasor is to increment its internal |
| | 120 | phase by an increment amount, determined by =freq*onedsr=. |
| | 121 | It is then wrapped to be in range 0 and 1. Instead of |
| | 122 | using something =fmod= for the wrapping, conditionals |
| | 123 | and arithmetic are used. In addition to being simpler, |
| | 124 | this is often a faster approach. |
| | 125 | |
| | 126 | #+NAME: funcs |
| | 127 | #+BEGIN_SRC c |
| | 128 | SKFLT sk_phasor_tick(sk_phasor *ph) |
| | 129 | { |
| | 130 | SKFLT phs; |
| | 131 | SKFLT incr; |
| | 132 | SKFLT out; |
| | 133 | |
| | 134 | phs = ph->phs; |
| | 135 | incr = ph->freq * ph->onedsr; |
| | 136 | |
| | 137 | out = phs; |
| | 138 | |
| | 139 | phs += incr; |
| | 140 | |
| | 141 | if (phs >= 1.0) { |
| | 142 | phs -= 1.0; |
| | 143 | } else if (phs < 0.0) { |
| | 144 | phs += 1.0; |
| | 145 | } |
| | 146 | |
| | 147 | ph->phs = phs; |
| | 148 | |
| | 149 | return out; |
| | 150 | } |
| | 151 | #+END_SRC |