From 86f5af554747a19dfaa044155000b1bb5277f41c Mon Sep 17 00:00:00 2001 From: Kacper Date: Fri, 25 Mar 2022 09:56:37 +0000 Subject: [PATCH 1/7] Trying to implement multiple notes --- src/main.cpp | 98 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 23 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bf0c762..900e2f5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,12 +12,13 @@ const uint32_t interval = 10; // Display update interval const uint32_t samplingRate = 44100; // Sampling rate const uint32_t canID = 0x123; // Variables -std::atomic currentStepSize; +std::atomic currentStepSize[12]; std::atomic keyArray[7]; std::atomic octave; std::atomic volume; std::atomic volumeFiner; std::atomic wave; +std::atomic pressedKeys; int8_t volumeHistory = 0; QueueHandle_t msgInQ; uint8_t RX_Message[8] = {0}; @@ -27,12 +28,16 @@ Knob K0(2,14,8); //Octave encoder Knob K1(0,6); //Waveform encoder Knob K3(0,10); //Volume encoder // Program Specific Structures -typedef struct{ - int32_t stepSize; - std::string note; -} Note; -const Note notes[] = { - {0, "None"}, {3185014, "C1"}, {3374405, "C1#"}, {3575058, "D1"}, {3787642, "D1#"}, {4012867, "E1"}, {4251484, "F1"}, {4504291, "F1#"}, {4772130, "G1"}, {5055895, "G1#"}, {5356535, "A1"}, {5675051, "A1#"}, {6012507, "B1"}, {6370029, "C2"}, {6748811, "C2#"}, {7150116, "D2"}, {7575284, "D2#"}, {8025734, "E2"}, {8502969, "F2"}, {9008582, "F2#"}, {9544260, "G2"}, {10111791, "G2#"}, {10713070, "A2"}, {11350102, "A2#"}, {12025014, "B2"}, {12740059, "C3"}, {13497622, "C3#"}, {14300233, "D3"}, {15150569, "D3#"}, {16051469, "E3"}, {17005939, "F3"}, {18017164, "F3#"}, {19088521, "G3"}, {20223583, "G3#"}, {21426140, "A3"}, {22700205, "A3#"}, {24050029, "B3"}, {25480118, "C4"}, {26995245, "C4#"}, {28600466, "D4"}, {30301138, "D4#"}, {32102938, "E4"}, {34011878, "F4"}, {36034329, "F4#"}, {38177042, "G4"}, {40447167, "G4#"}, {42852281, "A4"}, {45400410, "A4#"}, {48100059, "B4"}, {50960237, "C5"}, {53990491, "C5#"}, {57200933, "D5"}, {60602277, "D5#"}, {64205876, "E5"}, {68023756, "F5"}, {72068659, "F5#"}, {76354085, "G5"}, {80894335, "G5#"}, {85704562, "A5"}, {90800821, "A5#"}, {96200119, "B5"}, {101920475, "C6"}, {107980982, "C6#"}, {114401866, "D6"}, {121204555, "D6#"}, {128411753, "E6"}, {136047513, "F6"}, {144137319, "F6#"}, {152708170, "G6"}, {161788670, "G6#"}, {171409125, "A6"}, {181601642, "A6#"}, {192400238, "B6"}, {203840951, "C7"}, {215961965, "C7#"}, {228803732, "D7"}, {242409110, "D7#"}, {256823506, "E7"}, {272095026, "F7"}, {288274638, "F7#"}, {305416340, "G7"}, {323577341, "G7#"}, {342818251, "A7"}, {363203285, "A7#"}, {384800476, "B7"}}; +const int32_t notes[8][12] = { + {0}, + {3185014, 3374405, 3575058, 3787642, 4012867, 4251484, 4504291, 4772130, 5055895, 5356535, 5675051, 6012507}, + {6370029, 6748811, 7150116, 7575284, 8025734, 8502969, 9008582, 9544260, 10111791, 10713070, 11350102, 12025014}, + {12740059, 13497622, 14300233, 15150569, 16051469, 17005939, 18017164, 19088521, 20223583, 21426140, 22700205, 24050029}, + {25480118, 26995245, 28600466, 30301138, 32102938, 34011878, 36034329, 38177042, 40447167, 42852281, 45400410, 48100059}, + {50960237, 53990491, 57200933, 60602277, 64205876, 68023756, 72068659, 76354085, 80894335, 85704562, 90800821, 96200119}, + {101920475, 107980982, 114401866, 121204555, 128411753, 136047513, 144137319, 152708170, 161788670, 171409125, 181601642, 192400238}, + {203840951, 215961965, 228803732, 242409110, 256823506, 272095026, 288274638, 305416340, 323577341, 342818251, 363203285, 384800476} +}; enum waveform { SQUARE = 0, SAWTOOTH, @@ -141,31 +146,75 @@ uint16_t getTopKey() { for (uint8_t i = 0; i < 3; i++) { for (uint8_t j = 0; j < 4; j++) { if (keyArray[i] & (0x1 << j)) { - topKey = (octave - 1) * 12 + i * 4 + j + 1; + topKey = i * 4 + j; } } } return topKey; } +// Returns integer where each bit represents a separate key (1 = pressed, 0 = not pressed) +uint16_t getKeys() { + uint16_t keys = 0; + for (uint8_t i = 0; i < 3; i++) { + for (uint8_t j = 0; j < 4; j++) { + if (keyArray[i] & (0x1 << j)) { + keys += 0x1 << (j+i*4); + } + } + } + return keys; +} + +int8_t getKeyCount() { + uint8_t keyCount = 0; + for (uint8_t i = 0; i < 3; i++) { + for (uint8_t j = 0; j < 4; j++) { + if (keyArray[i] & (0x1 << j)) { + keyCount += 1; + } + } + } + return keyCount; +} + +int32_t mixInputs(int32_t *accs) { + int8_t keyCount = getKeyCount(); + int32_t output = 0; + if(keyCount!=0){ + for(int8_t i = 0; i<12; i++){ + if(wave==SAWTOOTH){ + output += (*(accs+i)/keyCount) >> 16; + }else if(wave==SQUARE){ + if(*(accs+i)<0){ + output += 0x8000/keyCount; + }else{ + output += 0; + } + }else if(wave==TRIANGLE){ + //TODO + }else if(wave==SINE){ + //TODO + } + } + } + return output; +} + // Interrupt driven routine to send waveform to DAC void sampleISR(){ - static int32_t phaseAcc = 0; - phaseAcc += currentStepSize; - int32_t Vout = 0; - if(wave==SAWTOOTH){ - Vout = phaseAcc >> 16; - }else if(wave==SQUARE){ - if(phaseAcc<0){ - Vout = 0x8000; + static int32_t phaseAcc[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; + int8_t keyCount = getKeyCount(); + uint16_t keys = getKeys(); + Serial.print(1); + for(int8_t i = 0; i<12; i++){ + if(keys & (0x1 << i)){ + phaseAcc[i] += notes[octave][i]; }else{ - Vout = 0; + phaseAcc[i] = 0; } - }else if(wave==TRIANGLE){ - //TODO - }else if(wave==SINE){ - //TODO } + int32_t Vout = mixInputs(phaseAcc); Vout = scaleVolume(Vout); analogWrite(OUTR_PIN, Vout + 128); } @@ -227,7 +276,7 @@ void scanKeysTask(void *pvParameters) { }else{ K3.changeLimitsVolume(0,10); }; - currentStepSize = notes[getTopKey()].stepSize; // Atomic Store + pressedKeys = getKeys(); K0.updateRotation(keyArray[4] & 0x4, keyArray[4] & 0x8); octave = K0.getRotation()/2; K1.updateRotation(keyArray[4] & 0x1, keyArray[4] & 0x2); @@ -250,7 +299,7 @@ void displayUpdateTask(void *pvParameters) { u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font uint16_t key = getTopKey(); - u8g2.drawStr(2, 10, notes[key].note.c_str()); // Print the current key + //u8g2.drawStr(2, 10, notes[key].note.c_str()); // Print the current key digitalToggle(LED_BUILTIN); u8g2.setCursor(2, 20); for (uint8_t i = 0; i < 7; i++) { @@ -261,6 +310,9 @@ void displayUpdateTask(void *pvParameters) { u8g2.print(RX_Message[1]); u8g2.print(RX_Message[2], HEX); + u8g2.setCursor(70, 30); + u8g2.print(getKeys()); + // Print waveform icon int K1_rot = K1.getRotation(); if(K1_rot<2){ From f44795a70e211fbd8298df89a4044fe2a49b8beb Mon Sep 17 00:00:00 2001 From: Aadi Desai <21363892+supleed2@users.noreply.github.com> Date: Fri, 25 Mar 2022 11:21:21 +0000 Subject: [PATCH 2/7] Minor cleanup --- docs/Note Step Calculation.xlsx | Bin 14698 -> 14676 bytes src/main.cpp | 52 +++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/docs/Note Step Calculation.xlsx b/docs/Note Step Calculation.xlsx index c15c705e4dc5008a261c85069cfa8c3f5fdd6461..972b1df58963d9670fcce201fa475cca7968339f 100644 GIT binary patch delta 6873 zcmZXZbySpLxAuqbMjGjokdp2gq&p;}k(>b~1cZm~2I+1Pkgh=y>28SukrE`O8_wW+ z);Z@}-~2Ue*1Fcd@4c`6+k2j8=k3hf+BtNzeqFLmkK`qE8sHF`B|&(9uX00|&l%KoV`RgxH_D03o{P6W^~hZ#s!u8P&`{NG;PbJ=_LI6fUHM}?FdIzBi34(c0S^74 zZUcYDX{0zC3D_IqrIsW-nXOHUZ+2uZYI{a8t?vMfBbk4)`C^e#;g|xq&h$8jh0%%A zV3#|>hGka9{7Hwlr13{u6hrMcN9ydzN~8#k*KhBZ0vSII6`u4Pzr5Cxo2Ongp~mV2 z-S-4`?dPGE1}o`HB+EsXNFmw#s$SFkug!;asCND4#Q^#lyqj$_mu6hYRnX~dp6t~H z8S<{(zMw*K@Gy>~_oh=gxmYrF_iW!_fi0`d6{??v8kFj=)T>mUa&2X9S__7h7&UlN z*SoBxrdfD=ag|B%Yfd})*!Z@&*rj5Ne&gv=F(yp8IpJLT80CtLstP+@ZKsJ{RCZvg-EPI0hfPZ%TSNpfOD8H6CC{+`@4C0Gsgm>rDKU=gT*rw zShT5);}N*P5yAXxL)e;*+YLk}x7)~oe|lkp(Y0CaKSi6(;+;}> zYptP7vn-#IttoJin>%!fzzBp+H07Z;5M-e(IQJW;vr2-7Mj@|`v0k4TS4_#{TS(J^ znwP{J+JF^P2=xfi}2&;SG>I?AmUAs>Qjezx-0WwB&28@(R75tm>SmMwIa?xVjI3lxaw?iYp)>;sm z;c^-}wT#QZE))NL%w{{}siz$Zoo~@ZFH;0a^Gi?8Rrf3YXg^)rlcy2)y$U2reV(*eB+g5QS2)_au6w7r*Gr{^#G z|D+9HZ6R>w=b<^3)bGLgKAQ0%`0R^O`ih@cCYJg&^ zA>%hN+jaha0s=L>0f8t$$z8%!Kz6bjG>exs?2v6=ntlbN_R3y{K|d3-dWxsi@3r{v z1f#C>1&w6KMc@!AsZqDI@)@$TbLY5koL~C#Q~hKVLOC6yFyT;f?4t0hT?L=1jtN80JCJPG!>X9wKNQGg3!bHX% z5k|w{_I@N436|p<^-p-_!s!=@eWve~f`z-;#NfE=h8il^Mb$Mq@f#K~S7LWu1IRo) z#k;pFAq8$0zhuubE@P|?0h~xyax&jA#lw&ia-&W&)|KH(dca66875+r+#AHh#)mgO zNIPth%o44W%2lMOX9o;^Fi@6QmEs(iF-t%jJuKtE`rS|v;IASwPwqEjFS zwkU=Q#yTlJ2W1{oU+B|G1ui+$qLbY4{azv0dM#P=4ZF&;@gkY|b}Q;;F9ebh3Dq3D zkMEJ&=r52Q7;?c$y5{P@Vj3b|i~QnYd`HE$tt^E-`IgYimw0MIb3qWtEzMlNl#QkN zjqOH5%Fl^Eqq{?UKylA`=$4>LeYs&xSG&%+__6eZ#lS7rqj&xps5oaij;1X!)ZCVS z+iE{r?>LXco~pLJrPe&Pf8Xv; zIs1xyaKBkKOX*VIJ(pIeYkP73>iY7m>;7{?p49C#==T0dO=CZ}#=74R>-=X8Z@x#5Vv?6m#`0%)PtCU#Rnc&0OFx33v z_@Hd%>;3f!OzGli@_qZQH0v-dThqAhEzB#wdVi_8dSr_VTMMpAt)b!bP?ebS^rU>ops|C z(na@)PW~#|q`W%vD;2y#LqhNKo8S|ZQx14`a^x+)Zx+_pj&FP1j4YcO1!e9^R-y3y zybVLIBq-6?rG=+-3{S)oiN?DbDLf3mqfOmwonAnaVdO3SJ(0lf2hr@cPdok#c?=_< zDB5jUNnfF~xnG@%W>pm?)w<3F2g+ke7^1D)yCoGU2Z z4(SeCcK$)PK<63nD2$MSa_Zg%YZ%KY<+@B1tcJz?d_9dyJN|hw=R%5#{LAL%Y^gh~ zV_2NCpwh8P+(_5PtJjXU%m0D_kcq+njh3}$om;TpOkrAE^@)b*rcx@WMqYgq>&M#- zh~)*>6k!K=>j&C)Ob)d>J=?=kDc88r_Jpgvl1o*5KW+{?=l6Y~cYkSwY@4`pYve~K zL_K}?7=knwFA}!ZM{`WbSLj&B@WkxQ1GYZ2b}1P{KxiJ$fF*67i4?ToEjxvC!G5_W z*Z*0OIs-NzO&6rEKS$DsfU2;PfS1a*g@W77%)7O>oT0!mN;=aJSn-!k4_?4upU)pn z^laW*vUI0)2}@jvJ>h5@1zTG+(<-#^e3g#LF$pn9uT2a$32|hEt1~j>GaZw3s1@$! z5ngmV9P6S3Stu!{$neqjb>7@3pPKs4LZdh4!+&xsaE`zwbPvfr*}0$NRAu(liJIspRDrb zDoi>LqCuS%{P(1?ARm{@Ci)J#zCRJliwWd&zb@Q!9ngIL}C!t>knlN7=THcfqGUo@b*W;8M!0wL0of zO2I+PAj1#)I%i#K)V?jeHMK6&lP(UFDlcL_k2LCT739Q?4soO;Njn5i#?<2qO|-H4 zcQA{d0bO}454e`j45S)%1mGw#bj!xIHa%<5>O3s8wshn6zNHrs(|}Ov)S%HCM2<*g znvOq@p^F_Lu1wTbxD?2l>lQKIl zDJv}5Niy6+qDyB{M`#eEAeTKZf3xP)%-yu*mr6ze*f_MnqW!|WYK?F#i zsq^80n*LavhxL}TZbAFXnYNl4wv1OTX|3G{um>aJ(shEw?Ih!i4}B?Yzq=5Y#tOtw z5Th!%jaL%V8?W#!`k7FZR2=+#+3r zs-Nf2^~q%f-62Gk3s+f0z|MSI^+Ma^Db22T_5$mMi)V|iAf;YFY!5SBRv^OLT=PM| zbr^|`;5#dI9no(__m`S^VpWnykgkfztE{d+$na*?-^R<9q&|)@Yaq?znVeqhSk3K> zR&UyORZm$DPoqq3e4SG7ey_lLD_zv9VQsEDyWvW^4@M=@guhMb0kLhbdJ?fShP!3? zpkK_dNFqIO>IT8nzbgrpd+@%Pm^v=r+L?+iIy)^^p;7f2vV_Od(KWcEZQ52!EKdBg zTMY@p=kAYY^POQCsCj<0!}D6>?e6G-B?@%`d$IY{+9BiTySGW29VXib+x32>!*6_) zu!Q;W1)vn>3(M#Y{gD6*I$HP>_eqsaZ;+o zB5h#^n=x~P%Xt&iHMK}+`Fs%&Gma^BvXtUVQvSIB@w#ika%jsZsp3qF?PaS*xqLIFe8jQrACEOmtjv627J*Tq zM61h*h>W`j`uBrYuj^(MzOGGY7I z{vnHRtl+%iu>-`rPeebZy)qNwKY}nA7t9}ISmk*Xt29Kql`rsWOfU*lCTj1sh*M*S zGW+x{v%$M~$x9NL0Y-?xi=gFFz2yB!ui#X32KZLD%bB%$GwisZa;|v>Iol971}YvHSw>IA0h~n^sB7N{O}akC=C}Zvs;HFb|I>)E-!px~CK)LfwLse0eva^}+{8C|QTkWa z%oH9HCpu@c9_NUL0PI7ZZ{nO)hm9H#!0pjw)Z8!HkYIfA+*nNl(RLKJ+e9HbO?_>t z+AqX1^v-0T9RKSg=uI3H!Gi{bi8i2_+(fqGf-5}1>I_K~StmV&Tu9lb-lUFYfr*E|_izmZ!(jlylF0^d+AN++SvRtqa5Q%fdHMRK!%>YX6D{epMV zhz4QSuYp9_U&Lq$$1W&rc@HLwJ5!v64&v* z;EVblFW0CY&f9yLs7^b@b@m z=aZSvhU+yEJYp`wb33BBF$RqqF?TYMxl|Ns#fh;3TlZ$DN{ zK1Iuy8$b1Kp{OR@pCCe{14pRJItCM%ZSf3tp`=TuZ!Dj|!$nc7XwHWNNB-STWTMbuiJiQw6`EsgC1(izzzk>ioqrL_1vQOp`qxL3__ULVKC*bEqc|$u z`&BeQby8ENL)v8N4UyYx2*tZH>bAc9&ZkA0j#>ZdJp6y1hfwsf>jJ^dbB*cpA&lD2 zVnnYHN$XJtyD6?M(jj)43650NSqxu~{7F0d4?6l-7g*lk0Y zK6aH7DwmSmkECwu2nUii*&6zR5Tp;a4~6#giP!7>RUS|drF&!KuAPQ#BKL6V~nZ-SsO^jdDPbJ~4qcURJ$W`w1GCMN0npO+# z@z0YoLOkCc#kan<5GVyJeOjy}sOWg=(?VFCuy?6eKR#~IDE+5_BD@lBor0SKX*2gl zQqT;#cJV$YwwEBfsPN>z#2rb7GU$I3cY_#)%+zbuIi!xo{ zz&}gC<7nXs6FDMG6k#&3LWE++zBg{@i9Y|ghSjpCQ>QWuB?K+Si1Sq?#B zT?C0Y(HN`ec{K%()9^R|Fb<&UX4-p8* z9zn=D1R+x@&XsJs8e)t1zwa!!;|k>{+TdU+`Y^8J?|cIwRi3+*1RPWUs!F&wL*)D) zQbxSAg6bHk&xC&#O{|}2*j93ET$q^Po5|PNMiZUEF!o;MkN%a0@;Lm0a&obn*@e!8 z&tJvJ_lCK}q>={I|4ty8i;s0TkAJp4#!6~P+nTOfB7{YJPNVTsvS8=i`Mg!ZF-D$QS1uKc8XLRH@nZHeo1#l z*b5ql&&R%0()mHwKE1D0*_a{kEH}dRb@Q1%;PbRee^LGry{JR3X*?Vruc*<<9`u09 zL@K=^j+qf4gfw@rVnbP-rL@%_oOgVGbo_xJ`(43@YxYB6v@9NT$(y<@mwD0>In`L$ zyppdt22ppbR=M7HO1%j0Zy~(TY+OIsqE~LXUYhvFip{ps@CI`;ImomX7vssnQpSe$ zn>%u)N~!+{8^8Cy>+dbu3`=BPH|nR*ey5&8{^GBy+>P_;DU_G2v zgk#ppTy85L6Gc5vOP!WlP}(gL$lkH!A+R*P+gbToKjxMF6jk!S!wLiZf4INwphO^+ zQw2_E39nU%1Er%_3`qng%(g2x0mdj;{{6?~0slNM<3V9|Ub7;Nj!#BK{sd=r>92q5 z6}Epa|9*v`u4_(cw;wW*_d%?9zz1suDJlF&oy;0sq0k6ImON`FSw&gUO3}gBk6LGe z->H1f8LnM#%*Ah|K@Gx;0t9Gdb5Xw)8=!owNPxL!H$7?Ziux@+-XZs$KpBGzxyM4X zs#^BsCHPkj6PXHV|9rn>-vBm(LD#nRcRic>xcBvsE@28zD*b%sC~Z*G3)AvSna^4J z@57OI%hwtqq@>#*GVY<>PuULNsZhpCCoQr;)bEq(L;fJ|XHTPA4|~#A#CFJgyTK`x za{%c~Y^Nb92Q-n`b}7Fl_!K*d5pgfaz{U6c9t6K|CMKl=Y(k=%au_xbZFOwI#l6)==7@EuocF8w(7 zEfH6jPU8AttBIw0waJYO6zy+#cLrkh8j=3`9fbb$Qp={?Rp)1Y|E68&kf6pi@7Q-i zI5Sqp=pfhC_ETxtN+=EbF~zEMPgWeoN|Hzpp=JPoKRI2|EmFgzfF{a+zg%Jw)yyZ^DX5!^!9(GDs@PN+M!NmdQaPvPdz>Jt9oh|LTGS0@)&i cKt%tedHkZ7j4UdJ)RinN%7h*u^!Uqv0S&lnjsO4v delta 6875 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 QJ(<8O!G@nCdh^r&0hI_ currentStepSize; std::atomic keyArray[7]; +std::atomic octave; +std::atomic selectedWaveform; QueueHandle_t msgInQ; uint8_t RX_Message[8] = {0}; // Objects U8G2_SSD1305_128X32_NONAME_F_HW_I2C u8g2(U8G2_R0); // Display driver object Knob K3 = Knob(0, 16); // Knob driver object // Program Specific Structures -typedef struct { - int32_t stepSize; - std::string note; -} Note; -const Note notes[] = { - {0, "None"}, {3185014, "C1"}, {3374405, "C1#"}, {3575058, "D1"}, {3787642, "D1#"}, {4012867, "E1"}, {4251484, "F1"}, {4504291, "F1#"}, {4772130, "G1"}, {5055895, "G1#"}, {5356535, "A1"}, {5675051, "A1#"}, {6012507, "B1"}, {6370029, "C2"}, {6748811, "C2#"}, {7150116, "D2"}, {7575284, "D2#"}, {8025734, "E2"}, {8502969, "F2"}, {9008582, "F2#"}, {9544260, "G2"}, {10111791, "G2#"}, {10713070, "A2"}, {11350102, "A2#"}, {12025014, "B2"}, {12740059, "C3"}, {13497622, "C3#"}, {14300233, "D3"}, {15150569, "D3#"}, {16051469, "E3"}, {17005939, "F3"}, {18017164, "F3#"}, {19088521, "G3"}, {20223583, "G3#"}, {21426140, "A3"}, {22700205, "A3#"}, {24050029, "B3"}, {25480118, "C4"}, {26995245, "C4#"}, {28600466, "D4"}, {30301138, "D4#"}, {32102938, "E4"}, {34011878, "F4"}, {36034329, "F4#"}, {38177042, "G4"}, {40447167, "G4#"}, {42852281, "A4"}, {45400410, "A4#"}, {48100059, "B4"}, {50960237, "C5"}, {53990491, "C5#"}, {57200933, "D5"}, {60602277, "D5#"}, {64205876, "E5"}, {68023756, "F5"}, {72068659, "F5#"}, {76354085, "G5"}, {80894335, "G5#"}, {85704562, "A5"}, {90800821, "A5#"}, {96200119, "B5"}, {101920475, "C6"}, {107980982, "C6#"}, {114401866, "D6"}, {121204555, "D6#"}, {128411753, "E6"}, {136047513, "F6"}, {144137319, "F6#"}, {152708170, "G6"}, {161788670, "G6#"}, {171409125, "A6"}, {181601642, "A6#"}, {192400238, "B6"}, {203840951, "C7"}, {215961965, "C7#"}, {228803732, "D7"}, {242409110, "D7#"}, {256823506, "E7"}, {272095026, "F7"}, {288274638, "F7#"}, {305416340, "G7"}, {323577341, "G7#"}, {342818251, "A7"}, {363203285, "A7#"}, {384800476, "B7"}}; +const int32_t stepSizes[85] = {0, 6384507, 6764150, 7166367, 7592501, 8043975, 8522295, 9029057, 9565952, 10134773, 10737418, 11375898, 12052344, 12769014, 13528299, 14332734, 15185002, 16087950, 17044589, 18058113, 19131904, 20269547, 21474836, 22751797, 24104689, 25538028, 27056599, 28665468, 30370005, 32175899, 34089178, 36116226, 38263809, 40539093, 42949673, 45503593, 48209378, 51076057, 54113197, 57330935, 60740010, 64351799, 68178356, 72232452, 76527617, 81078186, 85899346, 91007187, 96418756, 102152113, 108226394, 114661870, 121480020, 128703598, 136356712, 144464904, 153055234, 162156372, 171798692, 182014374, 192837512, 204304227, 216452788, 229323741, 242960040, 257407196, 272713424, 288929808, 306110469, 324312744, 343597384, 364028747, 385675023, 408608453, 432905576, 458647482, 485920080, 514814392, 545426848, 577859616, 612220937, 648625489, 687194767, 728057495, 771350046}; +const char *notes[85] = {"None", "C1", "C1#", "D1", "D1#", "E1", "F1", "F1#", "G1", "G1#", "A1", "A1#", "B1", "C2", "C2#", "D2", "D2#", "E2", "F2", "F2#", "G2", "G2#", "A2", "A2#", "B2", "C3", "C3#", "D3", "D3#", "E3", "F3", "F3#", "G3", "G3#", "A3", "A3#", "B3", "C4", "C4#", "D4", "D4#", "E4", "F4", "F4#", "G4", "G4#", "A4", "A4#", "B4", "C5", "C5#", "D5", "D5#", "E5", "F5", "F5#", "G5", "G5#", "A5", "A5#", "B5", "C6", "C6#", "D6", "D6#", "E6", "F6", "F6#", "G6", "G6#", "A6", "A6#", "B6", "C7", "C7#", "D7", "D7#", "E7", "F7", "F7#", "G7", "G7#", "A7", "A7#", "B7"}; +std::atomic activeNotes[85] = {{0}}; enum waveform { SQUARE = 0, SAWTOOTH, TRIANGLE, SINE }; -const unsigned char waveforms[4][18] = { +const unsigned char waveformIcons[4][18] = { {0x7f, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0xc1, 0x1f}, // Square Wave {0x70, 0x10, 0x58, 0x18, 0x48, 0x08, 0x4c, 0x0c, 0x44, @@ -43,7 +41,7 @@ const unsigned char waveforms[4][18] = { {0x1c, 0x00, 0x36, 0x00, 0x22, 0x00, 0x63, 0x00, 0x41, 0x10, 0xc0, 0x18, 0x80, 0x08, 0x80, 0x0d, 0x00, 0x07} // Sine Wave }; -const unsigned char volumes[6][18] = { +const unsigned char volumeIcons[6][18] = { {0x10, 0x02, 0x98, 0x04, 0x1c, 0x05, 0x5f, 0x09, 0x5f, 0x09, 0x5f, 0x09, 0x1c, 0x05, 0x98, 0x04, 0x10, 0x02}, // volume max {0x10, 0x00, 0x98, 0x00, 0x1c, 0x01, 0x5f, 0x01, 0x5f, @@ -118,7 +116,7 @@ void setRow(const uint8_t rowIdx) { digitalWrite(REN_PIN, HIGH); } -// Returns key value (as notes[] index) of highest currently pressed key +// Returns key value (as stepSizes[] index) of highest currently pressed key uint16_t getTopKey() { uint16_t topKey = 0; for (uint8_t i = 0; i < 3; i++) { @@ -150,9 +148,9 @@ void decodeTask(void *pvParameters) { while (1) { xQueueReceive(msgInQ, RX_Message, portMAX_DELAY); if (RX_Message[0] == 0x50) { // Pressed - currentStepSize = notes[(RX_Message[1] - 1) * 12 + RX_Message[2]].stepSize; + activeNotes[(RX_Message[1] - 1) * 12 + RX_Message[2]] = true; } else { // Released - currentStepSize = 0; + activeNotes[(RX_Message[1] - 1) * 12 + RX_Message[2]] = false; } } } @@ -191,7 +189,7 @@ void scanKeysTask(void *pvParameters) { } } } - currentStepSize = notes[getTopKey()].stepSize; // Atomic Store + currentStepSize = stepSizes[getTopKey()]; // Atomic Store K3.updateRotation(keyArray[3] & 0x1, keyArray[3] & 0x2); } } @@ -203,11 +201,16 @@ void displayUpdateTask(void *pvParameters) { while (1) { vTaskDelayUntil(&xLastWakeTime, xFrequency); uint32_t rxID; - u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font - uint16_t key = getTopKey(); - u8g2.drawStr(2, 10, notes[key].note.c_str()); // Print the current key + char currentKeys[64] = {0}; + for (uint8_t i = 0; i < 85; i++) { + if (activeNotes[i]) { + strcat(currentKeys, notes[i]); + strcat(currentKeys, " "); + } + } + u8g2.drawStr(2, 10, currentKeys); // Print the current keys digitalToggle(LED_BUILTIN); u8g2.setCursor(2, 20); for (uint8_t i = 0; i < 7; i++) { @@ -224,6 +227,7 @@ void displayUpdateTask(void *pvParameters) { } } +// Arduino framework setup function, sets up Pins, Display, UART, CAN and Tasks void setup() { #pragma region Pin Setup pinMode(RA0_PIN, OUTPUT); @@ -241,6 +245,9 @@ void setup() { pinMode(JOYX_PIN, INPUT); pinMode(JOYY_PIN, INPUT); #pragma endregion +#pragma region Variables Setup + octave = 4; +#pragma endregion #pragma region Display Setup setOutMuxBit(DRST_BIT, LOW); // Assert display logic reset delayMicroseconds(2); @@ -267,14 +274,23 @@ void setup() { sampleTimer->resume(); TaskHandle_t scanKeysHandle = nullptr; TaskHandle_t displayUpdateHandle = nullptr; + TaskHandle_t decodeHandle = nullptr; xTaskCreate( scanKeysTask, // Function that implements the task "scanKeys", // Text name for the task 64, // Stack size in words, not bytes nullptr, // Parameter passed into the task - 2, // Task priority + 3, // Task priority &scanKeysHandle // Pointer to store the task handle ); + xTaskCreate( + decodeTask, // Function that implements the task + "decode", // Text name for the task + 256, // Stack size in words, not bytes + nullptr, // Parameter passed into the task + 2, // Task priority + &decodeHandle // Pointer to store the task handle + ); xTaskCreate( displayUpdateTask, // Function that implements the task "displayUpdate", // Text name for the task From 4e9e486c960833a06ae26c68ac1e5fcd28fe5e8b Mon Sep 17 00:00:00 2001 From: Aadi Desai <21363892+supleed2@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:55:09 +0000 Subject: [PATCH 3/7] New buffer audio method --- src/main.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d366455..88851b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,15 +12,22 @@ const uint32_t interval = 10; // Display update interval const uint32_t samplingRate = 22000; // Sampling rate const uint32_t canID = 0x123; // Variables +std::atomic isMainSynth; std::atomic currentStepSize; std::atomic keyArray[7]; std::atomic octave; std::atomic selectedWaveform; QueueHandle_t msgInQ; uint8_t RX_Message[8] = {0}; +std::atomic bufferAactive; +int32_t bufferA[220]; +int32_t bufferB[220]; // Objects -U8G2_SSD1305_128X32_NONAME_F_HW_I2C u8g2(U8G2_R0); // Display driver object -Knob K3 = Knob(0, 16); // Knob driver object +U8G2_SSD1305_128X32_NONAME_F_HW_I2C u8g2(U8G2_R0); // Display Driver Object +Knob K0 = Knob(1, 7, 4); // Octave Knob Object +Knob K1 = Knob(0, 3); // Waveform Knob Object +Knob K2 = Knob(0, 1); // Send / Receive Knob Object +Knob K3 = Knob(0, 16); // Volume Knob Object // Program Specific Structures const int32_t stepSizes[85] = {0, 6384507, 6764150, 7166367, 7592501, 8043975, 8522295, 9029057, 9565952, 10134773, 10737418, 11375898, 12052344, 12769014, 13528299, 14332734, 15185002, 16087950, 17044589, 18058113, 19131904, 20269547, 21474836, 22751797, 24104689, 25538028, 27056599, 28665468, 30370005, 32175899, 34089178, 36116226, 38263809, 40539093, 42949673, 45503593, 48209378, 51076057, 54113197, 57330935, 60740010, 64351799, 68178356, 72232452, 76527617, 81078186, 85899346, 91007187, 96418756, 102152113, 108226394, 114661870, 121480020, 128703598, 136356712, 144464904, 153055234, 162156372, 171798692, 182014374, 192837512, 204304227, 216452788, 229323741, 242960040, 257407196, 272713424, 288929808, 306110469, 324312744, 343597384, 364028747, 385675023, 408608453, 432905576, 458647482, 485920080, 514814392, 545426848, 577859616, 612220937, 648625489, 687194767, 728057495, 771350046}; const char *notes[85] = {"None", "C1", "C1#", "D1", "D1#", "E1", "F1", "F1#", "G1", "G1#", "A1", "A1#", "B1", "C2", "C2#", "D2", "D2#", "E2", "F2", "F2#", "G2", "G2#", "A2", "A2#", "B2", "C3", "C3#", "D3", "D3#", "E3", "F3", "F3#", "G3", "G3#", "A3", "A3#", "B3", "C4", "C4#", "D4", "D4#", "E4", "F4", "F4#", "G4", "G4#", "A4", "A4#", "B4", "C5", "C5#", "D5", "D5#", "E5", "F5", "F5#", "G5", "G5#", "A5", "A5#", "B5", "C6", "C6#", "D6", "D6#", "E6", "F6", "F6#", "G6", "G6#", "A6", "A6#", "B6", "C7", "C7#", "D7", "D7#", "E7", "F7", "F7#", "G7", "G7#", "A7", "A7#", "B7"}; @@ -31,7 +38,7 @@ enum waveform { TRIANGLE, SINE }; -const unsigned char waveformIcons[4][18] = { +const unsigned char waveforms[4][18] = { {0x7f, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0x41, 0x10, 0xc1, 0x1f}, // Square Wave {0x70, 0x10, 0x58, 0x18, 0x48, 0x08, 0x4c, 0x0c, 0x44, @@ -41,7 +48,7 @@ const unsigned char waveformIcons[4][18] = { {0x1c, 0x00, 0x36, 0x00, 0x22, 0x00, 0x63, 0x00, 0x41, 0x10, 0xc0, 0x18, 0x80, 0x08, 0x80, 0x0d, 0x00, 0x07} // Sine Wave }; -const unsigned char volumeIcons[6][18] = { +const unsigned char volumes[6][18] = { {0x10, 0x02, 0x98, 0x04, 0x1c, 0x05, 0x5f, 0x09, 0x5f, 0x09, 0x5f, 0x09, 0x1c, 0x05, 0x98, 0x04, 0x10, 0x02}, // volume max {0x10, 0x00, 0x98, 0x00, 0x1c, 0x01, 0x5f, 0x01, 0x5f, @@ -131,10 +138,21 @@ uint16_t getTopKey() { // Interrupt driven routine to send waveform to DAC void sampleISR() { + static uint8_t bufferSample = 0; // up to 255; + if (bufferSample = 220) { + bufferSample = 0; + bufferAactive = !bufferAactive; + } + if (bufferAactive) { + analogWrite(OUTR_PIN, bufferA[bufferSample]); + } else { + analogWrite(OUTR_PIN, bufferB[bufferSample]); + } static int32_t phaseAcc = 0; phaseAcc += currentStepSize; int32_t Vout = phaseAcc >> (32 - K3.getRotation() / 2); // Volume range from (>> 32) to (>> 24), range of 8 analogWrite(OUTR_PIN, Vout + 128); + bufferSample++; } void CAN_RX_ISR() { @@ -144,6 +162,26 @@ void CAN_RX_ISR() { xQueueSendFromISR(msgInQ, ISR_RX_Message, nullptr); } +void generateWaveformBufferTask(void *pvParameters) { + const TickType_t xFrequency = 50 / portTICK_PERIOD_MS; + TickType_t xLastWakeTime = xTaskGetTickCount(); + static int32_t waveformBuffers[85][220] = {0}; + while (1) { + vTaskDelayUntil(&xLastWakeTime, xFrequency); + // Generate waveforms and write to buffers + int32_t nextBuffer[220] = {0}; + // Combine waveforms into nextBuffer waveform + // Write waveform to inactive buffer + for (uint8_t i = 0; i < 220; i++) { + if (bufferAactive) { + bufferB[i] = nextBuffer[i]; + } else { + bufferA[i] = nextBuffer[i]; + } + } + } +} + void decodeTask(void *pvParameters) { while (1) { xQueueReceive(msgInQ, RX_Message, portMAX_DELAY); @@ -190,6 +228,9 @@ void scanKeysTask(void *pvParameters) { } } currentStepSize = stepSizes[getTopKey()]; // Atomic Store + K0.updateRotation(keyArray[4] & 0x4, keyArray[4] & 0x8); + K1.updateRotation(keyArray[4] & 0x1, keyArray[4] & 0x2); + K2.updateRotation(keyArray[3] & 0x4, keyArray[3] & 0x8); K3.updateRotation(keyArray[3] & 0x1, keyArray[3] & 0x2); } } @@ -210,20 +251,33 @@ void displayUpdateTask(void *pvParameters) { strcat(currentKeys, " "); } } - u8g2.drawStr(2, 10, currentKeys); // Print the current keys - digitalToggle(LED_BUILTIN); + u8g2.drawStr(2, 10, currentKeys); // Print the currently pressed keys u8g2.setCursor(2, 20); for (uint8_t i = 0; i < 7; i++) { u8g2.print(keyArray[i], HEX); - } - // u8g2.drawXBM(118, 0, 10, 10, icon_bits); - u8g2.setCursor(100, 10); + }; + u8g2.setCursor(100, 10); // Debug print of received CAN message u8g2.print((char)RX_Message[0]); u8g2.print(RX_Message[1]); u8g2.print(RX_Message[2], HEX); - u8g2.setCursor(2, 30); + + // Draw currently selected waveform above knob 1 + u8g2.drawXBM(38, 22, 13, 9, waveforms[K1.getRotation()]); + + // Print Send / Receive State above knob 2 + if (K2.getRotation()) { + u8g2.drawStr(70, 30, "SEND"); + } else { + u8g2.drawStr(70, 30, "RECV"); + } + + // Print currently selected volume level above knob 3 + u8g2.setCursor(110, 30); + u8g2.print("V:"); u8g2.print(K3.getRotation()); + u8g2.sendBuffer(); // transfer internal memory to the display + digitalToggle(LED_BUILTIN); } } @@ -246,6 +300,7 @@ void setup() { pinMode(JOYY_PIN, INPUT); #pragma endregion #pragma region Variables Setup + isMainSynth = true; octave = 4; #pragma endregion #pragma region Display Setup From 4d0cc666f60718d3263431577d2bc60ac560568d Mon Sep 17 00:00:00 2001 From: Aadi Desai <21363892+supleed2@users.noreply.github.com> Date: Fri, 25 Mar 2022 14:55:43 +0000 Subject: [PATCH 4/7] Fix Encoder Driver --- lib/knob/knob | 13 +++++--- lib/knob/knob.cpp | 83 +++++++++++++++++++++++++++++++---------------- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/lib/knob/knob b/lib/knob/knob index 9c035a1..632bfa1 100644 --- a/lib/knob/knob +++ b/lib/knob/knob @@ -3,13 +3,18 @@ class Knob { private: - int rotation; - int minimum, maximum; + int rotation, rotationInternal; + int minimum, maximum, previousRotation; bool A, B; - bool rotPlusOnePrev, rotMinOnePrev; + enum PrevRot { + NONE, + CW, + ACW, + }; public: - Knob(int minimum, int max); + Knob(int minimum, int maximum, int initialRotation); + Knob(int minimum, int maximum) : Knob(minimum, maximum, minimum) {} // Delegate to full constructor, using minimum as initial rotation int getRotation(); diff --git a/lib/knob/knob.cpp b/lib/knob/knob.cpp index 25bf475..4550afb 100644 --- a/lib/knob/knob.cpp +++ b/lib/knob/knob.cpp @@ -1,13 +1,13 @@ #include -Knob::Knob(int minimum, int maximum) { +Knob::Knob(int minimum, int maximum, int initialRotation) { Knob::minimum = minimum; Knob::maximum = maximum; Knob::A = false; Knob::B = false; - Knob::rotPlusOnePrev = false; - Knob::rotMinOnePrev = false; - Knob::rotation = 0; + Knob::previousRotation = NONE; + Knob::rotation = initialRotation; + Knob::rotationInternal = initialRotation; } int Knob::getRotation() { @@ -15,34 +15,61 @@ int Knob::getRotation() { }; void Knob::updateRotation(bool ANew, bool BNew) { - bool rotPlusOneNew = (!B && !A && !BNew && ANew) || - (!B && A && BNew && ANew) || - (B && !A && !BNew && !ANew) || - (B && A && BNew && !ANew); + if (A == ANew && B == BNew) + return; // No change, do not update values - bool rotMinOneNew = (!B && !A && BNew && !ANew) || - (!B && A && !BNew && !ANew) || - (B && !A && BNew && ANew) || - (B && A && !BNew && ANew); + bool fullstep = (BNew && ANew) || (!BNew && !ANew); - bool impossibleState = (!B && !A && BNew && ANew) || - (!B && A && BNew && !ANew) || - (B && !A && !BNew && ANew) || - (B && A && !BNew && !ANew); + bool cwRot = (!B && !A && !BNew && ANew) || + (!B && A && BNew && ANew) || + (B && !A && !BNew && !ANew) || + (B && A && BNew && !ANew); - if (rotPlusOneNew || (impossibleState && rotPlusOnePrev)) - rotation += 2; - if (rotMinOneNew || (impossibleState && rotMinOnePrev)) - rotation -= 2; - if (rotation < minimum) - rotation = minimum; - if (rotation > maximum) - rotation = maximum; + bool acwRot = (!B && !A && BNew && !ANew) || + (!B && A && !BNew && !ANew) || + (B && !A && BNew && ANew) || + (B && A && !BNew && ANew); + bool impossible = (!B && !A && BNew && ANew) || + (!B && A && BNew && !ANew) || + (B && !A && !BNew && ANew) || + (B && A && !BNew && !ANew); + + if (cwRot) { + if (previousRotation == CW && fullstep) { + rotationInternal++; + } else if (previousRotation == ACW) { + previousRotation = NONE; + } else { + previousRotation = CW; + } + } else if (acwRot) { + if (previousRotation == ACW && fullstep) { + rotationInternal--; + } else if (previousRotation == CW) { + previousRotation = NONE; + } else { + previousRotation = ACW; + } + } else if (impossible) { + if (fullstep) { + switch (previousRotation) { + case NONE: // Step skipped and no previous direction + break; + case CW: + rotationInternal++; + break; + case ACW: + rotationInternal--; + break; + } + } + } A = ANew; B = BNew; - if (!impossibleState) { - rotPlusOnePrev = rotPlusOneNew; - rotMinOnePrev = rotMinOneNew; - } + if (rotationInternal < minimum) + rotationInternal = minimum; + if (rotationInternal > maximum) + rotationInternal = maximum; + rotation = rotationInternal; } \ No newline at end of file From 88b456e2505b4f9fd3742bd99080eface51e0bc7 Mon Sep 17 00:00:00 2001 From: Aadi Desai <21363892+supleed2@users.noreply.github.com> Date: Fri, 25 Mar 2022 15:03:28 +0000 Subject: [PATCH 5/7] Single Key Mode --- src/main.cpp | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 88851b2..69e5a10 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ std::atomic currentStepSize; std::atomic keyArray[7]; std::atomic octave; std::atomic selectedWaveform; +std::atomic latestKey; QueueHandle_t msgInQ; uint8_t RX_Message[8] = {0}; std::atomic bufferAactive; @@ -138,21 +139,10 @@ uint16_t getTopKey() { // Interrupt driven routine to send waveform to DAC void sampleISR() { - static uint8_t bufferSample = 0; // up to 255; - if (bufferSample = 220) { - bufferSample = 0; - bufferAactive = !bufferAactive; - } - if (bufferAactive) { - analogWrite(OUTR_PIN, bufferA[bufferSample]); - } else { - analogWrite(OUTR_PIN, bufferB[bufferSample]); - } static int32_t phaseAcc = 0; phaseAcc += currentStepSize; int32_t Vout = phaseAcc >> (32 - K3.getRotation() / 2); // Volume range from (>> 32) to (>> 24), range of 8 analogWrite(OUTR_PIN, Vout + 128); - bufferSample++; } void CAN_RX_ISR() { @@ -187,8 +177,14 @@ void decodeTask(void *pvParameters) { xQueueReceive(msgInQ, RX_Message, portMAX_DELAY); if (RX_Message[0] == 0x50) { // Pressed activeNotes[(RX_Message[1] - 1) * 12 + RX_Message[2]] = true; + latestKey = (RX_Message[1] - 1) * 12 + RX_Message[2]; + currentStepSize = stepSizes[latestKey]; } else { // Released activeNotes[(RX_Message[1] - 1) * 12 + RX_Message[2]] = false; + if (latestKey == (RX_Message[1] - 1) * 12 + RX_Message[2]) { + latestKey = 0; + currentStepSize = stepSizes[latestKey]; // Atomic Store + } } } } @@ -220,14 +216,15 @@ void scanKeysTask(void *pvParameters) { continue; } else { keyArray[i] = newRow; - for (uint8_t j = 0; j < 4; j++) { - if ((oldRow & (0x1 << j)) ^ (newRow & (0x1 << j))) { - keyChangedSendTXMessage(octave, i * 4 + j + 1, newRow & (0x1 << j)); + if (i < 3) { + for (uint8_t j = 0; j < 4; j++) { + if ((oldRow & (0x1 << j)) ^ (newRow & (0x1 << j))) { + keyChangedSendTXMessage(octave, i * 4 + j + 1, newRow & (0x1 << j)); + } } } } } - currentStepSize = stepSizes[getTopKey()]; // Atomic Store K0.updateRotation(keyArray[4] & 0x4, keyArray[4] & 0x8); K1.updateRotation(keyArray[4] & 0x1, keyArray[4] & 0x2); K2.updateRotation(keyArray[3] & 0x4, keyArray[3] & 0x8); @@ -266,14 +263,13 @@ void displayUpdateTask(void *pvParameters) { // Print Send / Receive State above knob 2 if (K2.getRotation()) { - u8g2.drawStr(70, 30, "SEND"); + u8g2.drawStr(74, 30, "SEND"); } else { - u8g2.drawStr(70, 30, "RECV"); + u8g2.drawStr(74, 30, "RECV"); } // Print currently selected volume level above knob 3 - u8g2.setCursor(110, 30); - u8g2.print("V:"); + u8g2.setCursor(116, 30); u8g2.print(K3.getRotation()); u8g2.sendBuffer(); // transfer internal memory to the display From c484ebba343b2d11c958141baf4aa24c1cb50117 Mon Sep 17 00:00:00 2001 From: Kacper Date: Fri, 25 Mar 2022 15:07:53 +0000 Subject: [PATCH 6/7] Added triangle and sine --- src/main.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bf0c762..69617b1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,6 +66,24 @@ const unsigned char volumes[6][18] = { const unsigned char icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0xfe, 0x01, 0x00, 0x00}; +const int16_t sineLookUpTable[] = { + 128,131,134,137,140,143,146,149,152,156,159,162,165,168,171,174, + 176,179,182,185,188,191,193,196,199,201,204,206,209,211,213,216, + 218,220,222,224,226,228,230,232,234,236,237,239,240,242,243,245, + 246,247,248,249,250,251,252,252,253,254,254,255,255,255,255,255, + 255,255,255,255,255,255,254,254,253,252,252,251,250,249,248,247, + 246,245,243,242,240,239,237,236,234,232,230,228,226,224,222,220, + 218,216,213,211,209,206,204,201,199,196,193,191,188,185,182,179, + 176,174,171,168,165,162,159,156,152,149,146,143,140,137,134,131, + 128,124,121,118,115,112,109,106,103,99, 96, 93, 90, 87, 84, 81, + 79, 76, 73, 70, 67, 64, 62, 59, 56, 54, 51, 49, 46, 44, 42, 39, + 37, 35, 33, 31, 29, 27, 25, 23, 21, 19, 18, 16, 15, 13, 12, 10, + 9, 8, 7, 6, 5, 4, 3, 3, 2, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, 5, 6, 7, 8, + 9, 10, 12, 13, 15, 16, 18, 19, 21, 23, 25, 27, 29, 31, 33, 35, + 37, 39, 42, 44, 46, 49, 51, 54, 56, 59, 62, 64, 67, 70, 73, 76, + 79, 81, 84, 87, 90, 93, 96, 99, 103,106,109,112,115,118,121,124 +}; #pragma endregion #pragma region Pin Definitions @@ -157,14 +175,14 @@ void sampleISR(){ Vout = phaseAcc >> 16; }else if(wave==SQUARE){ if(phaseAcc<0){ - Vout = 0x8000; + Vout = 0x00007FFF; }else{ - Vout = 0; + Vout = 0xFFFF8000; } }else if(wave==TRIANGLE){ - //TODO + Vout = (abs(phaseAcc)-1073741824) >> 15; }else if(wave==SINE){ - //TODO + Vout = (sineLookUpTable[(uint32_t)phaseAcc>>24]-128)<<8; } Vout = scaleVolume(Vout); analogWrite(OUTR_PIN, Vout + 128); From 61f591673020da7fb7688554696050cd90add8ea Mon Sep 17 00:00:00 2001 From: Aadi Desai <21363892+supleed2@users.noreply.github.com> Date: Fri, 25 Mar 2022 16:02:31 +0000 Subject: [PATCH 7/7] Mostly Done --- src/main.cpp | 170 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 99 insertions(+), 71 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 69e5a10..839906b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,7 +9,7 @@ #pragma region Globals(Config values, Variables, Objects, Types, etc.) // Config values const uint32_t interval = 10; // Display update interval -const uint32_t samplingRate = 22000; // Sampling rate +const uint32_t samplingRate = 44100; // Sampling rate const uint32_t canID = 0x123; // Variables std::atomic isMainSynth; @@ -18,6 +18,9 @@ std::atomic keyArray[7]; std::atomic octave; std::atomic selectedWaveform; std::atomic latestKey; +std::atomic volume; +std::atomic volumeFiner; +int8_t volumeHistory = 0; QueueHandle_t msgInQ; uint8_t RX_Message[8] = {0}; std::atomic bufferAactive; @@ -25,12 +28,12 @@ int32_t bufferA[220]; int32_t bufferB[220]; // Objects U8G2_SSD1305_128X32_NONAME_F_HW_I2C u8g2(U8G2_R0); // Display Driver Object -Knob K0 = Knob(1, 7, 4); // Octave Knob Object -Knob K1 = Knob(0, 3); // Waveform Knob Object -Knob K2 = Knob(0, 1); // Send / Receive Knob Object -Knob K3 = Knob(0, 16); // Volume Knob Object +Knob K0(1, 7, 4); // Octave Knob Object +Knob K1(0, 3, 1); // Waveform Knob Object +Knob K2(0, 1); // Send / Receive Knob Object +Knob K3(0, 16); // Volume Knob Object // Program Specific Structures -const int32_t stepSizes[85] = {0, 6384507, 6764150, 7166367, 7592501, 8043975, 8522295, 9029057, 9565952, 10134773, 10737418, 11375898, 12052344, 12769014, 13528299, 14332734, 15185002, 16087950, 17044589, 18058113, 19131904, 20269547, 21474836, 22751797, 24104689, 25538028, 27056599, 28665468, 30370005, 32175899, 34089178, 36116226, 38263809, 40539093, 42949673, 45503593, 48209378, 51076057, 54113197, 57330935, 60740010, 64351799, 68178356, 72232452, 76527617, 81078186, 85899346, 91007187, 96418756, 102152113, 108226394, 114661870, 121480020, 128703598, 136356712, 144464904, 153055234, 162156372, 171798692, 182014374, 192837512, 204304227, 216452788, 229323741, 242960040, 257407196, 272713424, 288929808, 306110469, 324312744, 343597384, 364028747, 385675023, 408608453, 432905576, 458647482, 485920080, 514814392, 545426848, 577859616, 612220937, 648625489, 687194767, 728057495, 771350046}; +const int32_t stepSizes[85] = {0, 3185014, 3374405, 3575058, 3787642, 4012867, 4251484, 4504291, 4772130, 5055895, 5356535, 5675051, 6012507, 6370029, 6748811, 7150116, 7575284, 8025734, 8502969, 9008582, 9544260, 10111791, 10713070, 11350102, 12025014, 12740059, 13497622, 14300233, 15150569, 16051469, 17005939, 18017164, 19088521, 20223583, 21426140, 22700205, 24050029, 25480118, 26995245, 28600466, 30301138, 32102938, 34011878, 36034329, 38177042, 40447167, 42852281, 45400410, 48100059, 50960237, 53990491, 57200933, 60602277, 64205876, 68023756, 72068659, 76354085, 80894335, 85704562, 90800821, 96200119, 101920475, 107980982, 114401866, 121204555, 128411753, 136047513, 144137319, 152708170, 161788670, 171409125, 181601642, 192400238, 203840951, 215961965, 228803732, 242409110, 256823506, 272095026, 288274638, 305416340, 323577341, 342818251, 363203285, 384800476}; const char *notes[85] = {"None", "C1", "C1#", "D1", "D1#", "E1", "F1", "F1#", "G1", "G1#", "A1", "A1#", "B1", "C2", "C2#", "D2", "D2#", "E2", "F2", "F2#", "G2", "G2#", "A2", "A2#", "B2", "C3", "C3#", "D3", "D3#", "E3", "F3", "F3#", "G3", "G3#", "A3", "A3#", "B3", "C4", "C4#", "D4", "D4#", "E4", "F4", "F4#", "G4", "G4#", "A4", "A4#", "B4", "C5", "C5#", "D5", "D5#", "E5", "F5", "F5#", "G5", "G5#", "A5", "A5#", "B5", "C6", "C6#", "D6", "D6#", "E6", "F6", "F6#", "G6", "G6#", "A6", "A6#", "B6", "C7", "C7#", "D7", "D7#", "E7", "F7", "F7#", "G7", "G7#", "A7", "A7#", "B7"}; std::atomic activeNotes[85] = {{0}}; enum waveform { @@ -50,22 +53,39 @@ const unsigned char waveforms[4][18] = { 0x10, 0xc0, 0x18, 0x80, 0x08, 0x80, 0x0d, 0x00, 0x07} // Sine Wave }; const unsigned char volumes[6][18] = { - {0x10, 0x02, 0x98, 0x04, 0x1c, 0x05, 0x5f, 0x09, 0x5f, - 0x09, 0x5f, 0x09, 0x1c, 0x05, 0x98, 0x04, 0x10, 0x02}, // volume max - {0x10, 0x00, 0x98, 0x00, 0x1c, 0x01, 0x5f, 0x01, 0x5f, - 0x01, 0x5f, 0x01, 0x1c, 0x01, 0x98, 0x00, 0x10, 0x00}, // volume mid higher - {0x10, 0x00, 0x18, 0x00, 0x1c, 0x01, 0x5f, 0x01, 0x5f, - 0x01, 0x5f, 0x01, 0x1c, 0x01, 0x18, 0x00, 0x10, 0x00}, // volume mid lower - {0x10, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x5f, 0x00, 0x5f, - 0x00, 0x5f, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x10, 0x00}, // volume low + {0x10, 0x00, 0x18, 0x00, 0x5c, 0x04, 0x9f, 0x02, 0x1f, + 0x01, 0x9f, 0x02, 0x5c, 0x04, 0x18, 0x00, 0x10, 0x00}, // mute {0x10, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x1f, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x10, 0x00}, // volume lowest - {0x10, 0x00, 0x18, 0x00, 0x5c, 0x04, 0x9f, 0x02, 0x1f, - 0x01, 0x9f, 0x02, 0x5c, 0x04, 0x18, 0x00, 0x10, 0x00} // mute + {0x10, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x5f, 0x00, 0x5f, + 0x00, 0x5f, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x10, 0x00}, // volume low + {0x10, 0x00, 0x18, 0x00, 0x1c, 0x01, 0x5f, 0x01, 0x5f, + 0x01, 0x5f, 0x01, 0x1c, 0x01, 0x18, 0x00, 0x10, 0x00}, // volume mid lower + {0x10, 0x00, 0x98, 0x00, 0x1c, 0x01, 0x5f, 0x01, 0x5f, + 0x01, 0x5f, 0x01, 0x1c, 0x01, 0x98, 0x00, 0x10, 0x00}, // volume mid higher + {0x10, 0x02, 0x98, 0x04, 0x1c, 0x05, 0x5f, 0x09, 0x5f, + 0x09, 0x5f, 0x09, 0x1c, 0x05, 0x98, 0x04, 0x10, 0x02}, // volume max }; const unsigned char icon_bits[] = { 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x02, 0x01, 0xfe, 0x01, 0x00, 0x00}; +const int8_t sinLUT[256] = { + 0, 3, 6, 9, 12, 15, 18, 21, 24, 28, 31, 34, 37, 40, 43, 46, + 48, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88, + 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 109, 111, 112, 114, 115, 117, + 118, 119, 120, 121, 122, 123, 124, 124, 125, 126, 126, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 126, 126, 125, 124, 124, 123, 122, 121, 120, 119, + 118, 117, 115, 114, 112, 111, 109, 108, 106, 104, 102, 100, 98, 96, 94, 92, + 90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51, + 48, 46, 43, 40, 37, 34, 31, 28, 24, 21, 18, 15, 12, 9, 6, 3, + 0, -4, -7, -10, -13, -16, -19, -22, -25, -29, -32, -35, -38, -41, -44, -47, + -49, -52, -55, -58, -61, -64, -66, -69, -72, -74, -77, -79, -82, -84, -86, -89, + -91, -93, -95, -97, -99, -101, -103, -105, -107, -109, -110, -112, -113, -115, -116, -118, + -119, -120, -121, -122, -123, -124, -125, -125, -126, -127, -127, -128, -128, -128, -128, -128, + -128, -128, -128, -128, -128, -128, -127, -127, -126, -125, -125, -124, -123, -122, -121, -120, + -119, -118, -116, -115, -113, -112, -110, -109, -107, -105, -103, -101, -99, -97, -95, -93, + -91, -89, -86, -84, -82, -79, -77, -74, -72, -69, -66, -64, -61, -58, -55, -52, + -49, -47, -44, -41, -38, -35, -32, -29, -25, -22, -19, -16, -13, -10, -7, -4}; #pragma endregion #pragma region Pin Definitions @@ -124,27 +144,40 @@ void setRow(const uint8_t rowIdx) { digitalWrite(REN_PIN, HIGH); } -// Returns key value (as stepSizes[] index) of highest currently pressed key -uint16_t getTopKey() { - uint16_t topKey = 0; - for (uint8_t i = 0; i < 3; i++) { - for (uint8_t j = 0; j < 4; j++) { - if (keyArray[i] & (0x1 << j)) { - topKey = (octave - 1) * 12 + i * 4 + j + 1; - } - } +// Scales output signal according to global volume value, to range 0-255 +uint32_t scaleVolume(uint32_t Vout) { + uint32_t newVout = 0; + if (volumeFiner) { + newVout = (Vout * 12 * volume) >> 16; + } else { // 25 = floor( (1/10) << 8 ) + newVout = (Vout * 25 * volume) >> 16; // scale by 2*8 cuz 16-bit*8-bit=24-bit -> scale by 16 to get to 8 } - return topKey; + return newVout; } // Interrupt driven routine to send waveform to DAC void sampleISR() { static int32_t phaseAcc = 0; phaseAcc += currentStepSize; - int32_t Vout = phaseAcc >> (32 - K3.getRotation() / 2); // Volume range from (>> 32) to (>> 24), range of 8 + int32_t Vout = 0; + if (selectedWaveform == SAWTOOTH) { + Vout = phaseAcc >> 16; + } else if (selectedWaveform == SQUARE) { + if (phaseAcc < 0) { + Vout = 0x00007FFF; + } else { + Vout = 0xFFFF8000; + } + } else if (selectedWaveform == TRIANGLE) { + Vout = (abs(phaseAcc) - 1073741824) >> 15; + } else if (selectedWaveform == SINE) { + Vout = (sinLUT[(uint32_t)phaseAcc >> 24]) << 8; + } + Vout = scaleVolume(Vout); analogWrite(OUTR_PIN, Vout + 128); } +// void CAN_RX_ISR() { uint8_t ISR_RX_Message[8]; uint32_t ISR_rxID; @@ -152,26 +185,7 @@ void CAN_RX_ISR() { xQueueSendFromISR(msgInQ, ISR_RX_Message, nullptr); } -void generateWaveformBufferTask(void *pvParameters) { - const TickType_t xFrequency = 50 / portTICK_PERIOD_MS; - TickType_t xLastWakeTime = xTaskGetTickCount(); - static int32_t waveformBuffers[85][220] = {0}; - while (1) { - vTaskDelayUntil(&xLastWakeTime, xFrequency); - // Generate waveforms and write to buffers - int32_t nextBuffer[220] = {0}; - // Combine waveforms into nextBuffer waveform - // Write waveform to inactive buffer - for (uint8_t i = 0; i < 220; i++) { - if (bufferAactive) { - bufferB[i] = nextBuffer[i]; - } else { - bufferA[i] = nextBuffer[i]; - } - } - } -} - +// Task to update activeNotes[] and currentStepSize based on received CAN message void decodeTask(void *pvParameters) { while (1) { xQueueReceive(msgInQ, RX_Message, portMAX_DELAY); @@ -189,6 +203,7 @@ void decodeTask(void *pvParameters) { } } +// void keyChangedSendTXMessage(uint8_t octave, uint8_t key, bool pressed) { uint8_t TX_Message[8] = {0}; if (pressed) { @@ -225,10 +240,21 @@ void scanKeysTask(void *pvParameters) { } } } + if (volumeFiner) { + K3.changeLimitsVolume(0, 10); + } else { + K3.changeLimitsVolume(0, 5); + } K0.updateRotation(keyArray[4] & 0x4, keyArray[4] & 0x8); K1.updateRotation(keyArray[4] & 0x1, keyArray[4] & 0x2); K2.updateRotation(keyArray[3] & 0x4, keyArray[3] & 0x8); K3.updateRotation(keyArray[3] & 0x1, keyArray[3] & 0x2); + octave = K0.getRotation(); + selectedWaveform = K1.getRotation(); + isMainSynth = !K2.getRotation(); + volume = K3.getRotation(); + volumeHistory = (volumeHistory << 1) + ((keyArray[5] & 0x2) >> 1); + volumeFiner = ((!(volumeHistory == 1)) & volumeFiner) | ((volumeHistory == 1) & !volumeFiner); } } @@ -238,25 +264,23 @@ void displayUpdateTask(void *pvParameters) { TickType_t xLastWakeTime = xTaskGetTickCount(); while (1) { vTaskDelayUntil(&xLastWakeTime, xFrequency); - uint32_t rxID; - u8g2.clearBuffer(); // clear the internal memory - u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font - char currentKeys[64] = {0}; - for (uint8_t i = 0; i < 85; i++) { - if (activeNotes[i]) { - strcat(currentKeys, notes[i]); - strcat(currentKeys, " "); - } - } - u8g2.drawStr(2, 10, currentKeys); // Print the currently pressed keys - u8g2.setCursor(2, 20); - for (uint8_t i = 0; i < 7; i++) { - u8g2.print(keyArray[i], HEX); - }; - u8g2.setCursor(100, 10); // Debug print of received CAN message - u8g2.print((char)RX_Message[0]); - u8g2.print(RX_Message[1]); - u8g2.print(RX_Message[2], HEX); + u8g2.clearBuffer(); // clear the internal memory + u8g2.setFont(u8g2_font_profont12_mf); // choose a suitable font + u8g2.drawStr(2, 10, notes[latestKey]); // Print the currently pressed keys + + // u8g2.setCursor(2, 20); + // for (uint8_t i = 0; i < 7; i++) { + // u8g2.print(keyArray[i], HEX); + // }; + // u8g2.setCursor(100, 10); // Debug print of received CAN message + // u8g2.print((char)RX_Message[0]); + // u8g2.print(RX_Message[1]); + // u8g2.print(RX_Message[2], HEX); + + // Print current octave number above knob 0 + u8g2.drawStr(2, 30, "O:"); + u8g2.setCursor(14, 30); + u8g2.print(octave); // Draw currently selected waveform above knob 1 u8g2.drawXBM(38, 22, 13, 9, waveforms[K1.getRotation()]); @@ -268,12 +292,16 @@ void displayUpdateTask(void *pvParameters) { u8g2.drawStr(74, 30, "RECV"); } - // Print currently selected volume level above knob 3 - u8g2.setCursor(116, 30); - u8g2.print(K3.getRotation()); + // Print volume indicator above knob 3 + if (!volumeFiner) { + u8g2.drawXBM(116, 22, 13, 9, volumes[volume]); + } else { + u8g2.setCursor(117, 30); + u8g2.print(volume); + } - u8g2.sendBuffer(); // transfer internal memory to the display - digitalToggle(LED_BUILTIN); + u8g2.sendBuffer(); // transfer internal memory to the display + digitalToggle(LED_BUILTIN); // Toggle LED to show display update rate } }