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 115b351628a377
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
     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  }