Find card by a name pattern (regular expression) instead using hardcoded name.
By default, we look for card with name matching the "Pioneer DJ.*" pattern and we expect exactly one card to be found.
Custom pattern can be provided as a command-line argument.
Whole name would look something like this: "Pioneer DJ Corporation DJM-250MK2 at usb-0000:01:00.0-10.1, high speed".
1.1 --- a/AlsaBridge.cpp Mon Jan 04 00:15:56 2021 +0100
1.2 +++ b/AlsaBridge.cpp Mon Jan 04 13:38:08 2021 +0100
1.3 @@ -19,6 +19,7 @@
1.4 #include <thread>
1.5 #include <mutex>
1.6 #include <atomic>
1.7 +#include <regex>
1.8
1.9 #include <alsa/asoundlib.h>
1.10
1.11 @@ -36,6 +37,34 @@
1.12 std::recursive_mutex midiMutex;
1.13 std::atomic<bool> stopped{false};
1.14
1.15 + std::string findDeviceName(std::regex cardNamePattern) {
1.16 +
1.17 + std::vector<int> cardNumbers;
1.18 +
1.19 + std::cerr << "Looking for available cards:" << std::endl; // TODO: do not mess STDIO
1.20 +
1.21 + for (int card = -1; snd_card_next(&card) == 0 && card >= 0;) {
1.22 + char* longName = nullptr;
1.23 + snd_card_get_longname(card, &longName);
1.24 + std::cerr << "card: #" << card << ": '" << longName << "'"; // TODO: do not mess STDIO
1.25 + if (std::regex_match(longName, cardNamePattern)) {
1.26 + cardNumbers.push_back(card);
1.27 + std::cerr << " [matches]"; // TODO: do not mess STDIO
1.28 + }
1.29 + std::cerr << std::endl;
1.30 + free(longName);
1.31 + }
1.32 +
1.33 + if (cardNumbers.size() == 1) {
1.34 + std::cerr << "Going to fix card #" << cardNumbers[0] << std::endl; // TODO: do not mess STDIO
1.35 + return "hw:" + std::to_string(cardNumbers[0]);
1.36 + } else if (cardNumbers.empty()) {
1.37 + throw std::invalid_argument("No card with matching name found. Is the card connected? Maybe try to provide different name pattern.");
1.38 + } else {
1.39 + throw std::invalid_argument("Multiple cards with matching name found. Please provide a name pattern that matches only one card");
1.40 + }
1.41 + }
1.42 +
1.43 void run() {
1.44 while (!stopped) {
1.45 {
1.46 @@ -53,9 +82,11 @@
1.47 }
1.48 public:
1.49
1.50 - AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& deviceName) : djmFix(djmFix) {
1.51 + AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& cardNamePattern) : djmFix(djmFix) {
1.52 if (djmFix == nullptr) throw std::invalid_argument("need a djmFix for AlsaBridge");
1.53
1.54 + std::string deviceName = findDeviceName(std::regex(cardNamePattern));
1.55 +
1.56 int error = snd_rawmidi_open(&input, &output, deviceName.c_str(), SND_RAWMIDI_NONBLOCK);
1.57 if (error) throw std::invalid_argument("unable to open ALSA device");
1.58
2.1 --- a/AlsaBridge.h Mon Jan 04 00:15:56 2021 +0100
2.2 +++ b/AlsaBridge.h Mon Jan 04 13:38:08 2021 +0100
2.3 @@ -31,7 +31,7 @@
2.4
2.5 };
2.6
2.7 -AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName);
2.8 +AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& cardNamePattern);
2.9
2.10 }
2.11 }
3.1 --- a/djm-fix.cpp Mon Jan 04 00:15:56 2021 +0100
3.2 +++ b/djm-fix.cpp Mon Jan 04 13:38:08 2021 +0100
3.3 @@ -70,22 +70,29 @@
3.4 * make # we can skip this step, it will be compiled on the first run
3.5 *
3.6 * Run:
3.7 - * make run
3.8 + * make run # in most cases
3.9 + * build/djm-fix 'Pioneer DJ.*' # or provide custom name pattern (regular expression) to select the proper card
3.10 *
3.11 * Stop:
3.12 * press Ctrl+C
3.13 + *
3.14 + * Look for updates in the Mercurial repositories and at <https://blog.frantovo.cz/c/387/>.
3.15 */
3.16
3.17 int main(int argc, char**argv) {
3.18 - std::string deviceName = argc == 2 ? argv[1] : "hw:1"; // FIXME: parse CLI options + automatic device search
3.19 + try {
3.20 + std::string cardNamePattern = argc == 2 ? argv[1] : "Pioneer DJ.*";
3.21
3.22 - signal(SIGINT, interrupt);
3.23 - std::unique_ptr<djmfix::DJMFix> djmFix(djmfix::create());
3.24 - std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(djmfix::alsa::create(djmFix.get(), deviceName));
3.25 + signal(SIGINT, interrupt);
3.26 + std::unique_ptr<djmfix::DJMFix> djmFix(djmfix::create());
3.27 + std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(djmfix::alsa::create(djmFix.get(), cardNamePattern));
3.28
3.29 - alsaBridge->start();
3.30 - while (run) std::this_thread::sleep_for(std::chrono::milliseconds(100));
3.31 - alsaBridge->stop();
3.32 + alsaBridge->start();
3.33 + while (run) std::this_thread::sleep_for(std::chrono::milliseconds(100));
3.34 + alsaBridge->stop();
3.35
3.36 - return 0;
3.37 + return 0;
3.38 + } catch (const std::exception& e) {
3.39 + std::cerr << "ERROR: " << e.what() << std::endl; // TODO: do not mess STDIO
3.40 + }
3.41 }