#pragma once namespace Fem { class Oscillator { private: const uint32_t NOTELUT[128] = { 0x000b29a6, 0x000bd392, 0x000c879a, 0x000d4656, 0x000e1069, 0x000ee680, 0x000fc953, 0x0010b9a2, 0x0011b83c, 0x0012c5f9, 0x0013e3c0, 0x00151285, 0x0016534c, 0x0017a725, 0x00190f34, 0x001a8cac, 0x001c20d2, 0x001dcd01, 0x001f92a6, 0x00217345, 0x00237078, 0x00258bf2, 0x0027c780, 0x002a250b, 0x002ca698, 0x002f4e4b, 0x00321e68, 0x00351958, 0x003841a5, 0x003b9a03, 0x003f254d, 0x0042e68a, 0x0046e0f0, 0x004b17e4, 0x004f8f01, 0x00544a17, 0x00594d30, 0x005e9c96, 0x00643cd1, 0x006a32b0, 0x0070834b, 0x00773407, 0x007e4a9b, 0x0085cd15, 0x008dc1e0, 0x00962fc9, 0x009f1e02, 0x00a8942e, 0x00b29a61, 0x00bd392c, 0x00c879a3, 0x00d46561, 0x00e10697, 0x00ee680e, 0x00fc9536, 0x010b9a2a, 0x011b83c1, 0x012c5f92, 0x013e3c05, 0x0151285c, 0x016534c3, 0x017a7259, 0x0190f346, 0x01a8cac3, 0x01c20d2e, 0x01dcd01d, 0x01f92a6c, 0x02173455, 0x02370783, 0x0258bf25, 0x027c780b, 0x02a250b9, 0x02ca6986, 0x02f4e4b3, 0x0321e68d, 0x03519586, 0x03841a5d, 0x03b9a03a, 0x03f254d9, 0x042e68ab, 0x046e0f06, 0x04b17e4b, 0x04f8f016, 0x0544a173, 0x0594d30d, 0x05e9c967, 0x0643cd1a, 0x06a32b0c, 0x070834ba, 0x07734074, 0x07e4a9b2, 0x085cd157, 0x08dc1e0d, 0x0962fc96, 0x09f1e02d, 0x0a8942e6, 0x0b29a61a, 0x0bd392cf, 0x0c879a35, 0x0d465619, 0x0e106974, 0x0ee680e9, 0x0fc95364, 0x10b9a2ae, 0x11b83c1a, 0x12c5f92c, 0x13e3c05a, 0x151285cd, 0x16534c34, 0x17a7259f, 0x190f346a, 0x1a8cac33, 0x1c20d2e8, 0x1dcd01d2, 0x1f92a6c8, 0x2173455d, 0x23707834, 0x258bf258, 0x27c780b4, 0x2a250b9b, 0x2ca69869, 0x2f4e4b3f, 0x321e68d4, 0x35195867, 0x3841a5d0, 0x3b9a03a5, 0x3f254d90, 0x42e68abb }; // lanczos2 kernel LUT const int16_t LANCZOS2[512] = { 0x0, 0x0, -0x1, -0x2, -0x3, -0x4, -0x6, -0x8, -0xa, -0xc, -0xf, -0x12, -0x16, -0x19, -0x1d, -0x21, -0x26, -0x2b, -0x30, -0x35, -0x3b, -0x41, -0x47, -0x4d, -0x54, -0x5b, -0x63, -0x6a, -0x73, -0x7b, -0x84, -0x8d, -0x96, -0x9f, -0xa9, -0xb3, -0xbe, -0xc9, -0xd4, -0xdf, -0xeb, -0xf7, -0x103, -0x110, -0x11d, -0x12a, -0x138, -0x145, -0x154, -0x162, -0x171, -0x180, -0x18f, -0x19f, -0x1af, -0x1bf, -0x1cf, -0x1e0, -0x1f1, -0x202, -0x214, -0x226, -0x238, -0x24a, -0x25d, -0x270, -0x283, -0x296, -0x2aa, -0x2be, -0x2d2, -0x2e6, -0x2fb, -0x310, -0x325, -0x33a, -0x350, -0x365, -0x37b, -0x391, -0x3a8, -0x3be, -0x3d5, -0x3ec, -0x403, -0x41a, -0x431, -0x449, -0x461, -0x478, -0x490, -0x4a9, -0x4c1, -0x4d9, -0x4f2, -0x50a, -0x523, -0x53c, -0x554, -0x56d, -0x586, -0x59f, -0x5b9, -0x5d2, -0x5eb, -0x604, -0x61d, -0x637, -0x650, -0x669, -0x682, -0x69c, -0x6b5, -0x6ce, -0x6e7, -0x700, -0x719, -0x732, -0x74b, -0x764, -0x77d, -0x795, -0x7ae, -0x7c6, -0x7de, -0x7f7, -0x80e, -0x826, -0x83e, -0x855, -0x86c, -0x883, -0x89a, -0x8b1, -0x8c7, -0x8dd, -0x8f3, -0x909, -0x91e, -0x933, -0x948, -0x95c, -0x970, -0x984, -0x997, -0x9aa, -0x9bd, -0x9cf, -0x9e1, -0x9f3, -0xa04, -0xa14, -0xa25, -0xa34, -0xa44, -0xa53, -0xa61, -0xa6f, -0xa7c, -0xa89, -0xa96, -0xaa2, -0xaad, -0xab8, -0xac2, -0xacc, -0xad5, -0xadd, -0xae5, -0xaec, -0xaf3, -0xaf9, -0xafe, -0xb03, -0xb07, -0xb0a, -0xb0d, -0xb0e, -0xb10, -0xb10, -0xb10, -0xb0e, -0xb0d, -0xb0a, -0xb07, -0xb02, -0xafd, -0xaf7, -0xaf1, -0xae9, -0xae1, -0xad8, -0xace, -0xac3, -0xab7, -0xaaa, -0xa9c, -0xa8e, -0xa7e, -0xa6e, -0xa5c, -0xa4a, -0xa36, -0xa22, -0xa0d, -0x9f7, -0x9df, -0x9c7, -0x9ae, -0x993, -0x978, -0x95b, -0x93e, -0x91f, -0x8ff, -0x8df, -0x8bd, -0x89a, -0x876, -0x851, -0x82a, -0x803, -0x7da, -0x7b1, -0x786, -0x75a, -0x72d, -0x6fe, -0x6cf, -0x69e, -0x66c, -0x639, -0x605, -0x5d0, -0x599, -0x561, -0x528, -0x4ee, -0x4b2, -0x475, -0x437, -0x3f8, -0x3b8, -0x376, -0x333, -0x2ef, -0x2a9, -0x263, -0x21b, -0x1d2, -0x187, -0x13b, -0xee, -0xa0, -0x50, 0x0, 0x52, 0xa5, 0xfa, 0x150, 0x1a7, 0x1ff, 0x259, 0x2b4, 0x310, 0x36e, 0x3cd, 0x42d, 0x48e, 0x4f1, 0x555, 0x5ba, 0x621, 0x688, 0x6f1, 0x75c, 0x7c7, 0x834, 0x8a2, 0x911, 0x981, 0x9f3, 0xa66, 0xada, 0xb4f, 0xbc6, 0xc3e, 0xcb7, 0xd31, 0xdac, 0xe29, 0xea6, 0xf25, 0xfa5, 0x1026, 0x10a8, 0x112c, 0x11b0, 0x1236, 0x12bd, 0x1345, 0x13ce, 0x1458, 0x14e3, 0x156f, 0x15fc, 0x168b, 0x171a, 0x17aa, 0x183c, 0x18ce, 0x1962, 0x19f6, 0x1a8b, 0x1b22, 0x1bb9, 0x1c51, 0x1cea, 0x1d84, 0x1e1f, 0x1ebb, 0x1f58, 0x1ff5, 0x2094, 0x2133, 0x21d3, 0x2274, 0x2315, 0x23b8, 0x245b, 0x24ff, 0x25a3, 0x2649, 0x26ef, 0x2796, 0x283d, 0x28e5, 0x298e, 0x2a37, 0x2ae1, 0x2b8c, 0x2c37, 0x2ce2, 0x2d8e, 0x2e3b, 0x2ee8, 0x2f96, 0x3044, 0x30f3, 0x31a2, 0x3251, 0x3301, 0x33b2, 0x3462, 0x3513, 0x35c4, 0x3676, 0x3728, 0x37da, 0x388c, 0x393f, 0x39f2, 0x3aa5, 0x3b58, 0x3c0b, 0x3cbf, 0x3d72, 0x3e26, 0x3eda, 0x3f8d, 0x4041, 0x40f5, 0x41a9, 0x425d, 0x4310, 0x43c4, 0x4478, 0x452b, 0x45de, 0x4692, 0x4745, 0x47f7, 0x48aa, 0x495c, 0x4a0e, 0x4ac0, 0x4b72, 0x4c23, 0x4cd4, 0x4d84, 0x4e34, 0x4ee4, 0x4f93, 0x5042, 0x50f0, 0x519e, 0x524b, 0x52f8, 0x53a4, 0x5450, 0x54fb, 0x55a6, 0x564f, 0x56f9, 0x57a1, 0x5849, 0x58f0, 0x5996, 0x5a3c, 0x5ae0, 0x5b84, 0x5c28, 0x5cca, 0x5d6b, 0x5e0c, 0x5eab, 0x5f4a, 0x5fe8, 0x6085, 0x6120, 0x61bb, 0x6255, 0x62ee, 0x6385, 0x641c, 0x64b1, 0x6546, 0x65d9, 0x666b, 0x66fc, 0x678b, 0x681a, 0x68a7, 0x6933, 0x69bd, 0x6a47, 0x6acf, 0x6b56, 0x6bdb, 0x6c5f, 0x6ce1, 0x6d63, 0x6de3, 0x6e61, 0x6ede, 0x6f59, 0x6fd3, 0x704c, 0x70c3, 0x7138, 0x71ac, 0x721e, 0x728f, 0x72fe, 0x736c, 0x73d8, 0x7442, 0x74ab, 0x7512, 0x7577, 0x75db, 0x763d, 0x769d, 0x76fb, 0x7758, 0x77b3, 0x780c, 0x7864, 0x78b9, 0x790d, 0x795f, 0x79af, 0x79fe, 0x7a4a, 0x7a95, 0x7ade, 0x7b25, 0x7b6a, 0x7bad, 0x7bee, 0x7c2d, 0x7c6b, 0x7ca6, 0x7ce0, 0x7d17, 0x7d4d, 0x7d81, 0x7db2, 0x7de2, 0x7e10, 0x7e3b, 0x7e65, 0x7e8d, 0x7eb3, 0x7ed6, 0x7ef8, 0x7f18, 0x7f35, 0x7f51, 0x7f6b, 0x7f82, 0x7f98, 0x7fab, 0x7fbd, 0x7fcc, 0x7fda, 0x7fe5, 0x7fee, 0x7ff5, 0x7ffa, 0x7ffd, 0x7fff }; // 4-point lanczos2 resampling filter int16_t lanczos(uint32_t pitchAcc, int16_t* waveSample) { uint32_t sampleIndex = pitchAcc >> 21; uint32_t sampleError = (pitchAcc >> 13) & 0x000000ff; int32_t filteredSample = waveSample[sampleIndex] * LANCZOS2[255 - sampleError]; filteredSample += (waveSample[(sampleIndex + 1) & 0x000007ff] * LANCZOS2[511 - sampleError]); filteredSample += (waveSample[(sampleIndex + 2) & 0x000007ff] * LANCZOS2[256 + sampleError]); filteredSample += (waveSample[(sampleIndex + 3) & 0x000007ff] * LANCZOS2[sampleError]); filteredSample /= 32768; // should actually be /= 32767 but who's counting return (int16_t)filteredSample; } // linear interpolation of two samples int16_t lerp(int16_t a, int16_t b, uint16_t t) { int32_t lerpSum = (int32_t)(0x2000 - t) * (int32_t)a; lerpSum += (int32_t)t * (int32_t)b; return (int16_t)(lerpSum / 0x2000); } public: Oscillator() { this->pitchAcc = 0; } uint32_t pitchAcc; void reset() { pitchAcc = 0; } int16_t gen(uint8_t note, uint16_t wavePos, int16_t** waveTable) { // pitchAcc += noteFrequency(note); pitchAcc += NOTELUT[note]; // interpolate between two waves in the wavetable uint8_t waveIndex = wavePos >> 13; int16_t* waveA = waveTable[waveIndex]; int16_t* waveB = waveTable[(waveIndex + 1) & 0x07]; int16_t sampleA = lanczos(pitchAcc, waveA); int16_t sampleB = lanczos(pitchAcc, waveB); return lerp (sampleA, sampleB, wavePos & 0x1fff); } }; }