1
0

Ctrl_gen.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. /*
  2. * Ctrl_gen.cpp
  3. * ------------
  4. * Purpose: General tab, upper panel.
  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 "Mptrack.h"
  12. #include "Mainfrm.h"
  13. #include "InputHandler.h"
  14. #include "Moddoc.h"
  15. #include "Globals.h"
  16. #include "dlg_misc.h"
  17. #include "Ctrl_gen.h"
  18. #include "View_gen.h"
  19. #include "../common/misc_util.h"
  20. #include "../common/mptTime.h"
  21. #include "../soundlib/mod_specifications.h"
  22. OPENMPT_NAMESPACE_BEGIN
  23. BEGIN_MESSAGE_MAP(CCtrlGeneral, CModControlDlg)
  24. //{{AFX_MSG_MAP(CCtrlGeneral)
  25. ON_WM_VSCROLL()
  26. ON_COMMAND(IDC_BUTTON1, &CCtrlGeneral::OnTapTempo)
  27. ON_COMMAND(IDC_BUTTON_MODTYPE, &CCtrlGeneral::OnSongProperties)
  28. ON_COMMAND(IDC_CHECK_LOOPSONG, &CCtrlGeneral::OnLoopSongChanged)
  29. ON_EN_CHANGE(IDC_EDIT_SONGTITLE, &CCtrlGeneral::OnTitleChanged)
  30. ON_EN_CHANGE(IDC_EDIT_ARTIST, &CCtrlGeneral::OnArtistChanged)
  31. ON_EN_CHANGE(IDC_EDIT_TEMPO, &CCtrlGeneral::OnTempoChanged)
  32. ON_EN_CHANGE(IDC_EDIT_SPEED, &CCtrlGeneral::OnSpeedChanged)
  33. ON_EN_CHANGE(IDC_EDIT_GLOBALVOL, &CCtrlGeneral::OnGlobalVolChanged)
  34. ON_EN_CHANGE(IDC_EDIT_RESTARTPOS, &CCtrlGeneral::OnRestartPosChanged)
  35. ON_EN_CHANGE(IDC_EDIT_VSTIVOL, &CCtrlGeneral::OnVSTiVolChanged)
  36. ON_EN_CHANGE(IDC_EDIT_SAMPLEPA, &CCtrlGeneral::OnSamplePAChanged)
  37. ON_MESSAGE(WM_MOD_UPDATEPOSITION, &CCtrlGeneral::OnUpdatePosition)
  38. ON_EN_SETFOCUS(IDC_EDIT_SONGTITLE, &CCtrlGeneral::OnEnSetfocusEditSongtitle)
  39. ON_EN_KILLFOCUS(IDC_EDIT_RESTARTPOS, &CCtrlGeneral::OnRestartPosDone)
  40. ON_CBN_SELCHANGE(IDC_COMBO1, &CCtrlGeneral::OnResamplingChanged)
  41. //}}AFX_MSG_MAP
  42. END_MESSAGE_MAP()
  43. void CCtrlGeneral::DoDataExchange(CDataExchange* pDX)
  44. {
  45. CModControlDlg::DoDataExchange(pDX);
  46. //{{AFX_DATA_MAP(CCtrlGeneral)
  47. DDX_Control(pDX, IDC_EDIT_SONGTITLE, m_EditTitle);
  48. DDX_Control(pDX, IDC_EDIT_ARTIST, m_EditArtist);
  49. //DDX_Control(pDX, IDC_EDIT_TEMPO, m_EditTempo);
  50. DDX_Control(pDX, IDC_SPIN_TEMPO, m_SpinTempo);
  51. DDX_Control(pDX, IDC_EDIT_SPEED, m_EditSpeed);
  52. DDX_Control(pDX, IDC_SPIN_SPEED, m_SpinSpeed);
  53. DDX_Control(pDX, IDC_EDIT_GLOBALVOL, m_EditGlobalVol);
  54. DDX_Control(pDX, IDC_SPIN_GLOBALVOL, m_SpinGlobalVol);
  55. DDX_Control(pDX, IDC_EDIT_VSTIVOL, m_EditVSTiVol);
  56. DDX_Control(pDX, IDC_SPIN_VSTIVOL, m_SpinVSTiVol);
  57. DDX_Control(pDX, IDC_EDIT_SAMPLEPA, m_EditSamplePA);
  58. DDX_Control(pDX, IDC_SPIN_SAMPLEPA, m_SpinSamplePA);
  59. DDX_Control(pDX, IDC_EDIT_RESTARTPOS, m_EditRestartPos);
  60. DDX_Control(pDX, IDC_SPIN_RESTARTPOS, m_SpinRestartPos);
  61. DDX_Control(pDX, IDC_SLIDER_SONGTEMPO, m_SliderTempo);
  62. DDX_Control(pDX, IDC_SLIDER_VSTIVOL, m_SliderVSTiVol);
  63. DDX_Control(pDX, IDC_SLIDER_GLOBALVOL, m_SliderGlobalVol);
  64. DDX_Control(pDX, IDC_SLIDER_SAMPLEPREAMP, m_SliderSamplePreAmp);
  65. DDX_Control(pDX, IDC_BUTTON_MODTYPE, m_BtnModType);
  66. DDX_Control(pDX, IDC_VUMETER_LEFT, m_VuMeterLeft);
  67. DDX_Control(pDX, IDC_VUMETER_RIGHT, m_VuMeterRight);
  68. DDX_Control(pDX, IDC_COMBO1, m_CbnResampling);
  69. //}}AFX_DATA_MAP
  70. }
  71. CCtrlGeneral::CCtrlGeneral(CModControlView &parent, CModDoc &document) : CModControlDlg(parent, document)
  72. {
  73. }
  74. BOOL CCtrlGeneral::OnInitDialog()
  75. {
  76. const auto &specs = m_sndFile.GetModSpecifications();
  77. CModControlDlg::OnInitDialog();
  78. // Song Title
  79. m_EditTitle.SetLimitText(specs.modNameLengthMax);
  80. m_SpinGlobalVol.SetRange(0, (short)(256 / GetGlobalVolumeFactor()));
  81. m_SpinSamplePA.SetRange(0, 2000);
  82. m_SpinVSTiVol.SetRange(0, 2000);
  83. m_SpinRestartPos.SetRange32(0, ORDERINDEX_MAX);
  84. m_SliderGlobalVol.SetRange(0, MAX_SLIDER_GLOBAL_VOL);
  85. m_SliderVSTiVol.SetRange(0, MAX_SLIDER_VSTI_VOL);
  86. m_SliderSamplePreAmp.SetRange(0, MAX_SLIDER_SAMPLE_VOL);
  87. m_SpinTempo.SetRange(-10, 10);
  88. m_SliderTempo.SetLineSize(1);
  89. m_SliderTempo.SetPageSize(10);
  90. m_EditTempo.SubclassDlgItem(IDC_EDIT_TEMPO, this);
  91. m_EditTempo.AllowNegative(false);
  92. m_editsLocked = false;
  93. UpdateView(GeneralHint().ModType());
  94. OnActivatePage(0);
  95. m_bInitialized = TRUE;
  96. return FALSE;
  97. }
  98. CRuntimeClass *CCtrlGeneral::GetAssociatedViewClass()
  99. {
  100. return RUNTIME_CLASS(CViewGlobals);
  101. }
  102. void CCtrlGeneral::RecalcLayout()
  103. {
  104. }
  105. void CCtrlGeneral::OnActivatePage(LPARAM)
  106. {
  107. m_modDoc.SetNotifications(Notification::Default);
  108. m_modDoc.SetFollowWnd(m_hWnd);
  109. PostViewMessage(VIEWMSG_SETACTIVE, NULL);
  110. SetFocus();
  111. // Combo boxes randomly disappear without this... why?
  112. Invalidate();
  113. }
  114. void CCtrlGeneral::OnDeactivatePage()
  115. {
  116. m_modDoc.SetFollowWnd(NULL);
  117. m_VuMeterLeft.SetVuMeter(0, true);
  118. m_VuMeterRight.SetVuMeter(0, true);
  119. m_tapTimer = nullptr; // Reset high-precision clock if required
  120. }
  121. TEMPO CCtrlGeneral::TempoSliderRange() const
  122. {
  123. return (TEMPO_SPLIT_THRESHOLD - m_tempoMin) + TEMPO((m_tempoMax - TEMPO_SPLIT_THRESHOLD).GetInt() / TEMPO_SPLIT_PRECISION, 0);
  124. }
  125. TEMPO CCtrlGeneral::SliderToTempo(int value) const
  126. {
  127. if(m_tempoMax < TEMPO_SPLIT_THRESHOLD)
  128. {
  129. return m_tempoMax - TEMPO(value, 0);
  130. } else
  131. {
  132. const auto tempoSliderSplit = TempoToSlider(TEMPO_SPLIT_THRESHOLD);
  133. if(value <= tempoSliderSplit)
  134. return m_tempoMax - TEMPO(value * TEMPO_SPLIT_PRECISION, 0);
  135. else
  136. return m_tempoMin + TempoSliderRange() - TEMPO(value, 0);
  137. }
  138. }
  139. int CCtrlGeneral::TempoToSlider(TEMPO tempo) const
  140. {
  141. if(m_tempoMax < TEMPO_SPLIT_THRESHOLD)
  142. {
  143. return (m_tempoMax - tempo).GetInt();
  144. } else
  145. {
  146. if(tempo < TEMPO_SPLIT_THRESHOLD)
  147. return (TempoSliderRange() - (std::max(m_tempoMin, tempo) - m_tempoMin)).GetInt();
  148. else
  149. return (m_tempoMax - std::min(m_tempoMax, tempo)).GetInt() / TEMPO_SPLIT_PRECISION;
  150. }
  151. }
  152. void CCtrlGeneral::OnTapTempo()
  153. {
  154. using TapType = decltype(m_tapTimer->Now());
  155. static std::array<TapType, 32> tapTime;
  156. static TapType lastTap = 0;
  157. static uint32 numTaps = 0;
  158. if(m_tapTimer == nullptr)
  159. m_tapTimer = std::make_unique<Util::MultimediaClock>(1);
  160. const uint32 now = m_tapTimer->Now();
  161. if(now - lastTap >= 2000)
  162. numTaps = 0;
  163. lastTap = now;
  164. if(static_cast<size_t>(numTaps) >= tapTime.size())
  165. {
  166. // Shift back the previously recorded tap history
  167. // cppcheck false-positive
  168. // cppcheck-suppress mismatchingContainers
  169. std::copy(tapTime.begin() + 1, tapTime.end(), tapTime.begin());
  170. numTaps = static_cast<uint32>(tapTime.size() - 1);
  171. }
  172. tapTime[numTaps++] = now;
  173. if(numTaps <= 1)
  174. return;
  175. // Now apply least squares to tap history
  176. double sum = 0.0, weightedSum = 0.0;
  177. for(uint32 i = 0; i < numTaps; i++)
  178. {
  179. const double tapMs = tapTime[i] / 1000.0;
  180. sum += tapMs;
  181. weightedSum += i * tapMs;
  182. }
  183. const double lengthSum = numTaps * (numTaps - 1) / 2;
  184. const double lengthSumSum = lengthSum * (2 * numTaps - 1) / 3.0;
  185. const double secondsPerBeat = (numTaps * weightedSum - lengthSum * sum) / (lengthSumSum * numTaps - lengthSum * lengthSum);
  186. double newTempo = 60.0 / secondsPerBeat;
  187. if(m_sndFile.m_nTempoMode != TempoMode::Modern)
  188. newTempo *= (m_sndFile.m_nDefaultSpeed * m_sndFile.m_nDefaultRowsPerBeat) / 24.0;
  189. if(!m_sndFile.GetModSpecifications().hasFractionalTempo)
  190. newTempo = std::round(newTempo);
  191. TEMPO t(newTempo);
  192. Limit(t, m_tempoMin, m_tempoMax);
  193. m_EditTempo.SetTempoValue(t);
  194. }
  195. void CCtrlGeneral::UpdateView(UpdateHint hint, CObject *pHint)
  196. {
  197. if (pHint == this) return;
  198. FlagSet<HintType> hintType = hint.GetType();
  199. const bool updateAll = hintType[HINT_MODTYPE];
  200. const auto resamplingModes = Resampling::AllModes();
  201. if (hintType == HINT_MPTOPTIONS || updateAll)
  202. {
  203. CString defaultResampler;
  204. if(m_sndFile.m_SongFlags[SONG_ISAMIGA] && TrackerSettings::Instance().ResamplerEmulateAmiga != Resampling::AmigaFilter::Off)
  205. defaultResampler = _T("Amiga Resampler");
  206. else
  207. defaultResampler = CTrackApp::GetResamplingModeName(TrackerSettings::Instance().ResamplerMode, 1, false);
  208. m_CbnResampling.ResetContent();
  209. m_CbnResampling.SetItemData(m_CbnResampling.AddString(_T("Default (") + defaultResampler + _T(")")), SRCMODE_DEFAULT);
  210. for(auto mode : resamplingModes)
  211. {
  212. m_CbnResampling.SetItemData(m_CbnResampling.AddString(CTrackApp::GetResamplingModeName(mode, 2, true)), mode);
  213. }
  214. m_CbnResampling.Invalidate(FALSE);
  215. }
  216. if(updateAll)
  217. {
  218. const auto &specs = m_sndFile.GetModSpecifications();
  219. // S3M HACK: ST3 will ignore speed 255, even though it can be used with Axx.
  220. if(m_sndFile.GetType() == MOD_TYPE_S3M)
  221. m_SpinSpeed.SetRange32(1, 254);
  222. else
  223. m_SpinSpeed.SetRange32(specs.speedMin, specs.speedMax);
  224. m_tempoMin = specs.GetTempoMin();
  225. m_tempoMax = specs.GetTempoMax();
  226. // IT Hack: There are legacy OpenMPT-made ITs out there which use a higher default speed than 255.
  227. // Changing the upper tempo limit in the mod specs would break them, so do it here instead.
  228. if(m_sndFile.GetType() == MOD_TYPE_IT && m_sndFile.m_nDefaultTempo <= TEMPO(255, 0))
  229. m_tempoMax.Set(255);
  230. // Lower resolution for BPM above 256
  231. if(m_tempoMax >= TEMPO_SPLIT_THRESHOLD)
  232. m_SliderTempo.SetRange(0, TempoSliderRange().GetInt());
  233. else
  234. m_SliderTempo.SetRange(0, m_tempoMax.GetInt() - m_tempoMin.GetInt());
  235. m_EditTempo.AllowFractions(specs.hasFractionalTempo);
  236. const BOOL bIsNotMOD = (m_sndFile.GetType() != MOD_TYPE_MOD);
  237. const BOOL bIsNotMOD_XM = ((bIsNotMOD) && (m_sndFile.GetType() != MOD_TYPE_XM));
  238. m_EditArtist.EnableWindow(specs.hasArtistName);
  239. m_EditTempo.EnableWindow(bIsNotMOD);
  240. m_SpinTempo.EnableWindow(bIsNotMOD);
  241. GetDlgItem(IDC_BUTTON1)->EnableWindow(bIsNotMOD);
  242. m_SliderTempo.EnableWindow(bIsNotMOD);
  243. m_EditSpeed.EnableWindow(bIsNotMOD);
  244. m_SpinSpeed.EnableWindow(bIsNotMOD);
  245. const BOOL globalVol = bIsNotMOD_XM || m_sndFile.m_nDefaultGlobalVolume != MAX_GLOBAL_VOLUME;
  246. m_SliderGlobalVol.EnableWindow(globalVol);
  247. m_EditGlobalVol.EnableWindow(globalVol);
  248. m_SpinGlobalVol.EnableWindow(globalVol);
  249. m_EditSamplePA.EnableWindow(bIsNotMOD);
  250. m_SpinSamplePA.EnableWindow(bIsNotMOD);
  251. m_SliderVSTiVol.EnableWindow(bIsNotMOD);
  252. m_EditVSTiVol.EnableWindow(bIsNotMOD);
  253. m_SpinVSTiVol.EnableWindow(bIsNotMOD);
  254. m_EditRestartPos.EnableWindow((specs.hasRestartPos || m_sndFile.Order().GetRestartPos() != 0));
  255. m_SpinRestartPos.EnableWindow(m_EditRestartPos.IsWindowEnabled());
  256. //Note: Sample volume slider is not disabled for MOD
  257. //on purpose (can be used to control play volume)
  258. }
  259. if(updateAll || (hint.GetCategory() == HINTCAT_GLOBAL && hintType[HINT_MODCHANNELS]))
  260. {
  261. // MOD Type
  262. mpt::ustring modType;
  263. switch(m_sndFile.GetType())
  264. {
  265. case MOD_TYPE_MOD: modType = U_("MOD (ProTracker)"); break;
  266. case MOD_TYPE_S3M: modType = U_("S3M (Scream Tracker)"); break;
  267. case MOD_TYPE_XM: modType = U_("XM (FastTracker 2)"); break;
  268. case MOD_TYPE_IT: modType = U_("IT (Impulse Tracker)"); break;
  269. case MOD_TYPE_MPT: modType = U_("MPTM (OpenMPT)"); break;
  270. default: modType = MPT_UFORMAT("{} ({})")(mpt::ToUpperCase(m_sndFile.m_modFormat.type), m_sndFile.m_modFormat.formatName); break;
  271. }
  272. CString s;
  273. s.Format(_T("%s, %u channel%s"), mpt::ToCString(modType).GetString(), m_sndFile.GetNumChannels(), (m_sndFile.GetNumChannels() != 1) ? _T("s") : _T(""));
  274. m_BtnModType.SetWindowText(s);
  275. }
  276. if (updateAll || (hint.GetCategory() == HINTCAT_SEQUENCE && hintType[HINT_MODSEQUENCE | HINT_RESTARTPOS]))
  277. {
  278. // Set max valid restart position
  279. m_SpinRestartPos.SetRange32(0, std::max(m_sndFile.Order().GetRestartPos(), static_cast<ORDERINDEX>(m_sndFile.Order().GetLengthTailTrimmed() - 1)));
  280. SetDlgItemInt(IDC_EDIT_RESTARTPOS, m_sndFile.Order().GetRestartPos(), FALSE);
  281. }
  282. if (updateAll || (hint.GetCategory() == HINTCAT_GENERAL && hintType[HINT_MODGENERAL]))
  283. {
  284. if (!m_editsLocked)
  285. {
  286. m_EditTitle.SetWindowText(mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.GetTitle()));
  287. m_EditArtist.SetWindowText(mpt::ToCString(m_sndFile.m_songArtist));
  288. m_EditTempo.SetTempoValue(m_sndFile.m_nDefaultTempo);
  289. SetDlgItemInt(IDC_EDIT_SPEED, m_sndFile.m_nDefaultSpeed, FALSE);
  290. SetDlgItemInt(IDC_EDIT_GLOBALVOL, m_sndFile.m_nDefaultGlobalVolume / GetGlobalVolumeFactor(), FALSE);
  291. SetDlgItemInt(IDC_EDIT_VSTIVOL, m_sndFile.m_nVSTiVolume, FALSE);
  292. SetDlgItemInt(IDC_EDIT_SAMPLEPA, m_sndFile.m_nSamplePreAmp, FALSE);
  293. }
  294. m_SliderGlobalVol.SetPos(MAX_SLIDER_GLOBAL_VOL - m_sndFile.m_nDefaultGlobalVolume);
  295. m_SliderVSTiVol.SetPos(MAX_SLIDER_VSTI_VOL - m_sndFile.m_nVSTiVolume);
  296. m_SliderSamplePreAmp.SetPos(MAX_SLIDER_SAMPLE_VOL - m_sndFile.m_nSamplePreAmp);
  297. m_SliderTempo.SetPos(TempoToSlider(m_sndFile.m_nDefaultTempo));
  298. }
  299. if(updateAll || hintType == HINT_MPTOPTIONS || (hint.GetCategory() == HINTCAT_GENERAL && hintType[HINT_MODGENERAL]))
  300. {
  301. for(int i = 0; i < m_CbnResampling.GetCount(); ++i)
  302. {
  303. if(m_sndFile.m_nResampling == static_cast<ResamplingMode>(m_CbnResampling.GetItemData(i)))
  304. {
  305. m_CbnResampling.SetCurSel(i);
  306. break;
  307. }
  308. }
  309. }
  310. CheckDlgButton(IDC_CHECK_LOOPSONG, (TrackerSettings::Instance().gbLoopSong) ? TRUE : FALSE);
  311. if (hintType[HINT_MPTOPTIONS])
  312. {
  313. m_VuMeterLeft.InvalidateRect(NULL, FALSE);
  314. m_VuMeterRight.InvalidateRect(NULL, FALSE);
  315. }
  316. }
  317. void CCtrlGeneral::OnVScroll(UINT code, UINT pos, CScrollBar *pscroll)
  318. {
  319. CDialog::OnVScroll(code, pos, pscroll);
  320. if (m_bInitialized)
  321. {
  322. CSliderCtrl* pSlider = (CSliderCtrl*) pscroll;
  323. if (pSlider == &m_SliderTempo)
  324. {
  325. const TEMPO tempo = SliderToTempo(m_SliderTempo.GetPos());
  326. if ((tempo >= m_sndFile.GetModSpecifications().GetTempoMin()) && (tempo <= m_sndFile.GetModSpecifications().GetTempoMax()) && (tempo != m_sndFile.m_nDefaultTempo))
  327. {
  328. m_sndFile.m_nDefaultTempo = m_sndFile.m_PlayState.m_nMusicTempo = tempo;
  329. m_modDoc.SetModified();
  330. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  331. m_EditTempo.SetTempoValue(tempo);
  332. }
  333. }
  334. else if (pSlider == &m_SliderGlobalVol)
  335. {
  336. const UINT gv = MAX_SLIDER_GLOBAL_VOL - m_SliderGlobalVol.GetPos();
  337. if ((gv >= 0) && (gv <= MAX_SLIDER_GLOBAL_VOL) && (gv != m_sndFile.m_nDefaultGlobalVolume))
  338. {
  339. m_sndFile.m_PlayState.m_nGlobalVolume = gv;
  340. m_sndFile.m_nDefaultGlobalVolume = gv;
  341. m_modDoc.SetModified();
  342. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  343. SetDlgItemInt(IDC_EDIT_GLOBALVOL, m_sndFile.m_nDefaultGlobalVolume / GetGlobalVolumeFactor(), FALSE);
  344. }
  345. }
  346. else if (pSlider == &m_SliderSamplePreAmp)
  347. {
  348. const UINT spa = MAX_SLIDER_SAMPLE_VOL - m_SliderSamplePreAmp.GetPos();
  349. if ((spa >= 0) && (spa <= MAX_SLIDER_SAMPLE_VOL) && (spa != m_sndFile.m_nSamplePreAmp))
  350. {
  351. m_sndFile.m_nSamplePreAmp = spa;
  352. if(m_sndFile.GetType() != MOD_TYPE_MOD)
  353. m_modDoc.SetModified();
  354. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  355. SetDlgItemInt(IDC_EDIT_SAMPLEPA, m_sndFile.m_nSamplePreAmp, FALSE);
  356. }
  357. }
  358. else if (pSlider == &m_SliderVSTiVol)
  359. {
  360. const UINT vv = MAX_SLIDER_VSTI_VOL - m_SliderVSTiVol.GetPos();
  361. if ((vv >= 0) && (vv <= MAX_SLIDER_VSTI_VOL) && (vv != m_sndFile.m_nVSTiVolume))
  362. {
  363. m_sndFile.m_nVSTiVolume = vv;
  364. m_sndFile.RecalculateGainForAllPlugs();
  365. m_modDoc.SetModified();
  366. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  367. SetDlgItemInt(IDC_EDIT_VSTIVOL, m_sndFile.m_nVSTiVolume, FALSE);
  368. }
  369. }
  370. else if(pSlider == (CSliderCtrl*)&m_SpinTempo)
  371. {
  372. int pos32 = m_SpinTempo.GetPos32();
  373. if(pos32 != 0)
  374. {
  375. TEMPO newTempo;
  376. if(m_sndFile.GetModSpecifications().hasFractionalTempo)
  377. {
  378. pos32 *= TEMPO::fractFact;
  379. if(CMainFrame::GetMainFrame()->GetInputHandler()->CtrlPressed())
  380. pos32 /= 100;
  381. else if(CMainFrame::GetMainFrame()->GetInputHandler()->ShiftPressed())
  382. pos32 /= 10;
  383. newTempo.SetRaw(pos32);
  384. } else
  385. {
  386. newTempo = TEMPO(pos32, 0);
  387. }
  388. newTempo += m_sndFile.m_nDefaultTempo;
  389. Limit(newTempo, m_tempoMin, m_tempoMax);
  390. m_sndFile.m_nDefaultTempo = m_sndFile.m_PlayState.m_nMusicTempo = newTempo;
  391. m_modDoc.SetModified();
  392. LockControls();
  393. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  394. UnlockControls();
  395. m_SliderTempo.SetPos(TempoToSlider(newTempo));
  396. m_EditTempo.SetTempoValue(newTempo);
  397. }
  398. m_SpinTempo.SetPos(0);
  399. }
  400. }
  401. }
  402. void CCtrlGeneral::OnTitleChanged()
  403. {
  404. if (!m_EditTitle.m_hWnd || !m_EditTitle.GetModify()) return;
  405. CString title;
  406. m_EditTitle.GetWindowText(title);
  407. if(m_sndFile.SetTitle(mpt::ToCharset(m_sndFile.GetCharsetInternal(), title)))
  408. {
  409. m_EditTitle.SetModify(FALSE);
  410. m_modDoc.SetModified();
  411. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  412. }
  413. }
  414. void CCtrlGeneral::OnArtistChanged()
  415. {
  416. if (!m_EditArtist.m_hWnd || !m_EditArtist.GetModify()) return;
  417. mpt::ustring artist = GetWindowTextUnicode(m_EditArtist);
  418. if(artist != m_sndFile.m_songArtist)
  419. {
  420. m_EditArtist.SetModify(FALSE);
  421. m_sndFile.m_songArtist = artist;
  422. m_modDoc.SetModified();
  423. m_modDoc.UpdateAllViews(NULL, GeneralHint().General(), this);
  424. }
  425. }
  426. void CCtrlGeneral::OnTempoChanged()
  427. {
  428. if (m_bInitialized && m_EditTempo.GetWindowTextLength() > 0)
  429. {
  430. TEMPO tempo = m_EditTempo.GetTempoValue();
  431. Limit(tempo, m_tempoMin, m_tempoMax);
  432. if(!m_sndFile.GetModSpecifications().hasFractionalTempo) tempo.Set(tempo.GetInt());
  433. if (tempo != m_sndFile.m_nDefaultTempo)
  434. {
  435. m_editsLocked = true;
  436. m_EditTempo.SetModify(FALSE);
  437. m_sndFile.m_nDefaultTempo = tempo;
  438. m_sndFile.m_PlayState.m_nMusicTempo = tempo;
  439. m_modDoc.SetModified();
  440. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General());
  441. m_editsLocked = false;
  442. }
  443. }
  444. }
  445. void CCtrlGeneral::OnSpeedChanged()
  446. {
  447. TCHAR s[16];
  448. if(m_bInitialized)
  449. {
  450. m_EditSpeed.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
  451. if (s[0])
  452. {
  453. UINT n = ConvertStrTo<UINT>(s);
  454. n = Clamp(n, m_sndFile.GetModSpecifications().speedMin, m_sndFile.GetModSpecifications().speedMax);
  455. if (n != m_sndFile.m_nDefaultSpeed)
  456. {
  457. m_editsLocked = true;
  458. m_EditSpeed.SetModify(FALSE);
  459. m_sndFile.m_nDefaultSpeed = n;
  460. m_sndFile.m_PlayState.m_nMusicSpeed = n;
  461. m_modDoc.SetModified();
  462. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  463. // Update envelope grid view
  464. m_modDoc.UpdateAllViews(nullptr, InstrumentHint().Envelope(), this);
  465. m_editsLocked = false;
  466. }
  467. }
  468. }
  469. }
  470. void CCtrlGeneral::OnVSTiVolChanged()
  471. {
  472. TCHAR s[16];
  473. if (m_bInitialized)
  474. {
  475. m_EditVSTiVol.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
  476. if (s[0])
  477. {
  478. UINT n = ConvertStrTo<UINT>(s);
  479. Limit(n, 0u, 2000u);
  480. if (n != m_sndFile.m_nVSTiVolume)
  481. {
  482. m_editsLocked = true;
  483. m_sndFile.m_nVSTiVolume = n;
  484. m_sndFile.RecalculateGainForAllPlugs();
  485. m_modDoc.SetModified();
  486. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  487. UpdateView(GeneralHint().General());
  488. m_editsLocked = false;
  489. }
  490. }
  491. }
  492. }
  493. void CCtrlGeneral::OnSamplePAChanged()
  494. {
  495. TCHAR s[16];
  496. if(m_bInitialized)
  497. {
  498. m_EditSamplePA.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
  499. if (s[0])
  500. {
  501. UINT n = ConvertStrTo<UINT>(s);
  502. Limit(n, 0u, 2000u);
  503. if (n != m_sndFile.m_nSamplePreAmp)
  504. {
  505. m_editsLocked = true;
  506. m_sndFile.m_nSamplePreAmp = n;
  507. m_modDoc.SetModified();
  508. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  509. UpdateView(GeneralHint().General());
  510. m_editsLocked = false;
  511. }
  512. }
  513. }
  514. }
  515. void CCtrlGeneral::OnGlobalVolChanged()
  516. {
  517. TCHAR s[16];
  518. if(m_bInitialized)
  519. {
  520. m_EditGlobalVol.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
  521. if (s[0])
  522. {
  523. UINT n = ConvertStrTo<ORDERINDEX>(s) * GetGlobalVolumeFactor();
  524. Limit(n, 0u, 256u);
  525. if (n != m_sndFile.m_nDefaultGlobalVolume)
  526. {
  527. m_editsLocked = true;
  528. m_EditGlobalVol.SetModify(FALSE);
  529. m_sndFile.m_nDefaultGlobalVolume = n;
  530. m_sndFile.m_PlayState.m_nGlobalVolume = n;
  531. m_modDoc.SetModified();
  532. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  533. UpdateView(GeneralHint().General());
  534. m_editsLocked = false;
  535. }
  536. }
  537. }
  538. }
  539. void CCtrlGeneral::OnRestartPosChanged()
  540. {
  541. if(!m_bInitialized)
  542. return;
  543. TCHAR s[32];
  544. m_EditRestartPos.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
  545. if(!s[0])
  546. return;
  547. ORDERINDEX n = ConvertStrTo<ORDERINDEX>(s);
  548. LimitMax(n, m_sndFile.Order().GetLastIndex());
  549. while(n > 0 && n < m_sndFile.Order().GetLastIndex() && !m_sndFile.Order().IsValidPat(n))
  550. n++;
  551. if(n == m_sndFile.Order().GetRestartPos())
  552. return;
  553. m_EditRestartPos.SetModify(FALSE);
  554. m_sndFile.Order().SetRestartPos(n);
  555. m_modDoc.SetModified();
  556. m_modDoc.UpdateAllViews(nullptr, SequenceHint(m_sndFile.Order.GetCurrentSequenceIndex()).RestartPos(), this);
  557. }
  558. void CCtrlGeneral::OnRestartPosDone()
  559. {
  560. if(m_bInitialized)
  561. SetDlgItemInt(IDC_EDIT_RESTARTPOS, m_sndFile.Order().GetRestartPos());
  562. }
  563. void CCtrlGeneral::OnSongProperties()
  564. {
  565. m_modDoc.OnSongProperties();
  566. }
  567. void CCtrlGeneral::OnLoopSongChanged()
  568. {
  569. m_modDoc.SetLoopSong(IsDlgButtonChecked(IDC_CHECK_LOOPSONG) != BST_UNCHECKED);
  570. }
  571. LRESULT CCtrlGeneral::OnUpdatePosition(WPARAM, LPARAM lParam)
  572. {
  573. Notification *pnotify = (Notification *)lParam;
  574. if (pnotify)
  575. {
  576. m_VuMeterLeft.SetVuMeter(pnotify->masterVUout[0] & (~Notification::ClipVU), pnotify->type[Notification::Stop]);
  577. m_VuMeterRight.SetVuMeter(pnotify->masterVUout[1] & (~Notification::ClipVU), pnotify->type[Notification::Stop]);
  578. }
  579. return 0;
  580. }
  581. BOOL CCtrlGeneral::GetToolTipText(UINT uId, LPTSTR pszText)
  582. {
  583. const TCHAR moreRecentMixModeNote[] = _T("Use a more recent mixmode to see dB offsets.");
  584. if ((pszText) && (uId))
  585. {
  586. const bool displayDBValues = m_sndFile.GetPlayConfig().getDisplayDBValues();
  587. const CWnd *wnd = GetDlgItem(uId);
  588. const bool isEnabled = wnd ? (wnd->IsWindowEnabled() != FALSE) : true; // nullptr check is for a Wine bug workaround (https://bugs.openmpt.org/view.php?id=1553)
  589. mpt::tstring notAvailable;
  590. if(!isEnabled)
  591. notAvailable = MPT_TFORMAT("Feature is not available in the {} format.")(mpt::ToWin(mpt::Charset::ASCII, mpt::ToUpperCaseAscii(m_sndFile.GetModSpecifications().fileExtension)));
  592. switch(uId)
  593. {
  594. case IDC_BUTTON_MODTYPE:
  595. _tcscpy(pszText, _T("Song Properties"));
  596. {
  597. const auto keyText = CMainFrame::GetInputHandler()->m_activeCommandSet->GetKeyTextFromCommand(kcViewSongProperties, 0);
  598. if (!keyText.IsEmpty())
  599. _tcscat(pszText, MPT_TFORMAT(" ({})")(keyText).c_str());
  600. }
  601. return TRUE;
  602. case IDC_BUTTON1:
  603. if(isEnabled)
  604. _tcscpy(pszText, _T("Click button multiple times to tap in the desired tempo."));
  605. else
  606. _tcscpy(pszText, notAvailable.c_str());
  607. return TRUE;
  608. case IDC_SLIDER_SAMPLEPREAMP:
  609. _tcscpy(pszText, displayDBValues ? CModDoc::LinearToDecibels(m_sndFile.m_nSamplePreAmp, m_sndFile.GetPlayConfig().getNormalSamplePreAmp()).GetString() : moreRecentMixModeNote);
  610. return TRUE;
  611. case IDC_SLIDER_VSTIVOL:
  612. if(isEnabled)
  613. _tcscpy(pszText, displayDBValues ? CModDoc::LinearToDecibels(m_sndFile.m_nVSTiVolume, m_sndFile.GetPlayConfig().getNormalVSTiVol()).GetString() : moreRecentMixModeNote);
  614. else
  615. _tcscpy(pszText, notAvailable.c_str());
  616. return TRUE;
  617. case IDC_SLIDER_GLOBALVOL:
  618. if(isEnabled)
  619. _tcscpy(pszText, displayDBValues ? CModDoc::LinearToDecibels(m_sndFile.m_PlayState.m_nGlobalVolume, m_sndFile.GetPlayConfig().getNormalGlobalVol()).GetString() : moreRecentMixModeNote);
  620. else
  621. _tcscpy(pszText, notAvailable.c_str());
  622. return TRUE;
  623. case IDC_SLIDER_SONGTEMPO:
  624. case IDC_EDIT_ARTIST:
  625. case IDC_EDIT_TEMPO:
  626. case IDC_EDIT_SPEED:
  627. case IDC_EDIT_RESTARTPOS:
  628. case IDC_EDIT_GLOBALVOL:
  629. case IDC_EDIT_VSTIVOL:
  630. if(isEnabled)
  631. break;
  632. _tcscpy(pszText, notAvailable.c_str());
  633. return TRUE;
  634. }
  635. }
  636. return FALSE;
  637. }
  638. void CCtrlGeneral::OnEnSetfocusEditSongtitle()
  639. {
  640. m_EditTitle.SetLimitText(m_sndFile.GetModSpecifications().modNameLengthMax);
  641. }
  642. void CCtrlGeneral::OnResamplingChanged()
  643. {
  644. int sel = m_CbnResampling.GetCurSel();
  645. if(sel >= 0)
  646. {
  647. m_sndFile.m_nResampling = static_cast<ResamplingMode>(m_CbnResampling.GetItemData(sel));
  648. if(m_sndFile.GetModSpecifications().hasDefaultResampling)
  649. {
  650. m_modDoc.SetModified();
  651. m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
  652. }
  653. }
  654. }
  655. ////////////////////////////////////////////////////////////////////////////////
  656. //
  657. // CVuMeter
  658. //
  659. BEGIN_MESSAGE_MAP(CVuMeter, CWnd)
  660. ON_WM_PAINT()
  661. END_MESSAGE_MAP()
  662. void CVuMeter::OnPaint()
  663. {
  664. CRect rect;
  665. CPaintDC dc(this);
  666. GetClientRect(&rect);
  667. dc.FillSolidRect(rect.left, rect.top, rect.Width(), rect.Height(), RGB(0,0,0));
  668. m_lastDisplayedLevel = -1;
  669. DrawVuMeter(dc, true);
  670. }
  671. void CVuMeter::SetVuMeter(int level, bool force)
  672. {
  673. level >>= 8;
  674. if (level != m_lastLevel)
  675. {
  676. DWORD curTime = timeGetTime();
  677. if(curTime - m_lastVuUpdateTime >= TrackerSettings::Instance().VuMeterUpdateInterval || force)
  678. {
  679. m_lastLevel = level;
  680. CClientDC dc(this);
  681. DrawVuMeter(dc);
  682. m_lastVuUpdateTime = curTime;
  683. }
  684. }
  685. }
  686. void CVuMeter::DrawVuMeter(CDC &dc, bool /*redraw*/)
  687. {
  688. CRect rect;
  689. GetClientRect(&rect);
  690. int vu = (m_lastLevel * (rect.bottom-rect.top)) >> 8;
  691. int cy = rect.bottom - rect.top;
  692. if (cy < 1) cy = 1;
  693. for (int ry=rect.bottom-1; ry>rect.top; ry-=2)
  694. {
  695. int y0 = rect.bottom - ry;
  696. int n = Clamp((y0 * NUM_VUMETER_PENS) / cy, 0, NUM_VUMETER_PENS - 1);
  697. if (vu < y0)
  698. n += NUM_VUMETER_PENS;
  699. dc.FillSolidRect(rect.left, ry, rect.Width(), 1, CMainFrame::gcolrefVuMeter[n]);
  700. }
  701. m_lastDisplayedLevel = m_lastLevel;
  702. }
  703. OPENMPT_NAMESPACE_END