/**
* DJM-Fix
* Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <stdexcept>
#include <thread>
#include <mutex>
#include <atomic>
#include <alsa/asoundlib.h>
#include "AlsaBridge.h"
namespace djmfix {
namespace alsa {
class AlsaBridgeImpl : public AlsaBridge, private djmfix::MidiSender {
private:
djmfix::DJMFix* djmFix;
snd_rawmidi_t* input;
snd_rawmidi_t* output;
std::thread receivingThread;
std::recursive_mutex midiMutex;
std::atomic<bool> stopped{false};
void run() {
while (!stopped) {
{
std::lock_guard<std::recursive_mutex> lock(midiMutex);
// 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, 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::lock_guard<std::recursive_mutex> lock(midiMutex);
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, const std::string& deviceName) {
return new AlsaBridgeImpl(djmFix, deviceName);
}
}
}