diff -r aa7dc7faf1bb -r 358a601bfe81 DJMFix.cpp --- a/DJMFix.cpp Fri May 09 23:17:36 2025 +0200 +++ b/DJMFix.cpp Sun May 11 00:30:03 2025 +0200 @@ -23,8 +23,10 @@ #include #include #include +#include #include "DJMFix.h" +#include "MessageCodec.h" namespace djmfix { @@ -36,6 +38,7 @@ private: MidiSender* midiSender; djmfix::logging::Logger* logger; + MessageCodec codec; const int keepAliveInterval = 200; int keepAliveCounter = 0; std::thread keepAliveThread; @@ -43,14 +46,31 @@ std::atomic running{false}; std::atomic stopped{false}; std::atomic sendKeepAlive{false}; + /** + * Device (V10) may send multiple greeting messages. + * It works even if we respond multiple times. But one response is enough. + */ + std::atomic greetingReceived{false}; + + Bytes seed0 = {0x68, 0x01, 0x31, 0xFB}; + Bytes seed1 = {0x29, 0x00, 0x00, 0x00, 0x23, 0x48, 0x00, 0x00}; Bytes seed2; + Bytes seed3; + + Bytes name1 = {0x50, 0x69, 0x6f, 0x6e, 0x65, 0x65, 0x72, 0x44, 0x4a}; + Bytes name2 = {0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x62, 0x6f, 0x78}; + + Bytes hash1; + Bytes hash2; + + uint8_t version = 0x17; void run() { while (!stopped) { logger->log(L::FINE, "DJMFixImpl::run()"); if (sendKeepAlive) send({ 0xf0, 0x00, 0x40, 0x05, - 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x00, version, 0x00, 0x50, 0x01, 0xf7 }); std::this_thread::sleep_for(chro::milliseconds(keepAliveInterval)); @@ -62,6 +82,11 @@ } } + void send(const Message& msg) { + logger->log(L::FINE, "" + msg.toString()); + send(codec.encode(msg)); + } + void send(const MidiMessage& midiMessage) { std::lock_guard lock(midiMutex); midiSender->send(midiMessage); @@ -150,77 +175,103 @@ if (running) stop(); } - void setMidiSender(MidiSender* midiSender) { + void setDeviceName(std::string name) override { + logger->log(L::FINE, "DJMFixImpl::setDeviceName(" + name + ")"); + + std::regex djm250pattern("Pioneer DJ Corporation DJM-250MK2.*"); + std::regex djm450pattern(".*450.*"); // TODO: correct pattern + + if (std::regex_match(name, djm250pattern)) { + // DJM-250MK2: + version = 0x17; + seed3 = { + 0x59, 0xb5, 0x4b, 0xfe, 0xe4, + 0x4a, 0x5a, 0xc8, 0xe4, 0xc5 + }; + logger->log(L::FINE, "Switched to DJM-250MK2 mode"); + } else if (std::regex_match(name, djm450pattern)) { + // DJM-450: + // DJM-450 - not tested yet: + version = 0x13; + seed0 = {0x8c, 0x5b, 0x3f, 0x5d}; + seed3 = { + 0x08, 0xef, 0x3f, 0x2f, 0x1e, + 0x7a, 0x90, 0x17, 0xf6, 0xaf + }; + logger->log(L::FINE, "Switched to DJM-450 mode"); + } else { + // DJM-V10: + version = 0x34; + seed3 = { + 0x70, 0x01, 0x4d, 0x05, 0xbe, + 0xf2, 0xe4, 0xde, 0x60, 0xd6 + }; + logger->log(L::FINE, "Switched to DJM-V10 mode"); + } + } + + void setMidiSender(MidiSender* midiSender) override { logger->log(L::FINER, "DJMFixImpl::setMidiSender()"); this->midiSender = midiSender; } virtual void receive(const MidiMessage& msg) override { + // TODO: remove try/catch - there should be no unknown messages + try { + receive0(msg); + } catch (const std::exception& e) { + logger->log(L::SEVERE, + std::string("Message receiving failed: ") + e.what()); + } + } + + virtual void receive0(const MidiMessage& msg) { logger->log(L::FINE, "Received a message: " "size = " + std::to_string(msg.size()) + " " "data = " + toString(msg)); std::lock_guard lock(midiMutex); + Message msgIn = codec.decode(msg); + logger->log(L::FINE, "" + msgIn.toString()); - if (msg.size() == 12 && msg[9] == 0x11) { + if (msgIn.type == MessageType::D11_GREETING && !greetingReceived) { 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 + Message msgOut(MessageType::H12_SEED1, version,{ + {FieldType::F01, name1}, + {FieldType::F02, name2}, + {FieldType::F03, denormalize(seed1)} }); + send(msgOut); + greetingReceived = true; logger->log(L::INFO, "Sent message with seed1."); - } 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); + } else if (msgIn.type == MessageType::D13_HASH1_SEED2) { + std::vector hash1F = msgIn.findFields(FieldType::F04); + std::vector seed2F = msgIn.findFields(FieldType::F03); + + if (hash1F.empty()) throw std::logic_error("hash1 not found"); + if (seed2F.empty()) throw std::logic_error("seed2 not found"); + + hash1 = normalize(hash1F[0].data); + seed2 = normalize(seed2F[0].data); + 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)))); 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 - }))); + hash2 = toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2)))); + + Message msgOut(MessageType::H14_HASH2, version,{ + {FieldType::F01, name1}, + {FieldType::F02, name2}, + {FieldType::F04, denormalize(hash2)}, + {FieldType::F05, denormalize(seed3)} + }); + send(msgOut); logger->log(L::INFO, "Sent message with hash2."); } else { std::stringstream logMessage; @@ -235,11 +286,13 @@ logger->log(L::SEVERE, logMessage.str()); // TODO: graceful death } - } else if (msg.size() == 12 && msg[9] == 0x15) { + } else if (msgIn.type == MessageType::D15_CONFIRMATION) { sendKeepAlive = true; logger->log(L::INFO, "Received acknowledgment message. " "Started sending keep-alive messages. " "LINE/PHONO channels should work now."); + } else { + logger->log(L::SEVERE, "Received unexpected message type."); } } @@ -249,12 +302,18 @@ 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, 0x00, 0x00, version, 0x00, 0x50, 0x01, 0xf7 }); + + // TODO: check whether this second message is neccessary for V10: + send({ + 0xf0, 0x00, 0x40, 0x05, + 0x00, 0x00, 0x00, version, + 0x00, 0x03, 0x01, 0xf7 + }); logger->log(L::INFO, "Sent greeting message."); keepAliveThread = std::thread(&DJMFixImpl::run, this);