14 | static inline int FIX_SQU(int a)
15 | {
16 | return FIX_MPY(a, a);
17 | }
18 |
19 | /**
20 | * Fixed point Log2
21 | *
22 | * https://stackoverflow.com/questions/4657468/fast-fixed-point-pow-log-exp-and-sqrt
23 | */
24 | template
25 | static inline int FIX_LOG2(unsigned int x)
26 | {
27 | unsigned int b = 1U << (p - 1);
28 | int y = 0;
29 |
30 | if (p < 1 || p > 31) {
31 | return INT_MAX; // indicates an error
32 | }
33 |
34 | if (x == 0) {
35 | return INT_MIN; // represents negative infinity
36 | }
37 |
38 | while (x < 1U << p) {
39 | x <<= 1;
40 | y -= 1U << p;
41 | }
42 |
43 | while (x >= 2U << p) {
44 | x >>= 1;
45 | y += 1U << p;
46 | }
47 |
48 | unsigned long z = x;
49 |
50 | for (int i = 0; i < p; i++) {
51 | z = (z * z + (1 << (p-1))) >> p;
52 | if (z >= 2 << p) {
53 | z >>= 1;
54 | y += b;
55 | }
56 | b >>= 1;
57 | }
58 |
59 | return y;
60 | }
61 |
--------------------------------------------------------------------------------
/fix_fft.cpp:
--------------------------------------------------------------------------------
1 | #include "fix_fft.h"
2 |
3 | const short Fixed15FFT::Sinewave[Fixed15FFT::SAMPLE_SIZE] = {
4 | 0, 201, 402, 603, 804, 1005, 1206, 1406,
5 | 1607, 1808, 2009, 2209, 2410, 2610, 2811, 3011,
6 | 3211, 3411, 3611, 3811, 4011, 4210, 4409, 4608,
7 | 4807, 5006, 5205, 5403, 5601, 5799, 5997, 6195,
8 | 6392, 6589, 6786, 6982, 7179, 7375, 7571, 7766,
9 | 7961, 8156, 8351, 8545, 8739, 8932, 9126, 9319,
10 | 9511, 9703, 9895, 10087, 10278, 10469, 10659, 10849,
11 | 11038, 11227, 11416, 11604, 11792, 11980, 12166, 12353,
12 | 12539, 12724, 12909, 13094, 13278, 13462, 13645, 13827,
13 | 14009, 14191, 14372, 14552, 14732, 14911, 15090, 15268,
14 | 15446, 15623, 15799, 15975, 16150, 16325, 16499, 16672,
15 | 16845, 17017, 17189, 17360, 17530, 17699, 17868, 18036,
16 | 18204, 18371, 18537, 18702, 18867, 19031, 19194, 19357,
17 | 19519, 19680, 19840, 20000, 20159, 20317, 20474, 20631,
18 | 20787, 20942, 21096, 21249, 21402, 21554, 21705, 21855,
19 | 22004, 22153, 22301, 22448, 22594, 22739, 22883, 23027,
20 | 23169, 23311, 23452, 23592, 23731, 23869, 24006, 24143,
21 | 24278, 24413, 24546, 24679, 24811, 24942, 25072, 25201,
22 | 25329, 25456, 25582, 25707, 25831, 25954, 26077, 26198,
23 | 26318, 26437, 26556, 26673, 26789, 26905, 27019, 27132,
24 | 27244, 27355, 27466, 27575, 27683, 27790, 27896, 28001,
25 | 28105, 28208, 28309, 28410, 28510, 28608, 28706, 28802,
26 | 28897, 28992, 29085, 29177, 29268, 29358, 29446, 29534,
27 | 29621, 29706, 29790, 29873, 29955, 30036, 30116, 30195,
28 | 30272, 30349, 30424, 30498, 30571, 30643, 30713, 30783,
29 | 30851, 30918, 30984, 31049, 31113, 31175, 31236, 31297,
30 | 31356, 31413, 31470, 31525, 31580, 31633, 31684, 31735,
31 | 31785, 31833, 31880, 31926, 31970, 32014, 32056, 32097,
32 | 32137, 32176, 32213, 32249, 32284, 32318, 32350, 32382,
33 | 32412, 32441, 32468, 32495, 32520, 32544, 32567, 32588,
34 | 32609, 32628, 32646, 32662, 32678, 32692, 32705, 32717,
35 | 32727, 32736, 32744, 32751, 32757, 32761, 32764, 32766,
36 | 32767, 32766, 32764, 32761, 32757, 32751, 32744, 32736,
37 | 32727, 32717, 32705, 32692, 32678, 32662, 32646, 32628,
38 | 32609, 32588, 32567, 32544, 32520, 32495, 32468, 32441,
39 | 32412, 32382, 32350, 32318, 32284, 32249, 32213, 32176,
40 | 32137, 32097, 32056, 32014, 31970, 31926, 31880, 31833,
41 | 31785, 31735, 31684, 31633, 31580, 31525, 31470, 31413,
42 | 31356, 31297, 31236, 31175, 31113, 31049, 30984, 30918,
43 | 30851, 30783, 30713, 30643, 30571, 30498, 30424, 30349,
44 | 30272, 30195, 30116, 30036, 29955, 29873, 29790, 29706,
45 | 29621, 29534, 29446, 29358, 29268, 29177, 29085, 28992,
46 | 28897, 28802, 28706, 28608, 28510, 28410, 28309, 28208,
47 | 28105, 28001, 27896, 27790, 27683, 27575, 27466, 27355,
48 | 27244, 27132, 27019, 26905, 26789, 26673, 26556, 26437,
49 | 26318, 26198, 26077, 25954, 25831, 25707, 25582, 25456,
50 | 25329, 25201, 25072, 24942, 24811, 24679, 24546, 24413,
51 | 24278, 24143, 24006, 23869, 23731, 23592, 23452, 23311,
52 | 23169, 23027, 22883, 22739, 22594, 22448, 22301, 22153,
53 | 22004, 21855, 21705, 21554, 21402, 21249, 21096, 20942,
54 | 20787, 20631, 20474, 20317, 20159, 20000, 19840, 19680,
55 | 19519, 19357, 19194, 19031, 18867, 18702, 18537, 18371,
56 | 18204, 18036, 17868, 17699, 17530, 17360, 17189, 17017,
57 | 16845, 16672, 16499, 16325, 16150, 15975, 15799, 15623,
58 | 15446, 15268, 15090, 14911, 14732, 14552, 14372, 14191,
59 | 14009, 13827, 13645, 13462, 13278, 13094, 12909, 12724,
60 | 12539, 12353, 12166, 11980, 11792, 11604, 11416, 11227,
61 | 11038, 10849, 10659, 10469, 10278, 10087, 9895, 9703,
62 | 9511, 9319, 9126, 8932, 8739, 8545, 8351, 8156,
63 | 7961, 7766, 7571, 7375, 7179, 6982, 6786, 6589,
64 | 6392, 6195, 5997, 5799, 5601, 5403, 5205, 5006,
65 | 4807, 4608, 4409, 4210, 4011, 3811, 3611, 3411,
66 | 3211, 3011, 2811, 2610, 2410, 2209, 2009, 1808,
67 | 1607, 1406, 1206, 1005, 804, 603, 402, 201,
68 | 0, -201, -402, -603, -804, -1005, -1206, -1406,
69 | -1607, -1808, -2009, -2209, -2410, -2610, -2811, -3011,
70 | -3211, -3411, -3611, -3811, -4011, -4210, -4409, -4608,
71 | -4807, -5006, -5205, -5403, -5601, -5799, -5997, -6195,
72 | -6392, -6589, -6786, -6982, -7179, -7375, -7571, -7766,
73 | -7961, -8156, -8351, -8545, -8739, -8932, -9126, -9319,
74 | -9511, -9703, -9895, -10087, -10278, -10469, -10659, -10849,
75 | -11038, -11227, -11416, -11604, -11792, -11980, -12166, -12353,
76 | -12539, -12724, -12909, -13094, -13278, -13462, -13645, -13827,
77 | -14009, -14191, -14372, -14552, -14732, -14911, -15090, -15268,
78 | -15446, -15623, -15799, -15975, -16150, -16325, -16499, -16672,
79 | -16845, -17017, -17189, -17360, -17530, -17699, -17868, -18036,
80 | -18204, -18371, -18537, -18702, -18867, -19031, -19194, -19357,
81 | -19519, -19680, -19840, -20000, -20159, -20317, -20474, -20631,
82 | -20787, -20942, -21096, -21249, -21402, -21554, -21705, -21855,
83 | -22004, -22153, -22301, -22448, -22594, -22739, -22883, -23027,
84 | -23169, -23311, -23452, -23592, -23731, -23869, -24006, -24143,
85 | -24278, -24413, -24546, -24679, -24811, -24942, -25072, -25201,
86 | -25329, -25456, -25582, -25707, -25831, -25954, -26077, -26198,
87 | -26318, -26437, -26556, -26673, -26789, -26905, -27019, -27132,
88 | -27244, -27355, -27466, -27575, -27683, -27790, -27896, -28001,
89 | -28105, -28208, -28309, -28410, -28510, -28608, -28706, -28802,
90 | -28897, -28992, -29085, -29177, -29268, -29358, -29446, -29534,
91 | -29621, -29706, -29790, -29873, -29955, -30036, -30116, -30195,
92 | -30272, -30349, -30424, -30498, -30571, -30643, -30713, -30783,
93 | -30851, -30918, -30984, -31049, -31113, -31175, -31236, -31297,
94 | -31356, -31413, -31470, -31525, -31580, -31633, -31684, -31735,
95 | -31785, -31833, -31880, -31926, -31970, -32014, -32056, -32097,
96 | -32137, -32176, -32213, -32249, -32284, -32318, -32350, -32382,
97 | -32412, -32441, -32468, -32495, -32520, -32544, -32567, -32588,
98 | -32609, -32628, -32646, -32662, -32678, -32692, -32705, -32717,
99 | -32727, -32736, -32744, -32751, -32757, -32761, -32764, -32766,
100 | };
101 |
102 | const short Fixed15FFT::Window[Fixed15FFT::N_WAVE] = {
103 | 2621, 2621, 2622, 2623, 2625, 2628, 2631, 2635,
104 | 2639, 2644, 2649, 2655, 2662, 2669, 2676, 2685,
105 | 2693, 2703, 2713, 2723, 2734, 2746, 2758, 2771,
106 | 2784, 2798, 2812, 2827, 2843, 2859, 2876, 2893,
107 | 2910, 2929, 2948, 2967, 2987, 3008, 3029, 3050,
108 | 3073, 3095, 3119, 3142, 3167, 3192, 3217, 3243,
109 | 3270, 3297, 3325, 3353, 3382, 3411, 3441, 3471,
110 | 3502, 3533, 3565, 3598, 3631, 3664, 3698, 3733,
111 | 3768, 3804, 3840, 3877, 3914, 3952, 3990, 4029,
112 | 4068, 4108, 4148, 4189, 4230, 4272, 4314, 4357,
113 | 4401, 4444, 4489, 4534, 4579, 4625, 4671, 4718,
114 | 4765, 4813, 4861, 4910, 4959, 5009, 5059, 5110,
115 | 5161, 5213, 5265, 5317, 5370, 5424, 5478, 5532,
116 | 5587, 5642, 5698, 5754, 5811, 5868, 5926, 5984,
117 | 6042, 6101, 6160, 6220, 6280, 6341, 6402, 6464,
118 | 6525, 6588, 6651, 6714, 6777, 6841, 6906, 6970,
119 | 7036, 7101, 7167, 7234, 7300, 7368, 7435, 7503,
120 | 7571, 7640, 7709, 7779, 7849, 7919, 7989, 8060,
121 | 8132, 8203, 8275, 8348, 8420, 8493, 8567, 8641,
122 | 8715, 8789, 8864, 8939, 9015, 9090, 9167, 9243,
123 | 9320, 9397, 9474, 9552, 9630, 9708, 9787, 9866,
124 | 9945, 10024, 10104, 10184, 10264, 10345, 10426, 10507,
125 | 10588, 10670, 10752, 10834, 10917, 11000, 11082, 11166,
126 | 11249, 11333, 11417, 11501, 11586, 11670, 11755, 11840,
127 | 11926, 12011, 12097, 12183, 12269, 12355, 12442, 12529,
128 | 12616, 12703, 12790, 12878, 12966, 13054, 13142, 13230,
129 | 13318, 13407, 13496, 13585, 13674, 13763, 13852, 13942,
130 | 14031, 14121, 14211, 14301, 14391, 14482, 14572, 14662,
131 | 14753, 14844, 14935, 15026, 15117, 15208, 15299, 15391,
132 | 15482, 15574, 15665, 15757, 15849, 15940, 16032, 16124,
133 | 16216, 16308, 16400, 16493, 16585, 16677, 16769, 16862,
134 | 16954, 17046, 17139, 17231, 17324, 17416, 17509, 17601,
135 | 17694, 17786, 17879, 17971, 18064, 18156, 18248, 18341,
136 | 18433, 18526, 18618, 18710, 18803, 18895, 18987, 19079,
137 | 19171, 19263, 19355, 19447, 19539, 19631, 19722, 19814,
138 | 19905, 19997, 20088, 20179, 20271, 20362, 20453, 20543,
139 | 20634, 20725, 20815, 20906, 20996, 21086, 21176, 21266,
140 | 21356, 21446, 21535, 21625, 21714, 21803, 21892, 21981,
141 | 22069, 22158, 22246, 22334, 22422, 22509, 22597, 22684,
142 | 22772, 22859, 22945, 23032, 23118, 23204, 23290, 23376,
143 | 23462, 23547, 23632, 23717, 23802, 23886, 23970, 24054,
144 | 24138, 24222, 24305, 24388, 24471, 24553, 24635, 24717,
145 | 24799, 24880, 24962, 25042, 25123, 25203, 25283, 25363,
146 | 25443, 25522, 25601, 25679, 25758, 25836, 25913, 25991,
147 | 26068, 26144, 26221, 26297, 26373, 26448, 26523, 26598,
148 | 26673, 26747, 26820, 26894, 26967, 27040, 27112, 27184,
149 | 27256, 27327, 27398, 27469, 27539, 27609, 27678, 27747,
150 | 27816, 27884, 27952, 28020, 28087, 28154, 28220, 28286,
151 | 28352, 28417, 28482, 28546, 28610, 28674, 28737, 28800,
152 | 28862, 28924, 28985, 29046, 29107, 29167, 29227, 29286,
153 | 29345, 29404, 29462, 29519, 29576, 29633, 29689, 29745,
154 | 29800, 29855, 29910, 29964, 30017, 30070, 30123, 30175,
155 | 30226, 30277, 30328, 30378, 30428, 30477, 30526, 30574,
156 | 30622, 30669, 30716, 30763, 30808, 30854, 30899, 30943,
157 | 30987, 31030, 31073, 31115, 31157, 31198, 31239, 31280,
158 | 31319, 31359, 31397, 31436, 31473, 31511, 31547, 31583,
159 | 31619, 31654, 31689, 31723, 31757, 31790, 31822, 31854,
160 | 31885, 31916, 31947, 31976, 32006, 32034, 32063, 32090,
161 | 32117, 32144, 32170, 32196, 32221, 32245, 32269, 32292,
162 | 32315, 32337, 32359, 32380, 32400, 32420, 32440, 32459,
163 | 32477, 32495, 32512, 32529, 32545, 32560, 32575, 32590,
164 | 32603, 32617, 32629, 32642, 32653, 32664, 32675, 32685,
165 | 32694, 32703, 32711, 32719, 32726, 32732, 32738, 32744,
166 | 32748, 32753, 32756, 32759, 32762, 32764, 32765, 32766,
167 | 32767, 32766, 32765, 32764, 32762, 32759, 32756, 32753,
168 | 32748, 32744, 32738, 32732, 32726, 32719, 32711, 32703,
169 | 32694, 32685, 32675, 32664, 32653, 32642, 32629, 32617,
170 | 32603, 32590, 32575, 32560, 32545, 32529, 32512, 32495,
171 | 32477, 32459, 32440, 32420, 32400, 32380, 32359, 32337,
172 | 32315, 32292, 32269, 32245, 32221, 32196, 32170, 32144,
173 | 32117, 32090, 32063, 32034, 32006, 31976, 31947, 31916,
174 | 31885, 31854, 31822, 31790, 31757, 31723, 31689, 31654,
175 | 31619, 31583, 31547, 31511, 31473, 31436, 31397, 31359,
176 | 31319, 31280, 31239, 31198, 31157, 31115, 31073, 31030,
177 | 30987, 30943, 30899, 30854, 30808, 30763, 30716, 30669,
178 | 30622, 30574, 30526, 30477, 30428, 30378, 30328, 30277,
179 | 30226, 30175, 30123, 30070, 30017, 29964, 29910, 29855,
180 | 29800, 29745, 29689, 29633, 29576, 29519, 29462, 29404,
181 | 29345, 29286, 29227, 29167, 29107, 29046, 28985, 28924,
182 | 28862, 28800, 28737, 28674, 28610, 28546, 28482, 28417,
183 | 28352, 28286, 28220, 28154, 28087, 28020, 27952, 27884,
184 | 27816, 27747, 27678, 27609, 27539, 27469, 27398, 27327,
185 | 27256, 27184, 27112, 27040, 26967, 26894, 26820, 26747,
186 | 26673, 26598, 26523, 26448, 26373, 26297, 26221, 26144,
187 | 26068, 25991, 25913, 25836, 25758, 25679, 25601, 25522,
188 | 25443, 25363, 25283, 25203, 25123, 25042, 24962, 24880,
189 | 24799, 24717, 24635, 24553, 24471, 24388, 24305, 24222,
190 | 24138, 24054, 23970, 23886, 23802, 23717, 23632, 23547,
191 | 23462, 23376, 23290, 23204, 23118, 23032, 22945, 22859,
192 | 22772, 22684, 22597, 22509, 22422, 22334, 22246, 22158,
193 | 22069, 21981, 21892, 21803, 21714, 21625, 21535, 21446,
194 | 21356, 21266, 21176, 21086, 20996, 20906, 20815, 20725,
195 | 20634, 20543, 20453, 20362, 20271, 20179, 20088, 19997,
196 | 19905, 19814, 19722, 19631, 19539, 19447, 19355, 19263,
197 | 19171, 19079, 18987, 18895, 18803, 18710, 18618, 18526,
198 | 18433, 18341, 18248, 18156, 18064, 17971, 17879, 17786,
199 | 17694, 17601, 17509, 17416, 17324, 17231, 17139, 17046,
200 | 16954, 16862, 16769, 16677, 16585, 16493, 16400, 16308,
201 | 16216, 16124, 16032, 15940, 15849, 15757, 15665, 15574,
202 | 15482, 15391, 15299, 15208, 15117, 15026, 14935, 14844,
203 | 14753, 14662, 14572, 14482, 14391, 14301, 14211, 14121,
204 | 14031, 13942, 13852, 13763, 13674, 13585, 13496, 13407,
205 | 13318, 13230, 13142, 13054, 12966, 12878, 12790, 12703,
206 | 12616, 12529, 12442, 12355, 12269, 12183, 12097, 12011,
207 | 11926, 11840, 11755, 11670, 11586, 11501, 11417, 11333,
208 | 11249, 11166, 11082, 11000, 10917, 10834, 10752, 10670,
209 | 10588, 10507, 10426, 10345, 10264, 10184, 10104, 10024,
210 | 9945, 9866, 9787, 9708, 9630, 9552, 9474, 9397,
211 | 9320, 9243, 9167, 9090, 9015, 8939, 8864, 8789,
212 | 8715, 8641, 8567, 8493, 8420, 8348, 8275, 8203,
213 | 8132, 8060, 7989, 7919, 7849, 7779, 7709, 7640,
214 | 7571, 7503, 7435, 7368, 7300, 7234, 7167, 7101,
215 | 7036, 6970, 6906, 6841, 6777, 6714, 6651, 6588,
216 | 6525, 6464, 6402, 6341, 6280, 6220, 6160, 6101,
217 | 6042, 5984, 5926, 5868, 5811, 5754, 5698, 5642,
218 | 5587, 5532, 5478, 5424, 5370, 5317, 5265, 5213,
219 | 5161, 5110, 5059, 5009, 4959, 4910, 4861, 4813,
220 | 4765, 4718, 4671, 4625, 4579, 4534, 4489, 4444,
221 | 4401, 4357, 4314, 4272, 4230, 4189, 4148, 4108,
222 | 4068, 4029, 3990, 3952, 3914, 3877, 3840, 3804,
223 | 3768, 3733, 3698, 3664, 3631, 3598, 3565, 3533,
224 | 3502, 3471, 3441, 3411, 3382, 3353, 3325, 3297,
225 | 3270, 3243, 3217, 3192, 3167, 3142, 3119, 3095,
226 | 3073, 3050, 3029, 3008, 2987, 2967, 2948, 2929,
227 | 2910, 2893, 2876, 2859, 2843, 2827, 2812, 2798,
228 | 2784, 2771, 2758, 2746, 2734, 2723, 2713, 2703,
229 | 2693, 2685, 2676, 2669, 2662, 2655, 2649, 2644,
230 | 2639, 2635, 2631, 2628, 2625, 2623, 2622, 2621,
231 | };
232 |
--------------------------------------------------------------------------------
/fix_fft.h:
--------------------------------------------------------------------------------
1 | /**
2 | * Fixed point FFT
3 | *
4 | * https://forum.arduino.cc/t/16-bit-fft-on-arduino/72061/19
5 | */
6 |
7 |
8 | #include
9 | #include "fix.h"
10 |
11 | class Fixed15FFT {
12 | public:
13 | constexpr static int N_WAVE = 1024;
14 | constexpr static int LOG2_N_WAVE = 10;
15 | /*
16 | Since we only use 3/4 of N_WAVE, we define only
17 | this many samples, in order to conserve data space.
18 | */
19 | constexpr static int SAMPLE_SIZE = N_WAVE-N_WAVE/4;
20 |
21 | private:
22 | static const short Sinewave[SAMPLE_SIZE];
23 | static const short Window[N_WAVE];
24 |
25 | public:
26 |
27 | template
28 | static void apply_window(T v[])
29 | {
30 | static_assert(std::is_integral::value, "Parameters must be integral.");
31 | int i;
32 | constexpr int n = N_WAVE;
33 | for (i = 0; i < n; ++i) {
34 | v[i] = FIX_MPY(v[i], Window[i]);
35 | }
36 | }
37 |
38 | /*
39 | fix_fft() - perform forward/inverse fast Fourier transform.
40 | fr[n],fi[n] are real and imaginary arrays, both INPUT AND
41 | RESULT (in-place FFT), with 0 <= n < 2**m; set inverse to
42 | 0 for forward transform (FFT), or 1 for iFFT.
43 | */
44 | template
45 | static void calc_fft(T fr[], T fi[])
46 | {
47 | static_assert(std::is_integral::value, "Parameters must be integral.");
48 | int mr, i, j, l, k, m, istep;
49 | int qr, qi, tr, ti, wr, wi;
50 |
51 | constexpr int n = N_WAVE;
52 | constexpr int nn = n - 1;
53 |
54 | mr = 0;
55 |
56 | /* decimation in time - re-order data */
57 | for (m=1; m<=nn; ++m) {
58 | l = n;
59 | do
60 | {
61 | l >>= 1;
62 | } while (mr+l > nn);
63 | mr = (mr & (l-1)) + l;
64 | if (mr <= m)
65 | continue;
66 |
67 | tr = fr[m];
68 | fr[m] = fr[mr];
69 | fr[mr] = tr;
70 | ti = fi[m];
71 | fi[m] = fi[mr];
72 | fi[mr] = ti;
73 | }
74 |
75 | l = 1;
76 | k = LOG2_N_WAVE-1;
77 | while (l < n)
78 | {
79 | /*
80 | it may not be obvious, but the shift will be
81 | performed on each data point exactly once,
82 | during this pass.
83 | */
84 | istep = l << 1;
85 | for (m=0; m>= 1;
92 | wi >>= 1;
93 | for (i=m; i>15;
97 | //ti = ((long)wr*(long)fi[j] + (long)wi*(long)fr[j])>>15;
98 | tr = FIX_MPY(wr,fr[j]) - FIX_MPY(wi,fi[j]);
99 | ti = FIX_MPY(wr,fi[j]) + FIX_MPY(wi,fr[j]);
100 | qr = fr[i];
101 | qr >>= 1;
102 | fr[i] = qr + tr;
103 | fr[j] = qr - tr;
104 | qi = fi[i];
105 | qi >>= 1;
106 | fi[i] = qi + ti;
107 | fi[j] = qi - ti;
108 | }
109 | }
110 | --k;
111 | l = istep;
112 | }
113 | }
114 |
115 | };
116 |
--------------------------------------------------------------------------------
/m5Cardputer_audiospectrum.ino:
--------------------------------------------------------------------------------
1 | /***
2 | M5Cardputer Audio Spectrum Display, Oscilloscope, and Tuner
3 | Portado por Aurélio Monteiro Avanzi
4 |
5 | ***/
6 |
7 | /*
8 | M5StickC Audio Spectrum Display, Oscilloscope, and Tuner
9 |
10 | Copyright 2020 KIRA Ryouta
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy of
13 | this software and associated documentation files (the "Software"), to deal in
14 | the Software without restriction, including without limitation the rights to
15 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
16 | the Software, and to permit persons to whom the Software is furnished to do so,
17 | subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in all
20 | copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | SOFTWARE.
29 |
30 | */
31 |
32 | /* ESP8266/32 Audio Spectrum Analyser on an SSD1306/SH1106 Display
33 | * The MIT License (MIT) Copyright (c) 2017 by David Bird.
34 | * The formulation and display of an AUdio Spectrum using an ESp8266 or ESP32 and SSD1306 or SH1106 OLED Display using a Fast Fourier Transform
35 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
36 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
37 | * publish, distribute, but not to use it commercially for profit making or to sub-license and/or to sell copies of the Software or to
38 | * permit persons to whom the Software is furnished to do so, subject to the following conditions:
39 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
40 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
41 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
42 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
43 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44 | * See more at http://dsbird.org.uk
45 | */
46 |
47 | // M5StickC Audio Spectrum : 2019.06.01 : macsbug
48 | // https://macsbug.wordpress.com/2019/06/01/
49 | // Audio Spectrum Display with M5STACK : 2017.12.31 : macsbug
50 | // https://macsbug.wordpress.com/2017/12/31/audio-spectrum-display-with-m5stack/
51 | // https://github.com/tobozo/ESP32-8-Octave-Audio-Spectrum-Display/tree/wrover-kit
52 | // https://github.com/G6EJD/ESP32-8266-Audio-Spectrum-Display
53 | // https://github.com/kosme/arduinoFFT
54 |
55 |
56 | // David Bird (https://github.com/G6EJD/ESP32-8266-Audio-Spectrum-Display)
57 | // tobozo (https://github.com/tobozo/ESP32-Audio-Spectrum-Waveform-Display)
58 | // macsbug (https://macsbug.wordpress.com/)
59 | // KIRA Ryouta (https://github.com/KKQ-KKQ/m5stickc-audiospectrum)
60 |
61 |
62 | #pragma GCC optimize ("O3")
63 | #include
64 | #include
65 | #include
66 | #include
67 | #include
68 | #include "dywapitchtrack.h"
69 | #include "fix_fft.h"
70 |
71 | #define PIN_CLK 43
72 | #define PIN_DATA 46
73 | #define SAMPLES 1024 // Must be a power of 2
74 | #define READ_LEN (2 * SAMPLES)
75 | #define TFT_WIDTH 240
76 | #define TFT_HEIGHT 135
77 | #define BANDS 8
78 | #define BANDS_WIDTH ( TFT_WIDTH / BANDS )
79 | #define BANDS_PADDING 8
80 | #define BAR_WIDTH ( BANDS_WIDTH - BANDS_PADDING )
81 | #define NOISE_FLOOR 1
82 | #define AMPLIFIER (1 << 2)
83 | #define MAGNIFY 3
84 | #define RSHIFT 13
85 | #define RSHIFT2 1
86 | #define OSC_NOISEFLOOR 100
87 | #define OSC_SAMPLES DYWAPT_SAMPLESIZE
88 | #define OSC_SKIPCOUNT (OSC_SAMPLES/SAMPLES)
89 | #define OSC_EXTRASKIP 0
90 |
91 | //#define MAXBUFSIZE (2*SAMPLES)
92 | #define MAXBUFSIZE OSC_SAMPLES
93 |
94 | struct eqBand {
95 | const char *freqname;
96 | int peak;
97 | int lastpeak;
98 | uint16_t lastval;
99 | };
100 |
101 | enum : uint8_t {
102 | ModeSpectrumBars,
103 | ModeOscilloscope,
104 | ModeTuner,
105 | ModeCount,
106 | };
107 |
108 | static uint8_t runmode = 0;
109 |
110 | static volatile bool semaphore = false;
111 | static volatile bool needinit = true;
112 |
113 | static eqBand audiospectrum[BANDS] = {
114 | // freqname,peak,lastpeak,lastval,
115 | { ".1k", 0 },
116 | { ".2k", 0 },
117 | { ".5k", 0 },
118 | { " 1k", 0 },
119 | { " 2k", 0 },
120 | { " 4k", 0 },
121 | { " 8k", 0 },
122 | { "16k", 0 }
123 | };
124 |
125 | static int vTemp[2][MAXBUFSIZE];
126 | static uint8_t curbuf = 0;
127 | static uint16_t colormap[TFT_HEIGHT];//color palette for the band meter(pre-fill in setup)
128 |
129 | static dywapitchtracker pitchTracker;
130 | static LGFX_Sprite sprite(&M5Cardputer.Display);
131 |
132 | static int bufposcount = 0;
133 |
134 | static const char *notestr[12] = {
135 | "C ", "C#", "D ", "D#", "E ", "F ", "F#", "G ", "G#", "A ", "A#", "B "
136 | };
137 |
138 | static uint16_t oscbuf[2][OSC_SAMPLES];
139 | #if OSC_EXTRASKIP > 0
140 | static int skipcount = 0;
141 | #endif
142 |
143 | void i2sInit(){
144 | i2s_config_t i2s_config = {
145 | .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
146 | .sample_rate = DYWAPT_SAMPLERATE,
147 | .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, //is fixed at 12bit,stereo,MSB
148 | .channel_format = I2S_CHANNEL_FMT_ALL_RIGHT,
149 | .communication_format = I2S_COMM_FORMAT_I2S,
150 | .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
151 | .dma_buf_count = 2,
152 | .dma_buf_len = 128,
153 | };
154 | i2s_pin_config_t pin_config;
155 | pin_config.bck_io_num = I2S_PIN_NO_CHANGE;
156 | pin_config.ws_io_num = PIN_CLK;
157 | pin_config.data_out_num = I2S_PIN_NO_CHANGE;
158 | pin_config.data_in_num = PIN_DATA;
159 | i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
160 | i2s_set_pin(I2S_NUM_0, &pin_config);
161 | i2s_set_clk(I2S_NUM_0, DYWAPT_SAMPLERATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
162 | }
163 |
164 | void setup() {
165 | M5.begin();
166 | setCpuFrequencyMhz(80);
167 | M5Cardputer.Display.setRotation(1);
168 | M5Cardputer.Display.fillScreen(BLACK);
169 |
170 | sprite.createSprite(TFT_WIDTH, TFT_HEIGHT);
171 | sprite.setTextSize(1);
172 |
173 | i2sInit();
174 | //M5Cardputer.Speaker.tone(440, 50);
175 |
176 | for(uint8_t i=0;i g)? 255./r : 255./g;
182 | r *= mag;
183 | g *= mag;
184 | colormap[i] = M5Cardputer.Display.color565((uint8_t)r,(uint8_t)g,0); // Modified by KKQ-KKQ
185 | }
186 |
187 | int core = 1 - xPortGetCoreID();
188 | xTaskCreatePinnedToCore(looptask,"calctask",32768,NULL,1,NULL,core);
189 | }
190 |
191 | void initMode() {
192 | M5Cardputer.Display.fillRect(0, 0,
193 | TFT_WIDTH, TFT_HEIGHT, BLACK);
194 | switch (runmode) {
195 | case ModeSpectrumBars:
196 | M5Cardputer.Display.setTextSize(1);
197 | M5Cardputer.Display.setTextColor(LIGHTGREY);
198 | for (byte band = 0; band < BANDS; band++) {
199 | M5Cardputer.Display.setCursor(BANDS_WIDTH*band + 2, 0);
200 | M5Cardputer.Display.print(audiospectrum[band].freqname);
201 | }
202 | break;
203 |
204 | case ModeOscilloscope:
205 | {
206 | sprite.setTextColor(GREEN);
207 | dywapitch_inittracking(&pitchTracker);
208 | }
209 | break;
210 |
211 | case ModeTuner:
212 | sprite.setTextSize(1);
213 | break;
214 | }
215 | }
216 |
217 | void showSpectrumBars(){
218 | int *vTemp_ = vTemp[curbuf^1];
219 |
220 | Fixed15FFT::apply_window(vTemp_);
221 | Fixed15FFT::calc_fft(vTemp_, vTemp_ + SAMPLES);
222 |
223 | int values[BANDS] = {};
224 | for (int i = 2; i < (SAMPLES/2); i++){
225 | // Don't use sample 0 and only first SAMPLES/2 are usable.
226 | // Each array element represents a frequency and its value the amplitude.
227 | int ampsq = vTemp_[i] * vTemp_[i] + vTemp_[i + SAMPLES] * vTemp_[i + SAMPLES];
228 | if (ampsq > NOISE_FLOOR) {
229 | byte bandNum = getBand(i);
230 | if(bandNum != 8 && ampsq > values[bandNum]) {
231 | values[bandNum] = ampsq;
232 | }
233 | }
234 | }
235 | for (byte band = 0; band < BANDS; band++) {
236 | int log2 = FIX_LOG2<15-RSHIFT>(values[band]);
237 | if (log2 > -AMPLIFIER) {
238 | displayBand(band, ((log2 + AMPLIFIER) * MAGNIFY) >> RSHIFT2);
239 | } else {
240 | displayBand(band, 0);
241 | }
242 | }
243 | //long vnow = millis();
244 | for (byte band = 0; band < BANDS; band++) {
245 | if (audiospectrum[band].peak > 0) {
246 | audiospectrum[band].peak -= 2;
247 | }
248 | if(audiospectrum[band].peak <= 0) {
249 | audiospectrum[band].peak = 0;
250 | }
251 | // only draw if peak changed
252 | if(audiospectrum[band].lastpeak != audiospectrum[band].peak) {
253 | // delete last peak
254 | uint16_t hpos = BANDS_WIDTH*band + (BANDS_PADDING/2);
255 | M5Cardputer.Display.drawFastHLine(hpos,TFT_HEIGHT-audiospectrum[band].lastpeak,BAR_WIDTH,BLACK);
256 | audiospectrum[band].lastpeak = audiospectrum[band].peak;
257 | uint16_t ypos = TFT_HEIGHT - audiospectrum[band].peak;
258 | M5Cardputer.Display.drawFastHLine(hpos, ypos,
259 | BAR_WIDTH, colormap[ypos]);
260 | }
261 | }
262 | }
263 |
264 | void displayBand(int band, int dsize){
265 | uint16_t hpos = BANDS_WIDTH*band + (BANDS_PADDING/2);
266 | if (dsize < 0) dsize = 0;
267 | if(dsize>TFT_HEIGHT-10) {
268 | dsize = TFT_HEIGHT-10; // leave some hspace for text
269 | }
270 | if(dsize < audiospectrum[band].lastval) {
271 | // lower value, delete some lines
272 | M5Cardputer.Display.fillRect(hpos, TFT_HEIGHT-audiospectrum[band].lastval,
273 | BAR_WIDTH, audiospectrum[band].lastval - dsize,BLACK);
274 | }
275 | for (int s = 0; s <= dsize; s=s+4){
276 | uint16_t ypos = TFT_HEIGHT - s;
277 | M5Cardputer.Display.drawFastHLine(hpos, ypos, BAR_WIDTH, colormap[ypos]);
278 | }
279 | if (dsize > audiospectrum[band].peak){audiospectrum[band].peak = dsize;}
280 | audiospectrum[band].lastval = dsize;
281 | }
282 |
283 | byte getBand(int i) {
284 | if (i >= 2 && i < 4 ) return 0; // 125Hz
285 | if (i >= 4 && i < 8 ) return 1; // 250Hz
286 | if (i >= 8 && i < 16 ) return 2; // 500Hz
287 | if (i >= 16 && i < 32 ) return 3; // 1000Hz
288 | if (i >= 32 && i < 64 ) return 4; // 2000Hz
289 | if (i >= 64 && i < 128) return 5; // 4000Hz
290 | if (i >= 128 && i < 256) return 6; // 8000Hz
291 | if (i >= 256 && i < 512) return 7; // 16000Hz
292 | return 8;
293 | }
294 |
295 | float calcNumSamples(float f) {
296 | if (f == 0.0) return SAMPLES/2;
297 | float s = (float)(DYWAPT_SAMPLERATE * 2) / f;
298 | if (s > (float)OSC_SAMPLES) {
299 | do {
300 | s *= 0.5;
301 | } while (s > (float)OSC_SAMPLES);
302 | }
303 | return s;
304 | }
305 |
306 | void showFreq(float freq) {
307 | if (freq > 0) {
308 | char strbuf[16];
309 | sprintf(strbuf, "%8.2fHz", freq);
310 | sprite.drawString(strbuf, 0, 0, 1);
311 | float fnote = log2(freq)*12 - 36.376316562f;
312 | int note = fnote + 0.5f;
313 | if (note >= 0) {
314 | sprite.setCursor(TFT_WIDTH/2 - 4, 0);
315 | sprite.print(notestr[note % 12]);
316 | sprite.print(note / 12 - 1);
317 | float cent = (fnote - note) * 100;
318 | sprintf(strbuf, "%.1fcents", cent);
319 | sprite.drawRightString(strbuf, TFT_WIDTH, 0, 1);
320 | }
321 | }
322 | }
323 |
324 | void showOscilloscope()
325 | {
326 | uint16_t i,j;
327 | uint16_t *oscbuf_ = oscbuf[curbuf^1];
328 | int *vTemp_ = vTemp[curbuf ^ 1];
329 | float freq = dywapitch_computepitch(&pitchTracker, vTemp_);
330 | #if OSC_EXTRASKIP > 0
331 | if (skipcount < OSC_EXTRASKIP) {
332 | ++skipcount;
333 | return;
334 | }
335 | skipcount = 0;
336 | #endif
337 | uint16_t s = calcNumSamples(freq);
338 | float mx = (float)TFT_WIDTH / s;
339 | float my;
340 | uint16_t maxV = 0;
341 | uint16_t minV = 65535;
342 | uint16_t offset = 0;
343 | for (i = 0; i < s; ++i) {
344 | if (maxV < oscbuf_[i]) maxV = oscbuf_[i];
345 | if (minV > oscbuf_[i]) {
346 | minV = oscbuf_[i];
347 | if (i + s <= OSC_SAMPLES) offset = i;
348 | }
349 | }
350 | if (maxV - minV > OSC_NOISEFLOOR) {
351 | my = (float)(TFT_HEIGHT-10) / (maxV - minV);
352 | }
353 | else {
354 | my = (float)(TFT_HEIGHT-10) / OSC_NOISEFLOOR;
355 | minV = (((int)maxV + (int)minV) >> 1) - OSC_NOISEFLOOR/2;
356 | }
357 | sprite.fillSprite(BLACK);
358 | uint16_t y = TFT_HEIGHT - (oscbuf_[offset] - minV) * my;
359 | for (i = 1; i < s; ++i) {
360 | uint16_t y2 = TFT_HEIGHT - (oscbuf_[offset + i] - minV) * my;
361 | sprite.drawLine((uint16_t)((i-1) * mx), y,
362 | (uint16_t)(i * mx), y2, LIGHTGREY);
363 | y = y2;
364 | }
365 | showFreq(freq);
366 | sprite.pushSprite(0,0);
367 | }
368 |
369 | void showTuner() {
370 | int *vTemp_ = vTemp[curbuf ^ 1];
371 | float freq = dywapitch_computepitch(&pitchTracker, vTemp_);
372 | float fnote;
373 | int note;
374 | if (freq > 0) {
375 | fnote = log2(freq)*12 - 36.376316562;
376 | note = fnote + 0.5;
377 | }
378 | else {
379 | note = -1;
380 | }
381 | uint32_t bgcolor, fgcolor;
382 | if (note >= 0) {
383 | float cent = (fnote - note) * 100;
384 | if (abs(cent) < 2.) {
385 | bgcolor = GREEN;
386 | fgcolor = BLACK;
387 | }
388 | else {
389 | bgcolor = DARKGREY;
390 | fgcolor = BLACK;
391 | }
392 | sprite.fillSprite(bgcolor);
393 | sprite.fillRect(0, 0, TFT_WIDTH, TFT_HEIGHT, bgcolor);
394 | sprite.setTextColor(fgcolor);
395 | sprite.drawRect(2, 36, TFT_WIDTH-3, TFT_HEIGHT-40, fgcolor);
396 | sprite.drawRect(TFT_WIDTH/2 + 1, 36, 1, TFT_HEIGHT - 40, fgcolor);
397 | sprite.fillCircle(((float)TFT_WIDTH/2 + 1) + cent * ((float)(TFT_WIDTH-3)/100), (TFT_HEIGHT+34)/2, 5, fgcolor);
398 | char strbuf[8];
399 | sprintf(strbuf, "%s%d", notestr[note % 12], note / 12 - 1);
400 | sprite.drawCentreString(strbuf, TFT_WIDTH/2, 3, 4);
401 | }
402 | else {
403 | sprite.fillSprite(DARKGREY);
404 | sprite.drawRect(2, 36, TFT_WIDTH-3, TFT_HEIGHT-40, BLACK);
405 | sprite.drawLine(TFT_WIDTH/2 + 1, 36, TFT_WIDTH/2 + 1, TFT_HEIGHT - 4, BLACK);
406 | }
407 | sprite.pushSprite(0,0);
408 | }
409 |
410 | void looptask(void *) {
411 | while (1) {
412 | if (needinit) {
413 | initMode();
414 | needinit = false;
415 | }
416 | if (semaphore) {
417 | switch(runmode) {
418 | case ModeSpectrumBars:
419 | showSpectrumBars();
420 | break;
421 |
422 | case ModeOscilloscope:
423 | showOscilloscope();
424 | break;
425 |
426 | case ModeTuner:
427 | showTuner();
428 | break;
429 | }
430 | semaphore = false;
431 | }
432 | else {
433 | vTaskDelay(10);
434 | }
435 | }
436 | }
437 |
438 | void loop() {
439 | M5Cardputer.update();
440 | if (!M5Cardputer.BtnA.isPressed()) {
441 | ++runmode;
442 | if (runmode >= ModeCount) runmode = 0;
443 |
444 | bufposcount = 0;
445 | needinit = true;;
446 | }
447 | uint16_t i,j;
448 | j = bufposcount * SAMPLES;
449 | uint16_t *adcBuffer = &oscbuf[curbuf][j];
450 | size_t bytesread;
451 | i2s_read(I2S_NUM_0,(char*)adcBuffer,READ_LEN,&bytesread,portMAX_DELAY);
452 | int32_t dc = 0;
453 | for (int i = 0; i < SAMPLES; ++i) {
454 | dc += adcBuffer[i];
455 | }
456 | dc /= SAMPLES;
457 |
458 | switch(runmode) {
459 | case ModeSpectrumBars:
460 | for (int i = 0; i < SAMPLES; ++i) {
461 |
462 | vTemp[curbuf][i] = (int)adcBuffer[i] - dc;
463 | vTemp[curbuf][i + SAMPLES] = 0;
464 | }
465 | curbuf ^= 1;
466 | semaphore = true;
467 | break;
468 | case ModeOscilloscope:
469 | for (i = 0; i < SAMPLES; ++i) {
470 | vTemp[curbuf][i + j] = (int)adcBuffer[i] - dc;
471 | }
472 | if (++bufposcount >= OSC_SKIPCOUNT) {
473 | bufposcount = 0;
474 | curbuf ^= 1;
475 | semaphore = true;
476 | }
477 | break;
478 | case ModeTuner:
479 | j = bufposcount * SAMPLES;
480 | for (i = 0; i < SAMPLES; ++i) {
481 | vTemp[curbuf][i + j] = (int)adcBuffer[i] - dc;
482 | }
483 | if (++bufposcount >= OSC_SKIPCOUNT) {
484 | bufposcount = 0;
485 | curbuf ^= 1;
486 | semaphore = true;
487 | }
488 | break;
489 | }
490 | }
491 |
--------------------------------------------------------------------------------
/m5Cardputer_audiospectrum.ino.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cyberwisk/m5Cardputer_audiospectrum/7c36f3537cdbcc41d1e7598f4d877c05a54fce28/m5Cardputer_audiospectrum.ino.bin
--------------------------------------------------------------------------------