branch | v_0 |
changeset 13 | 334b727f7516 |
parent 12 | 15d87fdd6e6c |
12:15d87fdd6e6c | 13:334b727f7516 |
---|---|
33 |
33 |
34 class DJMFixImpl : public DJMFix { |
34 class DJMFixImpl : public DJMFix { |
35 private: |
35 private: |
36 MidiSender* midiSender; |
36 MidiSender* midiSender; |
37 djmfix::logging::Logger* logger; |
37 djmfix::logging::Logger* logger; |
38 const int keepAliveInterval = 200; |
|
39 int keepAliveCounter = 0; |
|
38 std::thread keepAliveThread; |
40 std::thread keepAliveThread; |
39 std::recursive_mutex midiMutex; |
41 std::recursive_mutex midiMutex; |
40 std::atomic<bool> running{false}; |
42 std::atomic<bool> running{false}; |
41 std::atomic<bool> stopped{false}; |
43 std::atomic<bool> stopped{false}; |
42 std::atomic<bool> sendKeepAlive{false}; |
44 std::atomic<bool> sendKeepAlive{false}; |
44 |
46 |
45 void run() { |
47 void run() { |
46 while (!stopped) { |
48 while (!stopped) { |
47 logger->log(L::FINE, "DJMFixImpl::run()"); |
49 logger->log(L::FINE, "DJMFixImpl::run()"); |
48 if (sendKeepAlive) send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); |
50 if (sendKeepAlive) send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); |
49 std::this_thread::sleep_for(std::chrono::milliseconds(200)); |
51 std::this_thread::sleep_for(std::chrono::milliseconds(keepAliveInterval)); |
52 keepAliveCounter++; |
|
53 if (keepAliveCounter % (60 * 1000 / keepAliveInterval) == 0) logger->log(L::INFO, "Still sending periodic keep-alive messages (each " + std::to_string(keepAliveInterval) + " ms)."); |
|
50 } |
54 } |
51 } |
55 } |
52 |
56 |
53 void send(const MidiMessage& midiMessage) { |
57 void send(const MidiMessage& midiMessage) { |
54 std::lock_guard<std::recursive_mutex> lock(midiMutex); |
58 std::lock_guard<std::recursive_mutex> lock(midiMutex); |
60 for (uint8_t b : midiMessage) result << std::hex << std::setw(2) << std::setfill('0') << (int) b; |
64 for (uint8_t b : midiMessage) result << std::hex << std::setw(2) << std::setfill('0') << (int) b; |
61 return result.str(); |
65 return result.str(); |
62 } |
66 } |
63 |
67 |
64 Bytes normalize(const Bytes& data) { |
68 Bytes normalize(const Bytes& data) { |
65 if (data.size() % 2) throw std::invalid_argument("data before normalization must have even number of bytes"); |
69 if (data.size() % 2) throw std::invalid_argument("Data before normalization must have even number of bytes."); |
66 Bytes result; |
70 Bytes result; |
67 result.reserve(data.size() / 2); |
71 result.reserve(data.size() / 2); |
68 for (size_t i = 0; i < data.size() / 2; i++) result.push_back((data[i * 2] & 0x0F) << 4 | (data[i * 2 + 1] & 0x0F)); |
72 for (size_t i = 0; i < data.size() / 2; i++) result.push_back((data[i * 2] & 0x0F) << 4 | (data[i * 2 + 1] & 0x0F)); |
69 return result; |
73 return result; |
70 } |
74 } |
109 for (size_t i = 0; i < c.size(); i++) result.push_back(c[i]); |
113 for (size_t i = 0; i < c.size(); i++) result.push_back(c[i]); |
110 return result; |
114 return result; |
111 } |
115 } |
112 |
116 |
113 template<typename T> std::vector<T> xOR(const std::vector<T>& a, const std::vector<T>& b) { |
117 template<typename T> std::vector<T> xOR(const std::vector<T>& a, const std::vector<T>& b) { |
114 if (a.size() != b.size()) throw std::invalid_argument("xor: both must be the same length"); |
118 if (a.size() != b.size()) throw std::invalid_argument("Both must be the same length when doing XOR."); |
115 std::vector<T> result; |
119 std::vector<T> result; |
116 result.reserve(a.size()); |
120 result.reserve(a.size()); |
117 for (size_t i = 0; i < a.size(); i++) result.push_back(a[i] ^ b[i]); |
121 for (size_t i = 0; i < a.size(); i++) result.push_back(a[i] ^ b[i]); |
118 return result; |
122 return result; |
119 } |
123 } |
122 |
126 |
123 DJMFixImpl(djmfix::logging::Logger* logger) : logger(logger ? logger : djmfix::logging::blackhole()) { |
127 DJMFixImpl(djmfix::logging::Logger* logger) : logger(logger ? logger : djmfix::logging::blackhole()) { |
124 } |
128 } |
125 |
129 |
126 virtual ~DJMFixImpl() override { |
130 virtual ~DJMFixImpl() override { |
127 logger->log(L::FINE, "~DJMFixImpl()"); |
131 logger->log(L::FINER, "~DJMFixImpl()"); |
128 if (running) stop(); |
132 if (running) stop(); |
129 } |
133 } |
130 |
134 |
131 void setMidiSender(MidiSender* midiSender) { |
135 void setMidiSender(MidiSender* midiSender) { |
132 logger->log(L::FINE, "DJMFixImpl::setMidiSender()"); |
136 logger->log(L::FINER, "DJMFixImpl::setMidiSender()"); |
133 this->midiSender = midiSender; |
137 this->midiSender = midiSender; |
134 } |
138 } |
135 |
139 |
136 virtual void receive(const MidiMessage& midiMessage) override { |
140 virtual void receive(const MidiMessage& midiMessage) override { |
137 logger->log(L::INFO, "received message: size = " + std::to_string(midiMessage.size()) + " data = " + toString(midiMessage)); |
141 logger->log(L::FINE, "Received a message: size = " + std::to_string(midiMessage.size()) + " data = " + toString(midiMessage)); |
138 std::lock_guard<std::recursive_mutex> lock(midiMutex); |
142 std::lock_guard<std::recursive_mutex> lock(midiMutex); |
139 |
143 |
140 |
144 |
141 if (midiMessage.size() == 12 && midiMessage[9] == 0x11) { |
145 if (midiMessage.size() == 12 && midiMessage[9] == 0x11) { |
146 logger->log(L::INFO, "Received greeting message."); |
|
142 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}); |
147 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}); |
148 logger->log(L::INFO, "Sent message with seed1."); |
|
143 } else if (midiMessage.size() == 54 && midiMessage[9] == 0x13 && midiMessage[33] == 0x04 && midiMessage[43] == 0x03) { |
149 } else if (midiMessage.size() == 54 && midiMessage[9] == 0x13 && midiMessage[33] == 0x04 && midiMessage[43] == 0x03) { |
144 Bytes hash1(midiMessage.begin() + 35, midiMessage.begin() + 35 + 8); |
150 Bytes hash1(midiMessage.begin() + 35, midiMessage.begin() + 35 + 8); |
145 seed2 = Bytes(midiMessage.begin() + 45, midiMessage.begin() + 45 + 8); |
151 seed2 = Bytes(midiMessage.begin() + 45, midiMessage.begin() + 45 + 8); |
146 hash1 = normalize(hash1); |
152 hash1 = normalize(hash1); |
147 seed2 = normalize(seed2); |
153 seed2 = normalize(seed2); |
148 logger->log(L::INFO, "got message with hash1 = " + toString(hash1) + " and seed2 = " + toString(seed2)); |
154 logger->log(L::INFO, "Received message with hash1 = " + toString(hash1) + " and seed2 = " + toString(seed2)); |
149 |
155 |
150 Bytes seed0 = {0x68, 0x01, 0x31, 0xFB}; |
156 Bytes seed0 = {0x68, 0x01, 0x31, 0xFB}; |
151 Bytes seed1 = {0x29, 0x00, 0x00, 0x00, 0x23, 0x48, 0x00, 0x00}; |
157 Bytes seed1 = {0x29, 0x00, 0x00, 0x00, 0x23, 0x48, 0x00, 0x00}; |
152 |
158 |
153 Bytes hash1check = toBytes(fnv32hash(concat(seed1, xOR(seed0, seed2)))); |
159 Bytes hash1check = toBytes(fnv32hash(concat(seed1, xOR(seed0, seed2)))); |
154 |
160 |
155 if (equals(hash1, hash1check)) { |
161 if (equals(hash1, hash1check)) { |
156 logger->log(L::INFO, "hash1 verification: OK"); |
162 logger->log(L::INFO, "Verification of hash1 was successful."); |
157 Bytes hash2 = toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2)))); |
163 Bytes hash2 = toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2)))); |
158 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}))); |
164 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}))); |
165 logger->log(L::INFO, "Sent message with hash2."); |
|
159 } else { |
166 } else { |
160 std::stringstream logMessage; |
167 std::stringstream logMessage; |
161 logMessage |
168 logMessage |
162 << "hash1 verification failed: " |
169 << "Verification of hash1 failed: " |
163 << " midiMessage = " << toString(midiMessage) |
170 << " midiMessage = " << toString(midiMessage) |
164 << " seed0 = " << toString(seed0) |
171 << " seed0 = " << toString(seed0) |
165 << " seed1 = " << toString(seed1) |
172 << " seed1 = " << toString(seed1) |
166 << " seed2 = " << toString(seed2) |
173 << " seed2 = " << toString(seed2) |
167 << " hash1 = " << toString(hash1) |
174 << " hash1 = " << toString(hash1) |
169 logger->log(L::SEVERE, logMessage.str()); |
176 logger->log(L::SEVERE, logMessage.str()); |
170 // TODO: graceful death |
177 // TODO: graceful death |
171 } |
178 } |
172 } else if (midiMessage.size() == 12 && midiMessage[9] == 0x15) { |
179 } else if (midiMessage.size() == 12 && midiMessage[9] == 0x15) { |
173 sendKeepAlive = true; |
180 sendKeepAlive = true; |
181 logger->log(L::INFO, "Received acknowledgment message. Started sending keep-alive messages. LINE/PHONO channels should work now."); |
|
174 } |
182 } |
175 |
183 |
176 } |
184 } |
177 |
185 |
178 void start() override { |
186 void start() override { |
179 logger->log(L::FINE, "DJMFixImpl::start()"); |
187 logger->log(L::FINE, "DJMFixImpl::start()"); |
180 if (midiSender == nullptr) throw std::logic_error("need a midiSender when starting DJMFix"); |
188 if (midiSender == nullptr) throw std::logic_error("Need a midiSender when starting DJMFix"); |
181 |
189 |
182 // TODO: methods for parsing and constructing messages from parts (TLV) |
190 // TODO: methods for parsing and constructing messages from parts (TLV) |
183 send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); |
191 send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); |
192 logger->log(L::INFO, "Sent greeting message."); |
|
184 |
193 |
185 keepAliveThread = std::thread(&DJMFixImpl::run, this); |
194 keepAliveThread = std::thread(&DJMFixImpl::run, this); |
186 running = true; |
195 running = true; |
187 |
196 |
188 } |
197 } |