| @@ -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 |