1
0

SampleTrimmer.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /*
  2. * SampleTrimmer.cpp
  3. * -----------------
  4. * Purpose: Automatic trimming of unused sample parts for module size optimization.
  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 <numeric>
  11. #include "InputHandler.h"
  12. #include "Mainfrm.h"
  13. #include "Moddoc.h"
  14. #include "ProgressDialog.h"
  15. #include "../tracklib/SampleEdit.h"
  16. #include "../soundlib/OPL.h"
  17. OPENMPT_NAMESPACE_BEGIN
  18. class CRenderProgressDlg : public CProgressDialog
  19. {
  20. CSoundFile &m_SndFile;
  21. class DummyAudioTarget : public IAudioTarget
  22. {
  23. public:
  24. void Process(mpt::audio_span_interleaved<MixSampleInt>) override { }
  25. void Process(mpt::audio_span_interleaved<MixSampleFloat>) override { }
  26. };
  27. public:
  28. std::vector<SmpLength> m_SamplePlayLengths;
  29. CRenderProgressDlg(CWnd *parent, CSoundFile &sndFile)
  30. : CProgressDialog{parent}
  31. , m_SndFile{sndFile}
  32. {
  33. m_SndFile.m_SamplePlayLengths = &m_SamplePlayLengths;
  34. }
  35. ~CRenderProgressDlg()
  36. {
  37. m_SndFile.m_SamplePlayLengths = nullptr;
  38. }
  39. void Run() override
  40. {
  41. // We're not interested in plugin rendering
  42. std::bitset<MAX_MIXPLUGINS> plugMuteStatus;
  43. for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
  44. {
  45. plugMuteStatus[i] = m_SndFile.m_MixPlugins[i].IsBypassed();
  46. m_SndFile.m_MixPlugins[i].SetBypass(true);
  47. }
  48. m_SamplePlayLengths.assign(m_SndFile.GetNumSamples() + 1, 0);
  49. const auto origSequence = m_SndFile.Order.GetCurrentSequenceIndex();
  50. const auto origRepeatCount = m_SndFile.GetRepeatCount();
  51. auto opl = std::move(m_SndFile.m_opl);
  52. m_SndFile.SetRepeatCount(0);
  53. m_SndFile.m_bIsRendering = true;
  54. auto prevTime = timeGetTime();
  55. const auto subSongs = m_SndFile.GetAllSubSongs();
  56. SetRange(0, mpt::saturate_round<uint64>(std::accumulate(subSongs.begin(), subSongs.end(), 0.0, [](double acc, const auto& song) { return acc + song.duration; }) * m_SndFile.GetSampleRate()));
  57. size_t totalSamples = 0;
  58. for(size_t i = 0; i < subSongs.size() && !m_abort; i++)
  59. {
  60. SetWindowText(MPT_CFORMAT("Automatic Sample Trimmer - Song {} / {}")(i + 1, subSongs.size()));
  61. const auto &song = subSongs[i];
  62. m_SndFile.ResetPlayPos();
  63. m_SndFile.GetLength(eAdjust, GetLengthTarget(song.startOrder, song.startRow).StartPos(song.sequence, 0, 0));
  64. m_SndFile.m_SongFlags.reset(SONG_PLAY_FLAGS);
  65. size_t subsongSamples = 0;
  66. DummyAudioTarget target;
  67. while(!m_abort)
  68. {
  69. auto count = m_SndFile.Read(MIXBUFFERSIZE, target);
  70. if(count == 0)
  71. break;
  72. totalSamples += count;
  73. subsongSamples += count;
  74. auto currentTime = timeGetTime();
  75. if(currentTime - prevTime >= 16)
  76. {
  77. prevTime = currentTime;
  78. auto timeSec = subsongSamples / m_SndFile.GetSampleRate();
  79. SetText(MPT_CFORMAT("Analyzing... {}:{}:{}")(timeSec / 3600, mpt::cfmt::dec0<2>((timeSec / 60) % 60), mpt::cfmt::dec0<2>(timeSec % 60)));
  80. SetProgress(totalSamples);
  81. ProcessMessages();
  82. }
  83. }
  84. }
  85. // Reset globals to previous values
  86. m_SndFile.Order.SetSequence(origSequence);
  87. m_SndFile.SetRepeatCount(origRepeatCount);
  88. m_SndFile.ResetPlayPos();
  89. m_SndFile.StopAllVsti();
  90. m_SndFile.m_bIsRendering = false;
  91. m_SndFile.m_opl = std::move(opl);
  92. for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
  93. {
  94. m_SndFile.m_MixPlugins[i].SetBypass(plugMuteStatus[i]);
  95. }
  96. EndDialog(IDOK);
  97. }
  98. };
  99. void CModDoc::OnShowSampleTrimmer()
  100. {
  101. BypassInputHandler bih;
  102. CMainFrame::GetMainFrame()->StopMod(this);
  103. CRenderProgressDlg dlg(CMainFrame::GetMainFrame(), m_SndFile);
  104. dlg.DoModal();
  105. SAMPLEINDEX numTrimmed = 0;
  106. SmpLength numBytes = 0;
  107. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
  108. {
  109. ModSample &sample = m_SndFile.GetSample(smp);
  110. auto &newLength = dlg.m_SamplePlayLengths[smp];
  111. if(newLength == 0)
  112. continue;
  113. // Take interpolation look-ahead into account
  114. if((!sample.uFlags[CHN_LOOP] || newLength != sample.nLoopEnd)
  115. && (!sample.uFlags[CHN_SUSTAINLOOP] || newLength != sample.nSustainEnd))
  116. {
  117. newLength = std::min(newLength + InterpolationMaxLookahead, sample.nLength);
  118. }
  119. if(sample.nLength > newLength)
  120. {
  121. numTrimmed++;
  122. numBytes += (sample.nLength - newLength) * sample.GetBytesPerSample();
  123. }
  124. }
  125. if(numTrimmed == 0)
  126. {
  127. Reporting::Information(_T("No samples can be trimmed, because all samples are played in their full length."));
  128. return;
  129. }
  130. mpt::ustring s = MPT_UFORMAT("{} sample{} can be trimmed, saving {} byte{}.")(numTrimmed, (numTrimmed == 1) ? U_("") : U_("s"), mpt::ufmt::dec(3, ',', numBytes), numBytes != 1 ? U_("s") : U_(""));
  131. if(dlg.m_abort)
  132. {
  133. s += U_("\n\nWARNING: Only partial results are available, possibly causing used sample parts to be trimmed.\nContinue anyway?");
  134. } else
  135. {
  136. s += U_(" Continue?");
  137. }
  138. if(Reporting::Confirm(s, false, dlg.m_abort) == cnfYes)
  139. {
  140. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
  141. {
  142. ModSample &sample = m_SndFile.GetSample(smp);
  143. if(dlg.m_SamplePlayLengths[smp] != 0 && sample.nLength > dlg.m_SamplePlayLengths[smp])
  144. {
  145. GetSampleUndo().PrepareUndo(smp, sundo_delete, "Automatic Sample Trimming", dlg.m_SamplePlayLengths[smp], sample.nLength);
  146. SampleEdit::ResizeSample(sample, dlg.m_SamplePlayLengths[smp], m_SndFile);
  147. sample.uFlags.set(SMP_MODIFIED);
  148. }
  149. }
  150. SetModified();
  151. UpdateAllViews(SampleHint().Data().Info());
  152. }
  153. }
  154. OPENMPT_NAMESPACE_END