Find card by a name pattern (regular expression) instead using hardcoded name. v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Mon, 04 Jan 2021 13:38:08 +0100
branchv_0
changeset 11 5b351628a377
parent 10 4d95b089457d
child 12 15d87fdd6e6c
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".
AlsaBridge.cpp
AlsaBridge.h
djm-fix.cpp
--- a/AlsaBridge.cpp	Mon Jan 04 00:15:56 2021 +0100
+++ b/AlsaBridge.cpp	Mon Jan 04 13:38:08 2021 +0100
@@ -19,6 +19,7 @@
 #include <thread>
 #include <mutex>
 #include <atomic>
+#include <regex>
 
 #include <alsa/asoundlib.h>
 
@@ -36,6 +37,34 @@
 	std::recursive_mutex midiMutex;
 	std::atomic<bool> stopped{false};
 
+	std::string findDeviceName(std::regex cardNamePattern) {
+
+		std::vector<int> cardNumbers;
+
+		std::cerr << "Looking for available cards:" << std::endl; // TODO: do not mess STDIO
+
+		for (int card = -1; snd_card_next(&card) == 0 && card >= 0;) {
+			char* longName = nullptr;
+			snd_card_get_longname(card, &longName);
+			std::cerr << "card: #" << card << ": '" << longName << "'"; // TODO: do not mess STDIO
+			if (std::regex_match(longName, cardNamePattern)) {
+				cardNumbers.push_back(card);
+				std::cerr << " [matches]"; // TODO: do not mess STDIO
+			}
+			std::cerr << std::endl;
+			free(longName);
+		}
+
+		if (cardNumbers.size() == 1) {
+			std::cerr << "Going to fix card #" << cardNumbers[0] << std::endl; // TODO: do not mess STDIO
+			return "hw:" + std::to_string(cardNumbers[0]);
+		} else if (cardNumbers.empty()) {
+			throw std::invalid_argument("No card with matching name found. Is the card connected? Maybe try to provide different name pattern.");
+		} else {
+			throw std::invalid_argument("Multiple cards with matching name found. Please provide a name pattern that matches only one card");
+		}
+	}
+
 	void run() {
 		while (!stopped) {
 			{
@@ -53,9 +82,11 @@
 	}
 public:
 
-	AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& deviceName) : djmFix(djmFix) {
+	AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& cardNamePattern) : djmFix(djmFix) {
 		if (djmFix == nullptr) throw std::invalid_argument("need a djmFix for AlsaBridge");
 
+		std::string deviceName = findDeviceName(std::regex(cardNamePattern));
+
 		int error = snd_rawmidi_open(&input, &output, deviceName.c_str(), SND_RAWMIDI_NONBLOCK);
 		if (error) throw std::invalid_argument("unable to open ALSA device");
 
--- a/AlsaBridge.h	Mon Jan 04 00:15:56 2021 +0100
+++ b/AlsaBridge.h	Mon Jan 04 13:38:08 2021 +0100
@@ -31,7 +31,7 @@
 
 };
 
-AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName);
+AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& cardNamePattern);
 
 }
 }
--- a/djm-fix.cpp	Mon Jan 04 00:15:56 2021 +0100
+++ b/djm-fix.cpp	Mon Jan 04 13:38:08 2021 +0100
@@ -70,22 +70,29 @@
  *   make                                                        # we can skip this step, it will be compiled on the first run
  *
  * Run:
- *   make run
+ *   make run                                                    # in most cases
+ *   build/djm-fix 'Pioneer DJ.*'                                # or provide custom name pattern (regular expression) to select the proper card
  *
  * Stop:
  *   press Ctrl+C
+ * 
+ * Look for updates in the Mercurial repositories and at <https://blog.frantovo.cz/c/387/>.
  */
 
 int main(int argc, char**argv) {
-	std::string deviceName = argc == 2 ? argv[1] : "hw:1"; // FIXME: parse CLI options + automatic device search
+	try {
+		std::string cardNamePattern = argc == 2 ? argv[1] : "Pioneer DJ.*";
 
-	signal(SIGINT, interrupt);
-	std::unique_ptr<djmfix::DJMFix> djmFix(djmfix::create());
-	std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(djmfix::alsa::create(djmFix.get(), deviceName));
+		signal(SIGINT, interrupt);
+		std::unique_ptr<djmfix::DJMFix> djmFix(djmfix::create());
+		std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(djmfix::alsa::create(djmFix.get(), cardNamePattern));
 
-	alsaBridge->start();
-	while (run) std::this_thread::sleep_for(std::chrono::milliseconds(100));
-	alsaBridge->stop();
+		alsaBridge->start();
+		while (run) std::this_thread::sleep_for(std::chrono::milliseconds(100));
+		alsaBridge->stop();
 
-	return 0;
+		return 0;
+	} catch (const std::exception& e) {
+		std::cerr << "ERROR: " << e.what() << std::endl; // TODO: do not mess STDIO
+	}
 }