1
0

OPLInstrDlg.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. * OPLInstrDlg.cpp
  3. * ---------------
  4. * Purpose: Editor for OPL-based synth instruments
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "OPLInstrDlg.h"
  11. #include "../soundlib/OPL.h"
  12. #include "../soundlib/Sndfile.h"
  13. #include "resource.h"
  14. #include "Mainfrm.h"
  15. OPENMPT_NAMESPACE_BEGIN
  16. BEGIN_MESSAGE_MAP(OPLInstrDlg, CDialog)
  17. ON_WM_HSCROLL()
  18. ON_MESSAGE(WM_MOD_DRAGONDROPPING, &OPLInstrDlg::OnDragonDropping)
  19. ON_COMMAND(IDC_CHECK1, &OPLInstrDlg::ParamsChanged)
  20. ON_COMMAND(IDC_CHECK2, &OPLInstrDlg::ParamsChanged)
  21. ON_COMMAND(IDC_CHECK3, &OPLInstrDlg::ParamsChanged)
  22. ON_COMMAND(IDC_CHECK4, &OPLInstrDlg::ParamsChanged)
  23. ON_COMMAND(IDC_CHECK5, &OPLInstrDlg::ParamsChanged)
  24. ON_COMMAND(IDC_CHECK6, &OPLInstrDlg::ParamsChanged)
  25. ON_COMMAND(IDC_CHECK7, &OPLInstrDlg::ParamsChanged)
  26. ON_COMMAND(IDC_CHECK8, &OPLInstrDlg::ParamsChanged)
  27. ON_COMMAND(IDC_CHECK9, &OPLInstrDlg::ParamsChanged)
  28. ON_CBN_SELCHANGE(IDC_COMBO1, &OPLInstrDlg::ParamsChanged)
  29. ON_CBN_SELCHANGE(IDC_COMBO2, &OPLInstrDlg::ParamsChanged)
  30. ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &OPLInstrDlg::OnToolTip)
  31. END_MESSAGE_MAP()
  32. void OPLInstrDlg::DoDataExchange(CDataExchange *pDX)
  33. {
  34. CDialog::DoDataExchange(pDX);
  35. DDX_Control(pDX, IDC_CHECK1, m_additive);
  36. DDX_Control(pDX, IDC_SLIDER1, m_feedback);
  37. for(int op = 0; op < 2; op++)
  38. {
  39. const int slider = op * 7;
  40. const int check = op * 4;
  41. DDX_Control(pDX, IDC_SLIDER2 + slider, m_attackRate[op]);
  42. DDX_Control(pDX, IDC_SLIDER3 + slider, m_decayRate[op]);
  43. DDX_Control(pDX, IDC_SLIDER4 + slider, m_sustainLevel[op]);
  44. DDX_Control(pDX, IDC_SLIDER5 + slider, m_releaseRate[op]);
  45. DDX_Control(pDX, IDC_CHECK2 + check, m_sustain[op]);
  46. DDX_Control(pDX, IDC_SLIDER6 + slider, m_volume[op]);
  47. DDX_Control(pDX, IDC_CHECK3 + check, m_scaleEnv[op]);
  48. DDX_Control(pDX, IDC_SLIDER7 + slider, m_levelScaling[op]);
  49. DDX_Control(pDX, IDC_SLIDER8 + slider, m_freqMultiplier[op]);
  50. DDX_Control(pDX, IDC_COMBO1 + op, m_waveform[op]);
  51. DDX_Control(pDX, IDC_CHECK4 + check, m_vibrato[op]);
  52. DDX_Control(pDX, IDC_CHECK5 + check, m_tremolo[op]);
  53. }
  54. }
  55. OPLInstrDlg::OPLInstrDlg(CWnd &parent, const CSoundFile &sndFile)
  56. : m_parent(parent)
  57. , m_sndFile(sndFile)
  58. {
  59. Create(IDD_OPL_PARAMS, &parent);
  60. CRect rect;
  61. GetClientRect(rect);
  62. m_windowSize = rect.BottomRight();
  63. }
  64. OPLInstrDlg::~OPLInstrDlg()
  65. {
  66. DestroyWindow();
  67. }
  68. BOOL OPLInstrDlg::OnInitDialog()
  69. {
  70. CDialog::OnInitDialog();
  71. EnableToolTips();
  72. m_feedback.SetRange(0, 7);
  73. for(int op = 0; op < 2; op++)
  74. {
  75. m_attackRate[op].SetRange(0, 15);
  76. m_decayRate[op].SetRange(0, 15);
  77. m_sustainLevel[op].SetRange(0, 15);
  78. m_releaseRate[op].SetRange(0, 15);
  79. m_volume[op].SetRange(0, 63);
  80. m_volume[op].SetTicFreq(4);
  81. m_levelScaling[op].SetRange(0, 3);
  82. m_freqMultiplier[op].SetRange(0, 15);
  83. }
  84. return TRUE;
  85. }
  86. BOOL OPLInstrDlg::PreTranslateMessage(MSG *pMsg)
  87. {
  88. if(pMsg)
  89. {
  90. // Forward key presses and drag&drop support to parent editor
  91. if(pMsg->message == WM_SYSKEYUP || pMsg->message == WM_KEYUP ||
  92. pMsg->message == WM_SYSKEYDOWN || pMsg->message == WM_KEYDOWN ||
  93. pMsg->message == WM_DROPFILES)
  94. {
  95. if(pMsg->hwnd == m_hWnd)
  96. {
  97. pMsg->hwnd = m_parent.m_hWnd;
  98. }
  99. if(m_parent.PreTranslateMessage(pMsg))
  100. {
  101. return TRUE;
  102. }
  103. } else if(pMsg->message == WM_CHAR)
  104. {
  105. // Avoid Windows sounds on note key repeats
  106. if(HIWORD(pMsg->lParam) & 0x4000)
  107. return TRUE;
  108. }
  109. }
  110. return CDialog::PreTranslateMessage(pMsg);
  111. }
  112. LRESULT OPLInstrDlg::OnDragonDropping(WPARAM wParam, LPARAM lParam)
  113. {
  114. return m_parent.SendMessage(WM_MOD_DRAGONDROPPING, wParam, lParam);
  115. }
  116. // Swap OPL Key Scale Level bits for a "human-readable" value.
  117. static uint8 KeyScaleLevel(uint8 kslVolume)
  118. {
  119. static constexpr uint8 KSLFix[4] = { 0x00, 0x80, 0x40, 0xC0 };
  120. return KSLFix[kslVolume >> 6];
  121. }
  122. void OPLInstrDlg::SetPatch(OPLPatch &patch)
  123. {
  124. SetRedraw(FALSE);
  125. m_additive.SetCheck((patch[10] & OPL::CONNECTION_BIT) ? BST_CHECKED : BST_UNCHECKED);
  126. m_feedback.SetPos((patch[10] & OPL::FEEDBACK_MASK) >> 1);
  127. for(int op = 0; op < 2; op++)
  128. {
  129. m_attackRate[op].SetPos(15 - (patch[4 + op] >> 4));
  130. m_decayRate[op].SetPos(15 - (patch[4 + op] & 0x0F));
  131. m_sustainLevel[op].SetPos(15 - (patch[6 + op] >> 4));
  132. m_releaseRate[op].SetPos(15 - (patch[6 + op] & 0x0F));
  133. m_volume[op].SetPos(63 - (patch[2 + op] & OPL::TOTAL_LEVEL_MASK));
  134. m_levelScaling[op].SetPos(KeyScaleLevel(patch[2 + op]) >> 6);
  135. m_freqMultiplier[op].SetPos(patch[0 + op] & OPL::MULTIPLE_MASK);
  136. m_sustain[op].SetCheck((patch[0 + op] & OPL::SUSTAIN_ON) ? BST_CHECKED : BST_UNCHECKED);
  137. m_scaleEnv[op].SetCheck((patch[0 + op] & OPL::KSR) ? BST_CHECKED : BST_UNCHECKED);
  138. m_vibrato[op].SetCheck((patch[0 + op] & OPL::VIBRATO_ON) ? BST_CHECKED : BST_UNCHECKED);
  139. m_tremolo[op].SetCheck((patch[0 + op] & OPL::TREMOLO_ON) ? BST_CHECKED : BST_UNCHECKED);
  140. const auto waveform = patch[8 + op];
  141. const int numWaveforms = (m_sndFile.GetType() == MOD_TYPE_S3M && waveform < 4) ? 4 : 8;
  142. if(numWaveforms != m_waveform[op].GetCount())
  143. {
  144. m_waveform[op].ResetContent();
  145. static constexpr const TCHAR *waveformNames[] =
  146. {
  147. _T("Sine"), _T("Half Sine"), _T("Absolute Sine"), _T("Pulse Sine"),
  148. _T("Sine (Even Periods)"), _T("Absolute Sine (Even Periods)"), _T("Square"), _T("Derived Square")
  149. };
  150. for(int i = 0; i < numWaveforms; i++)
  151. {
  152. m_waveform[op].AddString(waveformNames[i]);
  153. }
  154. }
  155. m_waveform[op].SetCurSel(waveform);
  156. }
  157. SetRedraw(TRUE);
  158. m_patch = &patch;
  159. }
  160. void OPLInstrDlg::ParamsChanged()
  161. {
  162. OPLPatch patch{{}};
  163. if(m_additive.GetCheck() != BST_UNCHECKED) patch[10] |= OPL::CONNECTION_BIT;
  164. patch[10] |= static_cast<uint8>(m_feedback.GetPos() << 1);
  165. for(int op = 0; op < 2; op++)
  166. {
  167. patch[op] = static_cast<uint8>(m_freqMultiplier[op].GetPos());
  168. if(m_sustain[op].GetCheck() != BST_UNCHECKED) patch[op] |= OPL::SUSTAIN_ON;
  169. if(m_scaleEnv[op].GetCheck() != BST_UNCHECKED) patch[op] |= OPL::KSR;
  170. if(m_vibrato[op].GetCheck() != BST_UNCHECKED) patch[op] |= OPL::VIBRATO_ON;
  171. if(m_tremolo[op].GetCheck() != BST_UNCHECKED) patch[op] |= OPL::TREMOLO_ON;
  172. patch[2 + op] = static_cast<uint8>((63 - m_volume[op].GetPos()) | KeyScaleLevel(static_cast<uint8>(m_levelScaling[op].GetPos() << 6)));
  173. patch[4 + op] = static_cast<uint8>(((15 - m_attackRate[op].GetPos()) << 4) | (15 - m_decayRate[op].GetPos()));
  174. patch[6 + op] = static_cast<uint8>(((15 - m_sustainLevel[op].GetPos()) << 4) | (15 - m_releaseRate[op].GetPos()));
  175. patch[8 + op] = static_cast<uint8>(m_waveform[op].GetCurSel());
  176. }
  177. if(*m_patch != patch)
  178. {
  179. m_parent.SendMessage(WM_MOD_VIEWMSG, VIEWMSG_PREPAREUNDO);
  180. *m_patch = patch;
  181. m_parent.SendMessage(WM_MOD_VIEWMSG, VIEWMSG_SETMODIFIED, SampleHint().Data().AsLPARAM());
  182. }
  183. }
  184. BOOL OPLInstrDlg::OnToolTip(UINT /*id*/, NMHDR *pNMHDR, LRESULT* /*pResult*/)
  185. {
  186. TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR;
  187. UINT_PTR nID = pNMHDR->idFrom;
  188. if(pTTT->uFlags & TTF_IDISHWND)
  189. {
  190. // idFrom is actually the HWND of the tool
  191. nID = ::GetDlgCtrlID((HWND)nID);
  192. }
  193. static constexpr const char *feedback[] = {"disabled", "\xCF\x80/16", "\xCF\x80/8", "\xCF\x80/4", "\xCF\x80/2", "\xCF\x80", "2\xCF\x80", "4\xCF\x80"};
  194. static constexpr const TCHAR *ksl[] = {_T("disabled"), _T("1.5 dB / octave"), _T("3 dB / octave"), _T("6 dB / octave")};
  195. mpt::tstring text;
  196. const CWnd *wnd = GetDlgItem(static_cast<int>(nID));
  197. const CSliderCtrl *slider = static_cast<const CSliderCtrl *>(wnd);
  198. switch(nID)
  199. {
  200. case IDC_SLIDER1:
  201. // Feedback
  202. text = mpt::ToWin(mpt::Charset::UTF8, feedback[slider->GetPos() & 7]);
  203. break;
  204. case IDC_SLIDER2:
  205. case IDC_SLIDER3:
  206. case IDC_SLIDER5:
  207. case IDC_SLIDER9:
  208. case IDC_SLIDER10:
  209. case IDC_SLIDER12:
  210. // Attack / Decay / Release
  211. text = _T("faster < ") + mpt::tfmt::val(slider->GetPos()) + _T(" > slower");
  212. break;
  213. case IDC_SLIDER4:
  214. case IDC_SLIDER11:
  215. // Sustain Level
  216. {
  217. const int pos = slider->GetPos();
  218. text = mpt::tfmt::val((pos == 0) ? -93 : ((-15 + pos) * 3)) + _T(" dB");
  219. }
  220. break;
  221. case IDC_SLIDER6:
  222. case IDC_SLIDER13:
  223. // Volume Level
  224. text = mpt::tfmt::fix((-63 + slider->GetPos()) * 0.75, 2) + _T(" dB");
  225. break;
  226. case IDC_SLIDER7:
  227. case IDC_SLIDER14:
  228. // Key Scale Level
  229. text = ksl[slider->GetPos() & 3];
  230. break;
  231. case IDC_SLIDER8:
  232. case IDC_SLIDER15:
  233. // Frequency Multiplier
  234. if(slider->GetPos() == 0)
  235. text = _T("0.5");
  236. else
  237. text = mpt::tfmt::val(slider->GetPos());
  238. break;
  239. }
  240. mpt::String::WriteWinBuf(pTTT->szText) = text.c_str();
  241. return TRUE;
  242. }
  243. OPENMPT_NAMESPACE_END