SampleEditorDialogs.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  1. /*
  2. * SampleEditorDialogs.cpp
  3. * -----------------------
  4. * Purpose: Code for various dialogs that are used in the sample editor.
  5. * Notes : (currently none)
  6. * Authors: Olivier Lapicque
  7. * OpenMPT Devs
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #include "stdafx.h"
  11. #include "resource.h"
  12. #include "Reporting.h"
  13. #include "MPTrackUtil.h"
  14. #include "Mptrack.h"
  15. #include "../common/misc_util.h"
  16. #include "../soundlib/Snd_defs.h"
  17. #include "../soundlib/ModSample.h"
  18. #include "SampleEditorDialogs.h"
  19. #include "ProgressDialog.h"
  20. OPENMPT_NAMESPACE_BEGIN
  21. //////////////////////////////////////////////////////////////////////////
  22. // Sample amplification dialog
  23. BEGIN_MESSAGE_MAP(CAmpDlg, CDialog)
  24. ON_WM_DESTROY()
  25. ON_EN_CHANGE(IDC_EDIT2, &CAmpDlg::EnableFadeIn)
  26. ON_EN_CHANGE(IDC_EDIT3, &CAmpDlg::EnableFadeOut)
  27. END_MESSAGE_MAP()
  28. void CAmpDlg::DoDataExchange(CDataExchange* pDX)
  29. {
  30. CDialog::DoDataExchange(pDX);
  31. //{{AFX_DATA_MAP(CAmpDlg)
  32. DDX_Control(pDX, IDC_COMBO1, m_fadeBox);
  33. //}}AFX_DATA_MAP
  34. }
  35. CAmpDlg::CAmpDlg(CWnd *parent, AmpSettings &settings, int16 factorMin, int16 factorMax)
  36. : CDialog(IDD_SAMPLE_AMPLIFY, parent)
  37. , m_settings(settings)
  38. , m_nFactorMin(factorMin)
  39. , m_nFactorMax(factorMax)
  40. {}
  41. BOOL CAmpDlg::OnInitDialog()
  42. {
  43. CDialog::OnInitDialog();
  44. CSpinButtonCtrl *spin = (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN1);
  45. spin->SetRange32(m_nFactorMin, m_nFactorMax);
  46. spin->SetPos32(m_settings.factor);
  47. spin = (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN2);
  48. spin->SetRange32(0, 100);
  49. spin->SetPos32(m_settings.fadeInStart);
  50. spin = (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN3);
  51. spin->SetRange32(0, 100);
  52. spin->SetPos32(m_settings.fadeOutEnd);
  53. SetDlgItemInt(IDC_EDIT1, m_settings.factor);
  54. SetDlgItemInt(IDC_EDIT2, m_settings.fadeInStart);
  55. SetDlgItemInt(IDC_EDIT3, m_settings.fadeOutEnd);
  56. m_edit.SubclassDlgItem(IDC_EDIT1, this);
  57. m_edit.AllowFractions(false);
  58. m_edit.AllowNegative(m_nFactorMin < 0);
  59. m_editFadeIn.SubclassDlgItem(IDC_EDIT2, this);
  60. m_editFadeIn.AllowFractions(false);
  61. m_editFadeIn.AllowNegative(m_nFactorMin < 0);
  62. m_editFadeOut.SubclassDlgItem(IDC_EDIT3, this);
  63. m_editFadeOut.AllowFractions(false);
  64. m_editFadeOut.AllowNegative(m_nFactorMin < 0);
  65. const struct
  66. {
  67. const TCHAR *name;
  68. Fade::Law id;
  69. } fadeLaws[] =
  70. {
  71. { _T("Linear"), Fade::kLinear },
  72. { _T("Exponential"), Fade::kPow },
  73. { _T("Square Root"), Fade::kSqrt },
  74. { _T("Logarithmic"), Fade::kLog },
  75. { _T("Quarter Sine"), Fade::kQuarterSine },
  76. { _T("Half Sine"), Fade::kHalfSine },
  77. };
  78. // Create icons for fade laws
  79. const int cx = Util::ScalePixels(16, m_hWnd);
  80. const int cy = Util::ScalePixels(16, m_hWnd);
  81. const int imgWidth = cx * static_cast<int>(std::size(fadeLaws));
  82. m_list.Create(cx, cy, ILC_COLOR32 | ILC_MASK, 0, 1);
  83. std::vector<COLORREF> bits(imgWidth * cy, RGB(255, 0, 255));
  84. const COLORREF col = GetSysColor(COLOR_WINDOWTEXT);
  85. for(int i = 0, baseX = 0; i < static_cast<int>(std::size(fadeLaws)); i++, baseX += cx)
  86. {
  87. Fade::Func fadeFunc = Fade::GetFadeFunc(fadeLaws[i].id);
  88. int oldVal = cy - 1;
  89. for(int x = 0; x < cx; x++)
  90. {
  91. int val = cy - 1 - mpt::saturate_round<int>(cy * fadeFunc(static_cast<double>(x) / cx));
  92. Limit(val, 0, cy - 1);
  93. if(oldVal > val && x > 0)
  94. {
  95. int dy = (oldVal - val) / 2;
  96. for(int y = oldVal * imgWidth; dy != 0; y -= imgWidth, dy--)
  97. {
  98. bits[baseX + (x - 1) + y] = col;
  99. }
  100. oldVal -= dy + 1;
  101. }
  102. for(int y = oldVal * imgWidth; y >= val * imgWidth; y -= imgWidth)
  103. {
  104. bits[baseX + x + y] = col;
  105. }
  106. oldVal = val;
  107. }
  108. }
  109. CBitmap bitmap;
  110. bitmap.CreateBitmap(cx * static_cast<int>(std::size(fadeLaws)), cy, 1, 32, bits.data());
  111. m_list.Add(&bitmap, RGB(255, 0, 255));
  112. bitmap.DeleteObject();
  113. m_fadeBox.SetImageList(&m_list);
  114. // Add fade laws to list
  115. COMBOBOXEXITEM cbi;
  116. MemsetZero(cbi);
  117. cbi.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE | CBEIF_TEXT | CBEIF_LPARAM;
  118. for(int i = 0; i < static_cast<int>(std::size(fadeLaws)); i++)
  119. {
  120. cbi.iItem = i;
  121. cbi.pszText = const_cast<LPTSTR>(fadeLaws[i].name);
  122. cbi.iImage = cbi.iSelectedImage = i;
  123. cbi.lParam = fadeLaws[i].id;
  124. m_fadeBox.InsertItem(&cbi);
  125. if(fadeLaws[i].id == m_settings.fadeLaw) m_fadeBox.SetCurSel(i);
  126. }
  127. m_locked = false;
  128. return TRUE;
  129. }
  130. void CAmpDlg::OnDestroy()
  131. {
  132. m_list.DeleteImageList();
  133. }
  134. void CAmpDlg::OnOK()
  135. {
  136. m_settings.factor = static_cast<int16>(Clamp(static_cast<int>(GetDlgItemInt(IDC_EDIT1)), m_nFactorMin, m_nFactorMax));
  137. m_settings.fadeInStart = Clamp(static_cast<int>(GetDlgItemInt(IDC_EDIT2)), m_nFactorMin, m_nFactorMax);
  138. m_settings.fadeOutEnd = Clamp(static_cast<int>(GetDlgItemInt(IDC_EDIT3)), m_nFactorMin, m_nFactorMax);
  139. m_settings.fadeIn = (IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED);
  140. m_settings.fadeOut = (IsDlgButtonChecked(IDC_CHECK2) != BST_UNCHECKED);
  141. m_settings.fadeLaw = static_cast<Fade::Law>(m_fadeBox.GetItemData(m_fadeBox.GetCurSel()));
  142. CDialog::OnOK();
  143. }
  144. //////////////////////////////////////////////////////////////
  145. // Sample import dialog
  146. SampleIO CRawSampleDlg::m_format(SampleIO::_8bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM);
  147. SmpLength CRawSampleDlg::m_offset = 0;
  148. BEGIN_MESSAGE_MAP(CRawSampleDlg, CDialog)
  149. ON_COMMAND_RANGE(IDC_RADIO1, IDC_RADIO4, &CRawSampleDlg::OnBitDepthChanged)
  150. ON_COMMAND_RANGE(IDC_RADIO7, IDC_RADIO10, &CRawSampleDlg::OnEncodingChanged)
  151. ON_COMMAND(IDC_BUTTON1, &CRawSampleDlg::OnAutodetectFormat)
  152. END_MESSAGE_MAP()
  153. void CRawSampleDlg::DoDataExchange(CDataExchange *pDX)
  154. {
  155. CDialog::DoDataExchange(pDX);
  156. //{{AFX_DATA_MAP(CRawSampleDlg)
  157. DDX_Control(pDX, IDC_SPIN1, m_SpinOffset);
  158. //}}AFX_DATA_MAP
  159. }
  160. BOOL CRawSampleDlg::OnInitDialog()
  161. {
  162. CDialog::OnInitDialog();
  163. if(const auto filename = m_file.GetOptionalFileName(); filename)
  164. {
  165. CString title;
  166. GetWindowText(title);
  167. title += _T(" - ") + filename->GetFullFileName().ToCString();
  168. SetWindowText(title);
  169. }
  170. m_SpinOffset.SetRange32(0, mpt::saturate_cast<int>(m_file.GetLength() - 1u));
  171. UpdateDialog();
  172. return TRUE;
  173. }
  174. void CRawSampleDlg::OnOK()
  175. {
  176. const int bitDepth = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO4);
  177. const int channels = GetCheckedRadioButton(IDC_RADIO5, IDC_RADIO6);
  178. const int encoding = GetCheckedRadioButton(IDC_RADIO7, IDC_RADIO10);
  179. const int endianness = GetCheckedRadioButton(IDC_RADIO11, IDC_RADIO12);
  180. if(bitDepth == IDC_RADIO1)
  181. m_format |= SampleIO::_8bit;
  182. else if(bitDepth == IDC_RADIO2)
  183. m_format |= SampleIO::_16bit;
  184. else if(bitDepth == IDC_RADIO3)
  185. m_format |= SampleIO::_24bit;
  186. else if(bitDepth == IDC_RADIO4)
  187. m_format |= SampleIO::_32bit;
  188. if(channels == IDC_RADIO5)
  189. m_format |= SampleIO::mono;
  190. else if(channels == IDC_RADIO6)
  191. m_format |= SampleIO::stereoInterleaved;
  192. if(encoding == IDC_RADIO7)
  193. m_format |= SampleIO::signedPCM;
  194. else if(encoding == IDC_RADIO8)
  195. m_format |= SampleIO::unsignedPCM;
  196. else if(encoding == IDC_RADIO9)
  197. m_format |= SampleIO::deltaPCM;
  198. else if(encoding == IDC_RADIO10)
  199. m_format |= SampleIO::floatPCM;
  200. if(endianness == IDC_RADIO11)
  201. m_format |= SampleIO::littleEndian;
  202. else if(endianness == IDC_RADIO12)
  203. m_format |= SampleIO::bigEndian;
  204. m_rememberFormat = IsDlgButtonChecked(IDC_CHK_REMEMBERSETTINGS) != BST_UNCHECKED;
  205. m_offset = GetDlgItemInt(IDC_EDIT1, nullptr, FALSE);
  206. CDialog::OnOK();
  207. }
  208. void CRawSampleDlg::UpdateDialog()
  209. {
  210. const int bitDepthID = IDC_RADIO1 + m_format.GetBitDepth() / 8 - 1;
  211. CheckRadioButton(IDC_RADIO1, IDC_RADIO4, bitDepthID);
  212. CheckRadioButton(IDC_RADIO5, IDC_RADIO6, (m_format.GetChannelFormat() == SampleIO::mono) ? IDC_RADIO5 : IDC_RADIO6);
  213. int encodingID = IDC_RADIO7;
  214. switch(m_format.GetEncoding())
  215. {
  216. case SampleIO::signedPCM: encodingID = IDC_RADIO7; break;
  217. case SampleIO::unsignedPCM: encodingID = IDC_RADIO8; break;
  218. case SampleIO::deltaPCM: encodingID = IDC_RADIO9; break;
  219. case SampleIO::floatPCM: encodingID = IDC_RADIO10; break;
  220. default: MPT_ASSERT_NOTREACHED();
  221. }
  222. CheckRadioButton(IDC_RADIO7, IDC_RADIO10, encodingID);
  223. CheckRadioButton(IDC_RADIO11, IDC_RADIO12, (m_format.GetEndianness() == SampleIO::littleEndian) ? IDC_RADIO11 : IDC_RADIO12);
  224. CheckDlgButton(IDC_CHK_REMEMBERSETTINGS, (m_rememberFormat ? BST_CHECKED : BST_UNCHECKED));
  225. SetDlgItemInt(IDC_EDIT1, m_offset, FALSE);
  226. OnBitDepthChanged(bitDepthID);
  227. OnEncodingChanged(encodingID);
  228. }
  229. void CRawSampleDlg::OnBitDepthChanged(UINT id)
  230. {
  231. const auto bits = (id - IDC_RADIO1 + 1) * 8;
  232. // 8-bit: endianness doesn't matter
  233. BOOL enableEndianness = (bits == 8) ? FALSE : TRUE;
  234. GetDlgItem(IDC_RADIO11)->EnableWindow(enableEndianness);
  235. GetDlgItem(IDC_RADIO12)->EnableWindow(enableEndianness);
  236. if(bits == 8)
  237. CheckRadioButton(IDC_RADIO11, IDC_RADIO12, IDC_RADIO11);
  238. const BOOL hasUnsignedDelta = (bits <= 16) ? TRUE : FALSE;
  239. const BOOL hasFloat = (bits == 32) ? TRUE : FALSE;
  240. GetDlgItem(IDC_RADIO8)->EnableWindow(hasUnsignedDelta);
  241. GetDlgItem(IDC_RADIO9)->EnableWindow(hasUnsignedDelta);
  242. GetDlgItem(IDC_RADIO10)->EnableWindow(hasFloat);
  243. const int encoding = GetCheckedRadioButton(IDC_RADIO7, IDC_RADIO10);
  244. if((encoding == IDC_RADIO8 && !hasUnsignedDelta)
  245. || (encoding == IDC_RADIO9 && !hasUnsignedDelta)
  246. || (encoding == IDC_RADIO10 && !hasFloat))
  247. CheckRadioButton(IDC_RADIO7, IDC_RADIO10, IDC_RADIO7);
  248. }
  249. void CRawSampleDlg::OnEncodingChanged(UINT id)
  250. {
  251. const bool isUnsignedDelta = (id == IDC_RADIO8) || (id == IDC_RADIO9);
  252. const bool isFloat = (id == IDC_RADIO10);
  253. GetDlgItem(IDC_RADIO1)->EnableWindow(isFloat ? FALSE : TRUE);
  254. GetDlgItem(IDC_RADIO2)->EnableWindow(isFloat ? FALSE : TRUE);
  255. GetDlgItem(IDC_RADIO3)->EnableWindow((isFloat || isUnsignedDelta) ? FALSE : TRUE);
  256. GetDlgItem(IDC_RADIO4)->EnableWindow(isUnsignedDelta ? FALSE : TRUE);
  257. const int bitDepth = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO4);
  258. if(bitDepth != IDC_RADIO4 && isFloat)
  259. CheckRadioButton(IDC_RADIO1, IDC_RADIO4, IDC_RADIO4);
  260. if((bitDepth == IDC_RADIO3 || bitDepth == IDC_RADIO4) && isUnsignedDelta)
  261. CheckRadioButton(IDC_RADIO1, IDC_RADIO4, IDC_RADIO1);
  262. }
  263. class AutodetectFormatDlg : public CProgressDialog
  264. {
  265. CRawSampleDlg &m_parent;
  266. public:
  267. SampleIO m_bestFormat;
  268. AutodetectFormatDlg(CRawSampleDlg &parent)
  269. : CProgressDialog(&parent)
  270. , m_parent(parent) {}
  271. void Run() override
  272. {
  273. // Probed raw formats... little-endian and stereo versions are automatically checked as well.
  274. static constexpr SampleIO ProbeFormats[] =
  275. {
  276. // 8-Bit
  277. {SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM},
  278. {SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::unsignedPCM},
  279. {SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::deltaPCM},
  280. // 16-Bit
  281. {SampleIO::_16bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM},
  282. {SampleIO::_16bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::unsignedPCM},
  283. {SampleIO::_16bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::deltaPCM},
  284. // 24-Bit
  285. {SampleIO::_24bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM},
  286. // 32-Bit
  287. {SampleIO::_32bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM},
  288. {SampleIO::_32bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::floatPCM},
  289. };
  290. SetTitle(_T("Raw Import"));
  291. SetText(_T("Determining raw format..."));
  292. SetRange(0, std::size(ProbeFormats) * 4);
  293. double bestError = DBL_MAX;
  294. uint64 progress = 0;
  295. for(SampleIO format : ProbeFormats)
  296. {
  297. for(const auto endianness : {SampleIO::littleEndian, SampleIO::bigEndian})
  298. {
  299. if(endianness == SampleIO::bigEndian && format.GetBitDepth() == SampleIO::_8bit)
  300. continue;
  301. format |= endianness;
  302. for(const auto channels : {SampleIO::mono, SampleIO::stereoInterleaved})
  303. {
  304. format |= channels;
  305. ModSample sample;
  306. m_parent.m_file.Seek(m_parent.m_offset);
  307. const auto bytesPerSample = format.GetNumChannels() * format.GetBitDepth() / 8u;
  308. sample.nLength = mpt::saturate_cast<SmpLength>(m_parent.m_file.BytesLeft() / bytesPerSample);
  309. if(!format.ReadSample(sample, m_parent.m_file))
  310. continue;
  311. const uint8 numChannels = sample.GetNumChannels();
  312. double error = 0.0;
  313. for(uint8 chn = 0; chn < numChannels; chn++)
  314. {
  315. const auto ComputeSampleError = [](auto *v, SmpLength length, uint8 numChannels)
  316. {
  317. const double factor = 1.0 / (1u << (sizeof(*v) * 8u - 1u));
  318. double error = 0.0;
  319. int32 prev = 0;
  320. for(SmpLength i = length; i != 0; i--, v += numChannels)
  321. {
  322. auto diff = (*v - prev) * factor;
  323. error += diff * diff;
  324. prev = *v;
  325. }
  326. return error;
  327. };
  328. if(sample.uFlags[CHN_16BIT])
  329. error += ComputeSampleError(sample.sample16() + chn, sample.nLength, numChannels);
  330. else
  331. error += ComputeSampleError(sample.sample8() + chn, sample.nLength, numChannels);
  332. }
  333. sample.FreeSample();
  334. double errorFactor = format.GetBitDepth() * format.GetBitDepth() / 64;
  335. // Delta PCM often produces slightly worse error compared to signed PCM for real delta samples, so give it a bit of an advantage.
  336. if(format.GetEncoding() == SampleIO::deltaPCM)
  337. errorFactor *= 0.75;
  338. error *= errorFactor;
  339. if(error < bestError)
  340. {
  341. bestError = error;
  342. m_bestFormat = format;
  343. }
  344. SetProgress(++progress);
  345. ProcessMessages();
  346. if(m_abort)
  347. {
  348. EndDialog(IDCANCEL);
  349. return;
  350. }
  351. }
  352. }
  353. }
  354. EndDialog(IDOK);
  355. }
  356. };
  357. void CRawSampleDlg::OnAutodetectFormat()
  358. {
  359. m_offset = GetDlgItemInt(IDC_EDIT1, nullptr, FALSE);
  360. AutodetectFormatDlg dlg(*this);
  361. if(dlg.DoModal() == IDOK)
  362. {
  363. m_format = dlg.m_bestFormat;
  364. UpdateDialog();
  365. }
  366. }
  367. /////////////////////////////////////////////////////////////////////////
  368. // Add silence / resize sample dialog
  369. BEGIN_MESSAGE_MAP(AddSilenceDlg, CDialog)
  370. ON_CBN_SELCHANGE(IDC_COMBO1, &AddSilenceDlg::OnUnitChanged)
  371. ON_COMMAND(IDC_RADIO_ADDSILENCE_BEGIN, &AddSilenceDlg::OnEditModeChanged)
  372. ON_COMMAND(IDC_RADIO_ADDSILENCE_END, &AddSilenceDlg::OnEditModeChanged)
  373. ON_COMMAND(IDC_RADIO_RESIZETO, &AddSilenceDlg::OnEditModeChanged)
  374. ON_COMMAND(IDC_RADIO1, &AddSilenceDlg::OnEditModeChanged)
  375. END_MESSAGE_MAP()
  376. SmpLength AddSilenceDlg::m_addSamples = 32;
  377. SmpLength AddSilenceDlg::m_createSamples = 64;
  378. AddSilenceDlg::AddSilenceDlg(CWnd *parent, SmpLength origLength, uint32 sampleRate, bool allowOPL)
  379. : CDialog(IDD_ADDSILENCE, parent)
  380. , m_numSamples(m_addSamples)
  381. , m_sampleRate(sampleRate)
  382. , m_allowOPL(allowOPL)
  383. {
  384. if(origLength > 0)
  385. {
  386. m_length = origLength;
  387. m_editOption = kSilenceAtEnd;
  388. } else
  389. {
  390. m_length = m_createSamples;
  391. m_editOption = kResize;
  392. }
  393. }
  394. BOOL AddSilenceDlg::OnInitDialog()
  395. {
  396. CDialog::OnInitDialog();
  397. CSpinButtonCtrl *spin = (CSpinButtonCtrl *)GetDlgItem(IDC_SPIN_ADDSILENCE);
  398. if(spin)
  399. {
  400. spin->SetRange32(0, int32_max);
  401. spin->SetPos32(m_numSamples);
  402. }
  403. CComboBox *box = (CComboBox *)GetDlgItem(IDC_COMBO1);
  404. if(box)
  405. {
  406. box->AddString(_T("samples"));
  407. box->AddString(_T("ms"));
  408. box->SetCurSel(m_unit);
  409. if(m_sampleRate == 0)
  410. {
  411. // Can't do any conversions if samplerate is unknown
  412. box->EnableWindow(FALSE);
  413. }
  414. }
  415. int buttonID = IDC_RADIO_ADDSILENCE_END;
  416. switch(m_editOption)
  417. {
  418. case kSilenceAtBeginning: buttonID = IDC_RADIO_ADDSILENCE_BEGIN; break;
  419. case kSilenceAtEnd: buttonID = IDC_RADIO_ADDSILENCE_END; break;
  420. case kResize: buttonID = IDC_RADIO_RESIZETO; break;
  421. }
  422. CheckDlgButton(buttonID, BST_CHECKED);
  423. SetDlgItemInt(IDC_EDIT_ADDSILENCE, (m_editOption == kResize) ? m_length : m_numSamples, FALSE);
  424. GetDlgItem(IDC_RADIO1)->EnableWindow(m_allowOPL ? TRUE : FALSE);
  425. return TRUE;
  426. }
  427. void AddSilenceDlg::OnOK()
  428. {
  429. m_numSamples = GetDlgItemInt(IDC_EDIT_ADDSILENCE, nullptr, FALSE);
  430. if(m_unit == kMilliseconds)
  431. {
  432. m_numSamples = Util::muldivr_unsigned(m_numSamples, m_sampleRate, 1000);
  433. }
  434. switch(m_editOption = GetEditMode())
  435. {
  436. case kSilenceAtBeginning:
  437. case kSilenceAtEnd:
  438. m_addSamples = m_numSamples;
  439. break;
  440. case kResize:
  441. m_createSamples = m_numSamples;
  442. break;
  443. }
  444. CDialog::OnOK();
  445. }
  446. void AddSilenceDlg::OnEditModeChanged()
  447. {
  448. AddSilenceOptions newEditOption = GetEditMode();
  449. GetDlgItem(IDC_EDIT_ADDSILENCE)->EnableWindow((newEditOption == kOPLInstrument) ? FALSE : TRUE);
  450. if(newEditOption != kResize && m_editOption == kResize)
  451. {
  452. // Switch to "add silence"
  453. m_length = GetDlgItemInt(IDC_EDIT_ADDSILENCE);
  454. SetDlgItemInt(IDC_EDIT_ADDSILENCE, m_numSamples);
  455. } else if(newEditOption == kResize && m_editOption != kResize)
  456. {
  457. // Switch to "resize"
  458. m_numSamples = GetDlgItemInt(IDC_EDIT_ADDSILENCE);
  459. SetDlgItemInt(IDC_EDIT_ADDSILENCE, m_length);
  460. }
  461. m_editOption = newEditOption;
  462. }
  463. void AddSilenceDlg::OnUnitChanged()
  464. {
  465. const auto unit = static_cast<Unit>(static_cast<CComboBox*>(GetDlgItem(IDC_COMBO1))->GetCurSel());
  466. if(m_unit == unit)
  467. return;
  468. m_unit = unit;
  469. SmpLength duration = GetDlgItemInt(IDC_EDIT_ADDSILENCE);
  470. if(m_unit == kSamples)
  471. {
  472. // Convert from milliseconds
  473. duration = Util::muldivr_unsigned(duration, m_sampleRate, 1000);
  474. } else
  475. {
  476. // Convert from samples
  477. duration = Util::muldivr_unsigned(duration, 1000, m_sampleRate);
  478. }
  479. SetDlgItemInt(IDC_EDIT_ADDSILENCE, duration);
  480. }
  481. AddSilenceDlg::AddSilenceOptions AddSilenceDlg::GetEditMode() const
  482. {
  483. if(IsDlgButtonChecked(IDC_RADIO_ADDSILENCE_BEGIN)) return kSilenceAtBeginning;
  484. else if(IsDlgButtonChecked(IDC_RADIO_ADDSILENCE_END)) return kSilenceAtEnd;
  485. else if(IsDlgButtonChecked(IDC_RADIO_RESIZETO)) return kResize;
  486. else if(IsDlgButtonChecked(IDC_RADIO1)) return kOPLInstrument;
  487. MPT_ASSERT_NOTREACHED();
  488. return kSilenceAtEnd;
  489. }
  490. /////////////////////////////////////////////////////////////////////////
  491. // Sample grid dialog
  492. void CSampleGridDlg::DoDataExchange(CDataExchange* pDX)
  493. {
  494. CDialog::DoDataExchange(pDX);
  495. //{{AFX_DATA_MAP(CSampleGridDlg)
  496. DDX_Control(pDX, IDC_EDIT1, m_EditSegments);
  497. DDX_Control(pDX, IDC_SPIN1, m_SpinSegments);
  498. //}}AFX_DATA_MAP
  499. }
  500. BOOL CSampleGridDlg::OnInitDialog()
  501. {
  502. CDialog::OnInitDialog();
  503. m_SpinSegments.SetRange32(0, m_nMaxSegments);
  504. m_SpinSegments.SetPos(m_nSegments);
  505. SetDlgItemInt(IDC_EDIT1, m_nSegments, FALSE);
  506. GetDlgItem(IDC_EDIT1)->SetFocus();
  507. return TRUE;
  508. }
  509. void CSampleGridDlg::OnOK()
  510. {
  511. m_nSegments = GetDlgItemInt(IDC_EDIT1, NULL, FALSE);
  512. CDialog::OnOK();
  513. }
  514. /////////////////////////////////////////////////////////////////////////
  515. // Sample cross-fade dialog
  516. uint32 CSampleXFadeDlg::m_fadeLength = 20000;
  517. uint32 CSampleXFadeDlg::m_fadeLaw = 50000;
  518. bool CSampleXFadeDlg::m_afterloopFade = true;
  519. bool CSampleXFadeDlg::m_useSustainLoop = false;
  520. BEGIN_MESSAGE_MAP(CSampleXFadeDlg, CDialog)
  521. ON_WM_HSCROLL()
  522. ON_COMMAND(IDC_RADIO1, &CSampleXFadeDlg::OnLoopTypeChanged)
  523. ON_COMMAND(IDC_RADIO2, &CSampleXFadeDlg::OnLoopTypeChanged)
  524. ON_EN_CHANGE(IDC_EDIT1, &CSampleXFadeDlg::OnFadeLengthChanged)
  525. ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CSampleXFadeDlg::OnToolTipText)
  526. END_MESSAGE_MAP()
  527. void CSampleXFadeDlg::DoDataExchange(CDataExchange* pDX)
  528. {
  529. CDialog::DoDataExchange(pDX);
  530. //{{AFX_DATA_MAP(CSampleGridDlg)
  531. DDX_Control(pDX, IDC_EDIT1, m_EditSamples);
  532. DDX_Control(pDX, IDC_SPIN1, m_SpinSamples);
  533. DDX_Control(pDX, IDC_SLIDER1, m_SliderLength);
  534. DDX_Control(pDX, IDC_SLIDER2, m_SliderFadeLaw);
  535. DDX_Control(pDX, IDC_RADIO1, m_RadioNormalLoop);
  536. DDX_Control(pDX, IDC_RADIO2, m_RadioSustainLoop);
  537. //}}AFX_DATA_MAP
  538. }
  539. BOOL CSampleXFadeDlg::OnInitDialog()
  540. {
  541. CDialog::OnInitDialog();
  542. const bool hasNormal = m_sample.uFlags[CHN_LOOP] && m_sample.nLoopStart > 0;
  543. const bool hasSustain = m_sample.uFlags[CHN_SUSTAINLOOP] && m_sample.nSustainStart > 0;
  544. const bool hasBothLoops = hasNormal && hasSustain;
  545. m_RadioNormalLoop.EnableWindow(hasBothLoops);
  546. m_RadioSustainLoop.EnableWindow(hasBothLoops);
  547. CheckRadioButton(IDC_RADIO1, IDC_RADIO2, ((m_useSustainLoop && hasSustain) || !hasNormal) ? IDC_RADIO2 : IDC_RADIO1);
  548. m_SliderLength.SetRange(0, 100000);
  549. m_SliderLength.SetPos(m_fadeLength);
  550. m_SliderFadeLaw.SetRange(0, 100000);
  551. m_SliderFadeLaw.SetPos(m_fadeLaw);
  552. OnLoopTypeChanged();
  553. return TRUE;
  554. }
  555. void CSampleXFadeDlg::OnOK()
  556. {
  557. m_fadeLength = m_SliderLength.GetPos();
  558. m_fadeLaw = m_SliderFadeLaw.GetPos();
  559. m_afterloopFade = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED;
  560. m_useSustainLoop = IsDlgButtonChecked(IDC_RADIO2) != BST_UNCHECKED;
  561. Limit(m_fadeLength, uint32(0), uint32(100000));
  562. CDialog::OnOK();
  563. }
  564. void CSampleXFadeDlg::OnLoopTypeChanged()
  565. {
  566. SmpLength loopStart = m_sample.nLoopStart, loopEnd = m_sample.nLoopEnd;
  567. if(IsDlgButtonChecked(IDC_RADIO2))
  568. {
  569. loopStart = m_sample.nSustainStart;
  570. loopEnd = m_sample.nSustainEnd;
  571. }
  572. m_maxLength = std::min({ m_sample.nLength, loopStart, loopEnd / 2u });
  573. m_loopLength = loopEnd - loopStart;
  574. m_editLocked = true;
  575. m_SpinSamples.SetRange32(0, std::min(m_loopLength, m_maxLength));
  576. GetDlgItem(IDC_EDIT1)->SetFocus();
  577. CheckDlgButton(IDC_CHECK1, m_afterloopFade ? BST_CHECKED : BST_UNCHECKED);
  578. SmpLength numSamples = PercentToSamples(m_SliderLength.GetPos());
  579. numSamples = std::min({ numSamples, m_loopLength, m_maxLength });
  580. m_SpinSamples.SetPos(numSamples);
  581. SetDlgItemInt(IDC_EDIT1, numSamples, FALSE);
  582. m_editLocked = false;
  583. }
  584. void CSampleXFadeDlg::OnFadeLengthChanged()
  585. {
  586. if(m_editLocked) return;
  587. SmpLength numSamples = GetDlgItemInt(IDC_EDIT1, NULL, FALSE);
  588. numSamples = std::min({ numSamples, m_loopLength, m_maxLength });
  589. m_SliderLength.SetPos(SamplesToPercent(numSamples));
  590. }
  591. void CSampleXFadeDlg::OnHScroll(UINT, UINT, CScrollBar *sb)
  592. {
  593. if(sb == (CScrollBar *)(&m_SliderLength))
  594. {
  595. m_editLocked = true;
  596. SmpLength numSamples = PercentToSamples(m_SliderLength.GetPos());
  597. if(numSamples > m_maxLength)
  598. {
  599. numSamples = m_maxLength;
  600. m_SliderLength.SetPos(SamplesToPercent(numSamples));
  601. }
  602. m_SpinSamples.SetPos(numSamples);
  603. SetDlgItemInt(IDC_EDIT1, numSamples, FALSE);
  604. m_editLocked = false;
  605. }
  606. }
  607. BOOL CSampleXFadeDlg::OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *pResult)
  608. {
  609. TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
  610. UINT_PTR nID = pNMHDR->idFrom;
  611. if(pTTT->uFlags & TTF_IDISHWND)
  612. {
  613. // idFrom is actually the HWND of the tool
  614. nID = (UINT_PTR)::GetDlgCtrlID((HWND)nID);
  615. }
  616. switch(nID)
  617. {
  618. case IDC_SLIDER1:
  619. {
  620. uint32 percent = m_SliderLength.GetPos();
  621. wsprintf(pTTT->szText, _T("%u.%03u%% of the loop (%u samples)"), percent / 1000, percent % 1000, PercentToSamples(percent));
  622. }
  623. break;
  624. case IDC_SLIDER2:
  625. _tcscpy(pTTT->szText, _T("Slide towards constant power for fixing badly looped samples."));
  626. break;
  627. default:
  628. return FALSE;
  629. }
  630. *pResult = 0;
  631. // bring the tooltip window above other popup windows
  632. ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
  633. SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
  634. return TRUE;
  635. }
  636. /////////////////////////////////////////////////////////////////////////
  637. // Resampling dialog
  638. CResamplingDlg::ResamplingOption CResamplingDlg::m_lastChoice = CResamplingDlg::Upsample;
  639. uint32 CResamplingDlg::m_lastFrequency = 0;
  640. bool CResamplingDlg::m_updatePatterns = false;
  641. BEGIN_MESSAGE_MAP(CResamplingDlg, CDialog)
  642. ON_EN_SETFOCUS(IDC_EDIT1, &CResamplingDlg::OnFocusEdit)
  643. END_MESSAGE_MAP()
  644. BOOL CResamplingDlg::OnInitDialog()
  645. {
  646. CDialog::OnInitDialog();
  647. SetWindowText(m_resampleAll ? _T("Resample All") : _T("Resample"));
  648. CheckRadioButton(IDC_RADIO1, IDC_RADIO3, IDC_RADIO1 + m_lastChoice);
  649. if(m_frequency > 0)
  650. {
  651. TCHAR s[32];
  652. wsprintf(s, _T("&Upsample (%u Hz)"), m_frequency * 2);
  653. SetDlgItemText(IDC_RADIO1, s);
  654. wsprintf(s, _T("&Downsample (%u Hz)"), m_frequency / 2);
  655. SetDlgItemText(IDC_RADIO2, s);
  656. if(!m_lastFrequency)
  657. m_lastFrequency = m_frequency;
  658. }
  659. if(!m_lastFrequency)
  660. m_lastFrequency = 48000;
  661. SetDlgItemInt(IDC_EDIT1, m_lastFrequency, FALSE);
  662. CSpinButtonCtrl *spin = static_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN1));
  663. spin->SetRange32(1, 999999);
  664. spin->SetPos32(m_lastFrequency);
  665. CComboBox *cbnResampling = static_cast<CComboBox *>(GetDlgItem(IDC_COMBO_FILTER));
  666. cbnResampling->SetRedraw(FALSE);
  667. const auto resamplingModes = Resampling::AllModesWithDefault();
  668. for(auto mode : resamplingModes)
  669. {
  670. CString desc = _T("r8brain (High Quality)");
  671. if(mode != SRCMODE_DEFAULT)
  672. desc = CTrackApp::GetResamplingModeName(mode, 1, true);
  673. int index = cbnResampling->AddString(desc);
  674. cbnResampling->SetItemData(index, mode);
  675. if(m_srcMode == mode)
  676. cbnResampling->SetCurSel(index);
  677. }
  678. cbnResampling->SetRedraw(TRUE);
  679. CheckDlgButton(IDC_CHECK1, m_updatePatterns ? BST_CHECKED : BST_UNCHECKED);
  680. return TRUE;
  681. }
  682. void CResamplingDlg::OnOK()
  683. {
  684. const int choice = GetCheckedRadioButton(IDC_RADIO1, IDC_RADIO3);
  685. if(choice == IDC_RADIO1)
  686. {
  687. m_lastChoice = Upsample;
  688. m_frequency *= 2;
  689. } else if(choice == IDC_RADIO2)
  690. {
  691. m_lastChoice = Downsample;
  692. m_frequency /= 2;
  693. } else
  694. {
  695. m_lastChoice = Custom;
  696. uint32 newFrequency = GetDlgItemInt(IDC_EDIT1, NULL, FALSE);
  697. if(newFrequency > 0)
  698. {
  699. m_lastFrequency = m_frequency = newFrequency;
  700. } else
  701. {
  702. MessageBeep(MB_ICONWARNING);
  703. GetDlgItem(IDC_EDIT1)->SetFocus();
  704. return;
  705. }
  706. }
  707. CComboBox *cbnResampling = static_cast<CComboBox *>(GetDlgItem(IDC_COMBO_FILTER));
  708. m_srcMode = static_cast<ResamplingMode>(cbnResampling->GetItemData(cbnResampling->GetCurSel()));
  709. m_updatePatterns = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED;
  710. CDialog::OnOK();
  711. }
  712. ////////////////////////////////////////////////////////////////////////////////////////////
  713. // Sample mix dialog
  714. SmpLength CMixSampleDlg::sampleOffset = 0;
  715. int CMixSampleDlg::amplifyOriginal = 50;
  716. int CMixSampleDlg::amplifyMix = 50;
  717. void CMixSampleDlg::DoDataExchange(CDataExchange* pDX)
  718. {
  719. CDialog::DoDataExchange(pDX);
  720. //{{AFX_DATA_MAP(CMixSampleDlg)
  721. DDX_Control(pDX, IDC_EDIT_OFFSET, m_EditOffset);
  722. DDX_Control(pDX, IDC_SPIN_OFFSET, m_SpinOffset);
  723. DDX_Control(pDX, IDC_SPIN_SAMPVOL1, m_SpinVolOriginal);
  724. DDX_Control(pDX, IDC_SPIN_SAMPVOL2, m_SpinVolMix);
  725. //}}AFX_DATA_MAP
  726. }
  727. CMixSampleDlg::CMixSampleDlg(CWnd *parent)
  728. : CDialog(IDD_MIXSAMPLES, parent)
  729. { }
  730. BOOL CMixSampleDlg::OnInitDialog()
  731. {
  732. CDialog::OnInitDialog();
  733. // Offset
  734. m_SpinOffset.SetRange32(0, MAX_SAMPLE_LENGTH);
  735. SetDlgItemInt(IDC_EDIT_OFFSET, sampleOffset);
  736. // Volumes
  737. m_SpinVolOriginal.SetRange(-10000, 10000);
  738. m_SpinVolMix.SetRange(-10000, 10000);
  739. m_EditVolOriginal.SubclassDlgItem(IDC_EDIT_SAMPVOL1, this);
  740. m_EditVolOriginal.AllowNegative(true);
  741. m_EditVolMix.SubclassDlgItem(IDC_EDIT_SAMPVOL2, this);
  742. m_EditVolMix.AllowNegative(true);
  743. SetDlgItemInt(IDC_EDIT_SAMPVOL1, amplifyOriginal);
  744. SetDlgItemInt(IDC_EDIT_SAMPVOL2, amplifyMix);
  745. return TRUE;
  746. }
  747. void CMixSampleDlg::OnOK()
  748. {
  749. CDialog::OnOK();
  750. sampleOffset = Clamp<SmpLength, SmpLength>(GetDlgItemInt(IDC_EDIT_OFFSET), 0, MAX_SAMPLE_LENGTH);
  751. amplifyOriginal = Clamp<int, int>(GetDlgItemInt(IDC_EDIT_SAMPVOL1), -10000, 10000);
  752. amplifyMix = Clamp<int, int>(GetDlgItemInt(IDC_EDIT_SAMPVOL2), -10000, 10000);
  753. }
  754. OPENMPT_NAMESPACE_END