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