pianobooster/src/MidiDeviceRt.cpp

332 lines
10 KiB
C++

/*********************************************************************************/
/*!
@file MidiDeviceRt.cpp
@brief MidiDeviceRt talks to the MidiRt Real Time Version.
@author L. J. Barman
Copyright (c) 2008-2013, L. J. Barman, all rights reserved
This file is part of the PianoBooster application
PianoBooster 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, either version 3 of the License, or
(at your option) any later version.
PianoBooster 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 PianoBooster. If not, see <http://www.gnu.org/licenses/>.
*/
/*********************************************************************************/
#include "MidiDeviceRt.h"
#include <limits>
CMidiDeviceRt::CMidiDeviceRt()
{
m_validConnection = false;
m_midiPorts[0] = -1;
m_midiPorts[1] = -1;
m_rawDataIndex = 0;
init();
}
CMidiDeviceRt::~CMidiDeviceRt()
{
}
void CMidiDeviceRt::init()
{
if (m_midiin && m_midiout) {
return;
}
m_midiPorts[0] = -1;
m_midiPorts[1] = -1;
m_rawDataIndex = 0;
try {
m_midiout = std::make_unique<RtMidiOut>();
m_midiout->setErrorCallback(handleRtMidiError);
} catch (const RtMidiError &error) {
error.printMessage();
}
try {
m_midiin = std::make_unique<RtMidiIn>();
m_midiin->setErrorCallback(handleRtMidiError);
} catch (const RtMidiError &error) {
error.printMessage();
}
}
QString CMidiDeviceRt::addIndexToString(const QString &name, int index)
{
QString ret;
QString idx;
idx.setNum(index);
idx.append(" - ");
ret = idx + name;
return ret;
}
QStringList CMidiDeviceRt::getMidiPortList(midiType_t type)
{
init();
auto portNameList = QStringList();
if (!m_midiin || !m_midiout) {
return portNameList;
}
auto *const midiDevice = type == MIDI_INPUT ? static_cast<RtMidi *>(m_midiin.get()) : static_cast<RtMidi *>(m_midiout.get());
const auto nPorts = midiDevice->getPortCount();
portNameList.reserve(nPorts);
for (unsigned int i = 0; i < nPorts && i < std::numeric_limits<int>::max(); ++i) {
// kotechnology creating indexed string from the post name
const auto name = addIndexToString(midiDevice->getPortName(i).c_str(), static_cast<int>(i));
if (!name.contains(QLatin1String("RtMidi Output Client")) && !name.contains(QLatin1String("RtMidi Input Client")))
portNameList << name;
}
return portNameList;
}
bool CMidiDeviceRt::openMidiPort(midiType_t type, const QString &portName)
{
init();
if (!m_midiin || !m_midiout || portName.isEmpty()) {
return false;
}
int dev;
RtMidi *midiDevice;
if (type == MIDI_INPUT) {
midiDevice = m_midiin.get();
dev = 0;
} else {
midiDevice = m_midiout.get();
dev = 1;
}
const auto nPorts = midiDevice->getPortCount();
for (unsigned int i = 0; i < nPorts && i <= std::numeric_limits<int>::max(); i++) {
// kotechnology creating indexed string from the post name
const auto name = addIndexToString(midiDevice->getPortName(i).c_str(), static_cast<int>(i));
if (name == portName) // Test for a match
{
if (m_midiPorts[dev] >= 0)
midiDevice->closePort();
m_midiPorts[dev] = static_cast<int>(i);
m_rawDataIndex = 0;
midiDevice->openPort( i );
m_validConnection = true;
return true;
}
}
return false;
}
void CMidiDeviceRt::closeMidiPort(midiType_t type, int index)
{
Q_UNUSED(index)
m_validConnection = false;
if (type == MIDI_INPUT)
m_midiin->closePort();
else
m_midiout->closePort();
}
//! add a midi event to be played immediately
void CMidiDeviceRt::playMidiEvent(const CMidiEvent & event)
{
if (m_midiPorts[1] < 0)
return;
unsigned int channel;
std::vector<unsigned char> message;
channel = event.channel() & 0x0f;
switch(event.type())
{
case MIDI_NOTE_OFF: // NOTE_OFF
message.push_back(static_cast<unsigned char>(channel | MIDI_NOTE_OFF));
message.push_back(static_cast<unsigned char>(event.note()));
message.push_back(static_cast<unsigned char>(event.velocity()));
break;
case MIDI_NOTE_ON: // NOTE_ON
message.push_back(static_cast<unsigned char>(channel | MIDI_NOTE_ON));
message.push_back(static_cast<unsigned char>(event.note()));
message.push_back(static_cast<unsigned char>(event.velocity()));
break;
case MIDI_NOTE_PRESSURE: //POLY_AFTERTOUCH: 3 bytes
message.push_back(static_cast<unsigned char>(channel | MIDI_NOTE_PRESSURE));
message.push_back(static_cast<unsigned char>(event.data1()));
message.push_back(static_cast<unsigned char>(event.data2()));
break;
case MIDI_CONTROL_CHANGE: //CONTROL_CHANGE:
message.push_back(static_cast<unsigned char>(channel | MIDI_CONTROL_CHANGE));
message.push_back(static_cast<unsigned char>(event.data1()));
message.push_back(static_cast<unsigned char>(event.data2()));
break;
case MIDI_PROGRAM_CHANGE: //PROGRAM_CHANGE:
message.push_back(static_cast<unsigned char>(channel | MIDI_PROGRAM_CHANGE));
message.push_back(static_cast<unsigned char>(event.programme()));
break;
case MIDI_CHANNEL_PRESSURE: //AFTERTOUCH: 2 bytes only
message.push_back(static_cast<unsigned char>(channel | MIDI_CHANNEL_PRESSURE));
message.push_back(static_cast<unsigned char>(event.data1()));
break;
case MIDI_PITCH_BEND: //PITCH_BEND:
message.push_back(static_cast<unsigned char>(channel | MIDI_PITCH_BEND));
message.push_back(static_cast<unsigned char>(event.data1()));
message.push_back(static_cast<unsigned char>(event.data2()));
break;
case MIDI_PB_collateRawMidiData: //used for a SYSTEM_EVENT
if (m_rawDataIndex < arraySizeAs<unsigned int>(m_savedRawBytes))
m_savedRawBytes[m_rawDataIndex++] = static_cast<unsigned char>(event.data1());
return; // Don't output any thing yet so just return
case MIDI_PB_outputRawMidiData: //used for a SYSTEM_EVENT
for (size_t i = 0; i < m_rawDataIndex; i++)
message.push_back( m_savedRawBytes[i]);
m_rawDataIndex = 0;
break;
default:
return;
}
m_midiout->sendMessage(&message);
//event.printDetails(); // useful for debugging
}
// Return the number of events waiting to be read from the midi device
int CMidiDeviceRt::checkMidiInput()
{
if (m_midiPorts[0] < 0)
return 0;
m_stamp = m_midiin->getMessage( &m_inputMessage );
if (!m_validConnection)
return 0;
return m_inputMessage.size() > std::numeric_limits<int>::max() ? std::numeric_limits<int>::max() : static_cast<int>(m_inputMessage.size());
}
// reads the real midi event
CMidiEvent CMidiDeviceRt::readMidiInput()
{
CMidiEvent midiEvent;
if (Cfg::midiInputDump)
{
QString str;
for (unsigned int i = 0; i < m_inputMessage.size(); i++)
str += " 0x" + QString::number(m_inputMessage[i], 16) + ',';
ppLogInfo("midi input %f : %s", m_stamp, qPrintable(str));
}
int channel = m_inputMessage[0] & 0x0f;
switch (m_inputMessage[0] & 0xf0 )
{
case MIDI_NOTE_ON:
if (m_inputMessage[2] != 0 )
midiEvent.noteOnEvent(0, channel, m_inputMessage[1], m_inputMessage[2]);
else
midiEvent.noteOffEvent(0,channel, m_inputMessage[1], m_inputMessage[2]);
break;
case MIDI_NOTE_OFF:
midiEvent.noteOffEvent(0, channel, m_inputMessage[1], m_inputMessage[2]);
break;
case MIDI_NOTE_PRESSURE: //MIDI_CMD_NOTE_PRESSURE: //POLY_AFTERTOUCH:
midiEvent.notePressure(0, channel, m_inputMessage[1], m_inputMessage[2]);
break;
case MIDI_CONTROL_CHANGE: //CONTROL_CHANGE:
midiEvent.controlChangeEvent(0, channel, m_inputMessage[1], m_inputMessage[2]);
break;
case MIDI_PROGRAM_CHANGE: //PROGRAM_CHANGE:
midiEvent.programChangeEvent(0, channel, m_inputMessage[1]);
break;
case MIDI_CHANNEL_PRESSURE: //AFTERTOUCH:
midiEvent.channelPressure(0, channel, m_inputMessage[1]);
break;
case MIDI_PITCH_BEND: //PITCH_BEND:
midiEvent.pitchBendEvent(0, channel, m_inputMessage[1], m_inputMessage[2]);
break;
}
m_inputMessage.clear();
return midiEvent;
}
int CMidiDeviceRt::midiSettingsSetStr(const QString &name, const QString &str)
{
Q_UNUSED(name)
Q_UNUSED(str)
return 0;
}
int CMidiDeviceRt::midiSettingsSetNum(const QString &name, double val)
{
Q_UNUSED(name)
Q_UNUSED(val)
return 0;
}
int CMidiDeviceRt::midiSettingsSetInt(const QString &name, int val)
{
Q_UNUSED(name)
Q_UNUSED(val)
return 0;
}
QString CMidiDeviceRt::midiSettingsGetStr(const QString &name)
{
Q_UNUSED(name)
return QString();
}
double CMidiDeviceRt::midiSettingsGetNum(const QString &name)
{
Q_UNUSED(name)
return 0.0;
}
int CMidiDeviceRt::midiSettingsGetInt(const QString &name)
{
Q_UNUSED(name)
return 0;
}
void CMidiDeviceRt::handleRtMidiError(RtMidiError::Type type, const std::string &errorText, void *userData)
{
static_cast<CMidiDeviceRt *>(userData)->handleRtMidiError(type, errorText);
}
void CMidiDeviceRt::handleRtMidiError(RtMidiError::Type type, const std::string &errorText)
{
std::cerr
<< (type == RtMidiError::WARNING || type == RtMidiError::DEBUG_WARNING ? "MIDI warning: " : "MIDI error: ")
<< errorText << '\n';
m_validConnection = false;
}