diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a3f071d --- /dev/null +++ b/.gitignore @@ -0,0 +1,57 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# AVR objects +*.lst +*.eep +*.lss +*.sym + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf \ No newline at end of file diff --git a/Descriptors.h b/Descriptors.h index eb90d34..6a46027 100644 --- a/Descriptors.h +++ b/Descriptors.h @@ -38,7 +38,7 @@ // Define: ENABLE_JOYSTICK_SERIAL // When defined, includes USB COM serial port to the device // in addition to joystick. -#define ENABLE_JOYSTICK_SERIAL +//#define ENABLE_JOYSTICK_SERIAL /* Includes: */ #include diff --git a/debug.h b/debug.h index d567117..3cda918 100644 --- a/debug.h +++ b/debug.h @@ -46,7 +46,7 @@ bool DoDebug(const uint8_t type); // If below are defined, code for respective debug target is included into build //#define DEBUG_ENABLE_UART -#define DEBUG_ENABLE_USB +//#define DEBUG_ENABLE_USB #define DEBUG_BUFFER_SIZE 512 diff --git a/downloads/adaptffbjoy-r54.hex b/downloads/adaptffbjoy-0.3.0(r54).hex similarity index 100% rename from downloads/adaptffbjoy-r54.hex rename to downloads/adaptffbjoy-0.3.0(r54).hex diff --git a/downloads/adaptffbjoy-0.5.0beta1.hex b/downloads/adaptffbjoy-0.5.0beta1.hex new file mode 100644 index 0000000..dc692cb --- /dev/null +++ b/downloads/adaptffbjoy-0.5.0beta1.hex @@ -0,0 +1,1062 @@ +:1000000029C7000044C7000041C700003FC70000E7 +:100010003DC700003BC7000039C7000037C70000DC +:1000200035C7000033C700000C94161C2FC7000012 +:100030002DC700002BC7000029C7000027C70000FC +:1000400025C7000023C7000021C700001FC700000C +:100050001DC700001BC7000019C7000017C700001C +:1000600015C7000013C7000011C700000FC700002C +:100070000DC700000C948F0809C7000007C70000D7 +:1000800005C7000003C7000001C70000FFC600004D +:10009000FDC60000FBC60000F9C60000F7C6000060 +:1000A000F5C60000F3C60000F1C600000501090412 +:1000B000A10185010901A1000930093109321600A9 +:1000C000FE26FF01350046FF0395037510810209E6 +:1000D000351500253F3500452E95017506810275C1 +:1000E0000295018101650009330934150025FF459A +:1000F000FF7508750895028102C009361580257FB5 +:1001000045FF750895018102050209BB15C0253F11 +:10011000457F750895018102050919012908150017 +:1001200025014501750195088102190929107501FC +:100130009508810205010939950175041500250707 +:10014000463B0165148142750495018101550065A6 +:1001500000050F0992A1028502099F09A009A409BF +:10016000A509A615002501350045017501950581F4 +:100170000295038103099415002501350045017599 +:1001800001950181020922150125283501452875AF +:100190000795018102C00921A102850109221501EB +:1001A0002528350145287508950191020925A102E8 +:1001B0000926092709300931093209330934094070 +:1001C0000941094209430928250C15013501450C4F +:1001D000750895019100C009500954095115002670 +:1001E000FF7F350046FF7F66031055FD75109503B0 +:1001F000910255006600000952150026FF003500E7 +:1002000046102775089501910209531501250835F7 +:100210000145087508950191020955A102050109DA +:1002200030093115002501750195029102C0050FB5 +:10023000095695019102950591030957A1020B01F9 +:10024000000A000B02000A0066140055FE15002685 +:10025000B400350047A08C000066000075089502C8 +:1002600091025500660000C0050F66031055FD158C +:100270000026FF7F350046FF7F7510950166000060 +:100280005500C0050F095AA1028502092215012552 +:100290002835014528750895019102095B095D150E +:1002A0000026FF00350046102795029102095C09DF +:1002B0005E66031055FD26FF7F46FF7F7510910295 +:1002C00045006600005500C0095FA10285030922B0 +:1002D0001501252835014528750895019102092346 +:1002E0001500250135004501750495019102095855 +:1002F000A1020B01000A000B02000A007502950220 +:100300009102C01580257F36F0D846102709607508 +:10031000089501910236F0D84610270961950191A0 +:1003200002150026FF003500461027096309647591 +:100330000895024610279501C0096EA1028504099F +:1003400022150125283501452875089501910209D6 +:1003500070150026FF00350046102775089501919D +:1003600002096F1580257F36F0D846102795019138 +:1003700002097166140055FE150026FF003500477E +:10038000A08C00009102097226FF7F46FF7F660362 +:100390001055FD7510950191026600005500C009C9 +:1003A00073A1028505092215012528350145287507 +:1003B000089501910209701601FF26FF0036F0D85A +:1003C000461027751095019102C00974A102850697 +:1003D0000922150125283501452875089501910246 +:1003E000097509761580257F36F0D84610277508DF +:1003F00095029102C00968A102850709221501250D +:100400002835014528750895019102096C150026CB +:1004100010273500461027751095019102096915BE +:1004200081257F350046FF007508950C920201C0BA +:100430000966A10285080501093009311581257F6A +:10044000350046FF00750895029102C0050F097737 +:10045000A102850A092215012528350145287508BC +:10046000950191020978A1020979097A097B1501A0 +:100470002503750895019100C0097C150026FF0031 +:10048000350046FF009102C00990A102850B0922A8 +:100490002528150135014528750895019102C009E7 +:1004A00096A102850C099709980999099A099B094F +:1004B0009C15012506750895019100C0097DA102D2 +:1004C000850D097E150026FF0035004610277508AA +:1004D00095019102C0096BA102850E092215012523 +:1004E0002835014528750895019102096D150026EA +:1004F000FF00350046FF007508950191020951661D +:10050000031055FD150026FF7F350046FF7F75104F +:10051000950191025500660000C009ABA102850556 +:100520000925A102092609270930093109320933B1 +:10053000093409400941094209430928250C1501DC +:100540003501450C75089501B100C00501093B1541 +:100550000026FF01350046FF01750A9501B10275BD +:1005600006B101C0050F0989A102850609222528C7 +:1005700015013501452875089501B102098BA102C5 +:10058000098C098D098E2503150135014503750870 +:100590009501B100C009AC150027FFFF0000350030 +:1005A00047FFFF000075109501B100C0097FA1024F +:1005B00085070980751095011500350027FFFF009C +:1005C0000047FFFF0000B102098326FF0046FF003D +:1005D00075089501B10209A909AA750195021500CE +:1005E000250135004501B10275069501B103C0C072 +:1005F00005010904A10185010904A100093009319F +:10060000093216000026FF03350046FF03950375E7 +:1006100010810209351500253F3500452E950175DD +:1006200006810275029501810165000933093415BF +:100630000025FF45FF7508750895028102C009363F +:100640001580257F45FF750895018102050209BBCC +:100650001500253F453F75089501810205091901DF +:100660002908150025014501750195088102190920 +:100670002910750195088102050109399501750454 +:1006800015002507463B01651481427504950181DB +:100690000155006500050F0992A1028502099F0915 +:1006A000A009A409A509A615002501350045017575 +:1006B00001950581029503810309941500250135F3 +:1006C0000045017501950181020922150125283592 +:1006D000014528750795018102C00921A102850104 +:1006E0000922150125283501452875089501910233 +:1006F0000925A102092609270930093109320933E0 +:10070000093409400941094209430928250C15010A +:100710003501450C750895019100C009500954092F +:1007200051150026FF7F350046FF7F66031055FDFB +:1007300075109503910255006600000952150026B8 +:10074000FF003500461027750895019102095315E1 +:10075000012508350145087508950191020955A143 +:100760000205010930093115002501750195029135 +:1007700002C0050F0956950191029505910309578D +:10078000A1020B01000A000B02000A0066140055CA +:10079000FE150026B400350047A08C00006600005E +:1007A0007508950291025500660000C0050F6603AA +:1007B0001055FD150026FF7F350046FF7F7510950B +:1007C000016600005500C0050F095AA10285020903 +:1007D0002215012528350145287508950191020942 +:1007E0005B095D150026FF00350046102795029134 +:1007F00002095C095E66031055FD26FF7F46FF7FF8 +:100800007510910245006600005500C0095FA10205 +:10081000850309221501252835014528750895010C +:100820009102092315002501350045017504950144 +:1008300091020958A1020B01000A000B02000A00F4 +:10084000750295029102C01580257F36F0D84610BA +:1008500027096075089501910236F0D846102709DE +:100860006195019102150026FF0035004610270909 +:10087000630964750895024610279501C0096EA1A9 +:10088000028504092215012528350145287508959A +:100890000191020970150026FF00350046102775EA +:1008A0000895019102096F1580257F36F0D8461012 +:1008B0002795019102097166140055FE150026FF67 +:1008C00000350047A08C00009102097226FF7F4688 +:1008D000FF7F66031055FD751095019102660000BB +:1008E0005500C00973A10285050922150125283587 +:1008F00001452875089501910209701601FF26FF30 +:100900000036F0D8461027751095019102C0097481 +:10091000A1028506092215012528350145287508FB +:1009200095019102097509761580257F36F0D84624 +:100930001027750895029102C00968A10285070970 +:1009400022150125283501452875089501910209D0 +:100950006C1500261027350046102775109501915B +:100960000209691581257F350046FF007508950C41 +:10097000920201C00966A10285080501093009310A +:100980001581257F350046FF00750895029102C04C +:10099000050F0977A102850A0922150125283501CD +:1009A00045287508950191020978A1020979097A0B +:1009B000097B15012503750895019100C0097C1577 +:1009C0000026FF00350046FF009102C00990A102F9 +:1009D000850B092225281501350145287508950143 +:1009E0009102C00996A102850C09970998099909F5 +:1009F0009A099B099C15012506750895019100C06F +:100A0000097DA102850D097E150026FF00350046EF +:100A10001027750895019102C0096BA102850E0986 +:100A200022150125283501452875089501910209EF +:100A30006D150026FF00350046FF007508950191F1 +:100A400002095166031055FD150026FF7F3500464B +:100A5000FF7F7510950191025500660000C009AB3B +:100A6000A10285050925A1020926092709300931B6 +:100A70000932093309340940094109420943092867 +:100A8000250C15013501450C75089501B100C0050F +:100A900001093B150026FF01350046FF01750A9547 +:100AA00001B1027506B101C0050F0989A1028506D1 +:100AB0000922252815013501452875089501B1023F +:100AC000098BA102098C098D098E250315013501B9 +:100AD000450375089501B100C009AC150027FFFF5B +:100AE0000000350047FFFF000075109501B100C000 +:100AF000097FA10285070980751095011500350051 +:100B000027FFFF000047FFFF0000B102098326FF17 +:100B10000046FF0075089501B10209A909AA7501EF +:100B200095021500250135004501B10275069501B4 +:100B3000B103C0C01201100100000008EB035620F1 +:100B40000100010200011201100100000008EB0386 +:100B50004E2001000102000109022900010100C02C +:100B60003209040000020300000009211101000104 +:100B700022440507050203400001070581034000E8 +:100B8000010403090418034400650061006E00209D +:100B900000430061006D00650072006100000026E6 +:100BA000034C0055004600410020004A006F0079C8 +:100BB0000073007400690063006B0020007700463A +:100BC0000046004200000020034C00550046004152 +:100BD00000200057006800650065006C0020007769 +:100BE0000046004600420000004374726C5265717A +:100BF0002876616C2C6964782C726571293A0053EF +:100C0000746F70206D616E75616C3A002028456EBE +:100C100061626C6564290A00202844697361626C12 +:100C20006564290A002046726565002053656E746C +:100C30000020506C6179696E670A0020416C6C6F0E +:100C4000636174656400203D3E204D6964693A002B +:100C5000446576696365204761696E3A20004F7488 +:100C60006865722000426C6F636B20467265653A5E +:100C7000200020556E6B6E6F776E206F706572610D +:100C800074696F6E002053746F700020537461722A +:100C900074536F6C6F002053746172740045666604 +:100CA000656374204F7065726174696F6E3A0047B6 +:100CB00065745265706F72742050494420506F6F94 +:100CC0006C204665617475726500202062757474CD +:100CD0006F6E3D0020207265706561743D002020BC +:100CE000593D002020583D002020647572613D0070 +:100CF00020206761696E3D002020747970653D0099 +:100D00002020696420203D00536574204566666597 +:100D100063743A002C207374617475733D004372E0 +:100D200065617465204E657720456666656374204D +:100D30003D3E2069643D0055736220203D3E002009 +:100D400020656E6420203D00202073746172743D24 +:100D500000202069643D005365742052616D70204D +:100D6000466F7263653A0020206D61676E69747525 +:100D700064653D00202069643D0053657420436F25 +:100D80006E7374616E7420466F7263653A00202042 +:100D9000706572696F642020203D002020706861BA +:100DA0007365202020203D0020206F6666736574E7 +:100DB0002020203D0020206D61676E697475646598 +:100DC0003D00202069643D005365742050657269C0 +:100DD0006F6469633A002020636F6566662B3D008F +:100DE00020206F66667365743D002020626C6F631F +:100DF0006B203D00202069642020203D0053657455 +:100E000020436F6E646974696F6E3A0020206661DA +:100E1000646554696D6520203D0020206174746113 +:100E2000636B54696D653D00202066616465202018 +:100E30003D00202061747461636B3D002020696473 +:100E4000202020203D0053657420456E76656C6F30 +:100E500070653A0011241FBECFEFDAE0DEBFCDBFD0 +:100E600012E0A0E0B1E0ECE8F0E402C005900D92E1 +:100E7000A03CB107D9F716E0A0ECB2E001C01D928A +:100E8000AB37B107E1F7B4D40C944420B9C85F93F1 +:100E90005FB75F9357EE56BD4F93AF93BF93B6E0E6 +:100EA000A0913F0650914006452F53955093400620 +:100EB00053B15295507E477069F04530B9F04230D9 +:100EC00069F04330F1F04630E9F04130E1F0443070 +:100ED000D9F01BC0529556955E931AC0550F4C9190 +:100EE000542B5C935527551F5E9312C057FB550F2B +:100EF000550F4C91542B5C935527551F51F95E9318 +:100F000007C056955695569556954C91542B5C9323 +:100F1000A0933F06BF91AF914F915F915FBF5F91EB +:100F20001895DC0154E0A50F5C91509550FB50954D +:100F3000A1503C91369557955093660642E0340F88 +:100F400033705C91A1504C91469557955C7F532B23 +:100F50005093670638E0430F4F70A2505C91550FD5 +:100F6000550F5051507F542B509368063C9142E0EE +:100F7000340F3370A3954C91407F342B3295330F4F +:100F8000330F54E0A50F5C915095569537955695C3 +:100F900037953093690656F9A3504C9145FB57F9A4 +:100FA00050936A06A1505C9138E0530F5F70440F74 +:100FB000551F440F551F50936B060895DC0145E003 +:100FC000A40F5C91A395A3953C913873330F329590 +:100FD000550F369557954EEF340F33705093660684 +:100FE0004C914770A3505C91550F469557952EEF45 +:100FF000420F4370550F441F550F441F532B5093FE +:101000006706342FA2505C91A3954C914871550FFF +:10101000440F429546955795409540FB5295452F74 +:10102000407F432B409368065F7054F9352FA350DF +:101030005C91507747E0A40F4C9146FB57F9505113 +:1010400057FB550F532B50936906332730F9AA9558 +:101050005C91A3504C9146FB57F950954427550F8E +:10106000441F532B50936A06342FA2505C91A395D2 +:10107000A3954C914770550F469557952EEF420F0B +:10108000437057FB550F532B50936B06440F40F999 +:1010900040936C060895482F362F8827F894E09ADD +:1010A000E89A2227189B2FEF51E053BD5CE956BD0B +:1010B000A89A209340065FE350933F0678942498C3 +:1010C000259850E05A95F1F7249A259A50914006B8 +:1010D000521719F4A89BFACF089550914006521B5D +:1010E000250F231728F444238AF7450F7AF3E7CF17 +:1010F00083950895D0DF882371F0509140065631D2 +:1011000051F4F89455275093400650913F0653955B +:1011100050933F067894089581E090E008951F92DF +:101120000F920FB60F9211248F9381E08093C0022B +:101130008F910F900FBE0F901F901895CF93DF9354 +:10114000EB0183E085BD0E94081A8091C0028823CC +:10115000C9F11092C0028091C102813069F08130E2 +:1011600028F0843089F08530C9F414C0809179006A +:101170008093C20281E005C0809179008093C30210 +:1011800084E08093C1020CC0809179008093C402F6 +:1011900085E0F7CF809179008093C5021092C1025B +:1011A00080917C00887F80937C0090917C0080916E +:1011B000C1028770892B80937C0080917A008064C3 +:1011C00080937A0081E0888380916D068430A1F558 +:1011D00020916906229526952695237030E080910E +:1011E000680690E0880F991F880F991F282B392BCC +:1011F0008FEF90E0282739273D872C8780916A065A +:1012000090E083709070982F882720916B06820F52 +:10121000911D9A83898380916A0690E08C7F9070FB +:10122000880F991F880F991F9C838B838091690673 +:1012300080958F738B8771C08091670690E0837073 +:101240009070382F222780916606280F311D3A832F +:1012500029838091670681FF03C03C6F3A8329830D +:1012600080916806282F30E02F70307046E0220F02 +:10127000331F4A95E1F78091670686958695280F7A +:10128000311D3C832B838091680683FF03C03C6F34 +:101290003C832B8320916906229526952695237001 +:1012A00080916A0690E08F779070880F991F880F61 +:1012B000991F820F911D9D878C878091680682956A +:1012C0008F708E87809169068F7380528F83909183 +:1012D0006B069F73990F80916A06881F8827881F65 +:1012E000980F9B8780916B0685FF02C0906C9B874F +:1012F0001E821D828091C5022091C40290E0821B53 +:10130000910962E070E00E942A2060586A8780910B +:10131000C30288878091C202898781E090E0DF91D3 +:10132000CF9108950E943F1A86E08093410683E0A2 +:101330008093810085E085BD26E02150822F90E0DA +:10134000FC01EA59F94FE081DC01AD5BB94F8C91AA +:101350008E13EC93222389F788EE93E07DD312D489 +:1013600010BA80917A00876080937A0080917C0027 +:10137000806480937C0080917C00806280937C00FC +:1013800080917A00806880937A0080917A008860EA +:1013900080937A0080917A00806480937A00089527 +:1013A000CF92DF92EF92FF920F931F93DF93CF9331 +:1013B00000D0CDB7DEB7282F6A836A01933031F1B0 +:1013C000943030F4913091F0923009F054C007C05D +:1013D000913209F439C0923209F04DC03CC068E541 +:1013E000E62E6BE0F62E09E210E049C080916D0612 +:1013F000843029F054E3E52E5BE0F52E04C046E48A +:10140000E42E4BE0F42E02E110E039C0813049F0C7 +:10141000813018F0823079F50BC0E1E8FBE084916F +:1014200003C0E5E8FBE08491082F10E07F0127C0AE +:1014300080916D06843021F4E7ECFBE08491F4CFD9 +:10144000EFE9FBE08491F0CF3AE6E32E3BE0F32EA8 +:1014500009E010E014C080916D06843029F09CEA08 +:10146000E92E90E0F92E04C080EFE82E85E0F82EFA +:1014700004E415E004C0EE24FF2400E010E029831A +:10148000CE01019661E070E00E94B11ACE01029691 +:1014900061E070E00E94BC1AF601F182E082C801AE +:1014A0000F900F90CF91DF911F910F91FF90EF90D0 +:1014B000DF90CF9008955E9A08955E9808956F9298 +:1014C0007F928F929F92AF92BF92DF92EF92FF92A4 +:1014D0000F931F93DF93CF93CDB7DEB7C054D040A7 +:1014E0000FB6F894DEBF0FBECDBF80917206843078 +:1014F00009F083C081E08093E9008091E80080FFDB +:1015000013C0809100018E010F5F1F4FB80116DEDE +:10151000C8016FE070E040E050E00E94421E809100 +:10152000E8008E778093E80082E08093E900809164 +:10153000E80082FF62C05E9ADD245E010894A11C6F +:10154000B11C22E0622E712C6C0E7D1E90EA892E59 +:101550009FE0992E3EC0C50161E070E040E050E0A0 +:101560000E94F01D8530B9F3D394E981F0E0EE0FCD +:10157000FF1FEC50FE4F0081118101501040C80147 +:101580008D0D911D8034910598F020E931E05E9831 +:1015900088EE93E0F9013197F1F70197D9F75E9A58 +:1015A00088EE93E0F9013197F1F70197D9F7EFCF82 +:1015B000C301B80140E050E00E94F01D8530C1F346 +:1015C000D00E0F5F1F4FC501B80105D6C4010197AA +:1015D000F1F72091F3008091F200F22EEE2490E0DA +:1015E0008E299F29892B21F08FE38D1508F0B3CF29 +:1015F0008091E8008B778093E800C05CDF4F0FB6E6 +:10160000F894DEBF0FBECDBFCF91DF911F910F9138 +:10161000FF90EF90DF90BF90AF909F908F907F9062 +:101620006F9008950F931F93DF93CF93CDB7DEB7DD +:1016300064970FB6F894DEBF0FBECDBF0091BE0217 +:10164000802F0E94811A8823B1F089EE9BE00E94CE +:101650008A1A85E796E062E070E00E94B11A87E797 +:1016600096E062E070E00E94B11A84E796E061E0E3 +:1016700070E00E94BC1A80917406813021F089309C +:1016800009F06CC02BC0802F0E94811A882321F0A2 +:1016900081E091E00E94A61A80917306813A09F0D8 +:1016A0005DC05E9A809175069091760623E0873042 +:1016B000920731F48E010F5F1F4FC8014CD43EC01A +:1016C0008E010A5F1F4FB80139DD8091E800877FE6 +:1016D0008093E800C8016FE070E038C0802F0E945E +:1016E000811A882321F08BE091E00E94A61A809154 +:1016F0007306813299F55E9A8091E800877F809326 +:10170000E8008E010A5F1F4F6091790670917A069A +:10171000C8010E94421D0E94671B8091750690912E +:10172000760685509340C9F480ED97E00197F1F774 +:10173000C8018E010F5F1F4FB801DDD48091E80012 +:10174000877F8093E800C80165E070E00E94DC1CA0 +:101750008091E8008B778093E8005E9864960FB6DE +:10176000F894DEBF0FBECDBFCF91DF911F910F91D7 +:1017700008950F931F9381E061EC42E30E94321BB6 +:10178000082F82E060EC42E30E94321B10921501A8 +:1017900010921601109217011092180110E090E0BB +:1017A0000823192300FF02C090E001C090E48BB130 +:1017B0008F7B982B9BB91F910F91089584B7877FDA +:1017C00084BF88E10FB6F8948093600010926000A7 +:1017D0000FBE80E090E020E80FB6F89420936100FF +:1017E000809361000FBE569A5E989CDD0C94FE1BA0 +:1017F000E5DF5E98789408C05E9A8CE291E02CD187 +:101800005E988CE291E028D187DC892BA9F357DE22 +:101810000E94311B0E94311B0E9403200E94311B39 +:10182000F3CF94E2899FC001112483559E4F90937A +:10183000C7028093C6020895843180F4282F30E0D7 +:1018400088E290E0289FF001299FF00D389FF00D6D +:101850001124E853FD4F8081826080830895282FF2 +:101860008431F8F4A82FB0E088E290E0A89FF0015E +:10187000A99FF00DB89FF00D1124E853FD4F808112 +:101880008D7F8083A25BB94F15968C91882349F494 +:10189000E091C602F091C7020484F585E02D822F05 +:1018A000099508951F9311E0812FD9DF1F5F14312F +:1018B000D9F71F910895482F8431E8F4282F30E09C +:1018C00088E290E0289FF001299FF00D389FF00DED +:1018D0001124E853FD4F10828091F501481710F450 +:1018E0004093F501E091C602F091C7020684F785A6 +:1018F000E02D842F09950895482F8431C0F4282FB6 +:1019000030E088E290E0289FF001299FF00D389F99 +:10191000F00D1124E853FD4F8081882341F0E8E267 +:101920004E9FF0011124EB52FD4F818108958FEFFE +:1019300008950F931F93382FFB0180819181081721 +:10194000190711F480E010C01183008332FF0BC02F +:10195000E091C602F091C7020088F189E02D842F42 +:10196000622FA801099581E01F910F9108950F93AF +:10197000982FFB018081081711F480E010C00083CC +:1019800092FF0CC0E091C602F091C7020088F18975 +:10199000E02D842F622F402F50E0099581E00F91B8 +:1019A00008952FEF8F3F920719F420E030E008C030 +:1019B0009C0130703695279580709F77280F391FCE +:1019C000C90108952FEF8F3F920719F420E030E00E +:1019D00009C09C01220F331F20703F778F779070D2 +:1019E000280F391FC9010895282F332727FD309567 +:1019F00087FF02C020583048C9010895BC0180ED1E +:101A000097E00E9416206F5F7F4F76956795CB0118 +:101A1000089570E0992787FD90959C01629FC00111 +:101A2000639F900D729F900D11246FEF70E00E94E4 +:101A30002A20862F089520E304C0922F9A95F1F76B +:101A400081508823D1F708952498259885E0F3DF05 +:101A5000249A259A81E0EFCF20EA3FE004C0A895C0 +:101A6000F9013197F1F701974FEF8F3F9407B9F7DD +:101A70000895982F8091C80085FFFCCF9093CE00E9 +:101A80000895EF92FF920F931F93CF93DF937C0102 +:101A90008B018091BF02882341F086E49CE00E9484 +:101AA0008A1AC701B8010E94BC1AC0E0D0E006C083 +:101AB000F701EC0FFD1F8081DCDF2196C017D107F5 +:101AC000B8F3DF91CF911F910F91FF90EF900895A0 +:101AD000FF920F931F93DF93CF9300D00F92CDB758 +:101AE000DEB78C01F62EE091C602F091C7020280AB +:101AF000F381E02DCE0101960995698170E0C1DF87 +:101B0000C8016F2D70E0BDDF1A8206C0F801819117 +:101B10008F01980F9A83FA949A81FF20B9F79195D3 +:101B20009F779A83CE01029661E070E0AADF87EF8B +:101B30008B83CE01039661E070E0A3DF0F900F90DE +:101B40000F90CF91DF911F910F91FF9008950F9308 +:101B50001F938C0180E59CE00E948A1AC8010196BF +:101B600061E070E00E94BC1AE091C602F091C702E9 +:101B70000488F589E02DD80111968C9109951F9163 +:101B80000F9108952EE436E0C90160E070E048E16D +:101B900050E00E943D20539A1092C8008FE190E0DF +:101BA0009093CD008093CC0086E88093CA0088E0B3 +:101BB0008093C9001092CE0028EC32E0C90160E0A9 +:101BC00070E040E253E00E943D202BE436E0C90182 +:101BD00060E070E043E050E00E943D2082E08093AE +:101BE000F501E091C602F091C7020190F081E02D6D +:101BF0000995089582E08093F50128EC32E0C9014F +:101C000060E070E040E253E00C943D208091F501EB +:101C1000833111F440E038C04091F5014F5F4093AB +:101C2000F501415028E230E009C08091F50183318F +:101C3000A0F48091F5018F5F8093F5018091F5010B +:101C400090E0829FF001839FF00D929FF00D112490 +:101C5000E853FD4F8081882341F7242F30E088E24C +:101C600090E0289FF001299FF00D389FF00D11247E +:101C7000DF01A853BD4F81E08C93EB52FD4F8BE108 +:101C8000DF011D928A95E9F7842F08958CE191E098 +:101C90000C94A61A0F931F93CF93DF93EC01198135 +:101CA00082E080934B0680914C06846080934C06C2 +:101CB00080914C06806180934C0610924D06E09115 +:101CC000C602F091C7020480F581E02D812F0995AD +:101CD000082F133041F1143030F4113061F012301C +:101CE00009F04EC013C01530B9F1153028F1163087 +:101CF00009F046C03BC08DE291E00E94A61A002385 +:101D000009F44AC080914C0682600AC08EE391E0DB +:101D10000E94A61A002309F43FC080914C068D7FD3 +:101D200080934C0639C080E591E00E94A61A0023FA +:101D300099F110924D0630C081E691E00E94A61AFA +:101D4000002351F18BE490E087DE54DF80914C0654 +:101D5000826080934C0610C087E691E00E94A61A2C +:101D60000023D1F080914C068160DACF8DE691E0BE +:101D70000E94A61A002381F080914C068E7FD0CF5E +:101D8000107C51F08EE59CE00E948A1ACE010196EB +:101D900061E070E00E94BC1ADF91CF911F910F911A +:101DA0000895DF93CF930F92CDB7DEB7FC01818109 +:101DB00089838091BE020E94811A882351F085E6B2 +:101DC0009CE00E948A1ACE01019661E070E00E94B8 +:101DD000BC1A89818F3F59F40DDFE091C602F09162 +:101DE000C7020684F785E02D8FE7099501C063DD02 +:101DF0000F90CF91DF9108958091BE020E94811AC9 +:101E0000882321F086E791E00C94A61A089580912A +:101E1000BE020E94811A882321F080E991E00C948F +:101E2000A61A0895EF92FF921F93DF93CF930F921C +:101E3000CDB7DEB77C01FC01818189831091BE02A0 +:101E4000812F0E94811A882351F08DE99CE00E9425 +:101E50008A1ACE01019661E070E00E94B11A898170 +:101E60008F3F11F48FE78983F70182818130F1F48C +:101E7000812F0E94811A882321F086E99CE00E942C +:101E8000961AF7018181D8DC9981E92FF0E0E25BB5 +:101E9000F94F8581882309F057C0E091C602F0917F +:101EA000C7020284F385E02D892F2EC0823071F5A0 +:101EB000812F0E94811A882321F08BE89CE00E94E8 +:101EC000961AF0DCE981F0E0E25BF94F8581882326 +:101ED00049F4E091C602F091C7020484F585E02D33 +:101EE0008FE70995F7018181A7DCE981F0E0E25BEA +:101EF000F94F8581882341F5E091C602F091C70230 +:101F00000284F385E02D8FE709951EC0833069F4C4 +:101F1000812F0E94811A882321F085E89CE00E948D +:101F2000961AF70181819BDC0FC0812F0E94811AD4 +:101F3000882351F082E79CE00E94961AC70102961E +:101F400061E070E00E94BC1A0F90CF91DF911F9169 +:101F5000FF90EF900895CF93DF93EC018091BE0244 +:101F60000E94811A882321F08FEA9CE00E94961A31 +:101F700041DE87E088838FEF9FEF9A83898380E13A +:101F80008B8383E08C83DF91CF910895EF92FF9252 +:101F90000F931F93CF93DF93EC01998188E2989F71 +:101FA0007001112488EC92E0E80EF91E8091BE02C7 +:101FB0000E94811A882309F465C088E09DE00E9490 +:101FC0008A1ACE016EE070E00E94BC1A80E09DE0AB +:101FD0000E948A1ACE01019661E070E00E94BC1A4C +:101FE00088EF9CE00E948A1ACE01029661E070E0C0 +:101FF0000E94BC1A80EF9CE00E948A1ACE010996CA +:1020000061E070E00E94BC1A88EE9CE00E948A1A8F +:10201000CE01039662E070E00E94BC1A8B85882393 +:10202000A1F083EE9CE00E948A1ACE010C9661E03A +:1020300070E00E94BC1A8EED9CE00E948A1ACE01CC +:102040000D9661E070E00E94BC1A8D819E81892B03 +:1020500051F084ED9CE00E948A1ACE01059662E060 +:1020600070E00E94BC1A8A85882351F08AEC9CE0BB +:102070000E948A1ACE010A9661E070E00E94BC1AA2 +:102080000E94311B8701035F1F4F8B819C81EFEF03 +:102090008F3F9E0719F420E030E002C082DC9C01F3 +:1020A000F7018081E091C602F091C7020D5F1F4FDA +:1020B0000288F389E02DB80149810995E091C602B3 +:1020C000F091C70202A0F3A1E02DCE01B70109955E +:1020D000682FF701808182FD07C0C7010D96F8DCEB +:1020E000F701808184608083DF91CF911F910F91F0 +:1020F000FF90EF900895FF920F931F93CF93DF937C +:102100008C01EB0186E08883E091C602F091C70262 +:10211000D80111968C910680F781E02D81500995A8 +:10212000F82EE091C602F091C7020084F185E02DFF +:102130000995882311F0198202C068DD898399818D +:10214000992319F482E08A8315C081E08A83E8E24A +:102150009E9FF0011124E853FD4FF686A091C60220 +:10216000B091C70256962D913C915797C801BF0177 +:10217000F90109958FEF9FEF9C838B838091BE02BD +:102180000E94811A8823B1F08EE19DE00E948A1A94 +:10219000CE01019661E070E00E94B11A84E19DE0F9 +:1021A0000E948A1ACE01029661E070E00E94BC1A79 +:1021B0000E94311B86EA91E06881AE0125E030E0A3 +:1021C0000E94C71A85E090E047DCDF91CF911F9114 +:1021D0000F91FF900895CF93DF93EC019B015E9ADE +:1021E00087E39DE066EF71E0AE010E94EA1A9981F3 +:1021F0008881873009F470C08830A8F48330D1F129 +:10220000843030F4813029F1823009F079C024C063 +:10221000853009F447C0E091C602F091C7028630CC +:1022200008F04EC035C08B3009F45FC08C3038F4F4 +:10223000883009F454C08A3009F062C053C08D3030 +:1022400009F459C08D3008F453C08E3009F058C0DD +:1022500055C0CE019BDE54C0E091C602F091C7028A +:1022600068E2969FB001112468537D4F008CF18D78 +:10227000E02D2FC0E091C602F091C70268E2969F60 +:10228000B001112468537D4F028CF38DE02D21C0E5 +:1022900068E2969FB001112468537D4F048CF58D40 +:1022A000E02D17C0E091C602F091C70268E2969F48 +:1022B000B001112468537D4F068CF78DE02D09C0C5 +:1022C00068E2969FB001112468537D4F00A0F1A1F0 +:1022D000E02DCE01099514C0CE0199DD11C0CE01CB +:1022E0008BDD0EC0CE019EDD0BC0CE015ADD08C0D5 +:1022F000CE01D0DC05C0CE012ADC02C0CE01C6DC96 +:102300005E98DF91CF9108958C3010F080E00895B1 +:10231000E82FF0E0E258FD4F80810895FC0185E050 +:10232000808384E192E0089590E0880F991F6623EE +:1023300031F08C549F4F68E671E00E9416209C019A +:1023400020783170220F331F8F779070280F391F3C +:10235000C9010895282F8FEF689FC0011124622FB3 +:1023600070E00E941620CB016F3F710519F010F04C +:102370008FE70895969587958F770895CF93DF938C +:10238000EC01DB01FB013D962FE7218B81E090E022 +:10239000968B858B1686108A1786148A138A128A62 +:1023A00021871682158284E690E09387828783E2F4 +:1023B0001D968C931D97228380E19EE495878487E8 +:1023C0008981813019F4128E118E04C081E091E070 +:1023D000928F818F8981883028F4823030F4813067 +:1023E00091F519C08C3078F527C0FD0131968FEF3B +:1023F0009FEF12969C938E931197938382832FEF76 +:10240000248325831086178281E090E0928781875C +:102410001386268318C0FD0131968FEF9FEF129629 +:102420009C938E931197938382838FEF848385830C +:1024300017821086868307C0FD0131968FEF828355 +:1024400011961C921182DF91CF9108950F931F93E3 +:10245000890120E46EDA1F910F910895AF92BF9227 +:10246000CF92DF92EF92FF920F931F93CF93DF9360 +:102470006C01A62EEC012D96FC013196842F992734 +:1024800087FD909547FD06C080549040E82EEE0CE5 +:102490004FE707C00196880F991F482F415870E8F1 +:1024A000E72EB42EBE188385882311F0E42E4B1935 +:1024B000F601F0806796842F97DA8C018F2DBE018C +:1024C0004A2D24E736DAF601F08022968E2D8CDA3A +:1024D0008C018F2DBE014A2D28E72BDA8B2DDF9141 +:1024E000CF911F910F91FF90EF90DF90CF90BF9011 +:1024F000AF900895009759F46A31710518F464E6B5 +:1025000070E007C0660F771F660F771F02C076DA8C +:10251000BC01CB0108957F928F929F92AF92BF92A0 +:10252000CF92DF92EF92FF920F931F93CF93DF939F +:102530005C01EB01DC011196EC9011972DE0C22EAD +:10254000D12CC60ED71E1A968C918F3F19F420E01D +:1025500030E007C021E030E002C0220F331F8A952F +:10256000E2F78881B6016B5F7F4F890100781170B7 +:10257000000F111F2F773070020F131F4E2D24E410 +:10258000D8D9F5018281883030F4823058F4813016 +:1025900009F0D4C009C08B3008F47EC08B3009F03C +:1025A000CDC0A3C0772406C0D5011C968C91898725 +:1025B000772473944E010894811C911C8881E9E072 +:1025C000F0E0CE0EDF1ED50119960C910695B601EE +:1025D0004E2D2CE4CCD9F880EEEFFFEFCE0EDF1EAF +:1025E000D5011C968C9160E09FDE8C018F2DB60189 +:1025F000E9EFFFEFCE0EDF1E4E2D28E49AD9D5016C +:1026000013968D919C9114979A83898313968D913B +:102610009C911497BFEF8F3F9B07A9F0F401228193 +:102620003381FFEF2F3F3F0771F02817390728F458 +:10263000821B930BB6D98C0108C0D60113960D915D +:102640001C91149702C000E010E08881B6016E5E14 +:102650007F4F4E2D20E66DD9772009F06FC0FE0127 +:102660003196D50117968D919C9118979087878305 +:102670006185728517968D919C9118973BDFF88044 +:10268000EAE0F0E0CE0EDF1E9DD98C018F2DB60161 +:102690004E2D20E54ED952C06E010894C11CD11CAC +:1026A000D50119968C911997F6018283F88089815A +:1026B00019966C91AED998D98C018F2D6496BE0174 +:1026C00064974E2D28E435D9F8806696D601119688 +:1026D0008C91F50161859DD987D98C018F2DBE0123 +:1026E0004E2D2CE426D98FE02AC06E010894C11C1F +:1026F000D11CD50119968C911997F6018283F88027 +:10270000898119966C9185D96FD98C018F2D64962A +:10271000BE0164974E2D28E40CD9F8806696D60148 +:1027200011968C91F501618574D95ED98C018F2D3C +:10273000BE014E2D2CE4FDD88BE001C08BE190E072 +:10274000DF91CF911F910F91FF90EF90DF90CF908D +:10275000BF90AF909F908F907F9008957F928F92BF +:102760009F92AF92BF92CF92DF92EF92FF920F9320 +:102770001F93DF93CF930F92CDB7DEB78C014B0140 +:10278000FC0171808091BE02BCD6882319F1F8014A +:102790008181898387E59DE0BDD6C80164E070E052 +:1027A000EBD681E59DE0B6D6CE01019661E070E002 +:1027B000E3D688E49DE0AED6C801029661E070E001 +:1027C000DBD68FE39DE0A6D6C801039661E070E0FA +:1027D000D3D647D73DE0E32EF12CE80CF91C540189 +:1027E0000894A11CB11CF8012281938192173CF43A +:1027F00081E0F5018387F801D280838105C0F5016E +:102800001386F801D3808281D81A892F992787FDF2 +:102810009095332727FD3095820F931F62E070E07B +:102820000E942A20462FC401672D18DEC82EF4010D +:10283000108121E130E0E20EF31E6D2D8BDD082FBB +:10284000812FB701EFEEFFEFEE0EFF1E472D28E6BA +:102850008ED8F5018681C81609F1C682F40110816F +:1028600024E130E0E20EF31E8C2DF501658172DD6E +:10287000082F812FB701472D2CE679D8F40110815C +:102880002AEF3FEFE20EF31E8C2DF501648162DD2D +:10289000082F812FB701472D24E669D80F90CF91DB +:1028A000DF911F910F91FF90EF90DF90CF90BF903D +:1028B000AF909F908F907F9008957F928F929F927C +:1028C000AF92BF92CF92DF92EF92FF920F931F933E +:1028D000DF93CF930F92CDB7DEB77C015B01FC0194 +:1028E000818189838091BE020CD68823C1F08AE75A +:1028F0009DE010D6C70164E070E03ED684E79DE01D +:1029000009D6CE01019661E070E036D687E69DE0FB +:1029100001D6C701029662E070E02ED6A2D66DE025 +:10292000C62ED12CCA0CDB1C45010894811C911CBD +:10293000F7018281F4018783F7010281138117FF78 +:1029400002C000951095159507950F77F5018081C8 +:10295000B6016F5E7F4F498128E609D8F501708086 +:1029600047E0A42EB12CAC0CBD1C60E0F7018281C5 +:10297000938197FD61E0F4018085D6DC8C01872D81 +:10298000B501498128E40E94990C8FE790E0F60197 +:10299000908F878B128E118E0F90CF91DF911F91A8 +:1029A0000F91FF90EF90DF90CF90BF90AF909F90EE +:1029B0008F907F9008954F925F926F927F928F9247 +:1029C0009F92AF92BF92CF92DF92EF92FF920F93BE +:1029D0001F93DF93CF930F92CDB7DEB73C012B014E +:1029E000FC01818189838091BE028BD5882381F18E +:1029F00088EC9DE08FD5C30167E070E0BDD582EC27 +:102A00009DE088D5CE01019661E070E0B5D585EBFB +:102A10009DE080D5C301029661E070E0ADD588EA03 +:102A20009DE078D5C301039661E070E0A5D58BE900 +:102A30009DE070D5C301049661E070E09DD58EE8FD +:102A40009DE068D5C301059662E070E095D509D692 +:102A50007DE0A72EB12CA40CB51C42010894811C6A +:102A6000911CF301858196818E30910518F40DE457 +:102A700010E00AC0F3E0883E9F0718F001E010E084 +:102A800003C00E94FE0C8C01F4011287018787812C +:102A90009085B8012FDD6C01F201F08025E130E076 +:102AA000A20EB31EE980C8010E94E20C8C018F2D9A +:102AB000B5014E2D20E70E94990CF201F08025EF20 +:102AC0003FEFA20EB31EE980C6010E94E20C8C010A +:102AD0008F2DB501E6EFFFEFAE0EBF1E4E2D20E5A8 +:102AE0000E94990CF201808182FD2EC0F501818146 +:102AF000823019F081818330E1F4F3018481829581 +:102B000086958770853028F4833068F4882329F00F +:102B100006C0873050F08730B9F482E001C083E00E +:102B2000F50181830FC082E001C083E0F50181835C +:102B300005C0F301848181548F3720F481E0F401D2 +:102B4000838702C0F4011386C2016981F3014381C6 +:102B500085DCE82EF201108121E130E0A20EB31EE7 +:102B6000F3016281F7DB082F812FB5012FEE3FEFD4 +:102B7000A20EB31E498128E60E94B70CF40186819B +:102B8000E81619F1E682F201108124E130E0A20E8C +:102B9000B31E8E2DF4016581DDDB082F812FB50179 +:102BA00049812CE60E94B70CF20110812AEF3FEF19 +:102BB000A20EB31E8E2DF4016481CCDB082F812F71 +:102BC000B501498124E60E94B70C0F90CF91DF91A7 +:102BD0001F910F91FF90EF90DF90CF90BF90AF903B +:102BE0009F908F907F906F905F904F9008958F92FD +:102BF0009F92AF92BF92CF92DF92EF92FF920F938C +:102C00001F93DF93CF930F92CDB7DEB75C016B01BB +:102C1000FC0181818983FDE0EF2EF12CE60EF71E89 +:102C20008B010F5F1F4F8091BE026BD4882341F14F +:102C30008DEF9DE06FD4C50165E070E09DD484EF19 +:102C40009DE068D4CE01019661E070E095D48AEEF3 +:102C50009DE060D4C501029661E070E08DD480EE05 +:102C60009DE058D4C501039661E070E085D486EDFF +:102C70009DE050D4C501049661E070E07DD4F1D4AC +:102C8000F5018481F80162810E94090D282FF70166 +:102C900081818D3008F48AC0803120F0803109F0C4 +:102CA00085C05CC0EDE08E2E912C8C0C9D1CF50136 +:102CB00082819481E980882331F5F6019183F08047 +:102CC00084E190E0C80ED91E822F0E94F40C8C0182 +:102CD0008F2DB601ECEEFFEFCE0EDF1E4E2D28E459 +:102CE0000E94990CF601F08028E130E0C20ED31E5C +:102CF000E980F50183810E94F40C8C018F2DB601CF +:102D00004E2D20E551C0F8019183F601F08086E157 +:102D100090E0C80ED91E822F0E94F40C8C018F2DDA +:102D2000B601EAEEFFEFCE0EDF1E4E2D2CE40E9420 +:102D3000990CF5018381803819F40FE710E004C085 +:102D400081950E94F40C8C01F60180812DE030E029 +:102D5000820E931EB401498124E526C0F5018281CB +:102D60009481E980882381F4F6019183F08084E1E5 +:102D700090E0C80ED91E822F0E94F40C8C018F2D7A +:102D8000B6014E2D28E410C0F8019183F601F080C1 +:102D900086E190E0C80ED91E822F0E94F40C8C01AF +:102DA0008F2DB6014E2D2CE40E94990C0F90CF91DF +:102DB000DF911F910F91FF90EF90DF90CF90BF9028 +:102DC000AF909F908F9008956F927F928F929F9275 +:102DD000AF92BF92CF92DF92EF92FF920F931F9329 +:102DE000DF93CF930F92CDB7DEB75C014B01FC01AF +:102DF000818189838091BE0284D3882381F186E416 +:102E00009EE088D3C50168E070E0B6D38CE39EE015 +:102E100081D3CE01019661E070E0AED382E39EE003 +:102E200079D3C501029661E070E0A6D388E29EE006 +:102E300071D3C501039661E070E09ED38AE19EE004 +:102E400069D3C501049662E070E096D38CE09EE001 +:102E500061D3C501069662E070E08ED302D4ADE086 +:102E6000CA2ED12CC80CD91C34010894611C711CC9 +:102E7000F5018281F3018483F5018381F301858368 +:102E8000F50126813781F30133832283F5018681A1 +:102E900097818F5F9F4F19F400E010E008C0F401A4 +:102EA00081819281821B930B0E94D10C8C01F401D1 +:102EB000808122E130E0C20ED31EB601498120E6B6 +:102EC0000E94990CF401F0802DEF3FEFC20ED31E4B +:102ED000E980F501848195810E94D10C8C018F2DB0 +:102EE000B6014E2D2CE50E94990CF401108125E0CD +:102EF00030E0C20ED31EF3018681F50163812ADA28 +:102F0000082F812FB60149812CE60E94B70CF401ED +:102F100010812AEF3FEFC20ED31EF3018681F50127 +:102F2000628118DA082F812FB601498124E60E94B8 +:102F3000B70C0F90CF91DF911F910F91FF90EF9001 +:102F4000DF90CF90BF90AF909F908F907F906F90C9 +:102F50000895EF92FF920F931F93DF93CF9300D0CA +:102F60000F92CDB7DEB78A0195EB99836A838B8385 +:102F70007E010894E11CF11CC70163E070E00E942F +:102F8000410D85EA8983802F8F778A8300701F77B0 +:102F90001B83C70163E070E00E94410D0F900F900A +:102FA0000F90CF91DF911F910F91FF90EF900895B7 +:102FB000482F46958FE76CE750E0CBCFDF93CF9358 +:102FC00000D00F92CDB7DEB795EB99836A838B83E0 +:102FD000CE01019663E070E00E94410D0F900F90CA +:102FE0000F90CF91DF91089560E1E8CF60E3E6CFE5 +:102FF00060E2E4CFDF93CF9300D0CDB7DEB7E82F08 +:103000008150863010F080E00EC085EC8983F0E0BE +:10301000E85EFD4F80818A83CE01019662E070E018 +:103020000E94410D81E00F900F90CF91DF910895A4 +:103030001F93182F06C00E94240D8AE00E941B0DCA +:1030400011501123C1F71F91089584E690E00E946A +:103050002C0D0E94240D87E090E00E942C0D84E04E +:10306000E7DF83E290E00E942C0D83E0E1DF8EE059 +:1030700090E00E942C0D82E0DBDF8EE490E00E9465 +:103080002C0D82E0D5DF84E090E00E942C0D83E0DF +:10309000CFDF8BE390E00E942C0D82E0C9DF8CE74C +:1030A00092E062E070E00E94410D84E190E00E94B5 +:1030B0002C0D83E792E069E070E00E94410D89E306 +:1030C00090E00E942C0D80E492E063E370E00E94A7 +:1030D000410D8FE192E061E270E00E94410D84E0D9 +:1030E00089DF86E490E00C942C0DCF92DF92EF9272 +:1030F000FF920F931F9300E0FF24EE24DD24CC24E5 +:1031000012E08C3060F48A30B8F4873028F48530CF +:1031100098F48230D1F410C08830B9F40DC08F30EB +:1031200089F0803128F48D3049F08E3071F408C078 +:10313000803151F0823149F40F5F07C0F39405C02C +:10314000E39403C0D39401C0C394812F0E947C0CEC +:103150001F5F1531B1F60B3058F482E08F1540F047 +:103160008E1530F08D1520F080E092E09C1508F46B +:1031700081E01F910F91FF90EF90DF90CF90089525 +:103180008C3010F080E00895E82FF0E0E155FD4F1D +:103190008081089580E00895FC0185E0808380E9C6 +:1031A00092E0089508950895089508950895FC0102 +:1031B0008281883028F4823030F4813031F407C0C5 +:1031C0008C3018F406C083E105C080E003C08FE0B6 +:1031D00001C087E090E00895CF93DF93EC01FB01FD +:1031E000DB011D9680E285879FE712969C931297DC +:1031F00015961C9215978981823008F441C0883059 +:1032000018F08C30E8F539C080E415968C9315974A +:1032100016969C93169719969C9319971B961C9239 +:103220001E921A971C969C931C971F969C931F970F +:103230008EE352968C9352978981823059F4189676 +:103240001C921E9217978EE69EE11E969C938E937B +:103250001D970EC080E090E418969C938E9317976C +:1032600085E692E11E969C938E931D9784E793E0EA +:1032700051969C938E93509703C08EE716968C932D +:10328000DF91CF9108958AE892E066E070E00C94B7 +:10329000410DDF93CF9300D000D000D0CDB7DEB783 +:1032A00091EF998360646B838C834D835E8341953A +:1032B000481B4157451B6F7B461B4F774A83CE0106 +:1032C000019666E070E00E94410D26960FB6F894D4 +:1032D000DEBF0FBECDBFCF91DF910895DF93CF93B7 +:1032E00000D00F92CDB7DEB792EF99838B836295B2 +:1032F000607F982F92959F702DE09227262F229520 +:103300002F7092278F709827962B9A83CE01019663 +:1033100063E070E00E94410D0F900F900F90CF91ED +:10332000DF91089561E0DACF63E0D8CF62E0D6CFD5 +:10333000DF93CF9300D0CDB7DEB7843011F48DE1A9 +:1033400005C0833011F080E00BC08AE68A8383EFEA +:103350008983CE01019662E070E00E94410D81E018 +:103360000F900F90CF91DF9108950F931F938901D4 +:1033700020E00E94990C1F910F91089584E690E03F +:103380000E942C0D8DEA92E062E070E00E94410DF7 +:1033900085E992E068E170E00E94410D84E0C8DFB9 +:1033A00084E690E00C942C0D23B5216023BD90930E +:1033B00085008093840081E086BBB09BFECF08959A +:1033C000FC01238182812827848128278581282761 +:1033D000603121F48181908189272827822F82956D +:1033E0008F708227982F969596959827292F269546 +:1033F00029276B3011F481812827822F817008954D +:103400005E9A81EC9FEFD0DF5E9883E89FEFCCCF90 +:103410001F9380916D06843011F010E101C01BE014 +:1034200080E0612F0E944B08E898882361F4809126 +:10343000E9058F5F8093E9058B30F8F0F89480E21E +:103440008093D800FFCF1092E90589E396E0612FC1 +:10345000B7DF882391F080916D06843051F4A6E6A1 +:10346000B6E0E9E3F6E086E001900D928150E1F7E5 +:1034700004C089E396E00E9491071F9108951DB84A +:1034800080E48EB910BA83EF81BB93E095BD85E0EF +:1034900080938100909369008BEC93EF85DFB0DF20 +:1034A0008BEC93EF81DF80E06EE70E944B08A8DF92 +:1034B0008091400680914006813159F489E396E07D +:1034C00060E17EDF882329F083E080936D0680E051 +:1034D0000EC0809140068C3091F489E396E06BE059 +:1034E0006FDF882361F084E080936D0681E00E94A5 +:1034F000110CE898F8948FE08093E8050895E89817 +:10350000CECF9091BF0220E09823981709F421E0D4 +:10351000822F08959C018091BF02882331F0F90128 +:103520002F5F3F4FE491EE23D1F708959C018091E6 +:10353000BF02882311F0C901EDCF0895FC018091ED +:10354000BF02882311F08191FCCF08959C018091E6 +:10355000BF02882311F0C901F1CF08958091BF0205 +:1035600008959C018091BF02882329F0662319F0F9 +:10357000C90170E0F3CF08959C018091BF028823B8 +:1035800029F0662319F0C90170E0E8CF0895EF92A1 +:10359000FF920F931F93DF93CF930F92CDB7DEB7B8 +:1035A000FC0169838A0179018091BF02882351F06F +:1035B000CF01C4DFCE01019661E070E0D2DFC80127 +:1035C000B701DADF0F90CF91DF911F910F91FF903C +:1035D000EF900895AF92BF92CF92DF92EF92FF9259 +:1035E0000F931F93CF93DF93FC016B01F42EE52E15 +:1035F00089018091BF02882349F1CF018BDF8F2D94 +:103600009E2DEC017E01E00EF11E31E0A32EB12CC7 +:103610001AC088819501281B3109C901880F991F9B +:10362000F601E81BF90B1081CE0161E070E099DF33 +:10363000123038F0612F70E061507040CE01019679 +:1036400090DFC10FD11DCE15DF0518F3DF91CF91AB +:103650001F910F91FF90EF90DF90CF90BF90AF90B0 +:1036600008950895382F282F2CC02093E9002317A0 +:1036700021F4762F942F50E006C07091EC009091C9 +:10368000ED005091F00091FF1BC08091EB008E7F08 +:103690008093EB008091ED008D7F8093ED00809111 +:1036A000EB0081608093EB007093EC009093ED0051 +:1036B0005093F0008091EE0087FD02C080E00895F5 +:1036C0002F5F273090F23093E90081E008958091D8 +:1036D000730688239CF404C0809172068823B9F095 +:1036E0008091E80082FFF8CF8091E8008B7780938B +:1036F000E800089580917206882349F08091E800DF +:1037000080FFF8CF8091E8008E778093E8000895DD +:103710004091E4005091E50024E68091EC0080FFA8 +:1037200024C08091E80080FD1EC08091720688232D +:1037300011F482E00895853011F483E008958091BA +:10374000EB0085FF02C081E008958091E400909134 +:10375000E5008417950709F3222311F484E0089506 +:103760002150AC01DACF80E008958091E80082FF1B +:10377000DCCFF9CFEF92FF920F931F934AD051D035 +:1037800008ED10E0F80180818F7780838081806868 +:10379000808380818F7D808319BC10927206109285 +:1037A0006E061092700610926F0680EEE82EF12CD5 +:1037B000F70180818B7F8083F80180818160808325 +:1037C00080E060E042E04EDFE1EEF0E080818E7F5D +:1037D0008083E2EEF0E08081816080838081886078 +:1037E0008083F70180818E7F8083F80180818061F2 +:1037F00080831F910F91FF90EF900895E7EDF0E027 +:1038000080818160808384E082BF81E08093710643 +:10381000B1CFE8EDF0E080818E7F80831092E200EE +:1038200008951092DA001092E10008951F920F920D +:103830000FB60F9211242F933F934F935F936F9383 +:103840007F938F939F93AF93BF93EF93FF93809159 +:10385000E10082FF0AC08091E20082FF06C08091F1 +:10386000E1008B7F8093E100CDD38091DA0080FF6F +:103870001FC08091D80080FF1BC08091DA008E7F2E +:103880008093DA008091D90080FF0DC080E189BD6E +:1038900082E189BD09B400FEFDCF81E0809372060C +:1038A0000E945B0A05C019BC109272060E945D0A54 +:1038B0008091E10080FF18C08091E20080FF14C079 +:1038C0008091E2008E7F8093E2008091E20080612F +:1038D0008093E2008091D80080628093D80019BC68 +:1038E00085E0809372068ED38091E10084FF2DC025 +:1038F0008091E20084FF29C080E189BD82E189BD19 +:1039000009B400FEFDCF8091D8008F7D8093D80050 +:103910008091E1008F7E8093E1008091E2008F7EB4 +:103920008093E2008091E20081608093E2008091C8 +:103930006E06882331F48091E30087FD02C081E0A8 +:1039400001C084E0809372065DD38091E10083FF23 +:1039500022C08091E20083FF1EC08091E100877F3A +:103960008093E10082E08093720610926E0680914F +:10397000E1008E7F8093E1008091E2008E7F809352 +:10398000E2008091E20080618093E20080E060E0EC +:1039900042E068DE37D3FF91EF91BF91AF919F91E5 +:1039A0008F917F916F915F914F913F912F910F90E8 +:1039B0000FBE0F901F9018959C0140917906509171 +:1039C0007A064617570718F4F90190E045C06115CB +:1039D000710511F0AB01F8CF8091E8008E778093EC +:1039E000E80040E050E0F0CF80917206882309F4AF +:1039F00045C0853009F444C08091E80083FF02C0CF +:103A000081E008958091E80082FD32C08091E80055 +:103A100080FF22C08091F3009091F200782F60E047 +:103A2000292F30E0262B372B07C081918093F1009E +:103A3000415050402F5F3F4F4115510519F028303C +:103A4000310598F390E02830310509F491E0809138 +:103A5000E8008E778093E8004115510529F69923F7 +:103A600019F606C080917206882341F0853041F036 +:103A70008091E80082FFF6CF80E0089582E008950B +:103A800083E008959C016115710529F48091E80097 +:103A90008B778093E800F90127C080917206882314 +:103AA00099F1853099F18091E80083FF02C081E0AF +:103AB00008958091E80082FFF0CF06C08091F10068 +:103AC00081936150704059F02091F3008091F20091 +:103AD000322F20E090E0822B932B892B79F7809175 +:103AE000E8008B778093E80061157105B1F606C098 +:103AF00080917206882341F0853041F08091E80082 +:103B000080FFF6CF80E0089582E0089583E0089575 +:103B10009C014091790650917A064617570718F490 +:103B2000F90190E046C06115710511F0AB01F8CFC5 +:103B30008091E8008E778093E80040E050E0F0CF7D +:103B400080917206882309F447C0853009F446C085 +:103B50008091E80083FF02C081E008958091E80031 +:103B600082FD34C08091E80080FF23C08091F30083 +:103B70009091F200782F60E0292F30E0262B372B30 +:103B800008C084918093F1003196415050402F5FDE +:103B90003F4F4115510519F02830310590F390E061 +:103BA0002830310509F491E08091E8008E77809308 +:103BB000E8004115510521F6992309F0C1CF06C04F +:103BC00080917206882341F0853041F08091E800B1 +:103BD00082FFF6CF80E0089582E0089583E00895A3 +:103BE000BF92CF92DF92EF92FF920F931F93CF93EA +:103BF000DF93182F092F7B016A018ADDB82E8823F5 +:103C0000B1F5812F902F9C01E901C114D10439F045 +:103C1000F60180819181E81AF90AC80FD91F00E0E6 +:103C200010E022C08091E80085FD16C08091E80078 +:103C30008B778093E800E7D1C114D10449F0F601F5 +:103C400080819181800F911F9183808385E010C0D6 +:103C50005FDD882349F00CC08091F10089930894BE +:103C6000E108F1080F5F1F4FE114F104D9F68B2D25 +:103C7000DF91CF911F910F91FF90EF90DF90CF9048 +:103C8000BF900895BF92CF92DF92EF92FF920F9371 +:103C90001F93CF93DF93182F092F7B016A0138DD23 +:103CA000B82E8823B1F5812F902F9C01E901C11412 +:103CB000D10439F0F60180819181E81AF90AC80F20 +:103CC000D91F00E010E022C08091E80085FD16C0F9 +:103CD0008091E8008E778093E80095D1C114D104DB +:103CE00049F0F60180819181800F911F918380833B +:103CF00085E010C00DDD882349F00CC089918093C8 +:103D0000F1000894E108F1080F5F1F4FE114F1047E +:103D1000D9F68B2DDF91CF911F910F91FF90EF90EE +:103D2000DF90CF90BF9008950F931F93DF93CF93B1 +:103D3000CDB7DEB7AC970FB6F894DEBF0FBECDBFE0 +:103D4000E3E7F6E08091F100819326E0EB37F2079C +:103D5000C9F70E94120B8091E80083FF3AC180915D +:103D6000730630917406353009F487C0363040F45C +:103D70003130C9F1313070F0333009F02AC133C02D +:103D8000383009F4F7C0393009F406C1363009F08B +:103D900020C197C0803821F0823809F01AC108C0CC +:103DA00090916F0680917006882399F0926011C0FF +:103DB000809177068F708093E9008091EB0090E00E +:103DC00025E0969587952A95E1F7982F91701092A6 +:103DD000E9008091E800877F8093E8009093F100EC +:103DE0001092F100D2C0882319F0823009F0F1C09E +:103DF00090E08F719070009721F0029709F0E9C070 +:103E00000CC080917506813009F0E3C010927006F5 +:103E1000333069F5809370062AC080917506882337 +:103E200031F5209177062F7009F4D3C02093E90073 +:103E30008091EB0080FF1BC0333021F48091EB00B8 +:103E4000806213C08091EB0080618093EB0081E081 +:103E500090E002C0880F991F2A95E2F78093EA004C +:103E60001092EA008091EB0088608093EB00109242 +:103E7000E9008091E800877F8BC0882309F0A9C002 +:103E8000109175061F770FB7F8948091E800877F2F +:103E90008093E8001CDC8091E80080FFFCCF8091DB +:103EA000E3008078812B8093E30080688093E300B7 +:103EB000112311F482E001C083E0809372060FBFEA +:103EC00088C08058823008F084C080917506909137 +:103ED000760623E08C3D920709F033C083E08C83A3 +:103EE0008AE28B837FB7F894DE0115966EE040E09E +:103EF00050E011E2E62FF0E010935700849140FF6C +:103F000003C082958F706F5F8F70282F30E08A30EA +:103F100018F0C901C79602C0C901C0968D939D9340 +:103F20004F5F5F4F4431510529F77FBF8091E80013 +:103F3000877F8093E800CE0103966AE270E03CDD63 +:103F400013C060917706AE014F5F5F4F0E94D009AA +:103F5000BC01009709F43DC08091E800877F809301 +:103F6000E80089819A81D4DD8091E8008B77809385 +:103F7000E8002FC0803869F58091E800877F809342 +:103F8000E80080916E068093F1008091E8008E77C2 +:103F90008093E8009CDB1DC08823D9F490917506BE +:103FA0009230B8F48091E800877F8093E800909386 +:103FB0006E068DDB80916E06882331F48091E300DC +:103FC00087FD02C081E001C084E0809372060E94F8 +:103FD000B90B8091E80083FF0AC08091EB008062FA +:103FE0008093EB008091E800877F8093E800AC9697 +:103FF0000FB6F894DEBF0FBECDBFCF91DF911F91FA +:104000000F91089508951F9380917206882361F09F +:104010001091E9001092E9008091E80083FF01C04F +:1040200083DE1F701093E9001F910895AA1BBB1B2C +:1040300051E107C0AA1FBB1FA617B70710F0A61BA8 +:10404000B70B881F991F5A95A9F780959095BC01C9 +:10405000CD01089597FB092E07260AD077FD04D0DD +:10406000E5DF06D000201AF4709561957F4F089522 +:10407000F6F7909581959F4F0895DC0101C06D93EF +:0C40800041505040E0F70895F894FFCF45 +:10408C00FF4765745265706F7274005365745265A6 +:10409C00706F7274000000000000000853657420FB +:1040AC00437573746F6D20466F72636500456E6166 +:1040BC00626C65204163747561746F72730044693E +:1040CC007361626C65204163747561746F72730007 +:1040DC0053746F7020416C6C204566666563747315 +:1040EC0000526573657400506175736500436F6EA3 +:1040FC0074696E75650053657420446F776E6C6FD0 +:10410C00616420466F7263652053616D706C65004D +:10411C0053657420437573746F6D20466F726365BD +:10412C00204461746100557362203C3D0025188E5B +:10413C0011FA1784117518F817F617F417A9172622 +:10414C0012D817BE11E416F715DB145D14AE138BE1 +:10415C0012BE19CC189819C018CA1896199419922D +:10416C00194919B5194319EC18D218D318D418D504 +:10417C0018D618D718020E00080005000700040016 +:10418C0004000F0003000000040002000200020003 +:10419C0005000400F000010A01020306010504A554 +:1041AC001465B5647FA57E6BB5687FA53600B56CCC +:1041BC007FA52800B5707FA5664CB5747FA57E01E0 +:1041CC00B5407FA57257B5447FA53C43B5487FA544 +:1041DC007E00B54C7FA50400B5507FA50200B554F8 +:1041EC007FA50200B5587FA5007EB55C7FA53C007D +:1041FC00B5607FF000010A0110056BF7C5011206CE +:10420C000502080A0B0D0E0F1001F11040007F0083 +:10421C00F000010A15F10E4301007DF17E04013E10 +:10422C004EF11C45013E2FF10B46017D00F31D069E +:10423C0005030204050508090A0B0000010204022B +:00000001FF diff --git a/ffb-pro.c b/ffb-pro.c index fb2c6b6..7254026 100644 --- a/ffb-pro.c +++ b/ffb-pro.c @@ -5,6 +5,7 @@ Copyright 2012 Tero Loimuneva (tloimu [at] gmail [dot] com) Copyright 2013 Saku Kekkonen + Copyright 2023 Ed Wilkinson Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted @@ -54,6 +55,52 @@ uint8_t FfbproUsbToMidiEffectType(uint8_t usb_effect_type) return usbToMidiEffectType[usb_effect_type]; } +uint8_t FfbproEffectMemFull(uint8_t new_midi_type) +{ + uint8_t count_waveform = 0, + count_spring = 0, count_damper = 0, + count_inertia = 0, count_friction = 0; + + uint8_t midi_type = new_midi_type; //count the new one first + + for (uint8_t id = 2; id <= (MAX_EFFECTS + 1); id++) { + switch (midi_type) { + case 0x12: + case 0x06: + case 0x05: + case 0x02: + case 0x08: + case 0x0A: + case 0x0B: + count_waveform++; + break; + case 0x0D: + count_spring++; + break; + case 0x0E: + count_damper++; + break; + case 0x0F: + count_inertia++; + break; + case 0x10: + count_friction++; + break; +// case 0x01: +// count_custom++; //limit of 4 + } + midi_type = GetMidiEffectType(id); + } + + if (count_waveform > 10 || count_spring > 2 || count_damper > 2 || + count_inertia > 2 || count_friction > 2) { + return 1; + } else { + return 0; + } + //The FFP limit on all loaded effects is 32 total, but we can't get there with the USB PID supported effects only! +} + static void FfbproInitPulses(uint8_t count) { while (count--) { @@ -140,27 +187,38 @@ void FfbproEnableInterrupts(void) FfbSendData(startupFfbData_2, sizeof(startupFfbData_2)); // Initialize effects data memory FfbSendData(startupFfbData_3, sizeof(startupFfbData_3)); // Initialize effects data memory - FfbproSetAutoCenter(0); + FfbproDeviceControl(USB_DCTRL_RESET); // Leave auto centre on WaitMs(70); + } -void FfbproSetAutoCenter(uint8_t enable) +uint8_t FfbproDeviceControl(uint8_t usb_control) { - const uint8_t ac_enable[] = { - 0xc5, 0x01 - }; + /* + USB_DCTRL_ACTUATORS_ENABLE 0x01 + USB_DCTRL_ACTUATORS_DISABLE 0x02 + USB_DCTRL_STOPALL 0x03 + USB_DCTRL_RESET 0x04 + USB_DCTRL_PAUSE 0x05 + USB_DCTRL_CONTINUE 0x06 + */ + const uint8_t usbToMidiControl[] = { + 0x02, // Enable Actuators + 0x03, // Disable Actuators (time stepping continues in background) + 0x06, // Stop All (including stop auto centre) + 0x01, // Reset (stop all effects; free all effects; reset device gain to max; enable actuators; continue; enable auto spring centre) + 0x05, // Pause (time stepping is paused) + 0x04, // Continue + }; - const uint8_t ac_disable[] = { - 0xb5, 0x7c, 0x7f, - 0xa5, 0x7f, 0x00, - 0xc5, 0x06, - }; - - FfbSendData(ac_enable, sizeof(ac_enable)); - if (!enable) { - WaitMs(70); - FfbSendData(ac_disable, sizeof(ac_disable)); - } + if (usb_control < 1 || usb_control > 6) + return 0; //not supported + + uint8_t command[2] = {0xc5}; + command[1] = usbToMidiControl[usb_control-1]; + FfbSendData(command, sizeof(command)); + //Is a wait needed here? + return 1; //supported command } const uint8_t* FfbproGetSysExHeader(uint8_t* hdr_len) @@ -215,9 +273,85 @@ void FfbproSendModify(uint8_t effectId, uint8_t address, uint16_t value) FfbSendData(midi_cmd, 3); } -void FfbproModifyDuration(uint8_t effectId, uint16_t duration) +void FfbproModifyDuration(uint8_t effectState, uint16_t* midi_data_param, uint8_t effectId, uint16_t duration) { - FfbproSendModify(effectId, 0x40, duration); + FfbSetParamMidi_14bit(effectState, midi_data_param, effectId, + FFP_MIDI_MODIFY_DURATION, duration); +} + +void FfbproModifyDeviceGain(uint8_t usb_gain) +{ + FfbproSendModify(0x7f, FFP_MIDI_MODIFY_DEVICEGAIN, (usb_gain >> 1) & 0x7f); +} + +static uint16_t FfbproConvertDirection(uint8_t usbdir, uint8_t reciprocal) +{ + //Convert from USB 0..179 i.e. unit 2deg to MIDI uint_14 0..359 unit deg + //Take reciprocal direction if arg not 0 + uint16_t direction = usbdir * 2; + + if (reciprocal) + direction = (direction + 180) % 360; + + return (direction & 0x7F) + ( (direction & 0x0180) << 1 ); +} + +static uint8_t FfbproModifyParamRange(volatile TEffectState* effect, uint8_t effectId, int8_t offset) +{ + + volatile FFP_MIDI_Effect_Basic *midi_data = (volatile FFP_MIDI_Effect_Basic *)&effect->data; + + FFP_Share_Periodic_Ramp *effect_share = (FFP_Share_Periodic_Ramp *)&effect->share_data; + + int8_t param1, param2; + uint8_t range; + if (offset >= 0) { + param1 = 127; + param2 = -128 + offset * 2; + } else { + param1 = 127 + (offset + 1) * 2; // avoid overflow, but offset -1 has same effect as 0 + param2 = -128; + } // Note range of 0 should not occur - this would cause /div0 in FFbproCalcLevel + range = param1 - param2; + + if (effect_share->invert) //param1 is always set > param2 by MS drivers? Possible this inversion could cause some unexpected behaviour + { + param2 = param1; + param1 = param2 - range; + } + + FfbSetParamMidi_14bit(effect->state, &(midi_data->param1), effectId, + FFP_MIDI_MODIFY_PARAM1, UsbInt8ToMidiInt14(param1)); + FfbSetParamMidi_14bit(effect->state, &(midi_data->param2), effectId, + FFP_MIDI_MODIFY_PARAM2, UsbInt8ToMidiInt14(param2)); + + return range; +} + +static uint8_t FfbproCalcLevel(uint8_t range, uint8_t usb_level) +{ + // Initial levels assume full range - but range is reduced by application of offset + // So compensate by increasing levels (attack, magnitude or fade) + + uint16_t v = ((uint16_t) usb_level * 255) / range; //explicit cast was necessary here to avoid implicit to int16_t and overflow + + if (v > 255) { + return 0x7f; //saturated + } else { + return (v >> 1) & 0x7f; + } +} + +static uint16_t FfbproCalcSampleRate(uint16_t usb_samplePeriod, uint16_t frequency) +{ + if (usb_samplePeriod == USB_SAMPLEPERIOD_DEFAULT) { + if (frequency > (FFP_SAMPLERATE_DEFAULT / 4)) + return frequency * 4; //This is needed to avoid aliasing or attenuation of peaks + else + return FFP_SAMPLERATE_DEFAULT; + } else { + return UsbPeriodToFrequencyHz(usb_samplePeriod); + } } void FfbproSetEnvelope( @@ -226,6 +360,7 @@ void FfbproSetEnvelope( { uint8_t eid = data->effectBlockIndex; + uint16_t midi_fadeTime; /* USB effect data: uint8_t reportId; // =2 @@ -238,20 +373,21 @@ void FfbproSetEnvelope( MIDI effect data: uint8_t command; // always 0x23 -- start counting checksum from here uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant - uint8_t unknown1; // ? always 0x7F + uint8_t unknown1; // Overwrite an allocated effect uint16_t duration; // unit=2ms - uint16_t unknown2; // ? always 0x0000 + uint16_t triggerButton; // Bitwise buttons 1 to 9 from LSB uint16_t direction; - uint8_t unknown3[5]; // ? always 7f 64 00 10 4e + uint8_t gain; + uint16_t sampleRate; //default 0x64 0x00 = 100Hz + uint16_t truncate; //default 0x10 0x4e = 10000 for full waveform uint8_t attackLevel; uint16_t attackTime; uint8_t magnitude; uint16_t fadeTime; uint8_t fadeLevel; - uint8_t waveLength; // 0x6F..0x01 => 1/Hz - uint8_t unknown5; // ? always 0x00 - uint16_t param1; // Constant: positive=7f 00, negative=01 01, Other effects: 01 01 - uint16_t param2; // Constant: 00 00, Other effects 01 01 + uint16_t frequency; // unit=Hz; 1 for constant and ramps + uint16_t param1; // Varies by effect type; Constant: positive=7f 00, negative=01 01, Other effects: 01 01 + uint16_t param2; // Varies by effect type; Constant: 00 00, Other effects 01 01 */ if (DoDebug(DEBUG_DETAIL)) @@ -267,27 +403,26 @@ void FfbproSetEnvelope( } volatile FFP_MIDI_Effect_Basic *midi_data = (volatile FFP_MIDI_Effect_Basic *)&effect->data; + + FFP_Share_Basic_common_t *effect_share = (FFP_Share_Basic_common_t *)&effect->share_data; - effect->usb_attackLevel = data->attackLevel; - effect->usb_fadeLevel = data->fadeLevel; - effect->usb_fadeTime = data->fadeTime; - - midi_data->attackLevel = CalcGain(data->attackLevel, effect->usb_gain); - midi_data->fadeLevel = CalcGain(data->fadeLevel, effect->usb_gain); - - midi_data->attackTime = UsbUint16ToMidiUint14(data->attackTime); + effect_share->usb_attackLevel = data->attackLevel; + effect_share->usb_fadeLevel = data->fadeLevel; + effect_share->usb_fadeTime = data->fadeTime; - if (data->fadeTime == USB_DURATION_INFINITE) - midi_data->fadeTime = MIDI_DURATION_INFINITE; + if (data->fadeTime == USB_DURATION_INFINITE) // is this check needed? Only if duration is not INF but fadeTime is INF - can this occur? + midi_fadeTime = MIDI_DURATION_INFINITE; else - midi_data->fadeTime = UsbUint16ToMidiUint14(effect->usb_duration - effect->usb_fadeTime); - - if (effect->state & MEffectState_SentToJoystick) { - FfbproSendModify(eid, 0x60, midi_data->fadeTime); - FfbproSendModify(eid, 0x5C, midi_data->attackTime); - FfbproSendModify(eid, 0x6C, midi_data->fadeLevel); - FfbproSendModify(eid, 0x64, midi_data->attackLevel); - } + midi_fadeTime = UsbUint16ToMidiUint14_Time(effect_share->usb_duration - effect_share->usb_fadeTime); + + FfbSetParamMidi_14bit(effect->state, &(midi_data->fadeTime), eid, + FFP_MIDI_MODIFY_FADETIME, midi_fadeTime); + FfbSetParamMidi_14bit(effect->state, &(midi_data->attackTime), eid, + FFP_MIDI_MODIFY_ATTACKTIME, UsbUint16ToMidiUint14_Time(data->attackTime)); + FfbSetParamMidi_7bit(effect->state, &(midi_data->fadeLevel), eid, + FFP_MIDI_MODIFY_FADE, FfbproCalcLevel(effect_share->range, data->fadeLevel)); + FfbSetParamMidi_7bit(effect->state, &(midi_data->attackLevel), eid, + FFP_MIDI_MODIFY_ATTACK, FfbproCalcLevel(effect_share->range, data->attackLevel)); } void FfbproSetCondition( @@ -297,6 +432,7 @@ void FfbproSetCondition( uint8_t eid = data->effectBlockIndex; volatile FFP_MIDI_Effect_Basic *common_midi_data = (volatile FFP_MIDI_Effect_Basic *)&effect->data; + FFP_Share_Condition *effect_share = (FFP_Share_Condition *)&effect->share_data; /* USB effect data: uint8_t effectBlockIndex; // 1..40 @@ -322,6 +458,8 @@ void FfbproSetCondition( FlushDebugBuffer(); } + int8_t coeff = CalcGainCoeff(data->positiveCoefficient, effect_share->usb_gain); //Scale coefficients by gain since FFP conditional effects don't have gain parameter + switch (common_midi_data->waveForm) { case 0x0d: // spring (midi: 0x0d) case 0x0e: // damper (midi: 0x0e) @@ -329,28 +467,28 @@ void FfbproSetCondition( { volatile FFP_MIDI_Effect_Spring_Inertia_Damper *midi_data = (FFP_MIDI_Effect_Spring_Inertia_Damper *)&effect->data; - + + uint16_t midi_offsetAxis1; + if (data->parameterBlockOffset == 0) { - midi_data->coeffAxis0 = UsbInt8ToMidiInt14(data->positiveCoefficient); - midi_data->offsetAxis0 = UsbInt8ToMidiInt14(data->cpOffset); + effect_share->usb_coeffAxis0 = data->positiveCoefficient; + FfbSetParamMidi_14bit(effect->state, &(midi_data->coeffAxis0), eid, + FFP_MIDI_MODIFY_COEFFAXIS0, UsbInt8ToMidiInt14(coeff)); + FfbSetParamMidi_14bit(effect->state, &(midi_data->offsetAxis0), eid, + FFP_MIDI_MODIFY_OFFSETAXIS0, UsbInt8ToMidiInt14(data->cpOffset)); + } else { - midi_data->coeffAxis1 = UsbInt8ToMidiInt14(data->positiveCoefficient); + effect_share->usb_coeffAxis1 = data->positiveCoefficient; + FfbSetParamMidi_14bit(effect->state, &(midi_data->coeffAxis1), eid, + FFP_MIDI_MODIFY_COEFFAXIS1, UsbInt8ToMidiInt14(coeff)); if (data->cpOffset == 0x80) - midi_data->offsetAxis1 = 0x007f; + midi_offsetAxis1 = 0x007f; else - midi_data->offsetAxis1 = UsbInt8ToMidiInt14(-data->cpOffset); + midi_offsetAxis1 = UsbInt8ToMidiInt14(-data->cpOffset); + FfbSetParamMidi_14bit(effect->state, &(midi_data->offsetAxis1), eid, + FFP_MIDI_MODIFY_OFFSETAXIS1, midi_offsetAxis1); } - // Send data to MIDI - if (effect->state & MEffectState_SentToJoystick) { - if (data->parameterBlockOffset == 0) { - FfbproSendModify(eid, 0x48, midi_data->coeffAxis0); - FfbproSendModify(eid, 0x50, midi_data->offsetAxis0); - } else { - FfbproSendModify(eid, 0x4C, midi_data->coeffAxis1); - FfbproSendModify(eid, 0x54, midi_data->offsetAxis1); - } - } } break; @@ -359,17 +497,14 @@ void FfbproSetCondition( volatile FFP_MIDI_Effect_Friction *midi_data = (FFP_MIDI_Effect_Friction *)&effect->data; - if (data->parameterBlockOffset == 0) - midi_data->coeffAxis0 = UsbInt8ToMidiInt14(data->positiveCoefficient); - else - midi_data->coeffAxis1 = UsbInt8ToMidiInt14(data->positiveCoefficient); - - // Send data to MIDI - if (effect->state & MEffectState_SentToJoystick) { // Send update - if (data->parameterBlockOffset == 0) - FfbproSendModify(eid, 0x48, midi_data->coeffAxis0); - else - FfbproSendModify(eid, 0x4C, midi_data->coeffAxis1); + if (data->parameterBlockOffset == 0) { + effect_share->usb_coeffAxis0 = data->positiveCoefficient; + FfbSetParamMidi_14bit(effect->state, &(midi_data->coeffAxis0), eid, + FFP_MIDI_MODIFY_COEFFAXIS0, UsbInt8ToMidiInt14(coeff)); + } else { + effect_share->usb_coeffAxis1 = data->positiveCoefficient; + FfbSetParamMidi_14bit(effect->state, &(midi_data->coeffAxis1), eid, + FFP_MIDI_MODIFY_COEFFAXIS1, UsbInt8ToMidiInt14(coeff)); } } break; @@ -396,7 +531,6 @@ void FfbproSetPeriodic( MIDI effect data: - Offset values other than zero do not work and thus it is ignored on FFP */ if (DoDebug(DEBUG_DETAIL)) @@ -413,42 +547,90 @@ void FfbproSetPeriodic( volatile FFP_MIDI_Effect_Basic *midi_data = (volatile FFP_MIDI_Effect_Basic *)&effect->data; - effect->usb_magnitude = data->magnitude; + FFP_Share_Periodic_Ramp *effect_share = (FFP_Share_Periodic_Ramp *)&effect->share_data; - midi_data->param1 = 0x007f; - midi_data->param2 = 0x0101; - - // Calculate waveLength (in MIDI it is in units of 1/Hz and can have value 0x6F..0x01) - if (data->period >= 1000) - midi_data->waveLength = 0x01; - else if (data->period <= 9) - midi_data->waveLength = 0x6F; - else - midi_data->waveLength = (1000 / data->period) & 0x7F; - - // Check phase if relevant (+90 phase for sine makes it a cosine) - if (midi_data->waveForm == 2 || midi_data->waveForm == 3) // sine + uint16_t frequency = 0x0001; // 1Hz + + // Calculate frequency (in MIDI it is in units of Hz and can have value from 1 to 169Hz) + if (data->period <= 13) { //Can actually play up to 169Hz, but this seems a more sensible limit to avoid motor damage, plus the freq steps get quite big + frequency = 77; //Hz + } else if (data->period < 1000) { + frequency = UsbPeriodToFrequencyHz(data->period); + } + + effect_share->frequency = frequency; + + uint16_t sampleRate = FfbproCalcSampleRate(effect_share->usb_samplePeriod, frequency); //Sample rate may need to change as a result of frequency if usb value set to default + + FfbSetParamMidi_14bit(effect->state, &(midi_data->frequency), eid, + FFP_MIDI_MODIFY_FREQUENCY, UsbUint16ToMidiUint14(frequency)); + + FfbSetParamMidi_14bit(effect->state, &(midi_data->sampleRate), eid, + FFP_MIDI_MODIFY_SAMPLERATE, UsbUint16ToMidiUint14(sampleRate)); + + // Check phase and set closest waveform and sign only before effect is sent + // - don't allow changes on the fly - even where possible this would result in harsh steps + if (!(effect->state & MEffectState_SentToJoystick)) { - if (data->phase >= 32 && data->phase <= 224) { - midi_data->waveForm = 3; // cosine + if (midi_data->waveForm == 2 || midi_data->waveForm == 3) // sine or cosine + { + switch (data->phase / 32) //USB 255 = 2*pi or 360deg so 32 is 45deg + { + case 0: //0-44deg + case 7: + { + midi_data->waveForm = 2; + effect_share->invert = 0; + break; + } + case 1: + case 2: + { + midi_data->waveForm = 3; + effect_share->invert = 0; + break; + } + case 3: + case 4: + { + midi_data->waveForm = 2; + effect_share->invert = 1; //i.e. -sine + break; + } + case 5: + case 6: + { + midi_data->waveForm = 3; + effect_share->invert = 1; + break; + } + } } else { - midi_data->waveForm = 2; // sine - } - - // Calculate min-max from magnitude and offset - uint8_t magnitude = CalcGain(data->magnitude, effect->usb_gain); // already at MIDI-level i.e. 1/2 of USB level! - midi_data->param1 = UsbInt8ToMidiInt14(data->offset / 2 + magnitude); // max - midi_data->param2 = UsbInt8ToMidiInt14(data->offset / 2 - magnitude); // min - if (effect->state & MEffectState_SentToJoystick) { - FfbproSendModify(eid, 0x74, midi_data->param1); - FfbproSendModify(eid, 0x78, midi_data->param2); + if ((data->phase > 64) && (data->phase < 192)) { //for square, tri, sawtooth + effect_share->invert = 1; + } else { + effect_share->invert = 0; + } } } - - if (effect->state & MEffectState_SentToJoystick) { - // FfbProSendModify(eid, 0x74, midi_data->magnitude); // FFP does not actually support changing magnitude on-fly here - FfbproSendModify(eid, 0x70, midi_data->waveLength); + + // Calculate min max and available range from offset. Then Modify. Invert if needed. + uint8_t range = FfbproModifyParamRange(effect, eid, data->offset); + + // Calculate magnitude relative to available range + FfbSetParamMidi_7bit(effect->state, &(midi_data->magnitude), eid, + FFP_MIDI_MODIFY_MAGNITUDE, FfbproCalcLevel(range, data->magnitude)); + + // Check whether envelope levels need to be updated too + if (range != effect_share->range) + { + effect_share->range = range; + FfbSetParamMidi_7bit(effect->state, &(midi_data->fadeLevel), eid, + FFP_MIDI_MODIFY_FADE, FfbproCalcLevel(range, effect_share->usb_fadeLevel)); + FfbSetParamMidi_7bit(effect->state, &(midi_data->attackLevel), eid, + FFP_MIDI_MODIFY_ATTACK, FfbproCalcLevel(range, effect_share->usb_attackLevel)); } + } void FfbproSetConstantForce( @@ -465,20 +647,22 @@ void FfbproSetConstantForce( MIDI effect data: uint8_t command; // always 0x23 -- start counting checksum from here uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant - uint8_t unknown1; // ? always 0x7F + uint8_t unknown1; // Overwrite an allocated effect uint16_t duration; // unit=2ms - uint16_t unknown2; // ? always 0x0000 + uint16_t triggerButton; // Bitwise buttons 1 to 9 from LSB uint16_t direction; - uint8_t unknown3[5]; // ? always 7f 64 00 10 4e + uint8_t gain; + uint16_t sampleRate; //default 0x64 0x00 = 100Hz + uint16_t truncate; //default 0x10 0x4e = 10000 for full waveform uint8_t attackLevel; uint16_t attackTime; uint8_t magnitude; uint16_t fadeTime; uint8_t fadeLevel; - uint8_t waveLength; // 0x6F..0x01 => 1/Hz - uint8_t unknown5; // ? always 0x00 - uint16_t param1; // Constant: positive=7f 00, negative=01 01, Other effects: 01 01 - uint16_t param2; // Constant: 00 00, Other effects 01 01 + uint16_t frequency; // unit=Hz; 1 for constant and ramps + uint16_t param1; // Varies by effect type; Constant: positive=7f 00, negative=01 01, Other effects: 01 01 + uint16_t param2; // Varies by effect type; Constant: 00 00, Other effects 01 01 + */ if (DoDebug(DEBUG_DETAIL)) @@ -491,29 +675,36 @@ void FfbproSetConstantForce( } volatile FFP_MIDI_Effect_Basic *midi_data = (volatile FFP_MIDI_Effect_Basic *)&effect->data; + + FFP_Share_Constant *effect_share = (FFP_Share_Constant *)&effect->share_data; + + effect_share->usb_magnitude = data->magnitude; - effect->usb_magnitude = data->magnitude; + uint8_t midi_magnitude; if (data->magnitude >= 0) { - midi_data->magnitude = CalcGain(data->magnitude, effect->usb_gain); - midi_data->param1 = 0x007f; - } else { - midi_data->magnitude = CalcGain(-(data->magnitude+1), effect->usb_gain); - midi_data->param1 = 0x0101; - } + midi_magnitude = (data->magnitude >> 1) & 0x7f; - midi_data->param2 = 0x0000; + } else { + midi_magnitude = (( -(data->magnitude + 1)) >> 1) & 0x7f; - if (effect->state & MEffectState_SentToJoystick) { - FfbproSendModify(eid, 0x74, midi_data->magnitude); - FfbproSendModify(eid, 0x7C, midi_data->param1); } + + FfbSetParamMidi_7bit(effect->state, &(midi_data->magnitude), eid, + FFP_MIDI_MODIFY_MAGNITUDE, midi_magnitude); + FfbSetParamMidi_14bit(effect->state, &(midi_data->direction), eid, + FFP_MIDI_MODIFY_DIRECTION, FfbproConvertDirection(effect_share->usb_direction, (data->magnitude < 0))); + //reciprocal direction if -ve + + midi_data->param1 = 0x007F; // never again modified + midi_data->param2 = 0x0000; // never again modified } void FfbproSetRampForce( USB_FFBReport_SetRampForce_Output_Data_t* data, volatile TEffectState* effect) { + uint8_t eid = data->effectBlockIndex; if (DoDebug(DEBUG_DETAIL)) { uint8_t eid = data->effectBlockIndex; @@ -525,7 +716,6 @@ void FfbproSetRampForce( FlushDebugBuffer(); } - // FFP supports only ramp up from MIN to MAX and ramp down from MAX to MIN? /* USB effect data: uint8_t reportId; // =6 @@ -535,18 +725,40 @@ void FfbproSetRampForce( */ volatile FFP_MIDI_Effect_Basic *midi_data = (volatile FFP_MIDI_Effect_Basic *)&effect->data; + + FFP_Share_Periodic_Ramp *effect_share = (FFP_Share_Periodic_Ramp *)&effect->share_data; - if (data->start < 0) - midi_data->param1 = 0x0100 | (-(data->start+1)); - else - midi_data->param1 = data->start; + // Same approach as periodic waveforms + int8_t offset = ((int16_t)data->start + (int16_t)data->end)/2; //Finding midpoint could be done more efficiently without casting + uint8_t magnitude; + + if (data->start > data->end) { + effect_share->invert = 1; //Ramp Down + magnitude = data->start - data->end; + } else { + effect_share->invert = 0; //Ramp Up + magnitude = data->end - data->start; + } - midi_data->param2 = UsbInt8ToMidiInt14(data->end); - if (effect->state & MEffectState_SentToJoystick) { - FfbproSendModify(data->reportId, 0x78, midi_data->param1); - FfbproSendModify(data->reportId, 0x74, midi_data->param2); + // Calculate min max and available range from offset. Then Modify. Invert if needed. + uint8_t range = FfbproModifyParamRange(effect, eid, offset); + + + // Calculate magnitude relative to available range + FfbSetParamMidi_7bit(effect->state, &(midi_data->magnitude), eid, + FFP_MIDI_MODIFY_MAGNITUDE, FfbproCalcLevel(range, magnitude)); + + // Check whether envelope levels need to be updated too + if (range != effect_share->range) + { + effect_share->range = range; + FfbSetParamMidi_7bit(effect->state, &(midi_data->fadeLevel), eid, + FFP_MIDI_MODIFY_FADE, FfbproCalcLevel(range, effect_share->usb_fadeLevel)); + FfbSetParamMidi_7bit(effect->state, &(midi_data->attackLevel), eid, + FFP_MIDI_MODIFY_ATTACK, FfbproCalcLevel(range, effect_share->usb_attackLevel)); } + } int FfbproSetEffect( @@ -573,101 +785,98 @@ int FfbproSetEffect( volatile FFP_MIDI_Effect_Basic *midi_data = (volatile FFP_MIDI_Effect_Basic *)&effect->data; uint8_t midi_data_len = sizeof(FFP_MIDI_Effect_Basic); // default MIDI data size - bool is_periodic = false; - + + // Data applying to all effects + uint16_t buttonBits = 0; + if (data->triggerButton != USB_TRIGGERBUTTON_NULL) + buttonBits = (1 << data->triggerButton); + //Buttons 1-9 from LSB + FfbSetParamMidi_14bit(effect->state, &(midi_data->triggerButton), eid, + FFP_MIDI_MODIFY_TRIGGERBUTTON, (buttonBits & 0x7F) + ( (buttonBits & 0x0180) << 1 )); + + uint8_t reciprocal = 0; + bool is_constant = false; + // Fill in the effect type specific data switch (data->effectType) - { + { + case USB_EFFECT_CONSTANT: + { + FFP_Share_Constant *effect_share = (FFP_Share_Constant *)&effect->share_data; + is_constant = true; + if (effect_share->usb_magnitude < 0) { + reciprocal = 1; + } + effect_share->usb_direction = data->directionX; + } case USB_EFFECT_SQUARE: case USB_EFFECT_SINE: case USB_EFFECT_TRIANGLE: case USB_EFFECT_SAWTOOTHDOWN: case USB_EFFECT_SAWTOOTHUP: - is_periodic = true; - case USB_EFFECT_CONSTANT: case USB_EFFECT_RAMP: { + FFP_Share_Basic_common_t *effect_share = (FFP_Share_Basic_common_t *)&effect->share_data; /* MIDI effect data: uint8_t command; // always 0x23 -- start counting checksum from here uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant - uint8_t unknown1; // ? always 0x7F + uint8_t unknown1; // Overwrite an allocated effect uint16_t duration; // unit=2ms - uint16_t unknown2; // ? always 0x0000 + uint16_t triggerButton; // Bitwise buttons 1 to 9 from LSB uint16_t direction; - uint8_t unknown3[5]; // ? always 7f 64 00 10 4e + uint8_t gain; + uint16_t sampleRate; //default 0x64 0x00 = 100Hz + uint16_t truncate; //default 0x10 0x4e = 10000 for full waveform uint8_t attackLevel; uint16_t attackTime; uint8_t magnitude; uint16_t fadeTime; uint8_t fadeLevel; - uint8_t waveLength; // 0x6F..0x01 => 1/Hz - uint8_t unknown5; // ? always 0x00 - uint16_t param1; // Constant: positive=7f 00, negative=01 01, Other effects: 01 01 - uint16_t param2; // Constant: 00 00, Other effects 01 01 - */ + uint16_t frequency; // unit=Hz; 1 for constant and ramps + uint16_t param1; // Varies by effect type; Constant: positive=7f 00, negative=01 01, Other effects: 01 01 + uint16_t param2; // Varies by effect type; Constant: 00 00, Other effects 01 01 + */ + // Effect Gain + FfbSetParamMidi_7bit(effect->state, &(midi_data->gain), eid, + FFP_MIDI_MODIFY_GAIN, (data->gain >> 1) & 0x7f); + // Convert direction - uint16_t usbdir = data->directionX; - usbdir = usbdir * 2; - uint16_t dir = (usbdir & 0x7F) + ( (usbdir & 0x0180) << 1 ); - midi_data->direction = dir; - + FfbSetParamMidi_14bit(effect->state, &(midi_data->direction), eid, + FFP_MIDI_MODIFY_DIRECTION, FfbproConvertDirection(data->directionX, reciprocal)); //reciprocal only if -ve constant force + // Recalculate fadeTime for MIDI since change to duration changes the fadeTime too + effect_share->usb_duration = data->duration; // store for later calculation of + + uint16_t midi_fadeTime; if (data->duration == USB_DURATION_INFINITE) { - midi_data->fadeTime = MIDI_DURATION_INFINITE; + midi_fadeTime = MIDI_DURATION_INFINITE; } else { - if (effect->usb_fadeTime == USB_DURATION_INFINITE) { - midi_data->fadeTime = MIDI_DURATION_INFINITE; + if (effect_share->usb_fadeTime == USB_DURATION_INFINITE) { + midi_fadeTime = MIDI_DURATION_INFINITE; } else { - if (effect->usb_duration > effect->usb_fadeTime) { + if (data->duration > effect_share->usb_fadeTime) { // add some safety and special case handling - midi_data->fadeTime = UsbUint16ToMidiUint14(effect->usb_duration - effect->usb_fadeTime); + midi_fadeTime = UsbUint16ToMidiUint14_Time(data->duration - effect_share->usb_fadeTime); } else { - midi_data->fadeTime = midi_data->duration; + midi_fadeTime = midi_data->duration; } } - } - - // Gain and its effects (magnitude and envelope levels) - bool gain_changed = (effect->usb_gain != data->gain); - if (gain_changed) { -// LogTextP(PSTR(" New gain:")); -// LogBinary(&data->gain, 1); - - effect->usb_gain = data->gain; - midi_data->attackLevel = CalcGain(effect->usb_attackLevel, data->gain); - midi_data->fadeLevel = CalcGain(effect->usb_fadeLevel, data->gain); - - if (is_periodic) { - // Calculate min-max from magnitude and offset, since magnitude may be affected by gain we must calc them here too for periodic effects - uint8_t magnitude = CalcGain(effect->usb_magnitude, effect->usb_gain); // already at MIDI-level i.e. 1/2 of USB level! - midi_data->param1 = UsbInt8ToMidiInt14(effect->usb_offset + magnitude); // max - midi_data->param2 = UsbInt8ToMidiInt14(effect->usb_offset - magnitude); // min - if (effect->state & MEffectState_SentToJoystick) { - FfbproSendModify(eid, 0x74, midi_data->param1); // TODO - FfbproSendModify(eid, 0x78, midi_data->param2); - } - } else { - midi_data->magnitude = CalcGain(effect->usb_magnitude, data->gain); - } - } - - // Send data to MIDI - if (effect->state & MEffectState_SentToJoystick) + } + FfbSetParamMidi_14bit(effect->state, &(midi_data->fadeTime), eid, + FFP_MIDI_MODIFY_FADETIME, midi_fadeTime); + + if (!is_constant) { - FfbproSendModify(eid, 0x48, midi_data->direction); // TODO - FfbproSendModify(eid, 0x60, midi_data->fadeTime); - if (gain_changed) { - FfbproSendModify(eid, 0x6C, midi_data->fadeLevel); // might have changed due gain - FfbproSendModify(eid, 0x64, midi_data->attackLevel); // might have changed due gain - if (!is_periodic) { - FfbproSendModify(eid, 0x74, midi_data->magnitude); // might have changed due gain - } - } - } else { - FfbSendSysEx((uint8_t*)midi_data, sizeof(FFP_MIDI_Effect_Basic)); - effect->state |= MEffectState_SentToJoystick; + FFP_Share_Periodic_Ramp *effect_share = (FFP_Share_Periodic_Ramp *)&effect->share_data; + + effect_share->usb_samplePeriod = data->samplePeriod; + + uint16_t sampleRate = FfbproCalcSampleRate(data->samplePeriod, effect_share->frequency); + + FfbSetParamMidi_14bit(effect->state, &(midi_data->sampleRate), eid, + FFP_MIDI_MODIFY_SAMPLERATE, UsbUint16ToMidiUint14(sampleRate)); } } break; @@ -682,18 +891,26 @@ int FfbproSetEffect( uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant uint8_t unknown1; // ? always 0x7F uint16_t duration; // unit=2ms - uint16_t unknown2; // ? always 0x0000 + uint16_t triggerButton; // Bitwise buttons 1 to 9 from LSB uint16_t coeffAxis0; uint16_t coeffAxis1; uint16_t offsetAxis0; uint16_t offsetAxis1; */ -// volatile FFP_MIDI_Effect_Spring_Inertia_Damper *midi_data = (FFP_MIDI_Effect_Spring_Inertia_Damper *) &gEffectStates[eid].data; - midi_data_len = sizeof(FFP_MIDI_Effect_Spring_Inertia_Damper); - // Send data to MIDI - if (effect->state & MEffectState_SentToJoystick) { - } + volatile FFP_MIDI_Effect_Spring_Inertia_Damper *midi_data = + (FFP_MIDI_Effect_Spring_Inertia_Damper *)&effect->data; + + FFP_Share_Condition *effect_share = (FFP_Share_Condition *)&effect->share_data; + + effect_share->usb_gain = data->gain; //Scale coefficients by gain since FFP conditional effects don't have gain parameter + FfbSetParamMidi_14bit(effect->state, &(midi_data->coeffAxis0), eid, + FFP_MIDI_MODIFY_COEFFAXIS0, UsbInt8ToMidiInt14(CalcGainCoeff(effect_share->usb_coeffAxis0, data->gain))); + FfbSetParamMidi_14bit(effect->state, &(midi_data->coeffAxis1), eid, + FFP_MIDI_MODIFY_COEFFAXIS1, UsbInt8ToMidiInt14(CalcGainCoeff(effect_share->usb_coeffAxis1, data->gain))); + + midi_data_len = sizeof(FFP_MIDI_Effect_Spring_Inertia_Damper); + } break; @@ -705,16 +922,23 @@ int FfbproSetEffect( uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant uint8_t unknown1; // ? always 0x7F uint16_t duration; // unit=2ms - uint16_t unknown2; // ? always 0x0000 + uint16_t triggerButton; // Bitwise buttons 1 to 9 from LSB uint16_t coeffAxis0; uint16_t coeffAxis1; */ -// volatile FFP_MIDI_Effect_Friction *midi_data = (FFP_MIDI_Effect_Friction *) &gEffectStates[eid].data; + volatile FFP_MIDI_Effect_Friction *midi_data = + (FFP_MIDI_Effect_Friction *)&effect->data; + + FFP_Share_Condition *effect_share = (FFP_Share_Condition *)&effect->share_data; + + effect_share->usb_gain = data->gain; //Scale coefficients by gain since FFP conditional effects don't have gain parameter + FfbSetParamMidi_14bit(effect->state, &(midi_data->coeffAxis0), eid, + FFP_MIDI_MODIFY_COEFFAXIS0, UsbInt8ToMidiInt14(CalcGainCoeff(effect_share->usb_coeffAxis0, data->gain))); + FfbSetParamMidi_14bit(effect->state, &(midi_data->coeffAxis1), eid, + FFP_MIDI_MODIFY_COEFFAXIS1, UsbInt8ToMidiInt14(CalcGainCoeff(effect_share->usb_coeffAxis1, data->gain))); + midi_data_len = sizeof(FFP_MIDI_Effect_Friction); - // Send data to MIDI - if (effect->state & MEffectState_SentToJoystick) { - } } break; @@ -746,25 +970,72 @@ void FfbproCreateNewEffect( volatile FFP_MIDI_Effect_Basic *midi_data = (volatile FFP_MIDI_Effect_Basic *)&effect->data; midi_data->magnitude = 0x7f; - midi_data->waveLength = 0x01; + midi_data->frequency = 0x0001; midi_data->attackLevel = 0x00; midi_data->attackTime = 0x0000; midi_data->fadeLevel = 0x00; midi_data->fadeTime = 0x0000; - + midi_data->gain = 0x7F; + midi_data->triggerButton = 0x0000; + midi_data->sampleRate = FFP_SAMPLERATE_DEFAULT; + // Constants midi_data->command = 0x23; midi_data->unknown1 = 0x7F; - midi_data->unknown2 = 0x0000; - midi_data->unknown3[0] = 0x7F; - midi_data->unknown3[1] = 0x64; - midi_data->unknown3[2] = 0x00; - midi_data->unknown3[3] = 0x10; - midi_data->unknown3[4] = 0x4E; - + midi_data->truncate = 0x4E10; // 10000 if (inData->effectType == 0x01) // constant midi_data->param2 = 0x0000; else midi_data->param2 = 0x0101; -} + + //Set defaults for shared data + switch (inData->effectType) { + case USB_EFFECT_SQUARE: + case USB_EFFECT_SINE: + case USB_EFFECT_TRIANGLE: + case USB_EFFECT_SAWTOOTHDOWN: + case USB_EFFECT_SAWTOOTHUP: + case USB_EFFECT_RAMP: + { + FFP_Share_Periodic_Ramp *effect_share = (FFP_Share_Periodic_Ramp *)&effect->share_data; + + effect_share->usb_duration = USB_DURATION_INFINITE; + effect_share->usb_fadeTime = USB_DURATION_INFINITE; + effect_share->usb_attackLevel = 0xFF; + effect_share->usb_fadeLevel = 0xFF; + effect_share->usb_samplePeriod = USB_SAMPLEPERIOD_DEFAULT; + + effect_share->frequency = 1; // Hz constant for Ramp + effect_share->invert = 0; + effect_share->range = 255; + break; + } + case USB_EFFECT_CONSTANT: + { + FFP_Share_Constant *effect_share = (FFP_Share_Constant *)&effect->share_data; + + effect_share->usb_duration = USB_DURATION_INFINITE; + effect_share->usb_fadeTime = USB_DURATION_INFINITE; + effect_share->usb_attackLevel = 0xFF; + effect_share->usb_fadeLevel = 0xFF; + effect_share->usb_magnitude = 0; + effect_share->usb_direction = 0; + + effect_share->range = 255; //constant for Constant + break; + } + case USB_EFFECT_SPRING: + case USB_EFFECT_DAMPER: + case USB_EFFECT_INERTIA: + case USB_EFFECT_FRICTION: + { + FFP_Share_Condition *effect_share = (FFP_Share_Condition *)&effect->share_data; + + effect_share->usb_gain = 0xFF; + effect_share->usb_coeffAxis0 = 0; + effect_share->usb_coeffAxis1 = 0; + } + } + +} \ No newline at end of file diff --git a/ffb-pro.h b/ffb-pro.h index e6f5abd..866c723 100644 --- a/ffb-pro.h +++ b/ffb-pro.h @@ -12,18 +12,19 @@ typedef struct { uint8_t command; // always 0x23 -- start counting checksum from here uint8_t waveForm; // 2=sine, 5=Square, 6=RampUp, 7=RampDown, 8=Triange, 0x12=Constant - uint8_t unknown1; // ? always 0x7F + uint8_t unknown1; // Overwrite an allocated effect - leave as unknown1 because we don't need it and don't know what it does for wheel uint16_t duration; // unit=2ms - uint16_t unknown2; // ? always 0x0000 + uint16_t triggerButton; // Bitwise buttons 1 to 9 from LSB uint16_t direction; - uint8_t unknown3[5]; // ? always 7f 64 00 10 4e + uint8_t gain; + uint16_t sampleRate; //default 0x64 0x00 = 100Hz + uint16_t truncate; //default 0x10 0x4e = 10000 for full waveform uint8_t attackLevel; uint16_t attackTime; - uint8_t magnitude; + uint8_t magnitude; //i.e. envelope sustain uint16_t fadeTime; uint8_t fadeLevel; - uint8_t waveLength; // 0x6F..0x01 => 1/Hz - uint8_t unknown5; // ? always 0x00 + uint16_t frequency; // unit=Hz; 1 for constant and ramps uint16_t param1; // Varies by effect type; Constant: positive=7f 00, negative=01 01, Other effects: 01 01 uint16_t param2; // Varies by effect type; Constant: 00 00, Other effects 01 01 } FFP_MIDI_Effect_Basic; @@ -32,9 +33,9 @@ typedef struct { uint8_t command; // always 0x23 -- start counting checksum from here uint8_t waveForm; // 0xd=Spring, 0x0e=Damper, 0xf=Inertia - uint8_t unknown1; // ? always 0x7F + uint8_t unknown1; // Overwrite an allocated effect - leave as unknown1 because we don't need it and don't know what it does for wheel uint16_t duration; // unit=2ms - uint16_t unknown2; // ? always 0x0000 + uint16_t triggerButton; // Bitwise buttons 1 to 9 from LSB uint16_t coeffAxis0; uint16_t coeffAxis1; uint16_t offsetAxis0; @@ -45,22 +46,66 @@ typedef struct { uint8_t command; // always 0x23 -- start counting checksum from here uint8_t waveForm; // 0x10=Friction - uint8_t unknown1; // ? always 0x7F + uint8_t unknown1; // Overwrite an allocated effect - leave as unknown1 because we don't need it and don't know what it does for wheel uint16_t duration; // unit=2ms - uint16_t unknown2; // ? always 0x0000 + uint16_t triggerButton; // Bitwise buttons 1 to 9 from LSB uint16_t coeffAxis0; uint16_t coeffAxis1; } FFP_MIDI_Effect_Friction; +// Data structures: to be shared between Output reports for calculating MIDI parameters coupled to multiple USB parameters +// Set [MAX_SHARE_DATA] bytes to largest of +typedef struct + { + uint16_t usb_duration; + uint16_t usb_fadeTime; + uint8_t usb_attackLevel; + uint8_t usb_fadeLevel; + uint8_t range; + uint16_t usb_samplePeriod; + uint16_t frequency; + uint8_t invert; + } FFP_Share_Periodic_Ramp; + +typedef struct + { + uint16_t usb_duration; + uint16_t usb_fadeTime; + uint8_t usb_attackLevel; + uint8_t usb_fadeLevel; + uint8_t range; //Not varied for constant but simplifies use of common structure + uint8_t usb_magnitude; + uint8_t usb_direction; + } FFP_Share_Constant; + +typedef struct + { + uint16_t usb_duration; + uint16_t usb_fadeTime; + uint8_t usb_attackLevel; + uint8_t usb_fadeLevel; + uint8_t range; + } FFP_Share_Basic_common_t; + +typedef struct + { + uint8_t usb_coeffAxis0; + uint8_t usb_coeffAxis1; + uint8_t usb_gain; + } FFP_Share_Condition; + void FfbproEnableInterrupts(void); +uint8_t FfbproDeviceControl(uint8_t usb_control); const uint8_t* FfbproGetSysExHeader(uint8_t* hdr_len); -void FfbproSetAutoCenter(uint8_t enable); void FfbproStartEffect(uint8_t id); void FfbproStopEffect(uint8_t id); void FfbproFreeEffect(uint8_t id); -void FfbproModifyDuration(uint8_t effectId, uint16_t duration); +void FfbproSendModify(uint8_t effectId, uint8_t address, uint16_t value); + +void FfbproModifyDuration(uint8_t effectState, uint16_t* midi_data_param, uint8_t effectId, uint16_t duration); +void FfbproModifyDeviceGain(uint8_t gain); void FfbproSetEnvelope(USB_FFBReport_SetEnvelope_Output_Data_t* data, volatile TEffectState* effect); void FfbproSetCondition(USB_FFBReport_SetCondition_Output_Data_t* data, volatile TEffectState* effect); @@ -71,5 +116,28 @@ int FfbproSetEffect(USB_FFBReport_SetEffect_Output_Data_t *data, volatile TEffe void FfbproCreateNewEffect(USB_FFBReport_CreateNewEffect_Feature_Data_t* inData, volatile TEffectState* effect); uint8_t FfbproUsbToMidiEffectType(uint8_t usb_effect_type); +uint8_t FfbproEffectMemFull(uint8_t new_midi_type); + +#define FFP_MIDI_MODIFY_DURATION 0x40 +#define FFP_MIDI_MODIFY_TRIGGERBUTTON 0x44 +#define FFP_MIDI_MODIFY_DIRECTION 0x48 +#define FFP_MIDI_MODIFY_GAIN 0x4C +#define FFP_MIDI_MODIFY_SAMPLERATE 0x50 +#define FFP_MIDI_MODIFY_ATTACK 0x64 +#define FFP_MIDI_MODIFY_ATTACKTIME 0x5C +#define FFP_MIDI_MODIFY_MAGNITUDE 0x68 //byte 22 i.e. sustain +#define FFP_MIDI_MODIFY_FADETIME 0x60 +#define FFP_MIDI_MODIFY_FADE 0x6C +#define FFP_MIDI_MODIFY_FREQUENCY 0x70 +#define FFP_MIDI_MODIFY_PARAM1 0x74 +#define FFP_MIDI_MODIFY_PARAM2 0x78 +#define FFP_MIDI_MODIFY_COEFFAXIS0 0x48 //roll +#define FFP_MIDI_MODIFY_COEFFAXIS1 0x4C //pitch +#define FFP_MIDI_MODIFY_OFFSETAXIS0 0x50 //roll +#define FFP_MIDI_MODIFY_OFFSETAXIS1 0x54 //pitch + +#define FFP_MIDI_MODIFY_DEVICEGAIN 0x7C + +#define FFP_SAMPLERATE_DEFAULT 0x0064 //100Hz #endif // _FFB_PRO_ \ No newline at end of file diff --git a/ffb-wheel.c b/ffb-wheel.c index c614a70..1ffcbac 100644 --- a/ffb-wheel.c +++ b/ffb-wheel.c @@ -49,6 +49,11 @@ uint8_t FfbwheelUsbToMidiEffectType(uint8_t usb_effect_type) return usbToMidiEffectType[usb_effect_type]; } +uint8_t FfbwheelEffectMemFull(uint8_t new_midi_type) +{ + return 0; //Supported quantities of each effect not yet known +} + /** * Initialize wheel for FF. Releases spring effect. * @@ -74,27 +79,34 @@ void FfbwheelEnableInterrupts(void) FfbSendData(startupFfbWheelData_0, sizeof(startupFfbWheelData_0)); FfbSendData(startupFfbWheelData_1, sizeof(startupFfbWheelData_1)); - FfbwheelSetAutoCenter(0); + FfbwheelDeviceControl(USB_DCTRL_RESET); // Leave auto centre on WaitMs(100); } -void FfbwheelSetAutoCenter(uint8_t enable) -{ - const uint8_t ac_enable[] = { - 0xf3, 0x1d - }; - - const uint8_t ac_disable[] = { - 0xf1, 0x10, 0x40, 0x00, 0x7f, 0x00, - 0xf3, 0x6a - }; - - FfbSendData(ac_enable, sizeof(ac_enable)); +uint8_t FfbwheelDeviceControl(uint8_t usb_control) +{ // CHANGED FOR COMPATIBILITY - NOT TESTED FOR WHEEL + /* + USB_DCTRL_ACTUATORS_ENABLE 0x01 + USB_DCTRL_ACTUATORS_DISABLE 0x02 + USB_DCTRL_STOPALL 0x03 + USB_DCTRL_RESET 0x04 + USB_DCTRL_PAUSE 0x05 + USB_DCTRL_CONTINUE 0x06 + */ + uint8_t command[2] = {0xf3}; - if (!enable) { - FfbSendData(ac_disable, sizeof(ac_disable)); + if (usb_control == USB_DCTRL_RESET) { + command[1] = 0x1d; + } else if (usb_control == USB_DCTRL_STOPALL) { + command[1] = 0x6a; + } else { + return 0; //not supported } + + FfbSendData(command, sizeof(command)); + //Is a wait needed? + return 1; //supported command } const uint8_t* FfbwheelGetSysExHeader(uint8_t* hdr_len) @@ -140,7 +152,7 @@ void FfbwheelFreeEffect(uint8_t effectId) // modify operations --------------------------------------------------------- -static void FfbwheelSendModify(uint8_t effectId, uint8_t address, uint16_t value) +void FfbwheelSendModify(uint8_t effectId, uint8_t address, uint16_t value) { cmd_f1_t op; @@ -156,11 +168,20 @@ static void FfbwheelSendModify(uint8_t effectId, uint8_t address, uint16_t value FfbSendData(d, sizeof(op)); } -void FfbwheelModifyDuration(uint8_t effectId, uint16_t duration) +void FfbwheelModifyDuration(uint8_t effectState, uint16_t* midi_data_param, uint8_t effectId, uint16_t duration) { - FfbwheelSendModify(effectId, 0x00, duration); + //FfbwheelSendModify(effectId, 0x00, duration); + FfbSetParamMidi_14bit(effectState, midi_data_param, effectId, 0x00, duration); // CHANGED FOR COMPATIBILITY - NOT TESTED FOR WHEEL } +void FfbwheelModifyDeviceGain(uint8_t gain) +{ // TO IMPLEMENT: CHANGED FOR COMPATIBILITY - NOT TESTED FOR WHEEL + static const uint8_t gainCommand[] = {0xf1, 0x10, 0x40, 0x00, 0x7f, 0x00}; // only sends max gain for now + FfbSendData(gainCommand, sizeof(gainCommand)); + +} + + void FfbwheelSetEnvelope( USB_FFBReport_SetEnvelope_Output_Data_t* data, volatile TEffectState* effect) diff --git a/ffb-wheel.h b/ffb-wheel.h index 9335d3e..11ffeeb 100644 --- a/ffb-wheel.h +++ b/ffb-wheel.h @@ -98,14 +98,17 @@ typedef struct } cmd_f0_constant_force_t; void FfbwheelEnableInterrupts(void); +uint8_t FfbwheelDeviceControl(uint8_t usb_control); const uint8_t* FfbwheelGetSysExHeader(uint8_t* hdr_len); -void FfbwheelSetAutoCenter(uint8_t enable); void FfbwheelStartEffect(uint8_t effectId); void FfbwheelStopEffect(uint8_t effectId); void FfbwheelFreeEffect(uint8_t effectId); -void FfbwheelModifyDuration(uint8_t effectId, uint16_t duration); +void FfbwheelSendModify(uint8_t effectId, uint8_t address, uint16_t value); + +void FfbwheelModifyDuration(uint8_t effectState, uint16_t* midi_data_param, uint8_t effectId, uint16_t duration); +void FfbwheelModifyDeviceGain(uint8_t gain); void FfbwheelSetEnvelope(USB_FFBReport_SetEnvelope_Output_Data_t* data, volatile TEffectState* e); void FfbwheelSetCondition(USB_FFBReport_SetCondition_Output_Data_t* data, volatile TEffectState* e); @@ -116,5 +119,6 @@ int FfbwheelSetEffect(USB_FFBReport_SetEffect_Output_Data_t *data, volatile TEf void FfbwheelCreateNewEffect(USB_FFBReport_CreateNewEffect_Feature_Data_t* inData, volatile TEffectState* effect); uint8_t FfbwheelUsbToMidiEffectType(uint8_t usb_effect_type); +uint8_t FfbwheelEffectMemFull(uint8_t new_midi_type); #endif // _FFB_WHEEL_ \ No newline at end of file diff --git a/ffb.c b/ffb.c index 9c3d7dd..99991f6 100644 --- a/ffb.c +++ b/ffb.c @@ -5,6 +5,7 @@ Copyright 2012 Tero Loimuneva (tloimu [at] gmail [dot] com) Copyright 2013 Saku Kekkonen + Copyright 2023 Ed Wilkinson Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted @@ -46,8 +47,9 @@ const FFB_Driver ffb_drivers[2] = { .EnableInterrupts = FfbproEnableInterrupts, .GetSysExHeader = FfbproGetSysExHeader, - .SetAutoCenter = FfbproSetAutoCenter, + .DeviceControl = FfbproDeviceControl, .UsbToMidiEffectType = FfbproUsbToMidiEffectType, + .EffectMemFull = FfbproEffectMemFull, .StartEffect = FfbproStartEffect, .StopEffect = FfbproStopEffect, .FreeEffect = FfbproFreeEffect, @@ -59,12 +61,15 @@ const FFB_Driver ffb_drivers[2] = .SetRampForce = FfbproSetRampForce, .SetEffect = FfbproSetEffect, .ModifyDuration = FfbproModifyDuration, + .ModifyDeviceGain = FfbproModifyDeviceGain, + .SendModify = FfbproSendModify, }, { .EnableInterrupts = FfbwheelEnableInterrupts, .GetSysExHeader = FfbwheelGetSysExHeader, - .SetAutoCenter = FfbwheelSetAutoCenter, + .DeviceControl = FfbwheelDeviceControl, .UsbToMidiEffectType = FfbwheelUsbToMidiEffectType, + .EffectMemFull = FfbwheelEffectMemFull, .StartEffect = FfbwheelStartEffect, .StopEffect = FfbwheelStopEffect, .FreeEffect = FfbwheelFreeEffect, @@ -76,6 +81,8 @@ const FFB_Driver ffb_drivers[2] = .SetRampForce = FfbwheelSetRampForce, .SetEffect = FfbwheelSetEffect, .ModifyDuration = FfbwheelModifyDuration, + .ModifyDeviceGain = FfbwheelModifyDeviceGain, + .SendModify = FfbwheelSendModify, } }; @@ -172,6 +179,16 @@ void FreeAllEffects(void) // Utilities +uint8_t GetMidiEffectType(uint8_t id) +{ + if (id > MAX_EFFECTS || gEffectStates[id].state == MEffectState_Free) { + return 0xFF; //use this as null value since it can't be a valid value in MIDI + } else { + volatile TEffectState* effect = &gEffectStates[id]; + return ((midi_data_common_t*)effect->data)->waveForm; + } +} + void FfbSendSysEx(const uint8_t* midi_data, uint8_t len) { uint8_t hdr_len; @@ -190,13 +207,47 @@ void FfbSendSysEx(const uint8_t* midi_data, uint8_t len) FfbSendData(&mark, 1); } +uint8_t FfbSetParamMidi_14bit(uint8_t effectState, volatile uint16_t* midi_data_param, uint8_t effectId, uint8_t address, uint16_t value) + { // why does midi data need to be volatile? What else can change it?? Are the USB FFB messages not processed sequentially? + if (value == *midi_data_param) + return 0; + else + { + *midi_data_param = value; + if (effectState & MEffectState_SentToJoystick) + ffb->SendModify(effectId, address, value); + return 1; + } + } + +uint8_t FfbSetParamMidi_7bit(uint8_t effectState, volatile uint8_t* midi_data_param, uint8_t effectId, uint8_t address, uint8_t value) + { // why does midi data need to be volatile? What else can change it?? Are the USB FFB messages not processed sequentially? + if (value == *midi_data_param) + return 0; + else + { + *midi_data_param = value; + if (effectState & MEffectState_SentToJoystick) + ffb->SendModify(effectId, address, value); + return 1; + } + } + +uint16_t UsbUint16ToMidiUint14_Time(uint16_t inUsbValue) + { //Only use for Time conversion from ms. Includes /2 as MIDI duration is in units of 2ms and USB 1ms + if (inUsbValue == 0xFFFF) + return 0x0000; + + return (inUsbValue & 0x7F00) + ((inUsbValue & 0x00FF) >> 1); + } + uint16_t UsbUint16ToMidiUint14(uint16_t inUsbValue) { if (inUsbValue == 0xFFFF) return 0x0000; - return (inUsbValue & 0x7F00) + ((inUsbValue & 0x00FF) >> 1); // loss of the MSB-bit! - } + return ((inUsbValue << 1) & 0x7F00) + ((inUsbValue & 0x007F)); + } int16_t UsbInt8ToMidiInt14(int8_t inUsbValue) { @@ -212,12 +263,17 @@ int16_t UsbInt8ToMidiInt14(int8_t inUsbValue) return value; } -// Calculates the final value of the given when taking in given into account. -// Returns MIDI value (i.e. max 0..7f). -uint8_t CalcGain(uint8_t usbValue, uint8_t gain) +uint16_t UsbPeriodToFrequencyHz(uint16_t period) + { + //USB Period in ms to Frequency in Hz + return ((2000 / period) + 1) / 2; //Rounds to nearest Hz i.e. 1.51Hz rounds up to 2Hz + } + +// Calculates the final value of the given coefficient when taking in given into account. +int8_t CalcGainCoeff(int8_t usbValue, uint8_t gain) { int16_t v = usbValue; - return (((v * gain) / 256) >> 2 ) & 0x7f; + return ((v * gain) / 255); } // Lengths of each report type @@ -236,6 +292,7 @@ const uint16_t OutReportSize[] = { sizeof(USB_FFBReport_DeviceControl_Output_Data_t), // 12 sizeof(USB_FFBReport_DeviceGain_Output_Data_t), // 13 sizeof(USB_FFBReport_SetCustomForce_Output_Data_t), // 14 + sizeof(USB_FFBReport_CreateNewEffect_Feature_Data_t), // 15 SPOOFED ID }; void FfbHandle_EffectOperation(USB_FFBReport_EffectOperation_Output_Data_t *data); @@ -256,7 +313,9 @@ void FfbOnUsbData(uint8_t *data, uint16_t len) LogReport(PSTR("Usb =>"), OutReportSize, data, len); uint8_t effectId = data[1]; // effectBlockIndex is always the second byte. - + + + switch (data[0]) // reportID { case 1: @@ -300,6 +359,14 @@ void FfbOnUsbData(uint8_t *data, uint16_t len) case 14: FfbHandle_SetCustomForce((USB_FFBReport_SetCustomForce_Output_Data_t*) data); break; + #ifdef DEBUG_ENABLE_USB // only want to allow this behaviour when debugging since it should not be triggered otherwise + case 15: + {// This is a spoofed ID to allow CreateNewEffect to be triggered over USB virtual COM PORT, since it is a Feature Report not an Output Report + USB_FFBReport_PIDBlockLoad_Feature_Data_t pidBlockLoadData; // do nothing with this + FfbOnCreateNewEffect((USB_FFBReport_CreateNewEffect_Feature_Data_t*) data, &pidBlockLoadData); + break; + } + #endif // DEBUG_ENABLE_USB default: break; }; @@ -310,23 +377,22 @@ void FfbOnUsbData(uint8_t *data, uint16_t len) void FfbOnCreateNewEffect(USB_FFBReport_CreateNewEffect_Feature_Data_t* inData, USB_FFBReport_PIDBlockLoad_Feature_Data_t *outData) { outData->reportId = 6; - outData->effectBlockIndex = GetNextFreeEffect(); + uint8_t midi_effect_type = ffb->UsbToMidiEffectType(inData->effectType - 1); + if (ffb->EffectMemFull(midi_effect_type)) { + outData->effectBlockIndex = 0; + } else { + outData->effectBlockIndex = GetNextFreeEffect(); // can also return 0 if adapter full + } + if (outData->effectBlockIndex == 0) { outData->loadStatus = 2; // 1=Success,2=Full,3=Error } else { outData->loadStatus = 1; // 1=Success,2=Full,3=Error volatile TEffectState* effect = &gEffectStates[outData->effectBlockIndex]; - - effect->usb_duration = USB_DURATION_INFINITE; - effect->usb_fadeTime = USB_DURATION_INFINITE; - effect->usb_gain = 0xFF; - effect->usb_offset = 0; - effect->usb_attackLevel = 0xFF; - effect->usb_fadeLevel = 0xFF; - - ((midi_data_common_t*)effect->data)->waveForm = ffb->UsbToMidiEffectType(inData->effectType - 1); + + ((midi_data_common_t*)effect->data)->waveForm = midi_effect_type; ffb->CreateNewEffect(inData, effect); } @@ -374,18 +440,18 @@ void FfbHandle_SetEffect(USB_FFBReport_SetEffect_Output_Data_t *data) } FlushDebugBuffer(); } - + + uint16_t midi_duration; + midi_data_common_t* midi_data = (midi_data_common_t*)effect->data; if (data->duration == USB_DURATION_INFINITE) { - midi_data->duration = MIDI_DURATION_INFINITE; + midi_duration = MIDI_DURATION_INFINITE; } else { - midi_data->duration = UsbUint16ToMidiUint14(data->duration); // MIDI unit is 2ms + midi_duration = UsbUint16ToMidiUint14_Time(data->duration); // MIDI unit is 2ms } - effect->usb_duration = data->duration; // store for later calculation of - - if (effect->state & MEffectState_SentToJoystick) - ffb->ModifyDuration(data->effectBlockIndex, midi_data->duration); + + ffb->ModifyDuration(effect->state, &(midi_data->duration), data->effectBlockIndex, midi_duration); uint8_t midi_data_len = ffb->SetEffect((USB_FFBReport_SetEffect_Output_Data_t *) data, effect); @@ -408,7 +474,7 @@ void FfbOnPIDPool(USB_FFBReport_PIDPool_Feature_Data_t *data) data->reportId = 7; data->ramPoolSize = 0xFFFF; - data->maxSimultaneousEffects = 0x0A; // FFP supports playing up to 10 simultaneous effects + data->maxSimultaneousEffects = 0x10; // FFP supports playing up to 16 simultaneous effects data->memoryManagement = 3; } @@ -506,11 +572,13 @@ void FfbHandle_BlockFree(USB_FFBReport_BlockFree_Output_Data_t *data) } void FfbHandle_DeviceControl(USB_FFBReport_DeviceControl_Output_Data_t *data) - { +{ // LogTextP(PSTR("Device Control: ")); uint8_t control = data->control; // 1=Enable Actuators, 2=Disable Actuators, 3=Stop All Effects, 4=Reset, 5=Pause, 6=Continue + + uint8_t success; // PID State Report: // uint8_t reportId; // =2 @@ -518,69 +586,77 @@ void FfbHandle_DeviceControl(USB_FFBReport_DeviceControl_Output_Data_t *data) // uint8_t effectBlockIndex; // Bit7=Effect Playing, Bit0..7=EffectId (1..40) pidState.reportId = 2; - pidState.status |= 1 << 2; - pidState.status |= 1 << 4; + pidState.status |= 1 << 2; //Safety Switch: device usable + pidState.status |= 1 << 4; //Actuator Power: on pidState.effectBlockIndex = 0; - if (control == 0x01) - { - LogTextLf("Disable Actuators"); - pidState.status = (pidState.status & 0xFE); - } - else if (control == 0x02) - { - LogTextLf("Enable Actuators"); - pidState.status |= 1 << 2; - } - else if (control == 0x03) - { - // Stop all effects (e.g. FFB-application to foreground) - LogTextLf("Stop All Effects"); + success = ffb->DeviceControl(control); - // Disable auto-center spring and stop all effects -// ???? The below would take too long? - ffb->SetAutoCenter(0); - pidState.effectBlockIndex = 0; - } - else if (control == 0x04) - { - LogTextLf("Reset"); - // Reset (e.g. FFB-application out of focus) - // Enable auto-center spring and stop all effects - ffb->SetAutoCenter(1); - WaitMs(75); - FreeAllEffects(); - } - else if (control == 0x05) - { - LogTextLf("Pause"); - } - else if (control == 0x06) - { - LogTextLf("Continue"); - } - else if (control & (0xFF-0x3F)) - { - LogTextP(PSTR("Other ")); - LogBinaryLf(&data->control, 1); - } + switch (control) + { + case USB_DCTRL_ACTUATORS_ENABLE: + LogTextLf("Enable Actuators"); + if (success) + pidState.status |= (1 << 1); + break; + case USB_DCTRL_ACTUATORS_DISABLE: + LogTextLf("Disable Actuators"); + if (success) + pidState.status &= ~(1 << 1); + break; + case USB_DCTRL_STOPALL: + LogTextLf("Stop All Effects"); + if (success) + pidState.effectBlockIndex = 0; + //need to update all effect states to not playing? Maybe not needed since adapter doesn't track when effects finish anyway + break; + case USB_DCTRL_RESET: + LogTextLf("Reset"); + // Reset (e.g. FFB-application out of focus) + //Enables auto centre, continues, enables actuators, stop and free all effects, resets device gain (for FFP at least) + if (success) + { + WaitMs(75); + FreeAllEffects(); + pidState.status |= (1 << 1); //actuators + pidState.status &= ~1; //continue + } + break; + case USB_DCTRL_PAUSE: + LogTextLf("Pause"); + if (success) + pidState.status |= 1; + break; + case USB_DCTRL_CONTINUE: + LogTextLf("Continue"); + if (success) + pidState.status &= ~1; + break; + default: + if (control & (0xFF-0x3F)) + { + LogTextP(PSTR("Other ")); + LogBinaryLf(&data->control, 1); + } + } + // Send response - } +} -void -FfbHandle_DeviceGain(USB_FFBReport_DeviceGain_Output_Data_t *data) +void FfbHandle_DeviceGain(USB_FFBReport_DeviceGain_Output_Data_t *data) { LogTextP(PSTR("Device Gain: ")); LogBinaryLf(&data->gain, 1); + + ffb->ModifyDeviceGain(data->gain); } -void -FfbHandle_SetCustomForce(USB_FFBReport_SetCustomForce_Output_Data_t *data) +void FfbHandle_SetCustomForce(USB_FFBReport_SetCustomForce_Output_Data_t *data) { LogTextLf("Set Custom Force"); // LogBinary(&data, sizeof(USB_FFBReport_SetCustomForce_Output_Data_t)); @@ -759,20 +835,6 @@ void FfbSendDisable() { } -/* -typedef struct { - uint8_t state; // see constants - uint16_t usb_duration, usb_fadeTime; // used to calculate fadeTime to MIDI, since in USB it is given as time difference from the end while in MIDI it is given as time from start - // These are used to calculate effects of USB gain to MIDI data - uint8_t usb_gain, usb_offset, usb_attackLevel, usb_fadeLevel; - uint8_t usb_magnitude; - FFP_MIDI_Effect_Basic data; // For FFP, this is enough for all types of effects - cast for other effect types when necessary - } TEffectState; - -const uint8_t MEffectState_Allocated = 0x01; -const uint8_t MEffectState_Playing = 0x02; -const uint8_t MEffectState_SentToJoystick = 0x04; -*/ uint8_t FfbDebugListEffects(uint8_t *index) { @@ -799,7 +861,7 @@ uint8_t FfbDebugListEffects(uint8_t *index) LogTextP(PSTR(" (Disabled)\n")); else LogTextP(PSTR(" (Enabled)\n")); - +/* //These variables don't now exist for all effects - could be accessed for some effects share_data if (e->state) { LogTextP(PSTR(" duration=")); @@ -809,7 +871,7 @@ uint8_t FfbDebugListEffects(uint8_t *index) LogTextP(PSTR("\n gain=")); LogBinary(&e->usb_gain, 1); } - +*/ *index = *index + 1; return 1; diff --git a/ffb.h b/ffb.h index 1d4a684..afbf035 100644 --- a/ffb.h +++ b/ffb.h @@ -6,6 +6,7 @@ with some room for additional extra controls. Copyright 2012 Tero Loimuneva (tloimu [at] gmail [dot] com) + Copyright 2023 Ed Wilkinson MIT License. Permission to use, copy, modify, distribute, and sell this @@ -39,7 +40,10 @@ */ // Maximum number of parallel effects in memory -#define MAX_EFFECTS 20 +#define MAX_EFFECTS 19 //Actually Max Effect ID , but effects IDs start at 0x02 so 1 less than this +//FFP can support 10 waveforms + 2 of each conditional = 18 not including other unsupported effect types +//Wheel limits? + // ---- Input @@ -148,7 +152,7 @@ typedef struct typedef struct { // FFB: Device Control Output Report uint8_t reportId; // =12 - uint8_t control; // 1=Enable Actuators, 2=Disable Actuators, 4=Stop All Effects, 8=Reset, 16=Pause, 32=Continue + uint8_t control; // 1=Enable Actuators, 2=Disable Actuators, 3=Stop All Effects, 4=Reset, 5=Pause, 6=Continue } USB_FFBReport_DeviceControl_Output_Data_t; typedef struct @@ -246,10 +250,15 @@ typedef struct extern volatile TDisabledEffectTypes gDisabledEffects; +uint8_t GetMidiEffectType(uint8_t id); void FfbSendSysEx(const uint8_t* midi_data, uint8_t len); +uint8_t FfbSetParamMidi_14bit(uint8_t effectState, volatile uint16_t *midi_data_param, uint8_t effectId, uint8_t address, uint16_t value); +uint8_t FfbSetParamMidi_7bit(uint8_t effectState, volatile uint8_t *midi_data_param, uint8_t effectId, uint8_t address, uint8_t value); +uint16_t UsbUint16ToMidiUint14_Time(uint16_t inUsbValue); uint16_t UsbUint16ToMidiUint14(uint16_t inUsbValue); int16_t UsbInt8ToMidiInt14(int8_t inUsbValue); -uint8_t CalcGain(uint8_t usbValue, uint8_t gain); +uint16_t UsbPeriodToFrequencyHz(uint16_t period); +int8_t CalcGainCoeff(int8_t usbValue, uint8_t gain); void FfbEnableSprings(uint8_t inEnable); void FfbEnableConstants(uint8_t inEnable); @@ -263,8 +272,12 @@ void FfbEnableEffectId(uint8_t inId, uint8_t inEnable); #define MEffectState_Playing 0x02 #define MEffectState_SentToJoystick 0x04 -#define USB_DURATION_INFINITE 0x7FFF -#define MIDI_DURATION_INFINITE 0 +#define USB_DURATION_INFINITE 0xFFFF +#define MIDI_DURATION_INFINITE 0x0000 + +#define USB_SAMPLEPERIOD_DEFAULT 0x0000 + +#define USB_TRIGGERBUTTON_NULL 0xFF #define USB_EFFECT_CONSTANT 0x01 #define USB_EFFECT_RAMP 0x02 @@ -279,7 +292,15 @@ void FfbEnableEffectId(uint8_t inId, uint8_t inEnable); #define USB_EFFECT_FRICTION 0x0B #define USB_EFFECT_CUSTOM 0x0C +#define USB_DCTRL_ACTUATORS_ENABLE 0x01 +#define USB_DCTRL_ACTUATORS_DISABLE 0x02 +#define USB_DCTRL_STOPALL 0x03 +#define USB_DCTRL_RESET 0x04 +#define USB_DCTRL_PAUSE 0x05 +#define USB_DCTRL_CONTINUE 0x06 + #define MAX_MIDI_MSG_LEN 27 /* enough to hold longest midi message data part, FFP_MIDI_Effect_Basic */ +#define MAX_SHARE_DATA 12 /* enough bytes to hold all data that must be shared between Output reports for any effect type*/ /* start of midi data common for both pro and wheel protocols */ typedef struct { @@ -291,10 +312,7 @@ typedef struct { typedef struct { uint8_t state; // see constants - uint16_t usb_duration, usb_fadeTime; // used to calculate fadeTime to MIDI, since in USB it is given as time difference from the end while in MIDI it is given as time from start - // These are used to calculate effects of USB gain to MIDI data - uint8_t usb_gain, usb_offset, usb_attackLevel, usb_fadeLevel; - uint8_t usb_magnitude; + uint8_t share_data[MAX_SHARE_DATA]; // All data to be shared between Output reports for calculating MIDI parameters coupled to multiple USB parameters volatile uint8_t data[MAX_MIDI_MSG_LEN]; } TEffectState; @@ -302,15 +320,18 @@ typedef struct { void (*EnableInterrupts)(void); const uint8_t* (*GetSysExHeader)(uint8_t* hdr_len); - void (*SetAutoCenter)(uint8_t enable); + uint8_t (*DeviceControl)(uint8_t usb_control); uint8_t (*UsbToMidiEffectType)(uint8_t usb_effect_type); + uint8_t (*EffectMemFull)(uint8_t new_midi_type); void (*StartEffect)(uint8_t eid); void (*StopEffect)(uint8_t eid); void (*FreeEffect)(uint8_t eid); - void (*ModifyDuration)(uint8_t effectId, uint16_t duration); - + void (*SendModify)(uint8_t effectId, uint8_t address, uint16_t value); + void (*ModifyDuration)(uint8_t effectState, uint16_t* midi_data_param, uint8_t effectId, uint16_t duration); + void (*ModifyDeviceGain)(uint8_t gain); + void (*CreateNewEffect)(USB_FFBReport_CreateNewEffect_Feature_Data_t* inData, volatile TEffectState* effect); void (*SetEnvelope)(USB_FFBReport_SetEnvelope_Output_Data_t* data, volatile TEffectState* effect); void (*SetCondition)(USB_FFBReport_SetCondition_Output_Data_t* data, volatile TEffectState* effect); @@ -320,4 +341,4 @@ typedef struct int (*SetEffect)(USB_FFBReport_SetEffect_Output_Data_t* data, volatile TEffectState* effect); } FFB_Driver; -#endif // _FFB_PRO_ \ No newline at end of file +#endif // _FFB_ \ No newline at end of file