1 // Copyright 2012 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com)
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 // Square oscillator generated from a timer.
19 
20 #include <avr/pgmspace.h>
21 
22 #include "avrlibx/utils/op.h"
23 
24 #include "edges/resources.h"
25 #include "edges/timer_oscillator.h"
26 
27 namespace edges {
28 
29 using namespace avrlibx;
30 
31 static const int16_t kOctave = 12 << 7;
32 static const int16_t kFirstNoteNormalMode = 24 << 7;
33 static const int16_t kFirstNoteLFOMode = -(12 << 7);
34 
35 const uint8_t pulse_widths[] = {
36   128, 85, 64, 32, 13, 10
37 };
38 
UpdateTimerParameters(int16_t pitch,PulseWidth pulse_width)39 void TimerOscillator::UpdateTimerParameters(
40     int16_t pitch,
41     PulseWidth pulse_width) {
42   int8_t shifts = 0;
43 
44   // Lowest note: E0.
45   pitch -= prescaler_ == TIMER_PRESCALER_CLK_64 \
46       ? kFirstNoteLFOMode
47       : kFirstNoteNormalMode;
48 
49   // Transpose the lowest octave up.
50   while (pitch < 0) {
51     pitch += kOctave;
52   }
53   while (pitch >= kOctave) {
54     pitch -= kOctave;
55     ++shifts;
56   }
57   uint16_t index_integral = U16ShiftRight4(pitch);
58   uint16_t index_fractional = U8U8Mul(pitch & 0xf, 16);
59   uint16_t count = pgm_read_word(lut_res_timer_count + index_integral);
60   uint16_t next = pgm_read_word(lut_res_timer_count + index_integral + 1);
61   count -= U16U8MulShift8(count - next, index_fractional);
62 
63   period_ = count >> shifts;
64   uint8_t pw = pulse_width == PULSE_WIDTH_CV_CONTROLLED
65       ? cv_pw_
66       : pulse_widths[pulse_width];
67   value_ = U16U8MulShift8(period_, pw);
68 }
69 
70 }  // namespace edges
71