1.1 --- a/AlsaBridge.cpp Tue Apr 15 22:44:31 2025 +0200
1.2 +++ b/AlsaBridge.cpp Tue Apr 15 22:45:25 2025 +0200
1.3 @@ -1,6 +1,6 @@
1.4 /**
1.5 * DJM-Fix
1.6 - * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
1.7 + * Copyright © 2025 František Kučera (Frantovo.cz, GlobalCode.info)
1.8 *
1.9 * This program is free software: you can redistribute it and/or modify
1.10 * it under the terms of the GNU General Public License as published by
1.11 @@ -67,18 +67,24 @@
1.12 }
1.13
1.14 if (cardNumbers.size() == 1) {
1.15 - logger->log(L::INFO, "Going to fix card #" + std::to_string(cardNumbers[0]));
1.16 - return "hw:" + std::to_string(cardNumbers[0]);
1.17 + const auto n = std::to_string(cardNumbers[0]);
1.18 + logger->log(L::INFO, "Going to fix card #" + n);
1.19 + return "hw:" + n;
1.20 } else if (cardNumbers.empty()) {
1.21 - throw std::invalid_argument("No card with matching name found. Is the card connected? Maybe try to provide different name pattern.");
1.22 + throw std::invalid_argument(
1.23 + "No card with matching name found. Is the card connected? "
1.24 + "Maybe try to provide different name pattern.");
1.25 } else {
1.26 - throw std::invalid_argument("Multiple cards with matching name found. Please provide a name pattern that matches only one card");
1.27 + throw std::invalid_argument(
1.28 + "Multiple cards with matching name found. "
1.29 + "Please provide a name pattern that matches only one card");
1.30 }
1.31 }
1.32
1.33 std::string toString(const MidiMessage& midiMessage) {
1.34 std::stringstream result;
1.35 - for (uint8_t b : midiMessage) result << std::hex << std::setw(2) << std::setfill('0') << (int) b;
1.36 + for (uint8_t b : midiMessage)
1.37 + result << std::hex << std::setw(2) << std::setfill('0') << (int) b;
1.38 return result.str();
1.39 }
1.40
1.41 @@ -87,11 +93,11 @@
1.42 {
1.43 std::lock_guard<std::recursive_mutex> lock(midiMutex);
1.44 // TODO: poll
1.45 - uint8_t buffer[256];
1.46 - ssize_t length = snd_rawmidi_read(input, buffer, sizeof (buffer));
1.47 - if (length > 0 && length <= sizeof (buffer)) {
1.48 + uint8_t buf[256];
1.49 + ssize_t length = snd_rawmidi_read(input, buf, sizeof (buf));
1.50 + if (length > 0 && length <= sizeof (buf)) {
1.51 // TODO: multiple messages combined together?
1.52 - djmFix->receive(MidiMessage(buffer, buffer + length));
1.53 + djmFix->receive(MidiMessage(buf, buf + length));
1.54 }
1.55 }
1.56 std::this_thread::sleep_for(std::chrono::milliseconds(100));
1.57 @@ -99,15 +105,21 @@
1.58 }
1.59 public:
1.60
1.61 - AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& cardNamePattern, djmfix::logging::Logger* logger) : djmFix(djmFix), logger(logger ? logger : djmfix::logging::blackhole()) {
1.62 - if (djmFix == nullptr) throw std::invalid_argument("Need a djmFix for AlsaBridge.");
1.63 + AlsaBridgeImpl(
1.64 + djmfix::DJMFix* djmFix,
1.65 + const std::string& cardNamePattern,
1.66 + djmfix::logging::Logger* logger)
1.67 + : djmFix(djmFix), logger(logger ? logger : djmfix::logging::blackhole()) //
1.68 + {
1.69 + if (djmFix == nullptr)
1.70 + throw std::invalid_argument("Need a djmFix for AlsaBridge.");
1.71
1.72 std::string deviceName = findDeviceName(std::regex(cardNamePattern));
1.73
1.74 - int error = snd_rawmidi_open(&input, &output, deviceName.c_str(), SND_RAWMIDI_NONBLOCK);
1.75 + int mode = SND_RAWMIDI_NONBLOCK;
1.76 + int error = snd_rawmidi_open(&input, &output, deviceName.c_str(), mode);
1.77 if (error) throw std::invalid_argument("Unable to open ALSA device.");
1.78
1.79 -
1.80 djmFix->setMidiSender(this);
1.81 }
1.82
1.83 @@ -129,15 +141,21 @@
1.84 djmFix->stop();
1.85 }
1.86
1.87 - virtual void send(MidiMessage midiMessage) override {
1.88 + virtual void send(MidiMessage msg) override {
1.89 std::lock_guard<std::recursive_mutex> lock(midiMutex);
1.90 - ssize_t length = snd_rawmidi_write(output, midiMessage.data(), midiMessage.size());
1.91 - logger->log(L::FINE, "Sent message: length = " + std::to_string(length) + " data = " + toString(midiMessage));
1.92 + ssize_t length = snd_rawmidi_write(output, msg.data(), msg.size());
1.93 + logger->log(L::FINE, "Sent message:"
1.94 + " length = " + std::to_string(length)
1.95 + + " data = " + toString(msg));
1.96 }
1.97
1.98 };
1.99
1.100 -AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName, djmfix::logging::Logger* logger) {
1.101 +AlsaBridge* create(
1.102 + djmfix::DJMFix* djmFix,
1.103 + const std::string& deviceName,
1.104 + djmfix::logging::Logger* logger) //
1.105 +{
1.106 return new AlsaBridgeImpl(djmFix, deviceName, logger);
1.107 }
1.108
2.1 --- a/AlsaBridge.h Tue Apr 15 22:44:31 2025 +0200
2.2 +++ b/AlsaBridge.h Tue Apr 15 22:45:25 2025 +0200
2.3 @@ -1,6 +1,6 @@
2.4 /**
2.5 * DJM-Fix
2.6 - * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
2.7 + * Copyright © 2025 František Kučera (Frantovo.cz, GlobalCode.info)
2.8 *
2.9 * This program is free software: you can redistribute it and/or modify
2.10 * it under the terms of the GNU General Public License as published by
2.11 @@ -32,7 +32,10 @@
2.12
2.13 };
2.14
2.15 -AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& cardNamePattern, djmfix::logging::Logger* logger);
2.16 +AlsaBridge* create(
2.17 + djmfix::DJMFix* djmFix,
2.18 + const std::string& cardNamePattern,
2.19 + djmfix::logging::Logger* logger);
2.20
2.21 }
2.22 }
3.1 --- a/DJMFix.cpp Tue Apr 15 22:44:31 2025 +0200
3.2 +++ b/DJMFix.cpp Tue Apr 15 22:45:25 2025 +0200
3.3 @@ -1,6 +1,6 @@
3.4 /**
3.5 * DJM-Fix
3.6 - * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
3.7 + * Copyright © 2025 František Kučera (Frantovo.cz, GlobalCode.info)
3.8 *
3.9 * This program is free software: you can redistribute it and/or modify
3.10 * it under the terms of the GNU General Public License as published by
3.11 @@ -30,6 +30,7 @@
3.12
3.13 using L = djmfix::logging::Level;
3.14 using Bytes = std::vector<uint8_t>;
3.15 +namespace chro = std::chrono;
3.16
3.17 class DJMFixImpl : public DJMFix {
3.18 private:
3.19 @@ -47,10 +48,17 @@
3.20 void run() {
3.21 while (!stopped) {
3.22 logger->log(L::FINE, "DJMFixImpl::run()");
3.23 - if (sendKeepAlive) send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7});
3.24 - std::this_thread::sleep_for(std::chrono::milliseconds(keepAliveInterval));
3.25 + if (sendKeepAlive) send({
3.26 + 0xf0, 0x00, 0x40, 0x05,
3.27 + 0x00, 0x00, 0x00, 0x17,
3.28 + 0x00, 0x50, 0x01, 0xf7
3.29 + });
3.30 + std::this_thread::sleep_for(chro::milliseconds(keepAliveInterval));
3.31 keepAliveCounter++;
3.32 - if (keepAliveCounter % (60 * 1000 / keepAliveInterval) == 0) logger->log(L::INFO, "Still sending periodic keep-alive messages (each " + std::to_string(keepAliveInterval) + " ms).");
3.33 + if (keepAliveCounter % (60 * 1000 / keepAliveInterval) == 0)
3.34 + logger->log(L::INFO,
3.35 + "Still sending periodic keep-alive messages "
3.36 + "(each " + std::to_string(keepAliveInterval) + " ms).");
3.37 }
3.38 }
3.39
3.40 @@ -61,15 +69,18 @@
3.41
3.42 std::string toString(const Bytes& midiMessage) {
3.43 std::stringstream result;
3.44 - for (uint8_t b : midiMessage) result << std::hex << std::setw(2) << std::setfill('0') << (int) b;
3.45 + for (uint8_t b : midiMessage)
3.46 + result << std::hex << std::setw(2) << std::setfill('0') << (int) b;
3.47 return result.str();
3.48 }
3.49
3.50 Bytes normalize(const Bytes& data) {
3.51 - if (data.size() % 2) throw std::invalid_argument("Data before normalization must have even number of bytes.");
3.52 + if (data.size() % 2) throw std::invalid_argument(
3.53 + "Data before normalization must have even number of bytes.");
3.54 Bytes result;
3.55 result.reserve(data.size() / 2);
3.56 - for (size_t i = 0; i < data.size() / 2; i++) result.push_back((data[i * 2] & 0x0F) << 4 | (data[i * 2 + 1] & 0x0F));
3.57 + for (size_t i = 0; i < data.size() / 2; i++) result.push_back(
3.58 + (data[i * 2] & 0x0F) << 4 | (data[i * 2 + 1] & 0x0F));
3.59 return result;
3.60 }
3.61
3.62 @@ -105,7 +116,11 @@
3.63 return true;
3.64 }
3.65
3.66 - template<typename T> std::vector<T> concat(const std::vector<T>& a, const std::vector<T>& b, const std::vector<T>& c = {}) {
3.67 + template<typename T> std::vector<T> concat(
3.68 + const std::vector<T>& a,
3.69 + const std::vector<T>& b,
3.70 + const std::vector<T>& c = {}) //
3.71 + {
3.72 std::vector<T> result;
3.73 result.reserve(a.size() + b.size() + c.size());
3.74 for (size_t i = 0; i < a.size(); i++) result.push_back(a[i]);
3.75 @@ -114,8 +129,10 @@
3.76 return result;
3.77 }
3.78
3.79 - template<typename T> std::vector<T> xOR(const std::vector<T>& a, const std::vector<T>& b) {
3.80 - if (a.size() != b.size()) throw std::invalid_argument("Both must be the same length when doing XOR.");
3.81 + template<typename T>
3.82 + std::vector<T> xOR(const std::vector<T>& a, const std::vector<T>& b) {
3.83 + if (a.size() != b.size()) throw std::invalid_argument(
3.84 + "Both must be the same length when doing XOR.");
3.85 std::vector<T> result;
3.86 result.reserve(a.size());
3.87 for (size_t i = 0; i < a.size(); i++) result.push_back(a[i] ^ b[i]);
3.88 @@ -124,7 +141,8 @@
3.89
3.90 public:
3.91
3.92 - DJMFixImpl(djmfix::logging::Logger* logger) : logger(logger ? logger : djmfix::logging::blackhole()) {
3.93 + DJMFixImpl(djmfix::logging::Logger* logger)
3.94 + : logger(logger ? logger : djmfix::logging::blackhole()) {
3.95 }
3.96
3.97 virtual ~DJMFixImpl() override {
3.98 @@ -137,37 +155,78 @@
3.99 this->midiSender = midiSender;
3.100 }
3.101
3.102 - virtual void receive(const MidiMessage& midiMessage) override {
3.103 - logger->log(L::FINE, "Received a message: size = " + std::to_string(midiMessage.size()) + " data = " + toString(midiMessage));
3.104 + virtual void receive(const MidiMessage& msg) override {
3.105 + logger->log(L::FINE, "Received a message: "
3.106 + "size = " + std::to_string(msg.size()) + " "
3.107 + "data = " + toString(msg));
3.108 std::lock_guard<std::recursive_mutex> lock(midiMutex);
3.109
3.110
3.111 - if (midiMessage.size() == 12 && midiMessage[9] == 0x11) {
3.112 + if (msg.size() == 12 && msg[9] == 0x11) {
3.113 logger->log(L::INFO, "Received greeting message.");
3.114 - 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});
3.115 + send({
3.116 + 0xf0, 0x00, 0x40, 0x05,
3.117 + 0x00, 0x00, 0x00, 0x17,
3.118 + 0x00, 0x12, 0x2a, 0x01,
3.119 + 0x0b, 0x50, 0x69, 0x6f,
3.120 + 0x6e, 0x65, 0x65, 0x72,
3.121 + 0x44, 0x4a, 0x02, 0x0b,
3.122 + 0x72, 0x65, 0x6b, 0x6f,
3.123 + 0x72, 0x64, 0x62, 0x6f,
3.124 + 0x78, 0x03, 0x12, 0x02,
3.125 + 0x09, 0x00, 0x00, 0x00,
3.126 + 0x00, 0x00, 0x00, 0x02,
3.127 + 0x03, 0x04, 0x08, 0x00,
3.128 + 0x00, 0x00, 0x00, 0xf7
3.129 + });
3.130 logger->log(L::INFO, "Sent message with seed1.");
3.131 - } else if (midiMessage.size() == 54 && midiMessage[9] == 0x13 && midiMessage[33] == 0x04 && midiMessage[43] == 0x03) {
3.132 - Bytes hash1(midiMessage.begin() + 35, midiMessage.begin() + 35 + 8);
3.133 - seed2 = Bytes(midiMessage.begin() + 45, midiMessage.begin() + 45 + 8);
3.134 + } else if (msg.size() == 54
3.135 + && msg[9] == 0x13
3.136 + && msg[33] == 0x04
3.137 + && msg[43] == 0x03) //
3.138 + {
3.139 + Bytes hash1(msg.begin() + 35, msg.begin() + 35 + 8);
3.140 + seed2 = Bytes(msg.begin() + 45, msg.begin() + 45 + 8);
3.141 hash1 = normalize(hash1);
3.142 seed2 = normalize(seed2);
3.143 - logger->log(L::INFO, "Received message with hash1 = " + toString(hash1) + " and seed2 = " + toString(seed2));
3.144 + logger->log(L::INFO, "Received message with "
3.145 + "hash1 = " + toString(hash1) + " and "
3.146 + "seed2 = " + toString(seed2));
3.147
3.148 Bytes seed0 = {0x68, 0x01, 0x31, 0xFB};
3.149 Bytes seed1 = {0x29, 0x00, 0x00, 0x00, 0x23, 0x48, 0x00, 0x00};
3.150
3.151 - Bytes hash1check = toBytes(fnv32hash(concat(seed1, xOR(seed0, seed2))));
3.152 + Bytes hash1check =
3.153 + toBytes(fnv32hash(concat(seed1, xOR(seed0, seed2))));
3.154
3.155 if (equals(hash1, hash1check)) {
3.156 logger->log(L::INFO, "Verification of hash1 was successful.");
3.157 - Bytes hash2 = toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2))));
3.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})));
3.159 + Bytes hash2 =
3.160 + toBytes(fnv32hash(concat(seed2, xOR(seed0, seed2))));
3.161 + send(concat({
3.162 + 0xf0, 0x00, 0x40, 0x05,
3.163 + 0x00, 0x00, 0x00, 0x17,
3.164 + 0x00, 0x14, 0x38, 0x01,
3.165 + 0x0b, 0x50, 0x69, 0x6f,
3.166 + 0x6e, 0x65, 0x65, 0x72,
3.167 + 0x44, 0x4a, 0x02, 0x0b,
3.168 + 0x72, 0x65, 0x6b, 0x6f,
3.169 + 0x72, 0x64, 0x62, 0x6f,
3.170 + 0x78, 0x04, 0x0a
3.171 + }, concat(denormalize(hash2),{
3.172 + 0x05, 0x16, 0x05, 0x09,
3.173 + 0x0b, 0x05, 0x04, 0x0b,
3.174 + 0x0f, 0x0e, 0x0e, 0x04,
3.175 + 0x04, 0x0a, 0x05, 0x0a,
3.176 + 0x0c, 0x08, 0x0e, 0x04,
3.177 + 0x0c, 0x05, 0xf7
3.178 + })));
3.179 logger->log(L::INFO, "Sent message with hash2.");
3.180 } else {
3.181 std::stringstream logMessage;
3.182 logMessage
3.183 << "Verification of hash1 failed: "
3.184 - << " midiMessage = " << toString(midiMessage)
3.185 + << " midiMessage = " << toString(msg)
3.186 << " seed0 = " << toString(seed0)
3.187 << " seed1 = " << toString(seed1)
3.188 << " seed2 = " << toString(seed2)
3.189 @@ -176,19 +235,26 @@
3.190 logger->log(L::SEVERE, logMessage.str());
3.191 // TODO: graceful death
3.192 }
3.193 - } else if (midiMessage.size() == 12 && midiMessage[9] == 0x15) {
3.194 + } else if (msg.size() == 12 && msg[9] == 0x15) {
3.195 sendKeepAlive = true;
3.196 - logger->log(L::INFO, "Received acknowledgment message. Started sending keep-alive messages. LINE/PHONO channels should work now.");
3.197 + logger->log(L::INFO, "Received acknowledgment message. "
3.198 + "Started sending keep-alive messages. "
3.199 + "LINE/PHONO channels should work now.");
3.200 }
3.201
3.202 }
3.203
3.204 void start() override {
3.205 logger->log(L::FINE, "DJMFixImpl::start()");
3.206 - if (midiSender == nullptr) throw std::logic_error("Need a midiSender when starting DJMFix");
3.207 + if (midiSender == nullptr)
3.208 + throw std::logic_error("Need a midiSender when starting DJMFix");
3.209
3.210 // TODO: methods for parsing and constructing messages from parts (TLV)
3.211 - send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7});
3.212 + send({
3.213 + 0xf0, 0x00, 0x40, 0x05,
3.214 + 0x00, 0x00, 0x00, 0x17,
3.215 + 0x00, 0x50, 0x01, 0xf7
3.216 + });
3.217 logger->log(L::INFO, "Sent greeting message.");
3.218
3.219 keepAliveThread = std::thread(&DJMFixImpl::run, this);
4.1 --- a/Logger.cpp Tue Apr 15 22:44:31 2025 +0200
4.2 +++ b/Logger.cpp Tue Apr 15 22:45:25 2025 +0200
4.3 @@ -1,6 +1,6 @@
4.4 /**
4.5 * DJM-Fix
4.6 - * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
4.7 + * Copyright © 2025 František Kučera (Frantovo.cz, GlobalCode.info)
4.8 *
4.9 * This program is free software: you can redistribute it and/or modify
4.10 * it under the terms of the GNU General Public License as published by
4.11 @@ -56,12 +56,15 @@
4.12
4.13 public:
4.14
4.15 - LoggerImpl(std::ostream& output, Level minLevel) : output(output), minLevel(minLevel) {
4.16 + LoggerImpl(std::ostream& output, Level minLevel)
4.17 + : output(output), minLevel(minLevel) {
4.18 }
4.19
4.20 virtual void log(Level level, const std::string& message) override {
4.21 if (level <= minLevel) {
4.22 - output << getTimestamp() << " " << std::setw(8) << toString(level) << ": " << message << std::endl;
4.23 + output << getTimestamp()
4.24 + << " " << std::setw(8) << toString(level)
4.25 + << ": " << message << std::endl;
4.26 }
4.27 }
4.28 };
5.1 --- a/djm-fix.cpp Tue Apr 15 22:44:31 2025 +0200
5.2 +++ b/djm-fix.cpp Tue Apr 15 22:45:25 2025 +0200
5.3 @@ -1,6 +1,6 @@
5.4 /**
5.5 * DJM-Fix
5.6 - * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
5.7 + * Copyright © 2025 František Kučera (Frantovo.cz, GlobalCode.info)
5.8 *
5.9 * This program is free software: you can redistribute it and/or modify
5.10 * it under the terms of the GNU General Public License as published by
5.11 @@ -33,69 +33,86 @@
5.12 }
5.13
5.14 /**
5.15 - * The support for Pioneer DJ DJM-250MK2 (an external USB sound card / mixer) was added to the Linux (kernel) by these patches:
5.16 + * The support for Pioneer DJ DJM-250MK2 (an external USB sound card / mixer)
5.17 + * was added to the Linux (kernel) by these patches:
5.18 *
5.19 - * - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/sound/usb?id=73d8c94084341e2895169a0462dbc18167f01683 (playback)
5.20 - * - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/sound/usb?id=14335d8b9e1a2bf006f9d969a103f9731cabb210 (recording)
5.21 - * - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/sound/usb?id=cdc01a1558dedcee3daee7e1802d0349a07edb87 (mixer setup)
5.22 + * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/...
5.23 + * - ...sound/usb?id=73d8c94084341e2895169a0462dbc18167f01683 (playback)
5.24 + * - ...sound/usb?id=14335d8b9e1a2bf006f9d969a103f9731cabb210 (recording)
5.25 + * - ...sound/usb?id=cdc01a1558dedcee3daee7e1802d0349a07edb87 (mixer setup)
5.26 *
5.27 - * These patches are enough for playback and for recording from post CH faders.
5.28 + * These patches are enough for playback and for recording from post CH faders.
5.29 *
5.30 - * However this mixer is somehow incapacitated and if we want to record the raw signal from the PHONO or LINE channels,
5.31 - * we only get silence. This feature is important for DVS (Digital Vinyl Systems) setups where
5.32 - * the timecode signal from special control vinyls flows from mixer to the computer
5.33 - * where it is interpreted in a software like MIXXX and used for controlling the playback of files on our computer.
5.34 - * The signal (usually music) from these files flows back to the mixer and then to speakers and headphones.
5.35 + * However this mixer is somehow incapacitated and if we want to record the raw
5.36 + * signal from the PHONO or LINE channels, we only get silence. This feature is
5.37 + * important for DVS (Digital Vinyl Systems) setups where the timecode signal
5.38 + * from special control vinyls flows from mixer to the computer where it is
5.39 + * interpreted in a software like MIXXX and used for controlling the playback of
5.40 + * files on our computer. The signal (usually music) from these files flows back
5.41 + * to the mixer and then to speakers and headphones.
5.42 *
5.43 - * To make this work and enjoy all the features of the device we have bought, we need to tell the mixer that we
5.44 - * want the signal instead of silence on given channels. And this is the purpose of the djm-fix utility and
5.45 - * it is done by sending some magic packet to the mixer.
5.46 + * To make this work and enjoy all the features of the device we have bought, we
5.47 + * need to tell the mixer that we want the signal instead of silence on given
5.48 + * channels. And this is the purpose of the djm-fix utility and it is done by
5.49 + * sending some magic packet to the mixer.
5.50 *
5.51 - * Implementation of this magic in the AlsaBridge.cpp file is based on publicly available documentation
5.52 - * that can be found at <https://mixb.me/CDJHidProtocol/hid-analysis/handshake.html>.
5.53 - * This page pointed me to the proper hash function (according to the constants, it is bit uncommon but publicly known Fowler–Noll–Vo hash function, FNV)
5.54 - * and some magic bits. I wrote this standalone C++ program that talks with the mixer over MIDI SysEx messages and does the magic.
5.55 + * Implementation of this magic in the AlsaBridge.cpp file is based on publicly
5.56 + * available documentation that can be found at:
5.57 + * - https://swiftb0y.github.io/CDJHidProtocol/hid-analysis/handshake.html
5.58 + * - https://mixb.me/CDJHidProtocol/hid-analysis/handshake.html (formerly).
5.59 + * This page pointed me to the proper hash function (according to the constants,
5.60 + * it is bit uncommon but publicly known Fowler–Noll–Vo hash function, FNV) and
5.61 + * some magic bits. I wrote this standalone C++ program that talks with the
5.62 + * mixer over MIDI SysEx messages and does the magic.
5.63 *
5.64 - * When this program is started, it finds the mixer and makes it fully working.
5.65 - * It needs to be running all the time, otherwise we will get silence on the PHONO/LINE channels again.
5.66 + * When this program is started, it finds the mixer and makes it fully working.
5.67 + * It needs to be running all the time, otherwise we will get silence
5.68 + * on the PHONO/LINE channels again.
5.69 *
5.70 * Install dependencies:
5.71 - * apt install mercurial make pkg-config g++ libasound2-dev # in Debian or Ubuntu (it will be similar in other distributions)
5.72 + * apt install mercurial make pkg-config g++ libasound2-dev
5.73 + * (in Debian or Ubuntu - it will be similar in other distributions)
5.74 *
5.75 * Download djm-fix:
5.76 - * hg clone https://hg.frantovo.cz/midi/djm-fix/ # primary source
5.77 - * hg clone https://hg.globalcode.info/midi/djm-fix/ # or we can use this mirror
5.78 + * hg clone https://hg.frantovo.cz/midi/djm-fix/ # primary source
5.79 + * hg clone https://hg.globalcode.info/midi/djm-fix/ # mirror
5.80 *
5.81 * Compile:
5.82 - * make # we can skip this step, it will be compiled on the first run
5.83 + * make # can be skipped
5.84 *
5.85 * Run:
5.86 - * make run # in most cases
5.87 - * build/djm-fix 'Pioneer DJ.*' # or provide custom name pattern (regular expression) to select the proper card
5.88 + * make run # in most cases
5.89 + * build/djm-fix 'Pioneer DJ.*' # with custom name
5.90 + * ^ regular expression to select desired card
5.91 *
5.92 * Stop:
5.93 * press Ctrl+C
5.94 *
5.95 - * Look for updates in the Mercurial repositories and at <https://blog.frantovo.cz/c/387/>.
5.96 + * Look for updates in the Mercurial repositories and at:
5.97 + * - https://blog.frantovo.cz/c/387/
5.98 */
5.99
5.100 int main(int argc, char**argv) {
5.101 using L = djmfix::logging::Level;
5.102 - std::unique_ptr<djmfix::logging::Logger> logger(djmfix::logging::create(std::cerr, L::INFO));
5.103 + std::unique_ptr<djmfix::logging::Logger>
5.104 + logger(djmfix::logging::create(std::cerr, L::INFO));
5.105 try {
5.106 logger->log(L::INFO, "DJM-Fix started.");
5.107 std::string cardNamePattern = argc == 2 ? argv[1] : "Pioneer DJ.*";
5.108
5.109 signal(SIGINT, interrupt);
5.110 std::unique_ptr<djmfix::DJMFix> djmFix(djmfix::create(logger.get()));
5.111 - std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(djmfix::alsa::create(djmFix.get(), cardNamePattern, logger.get()));
5.112 + std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(
5.113 + djmfix::alsa::create(djmFix.get(),
5.114 + cardNamePattern,
5.115 + logger.get()));
5.116
5.117 alsaBridge->start();
5.118 while (run) std::this_thread::sleep_for(std::chrono::milliseconds(100));
5.119 -
5.120 +
5.121 std::cerr << std::endl;
5.122 logger->log(L::INFO, "DJM-Fix stopping.");
5.123 -
5.124 +
5.125 alsaBridge->stop();
5.126
5.127 return 0;