From: <rel...@us...> - 2009-07-05 21:02:00
|
Revision: 280 http://modplug.svn.sourceforge.net/modplug/?rev=280&view=rev Author: relabsoluness Date: 2009-07-05 21:01:58 +0000 (Sun, 05 Jul 2009) Log Message: ----------- (patch from Jojo, merged somewhat modified) [New] Sample tab: DC offset removal. (merge edits: refactoring: moved implementation to modsmp_ctrl and added some helper functions). Modified Paths: -------------- trunk/OpenMPT/mptrack/CommandSet.cpp trunk/OpenMPT/mptrack/CommandSet.h trunk/OpenMPT/mptrack/Ctrl_smp.cpp trunk/OpenMPT/mptrack/Ctrl_smp.h trunk/OpenMPT/mptrack/View_smp.cpp trunk/OpenMPT/mptrack/mptrack.rc trunk/OpenMPT/mptrack/res/patterns.bmp trunk/OpenMPT/mptrack/resource.h trunk/OpenMPT/soundlib/modsmp_ctrl.cpp trunk/OpenMPT/soundlib/modsmp_ctrl.h Modified: trunk/OpenMPT/mptrack/CommandSet.cpp =================================================================== --- trunk/OpenMPT/mptrack/CommandSet.cpp 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/mptrack/CommandSet.cpp 2009-07-05 21:01:58 UTC (rev 280) @@ -2328,6 +2328,11 @@ commands[kcNotePCS].isDummy = false; commands[kcNotePCS].Message = "Parameter control(smooth)(MPTm only)"; + commands[kcSampleRemoveDCOffset].UID = 1790; + commands[kcSampleRemoveDCOffset].Message = "Remove DC Offset"; + commands[kcSampleRemoveDCOffset].isHidden = false; + commands[kcSampleRemoveDCOffset].isDummy = false; + #ifdef _DEBUG for (int i=0; i<kcNumCommands; i++) { if (commands[i].UID != 0) { // ignore unset UIDs Modified: trunk/OpenMPT/mptrack/CommandSet.h =================================================================== --- trunk/OpenMPT/mptrack/CommandSet.h 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/mptrack/CommandSet.h 2009-07-05 21:01:58 UTC (rev 280) @@ -572,7 +572,8 @@ kcSampleZoomDown, kcSampleInvert, kcSampleSignUnsign, - kcEndSampleEditing=kcSampleSignUnsign, + kcSampleRemoveDCOffset, + kcEndSampleEditing=kcSampleRemoveDCOffset, //kcSampStartNotes to kcInsNoteMapEndNoteStops must be contiguous. kcSampStartNotes, Modified: trunk/OpenMPT/mptrack/Ctrl_smp.cpp =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_smp.cpp 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/mptrack/Ctrl_smp.cpp 2009-07-05 21:01:58 UTC (rev 280) @@ -14,6 +14,7 @@ #pragma warning(disable:4244) //"conversion from 'type1' to 'type2', possible loss of data" #include "smbPitchShift.cpp" #pragma warning(default:4244) //"conversion from 'type1' to 'type2', possible loss of data" +#include "modsmp_ctrl.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -67,6 +68,7 @@ ON_COMMAND(IDC_SAMPLE_SILENCE, OnSilence) ON_COMMAND(IDC_SAMPLE_INVERT, OnInvert) ON_COMMAND(IDC_SAMPLE_SIGN_UNSIGN, OnSignUnSign) + ON_COMMAND(IDC_SAMPLE_DCOFFSET, OnRemoveDCOffset) ON_COMMAND(IDC_CHECK1, OnSetPanningChanged) ON_COMMAND(ID_PREVINSTRUMENT, OnPrevInstrument) ON_COMMAND(ID_NEXTINSTRUMENT, OnNextInstrument) @@ -207,6 +209,7 @@ m_ToolBar2.AddButton(IDC_SAMPLE_PLAY, 14); m_ToolBar2.AddButton(IDC_SAMPLE_NORMALIZE, 8); m_ToolBar2.AddButton(IDC_SAMPLE_AMPLIFY, 9); + m_ToolBar2.AddButton(IDC_SAMPLE_DCOFFSET, 37); m_ToolBar2.AddButton(IDC_SAMPLE_UPSAMPLE, 10); m_ToolBar2.AddButton(IDC_SAMPLE_DOWNSAMPLE, 29); m_ToolBar2.AddButton(IDC_SAMPLE_REVERSE, 11); @@ -469,6 +472,10 @@ OnSignUnSign(); break; + case IDC_SAMPLE_DCOFFSET: + OnRemoveDCOffset(); + break; + case IDC_SAMPLE_NORMALIZE: OnNormalize(); break; @@ -1046,8 +1053,8 @@ //Shift -> Normalize all samples if(CMainFrame::GetInputHandler()->ShiftPressed()) { - int ans = MessageBox(GetStrI18N(TEXT("This will normalize all samples independently. Continue?")), GetStrI18N(TEXT("Normalize")), MB_YESNO | MB_ICONQUESTION); - if(ans == IDNO) return; + if(MessageBox(GetStrI18N(TEXT("This will normalize all samples independently. Continue?")), GetStrI18N(TEXT("Normalize")), MB_YESNO | MB_ICONQUESTION) == IDNO) + return; iMinSample = 1; iMaxSample = m_pSndFile->m_nSamples; } else { @@ -1203,6 +1210,89 @@ } +void CCtrlSamples::OnRemoveDCOffset() +//----------------------------------- +{ + 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()) + { + if(MessageBox(GetStrI18N(TEXT("This will process all samples independently. Continue?")), GetStrI18N(TEXT("DC Offset Removal")), MB_YESNO | MB_ICONQUESTION) == IDNO) + return; + iMinSample = 1; + iMaxSample = m_pSndFile->m_nSamples; + } + + BeginWaitCursor(); + + // for report / SetModified + UINT iModified = 0; + float fReportOffset = 0; + + for(UINT iSmp = iMinSample; iSmp <= iMaxSample; iSmp++) + { + UINT iStart, iEnd; + + if( m_pSndFile->Ins[iSmp].pSample == nullptr ) + continue; + + if (iMinSample != iMaxSample) + { + iStart = 0; + iEnd = m_pSndFile->Ins[iSmp].nLength; + } + else + { + iStart = viewstate.dwBeginSel; + iEnd = viewstate.dwEndSel; + } + const float fOffset = ctrlSmp::RemoveDCOffset(m_pSndFile->Ins[iSmp], iStart, iEnd, m_pSndFile->GetType(), m_pSndFile); + + if(fOffset == 0.0f) // No offset removed. + continue; + + fReportOffset += fOffset; + iModified++; + m_pModDoc->UpdateAllViews(NULL, (iSmp << HINT_SHIFT_SMP) | HINT_SAMPLEDATA | HINT_SAMPLEINFO, NULL); + } + + EndWaitCursor(); + SwitchToView(); + + // fill the statusbar with some nice information + + CString dcInfo; + if(iModified) + { + m_pModDoc->SetModified(); + if(iModified == 1) + { + dcInfo.Format(GetStrI18N(TEXT("Removed DC offset (%.1f%%)")), fReportOffset * 100); + } + else + { + dcInfo.Format(GetStrI18N(TEXT("Removed DC offset from %d samples (avg %0.1f%%)")), iModified, fReportOffset / iModified * 100); + } + } + else + { + dcInfo.SetString(GetStrI18N(TEXT("No DC offset found"))); + } + + CMainFrame *pMainFrm = CMainFrame::GetMainFrame(); + pMainFrm->SetXInfoText(dcInfo); + +} + + void CCtrlSamples::OnAmplify() //---------------------------- { Modified: trunk/OpenMPT/mptrack/Ctrl_smp.h =================================================================== --- trunk/OpenMPT/mptrack/Ctrl_smp.h 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/mptrack/Ctrl_smp.h 2009-07-05 21:01:58 UTC (rev 280) @@ -72,6 +72,7 @@ afx_msg void OnSamplePlay(); afx_msg void OnNormalize(); afx_msg void OnAmplify(); + afx_msg void OnRemoveDCOffset(); afx_msg void OnUpsample(); afx_msg void OnDownsample(); afx_msg void OnReverse(); Modified: trunk/OpenMPT/mptrack/View_smp.cpp =================================================================== --- trunk/OpenMPT/mptrack/View_smp.cpp 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/mptrack/View_smp.cpp 2009-07-05 21:01:58 UTC (rev 280) @@ -2543,12 +2543,13 @@ case kcSampleSave: PostCtrlMessage(IDC_SAMPLE_SAVEAS); return wParam; case kcSampleNew: PostCtrlMessage(IDC_SAMPLE_NEW); return wParam; - case kcSampleReverse: PostCtrlMessage(IDC_SAMPLE_REVERSE); return wParam; - case kcSampleSilence: PostCtrlMessage(IDC_SAMPLE_SILENCE); return wParam; - case kcSampleNormalize: PostCtrlMessage(IDC_SAMPLE_NORMALIZE); return wParam; - case kcSampleAmplify: PostCtrlMessage(IDC_SAMPLE_AMPLIFY); return wParam; - case kcSampleInvert: PostCtrlMessage(IDC_SAMPLE_INVERT); return wParam; - case kcSampleSignUnsign: PostCtrlMessage(IDC_SAMPLE_SIGN_UNSIGN); return wParam; + case kcSampleReverse: PostCtrlMessage(IDC_SAMPLE_REVERSE); return wParam; + case kcSampleSilence: PostCtrlMessage(IDC_SAMPLE_SILENCE); return wParam; + case kcSampleNormalize: PostCtrlMessage(IDC_SAMPLE_NORMALIZE); return wParam; + case kcSampleAmplify: PostCtrlMessage(IDC_SAMPLE_AMPLIFY); return wParam; + case kcSampleInvert: PostCtrlMessage(IDC_SAMPLE_INVERT); return wParam; + case kcSampleSignUnsign: PostCtrlMessage(IDC_SAMPLE_SIGN_UNSIGN); return wParam; + case kcSampleRemoveDCOffset: PostCtrlMessage(IDC_SAMPLE_DCOFFSET); return wParam; case kcNoteOff: PlayNote(NOTE_KEYOFF); return wParam; case kcNoteCut: PlayNote(NOTE_NOTECUT); return wParam; Modified: trunk/OpenMPT/mptrack/mptrack.rc =================================================================== --- trunk/OpenMPT/mptrack/mptrack.rc 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/mptrack/mptrack.rc 2009-07-05 21:01:58 UTC (rev 280) @@ -762,7 +762,7 @@ CTEXT "Length: 000000 (16-bit)",IDC_TEXT5,175,6,90,13, SS_CENTERIMAGE,WS_EX_STATICEDGE CONTROL "Toolbar2",IDC_TOOLBAR2,"ToolbarWindow32",WS_GROUP | - 0x4d,268,4,164,17 + 0x4d,268,4,195,17 GROUPBOX "",IDC_STATIC,3,22,94,78 LTEXT "Default Volume",IDC_STATIC,8,32,49,8 LTEXT "Global Volume",IDC_STATIC,8,45,46,8 @@ -2487,6 +2487,11 @@ STRINGTABLE BEGIN + IDC_SAMPLE_DCOFFSET "Remove DC Offset\nRemove DC Offset and normalize (hold shift to process all samples)" +END + +STRINGTABLE +BEGIN ID_ENVELOPE_SETLOOP "Enable or disable the envelope loop" ID_ENVELOPE_SUSTAIN "Enable or disable the envelope sustain" ID_ENVELOPE_INSERTPOINT "Insert a new point in the envelope at the current location" @@ -2499,6 +2504,11 @@ ID_PATTERN_PLAYROW "Play current row\nPlay Row" ID_IMPORT_MIDILIB "Defines the default MIDI library used when importing MIDI files" ID_CLEANUP_REARRANGE "Rearrange all patterns so that they are sorted in the order list\nRearrange Patterns" +END + +STRINGTABLE +BEGIN + ID_EDIT_GOTO_MENU "Go to row / channel / pattern / order" ID_CLEANUP_COMPO "Reset attributes to defaults (useful for creating sample packs)\nCompo Cleanup" END @@ -2585,11 +2595,6 @@ IDS_OPERATION_FAIL "Operation failed." END -STRINGTABLE -BEGIN - ID_EDIT_GOTO_MENU "Go to row / channel / pattern / order" -END - #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// Modified: trunk/OpenMPT/mptrack/res/patterns.bmp =================================================================== (Binary files differ) Modified: trunk/OpenMPT/mptrack/resource.h =================================================================== --- trunk/OpenMPT/mptrack/resource.h 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/mptrack/resource.h 2009-07-05 21:01:58 UTC (rev 280) @@ -829,6 +829,7 @@ #define IDC_MIDIPLAYPATTERNONMIDIIN 2340 #define IDC_DONTSHOWAGAIN 2341 #define IDC_MESSAGETEXT 2342 +#define IDC_SAMPLE_DCOFFSET 2343 #define ID_FILE_NEWMOD 32771 #define ID_FILE_NEWXM 32772 #define ID_FILE_NEWS3M 32773 @@ -1066,7 +1067,7 @@ #define _APS_3D_CONTROLS 1 #define _APS_NEXT_RESOURCE_VALUE 518 #define _APS_NEXT_COMMAND_VALUE 59226 -#define _APS_NEXT_CONTROL_VALUE 2343 +#define _APS_NEXT_CONTROL_VALUE 2344 #define _APS_NEXT_SYMED_VALUE 901 #endif #endif Modified: trunk/OpenMPT/soundlib/modsmp_ctrl.cpp =================================================================== --- trunk/OpenMPT/soundlib/modsmp_ctrl.cpp 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/soundlib/modsmp_ctrl.cpp 2009-07-05 21:01:58 UTC (rev 280) @@ -170,4 +170,125 @@ } +namespace +{ + struct OffsetData + { + double dMax, dMin, dOffset; + }; + + // Returns maximum sample amplitude for given sample type (int8/int16). + template <class T> + double GetMaxAmplitude() {return 1.0 + (std::numeric_limits<T>::max)();} + + // Calculates DC offset and returns struct with DC offset, max and min values. + // DC offset value is average of [-1.0, 1.0[-normalized offset values. + template<class T> + OffsetData CalculateOffset(const T* pStart, const SmpLength nLength) + //------------------------------------------------------------------ + { + OffsetData offsetVals = {0,0,0}; + + if(nLength < 1) + return offsetVals; + + const double dMaxAmplitude = GetMaxAmplitude<T>(); + + double dMax = -1, dMin = 1, dSum = 0; + + const T* p = pStart; + for (SmpLength i = 0; i < nLength; i++, p++) + { + const double dVal = double(*p) / dMaxAmplitude; + dSum += dVal; + if(dVal > dMax) dMax = dVal; + if(dVal < dMin) dMin = dVal; + } + + offsetVals.dMax = dMax; + offsetVals.dMin = dMin; + offsetVals.dOffset = (-dSum / (double)(nLength)); + return offsetVals; + } + + template <class T> + void RemoveOffsetAndNormalize(T* pStart, const SmpLength nLength, const double dOffset, const double dAmplify) + //------------------------------------------------------------------------------------------------------------ + { + T* p = pStart; + for (UINT i = 0; i < nLength; i++, p++) + { + double dVal = (*p) * dAmplify + dOffset; + Limit(dVal, (std::numeric_limits<T>::min)(), (std::numeric_limits<T>::max)()); + *p = static_cast<T>(dVal); + } + } +}; + +// Remove DC offset +float RemoveDCOffset(MODINSTRUMENT& smp, + SmpLength iStart, + SmpLength iEnd, + const MODTYPE modtype, + CSoundFile* const pSndFile) +//---------------------------------------------- +{ + if(smp.pSample == nullptr || smp.nLength < 1) + return 0; + + MODINSTRUMENT* const pins = &smp; + + if (iEnd > pins->nLength) iEnd = pins->nLength; + if (iStart > iEnd) iStart = iEnd; + if (iStart == iEnd) + { + iStart = 0; + iEnd = pins->nLength; + } + + iStart *= pins->GetNumChannels(); + iEnd *= pins->GetNumChannels(); + + const double dMaxAmplitude = (pins->GetElementarySampleSize() == 2) ? GetMaxAmplitude<int16>() : GetMaxAmplitude<int8>(); + + // step 1: Calculate offset. + OffsetData oData = {0,0,0}; + if(pins->GetElementarySampleSize() == 2) + oData = CalculateOffset(reinterpret_cast<int16*>(pins->pSample) + iStart, iEnd - iStart); + else if(pins->GetElementarySampleSize() == 1) + oData = CalculateOffset(reinterpret_cast<int8*>(pins->pSample) + iStart, iEnd - iStart); + + double dMin = oData.dMin, dMax = oData.dMax, dOffset = oData.dOffset; + + const float fReportOffset = (float)dOffset; + + if((int)(dOffset * dMaxAmplitude) == 0) + return 0; + + // those will be changed... + dMax += dOffset; + dMin += dOffset; + + // ... and that might cause distortion, so we will normalize this. + const double dAmplify = 1 / max(dMax, -dMin); + + // step 2: centralize + normalize sample + dOffset *= dMaxAmplitude * dAmplify; + if(pins->GetElementarySampleSize() == 2) + RemoveOffsetAndNormalize( reinterpret_cast<int16*>(pins->pSample) + iStart, iEnd - iStart, dOffset, dAmplify); + else if(pins->GetElementarySampleSize() == 1) + RemoveOffsetAndNormalize( reinterpret_cast<int8*>(pins->pSample) + iStart, iEnd - iStart, dOffset, dAmplify); + + // step 3: adjust either global vol or default vol of this sample + if(modtype == MOD_TYPE_IT || modtype == MOD_TYPE_MPT) + pins->nGlobalVol = min((WORD)(pins->nGlobalVol / dAmplify), 64); + else if(modtype != MOD_TYPE_NONE) + pins->nVolume = min((WORD)(pins->nVolume / dAmplify), 256); + + AdjustEndOfSample(smp, pSndFile); + + return fReportOffset; +} + + } // namespace ctrlSmp Modified: trunk/OpenMPT/soundlib/modsmp_ctrl.h =================================================================== --- trunk/OpenMPT/soundlib/modsmp_ctrl.h 2009-07-01 20:02:20 UTC (rev 279) +++ trunk/OpenMPT/soundlib/modsmp_ctrl.h 2009-07-05 21:01:58 UTC (rev 280) @@ -35,6 +35,16 @@ // Resets samples. void ResetSamples(CSoundFile& rSndFile, ResetFlag resetflag); -} +// Remove DC offset and normalize. +// Return: If DC offset was removed, returns original offset value, zero otherwise. +float RemoveDCOffset(MODINSTRUMENT& smp, + SmpLength iStart, // Start position (for partial DC offset removal). + SmpLength iEnd, // End position (for partial DC offset removal). + const MODTYPE modtype, // Used to determine whether to adjust global or default volume + // to keep volume level the same given the normalization. + // Volume adjustment is not done if this param is MOD_TYPE_NONE. + CSoundFile* const pSndFile); // Passed to AdjustEndOfSample. +} // Namespace ctrlSmp + #endif This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |