/* * MIDIMapping.cpp * --------------- * Purpose: MIDI Mapping management classes * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "Moddoc.h" #include "MIDIMapping.h" #include "../common/FileReader.h" #include "../soundlib/MIDIEvents.h" #include "../soundlib/plugins/PlugInterface.h" #include "mpt/io/io.hpp" #include "mpt/io/io_stdstream.hpp" OPENMPT_NAMESPACE_BEGIN size_t CMIDIMapper::Serialize(std::ostream *file) const { //Bytes: 1 Flags, 2 key, 1 plugindex, 1,2,4,8 plug/etc. size_t size = 0; for(const auto &d : m_Directives) { uint16 temp16 = (d.GetChnEvent() << 1) + (d.GetController() << 9); if(d.GetAnyChannel()) temp16 |= 1; uint32 temp32 = d.GetParamIndex(); uint8 temp8 = d.IsActive(); //bit 0 if(d.GetCaptureMIDI()) temp8 |= (1 << 1); //bit 1 //bits 2-4: Mapping type: 0 for plug param control. //bit 5: if(d.GetAllowPatternEdit()) temp8 |= (1 << 5); //bits 6-7: Size: 5, 6, 8, 12 uint8 parambytes = 4; if(temp32 <= uint16_max) { if(temp32 <= uint8_max) parambytes = 1; else {parambytes = 2; temp8 |= (1 << 6);} } else temp8 |= (2 << 6); if(file) { std::ostream & f = *file; mpt::IO::WriteIntLE(f, temp8); mpt::IO::WriteIntLE(f, temp16); mpt::IO::WriteIntLE(f, d.GetPlugIndex()); mpt::IO::WritePartial(f, mpt::as_le(temp32), parambytes); } size += sizeof(temp8) + sizeof(temp16) + sizeof(temp8) + parambytes; } return size; } bool CMIDIMapper::Deserialize(FileReader &file) { m_Directives.clear(); while(file.CanRead(1)) { uint8 i8 = file.ReadUint8(); uint8 psize = 0; // Determine size of this event (depends on size of plugin parameter index) switch(i8 >> 6) { case 0: psize = 4; break; case 1: psize = 5; break; case 2: psize = 7; break; case 3: default: psize = 11; break; } if(!file.CanRead(psize)) return false; if(((i8 >> 2) & 7) != 0) { file.Skip(psize); continue;} //Skipping unrecognised mapping types. CMIDIMappingDirective s; s.SetActive((i8 & 1) != 0); s.SetCaptureMIDI((i8 & (1 << 1)) != 0); s.SetAllowPatternEdit((i8 & (1 << 5)) != 0); uint16 i16 = file.ReadUint16LE(); //Channel, event, MIDIbyte1. i8 = file.ReadUint8(); //Plugindex uint32le i32; file.ReadStructPartial(i32, psize - 3); s.SetChannel(((i16 & 1) != 0) ? 0 : 1 + ((i16 >> 1) & 0xF)); s.SetEvent(static_cast((i16 >> 5) & 0xF)); s.SetController(i16 >> 9); s.SetPlugIndex(i8); s.SetParamIndex(i32); AddDirective(s); } return true; } bool CMIDIMapper::OnMIDImsg(const DWORD midimsg, PLUGINDEX &mappedIndex, PlugParamIndex ¶mindex, uint16 ¶mval) { const MIDIEvents::EventType eventType = MIDIEvents::GetTypeFromEvent(midimsg); const uint8 controller = MIDIEvents::GetDataByte1FromEvent(midimsg); const uint8 channel = MIDIEvents::GetChannelFromEvent(midimsg) & 0x7F; const uint8 controllerVal = MIDIEvents::GetDataByte2FromEvent(midimsg) & 0x7F; for(const auto &d : m_Directives) { if(!d.IsActive()) continue; if(d.GetEvent() != eventType) continue; if(eventType == MIDIEvents::evControllerChange && d.GetController() != controller && (d.GetController() >= 32 || d.GetController() + 32 != controller)) continue; if(!d.GetAnyChannel() && channel + 1 != d.GetChannel()) continue; const PLUGINDEX plugindex = d.GetPlugIndex(); const uint32 param = d.GetParamIndex(); uint16 val = (d.GetEvent() == MIDIEvents::evChannelAftertouch ? controller : controllerVal) << 7; if(eventType == MIDIEvents::evControllerChange) { // Fine (0...31) / Coarse (32...63) controller pairs - Fine should be sent first. if(controller == m_lastCC + 32 && m_lastCC < 32) { val = (val >> 7) | m_lastCCvalue; } m_lastCC = controller; m_lastCCvalue = val; } if(d.GetAllowPatternEdit()) { mappedIndex = plugindex; paramindex = param; paramval = val; } if(plugindex > 0 && plugindex <= MAX_MIXPLUGINS) { #ifndef NO_PLUGINS IMixPlugin *pPlug = m_rSndFile.m_MixPlugins[plugindex - 1].pMixPlugin; if(!pPlug) continue; pPlug->SetParameter(param, val / 16383.0f); if(m_rSndFile.GetpModDoc() != nullptr) m_rSndFile.GetpModDoc()->SetModified(); #endif // NO_PLUGINS } if(d.GetCaptureMIDI()) { return true; } } return false; } void CMIDIMapper::Swap(const size_t a, const size_t b) { if(a < m_Directives.size() && b < m_Directives.size()) { std::swap(m_Directives[a], m_Directives[b]); Sort(); } } OPENMPT_NAMESPACE_END