13 * |
13 * |
14 * You should have received a copy of the GNU General Public License |
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/>. |
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 */ |
16 */ |
17 #include <iostream> |
17 #include <iostream> |
|
18 #include <sstream> |
18 #include <stdexcept> |
19 #include <stdexcept> |
19 #include <thread> |
20 #include <thread> |
20 #include <mutex> |
21 #include <mutex> |
21 #include <atomic> |
22 #include <atomic> |
22 #include <regex> |
23 #include <regex> |
23 |
24 |
24 #include <alsa/asoundlib.h> |
25 #include <alsa/asoundlib.h> |
25 |
26 |
26 #include "AlsaBridge.h" |
27 #include "AlsaBridge.h" |
|
28 #include "Logger.h" |
27 |
29 |
28 namespace djmfix { |
30 namespace djmfix { |
29 namespace alsa { |
31 namespace alsa { |
30 |
32 |
|
33 using L = djmfix::logging::Level; |
|
34 |
31 class AlsaBridgeImpl : public AlsaBridge, private djmfix::MidiSender { |
35 class AlsaBridgeImpl : public AlsaBridge, private djmfix::MidiSender { |
32 private: |
36 private: |
33 djmfix::DJMFix* djmFix; |
37 djmfix::DJMFix* djmFix; |
|
38 djmfix::logging::Logger* logger; |
34 snd_rawmidi_t* input; |
39 snd_rawmidi_t* input; |
35 snd_rawmidi_t* output; |
40 snd_rawmidi_t* output; |
36 std::thread receivingThread; |
41 std::thread receivingThread; |
37 std::recursive_mutex midiMutex; |
42 std::recursive_mutex midiMutex; |
38 std::atomic<bool> stopped{false}; |
43 std::atomic<bool> stopped{false}; |
39 |
44 |
40 std::string findDeviceName(std::regex cardNamePattern) { |
45 std::string findDeviceName(std::regex cardNamePattern) { |
41 |
46 |
42 std::vector<int> cardNumbers; |
47 std::vector<int> cardNumbers; |
43 |
48 |
44 std::cerr << "Looking for available cards:" << std::endl; // TODO: do not mess STDIO |
49 logger->log(L::INFO, "Looking for available cards:"); |
45 |
50 |
46 for (int card = -1; snd_card_next(&card) == 0 && card >= 0;) { |
51 for (int card = -1; snd_card_next(&card) == 0 && card >= 0;) { |
47 char* longName = nullptr; |
52 char* longName = nullptr; |
48 snd_card_get_longname(card, &longName); |
53 snd_card_get_longname(card, &longName); |
49 std::cerr << "card: #" << card << ": '" << longName << "'"; // TODO: do not mess STDIO |
54 |
|
55 std::stringstream logMessage; |
|
56 logMessage << " - card: #" << card << ": '" << longName << "'"; |
|
57 |
50 if (std::regex_match(longName, cardNamePattern)) { |
58 if (std::regex_match(longName, cardNamePattern)) { |
51 cardNumbers.push_back(card); |
59 cardNumbers.push_back(card); |
52 std::cerr << " [matches]"; // TODO: do not mess STDIO |
60 logMessage << " [matches]"; |
53 } |
61 } |
54 std::cerr << std::endl; |
62 |
|
63 logger->log(L::INFO, logMessage.str()); |
|
64 |
55 free(longName); |
65 free(longName); |
56 } |
66 } |
57 |
67 |
58 if (cardNumbers.size() == 1) { |
68 if (cardNumbers.size() == 1) { |
59 std::cerr << "Going to fix card #" << cardNumbers[0] << std::endl; // TODO: do not mess STDIO |
69 logger->log(L::INFO, "Going to fix card #" + std::to_string(cardNumbers[0])); |
60 return "hw:" + std::to_string(cardNumbers[0]); |
70 return "hw:" + std::to_string(cardNumbers[0]); |
61 } else if (cardNumbers.empty()) { |
71 } else if (cardNumbers.empty()) { |
62 throw std::invalid_argument("No card with matching name found. Is the card connected? Maybe try to provide different name pattern."); |
72 throw std::invalid_argument("No card with matching name found. Is the card connected? Maybe try to provide different name pattern."); |
63 } else { |
73 } else { |
64 throw std::invalid_argument("Multiple cards with matching name found. Please provide a name pattern that matches only one card"); |
74 throw std::invalid_argument("Multiple cards with matching name found. Please provide a name pattern that matches only one card"); |
80 std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
90 std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
81 } |
91 } |
82 } |
92 } |
83 public: |
93 public: |
84 |
94 |
85 AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& cardNamePattern) : djmFix(djmFix) { |
95 AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& cardNamePattern, djmfix::logging::Logger* logger) : djmFix(djmFix), logger(logger ? logger : djmfix::logging::blackhole()) { |
86 if (djmFix == nullptr) throw std::invalid_argument("need a djmFix for AlsaBridge"); |
96 if (djmFix == nullptr) throw std::invalid_argument("need a djmFix for AlsaBridge"); |
87 |
97 |
88 std::string deviceName = findDeviceName(std::regex(cardNamePattern)); |
98 std::string deviceName = findDeviceName(std::regex(cardNamePattern)); |
89 |
99 |
90 int error = snd_rawmidi_open(&input, &output, deviceName.c_str(), SND_RAWMIDI_NONBLOCK); |
100 int error = snd_rawmidi_open(&input, &output, deviceName.c_str(), SND_RAWMIDI_NONBLOCK); |
93 |
103 |
94 djmFix->setMidiSender(this); |
104 djmFix->setMidiSender(this); |
95 } |
105 } |
96 |
106 |
97 virtual ~AlsaBridgeImpl() { |
107 virtual ~AlsaBridgeImpl() { |
98 // TODO: do not use raw/exclusive access to the device |
108 // TODO: do not use raw/exclusive access to the MIDI device |
99 snd_rawmidi_close(input); |
109 snd_rawmidi_close(input); |
100 snd_rawmidi_close(output); |
110 snd_rawmidi_close(output); |
101 std::cerr << "~AlsaBridgeImpl()" << std::endl; // TODO: do not mess STDIO |
111 logger->log(L::FINE, "~AlsaBridgeImpl()"); |
102 } |
112 } |
103 |
113 |
104 virtual void start() override { |
114 virtual void start() override { |
105 djmFix->start(); |
115 djmFix->start(); |
106 receivingThread = std::thread(&AlsaBridgeImpl::run, this); |
116 receivingThread = std::thread(&AlsaBridgeImpl::run, this); |
113 } |
123 } |
114 |
124 |
115 virtual void send(MidiMessage midiMessage) override { |
125 virtual void send(MidiMessage midiMessage) override { |
116 std::lock_guard<std::recursive_mutex> lock(midiMutex); |
126 std::lock_guard<std::recursive_mutex> lock(midiMutex); |
117 ssize_t length = snd_rawmidi_write(output, midiMessage.data(), midiMessage.size()); |
127 ssize_t length = snd_rawmidi_write(output, midiMessage.data(), midiMessage.size()); |
118 std::cerr << "AlsaBridgeImpl::send(): length = " << length << std::endl; // TODO: do not mess STDIO |
128 logger->log(L::INFO, "AlsaBridgeImpl::send(): length = " + std::to_string(length)); |
119 } |
129 } |
120 |
130 |
121 }; |
131 }; |
122 |
132 |
123 AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName) { |
133 AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName, djmfix::logging::Logger* logger) { |
124 return new AlsaBridgeImpl(djmFix, deviceName); |
134 return new AlsaBridgeImpl(djmFix, deviceName, logger); |
125 } |
135 } |
126 |
136 |
127 } |
137 } |
128 } |
138 } |