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