1.1 --- a/DJMFix.cpp Fri May 09 23:17:36 2025 +0200
1.2 +++ b/DJMFix.cpp Sun May 11 00:30:03 2025 +0200
1.3 @@ -23,8 +23,10 @@
1.4 #include <chrono>
1.5 #include <stdexcept>
1.6 #include <vector>
1.7 +#include <regex>
1.8
1.9 #include "DJMFix.h"
1.10 +#include "MessageCodec.h"
1.11
1.12 namespace djmfix {
1.13
1.14 @@ -36,6 +38,7 @@
1.15 private:
1.16 MidiSender* midiSender;
1.17 djmfix::logging::Logger* logger;
1.18 + MessageCodec codec;
1.19 const int keepAliveInterval = 200;
1.20 int keepAliveCounter = 0;
1.21 std::thread keepAliveThread;
1.22 @@ -43,14 +46,31 @@
1.23 std::atomic<bool> running{false};
1.24 std::atomic<bool> stopped{false};
1.25 std::atomic<bool> sendKeepAlive{false};
1.26 + /**
1.27 + * Device (V10) may send multiple greeting messages.
1.28 + * It works even if we respond multiple times. But one response is enough.
1.29 + */
1.30 + std::atomic<bool> greetingReceived{false};
1.31 +
1.32 + Bytes seed0 = {0x68, 0x01, 0x31, 0xFB};
1.33 + Bytes seed1 = {0x29, 0x00, 0x00, 0x00, 0x23, 0x48, 0x00, 0x00};
1.34 Bytes seed2;
1.35 + Bytes seed3;
1.36 +
1.37 + Bytes name1 = {0x50, 0x69, 0x6f, 0x6e, 0x65, 0x65, 0x72, 0x44, 0x4a};
1.38 + Bytes name2 = {0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x62, 0x6f, 0x78};
1.39 +
1.40 + Bytes hash1;
1.41 + Bytes hash2;
1.42 +
1.43 + uint8_t version = 0x17;
1.44
1.45 void run() {
1.46 while (!stopped) {
1.47 logger->log(L::FINE, "DJMFixImpl::run()");
1.48 if (sendKeepAlive) send({
1.49 0xf0, 0x00, 0x40, 0x05,
1.50 - 0x00, 0x00, 0x00, 0x17,
1.51 + 0x00, 0x00, 0x00, version,
1.52 0x00, 0x50, 0x01, 0xf7
1.53 });
1.54 std::this_thread::sleep_for(chro::milliseconds(keepAliveInterval));
1.55 @@ -62,6 +82,11 @@
1.56 }
1.57 }
1.58
1.59 + void send(const Message& msg) {
1.60 + logger->log(L::FINE, "<!-- Sent message: -->" + msg.toString());
1.61 + send(codec.encode(msg));
1.62 + }
1.63 +
1.64 void send(const MidiMessage& midiMessage) {
1.65 std::lock_guard<std::recursive_mutex> lock(midiMutex);
1.66 midiSender->send(midiMessage);
1.67 @@ -150,77 +175,103 @@
1.68 if (running) stop();
1.69 }
1.70
1.71 - void setMidiSender(MidiSender* midiSender) {
1.72 + void setDeviceName(std::string name) override {
1.73 + logger->log(L::FINE, "DJMFixImpl::setDeviceName(" + name + ")");
1.74 +
1.75 + std::regex djm250pattern("Pioneer DJ Corporation DJM-250MK2.*");
1.76 + std::regex djm450pattern(".*450.*"); // TODO: correct pattern
1.77 +
1.78 + if (std::regex_match(name, djm250pattern)) {
1.79 + // DJM-250MK2:
1.80 + version = 0x17;
1.81 + seed3 = {
1.82 + 0x59, 0xb5, 0x4b, 0xfe, 0xe4,
1.83 + 0x4a, 0x5a, 0xc8, 0xe4, 0xc5
1.84 + };
1.85 + logger->log(L::FINE, "Switched to DJM-250MK2 mode");
1.86 + } else if (std::regex_match(name, djm450pattern)) {
1.87 + // DJM-450:
1.88 + // DJM-450 - not tested yet:
1.89 + version = 0x13;
1.90 + seed0 = {0x8c, 0x5b, 0x3f, 0x5d};
1.91 + seed3 = {
1.92 + 0x08, 0xef, 0x3f, 0x2f, 0x1e,
1.93 + 0x7a, 0x90, 0x17, 0xf6, 0xaf
1.94 + };
1.95 + logger->log(L::FINE, "Switched to DJM-450 mode");
1.96 + } else {
1.97 + // DJM-V10:
1.98 + version = 0x34;
1.99 + seed3 = {
1.100 + 0x70, 0x01, 0x4d, 0x05, 0xbe,
1.101 + 0xf2, 0xe4, 0xde, 0x60, 0xd6
1.102 + };
1.103 + logger->log(L::FINE, "Switched to DJM-V10 mode");
1.104 + }
1.105 + }
1.106 +
1.107 + void setMidiSender(MidiSender* midiSender) override {
1.108 logger->log(L::FINER, "DJMFixImpl::setMidiSender()");
1.109 this->midiSender = midiSender;
1.110 }
1.111
1.112 virtual void receive(const MidiMessage& msg) override {
1.113 + // TODO: remove try/catch - there should be no unknown messages
1.114 + try {
1.115 + receive0(msg);
1.116 + } catch (const std::exception& e) {
1.117 + logger->log(L::SEVERE,
1.118 + std::string("Message receiving failed: ") + e.what());
1.119 + }
1.120 + }
1.121 +
1.122 + virtual void receive0(const MidiMessage& msg) {
1.123 logger->log(L::FINE, "Received a message: "
1.124 "size = " + std::to_string(msg.size()) + " "
1.125 "data = " + toString(msg));
1.126 std::lock_guard<std::recursive_mutex> lock(midiMutex);
1.127 + Message msgIn = codec.decode(msg);
1.128
1.129 + logger->log(L::FINE, "<!-- Received message: -->" + msgIn.toString());
1.130
1.131 - if (msg.size() == 12 && msg[9] == 0x11) {
1.132 + if (msgIn.type == MessageType::D11_GREETING && !greetingReceived) {
1.133 logger->log(L::INFO, "Received greeting message.");
1.134 - send({
1.135 - 0xf0, 0x00, 0x40, 0x05,
1.136 - 0x00, 0x00, 0x00, 0x17,
1.137 - 0x00, 0x12, 0x2a, 0x01,
1.138 - 0x0b, 0x50, 0x69, 0x6f,
1.139 - 0x6e, 0x65, 0x65, 0x72,
1.140 - 0x44, 0x4a, 0x02, 0x0b,
1.141 - 0x72, 0x65, 0x6b, 0x6f,
1.142 - 0x72, 0x64, 0x62, 0x6f,
1.143 - 0x78, 0x03, 0x12, 0x02,
1.144 - 0x09, 0x00, 0x00, 0x00,
1.145 - 0x00, 0x00, 0x00, 0x02,
1.146 - 0x03, 0x04, 0x08, 0x00,
1.147 - 0x00, 0x00, 0x00, 0xf7
1.148 + Message msgOut(MessageType::H12_SEED1, version,{
1.149 + {FieldType::F01, name1},
1.150 + {FieldType::F02, name2},
1.151 + {FieldType::F03, denormalize(seed1)}
1.152 });
1.153 + send(msgOut);
1.154 + greetingReceived = true;
1.155 logger->log(L::INFO, "Sent message with seed1.");
1.156 - } else if (msg.size() == 54
1.157 - && msg[9] == 0x13
1.158 - && msg[33] == 0x04
1.159 - && msg[43] == 0x03) //
1.160 - {
1.161 - Bytes hash1(msg.begin() + 35, msg.begin() + 35 + 8);
1.162 - seed2 = Bytes(msg.begin() + 45, msg.begin() + 45 + 8);
1.163 - hash1 = normalize(hash1);
1.164 - seed2 = normalize(seed2);
1.165 + } else if (msgIn.type == MessageType::D13_HASH1_SEED2) {
1.166 + std::vector<Field> hash1F = msgIn.findFields(FieldType::F04);
1.167 + std::vector<Field> seed2F = msgIn.findFields(FieldType::F03);
1.168 +
1.169 + if (hash1F.empty()) throw std::logic_error("hash1 not found");
1.170 + if (seed2F.empty()) throw std::logic_error("seed2 not found");
1.171 +
1.172 + hash1 = normalize(hash1F[0].data);
1.173 + seed2 = normalize(seed2F[0].data);
1.174 +
1.175 logger->log(L::INFO, "Received message with "
1.176 "hash1 = " + toString(hash1) + " and "
1.177 "seed2 = " + toString(seed2));
1.178
1.179 - Bytes seed0 = {0x68, 0x01, 0x31, 0xFB};
1.180 - Bytes seed1 = {0x29, 0x00, 0x00, 0x00, 0x23, 0x48, 0x00, 0x00};
1.181 -
1.182 Bytes hash1check =
1.183 toBytes(fnv32hash(concat(seed1, xOR(seed0, seed2))));
1.184
1.185 if (equals(hash1, hash1check)) {
1.186 logger->log(L::INFO, "Verification of hash1 was successful.");
1.187 - Bytes hash2 =
1.188 - toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2))));
1.189 - send(concat({
1.190 - 0xf0, 0x00, 0x40, 0x05,
1.191 - 0x00, 0x00, 0x00, 0x17,
1.192 - 0x00, 0x14, 0x38, 0x01,
1.193 - 0x0b, 0x50, 0x69, 0x6f,
1.194 - 0x6e, 0x65, 0x65, 0x72,
1.195 - 0x44, 0x4a, 0x02, 0x0b,
1.196 - 0x72, 0x65, 0x6b, 0x6f,
1.197 - 0x72, 0x64, 0x62, 0x6f,
1.198 - 0x78, 0x04, 0x0a
1.199 - }, concat(denormalize(hash2),{
1.200 - 0x05, 0x16, 0x05, 0x09,
1.201 - 0x0b, 0x05, 0x04, 0x0b,
1.202 - 0x0f, 0x0e, 0x0e, 0x04,
1.203 - 0x04, 0x0a, 0x05, 0x0a,
1.204 - 0x0c, 0x08, 0x0e, 0x04,
1.205 - 0x0c, 0x05, 0xf7
1.206 - })));
1.207 + hash2 = toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2))));
1.208 +
1.209 + Message msgOut(MessageType::H14_HASH2, version,{
1.210 + {FieldType::F01, name1},
1.211 + {FieldType::F02, name2},
1.212 + {FieldType::F04, denormalize(hash2)},
1.213 + {FieldType::F05, denormalize(seed3)}
1.214 + });
1.215 + send(msgOut);
1.216 logger->log(L::INFO, "Sent message with hash2.");
1.217 } else {
1.218 std::stringstream logMessage;
1.219 @@ -235,11 +286,13 @@
1.220 logger->log(L::SEVERE, logMessage.str());
1.221 // TODO: graceful death
1.222 }
1.223 - } else if (msg.size() == 12 && msg[9] == 0x15) {
1.224 + } else if (msgIn.type == MessageType::D15_CONFIRMATION) {
1.225 sendKeepAlive = true;
1.226 logger->log(L::INFO, "Received acknowledgment message. "
1.227 "Started sending keep-alive messages. "
1.228 "LINE/PHONO channels should work now.");
1.229 + } else {
1.230 + logger->log(L::SEVERE, "Received unexpected message type.");
1.231 }
1.232
1.233 }
1.234 @@ -249,12 +302,18 @@
1.235 if (midiSender == nullptr)
1.236 throw std::logic_error("Need a midiSender when starting DJMFix");
1.237
1.238 - // TODO: methods for parsing and constructing messages from parts (TLV)
1.239 send({
1.240 0xf0, 0x00, 0x40, 0x05,
1.241 - 0x00, 0x00, 0x00, 0x17,
1.242 + 0x00, 0x00, 0x00, version,
1.243 0x00, 0x50, 0x01, 0xf7
1.244 });
1.245 +
1.246 + // TODO: check whether this second message is neccessary for V10:
1.247 + send({
1.248 + 0xf0, 0x00, 0x40, 0x05,
1.249 + 0x00, 0x00, 0x00, version,
1.250 + 0x00, 0x03, 0x01, 0xf7
1.251 + });
1.252 logger->log(L::INFO, "Sent greeting message.");
1.253
1.254 keepAliveThread = std::thread(&DJMFixImpl::run, this);