1 // Copyright 2012 Olivier Gillet.
2 //
3 // Author: Olivier Gillet (olivier@mutable-instruments.net)
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
15 //
16 // -----------------------------------------------------------------------------
17 //
18 // Digital oscillator generated from a timer.
19
20 #include "edges/digital_oscillator.h"
21
22 #include "avrlibx/utils/op.h"
23
24 #include "edges/audio_buffer.h"
25 #include "edges/resources.h"
26
27 namespace edges {
28
29 static const uint8_t kMaxZone = 7;
30 static const int16_t kOctave = 12 * 128;
31 static const int16_t kPitchTableStart = 116 * 128;
32
33 using namespace avrlibx;
34
35 #define UPDATE_PHASE \
36 phase = U24Add(phase, phase_increment);
37
38 #define BEGIN_SAMPLE_LOOP \
39 uint24_t phase; \
40 uint24_t phase_increment; \
41 phase_increment.integral = phase_increment_.integral; \
42 phase_increment.fractional = phase_increment_.fractional; \
43 phase.integral = phase_.integral; \
44 phase.fractional = phase_.fractional; \
45 uint8_t size = kAudioBlockSize; \
46 while (size--) {
47
48 #define END_SAMPLE_LOOP \
49 } \
50 phase_.integral = phase.integral; \
51 phase_.fractional = phase.fractional;
52
ComputePhaseIncrement()53 void DigitalOscillator::ComputePhaseIncrement() {
54 int16_t ref_pitch = pitch_ - kPitchTableStart;
55 uint8_t num_shifts = shape_ >= OSC_PITCHED_NOISE ? 0 : 1;
56 while (ref_pitch < 0) {
57 ref_pitch += kOctave;
58 ++num_shifts;
59 }
60 uint24_t increment;
61 uint16_t pitch_lookup_index_integral = U16ShiftRight4(ref_pitch);
62 uint8_t pitch_lookup_index_fractional = U8ShiftLeft4(ref_pitch);
63 uint16_t increment16 = ResourcesManager::Lookup<uint16_t, uint16_t>(
64 lut_res_oscillator_increments, pitch_lookup_index_integral);
65 uint16_t increment16_next = ResourcesManager::Lookup<uint16_t, uint16_t>(
66 lut_res_oscillator_increments, pitch_lookup_index_integral + 1);
67 increment.integral = increment16 + U16U8MulShift8(
68 increment16_next - increment16, pitch_lookup_index_fractional);
69 increment.fractional = 0;
70 while (num_shifts--) {
71 increment = U24ShiftRight(increment);
72 }
73
74 note_ = U15ShiftRight7(pitch_);
75 if (note_ < 12) {
76 note_ = 12;
77 }
78 phase_increment_ = increment;
79 }
80
Render()81 void DigitalOscillator::Render() {
82 if (gate_) {
83 ComputePhaseIncrement();
84 }
85 while (audio_buffer.writable() >= kAudioBlockSize) {
86 if (!gate_) {
87 RenderSilence();
88 } else {
89 RenderFn fn;
90 ResourcesManager::Load(fn_table_, shape_, &fn);
91 (this->*fn)();
92 }
93 }
94 }
95
InterpolateSample(const prog_uint8_t * table,uint16_t phase)96 static inline uint8_t InterpolateSample(
97 const prog_uint8_t* table,
98 uint16_t phase) {
99 uint8_t result;
100 uint8_t work;
101 asm(
102 "movw r30, %A2" "\n\t" // copy base address to r30:r31
103 "mov %1, %A3" "\n\t" // duplicate phase increment
104 "add %1, %A3" "\n\t" // duplicate
105 "adc r30, %B3" "\n\t" // duplicate
106 "adc r31, r1" "\n\t" // duplicate
107 "add r30, %B3" "\n\t" // duplicate
108 "adc r31, r1" "\n\t" // duplicate
109 "lpm %0, z+" "\n\t" // load sample[n]
110 "lpm r1, z+" "\n\t" // load sample[n+1]
111 "mul %1, r1" "\n\t" // multiply second sample by phaseL
112 "movw r30, r0" "\n\t" // result to accumulator
113 "com %1" "\n\t" // 255 - phaseL -> phaseL
114 "mul %1, %0" "\n\t" // multiply first sample by phaseL
115 "add r30, r0" "\n\t" // accumulate L
116 "adc r31, r1" "\n\t" // accumulate H
117 "eor r1, r1" "\n\t" // reset r1 after multiplication
118 "mov %0, r31" "\n\t" // use sum H as output
119 : "=r" (result), "=r" (work)
120 : "r" (table), "r" (phase)
121 : "r30", "r31"
122 );
123 return result;
124 }
125
InterpolateSample16(const prog_uint8_t * table,uint16_t phase)126 static inline uint16_t InterpolateSample16(
127 const prog_uint8_t* table,
128 uint16_t phase) {
129 uint16_t result;
130 uint8_t work;
131 asm(
132 "movw r30, %A2" "\n\t" // copy base address to r30:r31
133 "mov %1, %A3" "\n\t" // duplicate phase increment
134 "add %1, %A3" "\n\t" // duplicate
135 "adc r30, %B3" "\n\t" // duplicate
136 "adc r31, r1" "\n\t" // duplicate
137 "add r30, %B3" "\n\t" // duplicate
138 "adc r31, r1" "\n\t" // duplicate
139 "lpm %0, z+" "\n\t" // load sample[n]
140 "lpm r1, z+" "\n\t" // load sample[n+1]
141 "mul %1, r1" "\n\t" // multiply second sample by phaseL
142 "movw r30, r0" "\n\t" // result to accumulator
143 "com %1" "\n\t" // 255 - phaseL -> phaseL
144 "mul %1, %0" "\n\t" // multiply first sample by phaseL
145 "add r30, r0" "\n\t" // accumulate L
146 "adc r31, r1" "\n\t" // accumulate H
147 "eor r1, r1" "\n\t" // reset r1 after multiplication
148 "movw %0, r30" "\n\t" // use sum H as output
149 : "=r" (result), "=r" (work)
150 : "r" (table), "r" (phase)
151 : "r30", "r31"
152 );
153 return result;
154 }
155
156
InterpolateTwoTables(const prog_uint8_t * table_a,const prog_uint8_t * table_b,uint16_t phase,uint8_t gain_a,uint8_t gain_b)157 static inline uint16_t InterpolateTwoTables(
158 const prog_uint8_t* table_a, const prog_uint8_t* table_b,
159 uint16_t phase, uint8_t gain_a, uint8_t gain_b) {
160 uint16_t result = 0;
161 result += U8U8Mul(InterpolateSample(table_a, phase), gain_a);
162 result += U8U8Mul(InterpolateSample(table_b, phase), gain_b);
163 return result;
164 }
165
RenderSilence()166 void DigitalOscillator::RenderSilence() {
167 uint8_t size = kAudioBlockSize;
168 while (size--) {
169 audio_buffer.Overwrite(2048);
170 }
171 }
172
RenderSine()173 void DigitalOscillator::RenderSine() {
174 uint16_t aux_phase_increment = pgm_read_word(
175 lut_res_bitcrusher_increments + cv_pw_);
176 BEGIN_SAMPLE_LOOP
177 UPDATE_PHASE
178 aux_phase_ += aux_phase_increment;
179 if (aux_phase_ < aux_phase_increment || !aux_phase_increment) {
180 sample_ = InterpolateSample16(
181 wav_res_bandlimited_triangle_6,
182 phase.integral);
183 }
184 audio_buffer.Overwrite(sample_ >> 4);
185 END_SAMPLE_LOOP
186 }
187
RenderBandlimitedTriangle()188 void DigitalOscillator::RenderBandlimitedTriangle() {
189 uint8_t balance_index = U8Swap4(note_ - 12);
190 uint8_t gain_2 = balance_index & 0xf0;
191 uint8_t gain_1 = ~gain_2;
192
193 uint8_t wave_index = balance_index & 0xf;
194 uint8_t base_resource_id = (shape_ == OSC_NES_TRIANGLE)
195 ? WAV_RES_BANDLIMITED_NES_TRIANGLE_0
196 : WAV_RES_BANDLIMITED_TRIANGLE_0;
197
198 const prog_uint8_t* wave_1 = waveform_table[base_resource_id + wave_index];
199 wave_index = U8AddClip(wave_index, 1, kMaxZone);
200 const prog_uint8_t* wave_2 = waveform_table[base_resource_id + wave_index];
201
202 BEGIN_SAMPLE_LOOP
203 UPDATE_PHASE
204 uint16_t sample = InterpolateTwoTables(
205 wave_1, wave_2,
206 phase.integral, gain_1, gain_2);
207 audio_buffer.Overwrite(sample >> 4);
208 END_SAMPLE_LOOP
209 }
210
RenderNoiseNES()211 void DigitalOscillator::RenderNoiseNES() {
212 uint16_t rng_state = rng_state_;
213 uint16_t sample = sample_;
214 BEGIN_SAMPLE_LOOP
215 phase = U24Add(phase, phase_increment);
216 if (phase.integral < phase_increment.integral) {
217 uint8_t tap = rng_state >> 1;
218 if (shape_ == OSC_NES_NOISE_SHORT) {
219 tap >>= 5;
220 }
221 uint8_t random_bit = (rng_state ^ tap) & 1;
222 rng_state >>= 1;
223 if (random_bit) {
224 rng_state |= 0x4000;
225 sample = 0x0300;
226 } else {
227 sample = 0x0cff;
228 }
229 }
230 audio_buffer.Overwrite(sample);
231 END_SAMPLE_LOOP
232 rng_state_ = rng_state;
233 sample_ = sample;
234 }
235
RenderNoise()236 void DigitalOscillator::RenderNoise() {
237 uint16_t rng_state = rng_state_;
238 uint16_t sample = sample_;
239 BEGIN_SAMPLE_LOOP
240 phase = U24Add(phase, phase_increment);
241 if (phase.integral < phase_increment.integral) {
242 rng_state = (rng_state >> 1) ^ (-(rng_state & 1) & 0xb400);
243 sample = rng_state & 0x0fff;
244 sample = 512 + ((sample * 3) >> 2);
245 }
246 audio_buffer.Overwrite(sample);
247 END_SAMPLE_LOOP
248 rng_state_ = rng_state;
249 sample_ = sample;
250 }
251
252 /* static */
253 const DigitalOscillator::RenderFn DigitalOscillator::fn_table_[] PROGMEM = {
254 &DigitalOscillator::RenderBandlimitedTriangle,
255 &DigitalOscillator::RenderBandlimitedTriangle,
256 &DigitalOscillator::RenderNoise,
257 &DigitalOscillator::RenderNoiseNES,
258 &DigitalOscillator::RenderNoiseNES,
259 &DigitalOscillator::RenderSine,
260 };
261
262 } // namespace shruthi
263