1.1 --- a/AlsaBridge.cpp Fri Dec 18 23:58:03 2020 +0100
1.2 +++ b/AlsaBridge.cpp Sat Dec 19 17:33:16 2020 +0100
1.3 @@ -15,6 +15,9 @@
1.4 * along with this program. If not, see <http://www.gnu.org/licenses/>.
1.5 */
1.6 #include <iostream>
1.7 +#include <stdexcept>
1.8 +#include <thread>
1.9 +#include <atomic>
1.10
1.11 #include <alsa/asoundlib.h>
1.12
1.13 @@ -26,32 +29,63 @@
1.14 class AlsaBridgeImpl : public AlsaBridge, private djmfix::MidiSender {
1.15 private:
1.16 djmfix::DJMFix* djmFix;
1.17 + snd_rawmidi_t* input;
1.18 + snd_rawmidi_t* output;
1.19 + std::thread receivingThread;
1.20 + std::atomic<bool> stopped{false};
1.21 +
1.22 + void run() {
1.23 + while (!stopped) {
1.24 + // TODO: poll
1.25 + uint8_t buffer[256];
1.26 + ssize_t length = snd_rawmidi_read(input, buffer, sizeof (buffer));
1.27 + if (length > 0 && length <= sizeof (buffer)) {
1.28 + // TODO: multiple messages combined together?
1.29 + djmFix->receive(MidiMessage(buffer, buffer + length));
1.30 + }
1.31 +
1.32 + std::this_thread::sleep_for(std::chrono::milliseconds(100));
1.33 + }
1.34 + }
1.35 public:
1.36
1.37 - AlsaBridgeImpl(djmfix::DJMFix* djmFix) : djmFix(djmFix) {
1.38 + AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& deviceName) : djmFix(djmFix) {
1.39 + if (djmFix == nullptr) throw std::invalid_argument("need a djmFix for AlsaBridge");
1.40 +
1.41 + int error = snd_rawmidi_open(&input, &output, deviceName.c_str(), SND_RAWMIDI_NONBLOCK);
1.42 + if (error) throw std::invalid_argument("unable to open ALSA device");
1.43 +
1.44 +
1.45 djmFix->setMidiSender(this);
1.46 }
1.47
1.48 virtual ~AlsaBridgeImpl() {
1.49 + // TODO: do not use raw/exclusive access to the device
1.50 + snd_rawmidi_close(input);
1.51 + snd_rawmidi_close(output);
1.52 std::cerr << "~AlsaBridgeImpl()" << std::endl; // TODO: do not mess STDIO
1.53 }
1.54
1.55 virtual void start() override {
1.56 djmFix->start();
1.57 + receivingThread = std::thread(&AlsaBridgeImpl::run, this);
1.58 }
1.59
1.60 virtual void stop() override {
1.61 + stopped = true;
1.62 + receivingThread.join();
1.63 djmFix->stop();
1.64 }
1.65
1.66 virtual void send(MidiMessage midiMessage) override {
1.67 - std::cerr << "AlsaBridgeImpl::send()" << std::endl; // TODO: do not mess STDIO
1.68 + ssize_t length = snd_rawmidi_write(output, midiMessage.data(), midiMessage.size());
1.69 + std::cerr << "AlsaBridgeImpl::send(): length = " << length << std::endl; // TODO: do not mess STDIO
1.70 }
1.71
1.72 };
1.73
1.74 -AlsaBridge* create(djmfix::DJMFix* djmFix) {
1.75 - return new AlsaBridgeImpl(djmFix);
1.76 +AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName) {
1.77 + return new AlsaBridgeImpl(djmFix, deviceName);
1.78 }
1.79
1.80 }
2.1 --- a/AlsaBridge.h Fri Dec 18 23:58:03 2020 +0100
2.2 +++ b/AlsaBridge.h Sat Dec 19 17:33:16 2020 +0100
2.3 @@ -16,6 +16,8 @@
2.4 */
2.5 #pragma once
2.6
2.7 +#include <string>
2.8 +
2.9 #include "DJMFix.h"
2.10
2.11 namespace djmfix {
2.12 @@ -29,7 +31,7 @@
2.13
2.14 };
2.15
2.16 -AlsaBridge* create(djmfix::DJMFix* djmFix);
2.17 +AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName);
2.18
2.19 }
2.20 }
3.1 --- a/DJMFix.cpp Fri Dec 18 23:58:03 2020 +0100
3.2 +++ b/DJMFix.cpp Sat Dec 19 17:33:16 2020 +0100
3.3 @@ -15,7 +15,9 @@
3.4 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3.5 */
3.6 #include <iostream>
3.7 +#include <iomanip>
3.8 #include <thread>
3.9 +#include <atomic>
3.10 #include <chrono>
3.11 #include <stdexcept>
3.12
3.13 @@ -27,8 +29,8 @@
3.14 private:
3.15 MidiSender* midiSender;
3.16 std::thread keepAliveThread;
3.17 - bool running = false;
3.18 - bool stopped = false;
3.19 + std::atomic<bool> running{false};
3.20 + std::atomic<bool> stopped{false};
3.21
3.22 void run() {
3.23 while (!stopped) {
3.24 @@ -38,6 +40,13 @@
3.25 }
3.26 }
3.27
3.28 + // TODO: remove
3.29 + std::string toString(const MidiMessage& midiMessage) {
3.30 + std::stringstream result;
3.31 + for (uint8_t b : midiMessage) result << std::hex << std::setw(2) << std::setfill('0') << (int) b;
3.32 + return result.str();
3.33 + }
3.34 +
3.35 public:
3.36
3.37 virtual ~DJMFixImpl() override {
3.38 @@ -51,13 +60,22 @@
3.39 }
3.40
3.41 virtual void receive(MidiMessage midiMessage) override {
3.42 - std::cerr << "DJMFixImpl::receive()" << std::endl; // TODO: do not mess STDIO
3.43 + std::cerr << "DJMFixImpl::receive(): size = " << midiMessage.size() << " data = " << toString(midiMessage) << std::endl; // TODO: do not mess STDIO
3.44 +
3.45 + if (midiMessage.size() == 54 && midiMessage[9] == 0x13) {
3.46 + std::cerr << "DJMFixImpl::receive(): got message with HashA and SeedE" << std::endl; // TODO: do not mess STDIO
3.47 + }
3.48 +
3.49 }
3.50
3.51 void start() override {
3.52 std::cerr << "DJMFixImpl::start()" << std::endl; // TODO: do not mess STDIO
3.53 - if (midiSender == nullptr) throw std::logic_error("need a midiSender when starting");
3.54 - midiSender->send({0xf0, 0xf7});
3.55 + if (midiSender == nullptr) throw std::logic_error("need a midiSender when starting DJMFix");
3.56 +
3.57 + // TODO: methods for parsing and constructing messages from parts (TLV)
3.58 + midiSender->send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7});
3.59 + std::this_thread::sleep_for(std::chrono::milliseconds(10));
3.60 + midiSender->send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x12, 0x2a, 0x01, 0x0b, 0x50, 0x69, 0x6f, 0x6e, 0x65, 0x65, 0x72, 0x44, 0x4a, 0x02, 0x0b, 0x72, 0x65, 0x6b, 0x6f, 0x72, 0x64, 0x62, 0x6f, 0x78, 0x03, 0x12, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0xf7});
3.61
3.62 keepAliveThread = std::thread(&DJMFixImpl::run, this);
3.63 running = true;
4.1 --- a/djm-fix.cpp Fri Dec 18 23:58:03 2020 +0100
4.2 +++ b/djm-fix.cpp Sat Dec 19 17:33:16 2020 +0100
4.3 @@ -20,11 +20,12 @@
4.4 #include <chrono>
4.5 #include <thread>
4.6 #include <csignal>
4.7 +#include <atomic>
4.8
4.9 #include "DJMFix.h"
4.10 #include "AlsaBridge.h"
4.11
4.12 -volatile static bool run = true;
4.13 +static std::atomic<bool> run{true};
4.14
4.15 void interrupt(int signal) {
4.16 run = false;
4.17 @@ -32,9 +33,11 @@
4.18 }
4.19
4.20 int main(int argc, char**argv) {
4.21 + std::string deviceName = argc == 2 ? argv[1] : "hw:1"; // FIXME: parse CLI options + automatic device search
4.22 +
4.23 signal(SIGINT, interrupt);
4.24 std::unique_ptr<djmfix::DJMFix> djmFix(djmfix::create());
4.25 - std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(djmfix::alsa::create(djmFix.get()));
4.26 + std::unique_ptr<djmfix::alsa::AlsaBridge> alsaBridge(djmfix::alsa::create(djmFix.get(), deviceName));
4.27
4.28 alsaBridge->start();
4.29 while (run) std::this_thread::sleep_for(std::chrono::milliseconds(100));