1
0

MIDIMacros.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. * MIDIMacros.cpp
  3. * --------------
  4. * Purpose: Helper functions / classes for MIDI Macro functionality.
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "MIDIMacros.h"
  11. #include "../soundlib/MIDIEvents.h"
  12. #ifdef MODPLUG_TRACKER
  13. #include "Sndfile.h"
  14. #include "plugins/PlugInterface.h"
  15. #endif // MODPLUG_TRACKER
  16. OPENMPT_NAMESPACE_BEGIN
  17. ParameteredMacro MIDIMacroConfig::GetParameteredMacroType(uint32 macroIndex) const
  18. {
  19. const std::string macro = SFx[macroIndex].NormalizedString();
  20. for(uint32 i = 0; i < kSFxMax; i++)
  21. {
  22. ParameteredMacro sfx = static_cast<ParameteredMacro>(i);
  23. if(sfx != kSFxCustom)
  24. {
  25. if(macro == CreateParameteredMacro(sfx))
  26. return sfx;
  27. }
  28. }
  29. // Special macros with additional "parameter":
  30. if(macro.size() == 5 && macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_start)) >= 0 && macro.compare(CreateParameteredMacro(kSFxCC, MIDIEvents::MIDICC_end)) <= 0)
  31. return kSFxCC;
  32. if(macro.size() == 7 && macro.compare(CreateParameteredMacro(kSFxPlugParam, 0)) >= 0 && macro.compare(CreateParameteredMacro(kSFxPlugParam, 0x17F)) <= 0)
  33. return kSFxPlugParam;
  34. return kSFxCustom; // custom / unknown
  35. }
  36. // Retrieve Zxx (Z80-ZFF) type from current macro configuration
  37. FixedMacro MIDIMacroConfig::GetFixedMacroType() const
  38. {
  39. // Compare with all possible preset patterns
  40. for(uint32 i = 0; i < kZxxMax; i++)
  41. {
  42. FixedMacro zxx = static_cast<FixedMacro>(i);
  43. if(zxx != kZxxCustom)
  44. {
  45. // Prepare macro pattern to compare
  46. decltype(Zxx) fixedMacros{};
  47. CreateFixedMacro(fixedMacros, zxx);
  48. if(fixedMacros == Zxx)
  49. return zxx;
  50. }
  51. }
  52. return kZxxCustom; // Custom setup
  53. }
  54. void MIDIMacroConfig::CreateParameteredMacro(Macro &parameteredMacro, ParameteredMacro macroType, int subType) const
  55. {
  56. switch(macroType)
  57. {
  58. case kSFxUnused: parameteredMacro = ""; break;
  59. case kSFxCutoff: parameteredMacro = "F0F000z"; break;
  60. case kSFxReso: parameteredMacro = "F0F001z"; break;
  61. case kSFxFltMode: parameteredMacro = "F0F002z"; break;
  62. case kSFxDryWet: parameteredMacro = "F0F003z"; break;
  63. case kSFxCC: parameteredMacro = MPT_AFORMAT("Bc{}z")(mpt::afmt::HEX0<2>(subType & 0x7F)); break;
  64. case kSFxPlugParam: parameteredMacro = MPT_AFORMAT("F0F{}z")(mpt::afmt::HEX0<3>(std::min(subType, 0x17F) + 0x80)); break;
  65. case kSFxChannelAT: parameteredMacro = "Dcz"; break;
  66. case kSFxPolyAT: parameteredMacro = "Acnz"; break;
  67. case kSFxPitch: parameteredMacro = "Ec00z"; break;
  68. case kSFxProgChange: parameteredMacro = "Ccz"; break;
  69. case kSFxCustom:
  70. default:
  71. MPT_ASSERT_NOTREACHED();
  72. break;
  73. }
  74. }
  75. std::string MIDIMacroConfig::CreateParameteredMacro(ParameteredMacro macroType, int subType) const
  76. {
  77. Macro parameteredMacro{};
  78. CreateParameteredMacro(parameteredMacro, macroType, subType);
  79. return parameteredMacro;
  80. }
  81. // Create Zxx (Z80 - ZFF) from preset
  82. void MIDIMacroConfig::CreateFixedMacro(std::array<Macro, kZxxMacros> &fixedMacros, FixedMacro macroType) const
  83. {
  84. for(uint32 i = 0; i < kZxxMacros; i++)
  85. {
  86. uint32 param = i;
  87. switch(macroType)
  88. {
  89. case kZxxUnused:
  90. fixedMacros[i] = "";
  91. break;
  92. case kZxxReso4Bit:
  93. param = i * 8;
  94. if(i < 16)
  95. fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param));
  96. else
  97. fixedMacros[i] = "";
  98. break;
  99. case kZxxReso7Bit:
  100. fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param));
  101. break;
  102. case kZxxCutoff:
  103. fixedMacros[i] = MPT_AFORMAT("F0F000{}")(mpt::afmt::HEX0<2>(param));
  104. break;
  105. case kZxxFltMode:
  106. fixedMacros[i] = MPT_AFORMAT("F0F002{}")(mpt::afmt::HEX0<2>(param));
  107. break;
  108. case kZxxResoFltMode:
  109. param = (i & 0x0F) * 8;
  110. if(i < 16)
  111. fixedMacros[i] = MPT_AFORMAT("F0F001{}")(mpt::afmt::HEX0<2>(param));
  112. else if(i < 32)
  113. fixedMacros[i] = MPT_AFORMAT("F0F002{}")(mpt::afmt::HEX0<2>(param));
  114. else
  115. fixedMacros[i] = "";
  116. break;
  117. case kZxxChannelAT:
  118. fixedMacros[i] = MPT_AFORMAT("Dc{}")(mpt::afmt::HEX0<2>(param));
  119. break;
  120. case kZxxPolyAT:
  121. fixedMacros[i] = MPT_AFORMAT("Acn{}")(mpt::afmt::HEX0<2>(param));
  122. break;
  123. case kZxxPitch:
  124. fixedMacros[i] = MPT_AFORMAT("Ec00{}")(mpt::afmt::HEX0<2>(param));
  125. break;
  126. case kZxxProgChange:
  127. fixedMacros[i] = MPT_AFORMAT("Cc{}")(mpt::afmt::HEX0<2>(param));
  128. break;
  129. case kZxxCustom:
  130. default:
  131. MPT_ASSERT_NOTREACHED();
  132. continue;
  133. }
  134. }
  135. }
  136. bool MIDIMacroConfig::operator== (const MIDIMacroConfig &other) const
  137. {
  138. return std::equal(begin(), end(), other.begin());
  139. }
  140. #ifdef MODPLUG_TRACKER
  141. // Returns macro description including plugin parameter / MIDI CC information
  142. CString MIDIMacroConfig::GetParameteredMacroName(uint32 macroIndex, IMixPlugin *plugin) const
  143. {
  144. const ParameteredMacro macroType = GetParameteredMacroType(macroIndex);
  145. switch(macroType)
  146. {
  147. case kSFxPlugParam:
  148. {
  149. const int param = MacroToPlugParam(macroIndex);
  150. CString formattedName;
  151. formattedName.Format(_T("Param %d"), param);
  152. #ifndef NO_PLUGINS
  153. if(plugin != nullptr)
  154. {
  155. CString paramName = plugin->GetParamName(param);
  156. if(!paramName.IsEmpty())
  157. {
  158. formattedName += _T(" (") + paramName + _T(")");
  159. }
  160. } else
  161. #else
  162. MPT_UNREFERENCED_PARAMETER(plugin);
  163. #endif // NO_PLUGINS
  164. {
  165. formattedName += _T(" (N/A)");
  166. }
  167. return formattedName;
  168. }
  169. case kSFxCC:
  170. {
  171. CString formattedCC;
  172. formattedCC.Format(_T("MIDI CC %d"), MacroToMidiCC(macroIndex));
  173. return formattedCC;
  174. }
  175. default:
  176. return GetParameteredMacroName(macroType);
  177. }
  178. }
  179. // Returns generic macro description.
  180. CString MIDIMacroConfig::GetParameteredMacroName(ParameteredMacro macroType) const
  181. {
  182. switch(macroType)
  183. {
  184. case kSFxUnused: return _T("Unused");
  185. case kSFxCutoff: return _T("Set Filter Cutoff");
  186. case kSFxReso: return _T("Set Filter Resonance");
  187. case kSFxFltMode: return _T("Set Filter Mode");
  188. case kSFxDryWet: return _T("Set Plugin Dry/Wet Ratio");
  189. case kSFxPlugParam: return _T("Control Plugin Parameter...");
  190. case kSFxCC: return _T("MIDI CC...");
  191. case kSFxChannelAT: return _T("Channel Aftertouch");
  192. case kSFxPolyAT: return _T("Polyphonic Aftertouch");
  193. case kSFxPitch: return _T("Pitch Bend");
  194. case kSFxProgChange: return _T("MIDI Program Change");
  195. case kSFxCustom:
  196. default: return _T("Custom");
  197. }
  198. }
  199. // Returns generic macro description.
  200. CString MIDIMacroConfig::GetFixedMacroName(FixedMacro macroType) const
  201. {
  202. switch(macroType)
  203. {
  204. case kZxxUnused: return _T("Unused");
  205. case kZxxReso4Bit: return _T("Z80 - Z8F controls Resonant Filter Resonance");
  206. case kZxxReso7Bit: return _T("Z80 - ZFF controls Resonant Filter Resonance");
  207. case kZxxCutoff: return _T("Z80 - ZFF controls Resonant Filter Cutoff");
  208. case kZxxFltMode: return _T("Z80 - ZFF controls Resonant Filter Mode");
  209. case kZxxResoFltMode: return _T("Z80 - Z9F controls Resonance + Filter Mode");
  210. case kZxxChannelAT: return _T("Z80 - ZFF controls Channel Aftertouch");
  211. case kZxxPolyAT: return _T("Z80 - ZFF controls Polyphonic Aftertouch");
  212. case kZxxPitch: return _T("Z80 - ZFF controls Pitch Bend");
  213. case kZxxProgChange: return _T("Z80 - ZFF controls MIDI Program Change");
  214. case kZxxCustom:
  215. default: return _T("Custom");
  216. }
  217. }
  218. PlugParamIndex MIDIMacroConfig::MacroToPlugParam(uint32 macroIndex) const
  219. {
  220. const std::string macro = SFx[macroIndex].NormalizedString();
  221. PlugParamIndex code = 0;
  222. const char *param = macro.c_str();
  223. param += 4;
  224. if ((param[0] >= '0') && (param[0] <= '9')) code = (param[0] - '0') << 4; else
  225. if ((param[0] >= 'A') && (param[0] <= 'F')) code = (param[0] - 'A' + 0x0A) << 4;
  226. if ((param[1] >= '0') && (param[1] <= '9')) code += (param[1] - '0'); else
  227. if ((param[1] >= 'A') && (param[1] <= 'F')) code += (param[1] - 'A' + 0x0A);
  228. if (macro.size() >= 4 && macro[3] == '0')
  229. return (code - 128);
  230. else
  231. return (code + 128);
  232. }
  233. int MIDIMacroConfig::MacroToMidiCC(uint32 macroIndex) const
  234. {
  235. const std::string macro = SFx[macroIndex].NormalizedString();
  236. int code = 0;
  237. const char *param = macro.c_str();
  238. param += 2;
  239. if ((param[0] >= '0') && (param[0] <= '9')) code = (param[0] - '0') << 4; else
  240. if ((param[0] >= 'A') && (param[0] <= 'F')) code = (param[0] - 'A' + 0x0A) << 4;
  241. if ((param[1] >= '0') && (param[1] <= '9')) code += (param[1] - '0'); else
  242. if ((param[1] >= 'A') && (param[1] <= 'F')) code += (param[1] - 'A' + 0x0A);
  243. return code;
  244. }
  245. int MIDIMacroConfig::FindMacroForParam(PlugParamIndex param) const
  246. {
  247. for(int macroIndex = 0; macroIndex < kSFxMacros; macroIndex++)
  248. {
  249. if(GetParameteredMacroType(macroIndex) == kSFxPlugParam && MacroToPlugParam(macroIndex) == param)
  250. {
  251. return macroIndex;
  252. }
  253. }
  254. return -1;
  255. }
  256. #endif // MODPLUG_TRACKER
  257. // Check if the MIDI Macro configuration used is the default one,
  258. // i.e. the configuration that is assumed when loading a file that has no macros embedded.
  259. bool MIDIMacroConfig::IsMacroDefaultSetupUsed() const
  260. {
  261. return *this == MIDIMacroConfig{};
  262. }
  263. // Reset MIDI macro config to default values.
  264. void MIDIMacroConfig::Reset()
  265. {
  266. std::fill(begin(), end(), Macro{});
  267. Global[MIDIOUT_START] = "FF";
  268. Global[MIDIOUT_STOP] = "FC";
  269. Global[MIDIOUT_NOTEON] = "9c n v";
  270. Global[MIDIOUT_NOTEOFF] = "9c n 0";
  271. Global[MIDIOUT_PROGRAM] = "Cc p";
  272. // SF0: Z00-Z7F controls cutoff
  273. CreateParameteredMacro(0, kSFxCutoff);
  274. // Z80-Z8F controls resonance
  275. CreateFixedMacro(kZxxReso4Bit);
  276. }
  277. // Clear all Zxx macros so that they do nothing.
  278. void MIDIMacroConfig::ClearZxxMacros()
  279. {
  280. std::fill(SFx.begin(), SFx.end(), Macro{});
  281. std::fill(Zxx.begin(), Zxx.end(), Macro{});
  282. }
  283. // Sanitize all macro config strings.
  284. void MIDIMacroConfig::Sanitize()
  285. {
  286. for(auto &macro : *this)
  287. {
  288. macro.Sanitize();
  289. }
  290. }
  291. // Fix old-format (not conforming to IT's MIDI macro definitions) MIDI config strings.
  292. void MIDIMacroConfig::UpgradeMacros()
  293. {
  294. for(auto &macro : *this)
  295. {
  296. macro.UpgradeLegacyMacro();
  297. }
  298. }
  299. // Normalize by removing blanks and other unwanted characters from macro strings for internal usage.
  300. std::string MIDIMacroConfig::Macro::NormalizedString() const
  301. {
  302. std::string sanitizedMacro = *this;
  303. std::string::size_type pos;
  304. while((pos = sanitizedMacro.find_first_not_of("0123456789ABCDEFabchmnopsuvxyz")) != std::string::npos)
  305. {
  306. sanitizedMacro.erase(pos, 1);
  307. }
  308. return sanitizedMacro;
  309. }
  310. void MIDIMacroConfig::Macro::Sanitize() noexcept
  311. {
  312. m_data.back() = '\0';
  313. const auto length = Length();
  314. std::fill(m_data.begin() + length, m_data.end(), '\0');
  315. for(size_t i = 0; i < length; i++)
  316. {
  317. if(m_data[i] < 32 || m_data[i] >= 127)
  318. m_data[i] = ' ';
  319. }
  320. }
  321. void MIDIMacroConfig::Macro::UpgradeLegacyMacro() noexcept
  322. {
  323. for(auto &c : m_data)
  324. {
  325. if(c >= 'a' && c <= 'f') // Both A-F and a-f were treated as hex constants
  326. {
  327. c = c - 'a' + 'A';
  328. } else if(c == 'K' || c == 'k') // Channel was K or k
  329. {
  330. c = 'c';
  331. } else if(c == 'X' || c == 'x' || c == 'Y' || c == 'y') // Those were pointless
  332. {
  333. c = 'z';
  334. }
  335. }
  336. }
  337. OPENMPT_NAMESPACE_END