/* * MIDIMacros.h * ------------ * Purpose: Helper functions / classes for MIDI Macro functionality. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "openmpt/all/BuildSettings.hpp" #include "openmpt/base/Endian.hpp" OPENMPT_NAMESPACE_BEGIN enum { kGlobalMacros = 9, // Number of global macros kSFxMacros = 16, // Number of parametered macros kZxxMacros = 128, // Number of fixed macros kMacroLength = 32, // Max number of chars per macro }; OPENMPT_NAMESPACE_END #ifdef MODPLUG_TRACKER #include "plugins/PluginStructs.h" #endif // MODPLUG_TRACKER OPENMPT_NAMESPACE_BEGIN // Parametered macro presets enum ParameteredMacro { kSFxUnused = 0, kSFxCutoff, // Z00 - Z7F controls resonant filter cutoff kSFxReso, // Z00 - Z7F controls resonant filter resonance kSFxFltMode, // Z00 - Z7F controls resonant filter mode (lowpass / highpass) kSFxDryWet, // Z00 - Z7F controls plugin Dry / Wet ratio kSFxPlugParam, // Z00 - Z7F controls a plugin parameter kSFxCC, // Z00 - Z7F controls MIDI CC kSFxChannelAT, // Z00 - Z7F controls Channel Aftertouch kSFxPolyAT, // Z00 - Z7F controls Poly Aftertouch kSFxPitch, // Z00 - Z7F controls Pitch Bend kSFxProgChange, // Z00 - Z7F controls MIDI Program Change kSFxCustom, kSFxMax }; // Fixed macro presets enum FixedMacro { kZxxUnused = 0, kZxxReso4Bit, // Z80 - Z8F controls resonant filter resonance kZxxReso7Bit, // Z80 - ZFF controls resonant filter resonance kZxxCutoff, // Z80 - ZFF controls resonant filter cutoff kZxxFltMode, // Z80 - ZFF controls resonant filter mode (lowpass / highpass) kZxxResoFltMode, // Z80 - Z9F controls resonance + filter mode kZxxChannelAT, // Z80 - ZFF controls Channel Aftertouch kZxxPolyAT, // Z80 - ZFF controls Poly Aftertouch kZxxPitch, // Z80 - ZFF controls Pitch Bend kZxxProgChange, // Z80 - ZFF controls MIDI Program Change kZxxCustom, kZxxMax }; // Global macro types enum GlobalMacro { MIDIOUT_START = 0, MIDIOUT_STOP, MIDIOUT_TICK, MIDIOUT_NOTEON, MIDIOUT_NOTEOFF, MIDIOUT_VOLUME, MIDIOUT_PAN, MIDIOUT_BANKSEL, MIDIOUT_PROGRAM, }; struct MIDIMacroConfigData { struct Macro { public: Macro &operator=(const Macro &other) = default; Macro &operator=(const std::string_view other) noexcept { const size_t copyLength = std::min({m_data.size() - 1u, other.size(), other.find('\0')}); std::copy(other.begin(), other.begin() + copyLength, m_data.begin()); m_data[copyLength] = '\0'; Sanitize(); return *this; } bool operator==(const Macro &other) const noexcept { return m_data == other.m_data; // Don't care about data past null-terminator as operator= and Sanitize() ensure there is no data behind it. } bool operator!=(const Macro &other) const noexcept { return !(*this == other); } operator mpt::span() const noexcept { return {m_data.data(), Length()}; } operator std::string_view() const noexcept { return {m_data.data(), Length()}; } operator std::string() const { return {m_data.data(), Length()}; } MPT_CONSTEXPR20_FUN size_t Length() const noexcept { return static_cast(std::distance(m_data.begin(), std::find(m_data.begin(), m_data.end(), '\0'))); } MPT_CONSTEXPR20_FUN void Clear() noexcept { m_data.fill('\0'); } // Remove blanks and other unwanted characters from macro strings for internal usage. std::string NormalizedString() const; void Sanitize() noexcept; void UpgradeLegacyMacro() noexcept; private: std::array m_data; }; std::array Global; std::array SFx; // Parametered macros for Z00...Z7F std::array Zxx; // Fixed macros Z80...ZFF constexpr Macro *begin() noexcept {return Global.data(); } constexpr const Macro *begin() const noexcept { return Global.data(); } constexpr Macro *end() noexcept { return Zxx.data() + Zxx.size(); } constexpr const Macro *end() const noexcept { return Zxx.data() + Zxx.size(); } }; // This is directly written to files, so the size must be correct! MPT_BINARY_STRUCT(MIDIMacroConfigData::Macro, 32) MPT_BINARY_STRUCT(MIDIMacroConfigData, 4896) class MIDIMacroConfig : public MIDIMacroConfigData { public: MIDIMacroConfig() { Reset(); } // Get macro type from a macro string ParameteredMacro GetParameteredMacroType(uint32 macroIndex) const; FixedMacro GetFixedMacroType() const; // Create a new macro protected: void CreateParameteredMacro(Macro ¶meteredMacro, ParameteredMacro macroType, int subType) const; public: void CreateParameteredMacro(uint32 macroIndex, ParameteredMacro macroType, int subType = 0) { if(macroIndex < std::size(SFx)) CreateParameteredMacro(SFx[macroIndex], macroType, subType); } std::string CreateParameteredMacro(ParameteredMacro macroType, int subType = 0) const; protected: void CreateFixedMacro(std::array &fixedMacros, FixedMacro macroType) const; public: void CreateFixedMacro(FixedMacro macroType) { CreateFixedMacro(Zxx, macroType); } bool operator==(const MIDIMacroConfig &other) const; bool operator!=(const MIDIMacroConfig &other) const { return !(*this == other); } #ifdef MODPLUG_TRACKER // Translate macro type or macro string to macro name CString GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin = nullptr) const; CString GetParameteredMacroName(ParameteredMacro macroType) const; CString GetFixedMacroName(FixedMacro macroType) const; // Extract information from a parametered macro string. PlugParamIndex MacroToPlugParam(uint32 macroIndex) const; int MacroToMidiCC(uint32 macroIndex) const; // Check if any macro can automate a given plugin parameter int FindMacroForParam(PlugParamIndex param) const; #endif // MODPLUG_TRACKER // Check if a given set of macros is the default IT macro set. bool IsMacroDefaultSetupUsed() const; // Reset MIDI macro config to default values. void Reset(); // Clear all Zxx macros so that they do nothing. void ClearZxxMacros(); // Sanitize all macro config strings. void Sanitize(); // Fix old-format (not conforming to IT's MIDI macro definitions) MIDI config strings. void UpgradeMacros(); }; static_assert(sizeof(MIDIMacroConfig) == sizeof(MIDIMacroConfigData)); // this is directly written to files, so the size must be correct! OPENMPT_NAMESPACE_END