From: <sag...@us...> - 2009-11-01 13:53:27
|
Revision: 408 http://modplug.svn.sourceforge.net/modplug/?rev=408&view=rev Author: saga-games Date: 2009-11-01 13:53:08 +0000 (Sun, 01 Nov 2009) Log Message: ----------- [New] Sample editor: Sample Undo. 100 steps per sample (independent), undo buffer is cut off at a tenth of physical memory (that would be 400 MB for a system with 4 GB of RAM). Cutoff size can be specified by setting UndoBufferSize (in Megabytes) in section [Sample Editor] of mptrack.ini. [Mod] Sample editor: Removed time stretcher's / pitch shifter's preview function as it's unnecessary now. [Imp] Sample editor: When sample is 8-bit, it will automatically be converted to 16-bit when applying time stretching / pitch shifting. [Imp] Comments tab: If sample size is < 1 KB, amount of bytes is shown instead of "0 KB" [Fix] Sample editor: Insert Silence: Loop points were not updated when adding silence at the beginning of the sample [Fix] IT Loader: Use correct header size (1.xx or 2.xx header) for comparison when checking instrument headers [Ref] Pattern undo uses vectors now. [Ref] Added GetBytesPerSample() in MODSAMPLE to get the bytes per sampling point (Basically a shorter version of GetElementarySampleSize() * GetNumChannels()) [Ref] More refactoring especially in the sample editor code to make undo more easy. Modified Paths: -------------- trunk/OpenMPT/mptrack/CleanupSong.cpp trunk/OpenMPT/mptrack/Ctrl_pat.cpp trunk/OpenMPT/mptrack/Ctrl_smp.cpp trunk/OpenMPT/mptrack/Ctrl_smp.h trunk/OpenMPT/mptrack/EffectVis.cpp trunk/OpenMPT/mptrack/MainFrm.cpp trunk/OpenMPT/mptrack/Mainfrm.h trunk/OpenMPT/mptrack/Moddoc.cpp trunk/OpenMPT/mptrack/Moddoc.h trunk/OpenMPT/mptrack/Modedit.cpp trunk/OpenMPT/mptrack/Mptrack.cpp trunk/OpenMPT/mptrack/View_pat.cpp trunk/OpenMPT/mptrack/View_smp.cpp trunk/OpenMPT/mptrack/View_smp.h trunk/OpenMPT/mptrack/dlg_misc.cpp trunk/OpenMPT/mptrack/dlg_misc.h trunk/OpenMPT/mptrack/mptrack.rc trunk/OpenMPT/mptrack/mptrack.vcproj trunk/OpenMPT/mptrack/mptrack_08.vcproj trunk/OpenMPT/mptrack/view_com.cpp trunk/OpenMPT/soundlib/Load_it.cpp trunk/OpenMPT/soundlib/Load_mod.cpp trunk/OpenMPT/soundlib/Snd_fx.cpp trunk/OpenMPT/soundlib/Sndfile.h trunk/OpenMPT/soundlib/modsmp_ctrl.cpp trunk/OpenMPT/soundlib/modsmp_ctrl.h trunk/OpenMPT/soundlib/pattern.cpp Added Paths: ----------- trunk/OpenMPT/mptrack/Undo.cpp trunk/OpenMPT/mptrack/Undo.h Modified: trunk/OpenMPT/mptrack/CleanupSong.cpp =================================================================== --- trunk/OpenMPT/mptrack/CleanupSong.cpp 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/CleanupSong.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -441,7 +441,7 @@ EndWaitCursor(); if ((nPatRemoved) || (bReordered)) { - m_pModDoc->ClearPatternUndo(); + m_pModDoc->GetPatternUndo()->ClearUndo(); if (nPatRemoved) { wsprintf(s, "%d pattern%s removed.\n", nPatRemoved, (nPatRemoved == 1) ? "" : "s"); @@ -535,6 +535,7 @@ if ((j == pSndFile->m_nSamples) && (j > 1)) pSndFile->m_nSamples--; END_CRITICAL(); nRemoved++; + m_pModDoc->GetSampleUndo()->ClearUndo(j); } } wsprintf(s, "%d unused sample%s removed\n" ,nRemoved, (nRemoved == 1) ? "" : "s"); @@ -573,7 +574,11 @@ && (pSndFile->Samples[nSmp].nLength > pSndFile->Samples[nSmp].nLoopEnd + 2)) { UINT lmax = pSndFile->Samples[nSmp].nLoopEnd + 2; - if ((lmax < pSndFile->Samples[nSmp].nLength) && (lmax >= 16)) pSndFile->Samples[nSmp].nLength = lmax; + if ((lmax < pSndFile->Samples[nSmp].nLength) && (lmax >= 2)) + { + m_pModDoc->GetSampleUndo()->PrepareUndo(nSmp, sundo_delete, lmax, pSndFile->Samples[nSmp].nLength); + ctrlSmp::ResizeSample(pSndFile->Samples[nSmp], lmax, pSndFile); + } } } wsprintf(s, "%d sample loop%s optimized\n" ,nLoopOpt, (nLoopOpt == 1) ? "" : "s"); @@ -651,6 +656,9 @@ } } + // Too lazy to fix sample undo... + m_pModDoc->GetSampleUndo()->ClearUndo(); + pSndFile->m_nSamples -= nRemap; return true; @@ -927,7 +935,8 @@ if (pSndFile->m_nSamples == 0) return false; for (SAMPLEINDEX nSmp = 1; nSmp <= pSndFile->m_nSamples; nSmp++) { - if (pSndFile->Samples[nSmp].pSample) + m_pModDoc->GetSampleUndo()->PrepareUndo(nSmp, sundo_delete, 0, pSndFile->Samples[nSmp].nLength); + if(pSndFile->Samples[nSmp].pSample) { BEGIN_CRITICAL(); pSndFile->DestroySample(nSmp); @@ -962,6 +971,8 @@ } } + if(removeSamples == -1) m_pModDoc->GetSampleUndo()->ClearUndo(); + for (INSTRUMENTINDEX i = 1; i <= pSndFile->m_nInstruments; i++) { pSndFile->DestroyInstrument(i, removeSamples); Modified: trunk/OpenMPT/mptrack/Ctrl_pat.cpp =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_pat.cpp 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/Ctrl_pat.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -377,7 +377,7 @@ } if (dwHintMask & (HINT_MODTYPE|HINT_UNDO)) { - m_ToolBar.EnableButton(ID_EDIT_UNDO, m_pModDoc->CanPatternUndo()); + m_ToolBar.EnableButton(ID_EDIT_UNDO, m_pModDoc->GetPatternUndo()->CanUndo()); } } Modified: trunk/OpenMPT/mptrack/Ctrl_smp.cpp =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_smp.cpp 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/Ctrl_smp.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -74,8 +74,6 @@ ON_COMMAND(ID_NEXTINSTRUMENT, OnNextInstrument) ON_COMMAND(IDC_BUTTON1, OnPitchShiftTimeStretch) ON_COMMAND(IDC_BUTTON2, OnEstimateSampleSize) - ON_COMMAND(IDC_BUTTON3, OnPitchShiftTimeStretchAccept) - ON_COMMAND(IDC_BUTTON4, OnPitchShiftTimeStretchCancel) ON_COMMAND(IDC_CHECK3, OnEnableStretchToSize) ON_EN_CHANGE(IDC_SAMPLE_NAME, OnNameChanged) ON_EN_CHANGE(IDC_SAMPLE_FILENAME, OnFileNameChanged) @@ -159,8 +157,6 @@ { m_nSample = 1; m_nLockCount = 1; - pSampleUndoBuffer = NULL; - UndoBufferSize = 0; } @@ -168,9 +164,6 @@ CCtrlSamples::~CCtrlSamples() //--------------------------- { - if(pSampleUndoBuffer) CSoundFile::FreeSample(pSampleUndoBuffer); - pSampleUndoBuffer = NULL; - UndoBufferSize = 0; } @@ -261,18 +254,13 @@ GetDlgItem(IDC_BUTTON1)->ShowWindow(SW_SHOW); // PitchShiftTimeStretch GetDlgItem(IDC_BUTTON2)->ShowWindow(SW_SHOW); // EstimateSampleSize - GetDlgItem(IDC_BUTTON3)->ShowWindow(SW_SHOW); // PitchShiftTimeStretchAccept - GetDlgItem(IDC_BUTTON4)->ShowWindow(SW_SHOW); // PitchShiftTimeStretchCancel - GetDlgItem(IDC_CHECK2)->ShowWindow(SW_SHOW); // Preview mode GetDlgItem(IDC_CHECK3)->ShowWindow(SW_SHOW); // EnableStretchToSize GetDlgItem(IDC_EDIT6)->ShowWindow(SW_SHOW); // GetDlgItem(IDC_GROUPBOX_PITCH_TIME)->ShowWindow(SW_SHOW); // GetDlgItem(IDC_TEXT_PITCH)->ShowWindow(SW_SHOW); // GetDlgItem(IDC_TEXT_QUALITY)->ShowWindow(SW_SHOW); // GetDlgItem(IDC_TEXT_FFT)->ShowWindow(SW_SHOW); // - GetDlgItem(IDC_TEXT_PREVIEW)->ShowWindow(SW_SHOW); // GetDlgItem(IDC_GROUPBOX_PITCH_TIME)->ShowWindow(SW_SHOW); // - GetDlgItem(IDC_TEXT_PERCENT)->ShowWindow(SW_SHOW); // CHAR str[16]; @@ -315,10 +303,6 @@ combo->SetCurSel(3); } - //Disable preview buttons - ((CButton *)GetDlgItem(IDC_BUTTON3))->ShowWindow(SW_HIDE); - ((CButton *)GetDlgItem(IDC_BUTTON4))->ShowWindow(SW_HIDE); - // Stretch ratio SetDlgItemInt(IDC_EDIT6,100,FALSE); @@ -348,8 +332,6 @@ if (pSndFile->m_nSamples < 1) pSndFile->m_nSamples = 1; if ((nSmp < 1) || (nSmp > pSndFile->m_nSamples)) return FALSE; - if(pSampleUndoBuffer) OnPitchShiftTimeStretchCancel(); - LockControls(); if (m_nSample != nSmp) { @@ -713,8 +695,6 @@ DWORD len; BOOL bOk; - if(pSampleUndoBuffer) OnPitchShiftTimeStretchCancel(); - BeginWaitCursor(); if ((!lpszFileName) || (!f.Open(lpszFileName))) { @@ -727,6 +707,9 @@ lpFile = f.Lock(len); if (!lpFile) goto OpenError; BEGIN_CRITICAL(); + + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_replace); + if (m_pSndFile->ReadSampleFromFile(m_nSample, lpFile, len)) { bOk = TRUE; @@ -779,6 +762,9 @@ bOk = TRUE; } END_CRITICAL(); + } else + { + m_pModDoc->GetSampleUndo()->RemoveLastUndoStep(m_nSample); } } f.Unlock(); @@ -830,10 +816,9 @@ { if ((!pSndFile) || (!nSample) || (nSample > pSndFile->m_nSamples)) return FALSE; - if(pSampleUndoBuffer) OnPitchShiftTimeStretchCancel(); - BeginWaitCursor(); BEGIN_CRITICAL(); + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_replace); m_pSndFile->DestroySample(m_nSample); m_pSndFile->ReadSampleFromSong(m_nSample, pSndFile, nSample); MODSAMPLE *pSmp = &m_pSndFile->Samples[m_nSample]; @@ -859,8 +844,6 @@ { if ((!IsLocked()) && (m_pSndFile)) { - if(pSampleUndoBuffer) OnPitchShiftTimeStretchCancel(); - UINT n = GetDlgItemInt(IDC_EDIT_SAMPLE); if ((n > 0) && (n <= m_pSndFile->m_nSamples) && (n != m_nSample)) { @@ -899,8 +882,6 @@ LONG smp = m_pModDoc->InsertSample(TRUE); if (smp != SAMPLEINDEX_INVALID) { - if(pSampleUndoBuffer) OnPitchShiftTimeStretchCancel(); - CSoundFile *pSndFile = m_pModDoc->GetSoundFile(); SetCurrentSample(smp); // 05/01/05 : ericus replaced "m_nSample << 24" by "m_nSample << 20" : 4000 samples -> 12bits [see Moddoc.h] @@ -1156,6 +1137,9 @@ iEnd = pSmp->nLength; } } + + m_pModDoc->GetSampleUndo()->PrepareUndo(iSmp, sundo_update, iStart, iEnd); + if (pSmp->uFlags & CHN_STEREO) { iStart *= 2; iEnd *= 2; } if (pSmp->uFlags & CHN_16BIT) @@ -1218,31 +1202,23 @@ void CCtrlSamples::ApplyAmplify(LONG lAmp, bool bFadeIn, bool bFadeOut) //----------------------------------------------------------------------- { - SAMPLEVIEWSTATE viewstate; - DWORD dwStart, dwEnd; MODSAMPLE *pSmp; - memset(&viewstate, 0, sizeof(viewstate)); - SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); if ((!m_pModDoc) || (!m_pSndFile) || (!m_pSndFile->Samples[m_nSample].pSample)) return; BeginWaitCursor(); pSmp = &m_pSndFile->Samples[m_nSample]; - dwStart = viewstate.dwBeginSel; - dwEnd = viewstate.dwEndSel; - if (dwEnd > pSmp->nLength) dwEnd = pSmp->nLength; - if (dwStart > dwEnd) dwStart = dwEnd; - if (dwStart >= dwEnd) - { - dwStart = 0; - dwEnd = pSmp->nLength; - } - if (pSmp->uFlags & CHN_STEREO) { dwStart *= 2; dwEnd *= 2; } - UINT len = dwEnd - dwStart; + + SELECTIONPOINTS selection = GetSelectionPoints(); + + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_update, selection.nStart, selection.nEnd); + + if (pSmp->uFlags & CHN_STEREO) { selection.nStart *= 2; selection.nEnd *= 2; } + UINT len = selection.nEnd - selection.nStart; if ((bFadeIn) && (bFadeOut)) lAmp *= 4; if (pSmp->uFlags & CHN_16BIT) { - signed short *p = ((signed short *)pSmp->pSample) + dwStart; + signed short *p = ((signed short *)pSmp->pSample) + selection.nStart; for (UINT i=0; i<len; i++) { @@ -1255,7 +1231,7 @@ } } else { - signed char *p = ((signed char *)pSmp->pSample) + dwStart; + signed char *p = ((signed char *)pSmp->pSample) + selection.nStart; for (UINT i=0; i<len; i++) { @@ -1282,12 +1258,8 @@ if(!m_pModDoc || !m_pSndFile) return; - SAMPLEVIEWSTATE viewstate; UINT iMinSample = m_nSample, iMaxSample = m_nSample; - memset(&viewstate, 0, sizeof(viewstate)); - SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); - //Shift -> Process all samples if(CMainFrame::GetInputHandler()->ShiftPressed()) { @@ -1317,9 +1289,13 @@ } else { - iStart = viewstate.dwBeginSel; - iEnd = viewstate.dwEndSel; + SELECTIONPOINTS selection = GetSelectionPoints(); + iStart = selection.nStart; + iEnd = selection.nEnd; } + + m_pModDoc->GetSampleUndo()->PrepareUndo(iSmp, sundo_update, iStart, iEnd); + const float fOffset = ctrlSmp::RemoveDCOffset(m_pSndFile->Samples[iSmp], iStart, iEnd, m_pSndFile->GetType(), m_pSndFile); if(fOffset == 0.0f) // No offset removed. @@ -1379,24 +1355,25 @@ void CCtrlSamples::OnUpsample() //----------------------------- { - SAMPLEVIEWSTATE viewstate; MODSAMPLE *pSmp; DWORD dwStart, dwEnd, dwNewLen; UINT smplsize, newsmplsize; PVOID pOriginal, pNewSample; - SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); if ((!m_pSndFile) || (!m_pSndFile->Samples[m_nSample].pSample)) return; BeginWaitCursor(); pSmp = &m_pSndFile->Samples[m_nSample]; - dwStart = viewstate.dwBeginSel; - dwEnd = viewstate.dwEndSel; + SELECTIONPOINTS selection = GetSelectionPoints(); + + dwStart = selection.nStart; + dwEnd = selection.nEnd; if (dwEnd > pSmp->nLength) dwEnd = pSmp->nLength; if (dwStart >= dwEnd) { dwStart = 0; dwEnd = pSmp->nLength; } + smplsize = (pSmp->uFlags & CHN_16BIT) ? 2 : 1; if (pSmp->uFlags & CHN_STEREO) smplsize *= 2; newsmplsize = (pSmp->uFlags & CHN_STEREO) ? 4 : 2; @@ -1406,6 +1383,8 @@ if (dwNewLen+4 <= MAX_SAMPLE_LENGTH) pNewSample = CSoundFile::AllocateSample((dwNewLen+4)*newsmplsize); if (pNewSample) { + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_replace); + UINT nCh = (pSmp->uFlags & CHN_STEREO) ? 2 : 1; for (UINT iCh=0; iCh<nCh; iCh++) { @@ -1495,10 +1474,7 @@ m_pSndFile->Chn[iFix].dwFlags |= CHN_16BIT; } } - pSmp->uFlags |= CHN_16BIT; - pSmp->pSample = (LPSTR)pNewSample; - pSmp->nLength = dwNewLen; - if (viewstate.dwEndSel <= viewstate.dwBeginSel) + if (selection.bSelected == false) { if(!(m_pSndFile->m_nType & MOD_TYPE_MOD)) { @@ -1506,11 +1482,19 @@ if (pSmp->RelativeTone < 84) pSmp->RelativeTone += 12; } } + pSmp->uFlags |= CHN_16BIT; + pSmp->pSample = (LPSTR)pNewSample; + pSmp->nLength = dwNewLen; + CSoundFile::FreeSample(pOriginal); END_CRITICAL(); m_pModDoc->AdjustEndOfSample(m_nSample); - if (viewstate.dwEndSel > viewstate.dwBeginSel) + if (selection.bSelected == true) { + SAMPLEVIEWSTATE viewstate; + memset(&viewstate, 0, sizeof(viewstate)); + SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); + viewstate.dwBeginSel = dwStart; viewstate.dwEndSel = dwEnd + (dwEnd-dwStart); SendViewMessage(VIEWMSG_LOADSTATE, (LPARAM)&viewstate); @@ -1527,18 +1511,18 @@ void CCtrlSamples::OnDownsample() //------------------------------- { - SAMPLEVIEWSTATE viewstate; MODSAMPLE *pSmp; DWORD dwStart, dwEnd, dwRemove, dwNewLen; UINT smplsize; PVOID pOriginal, pNewSample; - SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); if ((!m_pSndFile) || (!m_pSndFile->Samples[m_nSample].pSample)) return; BeginWaitCursor(); pSmp = &m_pSndFile->Samples[m_nSample]; - dwStart = viewstate.dwBeginSel; - dwEnd = viewstate.dwEndSel; + SELECTIONPOINTS selection = GetSelectionPoints(); + + dwStart = selection.nStart; + dwEnd = selection.nEnd; if (dwEnd > pSmp->nLength) dwEnd = pSmp->nLength; if (dwStart >= dwEnd) { @@ -1555,6 +1539,9 @@ if ((dwNewLen > 32) && (dwRemove)) pNewSample = CSoundFile::AllocateSample((dwNewLen+4)*smplsize); if (pNewSample) { + + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_replace); + UINT nCh = (pSmp->uFlags & CHN_STEREO) ? 2 : 1; for (UINT iCh=0; iCh<nCh; iCh++) { @@ -1624,7 +1611,7 @@ m_pSndFile->Chn[iFix].nLength = 0; } } - if (viewstate.dwEndSel <= viewstate.dwBeginSel) + if (selection.bSelected == false) { if(!(m_pSndFile->m_nType & MOD_TYPE_MOD)) { @@ -1637,8 +1624,12 @@ CSoundFile::FreeSample(pOriginal); END_CRITICAL(); m_pModDoc->AdjustEndOfSample(m_nSample); - if (viewstate.dwEndSel > viewstate.dwBeginSel) + if (selection.bSelected == true) { + SAMPLEVIEWSTATE viewstate; + memset(&viewstate, 0, sizeof(viewstate)); + SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); + viewstate.dwBeginSel = dwStart; viewstate.dwEndSel = dwStart + dwRemove; SendViewMessage(VIEWMSG_LOADSTATE, (LPARAM)&viewstate); @@ -1682,40 +1673,21 @@ //---------------------------------------- { // Enable time-stretching / disable unused pitch-shifting UI elements - if(IsDlgButtonChecked(IDC_CHECK3)){ - ((CComboBox *)GetDlgItem(IDC_COMBO4))->EnableWindow(FALSE); - ((CEdit *)GetDlgItem(IDC_EDIT6))->EnableWindow(TRUE); - ((CButton *)GetDlgItem(IDC_BUTTON2))->EnableWindow(TRUE); //rewbs.timeStretchMods - GetDlgItem(IDC_TEXT_QUALITY)->ShowWindow(SW_HIDE); - GetDlgItem(IDC_COMBO5)->ShowWindow(SW_HIDE); - GetDlgItem(IDC_TEXT_FFT)->ShowWindow(SW_HIDE); - GetDlgItem(IDC_COMBO6)->ShowWindow(SW_HIDE); - GetDlgItem(IDC_TEXT_PITCH)->ShowWindow(SW_HIDE); - GetDlgItem(IDC_COMBO4)->ShowWindow(SW_HIDE); - //if(CMainFrame::gbShowHackControls == true) - //{ - GetDlgItem(IDC_TEXT_STRETCHPARAMS)->ShowWindow(SW_SHOW); - GetDlgItem(IDC_EDIT_STRETCHPARAMS)->ShowWindow(SW_SHOW); - //} - SetDlgItemText(IDC_BUTTON1, "Time Stretch"); - UpdateTimeStretchParameterString(); - } - // Enable pitch-shifting / disable unused time-stretching UI elements - else{ - ReadTimeStretchParameters(); - GetDlgItem(IDC_TEXT_QUALITY)->ShowWindow(SW_SHOW); - GetDlgItem(IDC_COMBO5)->ShowWindow(SW_SHOW); - GetDlgItem(IDC_TEXT_FFT)->ShowWindow(SW_SHOW); - GetDlgItem(IDC_COMBO6)->ShowWindow(SW_SHOW); - ((CComboBox *)GetDlgItem(IDC_COMBO4))->EnableWindow(TRUE); - ((CEdit *)GetDlgItem(IDC_EDIT6))->EnableWindow(FALSE); - ((CButton *)GetDlgItem(IDC_BUTTON2))->EnableWindow(FALSE); //rewbs.timeStretchMods - GetDlgItem(IDC_TEXT_STRETCHPARAMS)->ShowWindow(SW_HIDE); - GetDlgItem(IDC_EDIT_STRETCHPARAMS)->ShowWindow(SW_HIDE); - GetDlgItem(IDC_TEXT_PITCH)->ShowWindow(SW_SHOW); - GetDlgItem(IDC_COMBO4)->ShowWindow(SW_SHOW); - SetDlgItemText(IDC_BUTTON1, "Pitch Shift"); - } + bool bTimeStretch = IsDlgButtonChecked(IDC_CHECK3) ? true : false; + if(!bTimeStretch) ReadTimeStretchParameters(); + ((CComboBox *)GetDlgItem(IDC_COMBO4))->EnableWindow(bTimeStretch ? false : true); + ((CEdit *)GetDlgItem(IDC_EDIT6))->EnableWindow(bTimeStretch ? true : false); + ((CButton *)GetDlgItem(IDC_BUTTON2))->EnableWindow(bTimeStretch ? true : false); //rewbs.timeStretchMods + GetDlgItem(IDC_TEXT_QUALITY)->ShowWindow(bTimeStretch ? SW_HIDE : SW_SHOW); + GetDlgItem(IDC_COMBO5)->ShowWindow(bTimeStretch ? SW_HIDE : SW_SHOW); + GetDlgItem(IDC_TEXT_FFT)->ShowWindow(bTimeStretch ? SW_HIDE : SW_SHOW); + GetDlgItem(IDC_COMBO6)->ShowWindow(bTimeStretch ? SW_HIDE : SW_SHOW); + GetDlgItem(IDC_TEXT_STRETCHPARAMS)->ShowWindow(bTimeStretch ? SW_SHOW : SW_HIDE); + GetDlgItem(IDC_EDIT_STRETCHPARAMS)->ShowWindow(bTimeStretch ? SW_SHOW : SW_HIDE); + GetDlgItem(IDC_TEXT_PITCH)->ShowWindow(bTimeStretch ? SW_HIDE : SW_SHOW); + GetDlgItem(IDC_COMBO4)->ShowWindow(bTimeStretch ? SW_HIDE : SW_SHOW); + SetDlgItemText(IDC_BUTTON1, "Time Stretch"); + if(bTimeStretch) UpdateTimeStretchParameterString(); } void CCtrlSamples::OnEstimateSampleSize() @@ -1758,25 +1730,6 @@ MODSAMPLE *pSmp = &m_pSndFile->Samples[m_nSample]; if(!pSmp || pSmp->nLength == 0) goto error; - // Preview management - if(IsDlgButtonChecked(IDC_CHECK2)){ - // Free previous undo buffer - if(pSampleUndoBuffer) CSoundFile::FreeSample(pSampleUndoBuffer); - // Allocate sample undo buffer - BYTE smpsize = (pSmp->uFlags & CHN_16BIT) ? 2 : 1; - UINT nChn = (pSmp->uFlags & CHN_STEREO) ? 2 : 1; - UndoBufferSize = pSmp->nLength * nChn * smpsize; - pSampleUndoBuffer = CSoundFile::AllocateSample(UndoBufferSize); - // Not enough memory... - if(pSampleUndoBuffer == NULL){ - UndoBufferSize = 0; - errorcode = 3; - goto error; - } - // Copy sample to sample undo buffer - memcpy(pSampleUndoBuffer,pSmp->pSample,UndoBufferSize); - } - // Time stretching if(IsDlgButtonChecked(IDC_CHECK3)){ //rewbs.timeStretchMods @@ -1811,24 +1764,6 @@ errorcode = PitchShift(pitch); } - // Preview management - if(errorcode == 0 && IsDlgButtonChecked(IDC_CHECK2)){ - ((CButton *)GetDlgItem(IDC_BUTTON1))->ShowWindow(SW_HIDE); - ((CButton *)GetDlgItem(IDC_BUTTON3))->ShowWindow(SW_SHOW); - ((CButton *)GetDlgItem(IDC_BUTTON4))->ShowWindow(SW_SHOW); - SetDlgItemText(IDC_STATIC1,"Preview..."); - - //rewbs.timeStretchMods - //Disable all pitch shift / timestrech buttons until accepted or restored: - GetDlgItem(IDC_COMBO5)->EnableWindow(FALSE); - GetDlgItem(IDC_COMBO6)->EnableWindow(FALSE); - GetDlgItem(IDC_CHECK3)->EnableWindow(FALSE); - GetDlgItem(IDC_COMBO4)->EnableWindow(FALSE); - GetDlgItem(IDC_EDIT6)->EnableWindow(FALSE); - GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE); - //end rewbs.timeStretchMods - } - // Error management error: @@ -1841,8 +1776,6 @@ break; case 3 : wsprintf(str,"Not enough memory..."); break; - case 4 : wsprintf(str, "Action can be applied only to 16-bit samples."); - break; case 5 : wsprintf(str, "Too low sample rate"); break; case 6 : wsprintf(str, "Too short sample"); @@ -1858,85 +1791,7 @@ m_pModDoc->SetModified(); } -void CCtrlSamples::OnPitchShiftTimeStretchAccept() -//------------------------------------------------ -{ - // Free sample undo buffer - if(pSampleUndoBuffer) CSoundFile::FreeSample(pSampleUndoBuffer); - pSampleUndoBuffer = NULL; - UndoBufferSize = 0; - // Restore UI buttons - ((CButton *)GetDlgItem(IDC_BUTTON1))->ShowWindow(SW_SHOW); - ((CButton *)GetDlgItem(IDC_BUTTON3))->ShowWindow(SW_HIDE); - ((CButton *)GetDlgItem(IDC_BUTTON4))->ShowWindow(SW_HIDE); - SetDlgItemText(IDC_STATIC1,""); - //rewbs.timeStretchMods - //Disable all pitch shift / timestrech buttons until accepted or restored: - GetDlgItem(IDC_COMBO5)->EnableWindow(TRUE); - GetDlgItem(IDC_COMBO6)->EnableWindow(TRUE); - GetDlgItem(IDC_CHECK3)->EnableWindow(TRUE); - if (IsDlgButtonChecked(IDC_CHECK3)) - { - GetDlgItem(IDC_EDIT6)->EnableWindow(TRUE); - GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE); - } - else - { - GetDlgItem(IDC_COMBO4)->EnableWindow(TRUE); - } - //end rewbs.timeStretchMods -} - -void CCtrlSamples::OnPitchShiftTimeStretchCancel() -{ - if((!m_pSndFile) || (!m_pSndFile->Samples[m_nSample].pSample)) return; - MODSAMPLE *pSmp = &m_pSndFile->Samples[m_nSample]; - if(!pSmp) return; - - // Save processed sample buffer pointer - PVOID oldbuffer = pSmp->pSample; - BYTE smpsize = (pSmp->uFlags & CHN_16BIT) ? 2 : 1; - UINT nChn = (pSmp->uFlags & CHN_STEREO) ? 2 : 1; - - // Restore undo buffer pointer & update song data - BEGIN_CRITICAL(); - for(UINT i=0 ; i < MAX_CHANNELS ; i++){ - if((PVOID)m_pSndFile->Chn[i].pSample == oldbuffer){ - m_pSndFile->Chn[i].pSample = (LPSTR)pSampleUndoBuffer; - m_pSndFile->Chn[i].pCurrentSample = (LPSTR)pSampleUndoBuffer; - m_pSndFile->Chn[i].nLength = 0; - } - } - pSmp->pSample = (LPSTR)pSampleUndoBuffer; - pSmp->nLength = UndoBufferSize / (smpsize * nChn); - - //rewbs.timeStretchMods - // Restore loop points, if they were time stretched: - // Note: m_dTimeStretchRatio will not have changed since we disable - // GUI controls until preview is accepted/restored. - if(IsDlgButtonChecked(IDC_CHECK3)) { - pSmp->nLoopStart /= m_dTimeStretchRatio/100.0; - pSmp->nLoopEnd /= m_dTimeStretchRatio/100.0; - pSmp->nSustainStart /= m_dTimeStretchRatio/100.0; - pSmp->nSustainEnd /= m_dTimeStretchRatio/100.0; - } - //end rewbs.timeStretchMods - - END_CRITICAL(); - - // Set processed sample buffer pointer as undo sample buffer pointer - pSampleUndoBuffer = oldbuffer; - - // Free sample undo buffer & restore UI buttons - OnPitchShiftTimeStretchAccept(); - - // Update sample view - m_pModDoc->UpdateAllViews(NULL, (m_nSample << HINT_SHIFT_SMP) | HINT_SAMPLEDATA | HINT_SAMPLEINFO, NULL); // !!!! see CODE#0006, update#3 - m_pModDoc->SetModified(); -} - - int CCtrlSamples::TimeStretch(double ratio) //----------------------------------------- { @@ -1958,11 +1813,6 @@ } - // Stretching is implemented only for 16-bit samples. Return with - // error if trying to use wtih non 16-bit samples. - if(pSmp->GetElementarySampleSize() != 2) - return 4; - // SoundTouch(1.3.1) seems to crash with short samples. Don't know what // the actual limit or whether it depends on sample rate, // but simply set some semiarbitrary threshold here. @@ -1989,14 +1839,21 @@ } // Get number of channels & sample size - const BYTE smpsize = pSmp->GetElementarySampleSize(); + BYTE smpsize = pSmp->GetElementarySampleSize(); const UINT nChn = pSmp->GetNumChannels(); + // Stretching is implemented only for 16-bit samples. + if(smpsize != 2) + { + // This has to be converted to 16-bit first. + OnUpsample(); + smpsize = pSmp->GetElementarySampleSize(); + } + // Allocate new sample. Returned sample may not be exactly the size what ratio would suggest // so allocate a bit more(1.03*). const DWORD nNewSampleLength = (DWORD)(1.03 * ratio * (double)pSmp->nLength); //const DWORD nNewSampleLength = (DWORD)(0.5 + ratio * (double)pSmp->nLength); - PVOID pSample = pSmp->pSample; PVOID pNewSample = CSoundFile::AllocateSample(nNewSampleLength * nChn * smpsize); if(pNewSample == NULL) { @@ -2112,19 +1969,9 @@ ASSERT(nNewSampleLength >= nLengthCounter); + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_replace); // Swap sample buffer pointer to new buffer, update song + sample data & free old sample buffer - BEGIN_CRITICAL(); - for(UINT i=0 ; i < MAX_CHANNELS ; i++){ - if((PVOID)m_pSndFile->Chn[i].pSample == pSample){ - m_pSndFile->Chn[i].pSample = (LPSTR)pNewSample; - m_pSndFile->Chn[i].pCurrentSample = (LPSTR)pNewSample; - m_pSndFile->Chn[i].nLength = 0; - } - } - pSmp->pSample = (LPSTR)pNewSample; - CSoundFile::FreeSample(pSample); - pSmp->nLength = min(nLengthCounter, nNewSampleLength); - END_CRITICAL(); + ctrlSmp::ReplaceSample(*pSmp, (LPSTR)pNewSample, min(nLengthCounter, nNewSampleLength), m_pSndFile); // Free progress bar brushes DeleteObject((HBRUSH)green); @@ -2151,14 +1998,9 @@ MODSAMPLE *pSmp = &m_pSndFile->Samples[m_nSample]; if(!pSmp) return 2; - // PitchShift seems to work only with 16-bit samples. Return with - // error if trying to use non 16-bit samples. - if(pSmp->GetElementarySampleSize() != 2) - return 4; - // Get number of channels & sample size - BYTE smpsize = (pSmp->uFlags & CHN_16BIT) ? 2 : 1; - UINT nChn = (pSmp->uFlags & CHN_STEREO) ? 2 : 1; + BYTE smpsize = pSmp->GetElementarySampleSize(); + UINT nChn = pSmp->GetNumChannels(); // Get selected oversampling - quality - (also refered as FFT overlapping) factor CComboBox *combo = (CComboBox *)GetDlgItem(IDC_COMBO5); @@ -2199,10 +2041,21 @@ BeginWaitCursor(); SetDlgItemText(IDC_STATIC1,"Pitch shifting..."); + + // PitchShift seems to work only with 16-bit samples. + if(smpsize != 2) + { + // This has to be converted to 16-bit first. + OnUpsample(); + smpsize = pSmp->GetElementarySampleSize(); + } + // Allocate working buffers float * buffer = new float[MAX_BUFFER_LENGTH + fft]; float * outbuf = new float[MAX_BUFFER_LENGTH + fft]; + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_replace); + // Process each channel separately for(UINT i = 0 ; i < nChn ; i++){ @@ -2316,73 +2169,18 @@ void CCtrlSamples::OnReverse() //---------------------------- { - SAMPLEVIEWSTATE viewstate; - MODSAMPLE *pSmp; - DWORD dwBeginSel, dwEndSel; - LPVOID pSample; - UINT rlen, smplsize; - - memset(&viewstate, 0, sizeof(viewstate)); - SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); - if ((!m_pSndFile) || (!m_pSndFile->Samples[m_nSample].pSample)) return; - BeginWaitCursor(); - dwBeginSel = viewstate.dwBeginSel; - dwEndSel = viewstate.dwEndSel; - pSmp = &m_pSndFile->Samples[m_nSample]; - rlen = pSmp->nLength; - pSample = pSmp->pSample; - smplsize = (pSmp->uFlags & CHN_16BIT) ? 2 : 1; - if (pSmp->uFlags & CHN_STEREO) smplsize *= 2; - if ((dwEndSel > dwBeginSel) && (dwEndSel <= rlen)) - { - rlen = dwEndSel - dwBeginSel; - pSample = ((LPBYTE)pSmp->pSample) + dwBeginSel * smplsize; - } - if (rlen >= 2) - { - - if (smplsize == 4) - { - UINT len = rlen / 2; - UINT max = rlen - 1; - int *p = (int *)pSample; + MODSAMPLE *pSmp = &m_pSndFile->Samples[m_nSample]; - for (UINT i=0; i<len; i++) - { - int tmp = p[max-i]; - p[max-i] = p[i]; - p[i] = tmp; - } - } else - if (smplsize == 2) - { - UINT len = rlen / 2; - UINT max = rlen - 1; - signed short *p = (signed short *)pSample; + SELECTIONPOINTS selection = GetSelectionPoints(); - for (UINT i=0; i<len; i++) - { - signed short tmp = p[max-i]; - p[max-i] = p[i]; - p[i] = tmp; - } - } else - { - UINT len = rlen / 2; - UINT max = rlen - 1; - signed char *p = (signed char *)pSample; - - for (UINT i=0; i<len; i++) - { - signed char tmp = p[max-i]; - p[max-i] = p[i]; - p[i] = tmp; - } - } - m_pModDoc->AdjustEndOfSample(m_nSample); - // 05/01/05 : ericus replaced "m_nSample << 24" by "m_nSample << 20" : 4000 samples -> 12bits [see Moddoc.h] + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_reverse, selection.nStart, selection.nEnd); + if(ctrlSmp::ReverseSample(pSmp, selection.nStart, selection.nEnd, m_pSndFile) == true) + { m_pModDoc->UpdateAllViews(NULL, (m_nSample << HINT_SHIFT_SMP) | HINT_SAMPLEDATA, NULL); m_pModDoc->SetModified(); + } else + { + m_pModDoc->GetSampleUndo()->RemoveLastUndoStep(m_nSample); } EndWaitCursor(); SwitchToView(); @@ -2390,22 +2188,31 @@ void CCtrlSamples::OnInvert() -//-------------------------------- +//--------------------------- { - ApplyAmplify(-100); + MODSAMPLE *pSmp = &m_pSndFile->Samples[m_nSample]; + + SELECTIONPOINTS selection = GetSelectionPoints(); + + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_invert, selection.nStart, selection.nEnd); + if(ctrlSmp::InvertSample(pSmp, selection.nStart, selection.nEnd, m_pSndFile) == true) + { + m_pModDoc->UpdateAllViews(NULL, (m_nSample << HINT_SHIFT_SMP) | HINT_SAMPLEDATA, NULL); + m_pModDoc->SetModified(); + } else + { + m_pModDoc->GetSampleUndo()->RemoveLastUndoStep(m_nSample); + } + EndWaitCursor(); + SwitchToView(); } void CCtrlSamples::OnSignUnSign() //------------------------------- { - // purpose: sign/unsign a sample ("distortion") - SAMPLEVIEWSTATE viewstate; - DWORD dwStart, dwEnd; MODSAMPLE *pSmp; - memset(&viewstate, 0, sizeof(viewstate)); - SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); if ((!m_pModDoc) || (!m_pSndFile) || (!m_pSndFile->Samples[m_nSample].pSample)) return; if(m_pModDoc->IsNotePlaying(0, m_nSample, 0) == TRUE) @@ -2413,29 +2220,17 @@ BeginWaitCursor(); pSmp = &m_pSndFile->Samples[m_nSample]; - dwStart = viewstate.dwBeginSel; - dwEnd = viewstate.dwEndSel; - if (dwEnd > pSmp->nLength) dwEnd = pSmp->nLength; - if (dwStart > dwEnd) dwStart = dwEnd; - if (dwStart >= dwEnd) + SELECTIONPOINTS selection = GetSelectionPoints(); + + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_unsign, selection.nStart, selection.nEnd); + if(ctrlSmp::UnsignSample(pSmp, selection.nStart, selection.nEnd, m_pSndFile) == true) { - dwStart = 0; - dwEnd = pSmp->nLength; - } - if (pSmp->uFlags & CHN_STEREO) { dwStart *= 2; dwEnd *= 2; } - UINT len = dwEnd - dwStart; - if (pSmp->uFlags & CHN_16BIT) + m_pModDoc->UpdateAllViews(NULL, (m_nSample << HINT_SHIFT_SMP) | HINT_SAMPLEDATA, NULL); + m_pModDoc->SetModified(); + } else { - signed short *p = ((signed short *)pSmp->pSample) + dwStart; - for (UINT i=0; i<len; ++i) p[i] += 0x8000; //unsign - } else { - signed char *p = ((signed char *)pSmp->pSample) + dwStart; - for (UINT i=0; i<len; ++i) p[i] += 0x80; //unsign + m_pModDoc->GetSampleUndo()->RemoveLastUndoStep(m_nSample); } - - m_pModDoc->AdjustEndOfSample(m_nSample); - m_pModDoc->UpdateAllViews(NULL, (m_nSample << HINT_SHIFT_SMP) | HINT_SAMPLEDATA, NULL); - m_pModDoc->SetModified(); EndWaitCursor(); SwitchToView(); } @@ -2444,55 +2239,54 @@ void CCtrlSamples::OnSilence() //---------------------------- { - SAMPLEVIEWSTATE viewstate; MODSAMPLE *pSmp; - DWORD dwBeginSel, dwEndSel; - memset(&viewstate, 0, sizeof(viewstate)); - SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); if ((!m_pSndFile) || (!m_pSndFile->Samples[m_nSample].pSample)) return; BeginWaitCursor(); - dwBeginSel = viewstate.dwBeginSel; - dwEndSel = viewstate.dwEndSel; - pSmp = &m_pSndFile->Samples[m_nSample]; - if (dwEndSel > pSmp->nLength) dwEndSel = pSmp->nLength; - if (dwEndSel > dwBeginSel+1) + SELECTIONPOINTS selection = GetSelectionPoints(); + + // never apply silence to a sample that has no selection + if(selection.bSelected == true) { - int len = dwEndSel - dwBeginSel; + pSmp = &m_pSndFile->Samples[m_nSample]; + m_pModDoc->GetSampleUndo()->PrepareUndo(m_nSample, sundo_update, selection.nStart, selection.nEnd); + + int len = selection.nEnd - selection.nStart; if (pSmp->uFlags & CHN_STEREO) { int smplsize = (pSmp->uFlags & CHN_16BIT) ? 4 : 2; - signed char *p = ((signed char *)pSmp->pSample) + dwBeginSel*smplsize; + signed char *p = ((signed char *)pSmp->pSample) + selection.nStart * smplsize; memset(p, 0, len*smplsize); } else - if (pSmp->uFlags & CHN_16BIT) - { - short int *p = ((short int *)pSmp->pSample) + dwBeginSel; - int dest = (dwEndSel < pSmp->nLength) ? p[len-1] : 0; - int base = (dwBeginSel) ? p[0] : 0; - int delta = dest - base; - for (int i=0; i<len; i++) + if (pSmp->uFlags & CHN_16BIT) { - int n = base + (int)(((LONGLONG)delta * (LONGLONG)i) / (len-1)); - p[i] = (signed short)n; - } - } else - { - signed char *p = ((signed char *)pSmp->pSample) + dwBeginSel; - int dest = (dwEndSel < pSmp->nLength) ? p[len-1] : 0; - int base = (dwBeginSel) ? p[0] : 0; - int delta = dest - base; - for (int i=0; i<len; i++) + short int *p = ((short int *)pSmp->pSample) + selection.nStart; + int dest = (selection.nEnd < pSmp->nLength) ? p[len-1] : 0; + int base = (selection.nStart) ? p[0] : 0; + int delta = dest - base; + for (int i=0; i<len; i++) + { + int n = base + (int)(((LONGLONG)delta * (LONGLONG)i) / (len-1)); + p[i] = (signed short)n; + } + } else { - int n = base + (delta * i) / (len-1); - p[i] = (signed char)n; + signed char *p = ((signed char *)pSmp->pSample) + selection.nStart; + int dest = (selection.nEnd < pSmp->nLength) ? p[len-1] : 0; + int base = (selection.nStart) ? p[0] : 0; + int delta = dest - base; + for (int i=0; i<len; i++) + { + int n = base + (delta * i) / (len-1); + p[i] = (signed char)n; + } } - } - m_pModDoc->AdjustEndOfSample(m_nSample); - // 05/01/05 : ericus replaced "m_nSample << 24" by "m_nSample << 20" : 4000 samples -> 12bits [see Moddoc.h] - m_pModDoc->UpdateAllViews(NULL, (m_nSample << HINT_SHIFT_SMP) | HINT_SAMPLEDATA, NULL); - m_pModDoc->SetModified(); + m_pModDoc->AdjustEndOfSample(m_nSample); + // 05/01/05 : ericus replaced "m_nSample << 24" by "m_nSample << 20" : 4000 samples -> 12bits [see Moddoc.h] + m_pModDoc->UpdateAllViews(NULL, (m_nSample << HINT_SHIFT_SMP) | HINT_SAMPLEDATA, NULL); + m_pModDoc->SetModified(); } + EndWaitCursor(); SwitchToView(); } @@ -3207,3 +3001,31 @@ return 0; } //end rewbs.customKeys + + +// Return currently selected part of the sample. +// The whole sample size will be returned if no part of the sample is selected. +// However, point.bSelected indicates whether a sample selection exists or not. +SELECTIONPOINTS CCtrlSamples::GetSelectionPoints() +//------------------------------------------------ +{ + SELECTIONPOINTS points; + SAMPLEVIEWSTATE viewstate; + MODSAMPLE *pSmp = &m_pSndFile->Samples[m_nSample]; + + memset(&viewstate, 0, sizeof(viewstate)); + SendViewMessage(VIEWMSG_SAVESTATE, (LPARAM)&viewstate); + points.nStart = viewstate.dwBeginSel; + points.nEnd = viewstate.dwEndSel; + if(points.nEnd > pSmp->nLength) points.nEnd = pSmp->nLength; + if(points.nStart > points.nEnd) points.nStart = points.nEnd; + points.bSelected = true; + if(points.nStart >= points.nEnd) + { + points.nStart = 0; + points.nEnd = pSmp->nLength; + points.bSelected = false; + } + return points; +} + Modified: trunk/OpenMPT/mptrack/Ctrl_smp.h =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_smp.h 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/Ctrl_smp.h 2009-11-01 13:53:08 UTC (rev 408) @@ -2,6 +2,13 @@ #define _CONTROL_SAMPLES_H_ +struct SELECTIONPOINTS +{ + UINT nStart; + UINT nEnd; + bool bSelected; // does sample selection exist or not? +}; + //======================================= class CCtrlSamples: public CModControlDlg //======================================= @@ -27,8 +34,6 @@ enum {nDefaultStretchChunkSize = 8192}; CComboBox m_ComboPitch, m_ComboQuality, m_ComboFFT; - PVOID pSampleUndoBuffer; - UINT UndoBufferSize; int PitchShift(float pitch); int TimeStretch(double ratio); @@ -38,6 +43,7 @@ // Applies amplification to sample. Negative values // can be used to invert phase. void ApplyAmplify(LONG nAmp, bool bFadeIn = false, bool bFadeOut = false); + SELECTIONPOINTS GetSelectionPoints(); public: CCtrlSamples(); @@ -104,8 +110,6 @@ afx_msg void OnPitchShiftTimeStretch(); afx_msg void OnEnableStretchToSize(); afx_msg void OnEstimateSampleSize(); - afx_msg void OnPitchShiftTimeStretchAccept(); - afx_msg void OnPitchShiftTimeStretchCancel(); //}}AFX_MSG DECLARE_MESSAGE_MAP() Modified: trunk/OpenMPT/mptrack/EffectVis.cpp =================================================================== --- trunk/OpenMPT/mptrack/EffectVis.cpp 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/EffectVis.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -578,7 +578,7 @@ rect.SetRect(x-NODEHALF, y-NODEHALF, x+NODEHALF+1, y+NODEHALF+1); if (rect.PtInRect(point)) { - m_pModDoc->PreparePatternUndo(m_nPattern, m_nChan, row, m_nChan+1, row+1); + m_pModDoc->GetPatternUndo()->PrepareUndo(m_nPattern, m_nChan, row, m_nChan+1, row+1); m_nDragItem = row; } } @@ -669,7 +669,7 @@ SetFocus(); SetCapture(); - m_pModDoc->PreparePatternUndo(m_nPattern, m_nChan, m_startRow, m_nChan+1, m_endRow); + m_pModDoc->GetPatternUndo()->PrepareUndo(m_nPattern, m_nChan, m_startRow, m_nChan+1, m_endRow); m_dwStatus |= FXVSTATUS_LDRAGGING; } Modified: trunk/OpenMPT/mptrack/MainFrm.cpp =================================================================== --- trunk/OpenMPT/mptrack/MainFrm.cpp 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/MainFrm.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -204,6 +204,7 @@ | PATTERN_SHOWPREVIOUS | PATTERN_CONTSCROLL | PATTERN_SYNCMUTE | PATTERN_AUTODELAY | PATTERN_NOTEFADE; DWORD CMainFrame::m_nRowSpacing = 16; DWORD CMainFrame::m_nRowSpacing2 = 4; +UINT CMainFrame::m_nSampleUndoMaxBuffer = 100; // GDI HICON CMainFrame::m_hIcon = NULL; @@ -443,6 +444,8 @@ CSoundFile::s_DefaultPlugVolumeHandling = static_cast<uint8>(GetPrivateProfileInt("Misc", "DefaultPlugVolumeHandling", PLUGIN_VOLUMEHANDLING_IGNORE, iniFile)); if(CSoundFile::s_DefaultPlugVolumeHandling > 2) CSoundFile::s_DefaultPlugVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; + m_nSampleUndoMaxBuffer = GetPrivateProfileLong("Sample Editor" , "UndoBufferSize", m_nSampleUndoMaxBuffer, iniFile) << 20; + TCHAR szPath[_MAX_PATH] = ""; GetPrivateProfileString("Paths", "Songs_Directory", GetDefaultDirectory(DIR_MODS), szPath, INIBUFFERSIZE, iniFile); SetDefaultDirectory(szPath, DIR_MODS); Modified: trunk/OpenMPT/mptrack/Mainfrm.h =================================================================== --- trunk/OpenMPT/mptrack/Mainfrm.h 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/Mainfrm.h 2009-11-01 13:53:08 UTC (rev 408) @@ -406,6 +406,9 @@ static BOOL gbPatternVUMeters, gbPatternPluginNames, gbPatternRecord; static DWORD m_dwPatternSetup, m_dwMidiSetup, m_nRowSpacing, m_nRowSpacing2, m_nKeyboardCfg, gnHotKeyMask; static bool m_bHideUnavailableCtxMenuItems; + // Sample Editor Setup + static UINT m_nSampleUndoMaxBuffer; + // GDI static HICON m_hIcon; static HFONT m_hGUIFont, m_hFixedFont, m_hLargeFixedFont; Modified: trunk/OpenMPT/mptrack/Moddoc.cpp =================================================================== --- trunk/OpenMPT/mptrack/Moddoc.cpp 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/Moddoc.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -95,7 +95,10 @@ m_bPaused = TRUE; m_lpszLog = NULL; m_hWndFollow = NULL; - memset(PatternUndo, 0, sizeof(PatternUndo)); + + m_PatternUndo.SetParent(this); + m_SampleUndo.SetParent(this); + #ifdef _DEBUG MODCHANNEL *p = m_SndFile.Chn; if (((DWORD)p) & 7) Log("MODCHANNEL is not aligned (0x%08X)\n", p); @@ -113,7 +116,6 @@ CModDoc::~CModDoc() //----------------- { - ClearPatternUndo(); ClearLog(); } @@ -149,7 +151,7 @@ m_SndFile.m_nMixLevels = m_SndFile.GetModSpecifications().defaultMixLevels; m_SndFile.m_pConfig->SetMixLevels(m_SndFile.m_nMixLevels); // ...and the order length - m_SndFile.Order.resize(min(MAX_ORDERS, m_SndFile.GetModSpecifications().ordersMax)); + m_SndFile.Order.resize(m_SndFile.GetModSpecifications().ordersMax); theApp.GetDefaultMidiMacro(&m_SndFile.m_MidiCfg); ReinitRecordState(); Modified: trunk/OpenMPT/mptrack/Moddoc.h =================================================================== --- trunk/OpenMPT/mptrack/Moddoc.h 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/Moddoc.h 2009-11-01 13:53:08 UTC (rev 408) @@ -11,9 +11,9 @@ #include "sndfile.h" #include "misc_util.h" +#include "Undo.h" - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Bit Mask for updating view (hints of what changed) @@ -89,20 +89,6 @@ STATIC_ASSERT( ((-1 << HINT_SHIFT_SEQUENCE) & HINT_MASK_ITEM) == (-1 << HINT_SHIFT_SEQUENCE) ); - -// Undo -#define MAX_UNDO_LEVEL 100 - -// Pattern Undo -typedef struct PATTERNUNDOBUFFER -{ - UINT pattern, patternsize; - UINT column, row; - UINT cx, cy; - MODCOMMAND *pbuffer; -} PATTERNUNDOBUFFER, *PPATTERNUNDOBUFFER; - - //parametered macro presets: enum { @@ -128,7 +114,6 @@ BOOL m_bPaused; HWND m_hWndFollow; DWORD m_dwNotifyType; - PATTERNUNDOBUFFER PatternUndo[MAX_UNDO_LEVEL]; bool bModifiedAutosave; // Modified since last autosave? @@ -140,6 +125,9 @@ std::bitset<MAX_BASECHANNELS> m_bsMultiSplitRecordMask; // -! NEW_FEATURE#0015 + CPatternUndo m_PatternUndo; + CSampleUndo m_SampleUndo; + protected: // create from serialization only CModDoc(); DECLARE_SERIAL(CModDoc) @@ -189,6 +177,10 @@ static int GetZxxType(const CHAR (&szMidiZXXExt)[128 * 32]); static void CreateZxxFromType(CHAR (&szMidiZXXExt)[128 * 32], int iZxxType); void SongProperties(); + + CPatternUndo *GetPatternUndo() { return &m_PatternUndo; } + CSampleUndo *GetSampleUndo() { return &m_SampleUndo; } + // operations public: BOOL ChangeModType(MODTYPE wType); @@ -243,15 +235,14 @@ bool MoveOrder(ORDERINDEX nSourceNdx, ORDERINDEX nDestNdx, bool bUpdate = true, bool bCopy = false, SEQUENCEINDEX nSourceSeq = SEQUENCEINDEX_INVALID, SEQUENCEINDEX nDestSeq = SEQUENCEINDEX_INVALID); BOOL ExpandPattern(PATTERNINDEX nPattern); BOOL ShrinkPattern(PATTERNINDEX nPattern); + + // Copy&Paste BOOL CopyPattern(PATTERNINDEX nPattern, DWORD dwBeginSel, DWORD dwEndSel); BOOL PastePattern(PATTERNINDEX nPattern, DWORD dwBeginSel, BOOL mix, BOOL ITStyleMix=FALSE); //rewbs.mixpaste BOOL CopyEnvelope(UINT nIns, UINT nEnv); BOOL PasteEnvelope(UINT nIns, UINT nEnv); - BOOL ClearPatternUndo(); - BOOL PreparePatternUndo(UINT pattern, UINT x, UINT y, UINT cx, UINT cy); - UINT DoPatternUndo(); - BOOL CanPatternUndo(); + LRESULT ActivateView(UINT nIdView, DWORD dwParam); void UpdateAllViews(CView *pSender, LPARAM lHint=0L, CObject *pHint=NULL); HWND GetEditPosition(ROWINDEX &row, PATTERNINDEX &pat, ORDERINDEX &ord); //rewbs.customKeys Modified: trunk/OpenMPT/mptrack/Modedit.cpp =================================================================== --- trunk/OpenMPT/mptrack/Modedit.cpp 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/Modedit.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -369,7 +369,8 @@ AddToLog("WARNING: Instrument envelopes have been shortened.\n"); SetModified(); - ClearPatternUndo(); + GetPatternUndo()->ClearUndo(); + GetSampleUndo()->ClearUndo(); UpdateAllViews(NULL, HINT_MODTYPE | HINT_MODGENERAL); EndWaitCursor(); return TRUE; @@ -470,7 +471,7 @@ EndWaitCursor(); } SetModified(); - ClearPatternUndo(); + GetPatternUndo()->ClearUndo(); UpdateAllViews(NULL, HINT_MODTYPE); return TRUE; } @@ -543,7 +544,7 @@ END_CRITICAL(); EndWaitCursor(); SetModified(); - ClearPatternUndo(); + GetPatternUndo()->ClearUndo(); UpdateAllViews(NULL, HINT_MODTYPE); return FALSE; } @@ -1144,7 +1145,7 @@ if ((hCpy) && ((p = (LPSTR)GlobalLock(hCpy)) != NULL)) { - PreparePatternUndo(nPattern, 0, 0, m_SndFile.m_nChannels, m_SndFile.PatternSize[nPattern]); + GetPatternUndo()->PrepareUndo(nPattern, 0, 0, m_SndFile.m_nChannels, m_SndFile.PatternSize[nPattern]); TEMPO spdmax = m_SndFile.GetModSpecifications().speedMax; DWORD dwMemSize = GlobalSize(hCpy); MODCOMMAND *m = m_SndFile.Patterns[nPattern]; @@ -1350,7 +1351,7 @@ nPattern = m_SndFile.Order[oNextOrder]; if(m_SndFile.Patterns.IsValidPat(nPattern) == false) goto PasteDone; m = m_SndFile.Patterns[nPattern]; - PreparePatternUndo(nPattern, 0,0, m_SndFile.m_nChannels, m_SndFile.PatternSize[nPattern]); + GetPatternUndo()->PrepareUndo(nPattern, 0,0, m_SndFile.m_nChannels, m_SndFile.PatternSize[nPattern]); oCurrentOrder = oNextOrder; } } @@ -1559,126 +1560,6 @@ } -///////////////////////////////////////////////////////////////////////////////////////// -// Pattern Undo Functions - -BOOL CModDoc::ClearPatternUndo() -//------------------------------ -{ - for (UINT i=0; i<MAX_UNDO_LEVEL; i++) - { - if (PatternUndo[i].pbuffer) delete[] PatternUndo[i].pbuffer; - PatternUndo[i].cx = 0; - PatternUndo[i].cy = 0; - PatternUndo[i].pbuffer = NULL; - } - return TRUE; -} - - -BOOL CModDoc::CanPatternUndo() -//---------------------------- -{ - return (PatternUndo[0].pbuffer) ? TRUE : FALSE; -} - - -BOOL CModDoc::PreparePatternUndo(UINT pattern, UINT x, UINT y, UINT cx, UINT cy) -//------------------------------------------------------------------------------ -{ - MODCOMMAND *pUndo, *pPattern; - UINT nRows; - BOOL bUpdate; - - if ((pattern >= m_SndFile.Patterns.Size()) || (!m_SndFile.Patterns[pattern])) return FALSE; - nRows = m_SndFile.PatternSize[pattern]; - pPattern = m_SndFile.Patterns[pattern]; - if ((y >= nRows) || (cx < 1) || (cy < 1) || (x >= m_SndFile.m_nChannels)) return FALSE; - if (y+cy >= nRows) cy = nRows-y; - if (x+cx >= m_SndFile.m_nChannels) cx = m_SndFile.m_nChannels - x; - BeginWaitCursor(); - pUndo = new MODCOMMAND[cx*cy]; - if (!pUndo) - { - EndWaitCursor(); - return FALSE; - } - bUpdate = (PatternUndo[0].pbuffer) ? FALSE : TRUE; - if (PatternUndo[MAX_UNDO_LEVEL-1].pbuffer) - { - delete[] PatternUndo[MAX_UNDO_LEVEL-1].pbuffer; - PatternUndo[MAX_UNDO_LEVEL-1].pbuffer = NULL; - } - for (UINT i=MAX_UNDO_LEVEL-1; i>=1; i--) - { - PatternUndo[i] = PatternUndo[i-1]; - } - PatternUndo[0].pattern = pattern; - PatternUndo[0].patternsize = m_SndFile.PatternSize[pattern]; - PatternUndo[0].column = x; - PatternUndo[0].row = y; - PatternUndo[0].cx = cx; - PatternUndo[0].cy = cy; - PatternUndo[0].pbuffer = pUndo; - pPattern += x + y*m_SndFile.m_nChannels; - for (UINT iy=0; iy<cy; iy++) - { - memcpy(pUndo, pPattern, cx*sizeof(MODCOMMAND)); - pUndo += cx; - pPattern += m_SndFile.m_nChannels; - } - EndWaitCursor(); - if (bUpdate) UpdateAllViews(NULL, HINT_UNDO); - return TRUE; -} - - -UINT CModDoc::DoPatternUndo() -//--------------------------- -{ - MODCOMMAND *pUndo, *pPattern; - UINT nPattern, nRows; - - if ((!PatternUndo[0].pbuffer) || (PatternUndo[0].pattern >= m_SndFile.Patterns.Size())) return (UINT)-1; - nPattern = PatternUndo[0].pattern; - nRows = PatternUndo[0].patternsize; - if (PatternUndo[0].column + PatternUndo[0].cx <= m_SndFile.m_nChannels) - { - if ((!m_SndFile.Patterns[nPattern]) || (m_SndFile.PatternSize[nPattern] < nRows)) - { - MODCOMMAND *newPattern = CSoundFile::AllocatePattern(nRows, m_SndFile.m_nChannels); - MODCOMMAND *oldPattern = m_SndFile.Patterns[nPattern]; - if (!newPattern) return (UINT)-1; - const ROWINDEX nOldRowCount = m_SndFile.Patterns[nPattern].GetNumRows(); - m_SndFile.Patterns[nPattern].SetData(newPattern, nRows); - if (oldPattern) - { - memcpy(newPattern, oldPattern, m_SndFile.m_nChannels*nOldRowCount*sizeof(MODCOMMAND)); - CSoundFile::FreePattern(oldPattern); - } - } - pUndo = PatternUndo[0].pbuffer; - pPattern = m_SndFile.Patterns[nPattern]; - if (!m_SndFile.Patterns[nPattern]) return (UINT)-1; - pPattern += PatternUndo[0].column + (PatternUndo[0].row * m_SndFile.m_nChannels); - for (UINT iy=0; iy<PatternUndo[0].cy; iy++) - { - memcpy(pPattern, pUndo, PatternUndo[0].cx * sizeof(MODCOMMAND)); - pPattern += m_SndFile.m_nChannels; - pUndo += PatternUndo[0].cx; - } - } - delete[] PatternUndo[0].pbuffer; - for (UINT i=0; i<MAX_UNDO_LEVEL-1; i++) - { - PatternUndo[i] = PatternUndo[i+1]; - } - PatternUndo[MAX_UNDO_LEVEL-1].pbuffer = NULL; - if (!PatternUndo[0].pbuffer) UpdateAllViews(NULL, HINT_UNDO); - return nPattern; -} - - void CModDoc::CheckUnusedChannels(BOOL mask[MAX_CHANNELS], CHANNELINDEX maxRemoveCount) //-------------------------------------------------------- { Modified: trunk/OpenMPT/mptrack/Mptrack.cpp =================================================================== --- trunk/OpenMPT/mptrack/Mptrack.cpp 2009-10-30 22:47:43 UTC (rev 407) +++ trunk/OpenMPT/mptrack/Mptrack.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -828,6 +828,9 @@ // Allow allocations of at least 16MB if (gMemStatus.dwTotalPhys < 16*1024*1024) gMemStatus.dwTotalPhys = 16*1024*1024; + CMainFrame::m_nSampleUndoMaxBuffer = gMemStatus.dwTotalPhys / 10; // set sample undo buffer size + if(CMainFrame::m_nSampleUndoMaxBuffer < 1) CMainFrame::m_nSampleUndoMaxBuffer = 1; + ASSERT(NULL == m_pDocManager); m_pDocManager = new CModDocManager(); Added: trunk/OpenMPT/mptrack/Undo.cpp =================================================================== --- trunk/OpenMPT/mptrack/Undo.cpp (rev 0) +++ trunk/OpenMPT/mptrack/Undo.cpp 2009-11-01 13:53:08 UTC (rev 408) @@ -0,0 +1,460 @@ +/* + * Undo.cpp + * -------- + * Purpose: Undo functions for pattern and sample editor + * Notes : (currently none) + * Authors: Olivier Lapicque + * OpenMPT Devs + */ + + +#include "stdafx.h" +#include "moddoc.h" +#include "MainFrm.h" +#include "modsmp_ctrl.h" +#include "Undo.h" + + +///////////////////////////////////////////////////////////////////////////////////////// +// Pattern Undo Functions + + +// Remove all undo steps. +void CPatternUndo::ClearUndo() +//---------------------------- +{ + while(UndoBuffer.size() > 0) + { + DeleteUndoStep(0); + } +} + + +// Create undo point. +bool CPatternUndo::PrepareUndo(PATTERNINDEX pattern, UINT x, UINT y, UINT cx, UINT cy) +//------------------------------------------------------------------------------------ +{ + if(m_pModDoc == nullptr) return false; + CSoundFile *pSndFile = m_pModDoc->GetSoundFile(); + if(pSndFile == nullptr) return false; + + PATTERNUNDOBUFFER sUndo; + MODCOMMAND *pUndoData, *pPattern; + UINT nRows; + + if (!pSndFile->Patterns.IsValidPat(pattern)) return false; + nRows = pSndFile->PatternSize[pattern]; + pPattern = pSndFile->Patterns[pattern]; + if ((y >= nRows) || (cx < 1) || (cy < 1) || (x >= pSndFile->m_nChannels)) return false; + if (y + cy >= nRows) cy = nRows - y; + if (x + cx >= pSndFile->m_nChannels) cx = pSndFile->m_nChannels - x; + + pUndoData = new MODCOMMAND[cx * cy]; + if (!pUndoData) return false; + + const bool bUpdate = !CanUndo(); // update undo status? + + // Remove an undo step if there are too many. + while(UndoBuffer.size() >= MAX_UNDO_LEVEL) + { + DeleteUndoStep(0); + } + + sUndo.pattern = pattern; + sUndo.patternsize = pSndFile->PatternSize[pattern]; + sUndo.column = x; + sUndo.row = y; + sUndo.cx = cx; + sUndo.cy = cy; + sUndo.pbuffer = pUndoData; + pPattern += x + y * pSndFile->m_nChannels; + for (UINT iy = 0; iy < cy; iy++) + { + memcpy(pUndoData, pPattern, cx * sizeof(MODCOMMAND)); + pUndoData += cx; + pPattern += pSndFile->m_nChannels; + } + + UndoBuffer.push_back(sUndo); + + if (bUpdate) m_pModDoc->UpdateAllViews(NULL, HINT_UNDO); + return true; +} + + +// Restore an undo point. Returns which pattern has been modified. +PATTERNINDEX CPatternUndo::Undo() +//------------------------------- +{ + if(m_pModDoc == nullptr) return PATTERNINDEX_INVALID; + CSoundFile *pSndFile = m_pModDoc->GetSoundFile(); + if(pSndFile == nullptr) return PATTERNINDEX_INVALID; + + MODCOMMAND *pUndoData, *pPattern; + PATTERNINDEX nPattern; + ROWINDEX nRows; + + if (CanUndo() == false) return PATTERNINDEX_INVALID; + + // If the most recent undo step is invalid, trash it. + while(UndoBuffer.back().pattern >= pSndFile->Patterns.Size()) + { + RemoveLastUndoStep(); + } + + // Select most recent undo slot + const PATTERNUNDOBUFFER *pUndo = &UndoBuffer.back(); + + nPattern = pUndo->pattern; + nRows = pUndo->patternsize; + if(pUndo->column + pUndo->cx <= pSndFile->m_nChannels) + { + if((!pSndFile->Patterns[nPattern]) || (pSndFile->PatternSize[nPattern] < nRows)) + { + MODCOMMAND *newPattern = CSoundFile::AllocatePattern(nRows, pSndFile->m_nChannels); + MODCOMMAND *oldPattern = pSndFile->Patterns[nPattern]; + if (!newPattern) return PATTERNINDEX_INVALID; + const ROWINDEX nOldRowCount = pSndFile->Patterns[nPattern].GetNumRows(); + pSndFile->Patterns[nPattern].SetData(newPattern, nRows); + if(oldPattern) + { + memcpy(newPattern, oldPattern, pSndFile->m_nChannels * nOldRowCount * sizeof(MODCOMMAND)); + CSoundFile::FreePattern(oldPattern); + } + } + pUndoData = pUndo->pbuffer; + pPattern = pSndFile->Patterns[nPattern]; + if (!pSndFile->Patterns[nPattern]) return PATTERNINDEX_INVALID; + pPattern += pUndo->column + (pUndo->row * pSndFile->m_nChannels); + for(UINT iy = 0; iy < pUndo->cy; iy++) + { + memcpy(pPattern, pUndoData, pUndo->cx * sizeof(MODCOMMAND)); + pPattern += pSndFile->m_nChannels; + pUndoData += pUndo->cx; + } + } + + RemoveLastUndoStep(); + + if (CanUndo() == false) m_pModDoc->UpdateAllViews(NULL, HINT_UNDO); + return nPattern; +} + + +// Check if an undo buffer actually exists. +bool CPatternUndo::CanUndo() +//-------------------------- +{ + return (UndoBuffer.size() > 0) ? true : false; +} + + +// Delete a given undo step. +void CPatternUndo::DeleteUndoStep(UINT nStep) +//------------------------------------------- +{ + if(nStep >= UndoBuffer.size()) return; + if (UndoBuffer[nStep].pbuffer) delete[] UndoBuffer[nStep].pbuffer; + UndoBuffer.erase(UndoBuffer.begin() + nStep); +} + + +// Public helper function to remove the most recent undo point. +void CPatternUndo::RemoveLastUndoStep() +//------------------------------------- +{ + if(CanUndo() == false) return; + DeleteUndoStep(UndoBuffer.size() - 1); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// Sample Undo Functions + + +// Remove all undo steps for all samples. +void CSampleUndo::ClearUndo() +//--------------------------- +{ + for(SAMPLEINDEX nSmp = 1; nSmp <= MAX_SAMPLES; nSmp++) + { + ClearUndo(nSmp); + } + UndoBuffer.clear(); +} + + +// Remove all undo steps of a given sample. +void CSampleUndo::ClearUndo(const SAMPLEINDEX nSmp) +//------------------------------------------------- +{ + if(!SampleBufferExists(nSmp, false)) return; + + while(UndoBuffer[nSmp - 1].size() > 0) + { + DeleteUndoStep(nSmp, 0); + } +} + + +// Create undo point for given sample. +// The main program has to tell what kind of changes are going to be made to the sample. +// That way, a lot of RAM can be saved, because some actions don't even require an undo sample buffer. +bool CSampleUndo::PrepareUndo(const SAMPLEINDEX nSmp, sampleUndoTypes nChangeType, UINT nChangeStart, UINT nChangeEnd) +//-------------------------------------------------------------------------------------------------------------------- +{ + if(m_pModDoc == nullptr || !SampleBufferExists(nSmp)) return false; + + CSoundFile *pSndFile = m_pModDoc->GetSoundFile(); + if(pSndFile == nullptr) return false; + + // Remove an undo step if there are too many. + while(UndoBuffer[nSmp - 1].size() >= MAX_UNDO_LEVEL) + { + DeleteUndoStep(nSmp, 0); + } + + // Restrict amount of memory that's being used + RestrictBufferSize(); + + // Create new undo slot + SAMPLEUNDOBUFFER sUndo; + + // Save old sample header + memcpy(&sUndo.OldSample, &pSndFile->Samples[nSmp], sizeof(MODSAMPLE)); + sUndo.nChangeType = nChangeType; + + if(nChangeType == sundo_replace) + { + // ensure that size information is correct here. + nChangeStart = 0; + nChangeEnd = pSndFile->Samples[nSmp].nLength; + } else if(nChangeType == sundo_none) + { + // we do nothing... + nChangeStart = nChangeEnd = 0; + } + + sUndo.nChangeStart = nChangeStart; + sUndo.nChangeEnd = nChangeEnd; + sUndo.SamplePtr = nullptr; + + switch(nChangeType) + { + case sundo_none: // we are done, no sample changes here. + case sundo_invert: // no action necessary, since those effects can be applied again to be undone. + case sundo_reverse: // dito + case sundo_unsign: // dito + case sundo_insert: // no action necessary, we already have stored the variables that are necessary. + break; + + case sundo_update: + case sundo_delete: + case sundo_replace: + { + UINT nBytesPerSample = pSndFile->Samples[nSmp].GetBytesPerSample(); + UINT nChangeLen = nChangeEnd - nChangeStart; + + sUndo.SamplePtr = pSndFile->AllocateSample(nChangeLen * nBytesPerSample + 4 * nBytesPerSample); + if(sUndo.SamplePtr == nullptr) return false; + memcpy(sUndo.SamplePtr, pSndFile->Samples[nSmp].pSample + nChangeStart * nBytesPerSample, nChangeLen * nBytesPerSample); + +#ifdef DEBUG + char s[64]; + const UINT nSize = (GetUndoBufferCapacity() + nChangeLen * nBytesPerSample) >> 10; + wsprintf(s, "Sample undo buffer size is now %u.%u MB\n", nSize >> 10, (nSize & 1023) * 100 / 1024); + Log(s); +#endif + + } + break; + + default: + ASSERT(false); // whoops, what's this? someone forgot to implement it, some code is obviously missing here! + return false; + } + + UndoBuffer[nSmp - 1].push_back(sUndo); + + m_pModDoc->UpdateAllViews(NULL, HINT_UNDO); + + return true; +} + + +// Restore undo point for given sample +bool CSampleUndo::Undo(const SAMPLEINDEX nSmp) +//-------------------------------------------- +{ + if(m_pModDoc == nullptr || CanUndo(nSmp) == false) return false; + + CSoundFile *pSndFile = m_pModDoc->GetSoundFile(); + if(pSndFile == nullptr) return false; + + // Select most recent undo slot + SAMPLEUNDOBUFFER *pUndo = &UndoBuffer[nSmp - 1].back(); + + LPSTR pCurrentSample = pSndFile->Samples[nSmp].pSample; + LPSTR pNewSample = nullptr; // a new sample is possibly going to be allocated, depending on what's going to be undone. + + UINT nBytesPerSample = pUndo->OldSample.GetBytesPerSample(); + UINT nChangeLen = pUndo->nChangeEnd - pUndo->nChangeStart; + + switch(pUndo->nChangeType) + { + case sundo_none: + break; + + case sundo_invert: + // invert again + ctrlSmp::InvertSample(&pSndFile->Samples[nSmp], pUndo->nChangeStart, pUndo->nChangeEnd, pSndFile); + break; + + case sundo_reverse: + // reverse again + ctrlSmp::ReverseSample(&pSndFile->Samples[nSmp], pUndo->nChangeStart, pUndo->nChangeEnd, pSndFile); + break; + + case sundo_unsign: + // unsign again + ctrlSmp::UnsignSample(&pSndFile->Samples[nSmp], pUndo->nChangeStart, pUndo->nChangeEnd, pSndFile); + break; + + case sundo_insert: + // delete inserted data + ASSERT(nChangeLen == pSndFile->Samples[nSmp].nLength - pUndo->OldSample.nLength); + memcpy(pCurrentSample + pUndo->nChangeStart * nBytesPerSample, pCurrentSample + pUndo->nChangeEnd * nBytesPerSample, (pSndFile->Samples[nSmp].nLength - pUndo->nChangeEnd) * nBytesPerSample); + // also clean the sample end + memset(pCurrentSample + pUndo->OldSample.nLength * nBytesPerSample, 0, (pSndFile->Samples[nSmp].nLength - pUndo->OldSample.nLength) * nBytesPerSample); + break; + + case sundo_update: + // simply replace what has been updated. + if(pSndFile->Samples[nSmp].nLength < pUndo->nChangeEnd) return false; + memcpy(pCurrentSample + pUndo->nChangeStart * nBytesPerSample, pUndo->SamplePtr, nChangeLen * nBytesPerSample); + break; + + case sundo_delete: + // insert deleted data + pNewSample = pSndFile->AllocateSamp... [truncated message content] |