diff -r 1c74985d4c4e -r 63154f9d24a2 DJMFix.cpp --- a/DJMFix.cpp Tue Apr 15 22:44:31 2025 +0200 +++ b/DJMFix.cpp Tue Apr 15 22:45:25 2025 +0200 @@ -1,6 +1,6 @@ /** * DJM-Fix - * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info) + * Copyright © 2025 František Kučera (Frantovo.cz, GlobalCode.info) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,6 +30,7 @@ using L = djmfix::logging::Level; using Bytes = std::vector; +namespace chro = std::chrono; class DJMFixImpl : public DJMFix { private: @@ -47,10 +48,17 @@ void run() { while (!stopped) { logger->log(L::FINE, "DJMFixImpl::run()"); - if (sendKeepAlive) send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); - std::this_thread::sleep_for(std::chrono::milliseconds(keepAliveInterval)); + if (sendKeepAlive) send({ + 0xf0, 0x00, 0x40, 0x05, + 0x00, 0x00, 0x00, 0x17, + 0x00, 0x50, 0x01, 0xf7 + }); + std::this_thread::sleep_for(chro::milliseconds(keepAliveInterval)); keepAliveCounter++; - if (keepAliveCounter % (60 * 1000 / keepAliveInterval) == 0) logger->log(L::INFO, "Still sending periodic keep-alive messages (each " + std::to_string(keepAliveInterval) + " ms)."); + if (keepAliveCounter % (60 * 1000 / keepAliveInterval) == 0) + logger->log(L::INFO, + "Still sending periodic keep-alive messages " + "(each " + std::to_string(keepAliveInterval) + " ms)."); } } @@ -61,15 +69,18 @@ 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; + 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."); + 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)); + 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; } @@ -105,7 +116,11 @@ return true; } - template std::vector concat(const std::vector& a, const std::vector& b, const std::vector& c = {}) { + 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]); @@ -114,8 +129,10 @@ return result; } - template std::vector xOR(const std::vector& a, const std::vector& b) { - if (a.size() != b.size()) throw std::invalid_argument("Both must be the same length when doing XOR."); + template + std::vector xOR(const std::vector& a, const std::vector& b) { + if (a.size() != b.size()) throw std::invalid_argument( + "Both must be the same length when doing XOR."); std::vector result; result.reserve(a.size()); for (size_t i = 0; i < a.size(); i++) result.push_back(a[i] ^ b[i]); @@ -124,7 +141,8 @@ public: - DJMFixImpl(djmfix::logging::Logger* logger) : logger(logger ? logger : djmfix::logging::blackhole()) { + DJMFixImpl(djmfix::logging::Logger* logger) + : logger(logger ? logger : djmfix::logging::blackhole()) { } virtual ~DJMFixImpl() override { @@ -137,37 +155,78 @@ this->midiSender = midiSender; } - virtual void receive(const MidiMessage& midiMessage) override { - logger->log(L::FINE, "Received a message: size = " + std::to_string(midiMessage.size()) + " data = " + toString(midiMessage)); + virtual void receive(const MidiMessage& msg) override { + logger->log(L::FINE, "Received a message: " + "size = " + std::to_string(msg.size()) + " " + "data = " + toString(msg)); std::lock_guard lock(midiMutex); - if (midiMessage.size() == 12 && midiMessage[9] == 0x11) { + if (msg.size() == 12 && msg[9] == 0x11) { logger->log(L::INFO, "Received greeting message."); - 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, 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 + }); logger->log(L::INFO, "Sent message with seed1."); - } else if (midiMessage.size() == 54 && midiMessage[9] == 0x13 && midiMessage[33] == 0x04 && midiMessage[43] == 0x03) { - Bytes hash1(midiMessage.begin() + 35, midiMessage.begin() + 35 + 8); - seed2 = Bytes(midiMessage.begin() + 45, midiMessage.begin() + 45 + 8); + } else if (msg.size() == 54 + && msg[9] == 0x13 + && msg[33] == 0x04 + && msg[43] == 0x03) // + { + Bytes hash1(msg.begin() + 35, msg.begin() + 35 + 8); + seed2 = Bytes(msg.begin() + 45, msg.begin() + 45 + 8); hash1 = normalize(hash1); seed2 = normalize(seed2); - logger->log(L::INFO, "Received message with hash1 = " + toString(hash1) + " and seed2 = " + toString(seed2)); + logger->log(L::INFO, "Received message with " + "hash1 = " + toString(hash1) + " and " + "seed2 = " + toString(seed2)); 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)))); + Bytes hash1check = + toBytes(fnv32hash(concat(seed1, xOR(seed0, seed2)))); if (equals(hash1, hash1check)) { logger->log(L::INFO, "Verification of hash1 was successful."); - Bytes hash2 = toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2)))); - send(concat({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x14, 0x38, 0x01, 0x0b, 0x50, 0x69, 0x6f, 0x6e, 0x65, 0x65, 0x72, 0x44, 0x4a, 0x02, 0x0b, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x62, 0x6f, 0x78, 0x04, 0x0a}, concat(denormalize(hash2),{0x05, 0x16, 0x05, 0x09, 0x0b, 0x05, 0x04, 0x0b, 0x0f, 0x0e, 0x0e, 0x04, 0x04, 0x0a, 0x05, 0x0a, 0x0c, 0x08, 0x0e, 0x04, 0x0c, 0x05, 0xf7}))); + Bytes hash2 = + toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2)))); + send(concat({ + 0xf0, 0x00, 0x40, 0x05, + 0x00, 0x00, 0x00, 0x17, + 0x00, 0x14, 0x38, 0x01, + 0x0b, 0x50, 0x69, 0x6f, + 0x6e, 0x65, 0x65, 0x72, + 0x44, 0x4a, 0x02, 0x0b, + 0x72, 0x65, 0x6b, 0x6f, + 0x72, 0x64, 0x62, 0x6f, + 0x78, 0x04, 0x0a + }, concat(denormalize(hash2),{ + 0x05, 0x16, 0x05, 0x09, + 0x0b, 0x05, 0x04, 0x0b, + 0x0f, 0x0e, 0x0e, 0x04, + 0x04, 0x0a, 0x05, 0x0a, + 0x0c, 0x08, 0x0e, 0x04, + 0x0c, 0x05, 0xf7 + }))); logger->log(L::INFO, "Sent message with hash2."); } else { std::stringstream logMessage; logMessage << "Verification of hash1 failed: " - << " midiMessage = " << toString(midiMessage) + << " midiMessage = " << toString(msg) << " seed0 = " << toString(seed0) << " seed1 = " << toString(seed1) << " seed2 = " << toString(seed2) @@ -176,19 +235,26 @@ logger->log(L::SEVERE, logMessage.str()); // TODO: graceful death } - } else if (midiMessage.size() == 12 && midiMessage[9] == 0x15) { + } else if (msg.size() == 12 && msg[9] == 0x15) { sendKeepAlive = true; - logger->log(L::INFO, "Received acknowledgment message. Started sending keep-alive messages. LINE/PHONO channels should work now."); + logger->log(L::INFO, "Received acknowledgment message. " + "Started sending keep-alive messages. " + "LINE/PHONO channels should work now."); } } void start() override { logger->log(L::FINE, "DJMFixImpl::start()"); - if (midiSender == nullptr) throw std::logic_error("Need a midiSender when starting DJMFix"); + if (midiSender == nullptr) + throw std::logic_error("Need a midiSender when starting DJMFix"); // TODO: methods for parsing and constructing messages from parts (TLV) - send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); + send({ + 0xf0, 0x00, 0x40, 0x05, + 0x00, 0x00, 0x00, 0x17, + 0x00, 0x50, 0x01, 0xf7 + }); logger->log(L::INFO, "Sent greeting message."); keepAliveThread = std::thread(&DJMFixImpl::run, this);