-
Notifications
You must be signed in to change notification settings - Fork 4
/
calc.rb
165 lines (150 loc) · 5.74 KB
/
calc.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#
# Wavetable calculator for the dr1.a firmware.
#
# Copyright (C) 2017-2018 John A. Tuffen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Questions/queries can be directed to [email protected]
#
def CopyrightNotice(f)
f.puts("/*")
f.puts(" * Wavetable calculator for the dr1.a firmware.")
f.puts(" *")
f.puts(" * Copyright (C) 2017-2018 John A. Tuffen")
f.puts(" *")
f.puts(" * This program is free software: you can redistribute it and/or modify")
f.puts(" * it under the terms of the GNU General Public License as published by")
f.puts(" * the Free Software Foundation, either version 3 of the License, or")
f.puts(" * (at your option) any later version.")
f.puts(" *")
f.puts(" * This program is distributed in the hope that it will be useful,")
f.puts(" * but WITHOUT ANY WARRANTY; without even the implied warranty of")
f.puts(" * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the")
f.puts(" * GNU General Public License for more details.")
f.puts(" *")
f.puts(" * You should have received a copy of the GNU General Public License")
f.puts(" * along with this program. If not, see <http://www.gnu.org/licenses/>.")
f.puts(" *")
f.puts(" * Questions/queries can be directed to [email protected]")
f.puts(" */")
end
OUTFILEROOT=File.basename(__FILE__,".rb")
# We're going to generate a lookup table to map ADC values
# (0-1023) to phase-increments. Each octave will be split
# into 128 steps, giving a range of 8 octaves.
# PWM rate is 250kHz, we'd like an integer-ratio of this
# for our sample rate. Also, bear in mind that we are
# generating 2 oscillators, so the interrupt will actually
# be running at twice this rate...
SRATE=50000
PHASECOUNTERBITS = 16
INTBITS = 10
FRACBITS = PHASECOUNTERBITS-INTBITS
# Now then. Although we are declaring that the WTSIZE
# is this, the actual size is going to be half of
# this because that's what Wolfgang Palm did: realise
# that, if you keep waves symmetrical, you only
# need half of the space…
WTSIZE = (2.0**INTBITS).to_i
# root frequency is that of A0 (according to Dodge and Jerse pg.37)
ROOT = 27.50
DACBITS = 10
DACRANGE = (2.0**DACBITS).to_i
# 128 steps per octave means that the 10 bit ADC covers 8 octaves
OCTSTEPS=128
OCTS=(DACRANGE/OCTSTEPS)
File.open("#{OUTFILEROOT}.h",'w') do |f|
f.puts "/**********************************************************************/"
f.puts "/* This file is autogenerated - DO NOT EDIT! (change calc.rb instead) */"
f.puts "/**********************************************************************/\n"
CopyrightNotice(f)
f.puts "#include \"arduino.h\"\n"
f.puts "#define SRATE (#{SRATE}L)"
f.puts "#define WTSIZE (#{WTSIZE/2}L)"
f.puts "#define FRACBITS (#{FRACBITS}L)"
f.puts "#define HALF (0x#{(1 << (FRACBITS-1)).to_s(16)})"
f.puts "#define DACRANGE (#{DACRANGE}L)"
f.puts "extern const uint16_t octaveLookup[DACRANGE];"
f.puts "extern const uint8_t sine[WTSIZE];"
f.puts "extern const uint8_t ramp[WTSIZE];"
f.puts "extern const uint8_t sq[WTSIZE];"
f.puts "extern const uint8_t triangle[WTSIZE];"
f.puts
end
# Let's calculate a single octave's worth of increments,
# subsequent octaves are simply power-of-2 multiples of those
# base octave values
File.open("#{OUTFILEROOT}.ino",'w') do |f|
f.puts "/**********************************************************************/"
f.puts "/* This file is autogenerated - DO NOT EDIT! (change calc.rb instead) */"
f.puts "/**********************************************************************/\n"
CopyrightNotice(f)
f.puts "#include \"#{OUTFILEROOT}.h\""
f.puts "const uint16_t octaveLookup[DACRANGE] PROGMEM = {"
(0...DACRANGE).each do |n|
freq = ROOT * (2.0**(n.to_f/OCTSTEPS))
# Dodge & Jerse (pg.67) say that the sample increment is WTSIZE*(fo/fs)
si = (WTSIZE * freq)/SRATE
sifixed = ((WTSIZE << FRACBITS)* freq)/SRATE
f.puts " 0x#{sifixed.to_i.to_s(16)}, // #{freq} [#{si}]"
end
f.puts "};\n"
# generate a sine table.
TWO_PI = 2 * Math::PI
LINELENGTH = 16
f.puts "const uint8_t sine[WTSIZE] PROGMEM = {"
f.print " "
(0...WTSIZE/2).each do |n|
# although we only need half of the wave in the table,
# we still treat the wave as if it were whole.
v = Math::sin(TWO_PI * n.to_f/WTSIZE.to_f)
f.print "0x#{(((v*127)+128)/2).to_i.to_s(16)}, "
if ((n % LINELENGTH) == (LINELENGTH-1))
f.print "\n "
end
end
f.puts "};"
f.puts "const uint8_t ramp[WTSIZE] PROGMEM = {"
f.print " "
(0...WTSIZE/2).each do |n|
f.print "0x#{((128+(n/4))/2).to_s(16)}, "
if ((n % LINELENGTH) == (LINELENGTH-1))
f.print "\n "
end
end
f.puts "};"
f.puts "const uint8_t sq[WTSIZE] PROGMEM = {"
f.print " "
(0...WTSIZE/2).each do |n|
f.print "0x7f, "
if ((n % LINELENGTH) == (LINELENGTH-1))
f.print "\n "
end
end
f.puts "};"
f.puts "const uint8_t triangle[WTSIZE] PROGMEM = {"
f.print " "
(0...WTSIZE/2).each do |n|
if (n < WTSIZE/4)
f.print "0x#{((128+n/2)/2).to_s(16)}, "
else
f.print "0x#{((383-n/2)/2).to_s(16)}, "
end
if ((n % LINELENGTH) == (LINELENGTH-1))
f.print "\n "
end
end
f.puts "};"
end