2016-05-20 06:16:07 +00:00
|
|
|
#include "boo/audiodev/MIDIDecoder.hpp"
|
2019-08-19 23:08:54 +00:00
|
|
|
|
2016-05-21 03:22:00 +00:00
|
|
|
#include <algorithm>
|
2019-08-25 02:44:31 +00:00
|
|
|
#include <optional>
|
2016-05-20 06:16:07 +00:00
|
|
|
|
2019-08-19 23:08:54 +00:00
|
|
|
#include "boo/audiodev/IMIDIReader.hpp"
|
|
|
|
#include "lib/audiodev/MIDICommon.hpp"
|
|
|
|
|
|
|
|
|
2018-12-08 05:17:51 +00:00
|
|
|
namespace boo {
|
2019-08-25 02:39:10 +00:00
|
|
|
namespace {
|
2019-07-20 04:22:36 +00:00
|
|
|
constexpr uint8_t clamp7(uint8_t val) { return std::max(0, std::min(127, int(val))); }
|
2016-05-20 06:16:07 +00:00
|
|
|
|
2019-08-25 02:44:31 +00:00
|
|
|
std::optional<uint32_t> readContinuedValue(std::vector<uint8_t>::const_iterator& it,
|
|
|
|
std::vector<uint8_t>::const_iterator end) {
|
2018-12-08 05:17:51 +00:00
|
|
|
uint8_t a = *it++;
|
2019-08-25 02:44:31 +00:00
|
|
|
uint32_t valOut = a & 0x7f;
|
2016-05-20 06:16:07 +00:00
|
|
|
|
2019-08-25 02:44:31 +00:00
|
|
|
if ((a & 0x80) != 0) {
|
|
|
|
if (it == end) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2018-12-08 05:17:51 +00:00
|
|
|
valOut <<= 7;
|
|
|
|
a = *it++;
|
|
|
|
valOut |= a & 0x7f;
|
2016-05-20 06:16:07 +00:00
|
|
|
|
2019-08-25 02:44:31 +00:00
|
|
|
if ((a & 0x80) != 0) {
|
|
|
|
if (it == end) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2018-12-08 05:17:51 +00:00
|
|
|
valOut <<= 7;
|
|
|
|
a = *it++;
|
|
|
|
valOut |= a & 0x7f;
|
2016-05-20 06:16:07 +00:00
|
|
|
}
|
2018-12-08 05:17:51 +00:00
|
|
|
}
|
2016-05-20 06:16:07 +00:00
|
|
|
|
2019-08-25 02:44:31 +00:00
|
|
|
return valOut;
|
2016-05-20 06:16:07 +00:00
|
|
|
}
|
2019-08-25 02:39:10 +00:00
|
|
|
} // Anonymous namespace
|
2016-05-20 06:16:07 +00:00
|
|
|
|
2018-12-08 05:17:51 +00:00
|
|
|
std::vector<uint8_t>::const_iterator MIDIDecoder::receiveBytes(std::vector<uint8_t>::const_iterator begin,
|
|
|
|
std::vector<uint8_t>::const_iterator end) {
|
|
|
|
std::vector<uint8_t>::const_iterator it = begin;
|
|
|
|
while (it != end) {
|
|
|
|
uint8_t a = *it++;
|
|
|
|
uint8_t b;
|
|
|
|
if (a & 0x80)
|
|
|
|
m_status = a;
|
|
|
|
else
|
|
|
|
it--;
|
2016-06-22 21:44:37 +00:00
|
|
|
|
2018-12-08 05:17:51 +00:00
|
|
|
if (m_status == 0xff) {
|
|
|
|
/* Meta events (ignored for now) */
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
2018-08-25 08:37:26 +00:00
|
|
|
|
2019-08-25 02:44:31 +00:00
|
|
|
const auto length = readContinuedValue(it, end);
|
|
|
|
it += *length;
|
2018-12-08 05:17:51 +00:00
|
|
|
} else {
|
|
|
|
uint8_t chan = m_status & 0xf;
|
|
|
|
switch (Status(m_status & 0xf0)) {
|
|
|
|
case Status::NoteOff: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
b = *it++;
|
|
|
|
m_out.noteOff(chan, clamp7(a), clamp7(b));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::NoteOn: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
b = *it++;
|
|
|
|
m_out.noteOn(chan, clamp7(a), clamp7(b));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::NotePressure: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
b = *it++;
|
|
|
|
m_out.notePressure(chan, clamp7(a), clamp7(b));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::ControlChange: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
b = *it++;
|
|
|
|
m_out.controlChange(chan, clamp7(a), clamp7(b));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::ProgramChange: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
m_out.programChange(chan, clamp7(a));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::ChannelPressure: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
m_out.channelPressure(chan, clamp7(a));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::PitchBend: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
b = *it++;
|
|
|
|
m_out.pitchBend(chan, clamp7(b) * 128 + clamp7(a));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::SysEx: {
|
|
|
|
switch (Status(m_status & 0xff)) {
|
|
|
|
case Status::SysEx: {
|
2019-08-25 02:44:31 +00:00
|
|
|
const auto len = readContinuedValue(it, end);
|
|
|
|
if (!len || end - it < *len) {
|
2018-12-08 05:17:51 +00:00
|
|
|
return begin;
|
2019-08-25 02:44:31 +00:00
|
|
|
}
|
|
|
|
m_out.sysex(&*it, *len);
|
2018-12-08 05:17:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::TimecodeQuarterFrame: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
m_out.timeCodeQuarterFrame(a >> 4 & 0x7, a & 0xf);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::SongPositionPointer: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
b = *it++;
|
|
|
|
m_out.songPositionPointer(clamp7(b) * 128 + clamp7(a));
|
|
|
|
break;
|
2016-05-20 06:16:07 +00:00
|
|
|
}
|
2018-12-08 05:17:51 +00:00
|
|
|
case Status::SongSelect: {
|
|
|
|
if (it == end)
|
|
|
|
return begin;
|
|
|
|
a = *it++;
|
|
|
|
m_out.songSelect(clamp7(a));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Status::TuneRequest:
|
|
|
|
m_out.tuneRequest();
|
|
|
|
break;
|
|
|
|
case Status::Start:
|
|
|
|
m_out.startSeq();
|
|
|
|
break;
|
|
|
|
case Status::Continue:
|
|
|
|
m_out.continueSeq();
|
|
|
|
break;
|
|
|
|
case Status::Stop:
|
|
|
|
m_out.stopSeq();
|
|
|
|
break;
|
|
|
|
case Status::Reset:
|
|
|
|
m_out.reset();
|
|
|
|
break;
|
|
|
|
case Status::SysExTerm:
|
|
|
|
case Status::TimingClock:
|
|
|
|
case Status::ActiveSensing:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2016-05-20 06:16:07 +00:00
|
|
|
}
|
2018-12-08 05:17:51 +00:00
|
|
|
}
|
|
|
|
return it;
|
2016-05-20 06:16:07 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:17:51 +00:00
|
|
|
} // namespace boo
|