diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/CleanupSong.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/CleanupSong.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/CleanupSong.cpp | 977 |
1 files changed, 977 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/CleanupSong.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/CleanupSong.cpp new file mode 100644 index 00000000..6120516f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/CleanupSong.cpp @@ -0,0 +1,977 @@ +/* + * CleanupSong.cpp + * --------------- + * Purpose: Dialog for cleaning up modules (rearranging, removing unused items). + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Moddoc.h" +#include "Mainfrm.h" +#include "CleanupSong.h" +#include "../common/mptStringBuffer.h" +#include "../soundlib/mod_specifications.h" +#include "../soundlib/modsmp_ctrl.h" +#include "../tracklib/SampleEdit.h" + + +OPENMPT_NAMESPACE_BEGIN + + +// Default checkbox state +bool CModCleanupDlg::m_CheckBoxes[kMaxCleanupOptions] = +{ + true, false, true, true, // patterns + false, false, // orders + true, false, false, true, // samples + true, false, // instruments + true, false, // plugins + false, true, // misc +}; + +// Checkbox -> Control ID LUT +WORD const CModCleanupDlg::m_CleanupIDtoDlgID[kMaxCleanupOptions] = +{ + // patterns + IDC_CHK_CLEANUP_PATTERNS, IDC_CHK_REMOVE_PATTERNS, + IDC_CHK_REARRANGE_PATTERNS, IDC_CHK_REMOVE_DUPLICATES, + // orders + IDC_CHK_MERGE_SEQUENCES, IDC_CHK_REMOVE_ORDERS, + // samples + IDC_CHK_CLEANUP_SAMPLES, IDC_CHK_REMOVE_SAMPLES, + IDC_CHK_REARRANGE_SAMPLES, IDC_CHK_OPTIMIZE_SAMPLES, + // instruments + IDC_CHK_CLEANUP_INSTRUMENTS, IDC_CHK_REMOVE_INSTRUMENTS, + // plugins + IDC_CHK_CLEANUP_PLUGINS, IDC_CHK_REMOVE_PLUGINS, + // misc + IDC_CHK_RESET_VARIABLES, IDC_CHK_UNUSED_CHANNELS, +}; + +// Options that are mutually exclusive to each other +CModCleanupDlg::CleanupOptions const CModCleanupDlg::m_MutuallyExclusive[CModCleanupDlg::kMaxCleanupOptions] = +{ + // patterns + kRemovePatterns, kCleanupPatterns, + kRemovePatterns, kRemovePatterns, + // orders + kRemoveOrders, kMergeSequences, + // samples + kRemoveSamples, kCleanupSamples, + kRemoveSamples, kRemoveSamples, + // instruments + kRemoveAllInstruments, kCleanupInstruments, + // plugins + kRemoveAllPlugins, kCleanupPlugins, + // misc + kNone, kNone, + +}; + +/////////////////////////////////////////////////////////////////////// +// CModCleanupDlg + +BEGIN_MESSAGE_MAP(CModCleanupDlg, CDialog) + //{{AFX_MSG_MAP(CModTypeDlg) + ON_COMMAND(IDC_BTN_CLEANUP_SONG, &CModCleanupDlg::OnPresetCleanupSong) + ON_COMMAND(IDC_BTN_COMPO_CLEANUP, &CModCleanupDlg::OnPresetCompoCleanup) + + ON_COMMAND(IDC_CHK_CLEANUP_PATTERNS, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_REMOVE_PATTERNS, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_REARRANGE_PATTERNS, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_REMOVE_DUPLICATES, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_MERGE_SEQUENCES, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_REMOVE_ORDERS, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_CLEANUP_SAMPLES, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_REMOVE_SAMPLES, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_REARRANGE_SAMPLES, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_OPTIMIZE_SAMPLES, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_CLEANUP_INSTRUMENTS, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_REMOVE_INSTRUMENTS, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_CLEANUP_PLUGINS, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_REMOVE_PLUGINS, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_RESET_VARIABLES, &CModCleanupDlg::OnVerifyMutualExclusive) + ON_COMMAND(IDC_CHK_UNUSED_CHANNELS, &CModCleanupDlg::OnVerifyMutualExclusive) + + ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CModCleanupDlg::OnToolTipNotify) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +BOOL CModCleanupDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + for(int i = 0; i < kMaxCleanupOptions; i++) + { + CheckDlgButton(m_CleanupIDtoDlgID[i], (m_CheckBoxes[i]) ? BST_CHECKED : BST_UNCHECKED); + } + + CSoundFile &sndFile = modDoc.GetSoundFile(); + + GetDlgItem(m_CleanupIDtoDlgID[kMergeSequences])->EnableWindow((sndFile.Order.GetNumSequences() > 1) ? TRUE : FALSE); + + GetDlgItem(m_CleanupIDtoDlgID[kRemoveSamples])->EnableWindow((sndFile.GetNumSamples() > 0) ? TRUE : FALSE); + GetDlgItem(m_CleanupIDtoDlgID[kRearrangeSamples])->EnableWindow((sndFile.GetNumSamples() > 1) ? TRUE : FALSE); + + GetDlgItem(m_CleanupIDtoDlgID[kCleanupInstruments])->EnableWindow((sndFile.GetNumInstruments() > 0) ? TRUE : FALSE); + GetDlgItem(m_CleanupIDtoDlgID[kRemoveAllInstruments])->EnableWindow((sndFile.GetNumInstruments() > 0) ? TRUE : FALSE); + + EnableToolTips(TRUE); + return TRUE; +} + + +void CModCleanupDlg::OnOK() +{ + ScopedLogCapturer logcapturer(modDoc, _T("cleanup"), this); + for(int i = 0; i < kMaxCleanupOptions; i++) + { + m_CheckBoxes[i] = IsDlgButtonChecked(m_CleanupIDtoDlgID[i]) != BST_UNCHECKED; + } + + bool modified = false; + + // Orders + if(m_CheckBoxes[kMergeSequences]) modified |= MergeSequences(); + if(m_CheckBoxes[kRemoveOrders]) modified |= RemoveAllOrders(); + + // Patterns + if(m_CheckBoxes[kRemovePatterns]) modified |= RemoveAllPatterns(); + if(m_CheckBoxes[kCleanupPatterns]) modified |= RemoveUnusedPatterns(); + if(m_CheckBoxes[kRemoveDuplicatePatterns]) modified |= RemoveDuplicatePatterns(); + if(m_CheckBoxes[kRearrangePatterns]) modified |= RearrangePatterns(); + + // Instruments + if(modDoc.GetNumInstruments() > 0) + { + if(m_CheckBoxes[kRemoveAllInstruments]) modified |= RemoveAllInstruments(); + if(m_CheckBoxes[kCleanupInstruments]) modified |= RemoveUnusedInstruments(); + } + + // Samples + if(m_CheckBoxes[kRemoveSamples]) modified |= RemoveAllSamples(); + if(m_CheckBoxes[kCleanupSamples]) modified |= RemoveUnusedSamples(); + if(m_CheckBoxes[kOptimizeSamples]) modified |= OptimizeSamples(); + if(modDoc.GetNumSamples() > 1) + { + if(m_CheckBoxes[kRearrangeSamples]) modified |= RearrangeSamples(); + } + + // Plugins + if(m_CheckBoxes[kRemoveAllPlugins]) modified |= RemoveAllPlugins(); + if(m_CheckBoxes[kCleanupPlugins]) modified |= RemoveUnusedPlugins(); + + // Create samplepack + if(m_CheckBoxes[kResetVariables]) modified |= ResetVariables(); + + // Remove unused channels + if(m_CheckBoxes[kCleanupChannels]) modified |= RemoveUnusedChannels(); + + if(modified) modDoc.SetModified(); + modDoc.UpdateAllViews(nullptr, UpdateHint().ModType()); + logcapturer.ShowLog(true); + CDialog::OnOK(); +} + + +void CModCleanupDlg::OnVerifyMutualExclusive() +{ + HWND hFocus = GetFocus()->m_hWnd; + for(int i = 0; i < kMaxCleanupOptions; i++) + { + // if this item is focussed, we have just (un)checked it. + if(hFocus == GetDlgItem(m_CleanupIDtoDlgID[i])->m_hWnd) + { + // if we just unchecked it, there's nothing to verify. + if(IsDlgButtonChecked(m_CleanupIDtoDlgID[i]) == BST_UNCHECKED) + return; + + // now we can disable all elements that are mutually exclusive. + if(m_MutuallyExclusive[i] != kNone) + CheckDlgButton(m_CleanupIDtoDlgID[m_MutuallyExclusive[i]], BST_UNCHECKED); + // find other elements which are mutually exclusive with the selected element. + for(int j = 0; j < kMaxCleanupOptions; j++) + { + if(m_MutuallyExclusive[j] == i) + CheckDlgButton(m_CleanupIDtoDlgID[j], BST_UNCHECKED); + } + return; + } + } +} + + +void CModCleanupDlg::OnPresetCleanupSong() +{ + // patterns + CheckDlgButton(IDC_CHK_CLEANUP_PATTERNS, BST_CHECKED); + CheckDlgButton(IDC_CHK_REMOVE_PATTERNS, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REARRANGE_PATTERNS, BST_CHECKED); + CheckDlgButton(IDC_CHK_REMOVE_DUPLICATES, BST_CHECKED); + // orders + CheckDlgButton(IDC_CHK_MERGE_SEQUENCES, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REMOVE_ORDERS, BST_UNCHECKED); + // samples + CheckDlgButton(IDC_CHK_CLEANUP_SAMPLES, BST_CHECKED); + CheckDlgButton(IDC_CHK_REMOVE_SAMPLES, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REARRANGE_SAMPLES, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_OPTIMIZE_SAMPLES, BST_CHECKED); + // instruments + CheckDlgButton(IDC_CHK_CLEANUP_INSTRUMENTS, BST_CHECKED); + CheckDlgButton(IDC_CHK_REMOVE_INSTRUMENTS, BST_UNCHECKED); + // plugins + CheckDlgButton(IDC_CHK_CLEANUP_PLUGINS, BST_CHECKED); + CheckDlgButton(IDC_CHK_REMOVE_PLUGINS, BST_UNCHECKED); + // misc + CheckDlgButton(IDC_CHK_SAMPLEPACK, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_UNUSED_CHANNELS, BST_CHECKED); +} + + +void CModCleanupDlg::OnPresetCompoCleanup() +{ + // patterns + CheckDlgButton(IDC_CHK_CLEANUP_PATTERNS, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REMOVE_PATTERNS, BST_CHECKED); + CheckDlgButton(IDC_CHK_REARRANGE_PATTERNS, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REMOVE_DUPLICATES, BST_UNCHECKED); + // orders + CheckDlgButton(IDC_CHK_MERGE_SEQUENCES, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REMOVE_ORDERS, BST_CHECKED); + // samples + CheckDlgButton(IDC_CHK_CLEANUP_SAMPLES, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REMOVE_SAMPLES, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REARRANGE_SAMPLES, BST_CHECKED); + CheckDlgButton(IDC_CHK_OPTIMIZE_SAMPLES, BST_UNCHECKED); + // instruments + CheckDlgButton(IDC_CHK_CLEANUP_INSTRUMENTS, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REMOVE_INSTRUMENTS, BST_CHECKED); + // plugins + CheckDlgButton(IDC_CHK_CLEANUP_PLUGINS, BST_UNCHECKED); + CheckDlgButton(IDC_CHK_REMOVE_PLUGINS, BST_CHECKED); + // misc + CheckDlgButton(IDC_CHK_SAMPLEPACK, BST_CHECKED); + CheckDlgButton(IDC_CHK_UNUSED_CHANNELS, BST_CHECKED); +} + + +BOOL CModCleanupDlg::OnToolTipNotify(UINT, NMHDR *pNMHDR, LRESULT *) +{ + TOOLTIPTEXT* pTTT = (TOOLTIPTEXT*)pNMHDR; + UINT_PTR nID = pNMHDR->idFrom; + if (pTTT->uFlags & TTF_IDISHWND) + { + // idFrom is actually the HWND of the tool + nID = ::GetDlgCtrlID((HWND)nID); + } + + LPCTSTR lpszText = nullptr; + switch(nID) + { + // patterns + case IDC_CHK_CLEANUP_PATTERNS: + lpszText = _T("Remove all unused patterns and rearrange them."); + break; + case IDC_CHK_REMOVE_PATTERNS: + lpszText = _T("Remove all patterns."); + break; + case IDC_CHK_REARRANGE_PATTERNS: + lpszText = _T("Number the patterns given by their order in the sequence."); + break; + case IDC_CHK_REMOVE_DUPLICATES: + lpszText = _T("Merge patterns with identical content."); + break; + // orders + case IDC_CHK_REMOVE_ORDERS: + lpszText = _T("Reset the order list."); + break; + case IDC_CHK_MERGE_SEQUENCES: + lpszText = _T("Merge multiple sequences into one."); + break; + // samples + case IDC_CHK_CLEANUP_SAMPLES: + lpszText = _T("Remove all unused samples."); + break; + case IDC_CHK_REMOVE_SAMPLES: + lpszText = _T("Remove all samples."); + break; + case IDC_CHK_REARRANGE_SAMPLES: + lpszText = _T("Reorder sample list by removing empty samples."); + break; + case IDC_CHK_OPTIMIZE_SAMPLES: + lpszText = _T("Remove unused data after the sample loop end."); + break; + // instruments + case IDC_CHK_CLEANUP_INSTRUMENTS: + lpszText = _T("Remove all unused instruments."); + break; + case IDC_CHK_REMOVE_INSTRUMENTS: + lpszText = _T("Remove all instruments and convert them to samples."); + break; + // plugins + case IDC_CHK_CLEANUP_PLUGINS: + lpszText = _T("Remove all unused plugins."); + break; + case IDC_CHK_REMOVE_PLUGINS: + lpszText = _T("Remove all plugins."); + break; + // misc + case IDC_CHK_SAMPLEPACK: + lpszText = _T("Convert the module to .IT and reset song / sample / instrument variables"); + break; + case IDC_CHK_UNUSED_CHANNELS: + lpszText = _T("Removes all empty pattern channels."); + break; + default: + lpszText = _T(""); + break; + } + pTTT->lpszText = const_cast<LPTSTR>(lpszText); + return TRUE; +} + + +/////////////////////////////////////////////////////////////////////// +// Actual cleanup implementations + +bool CModCleanupDlg::RemoveDuplicatePatterns() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + const PATTERNINDEX numPatterns = sndFile.Patterns.Size(); + std::vector<PATTERNINDEX> patternMapping(numPatterns, PATTERNINDEX_INVALID); + + BeginWaitCursor(); + CriticalSection cs; + + PATTERNINDEX foundDupes = 0; + for(PATTERNINDEX pat1 = 0; pat1 < numPatterns; pat1++) + { + if(!sndFile.Patterns.IsValidPat(pat1)) + continue; + const CPattern &pattern1 = sndFile.Patterns[pat1]; + for(PATTERNINDEX pat2 = pat1 + 1; pat2 < numPatterns; pat2++) + { + if(!sndFile.Patterns.IsValidPat(pat2) || patternMapping[pat2] != PATTERNINDEX_INVALID) + continue; + const CPattern &pattern2 = sndFile.Patterns[pat2]; + if(pattern1 == pattern2) + { + modDoc.GetPatternUndo().PrepareUndo(pat2, 0, 0, pattern2.GetNumChannels(), pattern2.GetNumRows(), "Remove Duplicate Patterns", foundDupes != 0, false); + sndFile.Patterns.Remove(pat2); + + patternMapping[pat2] = pat1; + foundDupes++; + } + } + } + + if(foundDupes != 0) + { + modDoc.AddToLog(MPT_AFORMAT("{} duplicate pattern{} merged.")(foundDupes, foundDupes == 1 ? "" : "s")); + + // Fix order list + for(auto &order : sndFile.Order) + { + for(auto &pat : order) + { + if(pat < numPatterns && patternMapping[pat] != PATTERNINDEX_INVALID) + { + pat = patternMapping[pat]; + } + } + } + } + + EndWaitCursor(); + + return foundDupes != 0; +} + + +// Remove unused patterns +bool CModCleanupDlg::RemoveUnusedPatterns() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + const PATTERNINDEX numPatterns = sndFile.Patterns.Size(); + std::vector<bool> patternUsed(numPatterns, false); + + BeginWaitCursor(); + // First, find all used patterns in all sequences. + for(auto &order : sndFile.Order) + { + for(auto pat : order) + { + if(pat < numPatterns) + { + patternUsed[pat] = true; + } + } + } + + // Remove all other patterns. + CriticalSection cs; + PATTERNINDEX numRemovedPatterns = 0; + for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) + { + if(!patternUsed[pat] && sndFile.Patterns.IsValidPat(pat)) + { + numRemovedPatterns++; + modDoc.GetPatternUndo().PrepareUndo(pat, 0, 0, sndFile.GetNumChannels(), sndFile.Patterns[pat].GetNumRows(), "Remove Unused Patterns", numRemovedPatterns != 0, false); + sndFile.Patterns.Remove(pat); + } + } + EndWaitCursor(); + + if(numRemovedPatterns) + { + modDoc.AddToLog(MPT_AFORMAT("{} pattern{} removed.")(numRemovedPatterns, numRemovedPatterns == 1 ? "" : "s")); + return true; + } + return false; +} + + +// Rearrange patterns (first pattern in order list = 0, etc...) +bool CModCleanupDlg::RearrangePatterns() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + const PATTERNINDEX numPatterns = sndFile.Patterns.Size(); + std::vector<PATTERNINDEX> newIndex(numPatterns, PATTERNINDEX_INVALID); + + bool modified = false; + + BeginWaitCursor(); + CriticalSection cs; + + // First, find all used patterns in all sequences. + PATTERNINDEX patOrder = 0; + for(auto &order : sndFile.Order) + { + for(auto &pat : order) + { + if(pat < numPatterns) + { + if(newIndex[pat] == PATTERNINDEX_INVALID) + { + newIndex[pat] = patOrder++; + } + pat = newIndex[pat]; + } + } + } + // All unused patterns are moved to the end of the pattern list. + for(PATTERNINDEX pat = 0; pat < numPatterns; pat++) + { + PATTERNINDEX &index = newIndex[pat]; + if(index == PATTERNINDEX_INVALID && sndFile.Patterns.IsValidPat(pat)) + { + index = patOrder++; + } + } + // Also need new indices for any non-existent patterns + for(auto &index : newIndex) + { + if(index == PATTERNINDEX_INVALID) + { + index = patOrder++; + } + } + + modDoc.GetPatternUndo().RearrangePatterns(newIndex); + + // Now rearrange the actual patterns + for(PATTERNINDEX i = 0; i < static_cast<PATTERNINDEX>(newIndex.size()); i++) + { + PATTERNINDEX j = newIndex[i]; + if(i == j) + continue; + while(i < j) + j = newIndex[j]; + std::swap(sndFile.Patterns[i], sndFile.Patterns[j]); + modified = true; + } + + EndWaitCursor(); + + return modified; +} + + +// Remove unused samples +bool CModCleanupDlg::RemoveUnusedSamples() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + std::vector<bool> samplesUsed(sndFile.GetNumSamples() + 1, true); + + BeginWaitCursor(); + + // Check if any samples are not referenced in the patterns (sample mode) or by an instrument (instrument mode). + // This doesn't check yet if a sample is referenced by an instrument, but actually unused in the patterns. + for(SAMPLEINDEX smp = 1; smp <= sndFile.GetNumSamples(); smp++) if (sndFile.GetSample(smp).HasSampleData()) + { + if(!modDoc.IsSampleUsed(smp)) + { + samplesUsed[smp] = false; + } + } + + SAMPLEINDEX nRemoved = sndFile.RemoveSelectedSamples(samplesUsed); + + const SAMPLEINDEX unusedInsSamples = sndFile.DetectUnusedSamples(samplesUsed); + + EndWaitCursor(); + + if(unusedInsSamples) + { + mpt::ustring s = MPT_UFORMAT("OpenMPT detected {} sample{} referenced by an instrument,\nbut not used in the song. Do you want to remove them?") + ( unusedInsSamples + , (unusedInsSamples == 1) ? U_("") : U_("s") + ); + if(Reporting::Confirm(s, "Sample Cleanup", false, false, this) == cnfYes) + { + nRemoved += sndFile.RemoveSelectedSamples(samplesUsed); + } + } + + if(nRemoved > 0) + { + modDoc.AddToLog(LogNotification, MPT_UFORMAT("{} unused sample{} removed")(nRemoved, (nRemoved == 1) ? U_("") : U_("s"))); + } + + return (nRemoved > 0); +} + + +// Check if the stereo channels of a sample contain identical data +template<typename T> +static bool ComapreStereoChannels(SmpLength length, const T *sampleData) +{ + for(SmpLength i = 0; i < length; i++, sampleData += 2) + { + if(sampleData[0] != sampleData[1]) + { + return false; + } + } + return true; +} + +// Remove unused sample data +bool CModCleanupDlg::OptimizeSamples() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + SAMPLEINDEX numLoopOpt = 0, numStereoOpt = 0; + std::vector<bool> stereoOptSamples(sndFile.GetNumSamples(), false); + + for(SAMPLEINDEX smp = 1; smp <= sndFile.GetNumSamples(); smp++) + { + const ModSample &sample = sndFile.GetSample(smp); + + // Determine how much of the sample will be played + SmpLength loopLength = sample.nLength; + if(sample.uFlags[CHN_LOOP]) + { + loopLength = sample.nLoopEnd; + if(sample.uFlags[CHN_SUSTAINLOOP]) + { + loopLength = std::max(sample.nLoopEnd, sample.nSustainEnd); + } + } + + // Check if the sample contains identical stereo channels + if(sample.GetNumChannels() == 2) + { + bool identicalChannels = false; + if(sample.GetElementarySampleSize() == 1) + { + identicalChannels = ComapreStereoChannels(loopLength, sample.sample8()); + } else if(sample.GetElementarySampleSize() == 2) + { + identicalChannels = ComapreStereoChannels(loopLength, sample.sample16()); + } + if(identicalChannels) + { + numStereoOpt++; + stereoOptSamples[smp - 1] = true; + } + } + + if(sample.HasSampleData() && sample.nLength > loopLength + 2) numLoopOpt++; + } + if(!numLoopOpt && !numStereoOpt) return false; + + std::string s; + if(numLoopOpt) + s = MPT_AFORMAT("{} sample{} unused data after the loop end point.\n")(numLoopOpt, (numLoopOpt == 1) ? " has" : "s have"); + if(numStereoOpt) + s += MPT_AFORMAT("{} stereo sample{} actually mono.\n")(numStereoOpt, (numStereoOpt == 1) ? " is" : "s are"); + if(numLoopOpt + numStereoOpt == 1) + s += "Do you want to optimize it and remove this unused data?"; + else + s += "Do you want to optimize them and remove this unused data?"; + + if(Reporting::Confirm(s.c_str(), "Sample Optimization", false, false, this) != cnfYes) + { + return false; + } + + for(SAMPLEINDEX smp = 1; smp <= sndFile.m_nSamples; smp++) + { + ModSample &sample = sndFile.GetSample(smp); + + // Determine how much of the sample will be played + SmpLength loopLength = sample.nLength; + if(sample.uFlags[CHN_LOOP]) + { + loopLength = sample.nLoopEnd; + + // Sustain loop is played before normal loop, and it can actually be located after the normal loop. + if(sample.uFlags[CHN_SUSTAINLOOP]) + { + loopLength = std::max(sample.nLoopEnd, sample.nSustainEnd); + } + } + + if(sample.nLength > loopLength && loopLength >= 2) + { + modDoc.GetSampleUndo().PrepareUndo(smp, sundo_delete, "Trim Unused Data", loopLength, sample.nLength); + SampleEdit::ResizeSample(sample, loopLength, sndFile); + } + + // Convert stereo samples with identical channels to mono + if(stereoOptSamples[smp - 1]) + { + modDoc.GetSampleUndo().PrepareUndo(smp, sundo_replace, "Mono Conversion"); + ctrlSmp::ConvertToMono(sample, sndFile, ctrlSmp::onlyLeft); + } + } + if(numLoopOpt) + { + s = MPT_AFORMAT("{} sample loop{} optimized")(numLoopOpt, (numLoopOpt == 1) ? "" : "s"); + modDoc.AddToLog(s); + } + if(numStereoOpt) + { + s = MPT_AFORMAT("{} sample{} converted to mono")(numStereoOpt, (numStereoOpt == 1) ? "" : "s"); + modDoc.AddToLog(s); + } + return true; +} + +// Rearrange sample list +bool CModCleanupDlg::RearrangeSamples() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + if(sndFile.GetNumSamples() < 2) + return false; + + std::vector<SAMPLEINDEX> sampleMap; + sampleMap.reserve(sndFile.GetNumSamples()); + + // First, find out which sample slots are unused and create the new sample map only with used samples + for(SAMPLEINDEX i = 1; i <= sndFile.GetNumSamples(); i++) + { + if(sndFile.GetSample(i).HasSampleData()) + { + sampleMap.push_back(i); + } + } + + // Nothing found to remove... + if(sndFile.GetNumSamples() == sampleMap.size()) + { + return false; + } + + return (modDoc.ReArrangeSamples(sampleMap) != SAMPLEINDEX_INVALID); +} + + +// Remove unused instruments +bool CModCleanupDlg::RemoveUnusedInstruments() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + if(!sndFile.GetNumInstruments()) + return false; + + deleteInstrumentSamples removeSamples = doNoDeleteAssociatedSamples; + if(Reporting::Confirm("Remove samples associated with unused instruments?", "Removing unused instruments", false, false, this) == cnfYes) + { + removeSamples = deleteAssociatedSamples; + } + + BeginWaitCursor(); + + std::vector<bool> instrUsed(sndFile.GetNumInstruments()); + bool prevUsed = true, reorder = false; + INSTRUMENTINDEX numUsed = 0, lastUsed = 1; + for(INSTRUMENTINDEX i = 0; i < sndFile.GetNumInstruments(); i++) + { + instrUsed[i] = (modDoc.IsInstrumentUsed(i + 1)); + if(instrUsed[i]) + { + numUsed++; + lastUsed = i; + if(!prevUsed) + { + reorder = true; + } + } + prevUsed = instrUsed[i]; + } + + EndWaitCursor(); + + if(reorder && numUsed >= 1) + { + reorder = (Reporting::Confirm("Do you want to reorganize the remaining instruments?", "Removing unused instruments", false, false, this) == cnfYes); + } else + { + reorder = false; + } + + const INSTRUMENTINDEX numRemoved = sndFile.GetNumInstruments() - numUsed; + + if(numRemoved != 0) + { + BeginWaitCursor(); + + std::vector<INSTRUMENTINDEX> instrMap; + instrMap.reserve(sndFile.GetNumInstruments()); + for(INSTRUMENTINDEX i = 0; i < sndFile.GetNumInstruments(); i++) + { + if(instrUsed[i]) + { + instrMap.push_back(i + 1); + } else if(!reorder && i < lastUsed) + { + instrMap.push_back(INSTRUMENTINDEX_INVALID); + } + } + + modDoc.ReArrangeInstruments(instrMap, removeSamples); + + EndWaitCursor(); + + modDoc.AddToLog(LogNotification, MPT_UFORMAT("{} unused instrument{} removed")(numRemoved, (numRemoved == 1) ? U_("") : U_("s"))); + return true; + } + return false; +} + + +// Remove ununsed plugins +bool CModCleanupDlg::RemoveUnusedPlugins() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + std::vector<bool> usedmap(MAX_MIXPLUGINS, false); + + for(PLUGINDEX nPlug = 0; nPlug < MAX_MIXPLUGINS; nPlug++) + { + + // Is the plugin assigned to a channel? + for(CHANNELINDEX nChn = 0; nChn < sndFile.GetNumChannels(); nChn++) + { + if (sndFile.ChnSettings[nChn].nMixPlugin == nPlug + 1) + { + usedmap[nPlug] = true; + break; + } + } + + // Is the plugin used by an instrument? + for(INSTRUMENTINDEX nIns = 1; nIns <= sndFile.GetNumInstruments(); nIns++) + { + if (sndFile.Instruments[nIns] && (sndFile.Instruments[nIns]->nMixPlug == nPlug + 1)) + { + usedmap[nPlug] = true; + break; + } + } + + // Is the plugin assigned to master? + if(sndFile.m_MixPlugins[nPlug].IsMasterEffect()) + usedmap[nPlug] = true; + + // All outputs of used plugins count as used + if(usedmap[nPlug]) + { + if(!sndFile.m_MixPlugins[nPlug].IsOutputToMaster()) + { + PLUGINDEX output = sndFile.m_MixPlugins[nPlug].GetOutputPlugin(); + if(output != PLUGINDEX_INVALID) + { + usedmap[output] = true; + } + } + } + + } + + PLUGINDEX numRemoved = modDoc.RemovePlugs(usedmap); + if(numRemoved != 0) + { + modDoc.AddToLog(LogInformation, MPT_UFORMAT("{} unused plugin{} removed")(numRemoved, (numRemoved == 1) ? U_("") : U_("s"))); + return true; + } + return false; +} + + +// Reset variables (convert to IT, reset global/smp/ins vars, etc.) +bool CModCleanupDlg::ResetVariables() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + if(Reporting::Confirm(_T("OpenMPT will convert the module to IT format and reset all song, sample and instrument attributes to default values. Continue?"), _T("Resetting variables"), false, false, this) == cnfNo) + return false; + + // Stop play. + CMainFrame::GetMainFrame()->StopMod(&modDoc); + + BeginWaitCursor(); + CriticalSection cs; + + // Convert to IT... + modDoc.ChangeModType(MOD_TYPE_IT); + sndFile.SetDefaultPlaybackBehaviour(sndFile.GetType()); + sndFile.SetMixLevels(MixLevels::Compatible); + sndFile.m_songArtist.clear(); + sndFile.m_nTempoMode = TempoMode::Classic; + sndFile.m_SongFlags = SONG_LINEARSLIDES; + sndFile.m_MidiCfg.Reset(); + + // Global vars + sndFile.m_nDefaultTempo.Set(125); + sndFile.m_nDefaultSpeed = 6; + sndFile.m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME; + sndFile.m_nSamplePreAmp = 48; + sndFile.m_nVSTiVolume = 48; + sndFile.Order().SetRestartPos(0); + + if(sndFile.Order().empty()) + { + modDoc.InsertPattern(64, 0); + } + + // Reset instruments (if there are any) + for(INSTRUMENTINDEX i = 1; i <= sndFile.GetNumInstruments(); i++) if(sndFile.Instruments[i]) + { + sndFile.Instruments[i]->nFadeOut = 256; + sndFile.Instruments[i]->nGlobalVol = 64; + sndFile.Instruments[i]->nPan = 128; + sndFile.Instruments[i]->dwFlags.reset(INS_SETPANNING); + sndFile.Instruments[i]->nMixPlug = 0; + + sndFile.Instruments[i]->nVolSwing = 0; + sndFile.Instruments[i]->nPanSwing = 0; + sndFile.Instruments[i]->nCutSwing = 0; + sndFile.Instruments[i]->nResSwing = 0; + } + + for(CHANNELINDEX chn = 0; chn < sndFile.GetNumChannels(); chn++) + { + sndFile.InitChannel(chn); + } + + // reset samples + SampleEdit::ResetSamples(sndFile, SampleEdit::SmpResetCompo); + + cs.Leave(); + EndWaitCursor(); + + return true; +} + + +bool CModCleanupDlg::RemoveUnusedChannels() +{ + // Avoid M.K. modules to become xCHN modules if some channels are unused. + if(modDoc.GetModType() == MOD_TYPE_MOD && modDoc.GetNumChannels() == 4) + return false; + + std::vector<bool> usedChannels; + modDoc.CheckUsedChannels(usedChannels, modDoc.GetNumChannels() - modDoc.GetSoundFile().GetModSpecifications().channelsMin); + return modDoc.RemoveChannels(usedChannels); +} + + +// Remove all patterns +bool CModCleanupDlg::RemoveAllPatterns() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + if(sndFile.Patterns.Size() == 0) return false; + modDoc.GetPatternUndo().ClearUndo(); + sndFile.Patterns.ResizeArray(0); + sndFile.SetCurrentOrder(0); + return true; +} + +// Remove all orders +bool CModCleanupDlg::RemoveAllOrders() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + sndFile.Order.Initialize(); + sndFile.SetCurrentOrder(0); + return true; +} + +// Remove all samples +bool CModCleanupDlg::RemoveAllSamples() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + if (sndFile.GetNumSamples() == 0) return false; + + std::vector<bool> keepSamples(sndFile.GetNumSamples() + 1, false); + sndFile.RemoveSelectedSamples(keepSamples); + + SampleEdit::ResetSamples(sndFile, SampleEdit::SmpResetInit, 1, MAX_SAMPLES - 1); + + return true; +} + +// Remove all instruments +bool CModCleanupDlg::RemoveAllInstruments() +{ + CSoundFile &sndFile = modDoc.GetSoundFile(); + + if(sndFile.GetNumInstruments() == 0) return false; + + modDoc.ConvertInstrumentsToSamples(); + + for(INSTRUMENTINDEX i = 1; i <= sndFile.GetNumInstruments(); i++) + { + sndFile.DestroyInstrument(i, doNoDeleteAssociatedSamples); + } + + sndFile.m_nInstruments = 0; + return true; +} + +// Remove all plugins +bool CModCleanupDlg::RemoveAllPlugins() +{ + std::vector<bool> keepMask(MAX_MIXPLUGINS, false); + modDoc.RemovePlugs(keepMask); + return true; +} + + +bool CModCleanupDlg::MergeSequences() +{ + return modDoc.GetSoundFile().Order.MergeSequences(); +} + + +OPENMPT_NAMESPACE_END |