djm-fix.cpp
author František Kučera <franta-hg@frantovo.cz>
Sun, 01 Jun 2025 13:14:18 +0200
branchv_0
changeset 19 4ed672cecc25
parent 18 358a601bfe81
permissions -rw-r--r--
rename 'version' field to 'model'
     1 /**
     2  * DJM-Fix
     3  * Copyright © 2025 František Kučera (Frantovo.cz, GlobalCode.info)
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 3 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
    16  */
    17 
    18 #include <memory>
    19 #include <iostream>
    20 #include <chrono>
    21 #include <thread>
    22 #include <csignal>
    23 #include <atomic>
    24 
    25 #include "DJMFix.h"
    26 #include "AlsaBridge.h"
    27 #include "Logger.h"
    28 
    29 static std::atomic<bool> run{true};
    30 
    31 void interrupt(int signal) {
    32 	run = false;
    33 }
    34 
    35 /**
    36  * The support for Pioneer DJ DJM-250MK2 (an external  USB  sound  card / mixer)
    37  * was added to the Linux (kernel) by these patches:
    38  *
    39  * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/...
    40  *   - ...sound/usb?id=73d8c94084341e2895169a0462dbc18167f01683 (playback)
    41  *   - ...sound/usb?id=14335d8b9e1a2bf006f9d969a103f9731cabb210 (recording)
    42  *   - ...sound/usb?id=cdc01a1558dedcee3daee7e1802d0349a07edb87 (mixer setup)
    43  *
    44  * These patches are enough for playback and for recording from post CH  faders.
    45  *
    46  * However this mixer is somehow incapacitated and if we want to record the  raw
    47  * signal  from the PHONO or LINE channels, we only get silence. This feature is
    48  * important for DVS (Digital Vinyl Systems) setups where  the  timecode  signal
    49  * from  special  control  vinyls  flows  from mixer to the computer where it is
    50  * interpreted in a software like MIXXX and used for controlling the playback of
    51  * files on our computer. The signal (usually music) from these files flows back
    52  * to the mixer and then to speakers and headphones.
    53  *
    54  * To make this work and enjoy all the features of the device we have bought, we
    55  * need  to  tell  the mixer that we want the signal instead of silence on given
    56  * channels. And this is the purpose of the djm-fix utility and it  is  done  by
    57  * sending some magic packet to the mixer.
    58  *
    59  * Implementation of this magic in the DJMFix.cpp file is based on  publicly
    60  * available documentation that can be found at:
    61  *   - https://swiftb0y.github.io/CDJHidProtocol/hid-analysis/handshake.html
    62  *   - https://mixb.me/CDJHidProtocol/hid-analysis/handshake.html (formerly).
    63  * This page pointed me to the proper hash function (according to the constants,
    64  * it  is bit uncommon but publicly known Fowler–Noll–Vo hash function, FNV) and
    65  * some magic bits. I wrote this standalone C++  program  that  talks  with  the
    66  * mixer over MIDI SysEx messages and does the magic.
    67  *
    68  * When this program is started, it finds the mixer and makes it fully  working.
    69  * It needs to be running all the time, otherwise we will get silence
    70  * on the PHONO/LINE channels again.
    71  *
    72  * Install dependencies:
    73  *   apt install mercurial make pkg-config g++ libasound2-dev
    74  * (in Debian or Ubuntu - it will be similar in other distributions)
    75  *
    76  * Download djm-fix:
    77  *   hg clone https://hg.frantovo.cz/midi/djm-fix/        # primary source
    78  *   hg clone https://hg.globalcode.info/midi/djm-fix/    # mirror
    79  *
    80  * Compile:
    81  *   make                                                 # can be skipped
    82  *
    83  * Run:
    84  *   make run                                             # in most cases
    85  *   build/djm-fix 'Pioneer DJ.*'                         # with custom name
    86  *                 ^ regular expression to select desired card
    87  *
    88  * Stop:
    89  *   press Ctrl+C
    90  * 
    91  * Look for updates in the Mercurial repositories and at:
    92  *   - https://blog.frantovo.cz/c/387/
    93  *   - https://blog.frantovo.cz/c/396/
    94  */
    95 
    96 int main(int argc, char**argv) {
    97 	using L = djmfix::logging::Level;
    98 	std::unique_ptr<djmfix::logging::Logger>
    99 			logger(djmfix::logging::create(std::cerr, L::INFO));
   100 	try {
   101 		logger->log(L::INFO, "DJM-Fix started.");
   102 		std::string cardNamePattern = argc == 2
   103 				? argv[1]
   104 				: "(Pioneer DJ|AlphaTheta).*";
   105 
   106 		signal(SIGINT, interrupt);
   107 		std::unique_ptr<djmfix::DJMFix> djmFix(djmfix::create(logger.get()));
   108 		std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(
   109 				djmfix::alsa::create(djmFix.get(),
   110 				cardNamePattern,
   111 				logger.get()));
   112 
   113 		alsaBridge->start();
   114 		while (run) std::this_thread::sleep_for(std::chrono::milliseconds(100));
   115 
   116 		std::cerr << std::endl;
   117 		logger->log(L::INFO, "DJM-Fix stopping.");
   118 
   119 		alsaBridge->stop();
   120 
   121 		return 0;
   122 	} catch (const std::exception& e) {
   123 		logger->log(L::SEVERE, e.what());
   124 	}
   125 }