AppendModule.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. /*
  2. * AppendModule.cpp
  3. * ----------------
  4. * Purpose: Appending one module to an existing module
  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 "Moddoc.h"
  11. #include "../soundlib/mod_specifications.h"
  12. OPENMPT_NAMESPACE_BEGIN
  13. // Add samples, instruments, plugins and patterns from another module to the current module
  14. void CModDoc::AppendModule(const CSoundFile &source)
  15. {
  16. const CModSpecifications &specs = m_SndFile.GetModSpecifications();
  17. // Mappings between old and new indices
  18. std::vector<PLUGINDEX> pluginMapping(MAX_MIXPLUGINS + 1, 0);
  19. std::vector<INSTRUMENTINDEX> instrMapping((source.GetNumInstruments() ? source.GetNumInstruments() : source.GetNumSamples()) + 1, INSTRUMENTINDEX_INVALID);
  20. std::vector<ORDERINDEX> orderMapping;
  21. std::vector<PATTERNINDEX> patternMapping(source.Patterns.GetNumPatterns(), PATTERNINDEX_INVALID);
  22. ///////////////////////////////////////////////////////////////////////////
  23. // Copy plugins
  24. #ifndef NO_PLUGINS
  25. if(specs.supportsPlugins)
  26. {
  27. PLUGINDEX plug = 0;
  28. for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
  29. {
  30. if(!source.m_MixPlugins[i].IsValidPlugin())
  31. {
  32. continue;
  33. }
  34. while(plug < MAX_MIXPLUGINS && m_SndFile.m_MixPlugins[plug].IsValidPlugin())
  35. {
  36. plug++;
  37. }
  38. if(plug < MAX_MIXPLUGINS)
  39. {
  40. ClonePlugin(m_SndFile.m_MixPlugins[plug], source.m_MixPlugins[i]);
  41. pluginMapping[i + 1] = plug + 1;
  42. } else
  43. {
  44. AddToLog("Too many plugins!");
  45. break;
  46. }
  47. }
  48. // Fix up references between plugins
  49. for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
  50. {
  51. if(pluginMapping[i + 1] != 0 && source.m_MixPlugins[i].GetOutputPlugin() < MAX_MIXPLUGINS)
  52. {
  53. m_SndFile.m_MixPlugins[pluginMapping[i + 1] - 1].SetOutputPlugin(pluginMapping[source.m_MixPlugins[i].GetOutputPlugin() + 1] - 1);
  54. }
  55. }
  56. }
  57. #endif // NO_PLUGINS
  58. ///////////////////////////////////////////////////////////////////////////
  59. // Copy samples / instruments
  60. if(source.GetNumInstruments() != 0 && m_SndFile.GetNumInstruments() == 0 && specs.instrumentsMax)
  61. {
  62. // Convert to instruments first
  63. ConvertSamplesToInstruments();
  64. }
  65. // Check which samples / instruments are actually referenced.
  66. for(const auto &pat : source.Patterns) if(pat.IsValid())
  67. {
  68. for(const auto &m : pat)
  69. {
  70. if(!m.IsPcNote() && m.instr < instrMapping.size()) instrMapping[m.instr] = 0;
  71. }
  72. }
  73. if(m_SndFile.GetNumInstruments())
  74. {
  75. INSTRUMENTINDEX targetIns = 0;
  76. if(source.GetNumInstruments())
  77. {
  78. for(INSTRUMENTINDEX i = 1; i <= source.GetNumInstruments(); i++) if(source.Instruments[i] != nullptr && !instrMapping[i])
  79. {
  80. targetIns = m_SndFile.GetNextFreeInstrument(targetIns + 1);
  81. if(targetIns == INSTRUMENTINDEX_INVALID)
  82. {
  83. AddToLog("Too many instruments!");
  84. break;
  85. }
  86. if(m_SndFile.ReadInstrumentFromSong(targetIns, source, i))
  87. {
  88. ModInstrument *ins = m_SndFile.Instruments[targetIns];
  89. if(ins->nMixPlug <= MAX_MIXPLUGINS)
  90. {
  91. ins->nMixPlug = pluginMapping[ins->nMixPlug];
  92. }
  93. instrMapping[i] = targetIns;
  94. }
  95. }
  96. } else
  97. {
  98. SAMPLEINDEX targetSmp = 0;
  99. for(SAMPLEINDEX i = 1; i <= source.GetNumSamples(); i++) if(!instrMapping[i])
  100. {
  101. targetIns = m_SndFile.GetNextFreeInstrument(targetIns + 1);
  102. targetSmp = m_SndFile.GetNextFreeSample(targetIns, targetSmp + 1);
  103. if(targetIns == INSTRUMENTINDEX_INVALID)
  104. {
  105. AddToLog("Too many instruments!");
  106. break;
  107. } else if(targetSmp == SAMPLEINDEX_INVALID)
  108. {
  109. AddToLog("Too many samples!");
  110. break;
  111. }
  112. if(m_SndFile.AllocateInstrument(targetIns, targetSmp) != nullptr)
  113. {
  114. m_SndFile.ReadSampleFromSong(targetSmp, source, i);
  115. }
  116. instrMapping[i] = targetIns;
  117. }
  118. }
  119. } else
  120. {
  121. SAMPLEINDEX targetSmp = 0;
  122. if(source.GetNumInstruments())
  123. {
  124. for(INSTRUMENTINDEX i = 1; i <= source.GetNumInstruments(); i++) if(source.Instruments[i] != nullptr && !instrMapping[i])
  125. {
  126. targetSmp = m_SndFile.GetNextFreeSample(INSTRUMENTINDEX_INVALID, targetSmp + 1);
  127. if(targetSmp == SAMPLEINDEX_INVALID)
  128. {
  129. AddToLog("Too many samples!");
  130. break;
  131. }
  132. m_SndFile.ReadSampleFromSong(targetSmp, source, source.Instruments[i]->Keyboard[NOTE_MIDDLEC - NOTE_MIN]);
  133. instrMapping[i] = targetSmp;
  134. }
  135. } else
  136. {
  137. for(SAMPLEINDEX i = 1; i <= source.GetNumSamples(); i++) if(!instrMapping[i])
  138. {
  139. targetSmp = m_SndFile.GetNextFreeSample(INSTRUMENTINDEX_INVALID, targetSmp + 1);
  140. if(targetSmp == SAMPLEINDEX_INVALID)
  141. {
  142. AddToLog("Too many samples!");
  143. break;
  144. }
  145. m_SndFile.ReadSampleFromSong(targetSmp, source, i);
  146. instrMapping[i] = targetSmp;
  147. }
  148. }
  149. }
  150. ///////////////////////////////////////////////////////////////////////////
  151. // Copy order lists
  152. const bool useOrderMapping = source.Order.GetNumSequences() == 1;
  153. for(auto &srcOrder : source.Order)
  154. {
  155. ORDERINDEX insertPos = 0;
  156. if(m_SndFile.Order.GetNumSequences() < specs.sequencesMax)
  157. {
  158. m_SndFile.Order.AddSequence();
  159. m_SndFile.Order().SetName(srcOrder.GetName());
  160. } else
  161. {
  162. insertPos = m_SndFile.Order().GetLengthTailTrimmed();
  163. if(specs.hasStopIndex)
  164. insertPos++;
  165. }
  166. const ORDERINDEX ordLen = srcOrder.GetLengthTailTrimmed();
  167. if(useOrderMapping) orderMapping.resize(ordLen, ORDERINDEX_INVALID);
  168. for(ORDERINDEX ord = 0; ord < ordLen; ord++)
  169. {
  170. if(insertPos >= specs.ordersMax)
  171. {
  172. AddToLog("Too many order items!");
  173. break;
  174. }
  175. PATTERNINDEX insertPat = PATTERNINDEX_INVALID;
  176. PATTERNINDEX srcPat = srcOrder[ord];
  177. if(source.Patterns.IsValidPat(srcPat) && srcPat < patternMapping.size())
  178. {
  179. if(patternMapping[srcPat] == PATTERNINDEX_INVALID && source.Patterns.IsValidPat(srcPat))
  180. {
  181. patternMapping[srcPat] = InsertPattern(Clamp(source.Patterns[srcPat].GetNumRows(), specs.patternRowsMin, specs.patternRowsMax));
  182. if(patternMapping[srcPat] == PATTERNINDEX_INVALID)
  183. {
  184. AddToLog("Too many patterns!");
  185. break;
  186. }
  187. }
  188. if(patternMapping[srcPat] == PATTERNINDEX_INVALID)
  189. {
  190. continue;
  191. }
  192. insertPat = patternMapping[srcPat];
  193. } else if(srcPat == srcOrder.GetIgnoreIndex() && specs.hasIgnoreIndex)
  194. {
  195. insertPat = m_SndFile.Order.GetIgnoreIndex();
  196. } else if(srcPat == srcOrder.GetInvalidPatIndex() && specs.hasStopIndex)
  197. {
  198. insertPat = m_SndFile.Order.GetInvalidPatIndex();
  199. } else
  200. {
  201. continue;
  202. }
  203. m_SndFile.Order().insert(insertPos, 1, insertPat);
  204. if(useOrderMapping) orderMapping[ord] = insertPos++;
  205. }
  206. }
  207. ///////////////////////////////////////////////////////////////////////////
  208. // Adjust number of channels
  209. if(source.GetNumChannels() > m_SndFile.GetNumChannels())
  210. {
  211. CHANNELINDEX newChn = source.GetNumChannels();
  212. if(newChn > specs.channelsMax)
  213. {
  214. AddToLog("Too many channels!");
  215. newChn = specs.channelsMax;
  216. }
  217. if(newChn > m_SndFile.GetNumChannels())
  218. {
  219. ChangeNumChannels(newChn, false);
  220. }
  221. }
  222. ///////////////////////////////////////////////////////////////////////////
  223. // Copy patterns
  224. const bool tempoSwingDiffers = source.m_tempoSwing != m_SndFile.m_tempoSwing;
  225. const bool timeSigDiffers = source.m_nDefaultRowsPerBeat != m_SndFile.m_nDefaultRowsPerBeat || source.m_nDefaultRowsPerMeasure != m_SndFile.m_nDefaultRowsPerMeasure;
  226. const CHANNELINDEX copyChannels = std::min(m_SndFile.GetNumChannels(), source.GetNumChannels());
  227. for(PATTERNINDEX pat = 0; pat < patternMapping.size(); pat++)
  228. {
  229. if(patternMapping[pat] == PATTERNINDEX_INVALID)
  230. {
  231. continue;
  232. }
  233. const CPattern &sourcePat = source.Patterns[pat];
  234. CPattern &targetPat = m_SndFile.Patterns[patternMapping[pat]];
  235. if(specs.hasPatternNames)
  236. {
  237. targetPat.SetName(sourcePat.GetName());
  238. }
  239. if(specs.hasPatternSignatures)
  240. {
  241. if(sourcePat.GetOverrideSignature())
  242. {
  243. targetPat.SetSignature(sourcePat.GetRowsPerBeat(), sourcePat.GetRowsPerMeasure());
  244. } else if(timeSigDiffers)
  245. {
  246. // Try fixing differing signature settings by copying them to the newly created patterns
  247. targetPat.SetSignature(source.m_nDefaultRowsPerBeat, source.m_nDefaultRowsPerMeasure);
  248. }
  249. }
  250. if(m_SndFile.m_nTempoMode == TempoMode::Modern)
  251. {
  252. // Swing only works in modern tempo mode
  253. if(sourcePat.HasTempoSwing())
  254. {
  255. targetPat.SetTempoSwing(sourcePat.GetTempoSwing());
  256. } else if(tempoSwingDiffers)
  257. {
  258. // Try fixing differing swing settings by copying them to the newly created patterns
  259. targetPat.SetSignature(source.m_nDefaultRowsPerBeat, source.m_nDefaultRowsPerMeasure);
  260. targetPat.SetTempoSwing(source.m_tempoSwing);
  261. }
  262. }
  263. const ROWINDEX copyRows = std::min(sourcePat.GetNumRows(), targetPat.GetNumRows());
  264. for(ROWINDEX row = 0; row < copyRows; row++)
  265. {
  266. const ModCommand *src = sourcePat.GetRow(row);
  267. ModCommand *m = targetPat.GetRow(row);
  268. for(CHANNELINDEX chn = 0; chn < copyChannels; chn++, src++, m++)
  269. {
  270. *m = *src;
  271. m->Convert(source.GetType(), m_SndFile.GetType(), source);
  272. if(m->IsPcNote())
  273. {
  274. if(m->instr && m->instr < pluginMapping.size()) m->instr = static_cast<ModCommand::INSTR>(pluginMapping[m->instr]);
  275. } else
  276. {
  277. if(m->instr && m->instr < instrMapping.size()) m->instr = static_cast<ModCommand::INSTR>(instrMapping[m->instr]);
  278. if(m->command == CMD_POSITIONJUMP && m->param < orderMapping.size())
  279. {
  280. if(orderMapping[m->param] == ORDERINDEX_INVALID)
  281. {
  282. m->command = CMD_NONE;
  283. } else
  284. {
  285. m->param = static_cast<ModCommand::PARAM>(orderMapping[m->param]);
  286. }
  287. }
  288. }
  289. }
  290. }
  291. if(copyRows < targetPat.GetNumRows())
  292. {
  293. // If source pattern was smaller, write pattern break effect.
  294. targetPat.WriteEffect(EffectWriter(CMD_PATTERNBREAK, 0).Row(copyRows - 1).RetryNextRow());
  295. }
  296. }
  297. }
  298. OPENMPT_NAMESPACE_END