From b1c73bcc9daa23258355e2e4e9c39c335276d440 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Tue, 13 Oct 2020 21:58:59 +0200 Subject: [PATCH] Refactor writing MIDI events to buffers --- include/MidiEventToByteSeq.h | 45 +++++++++++ plugins/carlabase/carla.cpp | 67 +---------------- src/core/CMakeLists.txt | 1 + src/core/lv2/Lv2Proc.cpp | 82 +------------------- src/core/midi/MidiEventToByteSeq.cpp | 107 +++++++++++++++++++++++++++ 5 files changed, 160 insertions(+), 142 deletions(-) create mode 100644 include/MidiEventToByteSeq.h create mode 100644 src/core/midi/MidiEventToByteSeq.cpp diff --git a/include/MidiEventToByteSeq.h b/include/MidiEventToByteSeq.h new file mode 100644 index 00000000000..65461765620 --- /dev/null +++ b/include/MidiEventToByteSeq.h @@ -0,0 +1,45 @@ +/* + * MidiEventToByteSeq.h - writeToByteSeq declaration + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * This file is part of LMMS - https://lmms.io + * + * 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; either + * version 2 of the License, or (at your option) any later version. + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef MIDIEVENTTOBYTESEQ_H +#define MIDIEVENTTOBYTESEQ_H + +#include +#include + +/** + Write MIDI event into byte sequence. + + Conforming to http://lv2plug.in/ns/ext/midi#MidiEvent + + @param data Pointer to the target buffer for the byte sequence. Must + point to existing memory with at least 3 bytes size. + @param bufsize Available size of the target buffer. + @return Used size of the target buffer, or 0 if the MidiEvent could not + be converted. +*/ +std::size_t writeToByteSeq( const class MidiEvent& ev, + uint8_t* data, std::size_t bufsize ); + +#endif // MIDIEVENTTOBYTESEQ_H diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index a6faa4d275d..f437431271c 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -29,6 +29,7 @@ #include "gui_templates.h" #include "InstrumentPlayHandle.h" #include "InstrumentTrack.h" +#include "MidiEventToByteSeq.h" #include "Mixer.h" #include @@ -372,69 +373,9 @@ bool CarlaInstrument::handleMidiEvent(const MidiEvent& event, const MidiTime&, f nEvent.port = 0; nEvent.time = offset; - nEvent.data[0] = event.type() | (event.channel() & 0x0F); - - switch (event.type()) - { - case MidiNoteOn: - if (event.velocity() > 0) - { - if (event.key() < 0 || event.key() > MidiMaxKey) - break; - - nEvent.data[1] = event.key(); - nEvent.data[2] = event.velocity(); - nEvent.size = 3; - break; - } - else - { - nEvent.data[0] = MidiNoteOff | (event.channel() & 0x0F); - // nobreak - } - - case MidiNoteOff: - if (event.key() < 0 || event.key() > MidiMaxKey) - break; - - nEvent.data[1] = event.key(); - nEvent.data[2] = event.velocity(); - nEvent.size = 3; - break; - - case MidiKeyPressure: - nEvent.data[1] = event.key(); - nEvent.data[2] = event.velocity(); - nEvent.size = 3; - break; - - case MidiControlChange: - nEvent.data[1] = event.controllerNumber(); - nEvent.data[2] = event.controllerValue(); - nEvent.size = 3; - break; - - case MidiProgramChange: - nEvent.data[1] = event.program(); - nEvent.size = 2; - break; - - case MidiChannelPressure: - nEvent.data[1] = event.channelPressure(); - nEvent.size = 2; - break; - - case MidiPitchBend: - nEvent.data[1] = event.pitchBend() & 0x7f; - nEvent.data[2] = event.pitchBend() >> 7; - nEvent.size = 3; - break; - - default: - // unhandled - --fMidiEventCount; - break; - } + std::size_t written = writeToByteSeq(event, nEvent.data, sizeof(NativeMidiEvent::data)); + if(written) { nEvent.size = written; } + else { --fMidiEventCount; } return true; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a1585b20e9c..22bb1195103 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -106,6 +106,7 @@ set(LMMS_SRCS core/midi/MidiAlsaSeq.cpp core/midi/MidiClient.cpp core/midi/MidiController.cpp + core/midi/MidiEventToByteSeq.cpp core/midi/MidiJack.cpp core/midi/MidiOss.cpp core/midi/MidiSndio.cpp diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index bb7f1d0b4d7..b7cfc27d62c 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -40,6 +40,7 @@ #include "Lv2Manager.h" #include "Lv2Ports.h" #include "Lv2Evbuf.h" +#include "MidiEventToByteSeq.h" #include "Mixer.h" @@ -164,83 +165,6 @@ void Lv2Proc::dumpPorts() -// this is almost completely copied from plugins/carlabase/carla.cpp -static std::size_t midiInputEventToData(const MidiEvent& event, uint8_t* data) -{ - std::size_t size = 0; - data[0] = event.type() | (event.channel() & 0x0F); - - switch (event.type()) - { - case MidiNoteOn: - if (event.velocity() > 0) - { - if (event.key() < 0 || event.key() > MidiMaxKey) - break; - - data[1] = event.key(); - data[2] = event.velocity(); - size = 3; - break; - } - else - { - // Lv2 MIDI specs: - // "Note On messages with velocity 0 are not allowed. - // These messages are equivalent to Note Off in standard - // MIDI streams, but here only proper Note Off messages - // are allowed." - data[0] = MidiNoteOff | (event.channel() & 0x0F); - // nobreak - } - - case MidiNoteOff: - if (event.key() < 0 || event.key() > MidiMaxKey) - break; - data[1] = event.key(); - data[2] = event.velocity(); // release time - size = 3; - break; - - case MidiKeyPressure: - data[1] = event.key(); - data[2] = event.velocity(); - size = 3; - break; - - case MidiControlChange: - data[1] = event.controllerNumber(); - data[2] = event.controllerValue(); - size = 3; - break; - - case MidiProgramChange: - data[1] = event.program(); - size = 2; - break; - - case MidiChannelPressure: - data[1] = event.channelPressure(); - size = 2; - break; - - case MidiPitchBend: - data[1] = event.pitchBend() & 0x7f; - data[2] = event.pitchBend() >> 7; - size = 3; - break; - - default: - // unhandled - break; - } - - return size; -} - - - - void Lv2Proc::copyModelsFromCore() { struct FloatFromModelVisitor : public ConstModelVisitor @@ -302,13 +226,13 @@ void Lv2Proc::copyModelsFromCore() // MIDI events waiting to go to the plugin? while(m_midiInputReader.read_space() > 0) { - MidiInputEvent ev = m_midiInputReader.read(1)[0]; + const MidiInputEvent ev = m_midiInputReader.read(1)[0]; uint32_t atomStamp = ev.time.frames(Engine::framesPerTick()) + ev.offset; uint32_t type = Engine::getLv2Manager()-> uridCache()[Lv2UridCache::Id::midi_MidiEvent]; uint8_t buf[4]; - std::size_t bufsize = midiInputEventToData(ev.ev, buf); + std::size_t bufsize = writeToByteSeq(ev.ev, buf, sizeof(buf)); if(bufsize) { lv2_evbuf_write(&iter, atomStamp, type, bufsize, buf); diff --git a/src/core/midi/MidiEventToByteSeq.cpp b/src/core/midi/MidiEventToByteSeq.cpp new file mode 100644 index 00000000000..98f0541d032 --- /dev/null +++ b/src/core/midi/MidiEventToByteSeq.cpp @@ -0,0 +1,107 @@ +/* + * MidiEventToByteSeq.cpp - writeToByteSeq implementation + * + * Copyright (c) 2020-2020 Johannes Lorenz + * + * This file is part of LMMS - https://lmms.io + * + * 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; either + * version 2 of the License, or (at your option) any later version. + * + * 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 (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "MidiEventToByteSeq.h" + +#include + +#include "MidiEvent.h" + + +std::size_t writeToByteSeq( + const MidiEvent& ev, uint8_t *data, std::size_t bufsize) +{ + Q_ASSERT(bufsize >= 3); + + std::size_t size = 0; + data[0] = ev.type() | (ev.channel() & 0x0F); + + switch (ev.type()) + { + case MidiNoteOn: + if (ev.velocity() > 0) + { + if (ev.key() < 0 || ev.key() > MidiMaxKey) + break; + + data[1] = ev.key(); + data[2] = ev.velocity(); + size = 3; + break; + } + else + { + // Lv2 MIDI specs: + // "Note On messages with velocity 0 are not allowed. + // These messages are equivalent to Note Off in standard + // MIDI streams, but here only proper Note Off messages + // are allowed." + data[0] = MidiNoteOff | (ev.channel() & 0x0F); + // nobreak + } + + case MidiNoteOff: + if (ev.key() < 0 || ev.key() > MidiMaxKey) + break; + data[1] = ev.key(); + data[2] = ev.velocity(); // release time + size = 3; + break; + + case MidiKeyPressure: + data[1] = ev.key(); + data[2] = ev.velocity(); + size = 3; + break; + + case MidiControlChange: + data[1] = ev.controllerNumber(); + data[2] = ev.controllerValue(); + size = 3; + break; + + case MidiProgramChange: + data[1] = ev.program(); + size = 2; + break; + + case MidiChannelPressure: + data[1] = ev.channelPressure(); + size = 2; + break; + + case MidiPitchBend: + data[1] = ev.pitchBend() & 0x7f; + data[2] = ev.pitchBend() >> 7; + size = 3; + break; + + default: + // unhandled + break; + } + + return size; +} +