From: <sag...@us...> - 2015-03-14 23:20:37
|
Revision: 4887 http://sourceforge.net/p/modplug/code/4887 Author: saga-games Date: 2015-03-14 23:20:30 +0000 (Sat, 14 Mar 2015) Log Message: ----------- [Imp] Sample tab: Loop cross-fader can now be configured between constant power and constant volume. Post-loop fade added. Modified Paths: -------------- trunk/OpenMPT/mptrack/Ctrl_smp.cpp trunk/OpenMPT/mptrack/SampleEditorDialogs.cpp trunk/OpenMPT/mptrack/SampleEditorDialogs.h trunk/OpenMPT/mptrack/mptrack.rc trunk/OpenMPT/soundlib/modsmp_ctrl.cpp trunk/OpenMPT/soundlib/modsmp_ctrl.h Modified: trunk/OpenMPT/mptrack/Ctrl_smp.cpp =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_smp.cpp 2015-03-14 16:51:10 UTC (rev 4886) +++ trunk/OpenMPT/mptrack/Ctrl_smp.cpp 2015-03-14 23:20:30 UTC (rev 4887) @@ -3147,42 +3147,32 @@ // Crossfade loop to create smooth loop transitions -#define DEFAULT_XFADE_LENGTH 16384 //4096 - -static SmpLength LimitXFadeLength(SmpLength len, const ModSample &sample) -{ - return Util::Min(len, sample.nLoopEnd - sample.nLoopStart, sample.nLoopEnd / 2); -} - void CCtrlSamples::OnXFade() //-------------------------- { - static SmpLength fadeLength = DEFAULT_XFADE_LENGTH; ModSample &sample = m_sndFile.GetSample(m_nSample); - if(sample.pSample == nullptr) return; - if(sample.nLoopEnd <= sample.nLoopStart || sample.nLoopEnd > sample.nLength) return; - // Case 1: The loop start is preceeded by > XFADE_LENGTH samples. Nothing has to be adjusted. - // Case 2: The actual loop is shorter than XFADE_LENGTH samples. - // Case 3: There is not enough sample material before the loop start. Move the loop start. - fadeLength = LimitXFadeLength(fadeLength, sample); + if(sample.pSample == nullptr + || sample.nLoopEnd <= sample.nLoopStart || sample.nLoopEnd > sample.nLength) + { + MessageBeep(MB_ICONWARNING); + return; + } + const SmpLength maxSamples = Util::Min(sample.nLength, sample.nLoopStart, sample.nLoopEnd / 2); - CSampleXFadeDlg dlg(this, fadeLength, LimitXFadeLength(sample.nLength, sample)); + CSampleXFadeDlg dlg(this, sample.nLoopEnd - sample.nLoopStart, maxSamples); if(dlg.DoModal() == IDOK) { - fadeLength = dlg.m_nSamples; + SmpLength fadeSamples = dlg.PercentToSamples(dlg.m_fadeLength); + LimitMax(fadeSamples, maxSamples); + if(fadeSamples < 2) return; - if(fadeLength < 4) return; + m_modDoc.GetSampleUndo().PrepareUndo(m_nSample, sundo_update, "Crossfade", + sample.nLoopEnd - fadeSamples, + sample.nLoopEnd + (dlg.m_afterloopFade ? std::min(sample.nLength - sample.nLoopEnd, fadeSamples) : 0)); - m_modDoc.GetSampleUndo().PrepareUndo(m_nSample, sundo_update, "Crossfade", sample.nLoopEnd - fadeLength, sample.nLoopEnd); - // If we want to fade nFadeLength bytes, we need as much sample material before the loop point. nFadeLength has been adjusted above. - if(sample.nLoopStart < fadeLength) + if(ctrlSmp::XFadeSample(sample, fadeSamples, dlg.m_fadeLaw, dlg.m_afterloopFade, m_sndFile)) { - sample.nLoopStart = fadeLength; - } - - if(ctrlSmp::XFadeSample(sample, fadeLength, m_sndFile)) - { SetModified(SampleHint().Info().Data(), true, true); } else { Modified: trunk/OpenMPT/mptrack/SampleEditorDialogs.cpp =================================================================== --- trunk/OpenMPT/mptrack/SampleEditorDialogs.cpp 2015-03-14 16:51:10 UTC (rev 4886) +++ trunk/OpenMPT/mptrack/SampleEditorDialogs.cpp 2015-03-14 23:20:30 UTC (rev 4887) @@ -220,6 +220,17 @@ ///////////////////////////////////////////////////////////////////////// // Sample cross-fade dialog +uint32 CSampleXFadeDlg::m_fadeLength = 20000; +uint32 CSampleXFadeDlg::m_fadeLaw = 50000; +bool CSampleXFadeDlg::m_afterloopFade = true; + +BEGIN_MESSAGE_MAP(CSampleXFadeDlg, CDialog) + ON_WM_HSCROLL() + ON_EN_CHANGE(IDC_EDIT1, OnFadeLengthChanged) + ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipText) +END_MESSAGE_MAP() + + void CSampleXFadeDlg::DoDataExchange(CDataExchange* pDX) //------------------------------------------------------ { @@ -227,6 +238,8 @@ //{{AFX_DATA_MAP(CSampleGridDlg) DDX_Control(pDX, IDC_EDIT1, m_EditSamples); DDX_Control(pDX, IDC_SPIN1, m_SpinSamples); + DDX_Control(pDX, IDC_SLIDER1, m_SliderLength); + DDX_Control(pDX, IDC_SLIDER2, m_SliderFadeLaw); //}}AFX_DATA_MAP } @@ -235,10 +248,21 @@ //---------------------------------- { CDialog::OnInitDialog(); - m_SpinSamples.SetRange32(0, m_nMaxSamples); - m_SpinSamples.SetPos(m_nSamples); - SetDlgItemInt(IDC_EDIT1, m_nSamples, FALSE); + m_editLocked = true; + m_SpinSamples.SetRange32(0, std::min(m_loopLength, m_maxLength)); GetDlgItem(IDC_EDIT1)->SetFocus(); + m_SliderLength.SetRange(0, 100000); + m_SliderLength.SetPos(m_fadeLength); + m_SliderFadeLaw.SetRange(0, 100000); + m_SliderFadeLaw.SetPos(m_fadeLaw); + CheckDlgButton(IDC_CHECK1, m_afterloopFade ? BST_CHECKED : BST_UNCHECKED); + + SmpLength numSamples = PercentToSamples(m_SliderLength.GetPos()); + numSamples = Util::Min(numSamples, m_loopLength, m_maxLength); + m_SpinSamples.SetPos(numSamples); + SetDlgItemInt(IDC_EDIT1, numSamples, FALSE); + + m_editLocked = false; return TRUE; } @@ -246,12 +270,76 @@ void CSampleXFadeDlg::OnOK() //-------------------------- { - m_nSamples = GetDlgItemInt(IDC_EDIT1, NULL, FALSE); - LimitMax(m_nSamples, m_nMaxSamples); + m_fadeLength = m_SliderLength.GetPos(); + m_fadeLaw = m_SliderFadeLaw.GetPos(); + m_afterloopFade = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED; + Limit(m_fadeLength, uint32(0), uint32(100000)); CDialog::OnOK(); } +void CSampleXFadeDlg::OnFadeLengthChanged() +//----------------------------------------- +{ + if(m_editLocked) return; + SmpLength numSamples = GetDlgItemInt(IDC_EDIT1, NULL, FALSE); + numSamples = Util::Min(numSamples, m_loopLength, m_maxLength); + m_SliderLength.SetPos(SamplesToPercent(numSamples)); +} + + +void CSampleXFadeDlg::OnHScroll(UINT, UINT, CScrollBar *sb) +//--------------------------------------------------------- +{ + if(sb == (CScrollBar *)(&m_SliderLength)) + { + m_editLocked = true; + SmpLength numSamples = PercentToSamples(m_SliderLength.GetPos()); + if(numSamples > m_maxLength) + { + numSamples = m_maxLength; + m_SliderLength.SetPos(SamplesToPercent(numSamples)); + } + m_SpinSamples.SetPos(numSamples); + SetDlgItemInt(IDC_EDIT1, numSamples, FALSE); + m_editLocked = false; + } +} + + +BOOL CSampleXFadeDlg::OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *pResult) +//------------------------------------------------------------------------ +{ + TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR; + UINT_PTR nID = pNMHDR->idFrom; + if(pTTT->uFlags & TTF_IDISHWND) + { + // idFrom is actually the HWND of the tool + nID = (UINT_PTR)::GetDlgCtrlID((HWND)nID); + } + switch(nID) + { + case IDC_SLIDER1: + { + uint32 percent = m_SliderLength.GetPos(); + wsprintf(pTTT->szText, _T("%u.%03u%% of the loop (%u samples)"), percent / 1000, percent % 1000, PercentToSamples(percent)); + } + break; + case IDC_SLIDER2: + _tcscpy(pTTT->szText, _T("Slide towards constant power for fixing badly looped samples.")); + break; + default: + return FALSE; + } + *pResult = 0; + + // bring the tooltip window above other popup windows + ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0, + SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER); + return TRUE; +} + + ///////////////////////////////////////////////////////////////////////// // Resampling dialog Modified: trunk/OpenMPT/mptrack/SampleEditorDialogs.h =================================================================== --- trunk/OpenMPT/mptrack/SampleEditorDialogs.h 2015-03-14 16:51:10 UTC (rev 4886) +++ trunk/OpenMPT/mptrack/SampleEditorDialogs.h 2015-03-14 23:20:30 UTC (rev 4887) @@ -139,19 +139,35 @@ //=================================== { public: - SmpLength m_nSamples, m_nMaxSamples; + static uint32 m_fadeLength; + static uint32 m_fadeLaw; + static bool m_afterloopFade; + SmpLength m_loopLength, m_maxLength; protected: + CSliderCtrl m_SliderLength, m_SliderFadeLaw; CEdit m_EditSamples; CSpinButtonCtrl m_SpinSamples; + bool m_editLocked : 1; public: - CSampleXFadeDlg(CWnd *parent, SmpLength nSamples, UINT nMaxSamples) : CDialog(IDD_SAMPLE_XFADE, parent), m_nSamples(nSamples), m_nMaxSamples(nMaxSamples) { }; + CSampleXFadeDlg(CWnd *parent, SmpLength loopLength, SmpLength maxLength) + : CDialog(IDD_SAMPLE_XFADE, parent) + , m_loopLength(loopLength) + , m_maxLength(maxLength) + , m_editLocked(true) { }; + SmpLength PercentToSamples(uint32 percent) const { return Util::muldivr_unsigned(percent, m_loopLength, 100000); } + uint32 SamplesToPercent(SmpLength samples) const { return Util::muldivr_unsigned(samples, 100000, m_loopLength); } + protected: virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog(); virtual void OnOK(); + afx_msg void OnFadeLengthChanged(); + afx_msg void OnHScroll(UINT, UINT, CScrollBar *); + afx_msg BOOL OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *pResult); + DECLARE_MESSAGE_MAP() }; Modified: trunk/OpenMPT/mptrack/mptrack.rc =================================================================== --- trunk/OpenMPT/mptrack/mptrack.rc 2015-03-14 16:51:10 UTC (rev 4886) +++ trunk/OpenMPT/mptrack/mptrack.rc 2015-03-14 23:20:30 UTC (rev 4887) @@ -193,17 +193,21 @@ LTEXT "Grid Segments:",IDC_STATIC,6,8,60,8 END -IDD_SAMPLE_XFADE DIALOGEX 0, 0, 178, 82 +IDD_SAMPLE_XFADE DIALOGEX 0, 0, 292, 82 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Sample Crossfader" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - DEFPUSHBUTTON "OK",IDOK,66,60,50,14 - PUSHBUTTON "Cancel",IDCANCEL,120,60,50,14 - EDITTEXT IDC_EDIT1,108,6,54,12,ES_AUTOHSCROLL | ES_NUMBER - CONTROL "",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,144,6,11,14 - LTEXT "Higher numbers generate smoother loop transitions. However, the number is limited by the loop length and the amount of samples before the loop start.",IDC_STATIC,6,24,168,36 - LTEXT "Samples used for fading:",IDC_STATIC,6,8,96,8 + DEFPUSHBUTTON "OK",IDOK,180,60,50,14 + PUSHBUTTON "Cancel",IDCANCEL,234,60,50,14 + RTEXT "&Samples used for fading:",IDC_STATIC,6,8,84,8 + CONTROL "",IDC_SLIDER1,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,96,5,120,15 + EDITTEXT IDC_EDIT1,222,6,54,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,258,6,11,14 + RTEXT "&Constant Volume",IDC_STATIC,30,27,60,8 + CONTROL "",IDC_SLIDER2,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | TBS_TOOLTIPS | WS_TABSTOP,96,24,120,15 + LTEXT "Constant Power",IDC_STATIC,222,27,60,8 + CONTROL "&Post-loop fade",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,102,42,180,12 END IDD_OPTIONS_UPDATE DIALOGEX 0, 0, 286, 281 @@ -432,7 +436,7 @@ IDD_SAMPLE_XFADE, DIALOG BEGIN LEFTMARGIN, 7 - RIGHTMARGIN, 171 + RIGHTMARGIN, 285 TOPMARGIN, 7 BOTTOMMARGIN, 75 END Modified: trunk/OpenMPT/soundlib/modsmp_ctrl.cpp =================================================================== --- trunk/OpenMPT/soundlib/modsmp_ctrl.cpp 2015-03-14 16:51:10 UTC (rev 4886) +++ trunk/OpenMPT/soundlib/modsmp_ctrl.cpp 2015-03-14 23:20:30 UTC (rev 4887) @@ -566,8 +566,8 @@ template <class T> -void ReverseSampleImpl(T *pStart, const SmpLength nLength) -//-------------------------------------------------------- +static void ReverseSampleImpl(T *pStart, const SmpLength nLength) +//--------------------------------------------------------------- { for(SmpLength i = 0; i < nLength / 2; i++) { @@ -604,8 +604,8 @@ template <class T> -void UnsignSampleImpl(T *pStart, const SmpLength nLength) -//------------------------------------------------------- +static void UnsignSampleImpl(T *pStart, const SmpLength nLength) +//-------------------------------------------------------------- { const T offset = (std::numeric_limits<T>::min)(); for(SmpLength i = 0; i < nLength; i++) @@ -639,8 +639,8 @@ template <class T> -void InvertSampleImpl(T *pStart, const SmpLength nLength) -//------------------------------------------------------- +static void InvertSampleImpl(T *pStart, const SmpLength nLength) +//-------------------------------------------------------------- { for(SmpLength i = 0; i < nLength; i++) { @@ -673,42 +673,49 @@ template <class T> -void XFadeSampleImpl(T *pStart, const SmpLength nOffset, SmpLength nFadeLength) -//----------------------------------------------------------------------------- +static void XFadeSampleImpl(const T *srcIn, const T *srcOut, T *output, const SmpLength fadeLength, double e) +//----------------------------------------------------------------------------------------------------------- { - const double length = 1.0 / static_cast<double>(nFadeLength); - for(SmpLength i = 0; i < nFadeLength; i++) + const double length = 1.0 / static_cast<double>(fadeLength); + for(SmpLength i = 0; i < fadeLength; i++, srcIn++, srcOut++, output++) { - // Constant-power crossfade - double fact1 = std::sqrt(i * length); - double fact2 = std::sqrt((nFadeLength - i) * length); + double fact1 = std::pow(i * length, e); + double fact2 = std::pow((fadeLength - i) * length, e); int32 val = static_cast<int32>( - static_cast<double>(pStart[nOffset + i]) * fact2 + - static_cast<double>(pStart[i]) * fact1); + static_cast<double>(*srcIn) * fact1 + + static_cast<double>(*srcOut) * fact2); Limit(val, std::numeric_limits<T>::min(), std::numeric_limits<T>::max()); - pStart[nOffset + i] = static_cast<T>(val); + *output = static_cast<T>(val); } } // X-Fade sample data to create smooth loop transitions -bool XFadeSample(ModSample &smp, SmpLength iFadeLength, CSoundFile &sndFile) -//-------------------------------------------------------------------------- +bool XFadeSample(ModSample &smp, SmpLength iFadeLength, int fadeLaw, bool afterloopFade, CSoundFile &sndFile) +//----------------------------------------------------------------------------------------------------------- { if(!smp.HasSampleData()) return false; if(smp.nLoopEnd <= smp.nLoopStart || smp.nLoopEnd > smp.nLength) return false; if(smp.nLoopStart < iFadeLength) return false; - SmpLength start = smp.nLoopStart - iFadeLength; - SmpLength end = smp.nLoopEnd - iFadeLength; - start *= smp.GetNumChannels(); - end *= smp.GetNumChannels(); + const SmpLength start = (smp.nLoopStart - iFadeLength) * smp.GetNumChannels(); + const SmpLength end = (smp.nLoopEnd - iFadeLength) * smp.GetNumChannels(); + const SmpLength afterloopStart = smp.nLoopStart * smp.GetNumChannels(); + const SmpLength afterloopEnd = smp.nLoopEnd * smp.GetNumChannels(); + const SmpLength afterLoopLength = std::min(smp.nLength - smp.nLoopEnd, iFadeLength) * smp.GetNumChannels(); iFadeLength *= smp.GetNumChannels(); + // e=0.5: constant power crossfade (for uncorrelated samples), e=1.0: constant volume crossfade (for perfectly correlated samples) + const double e = 1.0 - fadeLaw / 200000.0; + if(smp.GetElementarySampleSize() == 2) - XFadeSampleImpl(smp.pSample16 + start, end - start, iFadeLength); - else if(smp.GetElementarySampleSize() == 1) - XFadeSampleImpl(smp.pSample8 + start, end - start, iFadeLength); - else + { + XFadeSampleImpl(smp.pSample16 + start, smp.pSample16 + end, smp.pSample16 + end, iFadeLength, e); + if(afterloopFade) XFadeSampleImpl(smp.pSample16 + afterloopEnd, smp.pSample16 + afterloopStart, smp.pSample16 + afterloopEnd, afterLoopLength, e); + } else if(smp.GetElementarySampleSize() == 1) + { + XFadeSampleImpl(smp.pSample8 + start, smp.pSample8 + end, smp.pSample8 + end, iFadeLength, e); + if(afterloopFade) XFadeSampleImpl(smp.pSample8 + afterloopEnd, smp.pSample8 + afterloopStart, smp.pSample8 + afterloopEnd, afterLoopLength, e); + } else return false; PrecomputeLoops(smp, sndFile, true); @@ -717,8 +724,8 @@ template <class T> -void ConvertStereoToMonoMixImpl(T *pDest, const SmpLength length) -//--------------------------------------------------------------- +static void ConvertStereoToMonoMixImpl(T *pDest, const SmpLength length) +//---------------------------------------------------------------------- { const T *pEnd = pDest + length; for(T *pSource = pDest; pDest != pEnd; pDest++, pSource += 2) @@ -729,8 +736,8 @@ template <class T> -void ConvertStereoToMonoOneChannelImpl(T *pDest, const SmpLength length) -//---------------------------------------------------------------------- +static void ConvertStereoToMonoOneChannelImpl(T *pDest, const SmpLength length) +//----------------------------------------------------------------------------- { const T *pEnd = pDest + length; for(T *pSource = pDest; pDest != pEnd; pDest++, pSource += 2) Modified: trunk/OpenMPT/soundlib/modsmp_ctrl.h =================================================================== --- trunk/OpenMPT/soundlib/modsmp_ctrl.h 2015-03-14 16:51:10 UTC (rev 4886) +++ trunk/OpenMPT/soundlib/modsmp_ctrl.h 2015-03-14 23:20:30 UTC (rev 4887) @@ -80,7 +80,7 @@ bool InvertSample(ModSample &smp, SmpLength iStart, SmpLength iEnd, CSoundFile &sndFile); // Crossfade sample data to create smooth loops -bool XFadeSample(ModSample &smp, SmpLength iFadeLength, CSoundFile &sndFile); +bool XFadeSample(ModSample &smp, SmpLength iFadeLength, int fadeLaw, bool afterloopFade, CSoundFile &sndFile); enum StereoToMonoMode { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |