diff -r ef8f4023e32e -r bddcf2bf29f2 DJMFix.cpp --- a/DJMFix.cpp Sat Dec 19 17:33:16 2020 +0100 +++ b/DJMFix.cpp Sat Dec 19 23:59:39 2020 +0100 @@ -17,18 +17,23 @@ #include #include #include +#include #include #include #include +#include #include "DJMFix.h" namespace djmfix { +using Bytes = std::vector; + class DJMFixImpl : public DJMFix { private: MidiSender* midiSender; std::thread keepAliveThread; + std::recursive_mutex midiMutex; std::atomic running{false}; std::atomic stopped{false}; @@ -40,13 +45,64 @@ } } - // TODO: remove - std::string toString(const MidiMessage& midiMessage) { + void send(const MidiMessage& midiMessage) { + std::lock_guard lock(midiMutex); + midiSender->send(midiMessage); + } + + std::string toString(const Bytes& midiMessage) { std::stringstream result; for (uint8_t b : midiMessage) result << std::hex << std::setw(2) << std::setfill('0') << (int) b; return result.str(); } + Bytes normalize(const Bytes& data) { + if (data.size() % 2) throw std::invalid_argument("data before normalization must have even number of bytes"); + Bytes result; + result.reserve(data.size() / 2); + for (size_t i = 0; i < data.size() / 2; i++) result.push_back((data[i * 2] & 0x0F) << 4 | (data[i * 2 + 1] & 0x0F)); + return result; + } + + uint32_t fnv32hash(const Bytes& buff) { + uint32_t hash = 0x811c9dc5; + for (uint8_t b : buff) hash = ((b^hash) * 0x1000193); + return hash; + } + + Bytes toBytes(const uint32_t value) { + Bytes result; + result.reserve(4); + result.push_back(value >> 24); + result.push_back(value >> 16); + result.push_back(value >> 8); + result.push_back(value >> 0); + return result; + } + + bool equals(Bytes a, Bytes b) { + if (a.size() != b.size()) return false; + for (size_t i = 0; i < a.size(); i++) if (a[i] != b[i]) return false; + return true; + } + + template std::vector concat(const std::vector& a, const std::vector& b, const std::vector& c = {}) { + std::vector result; + result.reserve(a.size() + b.size() + c.size()); + for (size_t i = 0; i < a.size(); i++) result.push_back(a[i]); + for (size_t i = 0; i < b.size(); i++) result.push_back(b[i]); + for (size_t i = 0; i < c.size(); i++) result.push_back(c[i]); + return result; + } + + template std::vector xOR(const std::vector& a, const std::vector& b) { + if (a.size() != b.size()) throw std::invalid_argument("xor: both must be the same length"); + std::vector result; + result.reserve(a.size()); + for (size_t i = 0; i < a.size(); i++) result.push_back(a[i] ^ b[i]); + return result; + } + public: virtual ~DJMFixImpl() override { @@ -59,11 +115,27 @@ this->midiSender = midiSender; } - virtual void receive(MidiMessage midiMessage) override { + virtual void receive(const MidiMessage& midiMessage) override { std::cerr << "DJMFixImpl::receive(): size = " << midiMessage.size() << " data = " << toString(midiMessage) << std::endl; // TODO: do not mess STDIO + std::lock_guard lock(midiMutex); + + if (midiMessage.size() == 54 && midiMessage[9] == 0x13 && midiMessage[33] == 0x04 && midiMessage[43] == 0x03) { + Bytes hash1(midiMessage.begin() + 35, midiMessage.begin() + 35 + 8); + Bytes seed2(midiMessage.begin() + 45, midiMessage.begin() + 45 + 8); + hash1 = normalize(hash1); + seed2 = normalize(seed2); + std::cerr << "DJMFixImpl::receive(): got message with hash1 = " << toString(hash1) << " and seed2 = " << toString(seed2) << std::endl; // TODO: do not mess STDIO - if (midiMessage.size() == 54 && midiMessage[9] == 0x13) { - std::cerr << "DJMFixImpl::receive(): got message with HashA and SeedE" << std::endl; // TODO: do not mess STDIO + Bytes seed0 = {0x68, 0x01, 0x31, 0xFB}; + Bytes seed1 = {0x29, 0x00, 0x00, 0x00, 0x23, 0x48, 0x00, 0x00}; + + Bytes hash1check = toBytes(fnv32hash(concat(seed1, xOR(seed0, seed2)))); + + if (equals(hash1, hash1check)) { + std::cerr << "DJMFixImpl::receive(): hash1 verification: OK" << std::endl; + } else { + std::cerr << "DJMFixImpl::receive(): hash1 verification: ERROR: check = " << toString(hash1check) << std::endl; + } } } @@ -73,9 +145,9 @@ if (midiSender == nullptr) throw std::logic_error("need a midiSender when starting DJMFix"); // TODO: methods for parsing and constructing messages from parts (TLV) - midiSender->send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - midiSender->send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x12, 0x2a, 0x01, 0x0b, 0x50, 0x69, 0x6f, 0x6e, 0x65, 0x65, 0x72, 0x44, 0x4a, 0x02, 0x0b, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x62, 0x6f, 0x78, 0x03, 0x12, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0xf7}); + send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); + std::this_thread::sleep_for(std::chrono::milliseconds(30)); // TODO: wait until we got the response + send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x12, 0x2a, 0x01, 0x0b, 0x50, 0x69, 0x6f, 0x6e, 0x65, 0x65, 0x72, 0x44, 0x4a, 0x02, 0x0b, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x62, 0x6f, 0x78, 0x03, 0x12, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0xf7}); keepAliveThread = std::thread(&DJMFixImpl::run, this); running = true;