Modedit.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383
  1. /*
  2. * ModEdit.cpp
  3. * -----------
  4. * Purpose: Song (pattern, samples, instruments) editing functions
  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 "Mptrack.h"
  11. #include "Mainfrm.h"
  12. #include "Moddoc.h"
  13. #include "Clipboard.h"
  14. #include "dlg_misc.h"
  15. #include "Dlsbank.h"
  16. #include "../soundlib/modsmp_ctrl.h"
  17. #include "../soundlib/mod_specifications.h"
  18. #include "../soundlib/tuning.h"
  19. #include "../soundlib/OPL.h"
  20. #include "../common/misc_util.h"
  21. #include "../common/mptStringBuffer.h"
  22. #include "../common/mptFileIO.h"
  23. #include <sstream>
  24. // Plugin cloning
  25. #include "../soundlib/plugins/PluginManager.h"
  26. #include "../soundlib/plugins/PlugInterface.h"
  27. #include "VstPresets.h"
  28. #include "../common/FileReader.h"
  29. #include "mpt/io/io.hpp"
  30. #include "mpt/io/io_stdstream.hpp"
  31. OPENMPT_NAMESPACE_BEGIN
  32. // Change the number of channels.
  33. // Return true on success.
  34. bool CModDoc::ChangeNumChannels(CHANNELINDEX nNewChannels, const bool showCancelInRemoveDlg)
  35. {
  36. const CHANNELINDEX maxChans = m_SndFile.GetModSpecifications().channelsMax;
  37. if(nNewChannels > maxChans)
  38. {
  39. CString error;
  40. error.Format(_T("Error: Max number of channels for this file type is %u"), maxChans);
  41. Reporting::Warning(error);
  42. return false;
  43. }
  44. if(nNewChannels == GetNumChannels())
  45. return false;
  46. if(nNewChannels < GetNumChannels())
  47. {
  48. // Remove channels
  49. CHANNELINDEX chnsToRemove = 0, maxRemoveCount = 0;
  50. //nNewChannels = 0 means user can choose how many channels to remove
  51. if(nNewChannels > 0)
  52. {
  53. chnsToRemove = GetNumChannels() - nNewChannels;
  54. maxRemoveCount = chnsToRemove;
  55. } else
  56. {
  57. chnsToRemove = 0;
  58. maxRemoveCount = GetNumChannels();
  59. }
  60. CRemoveChannelsDlg rem(m_SndFile, chnsToRemove, showCancelInRemoveDlg);
  61. CheckUsedChannels(rem.m_bKeepMask, maxRemoveCount);
  62. if(rem.DoModal() != IDOK)
  63. return false;
  64. // Removing selected channels
  65. return RemoveChannels(rem.m_bKeepMask, true);
  66. } else
  67. {
  68. // Increasing number of channels
  69. BeginWaitCursor();
  70. std::vector<CHANNELINDEX> channels(nNewChannels, CHANNELINDEX_INVALID);
  71. for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
  72. {
  73. channels[chn] = chn;
  74. }
  75. bool success = (ReArrangeChannels(channels) == nNewChannels);
  76. if(success)
  77. {
  78. SetModified();
  79. UpdateAllViews(nullptr, UpdateHint().ModType());
  80. }
  81. EndWaitCursor();
  82. return success;
  83. }
  84. }
  85. // To remove all channels whose index corresponds to false in the keepMask vector.
  86. // Return true on success.
  87. bool CModDoc::RemoveChannels(const std::vector<bool> &keepMask, bool verbose)
  88. {
  89. CHANNELINDEX nRemainingChannels = 0;
  90. //First calculating how many channels are to be left
  91. for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
  92. {
  93. if(keepMask[chn])
  94. nRemainingChannels++;
  95. }
  96. if(nRemainingChannels == GetNumChannels() || nRemainingChannels < m_SndFile.GetModSpecifications().channelsMin)
  97. {
  98. if(verbose)
  99. {
  100. CString str;
  101. if(nRemainingChannels == GetNumChannels())
  102. str = _T("No channels chosen to be removed.");
  103. else
  104. str = _T("No removal done - channel number is already at minimum.");
  105. Reporting::Information(str, _T("Remove Channels"));
  106. }
  107. return false;
  108. }
  109. BeginWaitCursor();
  110. // Create new channel order, with only channels from m_bChnMask left.
  111. std::vector<CHANNELINDEX> channels(nRemainingChannels, 0);
  112. CHANNELINDEX i = 0;
  113. for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++)
  114. {
  115. if(keepMask[chn])
  116. {
  117. channels[i++] = chn;
  118. }
  119. }
  120. const bool success = (ReArrangeChannels(channels) == nRemainingChannels);
  121. if(success)
  122. {
  123. SetModified();
  124. UpdateAllViews(nullptr, UpdateHint().ModType());
  125. }
  126. EndWaitCursor();
  127. return success;
  128. }
  129. // Base code for adding, removing, moving and duplicating channels. Returns new number of channels on success, CHANNELINDEX_INVALID otherwise.
  130. // The new channel vector can contain CHANNELINDEX_INVALID for adding new (empty) channels.
  131. CHANNELINDEX CModDoc::ReArrangeChannels(const std::vector<CHANNELINDEX> &newOrder, const bool createUndoPoint)
  132. {
  133. //newOrder[i] tells which current channel should be placed to i:th position in
  134. //the new order, or if i is not an index of current channels, then new channel is
  135. //added to position i. If index of some current channel is missing from the
  136. //newOrder-vector, then the channel gets removed.
  137. const CHANNELINDEX newNumChannels = static_cast<CHANNELINDEX>(newOrder.size()), oldNumChannels = GetNumChannels();
  138. auto &specs = m_SndFile.GetModSpecifications();
  139. if(newNumChannels > specs.channelsMax || newNumChannels < specs.channelsMin)
  140. {
  141. CString str;
  142. str.Format(_T("Can't apply change: Number of channels should be between %u and %u."), specs.channelsMin, specs.channelsMax);
  143. Reporting::Error(str, _T("Rearrange Channels"));
  144. return CHANNELINDEX_INVALID;
  145. }
  146. if(createUndoPoint)
  147. {
  148. PrepareUndoForAllPatterns(true, "Rearrange Channels");
  149. }
  150. CriticalSection cs;
  151. if(oldNumChannels == newNumChannels)
  152. {
  153. // Optimization with no pattern re-allocation
  154. std::vector<ModCommand> oldRow(oldNumChannels);
  155. for(auto &pat : m_SndFile.Patterns)
  156. {
  157. auto m = pat.begin();
  158. for(ROWINDEX row = 0; row < pat.GetNumRows(); row++)
  159. {
  160. oldRow.assign(m, m + oldNumChannels);
  161. for(CHANNELINDEX chn = 0; chn < newNumChannels; chn++, m++)
  162. {
  163. if(newOrder[chn] < oldNumChannels) // Case: getting old channel to the new channel order.
  164. *m = oldRow[newOrder[chn]];
  165. else
  166. *m = ModCommand::Empty();
  167. }
  168. }
  169. }
  170. } else
  171. {
  172. // Create all patterns first so that we can exit cleanly in case of OOM
  173. std::vector<std::vector<ModCommand>> newPatterns;
  174. try
  175. {
  176. newPatterns.resize(m_SndFile.Patterns.Size());
  177. for(PATTERNINDEX i = 0; i < m_SndFile.Patterns.Size(); i++)
  178. {
  179. newPatterns[i].resize(m_SndFile.Patterns[i].GetNumRows() * newNumChannels);
  180. }
  181. } catch(mpt::out_of_memory e)
  182. {
  183. mpt::delete_out_of_memory(e);
  184. Reporting::Error("Out of memory!", "Rearrange Channels");
  185. return oldNumChannels;
  186. }
  187. m_SndFile.m_nChannels = newNumChannels;
  188. for(PATTERNINDEX i = 0; i < m_SndFile.Patterns.Size(); i++)
  189. {
  190. CPattern &pat = m_SndFile.Patterns[i];
  191. if(pat.IsValid())
  192. {
  193. auto mNew = newPatterns[i].begin(), mOld = pat.begin();
  194. for(ROWINDEX row = 0; row < pat.GetNumRows(); row++, mOld += oldNumChannels)
  195. {
  196. for(CHANNELINDEX chn = 0; chn < newNumChannels; chn++, mNew++)
  197. {
  198. if(newOrder[chn] < oldNumChannels) // Case: getting old channel to the new channel order.
  199. *mNew = mOld[newOrder[chn]];
  200. }
  201. }
  202. pat.SetData(std::move(newPatterns[i]));
  203. }
  204. }
  205. }
  206. // Reverse mapping: One of the new indices of an old channel
  207. std::vector<CHANNELINDEX> newIndex(oldNumChannels, CHANNELINDEX_INVALID);
  208. for(CHANNELINDEX chn = 0; chn < newNumChannels; chn++)
  209. {
  210. if(newOrder[chn] < oldNumChannels)
  211. {
  212. newIndex[newOrder[chn]] = chn;
  213. }
  214. }
  215. // Reassign NNA channels (note: if we increase the number of channels, the lowest-indexed NNA channels will still be lost)
  216. const auto muteFlag = CSoundFile::GetChannelMuteFlag();
  217. for(CHANNELINDEX chn = oldNumChannels; chn < MAX_CHANNELS; chn++)
  218. {
  219. auto &channel = m_SndFile.m_PlayState.Chn[chn];
  220. CHANNELINDEX masterChn = channel.nMasterChn, newMasterChn;
  221. if(masterChn > 0 && masterChn <= newIndex.size() && (newMasterChn = newIndex[masterChn - 1]) != CHANNELINDEX_INVALID)
  222. channel.nMasterChn = newMasterChn + 1;
  223. else
  224. channel.Reset(ModChannel::resetTotal, m_SndFile, chn, muteFlag);
  225. }
  226. std::vector<ModChannel> chns(std::begin(m_SndFile.m_PlayState.Chn), std::begin(m_SndFile.m_PlayState.Chn) + oldNumChannels);
  227. std::vector<ModChannelSettings> settings(std::begin(m_SndFile.ChnSettings), std::begin(m_SndFile.ChnSettings) + oldNumChannels);
  228. std::vector<RecordGroup> recordStates(oldNumChannels);
  229. auto chnMutePendings = m_SndFile.m_bChannelMuteTogglePending;
  230. for(CHANNELINDEX chn = 0; chn < oldNumChannels; chn++)
  231. {
  232. recordStates[chn] = GetChannelRecordGroup(chn);
  233. }
  234. ReinitRecordState();
  235. for(CHANNELINDEX chn = 0; chn < newNumChannels; chn++)
  236. {
  237. CHANNELINDEX srcChn = newOrder[chn];
  238. if(srcChn < oldNumChannels)
  239. {
  240. m_SndFile.ChnSettings[chn] = settings[srcChn];
  241. m_SndFile.m_PlayState.Chn[chn] = chns[srcChn];
  242. SetChannelRecordGroup(chn, recordStates[srcChn]);
  243. m_SndFile.m_bChannelMuteTogglePending[chn] = chnMutePendings[srcChn];
  244. if(m_SndFile.m_opl)
  245. m_SndFile.m_opl->MoveChannel(srcChn, chn);
  246. } else
  247. {
  248. m_SndFile.InitChannel(chn);
  249. SetDefaultChannelColors(chn);
  250. }
  251. }
  252. // Reset MOD panning (won't affect other module formats)
  253. m_SndFile.SetupMODPanning();
  254. m_SndFile.InitAmigaResampler();
  255. return newNumChannels;
  256. }
  257. // Base code for adding, removing, moving and duplicating samples. Returns new number of samples on success, SAMPLEINDEX_INVALID otherwise.
  258. // The new sample vector can contain SAMPLEINDEX_INVALID for adding new (empty) samples.
  259. // newOrder indices are zero-based, i.e. newOrder[0] will define the contents of the first sample slot.
  260. SAMPLEINDEX CModDoc::ReArrangeSamples(const std::vector<SAMPLEINDEX> &newOrder)
  261. {
  262. if(newOrder.size() > m_SndFile.GetModSpecifications().samplesMax)
  263. {
  264. return SAMPLEINDEX_INVALID;
  265. }
  266. CriticalSection cs;
  267. const SAMPLEINDEX oldNumSamples = m_SndFile.GetNumSamples(), newNumSamples = static_cast<SAMPLEINDEX>(newOrder.size());
  268. std::vector<int> sampleCount(oldNumSamples + 1, 0);
  269. std::vector<ModSample> sampleHeaders(oldNumSamples + 1);
  270. std::vector<SAMPLEINDEX> newIndex(oldNumSamples + 1, 0); // One of the new indexes for the old sample
  271. std::vector<std::string> sampleNames(oldNumSamples + 1);
  272. std::vector<mpt::PathString> samplePaths(oldNumSamples + 1);
  273. for(SAMPLEINDEX i = 0; i < newNumSamples; i++)
  274. {
  275. const SAMPLEINDEX origSlot = newOrder[i];
  276. if(origSlot > 0 && origSlot <= oldNumSamples)
  277. {
  278. sampleCount[origSlot]++;
  279. sampleHeaders[origSlot] = m_SndFile.GetSample(origSlot);
  280. if(!newIndex[origSlot])
  281. newIndex[origSlot] = i + 1;
  282. }
  283. }
  284. // First, delete all samples that will be removed anyway.
  285. for(SAMPLEINDEX i = 1; i < sampleCount.size(); i++)
  286. {
  287. if(sampleCount[i] == 0)
  288. {
  289. m_SndFile.DestroySample(i);
  290. GetSampleUndo().ClearUndo(i);
  291. }
  292. sampleNames[i] = m_SndFile.m_szNames[i];
  293. samplePaths[i] = m_SndFile.GetSamplePath(i);
  294. }
  295. // Remove sample data references from now unused slots.
  296. for(SAMPLEINDEX i = newNumSamples + 1; i <= oldNumSamples; i++)
  297. {
  298. m_SndFile.GetSample(i).pData.pSample = nullptr;
  299. m_SndFile.GetSample(i).nLength = 0;
  300. m_SndFile.m_szNames[i] = "";
  301. }
  302. // Now, create new sample list.
  303. m_SndFile.m_nSamples = std::max(m_SndFile.m_nSamples, newNumSamples); // Avoid assertions when using GetSample()...
  304. for(SAMPLEINDEX i = 0; i < newNumSamples; i++)
  305. {
  306. const SAMPLEINDEX origSlot = newOrder[i];
  307. ModSample &target = m_SndFile.GetSample(i + 1);
  308. if(origSlot > 0 && origSlot <= oldNumSamples)
  309. {
  310. // Copy an original sample.
  311. target = sampleHeaders[origSlot];
  312. if(--sampleCount[origSlot] > 0 && sampleHeaders[origSlot].HasSampleData())
  313. {
  314. // This sample slot is referenced multiple times, so we have to copy the actual sample.
  315. if(target.CopyWaveform(sampleHeaders[origSlot]))
  316. {
  317. target.PrecomputeLoops(m_SndFile, false);
  318. } else
  319. {
  320. Reporting::Error("Cannot duplicate sample - out of memory!");
  321. }
  322. }
  323. m_SndFile.m_szNames[i + 1] = sampleNames[origSlot];
  324. m_SndFile.SetSamplePath(i + 1, samplePaths[origSlot]);
  325. } else
  326. {
  327. // Invalid sample reference.
  328. target.pData.pSample = nullptr;
  329. target.Initialize(m_SndFile.GetType());
  330. m_SndFile.m_szNames[i + 1] = "";
  331. m_SndFile.ResetSamplePath(i + 1);
  332. }
  333. }
  334. GetSampleUndo().RearrangeSamples(newIndex);
  335. const auto muteFlag = CSoundFile::GetChannelMuteFlag();
  336. for(CHANNELINDEX c = 0; c < std::size(m_SndFile.m_PlayState.Chn); c++)
  337. {
  338. ModChannel &chn = m_SndFile.m_PlayState.Chn[c];
  339. for(SAMPLEINDEX i = 1; i <= oldNumSamples; i++)
  340. {
  341. if(chn.pModSample == &m_SndFile.GetSample(i))
  342. {
  343. chn.pModSample = &m_SndFile.GetSample(newIndex[i]);
  344. if(i == 0 || i > newNumSamples)
  345. {
  346. chn.Reset(ModChannel::resetTotal, m_SndFile, c, muteFlag);
  347. }
  348. break;
  349. }
  350. }
  351. }
  352. m_SndFile.m_nSamples = newNumSamples;
  353. if(m_SndFile.GetNumInstruments())
  354. {
  355. // Instrument mode: Update sample maps.
  356. for(INSTRUMENTINDEX i = 0; i <= m_SndFile.GetNumInstruments(); i++)
  357. {
  358. ModInstrument *ins = m_SndFile.Instruments[i];
  359. if(ins == nullptr)
  360. {
  361. continue;
  362. }
  363. GetInstrumentUndo().RearrangeSamples(i, newIndex);
  364. for(auto &sample : ins->Keyboard)
  365. {
  366. if(sample < newIndex.size())
  367. sample = newIndex[sample];
  368. else
  369. sample = 0;
  370. }
  371. }
  372. } else
  373. {
  374. PrepareUndoForAllPatterns(false, "Rearrange Samples");
  375. m_SndFile.Patterns.ForEachModCommand([&newIndex] (ModCommand &m)
  376. {
  377. if(!m.IsPcNote() && m.instr < newIndex.size())
  378. {
  379. m.instr = static_cast<ModCommand::INSTR>(newIndex[m.instr]);
  380. }
  381. });
  382. }
  383. return GetNumSamples();
  384. }
  385. // Base code for adding, removing, moving and duplicating instruments. Returns new number of instruments on success, INSTRUMENTINDEX_INVALID otherwise.
  386. // The new instrument vector can contain INSTRUMENTINDEX_INVALID for adding new (empty) instruments.
  387. // newOrder indices are zero-based, i.e. newOrder[0] will define the contents of the first instrument slot.
  388. INSTRUMENTINDEX CModDoc::ReArrangeInstruments(const std::vector<INSTRUMENTINDEX> &newOrder, deleteInstrumentSamples removeSamples)
  389. {
  390. if(newOrder.size() > m_SndFile.GetModSpecifications().instrumentsMax || GetNumInstruments() == 0)
  391. {
  392. return INSTRUMENTINDEX_INVALID;
  393. }
  394. CriticalSection cs;
  395. const INSTRUMENTINDEX oldNumInstruments = m_SndFile.GetNumInstruments(), newNumInstruments = static_cast<INSTRUMENTINDEX>(newOrder.size());
  396. std::vector<ModInstrument> instrumentHeaders(oldNumInstruments + 1);
  397. std::vector<INSTRUMENTINDEX> newIndex(oldNumInstruments + 1, 0); // One of the new indexes for the old instrument
  398. for(INSTRUMENTINDEX i = 0; i < newNumInstruments; i++)
  399. {
  400. const INSTRUMENTINDEX origSlot = newOrder[i];
  401. if(origSlot > 0 && origSlot <= oldNumInstruments)
  402. {
  403. if(m_SndFile.Instruments[origSlot] != nullptr)
  404. instrumentHeaders[origSlot] = *m_SndFile.Instruments[origSlot];
  405. newIndex[origSlot] = i + 1;
  406. }
  407. }
  408. // Delete unused instruments first.
  409. for(INSTRUMENTINDEX i = 1; i <= oldNumInstruments; i++)
  410. {
  411. if(newIndex[i] == 0)
  412. {
  413. m_SndFile.DestroyInstrument(i, removeSamples);
  414. }
  415. }
  416. m_SndFile.m_nInstruments = newNumInstruments;
  417. // Now, create new instrument list.
  418. for(INSTRUMENTINDEX i = 0; i < newNumInstruments; i++)
  419. {
  420. ModInstrument *ins = m_SndFile.AllocateInstrument(i + 1);
  421. if(ins == nullptr)
  422. {
  423. continue;
  424. }
  425. const INSTRUMENTINDEX origSlot = newOrder[i];
  426. if(origSlot > 0 && origSlot <= oldNumInstruments)
  427. {
  428. // Copy an original instrument.
  429. *ins = instrumentHeaders[origSlot];
  430. }
  431. }
  432. // Free unused instruments
  433. for(INSTRUMENTINDEX i = newNumInstruments + 1; i <= oldNumInstruments; i++)
  434. {
  435. m_SndFile.DestroyInstrument(i, doNoDeleteAssociatedSamples);
  436. }
  437. PrepareUndoForAllPatterns(false, "Rearrange Instruments");
  438. GetInstrumentUndo().RearrangeInstruments(newIndex);
  439. m_SndFile.Patterns.ForEachModCommand([&newIndex] (ModCommand &m)
  440. {
  441. if(!m.IsPcNote() && m.instr < newIndex.size())
  442. {
  443. m.instr = static_cast<ModCommand::INSTR>(newIndex[m.instr]);
  444. }
  445. });
  446. return GetNumInstruments();
  447. }
  448. SEQUENCEINDEX CModDoc::ReArrangeSequences(const std::vector<SEQUENCEINDEX> &newOrder)
  449. {
  450. CriticalSection cs;
  451. return m_SndFile.Order.Rearrange(newOrder) ? m_SndFile.Order.GetNumSequences() : SEQUENCEINDEX_INVALID;
  452. }
  453. bool CModDoc::ConvertInstrumentsToSamples()
  454. {
  455. if(!m_SndFile.GetNumInstruments())
  456. return false;
  457. GetInstrumentUndo().ClearUndo();
  458. m_SndFile.Patterns.ForEachModCommand([&] (ModCommand &m)
  459. {
  460. if(m.instr && !m.IsPcNote())
  461. {
  462. ModCommand::INSTR instr = m.instr, newinstr = 0;
  463. ModCommand::NOTE note = m.note, newnote = note;
  464. if(ModCommand::IsNote(note))
  465. note = note - NOTE_MIN;
  466. else
  467. note = NOTE_MIDDLEC - NOTE_MIN;
  468. if((instr < MAX_INSTRUMENTS) && (m_SndFile.Instruments[instr]))
  469. {
  470. const ModInstrument *pIns = m_SndFile.Instruments[instr];
  471. newinstr = static_cast<ModCommand::INSTR>(pIns->Keyboard[note]);
  472. newnote = pIns->NoteMap[note];
  473. if(pIns->Keyboard[note] > Util::MaxValueOfType(m.instr))
  474. newinstr = 0;
  475. }
  476. m.instr = newinstr;
  477. if(m.IsNote())
  478. {
  479. m.note = newnote;
  480. }
  481. }
  482. });
  483. return true;
  484. }
  485. bool CModDoc::ConvertSamplesToInstruments()
  486. {
  487. const INSTRUMENTINDEX instrumentMax = m_SndFile.GetModSpecifications().instrumentsMax;
  488. if(GetNumInstruments() > 0 || instrumentMax == 0)
  489. return false;
  490. // If there is no actual sample data, don't bother creating any instruments
  491. bool anySamples = false;
  492. for(SAMPLEINDEX smp = 1; smp <= m_SndFile.m_nSamples; smp++)
  493. {
  494. if(m_SndFile.GetSample(smp).HasSampleData())
  495. {
  496. anySamples = true;
  497. break;
  498. }
  499. }
  500. if(!anySamples)
  501. return true;
  502. m_SndFile.m_nInstruments = std::min(m_SndFile.GetNumSamples(), instrumentMax);
  503. for(SAMPLEINDEX smp = 1; smp <= m_SndFile.m_nInstruments; smp++)
  504. {
  505. const bool muted = IsSampleMuted(smp);
  506. MuteSample(smp, false);
  507. ModInstrument *instrument = m_SndFile.AllocateInstrument(smp, smp);
  508. if(instrument == nullptr)
  509. {
  510. ErrorBox(IDS_ERR_OUTOFMEMORY, CMainFrame::GetMainFrame());
  511. return false;
  512. }
  513. InitializeInstrument(instrument);
  514. instrument->name = m_SndFile.m_szNames[smp];
  515. MuteInstrument(smp, muted);
  516. }
  517. return true;
  518. }
  519. PLUGINDEX CModDoc::RemovePlugs(const std::vector<bool> &keepMask)
  520. {
  521. // Remove all plugins whose keepMask[plugindex] is false.
  522. PLUGINDEX nRemoved = 0;
  523. const PLUGINDEX maxPlug = std::min(MAX_MIXPLUGINS, static_cast<PLUGINDEX>(keepMask.size()));
  524. CriticalSection cs;
  525. for(PLUGINDEX nPlug = 0; nPlug < maxPlug; nPlug++)
  526. {
  527. SNDMIXPLUGIN &plug = m_SndFile.m_MixPlugins[nPlug];
  528. if(keepMask[nPlug])
  529. {
  530. continue;
  531. }
  532. if(plug.pMixPlugin || plug.IsValidPlugin())
  533. {
  534. nRemoved++;
  535. }
  536. plug.Destroy();
  537. plug = {};
  538. for(PLUGINDEX srcPlugSlot = 0; srcPlugSlot < nPlug; srcPlugSlot++)
  539. {
  540. SNDMIXPLUGIN &srcPlug = GetSoundFile().m_MixPlugins[srcPlugSlot];
  541. if(srcPlug.GetOutputPlugin() == nPlug)
  542. {
  543. srcPlug.SetOutputToMaster();
  544. UpdateAllViews(nullptr, PluginHint(static_cast<PLUGINDEX>(srcPlugSlot + 1)).Info());
  545. }
  546. }
  547. UpdateAllViews(nullptr, PluginHint(static_cast<PLUGINDEX>(nPlug + 1)).Info().Names());
  548. }
  549. if(nRemoved && m_SndFile.GetModSpecifications().supportsPlugins)
  550. SetModified();
  551. return nRemoved;
  552. }
  553. bool CModDoc::RemovePlugin(PLUGINDEX plugin)
  554. {
  555. if(plugin >= MAX_MIXPLUGINS)
  556. return false;
  557. std::vector<bool> keepMask(MAX_MIXPLUGINS, true);
  558. keepMask[plugin] = false;
  559. return RemovePlugs(keepMask) == 1;
  560. }
  561. // Clone a plugin slot (source does not necessarily have to be from the current module)
  562. void CModDoc::ClonePlugin(SNDMIXPLUGIN &target, const SNDMIXPLUGIN &source)
  563. {
  564. IMixPlugin *srcVstPlug = source.pMixPlugin;
  565. target.Destroy();
  566. target = source;
  567. // Don't want this plugin to be accidentally erased again...
  568. target.pMixPlugin = nullptr;
  569. if(target.editorX != int32_min)
  570. {
  571. // Move target editor a bit to visually distinguish it from the original editor
  572. int addPixels = Util::ScalePixels(16, CMainFrame::GetMainFrame()->m_hWnd);
  573. target.editorX += addPixels;
  574. target.editorY += addPixels;
  575. }
  576. #ifndef NO_PLUGINS
  577. if(theApp.GetPluginManager()->CreateMixPlugin(target, GetSoundFile()))
  578. {
  579. IMixPlugin *newVstPlug = target.pMixPlugin;
  580. newVstPlug->SetCurrentProgram(srcVstPlug->GetCurrentProgram());
  581. std::ostringstream f(std::ios::out | std::ios::binary);
  582. if(VSTPresets::SaveFile(f, *srcVstPlug, false))
  583. {
  584. const std::string data = f.str();
  585. FileReader file(mpt::as_span(data));
  586. VSTPresets::LoadFile(file, *newVstPlug);
  587. }
  588. }
  589. #endif // !NO_PLUGINS
  590. }
  591. PATTERNINDEX CModDoc::InsertPattern(ROWINDEX rows, ORDERINDEX ord)
  592. {
  593. if(ord != ORDERINDEX_INVALID && m_SndFile.Order().GetLengthTailTrimmed() >= m_SndFile.GetModSpecifications().ordersMax)
  594. return PATTERNINDEX_INVALID;
  595. PATTERNINDEX pat = m_SndFile.Patterns.InsertAny(rows, true);
  596. if(pat != PATTERNINDEX_INVALID)
  597. {
  598. if(ord != ORDERINDEX_INVALID)
  599. {
  600. m_SndFile.Order().insert(ord, 1, pat);
  601. }
  602. SetModified();
  603. }
  604. return pat;
  605. }
  606. SAMPLEINDEX CModDoc::InsertSample()
  607. {
  608. SAMPLEINDEX i = m_SndFile.GetNextFreeSample();
  609. if((i > std::numeric_limits<ModCommand::INSTR>::max() && !m_SndFile.GetNumInstruments()) || i == SAMPLEINDEX_INVALID)
  610. {
  611. ErrorBox(IDS_ERR_TOOMANYSMP, CMainFrame::GetMainFrame());
  612. return SAMPLEINDEX_INVALID;
  613. }
  614. const bool newSlot = (i > m_SndFile.GetNumSamples());
  615. if(newSlot || !m_SndFile.m_szNames[i][0])
  616. m_SndFile.m_szNames[i] = "untitled";
  617. if(newSlot)
  618. m_SndFile.m_nSamples = i;
  619. m_SndFile.GetSample(i).Initialize(m_SndFile.GetType());
  620. m_SndFile.ResetSamplePath(i);
  621. SetModified();
  622. return i;
  623. }
  624. // Insert a new instrument assigned to sample nSample or duplicate instrument "duplicateSource".
  625. // If "sample" is invalid, an appropriate sample slot is selected. 0 means "no sample".
  626. INSTRUMENTINDEX CModDoc::InsertInstrument(SAMPLEINDEX sample, INSTRUMENTINDEX duplicateSource, bool silent)
  627. {
  628. if(m_SndFile.GetModSpecifications().instrumentsMax == 0)
  629. return INSTRUMENTINDEX_INVALID;
  630. ModInstrument *pDup = nullptr;
  631. if(duplicateSource > 0 && duplicateSource <= m_SndFile.m_nInstruments)
  632. {
  633. pDup = m_SndFile.Instruments[duplicateSource];
  634. }
  635. if(!m_SndFile.GetNumInstruments() && (m_SndFile.GetNumSamples() > 1 || m_SndFile.GetSample(1).HasSampleData()))
  636. {
  637. bool doConvert = true;
  638. if(!silent)
  639. {
  640. ConfirmAnswer result = Reporting::Confirm("Convert existing samples to instruments first?", true);
  641. if(result == cnfCancel)
  642. {
  643. return INSTRUMENTINDEX_INVALID;
  644. }
  645. doConvert = (result == cnfYes);
  646. }
  647. if(doConvert)
  648. {
  649. if(!ConvertSamplesToInstruments())
  650. {
  651. return INSTRUMENTINDEX_INVALID;
  652. }
  653. }
  654. }
  655. const INSTRUMENTINDEX newins = m_SndFile.GetNextFreeInstrument();
  656. if(newins == INSTRUMENTINDEX_INVALID)
  657. {
  658. if(!silent)
  659. {
  660. ErrorBox(IDS_ERR_TOOMANYINS, CMainFrame::GetMainFrame());
  661. }
  662. return INSTRUMENTINDEX_INVALID;
  663. } else if(newins > m_SndFile.GetNumInstruments())
  664. {
  665. m_SndFile.m_nInstruments = newins;
  666. }
  667. // Determine which sample slot to use
  668. SAMPLEINDEX newsmp = 0;
  669. if(sample < m_SndFile.GetModSpecifications().samplesMax)
  670. {
  671. // Use specified slot
  672. newsmp = sample;
  673. } else if(!pDup)
  674. {
  675. newsmp = m_SndFile.GetNextFreeSample(newins);
  676. if(newsmp > m_SndFile.GetNumSamples())
  677. {
  678. // Add a new sample
  679. const SAMPLEINDEX inssmp = InsertSample();
  680. if(inssmp != SAMPLEINDEX_INVALID)
  681. newsmp = inssmp;
  682. }
  683. }
  684. CriticalSection cs;
  685. ModInstrument *pIns = m_SndFile.AllocateInstrument(newins, newsmp);
  686. if(pIns == nullptr)
  687. {
  688. if(!silent)
  689. {
  690. cs.Leave();
  691. ErrorBox(IDS_ERR_OUTOFMEMORY, CMainFrame::GetMainFrame());
  692. }
  693. return INSTRUMENTINDEX_INVALID;
  694. }
  695. InitializeInstrument(pIns);
  696. if(pDup)
  697. {
  698. *pIns = *pDup;
  699. }
  700. SetModified();
  701. return newins;
  702. }
  703. // Load default instrument values for inserting new instrument during editing
  704. void CModDoc::InitializeInstrument(ModInstrument *pIns)
  705. {
  706. pIns->pluginVolumeHandling = TrackerSettings::Instance().DefaultPlugVolumeHandling;
  707. }
  708. // Try to set up a new instrument that is linked to a given plugin
  709. INSTRUMENTINDEX CModDoc::InsertInstrumentForPlugin(PLUGINDEX plug)
  710. {
  711. #ifndef NO_PLUGINS
  712. const bool first = (GetNumInstruments() == 0);
  713. INSTRUMENTINDEX instr = InsertInstrument(0, INSTRUMENTINDEX_INVALID, true);
  714. if(instr == INSTRUMENTINDEX_INVALID)
  715. return INSTRUMENTINDEX_INVALID;
  716. ModInstrument &ins = *m_SndFile.Instruments[instr];
  717. ins.name = mpt::ToCharset(m_SndFile.GetCharsetInternal(), MPT_UFORMAT("{}: {}")(plug + 1, m_SndFile.m_MixPlugins[plug].GetName()));
  718. ins.filename = mpt::ToCharset(m_SndFile.GetCharsetInternal(), m_SndFile.m_MixPlugins[plug].GetLibraryName());
  719. ins.nMixPlug = plug + 1;
  720. ins.nMidiChannel = 1;
  721. InstrumentHint hint = InstrumentHint(instr).Info().Envelope().Names();
  722. if(first)
  723. hint.ModType();
  724. UpdateAllViews(nullptr, hint);
  725. if(m_SndFile.GetModSpecifications().supportsPlugins)
  726. {
  727. SetModified();
  728. }
  729. return instr;
  730. #else
  731. return INSTRUMENTINDEX_INVALID;
  732. #endif
  733. }
  734. INSTRUMENTINDEX CModDoc::HasInstrumentForPlugin(PLUGINDEX plug) const
  735. {
  736. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
  737. {
  738. if(m_SndFile.Instruments[i] != nullptr && m_SndFile.Instruments[i]->nMixPlug == plug + 1)
  739. {
  740. return i;
  741. }
  742. }
  743. return INSTRUMENTINDEX_INVALID;
  744. }
  745. bool CModDoc::RemoveOrder(SEQUENCEINDEX nSeq, ORDERINDEX nOrd)
  746. {
  747. if(nSeq >= m_SndFile.Order.GetNumSequences() || nOrd >= m_SndFile.Order(nSeq).size())
  748. return false;
  749. CriticalSection cs;
  750. m_SndFile.Order(nSeq).Remove(nOrd, nOrd);
  751. SetModified();
  752. return true;
  753. }
  754. bool CModDoc::RemovePattern(PATTERNINDEX nPat)
  755. {
  756. if(m_SndFile.Patterns.IsValidPat(nPat))
  757. {
  758. CriticalSection cs;
  759. GetPatternUndo().PrepareUndo(nPat, 0, 0, GetNumChannels(), m_SndFile.Patterns[nPat].GetNumRows(), "Remove Pattern");
  760. m_SndFile.Patterns.Remove(nPat);
  761. SetModified();
  762. return true;
  763. }
  764. return false;
  765. }
  766. bool CModDoc::RemoveSample(SAMPLEINDEX nSmp)
  767. {
  768. if((nSmp) && (nSmp <= m_SndFile.GetNumSamples()))
  769. {
  770. CriticalSection cs;
  771. m_SndFile.DestroySample(nSmp);
  772. m_SndFile.m_szNames[nSmp] = "";
  773. while((m_SndFile.GetNumSamples() > 1)
  774. && (!m_SndFile.m_szNames[m_SndFile.GetNumSamples()][0])
  775. && (!m_SndFile.GetSample(m_SndFile.GetNumSamples()).HasSampleData()))
  776. {
  777. m_SndFile.m_nSamples--;
  778. }
  779. SetModified();
  780. return true;
  781. }
  782. return false;
  783. }
  784. bool CModDoc::RemoveInstrument(INSTRUMENTINDEX nIns)
  785. {
  786. if((nIns) && (nIns <= m_SndFile.GetNumInstruments()) && (m_SndFile.Instruments[nIns]))
  787. {
  788. ConfirmAnswer result = cnfNo;
  789. if(!m_SndFile.Instruments[nIns]->GetSamples().empty())
  790. result = Reporting::Confirm("Remove samples associated with an instrument if they are unused?", "Removing instrument", true);
  791. if(result == cnfCancel)
  792. {
  793. return false;
  794. }
  795. if(m_SndFile.DestroyInstrument(nIns, (result == cnfYes) ? deleteAssociatedSamples : doNoDeleteAssociatedSamples))
  796. {
  797. CriticalSection cs;
  798. if(nIns == m_SndFile.m_nInstruments)
  799. m_SndFile.m_nInstruments--;
  800. bool instrumentsLeft = std::find_if(std::begin(m_SndFile.Instruments), std::end(m_SndFile.Instruments), [](ModInstrument *ins) { return ins != nullptr; }) != std::end(m_SndFile.Instruments);
  801. if(!instrumentsLeft)
  802. m_SndFile.m_nInstruments = 0;
  803. SetModified();
  804. return true;
  805. }
  806. }
  807. return false;
  808. }
  809. bool CModDoc::MoveOrder(ORDERINDEX sourceOrd, ORDERINDEX destOrd, bool update, bool copy, SEQUENCEINDEX sourceSeq, SEQUENCEINDEX destSeq)
  810. {
  811. if(sourceSeq == SEQUENCEINDEX_INVALID)
  812. sourceSeq = m_SndFile.Order.GetCurrentSequenceIndex();
  813. if(destSeq == SEQUENCEINDEX_INVALID)
  814. destSeq = m_SndFile.Order.GetCurrentSequenceIndex();
  815. if(std::max(sourceSeq, destSeq) >= m_SndFile.Order.GetNumSequences())
  816. return false;
  817. const ORDERINDEX maxOrders = m_SndFile.GetModSpecifications().ordersMax;
  818. if(destOrd > maxOrders)
  819. return false;
  820. if(destOrd == maxOrders && (sourceSeq != destSeq || copy))
  821. return false;
  822. auto &sourceSequence = m_SndFile.Order(sourceSeq);
  823. const PATTERNINDEX sourcePat = sourceOrd < sourceSequence.size() ? sourceSequence[sourceOrd] : sourceSequence.GetInvalidPatIndex();
  824. // Delete source
  825. if(!copy)
  826. {
  827. sourceSequence.Remove(sourceOrd, sourceOrd);
  828. if(sourceOrd < destOrd && sourceSeq == destSeq)
  829. destOrd--;
  830. }
  831. // Insert at dest
  832. m_SndFile.Order(destSeq).insert(destOrd, 1, sourcePat);
  833. if(update)
  834. {
  835. UpdateAllViews(nullptr, SequenceHint().Data());
  836. }
  837. return true;
  838. }
  839. BOOL CModDoc::ExpandPattern(PATTERNINDEX nPattern)
  840. {
  841. ROWINDEX numRows;
  842. if(!m_SndFile.Patterns.IsValidPat(nPattern)
  843. || (numRows = m_SndFile.Patterns[nPattern].GetNumRows()) > m_SndFile.GetModSpecifications().patternRowsMax / 2)
  844. {
  845. return false;
  846. }
  847. BeginWaitCursor();
  848. CriticalSection cs;
  849. GetPatternUndo().PrepareUndo(nPattern, 0, 0, GetNumChannels(), numRows, "Expand Pattern");
  850. bool success = m_SndFile.Patterns[nPattern].Expand();
  851. cs.Leave();
  852. EndWaitCursor();
  853. if(success)
  854. {
  855. SetModified();
  856. UpdateAllViews(NULL, PatternHint(nPattern).Data(), NULL);
  857. } else
  858. {
  859. GetPatternUndo().RemoveLastUndoStep();
  860. }
  861. return success;
  862. }
  863. BOOL CModDoc::ShrinkPattern(PATTERNINDEX nPattern)
  864. {
  865. ROWINDEX numRows;
  866. if(!m_SndFile.Patterns.IsValidPat(nPattern)
  867. || (numRows = m_SndFile.Patterns[nPattern].GetNumRows()) < m_SndFile.GetModSpecifications().patternRowsMin * 2)
  868. {
  869. return false;
  870. }
  871. BeginWaitCursor();
  872. CriticalSection cs;
  873. GetPatternUndo().PrepareUndo(nPattern, 0, 0, GetNumChannels(), numRows, "Shrink Pattern");
  874. bool success = m_SndFile.Patterns[nPattern].Shrink();
  875. cs.Leave();
  876. EndWaitCursor();
  877. if(success)
  878. {
  879. SetModified();
  880. UpdateAllViews(NULL, PatternHint(nPattern).Data(), NULL);
  881. } else
  882. {
  883. GetPatternUndo().RemoveLastUndoStep();
  884. }
  885. return success;
  886. }
  887. /////////////////////////////////////////////////////////////////////////////////////////
  888. // Copy/Paste envelope
  889. static constexpr const char pszEnvHdr[] = "ModPlug Tracker Envelope\r\n";
  890. static constexpr const char pszEnvFmt[] = "%d,%d,%d,%d,%d,%d,%d,%d\r\n";
  891. static bool EnvelopeToString(CStringA &s, const InstrumentEnvelope &env)
  892. {
  893. // We don't want to copy empty envelopes
  894. if(env.empty())
  895. {
  896. return false;
  897. }
  898. s.Preallocate(2048);
  899. s = pszEnvHdr;
  900. s.AppendFormat(pszEnvFmt, env.size(), env.nSustainStart, env.nSustainEnd, env.nLoopStart, env.nLoopEnd, env.dwFlags[ENV_SUSTAIN] ? 1 : 0, env.dwFlags[ENV_LOOP] ? 1 : 0, env.dwFlags[ENV_CARRY] ? 1 : 0);
  901. for(auto &p : env)
  902. {
  903. s.AppendFormat("%d,%d\r\n", p.tick, p.value);
  904. }
  905. // Writing release node
  906. s.AppendFormat("%u\r\n", env.nReleaseNode);
  907. return true;
  908. }
  909. static bool StringToEnvelope(const std::string_view &s, InstrumentEnvelope &env, const CModSpecifications &specs)
  910. {
  911. uint32 susBegin = 0, susEnd = 0, loopBegin = 0, loopEnd = 0, bSus = 0, bLoop = 0, bCarry = 0, nPoints = 0, releaseNode = ENV_RELEASE_NODE_UNSET;
  912. size_t length = s.size(), pos = std::size(pszEnvHdr) - 1;
  913. if(length <= pos || mpt::CompareNoCaseAscii(s.data(), pszEnvHdr, pos - 2))
  914. {
  915. return false;
  916. }
  917. sscanf(&s[pos], pszEnvFmt, &nPoints, &susBegin, &susEnd, &loopBegin, &loopEnd, &bSus, &bLoop, &bCarry);
  918. while(pos < length && s[pos] != '\r' && s[pos] != '\n')
  919. pos++;
  920. nPoints = std::min(nPoints, static_cast<uint32>(specs.envelopePointsMax));
  921. if(susEnd >= nPoints)
  922. susEnd = 0;
  923. if(susBegin > susEnd)
  924. susBegin = susEnd;
  925. if(loopEnd >= nPoints)
  926. loopEnd = 0;
  927. if(loopBegin > loopEnd)
  928. loopBegin = loopEnd;
  929. try
  930. {
  931. env.resize(nPoints);
  932. } catch(mpt::out_of_memory e)
  933. {
  934. mpt::delete_out_of_memory(e);
  935. return false;
  936. }
  937. env.nSustainStart = static_cast<decltype(env.nSustainStart)>(susBegin);
  938. env.nSustainEnd = static_cast<decltype(env.nSustainEnd)>(susEnd);
  939. env.nLoopStart = static_cast<decltype(env.nLoopStart)>(loopBegin);
  940. env.nLoopEnd = static_cast<decltype(env.nLoopEnd)>(loopEnd);
  941. env.nReleaseNode = static_cast<decltype(env.nReleaseNode)>(releaseNode);
  942. env.dwFlags.set(ENV_LOOP, bLoop != 0);
  943. env.dwFlags.set(ENV_SUSTAIN, bSus != 0);
  944. env.dwFlags.set(ENV_CARRY, bCarry != 0);
  945. env.dwFlags.set(ENV_ENABLED, nPoints > 0);
  946. int oldn = 0;
  947. for(auto &p : env)
  948. {
  949. while(pos < length && (s[pos] < '0' || s[pos] > '9'))
  950. pos++;
  951. if(pos >= length)
  952. break;
  953. int n1 = atoi(&s[pos]);
  954. while(pos < length && s[pos] != ',')
  955. pos++;
  956. while(pos < length && (s[pos] < '0' || s[pos] > '9'))
  957. pos++;
  958. if(pos >= length)
  959. break;
  960. int n2 = atoi(&s[pos]);
  961. if(n1 < oldn)
  962. n1 = oldn + 1;
  963. Limit(n2, ENVELOPE_MIN, ENVELOPE_MAX);
  964. p.tick = (uint16)n1;
  965. p.value = (uint8)n2;
  966. oldn = n1;
  967. while(pos < length && s[pos] != '\r' && s[pos] != '\n')
  968. pos++;
  969. if(pos >= length)
  970. break;
  971. }
  972. env.Sanitize();
  973. // Read release node information.
  974. env.nReleaseNode = ENV_RELEASE_NODE_UNSET;
  975. if(pos < length)
  976. {
  977. auto r = static_cast<decltype(env.nReleaseNode)>(atoi(&s[pos]));
  978. if(r == 0 || r >= nPoints || !specs.hasReleaseNode)
  979. r = ENV_RELEASE_NODE_UNSET;
  980. env.nReleaseNode = r;
  981. }
  982. return true;
  983. }
  984. bool CModDoc::CopyEnvelope(INSTRUMENTINDEX nIns, EnvelopeType nEnv)
  985. {
  986. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  987. if((nIns < 1) || (nIns > m_SndFile.m_nInstruments) || (!m_SndFile.Instruments[nIns]) || (!pMainFrm))
  988. return false;
  989. BeginWaitCursor();
  990. const ModInstrument *pIns = m_SndFile.Instruments[nIns];
  991. if(pIns == nullptr)
  992. return false;
  993. CStringA s;
  994. EnvelopeToString(s, pIns->GetEnvelope(nEnv));
  995. int memSize = s.GetLength() + 1;
  996. Clipboard clipboard(CF_TEXT, memSize);
  997. if(auto p = clipboard.As<char>())
  998. {
  999. memcpy(p, s.GetString(), memSize);
  1000. }
  1001. EndWaitCursor();
  1002. return true;
  1003. }
  1004. bool CModDoc::SaveEnvelope(INSTRUMENTINDEX ins, EnvelopeType env, const mpt::PathString &fileName)
  1005. {
  1006. if(ins < 1 || ins > m_SndFile.m_nInstruments || !m_SndFile.Instruments[ins])
  1007. return false;
  1008. BeginWaitCursor();
  1009. const ModInstrument *pIns = m_SndFile.Instruments[ins];
  1010. if(pIns == nullptr)
  1011. return false;
  1012. CStringA s;
  1013. EnvelopeToString(s, pIns->GetEnvelope(env));
  1014. bool ok = false;
  1015. try
  1016. {
  1017. mpt::SafeOutputFile sf(fileName, std::ios::binary, mpt::FlushModeFromBool(TrackerSettings::Instance().MiscFlushFileBuffersOnSave));
  1018. mpt::ofstream &f = sf;
  1019. f.exceptions(f.exceptions() | std::ios::badbit | std::ios::failbit);
  1020. if(f)
  1021. ok = mpt::IO::WriteRaw(f, s.GetString(), s.GetLength());
  1022. else
  1023. ok = false;
  1024. } catch(const std::exception &)
  1025. {
  1026. ok = false;
  1027. }
  1028. EndWaitCursor();
  1029. return ok;
  1030. }
  1031. bool CModDoc::PasteEnvelope(INSTRUMENTINDEX ins, EnvelopeType env)
  1032. {
  1033. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  1034. if(ins < 1 || ins > m_SndFile.m_nInstruments || !m_SndFile.Instruments[ins] || !pMainFrm)
  1035. return false;
  1036. BeginWaitCursor();
  1037. Clipboard clipboard(CF_TEXT);
  1038. auto data = clipboard.GetString();
  1039. if(!data.length())
  1040. {
  1041. EndWaitCursor();
  1042. return false;
  1043. }
  1044. bool result = StringToEnvelope(data, m_SndFile.Instruments[ins]->GetEnvelope(env), m_SndFile.GetModSpecifications());
  1045. EndWaitCursor();
  1046. return result;
  1047. }
  1048. bool CModDoc::LoadEnvelope(INSTRUMENTINDEX nIns, EnvelopeType nEnv, const mpt::PathString &fileName)
  1049. {
  1050. InputFile f(fileName, TrackerSettings::Instance().MiscCacheCompleteFileBeforeLoading);
  1051. if(nIns < 1 || nIns > m_SndFile.m_nInstruments || !m_SndFile.Instruments[nIns] || !f.IsValid())
  1052. return false;
  1053. BeginWaitCursor();
  1054. FileReader file = GetFileReader(f);
  1055. std::string data;
  1056. file.ReadNullString(data, 1 << 16);
  1057. bool result = StringToEnvelope(data, m_SndFile.Instruments[nIns]->GetEnvelope(nEnv), m_SndFile.GetModSpecifications());
  1058. EndWaitCursor();
  1059. return result;
  1060. }
  1061. // Check which channels contain note data. maxRemoveCount specified how many empty channels are reported at max.
  1062. void CModDoc::CheckUsedChannels(std::vector<bool> &usedMask, CHANNELINDEX maxRemoveCount) const
  1063. {
  1064. // Checking for unused channels
  1065. CHANNELINDEX chn = GetNumChannels();
  1066. usedMask.assign(chn, true);
  1067. while(chn-- > 0)
  1068. {
  1069. if(IsChannelUnused(chn))
  1070. {
  1071. usedMask[chn] = false;
  1072. // Found enough empty channels yet?
  1073. if((--maxRemoveCount) == 0)
  1074. break;
  1075. }
  1076. }
  1077. }
  1078. // Check if a given channel contains note data or global effects.
  1079. bool CModDoc::IsChannelUnused(CHANNELINDEX nChn) const
  1080. {
  1081. const CHANNELINDEX nChannels = GetNumChannels();
  1082. if(nChn >= nChannels)
  1083. {
  1084. return true;
  1085. }
  1086. for(auto &pat : m_SndFile.Patterns)
  1087. {
  1088. if(pat.IsValid())
  1089. {
  1090. const ModCommand *p = pat.GetpModCommand(0, nChn);
  1091. for(ROWINDEX row = pat.GetNumRows(); row > 0; row--, p += nChannels)
  1092. {
  1093. if(p->IsNote() || p->IsInstrPlug() || p->IsGlobalCommand())
  1094. return false;
  1095. }
  1096. }
  1097. }
  1098. return true;
  1099. }
  1100. bool CModDoc::IsSampleUsed(SAMPLEINDEX sample, bool searchInMutedChannels) const
  1101. {
  1102. if(!sample || sample > GetNumSamples())
  1103. return false;
  1104. if(GetNumInstruments())
  1105. {
  1106. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
  1107. {
  1108. if(m_SndFile.IsSampleReferencedByInstrument(sample, i))
  1109. return true;
  1110. }
  1111. } else
  1112. {
  1113. for(const auto &pattern : m_SndFile.Patterns)
  1114. {
  1115. if(!pattern.IsValid())
  1116. continue;
  1117. auto m = pattern.cbegin();
  1118. for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
  1119. {
  1120. for(CHANNELINDEX chn = 0; chn < pattern.GetNumChannels(); chn++, m++)
  1121. {
  1122. if(searchInMutedChannels || !m_SndFile.ChnSettings[chn].dwFlags[CHN_MUTE])
  1123. {
  1124. if(m->instr == sample && !m->IsPcNote())
  1125. return true;
  1126. }
  1127. }
  1128. }
  1129. }
  1130. }
  1131. return false;
  1132. }
  1133. bool CModDoc::IsInstrumentUsed(INSTRUMENTINDEX instr, bool searchInMutedChannels) const
  1134. {
  1135. if(instr < 1 || instr > GetNumInstruments())
  1136. return false;
  1137. for(const auto &pattern : m_SndFile.Patterns)
  1138. {
  1139. if(!pattern.IsValid())
  1140. continue;
  1141. auto m = pattern.cbegin();
  1142. for(ROWINDEX row = 0; row < pattern.GetNumRows(); row++)
  1143. {
  1144. for(CHANNELINDEX chn = 0; chn < pattern.GetNumChannels(); chn++, m++)
  1145. {
  1146. if(searchInMutedChannels || !m_SndFile.ChnSettings[chn].dwFlags[CHN_MUTE])
  1147. {
  1148. if(m->instr == instr && !m->IsPcNote())
  1149. return true;
  1150. }
  1151. }
  1152. }
  1153. }
  1154. return false;
  1155. }
  1156. // Convert module's default global volume to a pattern command.
  1157. bool CModDoc::GlobalVolumeToPattern()
  1158. {
  1159. bool result = false;
  1160. if(m_SndFile.GetModSpecifications().HasCommand(CMD_GLOBALVOLUME))
  1161. {
  1162. for(PATTERNINDEX pat : m_SndFile.Order())
  1163. {
  1164. if(m_SndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_GLOBALVOLUME, mpt::saturate_cast<ModCommand::PARAM>(m_SndFile.m_nDefaultGlobalVolume * 64 / MAX_GLOBAL_VOLUME)).RetryNextRow()))
  1165. {
  1166. result = true;
  1167. break;
  1168. }
  1169. }
  1170. }
  1171. m_SndFile.m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
  1172. return result;
  1173. }
  1174. SAMPLEINDEX CModDoc::GetSampleIndex(const ModCommand &m, ModCommand::INSTR lastInstr) const
  1175. {
  1176. if(m.IsPcNote())
  1177. return 0;
  1178. return m_SndFile.GetSampleIndex(m.note, m.instr > 0 ? m.instr : lastInstr);
  1179. }
  1180. int CModDoc::GetInstrumentGroupSize(INSTRUMENTINDEX instr) const
  1181. {
  1182. const ModInstrument *ins;
  1183. if(instr > 0 && instr <= GetNumInstruments()
  1184. && (ins = m_SndFile.Instruments[instr]) != nullptr && ins->pTuning != nullptr
  1185. && ins->pTuning->GetGroupSize() != 0)
  1186. {
  1187. return ins->pTuning->GetGroupSize();
  1188. }
  1189. return 12;
  1190. }
  1191. int CModDoc::GetBaseNote(INSTRUMENTINDEX instr) const
  1192. {
  1193. // This may look a bit strange (using -12 and -4 instead of just -5 in the second part) but this is to keep custom tunings centered around middle-C on the keyboard.
  1194. return NOTE_MIDDLEC - 12 + (CMainFrame::GetMainFrame()->GetBaseOctave() - 4) * GetInstrumentGroupSize(instr);
  1195. }
  1196. ModCommand::NOTE CModDoc::GetNoteWithBaseOctave(int noteOffset, INSTRUMENTINDEX instr) const
  1197. {
  1198. return static_cast<ModCommand::NOTE>(Clamp(GetBaseNote(instr) + noteOffset, NOTE_MIN, NOTE_MAX));
  1199. }
  1200. OPENMPT_NAMESPACE_END