/* * SampleEditorDialogs.cpp * ----------------------- * Purpose: Code for various dialogs that are used in the sample editor. * 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 "resource.h" #include "Reporting.h" #include "MPTrackUtil.h" #include "Mptrack.h" #include "../common/misc_util.h" #include "../soundlib/Snd_defs.h" #include "../soundlib/ModSample.h" #include "SampleEditorDialogs.h" #include "ProgressDialog.h" OPENMPT_NAMESPACE_BEGIN ////////////////////////////////////////////////////////////////////////// // Sample amplification dialog BEGIN_MESSAGE_MAP(CAmpDlg, CDialog) ON_WM_DESTROY() ON_EN_CHANGE(IDC_EDIT2, &CAmpDlg::EnableFadeIn) ON_EN_CHANGE(IDC_EDIT3, &CAmpDlg::EnableFadeOut) END_MESSAGE_MAP() void CAmpDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAmpDlg) DDX_Control(pDX, IDC_COMBO1, m_fadeBox); //}}AFX_DATA_MAP } CAmpDlg::CAmpDlg(CWnd *parent, AmpSettings &settings, int16 factorMin, int16 factorMax) : CDialog(IDD_SAMPLE_AMPLIFY, parent) , m_settings(settings) , m_nFactorMin(factorMin) , m_nFactorMax(factorMax) {} BOOL CAmpDlg::OnInitDialog() { CDialog::OnInitDialog(); CSpinButtonCtrl *spin = (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN1); spin->SetRange32(m_nFactorMin, m_nFactorMax); spin->SetPos32(m_settings.factor); spin = (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN2); spin->SetRange32(0, 100); spin->SetPos32(m_settings.fadeInStart); spin = (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN3); spin->SetRange32(0, 100); spin->SetPos32(m_settings.fadeOutEnd); SetDlgItemInt(IDC_EDIT1, m_settings.factor); SetDlgItemInt(IDC_EDIT2, m_settings.fadeInStart); SetDlgItemInt(IDC_EDIT3, m_settings.fadeOutEnd); m_edit.SubclassDlgItem(IDC_EDIT1, this); m_edit.AllowFractions(false); m_edit.AllowNegative(m_nFactorMin < 0); m_editFadeIn.SubclassDlgItem(IDC_EDIT2, this); m_editFadeIn.AllowFractions(false); m_editFadeIn.AllowNegative(m_nFactorMin < 0); m_editFadeOut.SubclassDlgItem(IDC_EDIT3, this); m_editFadeOut.AllowFractions(false); m_editFadeOut.AllowNegative(m_nFactorMin < 0); const struct { const TCHAR *name; Fade::Law id; } fadeLaws[] = { { _T("Linear"), Fade::kLinear }, { _T("Exponential"), Fade::kPow }, { _T("Square Root"), Fade::kSqrt }, { _T("Logarithmic"), Fade::kLog }, { _T("Quarter Sine"), Fade::kQuarterSine }, { _T("Half Sine"), Fade::kHalfSine }, }; // Create icons for fade laws const int cx = Util::ScalePixels(16, m_hWnd); const int cy = Util::ScalePixels(16, m_hWnd); const int imgWidth = cx * static_cast(std::size(fadeLaws)); m_list.Create(cx, cy, ILC_COLOR32 | ILC_MASK, 0, 1); std::vector bits(imgWidth * cy, RGB(255, 0, 255)); const COLORREF col = GetSysColor(COLOR_WINDOWTEXT); for(int i = 0, baseX = 0; i < static_cast(std::size(fadeLaws)); i++, baseX += cx) { Fade::Func fadeFunc = Fade::GetFadeFunc(fadeLaws[i].id); int oldVal = cy - 1; for(int x = 0; x < cx; x++) { int val = cy - 1 - mpt::saturate_round(cy * fadeFunc(static_cast(x) / cx)); Limit(val, 0, cy - 1); if(oldVal > val && x > 0) { int dy = (oldVal - val) / 2; for(int y = oldVal * imgWidth; dy != 0; y -= imgWidth, dy--) { bits[baseX + (x - 1) + y] = col; } oldVal -= dy + 1; } for(int y = oldVal * imgWidth; y >= val * imgWidth; y -= imgWidth) { bits[baseX + x + y] = col; } oldVal = val; } } CBitmap bitmap; bitmap.CreateBitmap(cx * static_cast(std::size(fadeLaws)), cy, 1, 32, bits.data()); m_list.Add(&bitmap, RGB(255, 0, 255)); bitmap.DeleteObject(); m_fadeBox.SetImageList(&m_list); // Add fade laws to list COMBOBOXEXITEM cbi; MemsetZero(cbi); cbi.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM; for(int i = 0; i < static_cast(std::size(fadeLaws)); i++) { cbi.iItem = i; cbi.pszText = const_cast(fadeLaws[i].name); cbi.iImage = cbi.iSelectedImage = i; cbi.lParam = fadeLaws[i].id; m_fadeBox.InsertItem(&cbi); if(fadeLaws[i].id == m_settings.fadeLaw) m_fadeBox.SetCurSel(i); } m_locked = false; return TRUE; } void CAmpDlg::OnDestroy() { m_list.DeleteImageList(); } void CAmpDlg::OnOK() { m_settings.factor = static_cast(Clamp(static_cast(GetDlgItemInt(IDC_EDIT1)), m_nFactorMin, m_nFactorMax)); m_settings.fadeInStart = Clamp(static_cast(GetDlgItemInt(IDC_EDIT2)), m_nFactorMin, m_nFactorMax); m_settings.fadeOutEnd = Clamp(static_cast(GetDlgItemInt(IDC_EDIT3)), m_nFactorMin, m_nFactorMax); m_settings.fadeIn = (IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED); m_settings.fadeOut = (IsDlgButtonChecked(IDC_CHECK2) != BST_UNCHECKED); m_settings.fadeLaw = static_cast(m_fadeBox.GetItemData(m_fadeBox.GetCurSel())); CDialog::OnOK(); } ////////////////////////////////////////////////////////////// // Sample import dialog SampleIO CRawSampleDlg::m_format(SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM); SmpLength CRawSampleDlg::m_offset = 0; BEGIN_MESSAGE_MAP(CRawSampleDlg, CDialog) ON_COMMAND_RANGE(IDC_RADIO1, IDC_RADIO4, &CRawSampleDlg::OnBitDepthChanged) ON_COMMAND_RANGE(IDC_RADIO7, IDC_RADIO10, &CRawSampleDlg::OnEncodingChanged) ON_COMMAND(IDC_BUTTON1, &CRawSampleDlg::OnAutodetectFormat) END_MESSAGE_MAP() void CRawSampleDlg::DoDataExchange(CDataExchange *pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CRawSampleDlg) DDX_Control(pDX, IDC_SPIN1, m_SpinOffset); //}}AFX_DATA_MAP } BOOL CRawSampleDlg::OnInitDialog() { CDialog::OnInitDialog(); if(const auto filename = m_file.GetOptionalFileName(); filename) { CString title; GetWindowText(title); title += _T(" - ") + filename->GetFullFileName().ToCString(); SetWindowText(title); } m_SpinOffset.SetRange32(0, mpt::saturate_cast(m_file.GetLength() - 1u)); UpdateDialog(); return TRUE; } void CRawSampleDlg::OnOK() { const int bitDepth = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO4); const int channels = GetCheckedRadioButton(IDC_RADIO5, IDC_RADIO6); const int encoding = GetCheckedRadioButton(IDC_RADIO7, IDC_RADIO10); const int endianness = GetCheckedRadioButton(IDC_RADIO11, IDC_RADIO12); if(bitDepth == IDC_RADIO1) m_format |= SampleIO::_8bit; else if(bitDepth == IDC_RADIO2) m_format |= SampleIO::_16bit; else if(bitDepth == IDC_RADIO3) m_format |= SampleIO::_24bit; else if(bitDepth == IDC_RADIO4) m_format |= SampleIO::_32bit; if(channels == IDC_RADIO5) m_format |= SampleIO::mono; else if(channels == IDC_RADIO6) m_format |= SampleIO::stereoInterleaved; if(encoding == IDC_RADIO7) m_format |= SampleIO::signedPCM; else if(encoding == IDC_RADIO8) m_format |= SampleIO::unsignedPCM; else if(encoding == IDC_RADIO9) m_format |= SampleIO::deltaPCM; else if(encoding == IDC_RADIO10) m_format |= SampleIO::floatPCM; if(endianness == IDC_RADIO11) m_format |= SampleIO::littleEndian; else if(endianness == IDC_RADIO12) m_format |= SampleIO::bigEndian; m_rememberFormat = IsDlgButtonChecked(IDC_CHK_REMEMBERSETTINGS) != BST_UNCHECKED; m_offset = GetDlgItemInt(IDC_EDIT1, nullptr, FALSE); CDialog::OnOK(); } void CRawSampleDlg::UpdateDialog() { const int bitDepthID = IDC_RADIO1 + m_format.GetBitDepth() / 8 - 1; CheckRadioButton(IDC_RADIO1, IDC_RADIO4, bitDepthID); CheckRadioButton(IDC_RADIO5, IDC_RADIO6, (m_format.GetChannelFormat() == SampleIO::mono) ? IDC_RADIO5 : IDC_RADIO6); int encodingID = IDC_RADIO7; switch(m_format.GetEncoding()) { case SampleIO::signedPCM: encodingID = IDC_RADIO7; break; case SampleIO::unsignedPCM: encodingID = IDC_RADIO8; break; case SampleIO::deltaPCM: encodingID = IDC_RADIO9; break; case SampleIO::floatPCM: encodingID = IDC_RADIO10; break; default: MPT_ASSERT_NOTREACHED(); } CheckRadioButton(IDC_RADIO7, IDC_RADIO10, encodingID); CheckRadioButton(IDC_RADIO11, IDC_RADIO12, (m_format.GetEndianness() == SampleIO::littleEndian) ? IDC_RADIO11 : IDC_RADIO12); CheckDlgButton(IDC_CHK_REMEMBERSETTINGS, (m_rememberFormat ? BST_CHECKED : BST_UNCHECKED)); SetDlgItemInt(IDC_EDIT1, m_offset, FALSE); OnBitDepthChanged(bitDepthID); OnEncodingChanged(encodingID); } void CRawSampleDlg::OnBitDepthChanged(UINT id) { const auto bits = (id - IDC_RADIO1 + 1) * 8; // 8-bit: endianness doesn't matter BOOL enableEndianness = (bits == 8) ? FALSE : TRUE; GetDlgItem(IDC_RADIO11)->EnableWindow(enableEndianness); GetDlgItem(IDC_RADIO12)->EnableWindow(enableEndianness); if(bits == 8) CheckRadioButton(IDC_RADIO11, IDC_RADIO12, IDC_RADIO11); const BOOL hasUnsignedDelta = (bits <= 16) ? TRUE : FALSE; const BOOL hasFloat = (bits == 32) ? TRUE : FALSE; GetDlgItem(IDC_RADIO8)->EnableWindow(hasUnsignedDelta); GetDlgItem(IDC_RADIO9)->EnableWindow(hasUnsignedDelta); GetDlgItem(IDC_RADIO10)->EnableWindow(hasFloat); const int encoding = GetCheckedRadioButton(IDC_RADIO7, IDC_RADIO10); if((encoding == IDC_RADIO8 && !hasUnsignedDelta) || (encoding == IDC_RADIO9 && !hasUnsignedDelta) || (encoding == IDC_RADIO10 && !hasFloat)) CheckRadioButton(IDC_RADIO7, IDC_RADIO10, IDC_RADIO7); } void CRawSampleDlg::OnEncodingChanged(UINT id) { const bool isUnsignedDelta = (id == IDC_RADIO8) || (id == IDC_RADIO9); const bool isFloat = (id == IDC_RADIO10); GetDlgItem(IDC_RADIO1)->EnableWindow(isFloat ? FALSE : TRUE); GetDlgItem(IDC_RADIO2)->EnableWindow(isFloat ? FALSE : TRUE); GetDlgItem(IDC_RADIO3)->EnableWindow((isFloat || isUnsignedDelta) ? FALSE : TRUE); GetDlgItem(IDC_RADIO4)->EnableWindow(isUnsignedDelta ? FALSE : TRUE); const int bitDepth = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO4); if(bitDepth != IDC_RADIO4 && isFloat) CheckRadioButton(IDC_RADIO1, IDC_RADIO4, IDC_RADIO4); if((bitDepth == IDC_RADIO3 || bitDepth == IDC_RADIO4) && isUnsignedDelta) CheckRadioButton(IDC_RADIO1, IDC_RADIO4, IDC_RADIO1); } class AutodetectFormatDlg : public CProgressDialog { CRawSampleDlg &m_parent; public: SampleIO m_bestFormat; AutodetectFormatDlg(CRawSampleDlg &parent) : CProgressDialog(&parent) , m_parent(parent) {} void Run() override { // Probed raw formats... little-endian and stereo versions are automatically checked as well. static constexpr SampleIO ProbeFormats[] = { // 8-Bit {SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM}, {SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::unsignedPCM}, {SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::deltaPCM}, // 16-Bit {SampleIO::_16bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM}, {SampleIO::_16bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::unsignedPCM}, {SampleIO::_16bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::deltaPCM}, // 24-Bit {SampleIO::_24bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM}, // 32-Bit {SampleIO::_32bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM}, {SampleIO::_32bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::floatPCM}, }; SetTitle(_T("Raw Import")); SetText(_T("Determining raw format...")); SetRange(0, std::size(ProbeFormats) * 4); double bestError = DBL_MAX; uint64 progress = 0; for(SampleIO format : ProbeFormats) { for(const auto endianness : {SampleIO::littleEndian, SampleIO::bigEndian}) { if(endianness == SampleIO::bigEndian && format.GetBitDepth() == SampleIO::_8bit) continue; format |= endianness; for(const auto channels : {SampleIO::mono, SampleIO::stereoInterleaved}) { format |= channels; ModSample sample; m_parent.m_file.Seek(m_parent.m_offset); const auto bytesPerSample = format.GetNumChannels() * format.GetBitDepth() / 8u; sample.nLength = mpt::saturate_cast(m_parent.m_file.BytesLeft() / bytesPerSample); if(!format.ReadSample(sample, m_parent.m_file)) continue; const uint8 numChannels = sample.GetNumChannels(); double error = 0.0; for(uint8 chn = 0; chn < numChannels; chn++) { const auto ComputeSampleError = [](auto *v, SmpLength length, uint8 numChannels) { const double factor = 1.0 / (1u << (sizeof(*v) * 8u - 1u)); double error = 0.0; int32 prev = 0; for(SmpLength i = length; i != 0; i--, v += numChannels) { auto diff = (*v - prev) * factor; error += diff * diff; prev = *v; } return error; }; if(sample.uFlags[CHN_16BIT]) error += ComputeSampleError(sample.sample16() + chn, sample.nLength, numChannels); else error += ComputeSampleError(sample.sample8() + chn, sample.nLength, numChannels); } sample.FreeSample(); double errorFactor = format.GetBitDepth() * format.GetBitDepth() / 64; // Delta PCM often produces slightly worse error compared to signed PCM for real delta samples, so give it a bit of an advantage. if(format.GetEncoding() == SampleIO::deltaPCM) errorFactor *= 0.75; error *= errorFactor; if(error < bestError) { bestError = error; m_bestFormat = format; } SetProgress(++progress); ProcessMessages(); if(m_abort) { EndDialog(IDCANCEL); return; } } } } EndDialog(IDOK); } }; void CRawSampleDlg::OnAutodetectFormat() { m_offset = GetDlgItemInt(IDC_EDIT1, nullptr, FALSE); AutodetectFormatDlg dlg(*this); if(dlg.DoModal() == IDOK) { m_format = dlg.m_bestFormat; UpdateDialog(); } } ///////////////////////////////////////////////////////////////////////// // Add silence / resize sample dialog BEGIN_MESSAGE_MAP(AddSilenceDlg, CDialog) ON_CBN_SELCHANGE(IDC_COMBO1, &AddSilenceDlg::OnUnitChanged) ON_COMMAND(IDC_RADIO_ADDSILENCE_BEGIN, &AddSilenceDlg::OnEditModeChanged) ON_COMMAND(IDC_RADIO_ADDSILENCE_END, &AddSilenceDlg::OnEditModeChanged) ON_COMMAND(IDC_RADIO_RESIZETO, &AddSilenceDlg::OnEditModeChanged) ON_COMMAND(IDC_RADIO1, &AddSilenceDlg::OnEditModeChanged) END_MESSAGE_MAP() SmpLength AddSilenceDlg::m_addSamples = 32; SmpLength AddSilenceDlg::m_createSamples = 64; AddSilenceDlg::AddSilenceDlg(CWnd *parent, SmpLength origLength, uint32 sampleRate, bool allowOPL) : CDialog(IDD_ADDSILENCE, parent) , m_numSamples(m_addSamples) , m_sampleRate(sampleRate) , m_allowOPL(allowOPL) { if(origLength > 0) { m_length = origLength; m_editOption = kSilenceAtEnd; } else { m_length = m_createSamples; m_editOption = kResize; } } BOOL AddSilenceDlg::OnInitDialog() { CDialog::OnInitDialog(); CSpinButtonCtrl *spin = (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_ADDSILENCE); if(spin) { spin->SetRange32(0, int32_max); spin->SetPos32(m_numSamples); } CComboBox *box = (CComboBox *)GetDlgItem(IDC_COMBO1); if(box) { box->AddString(_T("samples")); box->AddString(_T("ms")); box->SetCurSel(m_unit); if(m_sampleRate == 0) { // Can't do any conversions if samplerate is unknown box->EnableWindow(FALSE); } } int buttonID = IDC_RADIO_ADDSILENCE_END; switch(m_editOption) { case kSilenceAtBeginning: buttonID = IDC_RADIO_ADDSILENCE_BEGIN; break; case kSilenceAtEnd: buttonID = IDC_RADIO_ADDSILENCE_END; break; case kResize: buttonID = IDC_RADIO_RESIZETO; break; } CheckDlgButton(buttonID, BST_CHECKED); SetDlgItemInt(IDC_EDIT_ADDSILENCE, (m_editOption == kResize) ? m_length : m_numSamples, FALSE); GetDlgItem(IDC_RADIO1)->EnableWindow(m_allowOPL ? TRUE : FALSE); return TRUE; } void AddSilenceDlg::OnOK() { m_numSamples = GetDlgItemInt(IDC_EDIT_ADDSILENCE, nullptr, FALSE); if(m_unit == kMilliseconds) { m_numSamples = Util::muldivr_unsigned(m_numSamples, m_sampleRate, 1000); } switch(m_editOption = GetEditMode()) { case kSilenceAtBeginning: case kSilenceAtEnd: m_addSamples = m_numSamples; break; case kResize: m_createSamples = m_numSamples; break; } CDialog::OnOK(); } void AddSilenceDlg::OnEditModeChanged() { AddSilenceOptions newEditOption = GetEditMode(); GetDlgItem(IDC_EDIT_ADDSILENCE)->EnableWindow((newEditOption == kOPLInstrument) ? FALSE : TRUE); if(newEditOption != kResize && m_editOption == kResize) { // Switch to "add silence" m_length = GetDlgItemInt(IDC_EDIT_ADDSILENCE); SetDlgItemInt(IDC_EDIT_ADDSILENCE, m_numSamples); } else if(newEditOption == kResize && m_editOption != kResize) { // Switch to "resize" m_numSamples = GetDlgItemInt(IDC_EDIT_ADDSILENCE); SetDlgItemInt(IDC_EDIT_ADDSILENCE, m_length); } m_editOption = newEditOption; } void AddSilenceDlg::OnUnitChanged() { const auto unit = static_cast(static_cast(GetDlgItem(IDC_COMBO1))->GetCurSel()); if(m_unit == unit) return; m_unit = unit; SmpLength duration = GetDlgItemInt(IDC_EDIT_ADDSILENCE); if(m_unit == kSamples) { // Convert from milliseconds duration = Util::muldivr_unsigned(duration, m_sampleRate, 1000); } else { // Convert from samples duration = Util::muldivr_unsigned(duration, 1000, m_sampleRate); } SetDlgItemInt(IDC_EDIT_ADDSILENCE, duration); } AddSilenceDlg::AddSilenceOptions AddSilenceDlg::GetEditMode() const { if(IsDlgButtonChecked(IDC_RADIO_ADDSILENCE_BEGIN)) return kSilenceAtBeginning; else if(IsDlgButtonChecked(IDC_RADIO_ADDSILENCE_END)) return kSilenceAtEnd; else if(IsDlgButtonChecked(IDC_RADIO_RESIZETO)) return kResize; else if(IsDlgButtonChecked(IDC_RADIO1)) return kOPLInstrument; MPT_ASSERT_NOTREACHED(); return kSilenceAtEnd; } ///////////////////////////////////////////////////////////////////////// // Sample grid dialog void CSampleGridDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CSampleGridDlg) DDX_Control(pDX, IDC_EDIT1, m_EditSegments); DDX_Control(pDX, IDC_SPIN1, m_SpinSegments); //}}AFX_DATA_MAP } BOOL CSampleGridDlg::OnInitDialog() { CDialog::OnInitDialog(); m_SpinSegments.SetRange32(0, m_nMaxSegments); m_SpinSegments.SetPos(m_nSegments); SetDlgItemInt(IDC_EDIT1, m_nSegments, FALSE); GetDlgItem(IDC_EDIT1)->SetFocus(); return TRUE; } void CSampleGridDlg::OnOK() { m_nSegments = GetDlgItemInt(IDC_EDIT1, NULL, FALSE); CDialog::OnOK(); } ///////////////////////////////////////////////////////////////////////// // Sample cross-fade dialog uint32 CSampleXFadeDlg::m_fadeLength = 20000; uint32 CSampleXFadeDlg::m_fadeLaw = 50000; bool CSampleXFadeDlg::m_afterloopFade = true; bool CSampleXFadeDlg::m_useSustainLoop = false; BEGIN_MESSAGE_MAP(CSampleXFadeDlg, CDialog) ON_WM_HSCROLL() ON_COMMAND(IDC_RADIO1, &CSampleXFadeDlg::OnLoopTypeChanged) ON_COMMAND(IDC_RADIO2, &CSampleXFadeDlg::OnLoopTypeChanged) ON_EN_CHANGE(IDC_EDIT1, &CSampleXFadeDlg::OnFadeLengthChanged) ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CSampleXFadeDlg::OnToolTipText) END_MESSAGE_MAP() void CSampleXFadeDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{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); DDX_Control(pDX, IDC_RADIO1, m_RadioNormalLoop); DDX_Control(pDX, IDC_RADIO2, m_RadioSustainLoop); //}}AFX_DATA_MAP } BOOL CSampleXFadeDlg::OnInitDialog() { CDialog::OnInitDialog(); const bool hasNormal = m_sample.uFlags[CHN_LOOP] && m_sample.nLoopStart > 0; const bool hasSustain = m_sample.uFlags[CHN_SUSTAINLOOP] && m_sample.nSustainStart > 0; const bool hasBothLoops = hasNormal && hasSustain; m_RadioNormalLoop.EnableWindow(hasBothLoops); m_RadioSustainLoop.EnableWindow(hasBothLoops); CheckRadioButton(IDC_RADIO1, IDC_RADIO2, ((m_useSustainLoop && hasSustain) || !hasNormal) ? IDC_RADIO2 : IDC_RADIO1); m_SliderLength.SetRange(0, 100000); m_SliderLength.SetPos(m_fadeLength); m_SliderFadeLaw.SetRange(0, 100000); m_SliderFadeLaw.SetPos(m_fadeLaw); OnLoopTypeChanged(); return TRUE; } void CSampleXFadeDlg::OnOK() { m_fadeLength = m_SliderLength.GetPos(); m_fadeLaw = m_SliderFadeLaw.GetPos(); m_afterloopFade = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED; m_useSustainLoop = IsDlgButtonChecked(IDC_RADIO2) != BST_UNCHECKED; Limit(m_fadeLength, uint32(0), uint32(100000)); CDialog::OnOK(); } void CSampleXFadeDlg::OnLoopTypeChanged() { SmpLength loopStart = m_sample.nLoopStart, loopEnd = m_sample.nLoopEnd; if(IsDlgButtonChecked(IDC_RADIO2)) { loopStart = m_sample.nSustainStart; loopEnd = m_sample.nSustainEnd; } m_maxLength = std::min({ m_sample.nLength, loopStart, loopEnd / 2u }); m_loopLength = loopEnd - loopStart; m_editLocked = true; m_SpinSamples.SetRange32(0, std::min(m_loopLength, m_maxLength)); GetDlgItem(IDC_EDIT1)->SetFocus(); CheckDlgButton(IDC_CHECK1, m_afterloopFade ? BST_CHECKED : BST_UNCHECKED); SmpLength numSamples = PercentToSamples(m_SliderLength.GetPos()); numSamples = std::min({ numSamples, m_loopLength, m_maxLength }); m_SpinSamples.SetPos(numSamples); SetDlgItemInt(IDC_EDIT1, numSamples, FALSE); m_editLocked = false; } void CSampleXFadeDlg::OnFadeLengthChanged() { if(m_editLocked) return; SmpLength numSamples = GetDlgItemInt(IDC_EDIT1, NULL, FALSE); numSamples = std::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 CResamplingDlg::ResamplingOption CResamplingDlg::m_lastChoice = CResamplingDlg::Upsample; uint32 CResamplingDlg::m_lastFrequency = 0; bool CResamplingDlg::m_updatePatterns = false; BEGIN_MESSAGE_MAP(CResamplingDlg, CDialog) ON_EN_SETFOCUS(IDC_EDIT1, &CResamplingDlg::OnFocusEdit) END_MESSAGE_MAP() BOOL CResamplingDlg::OnInitDialog() { CDialog::OnInitDialog(); SetWindowText(m_resampleAll ? _T("Resample All") : _T("Resample")); CheckRadioButton(IDC_RADIO1, IDC_RADIO3, IDC_RADIO1 + m_lastChoice); if(m_frequency > 0) { TCHAR s[32]; wsprintf(s, _T("&Upsample (%u Hz)"), m_frequency * 2); SetDlgItemText(IDC_RADIO1, s); wsprintf(s, _T("&Downsample (%u Hz)"), m_frequency / 2); SetDlgItemText(IDC_RADIO2, s); if(!m_lastFrequency) m_lastFrequency = m_frequency; } if(!m_lastFrequency) m_lastFrequency = 48000; SetDlgItemInt(IDC_EDIT1, m_lastFrequency, FALSE); CSpinButtonCtrl *spin = static_cast(GetDlgItem(IDC_SPIN1)); spin->SetRange32(1, 999999); spin->SetPos32(m_lastFrequency); CComboBox *cbnResampling = static_cast(GetDlgItem(IDC_COMBO_FILTER)); cbnResampling->SetRedraw(FALSE); const auto resamplingModes = Resampling::AllModesWithDefault(); for(auto mode : resamplingModes) { CString desc = _T("r8brain (High Quality)"); if(mode != SRCMODE_DEFAULT) desc = CTrackApp::GetResamplingModeName(mode, 1, true); int index = cbnResampling->AddString(desc); cbnResampling->SetItemData(index, mode); if(m_srcMode == mode) cbnResampling->SetCurSel(index); } cbnResampling->SetRedraw(TRUE); CheckDlgButton(IDC_CHECK1, m_updatePatterns ? BST_CHECKED : BST_UNCHECKED); return TRUE; } void CResamplingDlg::OnOK() { const int choice = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO3); if(choice == IDC_RADIO1) { m_lastChoice = Upsample; m_frequency *= 2; } else if(choice == IDC_RADIO2) { m_lastChoice = Downsample; m_frequency /= 2; } else { m_lastChoice = Custom; uint32 newFrequency = GetDlgItemInt(IDC_EDIT1, NULL, FALSE); if(newFrequency > 0) { m_lastFrequency = m_frequency = newFrequency; } else { MessageBeep(MB_ICONWARNING); GetDlgItem(IDC_EDIT1)->SetFocus(); return; } } CComboBox *cbnResampling = static_cast(GetDlgItem(IDC_COMBO_FILTER)); m_srcMode = static_cast(cbnResampling->GetItemData(cbnResampling->GetCurSel())); m_updatePatterns = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED; CDialog::OnOK(); } //////////////////////////////////////////////////////////////////////////////////////////// // Sample mix dialog SmpLength CMixSampleDlg::sampleOffset = 0; int CMixSampleDlg::amplifyOriginal = 50; int CMixSampleDlg::amplifyMix = 50; void CMixSampleDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CMixSampleDlg) DDX_Control(pDX, IDC_EDIT_OFFSET, m_EditOffset); DDX_Control(pDX, IDC_SPIN_OFFSET, m_SpinOffset); DDX_Control(pDX, IDC_SPIN_SAMPVOL1, m_SpinVolOriginal); DDX_Control(pDX, IDC_SPIN_SAMPVOL2, m_SpinVolMix); //}}AFX_DATA_MAP } CMixSampleDlg::CMixSampleDlg(CWnd *parent) : CDialog(IDD_MIXSAMPLES, parent) { } BOOL CMixSampleDlg::OnInitDialog() { CDialog::OnInitDialog(); // Offset m_SpinOffset.SetRange32(0, MAX_SAMPLE_LENGTH); SetDlgItemInt(IDC_EDIT_OFFSET, sampleOffset); // Volumes m_SpinVolOriginal.SetRange(-10000, 10000); m_SpinVolMix.SetRange(-10000, 10000); m_EditVolOriginal.SubclassDlgItem(IDC_EDIT_SAMPVOL1, this); m_EditVolOriginal.AllowNegative(true); m_EditVolMix.SubclassDlgItem(IDC_EDIT_SAMPVOL2, this); m_EditVolMix.AllowNegative(true); SetDlgItemInt(IDC_EDIT_SAMPVOL1, amplifyOriginal); SetDlgItemInt(IDC_EDIT_SAMPVOL2, amplifyMix); return TRUE; } void CMixSampleDlg::OnOK() { CDialog::OnOK(); sampleOffset = Clamp(GetDlgItemInt(IDC_EDIT_OFFSET), 0, MAX_SAMPLE_LENGTH); amplifyOriginal = Clamp(GetDlgItemInt(IDC_EDIT_SAMPVOL1), -10000, 10000); amplifyMix = Clamp(GetDlgItemInt(IDC_EDIT_SAMPVOL2), -10000, 10000); } OPENMPT_NAMESPACE_END