# HG changeset patch # User František Kučera # Date 1608395596 -3600 # Node ID ef8f4023e32e9f1d5f04faf44b602bda39037734 # Parent 4d777d6c80249b9bc6c5f94b063a6e874d19fe91 sending and receiving MIDI messages through ALSA (the dirty way) diff -r 4d777d6c8024 -r ef8f4023e32e AlsaBridge.cpp --- a/AlsaBridge.cpp Fri Dec 18 23:58:03 2020 +0100 +++ b/AlsaBridge.cpp Sat Dec 19 17:33:16 2020 +0100 @@ -15,6 +15,9 @@ * along with this program. If not, see . */ #include +#include +#include +#include #include @@ -26,32 +29,63 @@ class AlsaBridgeImpl : public AlsaBridge, private djmfix::MidiSender { private: djmfix::DJMFix* djmFix; + snd_rawmidi_t* input; + snd_rawmidi_t* output; + std::thread receivingThread; + std::atomic stopped{false}; + + void run() { + while (!stopped) { + // TODO: poll + uint8_t buffer[256]; + ssize_t length = snd_rawmidi_read(input, buffer, sizeof (buffer)); + if (length > 0 && length <= sizeof (buffer)) { + // TODO: multiple messages combined together? + djmFix->receive(MidiMessage(buffer, buffer + length)); + } + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } public: - AlsaBridgeImpl(djmfix::DJMFix* djmFix) : djmFix(djmFix) { + AlsaBridgeImpl(djmfix::DJMFix* djmFix, const std::string& deviceName) : djmFix(djmFix) { + if (djmFix == nullptr) throw std::invalid_argument("need a djmFix for AlsaBridge"); + + int error = snd_rawmidi_open(&input, &output, deviceName.c_str(), SND_RAWMIDI_NONBLOCK); + if (error) throw std::invalid_argument("unable to open ALSA device"); + + djmFix->setMidiSender(this); } virtual ~AlsaBridgeImpl() { + // TODO: do not use raw/exclusive access to the device + snd_rawmidi_close(input); + snd_rawmidi_close(output); std::cerr << "~AlsaBridgeImpl()" << std::endl; // TODO: do not mess STDIO } virtual void start() override { djmFix->start(); + receivingThread = std::thread(&AlsaBridgeImpl::run, this); } virtual void stop() override { + stopped = true; + receivingThread.join(); djmFix->stop(); } virtual void send(MidiMessage midiMessage) override { - std::cerr << "AlsaBridgeImpl::send()" << std::endl; // TODO: do not mess STDIO + ssize_t length = snd_rawmidi_write(output, midiMessage.data(), midiMessage.size()); + std::cerr << "AlsaBridgeImpl::send(): length = " << length << std::endl; // TODO: do not mess STDIO } }; -AlsaBridge* create(djmfix::DJMFix* djmFix) { - return new AlsaBridgeImpl(djmFix); +AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName) { + return new AlsaBridgeImpl(djmFix, deviceName); } } diff -r 4d777d6c8024 -r ef8f4023e32e AlsaBridge.h --- a/AlsaBridge.h Fri Dec 18 23:58:03 2020 +0100 +++ b/AlsaBridge.h Sat Dec 19 17:33:16 2020 +0100 @@ -16,6 +16,8 @@ */ #pragma once +#include + #include "DJMFix.h" namespace djmfix { @@ -29,7 +31,7 @@ }; -AlsaBridge* create(djmfix::DJMFix* djmFix); +AlsaBridge* create(djmfix::DJMFix* djmFix, const std::string& deviceName); } } diff -r 4d777d6c8024 -r ef8f4023e32e DJMFix.cpp --- a/DJMFix.cpp Fri Dec 18 23:58:03 2020 +0100 +++ b/DJMFix.cpp Sat Dec 19 17:33:16 2020 +0100 @@ -15,7 +15,9 @@ * along with this program. If not, see . */ #include +#include #include +#include #include #include @@ -27,8 +29,8 @@ private: MidiSender* midiSender; std::thread keepAliveThread; - bool running = false; - bool stopped = false; + std::atomic running{false}; + std::atomic stopped{false}; void run() { while (!stopped) { @@ -38,6 +40,13 @@ } } + // TODO: remove + std::string toString(const MidiMessage& midiMessage) { + std::stringstream result; + for (uint8_t b : midiMessage) result << std::hex << std::setw(2) << std::setfill('0') << (int) b; + return result.str(); + } + public: virtual ~DJMFixImpl() override { @@ -51,13 +60,22 @@ } virtual void receive(MidiMessage midiMessage) override { - std::cerr << "DJMFixImpl::receive()" << std::endl; // TODO: do not mess STDIO + std::cerr << "DJMFixImpl::receive(): size = " << midiMessage.size() << " data = " << toString(midiMessage) << std::endl; // TODO: do not mess STDIO + + if (midiMessage.size() == 54 && midiMessage[9] == 0x13) { + std::cerr << "DJMFixImpl::receive(): got message with HashA and SeedE" << std::endl; // TODO: do not mess STDIO + } + } void start() override { std::cerr << "DJMFixImpl::start()" << std::endl; // TODO: do not mess STDIO - if (midiSender == nullptr) throw std::logic_error("need a midiSender when starting"); - midiSender->send({0xf0, 0xf7}); + if (midiSender == nullptr) throw std::logic_error("need a midiSender when starting DJMFix"); + + // TODO: methods for parsing and constructing messages from parts (TLV) + midiSender->send({0xf0, 0x00, 0x40, 0x05, 0x00, 0x00, 0x00, 0x17, 0x00, 0x50, 0x01, 0xf7}); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + 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}); keepAliveThread = std::thread(&DJMFixImpl::run, this); running = true; diff -r 4d777d6c8024 -r ef8f4023e32e djm-fix.cpp --- a/djm-fix.cpp Fri Dec 18 23:58:03 2020 +0100 +++ b/djm-fix.cpp Sat Dec 19 17:33:16 2020 +0100 @@ -20,11 +20,12 @@ #include #include #include +#include #include "DJMFix.h" #include "AlsaBridge.h" -volatile static bool run = true; +static std::atomic run{true}; void interrupt(int signal) { run = false; @@ -32,9 +33,11 @@ } int main(int argc, char**argv) { + std::string deviceName = argc == 2 ? argv[1] : "hw:1"; // FIXME: parse CLI options + automatic device search + signal(SIGINT, interrupt); std::unique_ptr djmFix(djmfix::create()); - std::unique_ptr alsaBridge(djmfix::alsa::create(djmFix.get())); + std::unique_ptr alsaBridge(djmfix::alsa::create(djmFix.get(), deviceName)); alsaBridge->start(); while (run) std::this_thread::sleep_for(std::chrono::milliseconds(100));