1
0

ModInstrument.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*
  2. * ModInstrument.cpp
  3. * -----------------
  4. * Purpose: Helper functions for Module Instrument handling
  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 "Sndfile.h"
  11. #include "ModInstrument.h"
  12. OPENMPT_NAMESPACE_BEGIN
  13. // Convert envelope data between various formats.
  14. void InstrumentEnvelope::Convert(MODTYPE fromType, MODTYPE toType)
  15. {
  16. if(!(fromType & MOD_TYPE_XM) && (toType & MOD_TYPE_XM))
  17. {
  18. // IT / MPTM -> XM: Expand loop by one tick, convert sustain loops to sustain points, remove carry flag.
  19. nSustainStart = nSustainEnd;
  20. dwFlags.reset(ENV_CARRY);
  21. if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP])
  22. {
  23. for(uint32 node = nLoopEnd; node < size(); node++)
  24. {
  25. at(node).tick++;
  26. }
  27. }
  28. } else if((fromType & MOD_TYPE_XM) && !(toType & MOD_TYPE_XM))
  29. {
  30. if(nSustainStart > nLoopEnd && dwFlags[ENV_LOOP])
  31. {
  32. // In the IT format, the sustain loop is always considered before the envelope loop.
  33. // In the XM format, whichever of the two is encountered first is considered.
  34. // So we have to disable the sustain loop if it was behind the normal loop.
  35. dwFlags.reset(ENV_SUSTAIN);
  36. }
  37. // XM -> IT / MPTM: Shorten loop by one tick by inserting bogus point
  38. if(nLoopEnd > nLoopStart && dwFlags[ENV_LOOP] && nLoopEnd < size())
  39. {
  40. if(at(nLoopEnd).tick - 1 > at(nLoopEnd - 1).tick)
  41. {
  42. // Insert an interpolated point just before the loop point.
  43. EnvelopeNode::tick_t tick = at(nLoopEnd).tick - 1u;
  44. auto interpolatedValue = static_cast<EnvelopeNode::value_t>(GetValueFromPosition(tick, 64));
  45. insert(begin() + nLoopEnd, EnvelopeNode(tick, interpolatedValue));
  46. } else
  47. {
  48. // There is already a point before the loop point: Use it as new loop end.
  49. nLoopEnd--;
  50. }
  51. }
  52. }
  53. if(toType != MOD_TYPE_MPT)
  54. {
  55. nReleaseNode = ENV_RELEASE_NODE_UNSET;
  56. }
  57. }
  58. // Get envelope value at a given tick. Assumes that the envelope data is in rage [0, rangeIn],
  59. // returns value in range [0, rangeOut].
  60. int32 InstrumentEnvelope::GetValueFromPosition(int position, int32 rangeOut, int32 rangeIn) const
  61. {
  62. uint32 pt = size() - 1u;
  63. const int32 ENV_PRECISION = 1 << 16;
  64. // Checking where current 'tick' is relative to the envelope points.
  65. for(uint32 i = 0; i < size() - 1u; i++)
  66. {
  67. if (position <= at(i).tick)
  68. {
  69. pt = i;
  70. break;
  71. }
  72. }
  73. int x2 = at(pt).tick;
  74. int32 value = 0;
  75. if(position >= x2)
  76. {
  77. // Case: current 'tick' is on a envelope point.
  78. value = at(pt).value * ENV_PRECISION / rangeIn;
  79. } else
  80. {
  81. // Case: current 'tick' is between two envelope points.
  82. int x1 = 0;
  83. if(pt)
  84. {
  85. // Get previous node's value and tick.
  86. value = at(pt - 1).value * ENV_PRECISION / rangeIn;
  87. x1 = at(pt - 1).tick;
  88. }
  89. if(x2 > x1 && position > x1)
  90. {
  91. // Linear approximation between the points;
  92. // f(x + d) ~ f(x) + f'(x) * d, where f'(x) = (y2 - y1) / (x2 - x1)
  93. value += Util::muldiv(position - x1, (at(pt).value * ENV_PRECISION / rangeIn - value), x2 - x1);
  94. }
  95. }
  96. Limit(value, int32(0), ENV_PRECISION);
  97. return (value * rangeOut + ENV_PRECISION / 2) / ENV_PRECISION;
  98. }
  99. void InstrumentEnvelope::Sanitize(uint8 maxValue)
  100. {
  101. if(!empty())
  102. {
  103. front().tick = 0;
  104. LimitMax(front().value, maxValue);
  105. for(iterator it = begin() + 1; it != end(); it++)
  106. {
  107. it->tick = std::max(it->tick, (it - 1)->tick);
  108. LimitMax(it->value, maxValue);
  109. }
  110. }
  111. LimitMax(nLoopEnd, static_cast<decltype(nLoopEnd)>(size() - 1));
  112. LimitMax(nLoopStart, nLoopEnd);
  113. LimitMax(nSustainEnd, static_cast<decltype(nSustainEnd)>(size() - 1));
  114. LimitMax(nSustainStart, nSustainEnd);
  115. if(nReleaseNode != ENV_RELEASE_NODE_UNSET)
  116. LimitMax(nReleaseNode, static_cast<decltype(nReleaseNode)>(size() - 1));
  117. }
  118. ModInstrument::ModInstrument(SAMPLEINDEX sample)
  119. {
  120. SetCutoff(0, false);
  121. SetResonance(0, false);
  122. pitchToTempoLock.Set(0);
  123. pTuning = CSoundFile::GetDefaultTuning();
  124. AssignSample(sample);
  125. ResetNoteMap();
  126. }
  127. // Translate instrument properties between two given formats.
  128. void ModInstrument::Convert(MODTYPE fromType, MODTYPE toType)
  129. {
  130. MPT_UNREFERENCED_PARAMETER(fromType);
  131. if(toType & MOD_TYPE_XM)
  132. {
  133. ResetNoteMap();
  134. PitchEnv.dwFlags.reset(ENV_ENABLED | ENV_FILTER);
  135. dwFlags.reset(INS_SETPANNING);
  136. SetCutoff(GetCutoff(), false);
  137. SetResonance(GetResonance(), false);
  138. filterMode = FilterMode::Unchanged;
  139. nCutSwing = nPanSwing = nResSwing = nVolSwing = 0;
  140. nPPC = NOTE_MIDDLEC - 1;
  141. nPPS = 0;
  142. nNNA = NewNoteAction::NoteCut;
  143. nDCT = DuplicateCheckType::None;
  144. nDNA = DuplicateNoteAction::NoteCut;
  145. if(nMidiChannel == MidiMappedChannel)
  146. {
  147. nMidiChannel = MidiFirstChannel;
  148. }
  149. // FT2 only has unsigned Pitch Wheel Depth, and it's limited to 0...36 (in the GUI, at least. As you would expect it from FT2, this value is actually not sanitized on load).
  150. midiPWD = static_cast<int8>(std::abs(midiPWD));
  151. Limit(midiPWD, int8(0), int8(36));
  152. nGlobalVol = 64;
  153. nPan = 128;
  154. LimitMax(nFadeOut, 32767u);
  155. }
  156. VolEnv.Convert(fromType, toType);
  157. PanEnv.Convert(fromType, toType);
  158. PitchEnv.Convert(fromType, toType);
  159. if(fromType == MOD_TYPE_XM && (toType & (MOD_TYPE_IT | MOD_TYPE_MPT)))
  160. {
  161. if(!VolEnv.dwFlags[ENV_ENABLED])
  162. {
  163. // Note-Off with no envelope cuts the note immediately in XM
  164. VolEnv.resize(2);
  165. VolEnv[0].tick = 0;
  166. VolEnv[0].value = ENVELOPE_MAX;
  167. VolEnv[1].tick = 1;
  168. VolEnv[1].value = ENVELOPE_MIN;
  169. VolEnv.dwFlags.set(ENV_ENABLED | ENV_SUSTAIN);
  170. VolEnv.dwFlags.reset(ENV_LOOP);
  171. VolEnv.nSustainStart = VolEnv.nSustainEnd = 0;
  172. }
  173. }
  174. // Limit fadeout length for IT
  175. if(toType & MOD_TYPE_IT)
  176. {
  177. LimitMax(nFadeOut, 8192u);
  178. }
  179. // MPT-specific features - remove instrument tunings, Pitch/Tempo Lock, cutoff / resonance swing and filter mode for other formats
  180. if(!(toType & MOD_TYPE_MPT))
  181. {
  182. SetTuning(nullptr);
  183. pitchToTempoLock.Set(0);
  184. nCutSwing = nResSwing = 0;
  185. filterMode = FilterMode::Unchanged;
  186. nVolRampUp = 0;
  187. }
  188. }
  189. // Get a set of all samples referenced by this instrument
  190. std::set<SAMPLEINDEX> ModInstrument::GetSamples() const
  191. {
  192. std::set<SAMPLEINDEX> referencedSamples;
  193. for(const auto sample : Keyboard)
  194. {
  195. if(sample)
  196. {
  197. referencedSamples.insert(sample);
  198. }
  199. }
  200. return referencedSamples;
  201. }
  202. // Write sample references into a bool vector. If a sample is referenced by this instrument, true is written.
  203. // The caller has to initialize the vector.
  204. void ModInstrument::GetSamples(std::vector<bool> &referencedSamples) const
  205. {
  206. for(const auto sample : Keyboard)
  207. {
  208. if(sample != 0 && sample < referencedSamples.size())
  209. {
  210. referencedSamples[sample] = true;
  211. }
  212. }
  213. }
  214. void ModInstrument::Sanitize(MODTYPE modType)
  215. {
  216. LimitMax(nFadeOut, 65536u);
  217. LimitMax(nGlobalVol, 64u);
  218. LimitMax(nPan, 256u);
  219. LimitMax(wMidiBank, uint16(16384));
  220. LimitMax(nMidiProgram, uint8(128));
  221. LimitMax(nMidiChannel, uint8(17));
  222. if(nNNA > NewNoteAction::NoteFade) nNNA = NewNoteAction::NoteCut;
  223. if(nDCT > DuplicateCheckType::Plugin) nDCT = DuplicateCheckType::None;
  224. if(nDNA > DuplicateNoteAction::NoteFade) nDNA = DuplicateNoteAction::NoteCut;
  225. LimitMax(nPanSwing, uint8(64));
  226. LimitMax(nVolSwing, uint8(100));
  227. Limit(nPPS, int8(-32), int8(32));
  228. LimitMax(nCutSwing, uint8(64));
  229. LimitMax(nResSwing, uint8(64));
  230. #ifdef MODPLUG_TRACKER
  231. MPT_UNREFERENCED_PARAMETER(modType);
  232. const uint8 range = ENVELOPE_MAX;
  233. #else
  234. const uint8 range = modType == MOD_TYPE_AMS ? uint8_max : ENVELOPE_MAX;
  235. #endif
  236. VolEnv.Sanitize();
  237. PanEnv.Sanitize();
  238. PitchEnv.Sanitize(range);
  239. for(size_t i = 0; i < std::size(NoteMap); i++)
  240. {
  241. if(NoteMap[i] < NOTE_MIN || NoteMap[i] > NOTE_MAX)
  242. NoteMap[i] = static_cast<uint8>(i + NOTE_MIN);
  243. }
  244. if(!Resampling::IsKnownMode(resampling))
  245. resampling = SRCMODE_DEFAULT;
  246. if(nMixPlug > MAX_MIXPLUGINS)
  247. nMixPlug = 0;
  248. }
  249. std::map<SAMPLEINDEX, int8> ModInstrument::CanConvertToDefaultNoteMap() const
  250. {
  251. std::map<SAMPLEINDEX, int8> transposeMap;
  252. for(size_t i = 0; i < std::size(NoteMap); i++)
  253. {
  254. if(Keyboard[i] == 0)
  255. continue;
  256. if(!NoteMap[i] || NoteMap[i] == (i + 1))
  257. continue;
  258. const int8 relativeNote = static_cast<int8>(NoteMap[i] - (i + NOTE_MIN));
  259. if(transposeMap.count(Keyboard[i]) && transposeMap[Keyboard[i]] != relativeNote)
  260. return {};
  261. transposeMap[Keyboard[i]] = relativeNote;
  262. }
  263. return transposeMap;
  264. }
  265. void ModInstrument::Transpose(int8 amount)
  266. {
  267. for(auto &note : NoteMap)
  268. {
  269. note = static_cast<uint8>(Clamp(note + amount, NOTE_MIN, NOTE_MAX));
  270. }
  271. }
  272. uint8 ModInstrument::GetMIDIChannel(const ModChannel &channel, CHANNELINDEX chn) const
  273. {
  274. // For mapped channels, return their pattern channel, modulo 16 (because there are only 16 MIDI channels)
  275. if(nMidiChannel == MidiMappedChannel)
  276. return static_cast<uint8>((channel.nMasterChn ? (channel.nMasterChn - 1u) : chn) % 16u);
  277. else if(HasValidMIDIChannel())
  278. return (nMidiChannel - MidiFirstChannel) % 16u;
  279. else
  280. return 0;
  281. }
  282. OPENMPT_NAMESPACE_END