winamp/Src/external_dependencies/openmpt-trunk/mptrack/SampleTrimmer.cpp
2024-09-24 14:54:57 +02:00

179 lines
5.2 KiB
C++

/*
* SampleTrimmer.cpp
* -----------------
* Purpose: Automatic trimming of unused sample parts for module size optimization.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include <numeric>
#include "InputHandler.h"
#include "Mainfrm.h"
#include "Moddoc.h"
#include "ProgressDialog.h"
#include "../tracklib/SampleEdit.h"
#include "../soundlib/OPL.h"
OPENMPT_NAMESPACE_BEGIN
class CRenderProgressDlg : public CProgressDialog
{
CSoundFile &m_SndFile;
class DummyAudioTarget : public IAudioTarget
{
public:
void Process(mpt::audio_span_interleaved<MixSampleInt>) override { }
void Process(mpt::audio_span_interleaved<MixSampleFloat>) override { }
};
public:
std::vector<SmpLength> m_SamplePlayLengths;
CRenderProgressDlg(CWnd *parent, CSoundFile &sndFile)
: CProgressDialog{parent}
, m_SndFile{sndFile}
{
m_SndFile.m_SamplePlayLengths = &m_SamplePlayLengths;
}
~CRenderProgressDlg()
{
m_SndFile.m_SamplePlayLengths = nullptr;
}
void Run() override
{
// We're not interested in plugin rendering
std::bitset<MAX_MIXPLUGINS> plugMuteStatus;
for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
{
plugMuteStatus[i] = m_SndFile.m_MixPlugins[i].IsBypassed();
m_SndFile.m_MixPlugins[i].SetBypass(true);
}
m_SamplePlayLengths.assign(m_SndFile.GetNumSamples() + 1, 0);
const auto origSequence = m_SndFile.Order.GetCurrentSequenceIndex();
const auto origRepeatCount = m_SndFile.GetRepeatCount();
auto opl = std::move(m_SndFile.m_opl);
m_SndFile.SetRepeatCount(0);
m_SndFile.m_bIsRendering = true;
auto prevTime = timeGetTime();
const auto subSongs = m_SndFile.GetAllSubSongs();
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()));
size_t totalSamples = 0;
for(size_t i = 0; i < subSongs.size() && !m_abort; i++)
{
SetWindowText(MPT_CFORMAT("Automatic Sample Trimmer - Song {} / {}")(i + 1, subSongs.size()));
const auto &song = subSongs[i];
m_SndFile.ResetPlayPos();
m_SndFile.GetLength(eAdjust, GetLengthTarget(song.startOrder, song.startRow).StartPos(song.sequence, 0, 0));
m_SndFile.m_SongFlags.reset(SONG_PLAY_FLAGS);
size_t subsongSamples = 0;
DummyAudioTarget target;
while(!m_abort)
{
auto count = m_SndFile.Read(MIXBUFFERSIZE, target);
if(count == 0)
break;
totalSamples += count;
subsongSamples += count;
auto currentTime = timeGetTime();
if(currentTime - prevTime >= 16)
{
prevTime = currentTime;
auto timeSec = subsongSamples / m_SndFile.GetSampleRate();
SetText(MPT_CFORMAT("Analyzing... {}:{}:{}")(timeSec / 3600, mpt::cfmt::dec0<2>((timeSec / 60) % 60), mpt::cfmt::dec0<2>(timeSec % 60)));
SetProgress(totalSamples);
ProcessMessages();
}
}
}
// Reset globals to previous values
m_SndFile.Order.SetSequence(origSequence);
m_SndFile.SetRepeatCount(origRepeatCount);
m_SndFile.ResetPlayPos();
m_SndFile.StopAllVsti();
m_SndFile.m_bIsRendering = false;
m_SndFile.m_opl = std::move(opl);
for(PLUGINDEX i = 0; i < MAX_MIXPLUGINS; i++)
{
m_SndFile.m_MixPlugins[i].SetBypass(plugMuteStatus[i]);
}
EndDialog(IDOK);
}
};
void CModDoc::OnShowSampleTrimmer()
{
BypassInputHandler bih;
CMainFrame::GetMainFrame()->StopMod(this);
CRenderProgressDlg dlg(CMainFrame::GetMainFrame(), m_SndFile);
dlg.DoModal();
SAMPLEINDEX numTrimmed = 0;
SmpLength numBytes = 0;
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
ModSample &sample = m_SndFile.GetSample(smp);
auto &newLength = dlg.m_SamplePlayLengths[smp];
if(newLength == 0)
continue;
// Take interpolation look-ahead into account
if((!sample.uFlags[CHN_LOOP] || newLength != sample.nLoopEnd)
&& (!sample.uFlags[CHN_SUSTAINLOOP] || newLength != sample.nSustainEnd))
{
newLength = std::min(newLength + InterpolationMaxLookahead, sample.nLength);
}
if(sample.nLength > newLength)
{
numTrimmed++;
numBytes += (sample.nLength - newLength) * sample.GetBytesPerSample();
}
}
if(numTrimmed == 0)
{
Reporting::Information(_T("No samples can be trimmed, because all samples are played in their full length."));
return;
}
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_(""));
if(dlg.m_abort)
{
s += U_("\n\nWARNING: Only partial results are available, possibly causing used sample parts to be trimmed.\nContinue anyway?");
} else
{
s += U_(" Continue?");
}
if(Reporting::Confirm(s, false, dlg.m_abort) == cnfYes)
{
for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
{
ModSample &sample = m_SndFile.GetSample(smp);
if(dlg.m_SamplePlayLengths[smp] != 0 && sample.nLength > dlg.m_SamplePlayLengths[smp])
{
GetSampleUndo().PrepareUndo(smp, sundo_delete, "Automatic Sample Trimming", dlg.m_SamplePlayLengths[smp], sample.nLength);
SampleEdit::ResizeSample(sample, dlg.m_SamplePlayLengths[smp], m_SndFile);
sample.uFlags.set(SMP_MODIFIED);
}
}
SetModified();
UpdateAllViews(SampleHint().Data().Info());
}
}
OPENMPT_NAMESPACE_END