DJMFix.cpp
branchv_0
changeset 18 358a601bfe81
parent 16 63154f9d24a2
child 19 4ed672cecc25
     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);