winamp/Src/external_dependencies/openmpt-trunk/mptrack/Ctrl_gen.cpp

817 lines
25 KiB
C++

/*
* Ctrl_gen.cpp
* ------------
* Purpose: General tab, upper panel.
* 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 "Mptrack.h"
#include "Mainfrm.h"
#include "InputHandler.h"
#include "Moddoc.h"
#include "Globals.h"
#include "dlg_misc.h"
#include "Ctrl_gen.h"
#include "View_gen.h"
#include "../common/misc_util.h"
#include "../common/mptTime.h"
#include "../soundlib/mod_specifications.h"
OPENMPT_NAMESPACE_BEGIN
BEGIN_MESSAGE_MAP(CCtrlGeneral, CModControlDlg)
//{{AFX_MSG_MAP(CCtrlGeneral)
ON_WM_VSCROLL()
ON_COMMAND(IDC_BUTTON1, &CCtrlGeneral::OnTapTempo)
ON_COMMAND(IDC_BUTTON_MODTYPE, &CCtrlGeneral::OnSongProperties)
ON_COMMAND(IDC_CHECK_LOOPSONG, &CCtrlGeneral::OnLoopSongChanged)
ON_EN_CHANGE(IDC_EDIT_SONGTITLE, &CCtrlGeneral::OnTitleChanged)
ON_EN_CHANGE(IDC_EDIT_ARTIST, &CCtrlGeneral::OnArtistChanged)
ON_EN_CHANGE(IDC_EDIT_TEMPO, &CCtrlGeneral::OnTempoChanged)
ON_EN_CHANGE(IDC_EDIT_SPEED, &CCtrlGeneral::OnSpeedChanged)
ON_EN_CHANGE(IDC_EDIT_GLOBALVOL, &CCtrlGeneral::OnGlobalVolChanged)
ON_EN_CHANGE(IDC_EDIT_RESTARTPOS, &CCtrlGeneral::OnRestartPosChanged)
ON_EN_CHANGE(IDC_EDIT_VSTIVOL, &CCtrlGeneral::OnVSTiVolChanged)
ON_EN_CHANGE(IDC_EDIT_SAMPLEPA, &CCtrlGeneral::OnSamplePAChanged)
ON_MESSAGE(WM_MOD_UPDATEPOSITION, &CCtrlGeneral::OnUpdatePosition)
ON_EN_SETFOCUS(IDC_EDIT_SONGTITLE, &CCtrlGeneral::OnEnSetfocusEditSongtitle)
ON_EN_KILLFOCUS(IDC_EDIT_RESTARTPOS, &CCtrlGeneral::OnRestartPosDone)
ON_CBN_SELCHANGE(IDC_COMBO1, &CCtrlGeneral::OnResamplingChanged)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CCtrlGeneral::DoDataExchange(CDataExchange* pDX)
{
CModControlDlg::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CCtrlGeneral)
DDX_Control(pDX, IDC_EDIT_SONGTITLE, m_EditTitle);
DDX_Control(pDX, IDC_EDIT_ARTIST, m_EditArtist);
//DDX_Control(pDX, IDC_EDIT_TEMPO, m_EditTempo);
DDX_Control(pDX, IDC_SPIN_TEMPO, m_SpinTempo);
DDX_Control(pDX, IDC_EDIT_SPEED, m_EditSpeed);
DDX_Control(pDX, IDC_SPIN_SPEED, m_SpinSpeed);
DDX_Control(pDX, IDC_EDIT_GLOBALVOL, m_EditGlobalVol);
DDX_Control(pDX, IDC_SPIN_GLOBALVOL, m_SpinGlobalVol);
DDX_Control(pDX, IDC_EDIT_VSTIVOL, m_EditVSTiVol);
DDX_Control(pDX, IDC_SPIN_VSTIVOL, m_SpinVSTiVol);
DDX_Control(pDX, IDC_EDIT_SAMPLEPA, m_EditSamplePA);
DDX_Control(pDX, IDC_SPIN_SAMPLEPA, m_SpinSamplePA);
DDX_Control(pDX, IDC_EDIT_RESTARTPOS, m_EditRestartPos);
DDX_Control(pDX, IDC_SPIN_RESTARTPOS, m_SpinRestartPos);
DDX_Control(pDX, IDC_SLIDER_SONGTEMPO, m_SliderTempo);
DDX_Control(pDX, IDC_SLIDER_VSTIVOL, m_SliderVSTiVol);
DDX_Control(pDX, IDC_SLIDER_GLOBALVOL, m_SliderGlobalVol);
DDX_Control(pDX, IDC_SLIDER_SAMPLEPREAMP, m_SliderSamplePreAmp);
DDX_Control(pDX, IDC_BUTTON_MODTYPE, m_BtnModType);
DDX_Control(pDX, IDC_VUMETER_LEFT, m_VuMeterLeft);
DDX_Control(pDX, IDC_VUMETER_RIGHT, m_VuMeterRight);
DDX_Control(pDX, IDC_COMBO1, m_CbnResampling);
//}}AFX_DATA_MAP
}
CCtrlGeneral::CCtrlGeneral(CModControlView &parent, CModDoc &document) : CModControlDlg(parent, document)
{
}
BOOL CCtrlGeneral::OnInitDialog()
{
const auto &specs = m_sndFile.GetModSpecifications();
CModControlDlg::OnInitDialog();
// Song Title
m_EditTitle.SetLimitText(specs.modNameLengthMax);
m_SpinGlobalVol.SetRange(0, (short)(256 / GetGlobalVolumeFactor()));
m_SpinSamplePA.SetRange(0, 2000);
m_SpinVSTiVol.SetRange(0, 2000);
m_SpinRestartPos.SetRange32(0, ORDERINDEX_MAX);
m_SliderGlobalVol.SetRange(0, MAX_SLIDER_GLOBAL_VOL);
m_SliderVSTiVol.SetRange(0, MAX_SLIDER_VSTI_VOL);
m_SliderSamplePreAmp.SetRange(0, MAX_SLIDER_SAMPLE_VOL);
m_SpinTempo.SetRange(-10, 10);
m_SliderTempo.SetLineSize(1);
m_SliderTempo.SetPageSize(10);
m_EditTempo.SubclassDlgItem(IDC_EDIT_TEMPO, this);
m_EditTempo.AllowNegative(false);
m_editsLocked = false;
UpdateView(GeneralHint().ModType());
OnActivatePage(0);
m_bInitialized = TRUE;
return FALSE;
}
CRuntimeClass *CCtrlGeneral::GetAssociatedViewClass()
{
return RUNTIME_CLASS(CViewGlobals);
}
void CCtrlGeneral::RecalcLayout()
{
}
void CCtrlGeneral::OnActivatePage(LPARAM)
{
m_modDoc.SetNotifications(Notification::Default);
m_modDoc.SetFollowWnd(m_hWnd);
PostViewMessage(VIEWMSG_SETACTIVE, NULL);
SetFocus();
// Combo boxes randomly disappear without this... why?
Invalidate();
}
void CCtrlGeneral::OnDeactivatePage()
{
m_modDoc.SetFollowWnd(NULL);
m_VuMeterLeft.SetVuMeter(0, true);
m_VuMeterRight.SetVuMeter(0, true);
m_tapTimer = nullptr; // Reset high-precision clock if required
}
TEMPO CCtrlGeneral::TempoSliderRange() const
{
return (TEMPO_SPLIT_THRESHOLD - m_tempoMin) + TEMPO((m_tempoMax - TEMPO_SPLIT_THRESHOLD).GetInt() / TEMPO_SPLIT_PRECISION, 0);
}
TEMPO CCtrlGeneral::SliderToTempo(int value) const
{
if(m_tempoMax < TEMPO_SPLIT_THRESHOLD)
{
return m_tempoMax - TEMPO(value, 0);
} else
{
const auto tempoSliderSplit = TempoToSlider(TEMPO_SPLIT_THRESHOLD);
if(value <= tempoSliderSplit)
return m_tempoMax - TEMPO(value * TEMPO_SPLIT_PRECISION, 0);
else
return m_tempoMin + TempoSliderRange() - TEMPO(value, 0);
}
}
int CCtrlGeneral::TempoToSlider(TEMPO tempo) const
{
if(m_tempoMax < TEMPO_SPLIT_THRESHOLD)
{
return (m_tempoMax - tempo).GetInt();
} else
{
if(tempo < TEMPO_SPLIT_THRESHOLD)
return (TempoSliderRange() - (std::max(m_tempoMin, tempo) - m_tempoMin)).GetInt();
else
return (m_tempoMax - std::min(m_tempoMax, tempo)).GetInt() / TEMPO_SPLIT_PRECISION;
}
}
void CCtrlGeneral::OnTapTempo()
{
using TapType = decltype(m_tapTimer->Now());
static std::array<TapType, 32> tapTime;
static TapType lastTap = 0;
static uint32 numTaps = 0;
if(m_tapTimer == nullptr)
m_tapTimer = std::make_unique<Util::MultimediaClock>(1);
const uint32 now = m_tapTimer->Now();
if(now - lastTap >= 2000)
numTaps = 0;
lastTap = now;
if(static_cast<size_t>(numTaps) >= tapTime.size())
{
// Shift back the previously recorded tap history
// cppcheck false-positive
// cppcheck-suppress mismatchingContainers
std::copy(tapTime.begin() + 1, tapTime.end(), tapTime.begin());
numTaps = static_cast<uint32>(tapTime.size() - 1);
}
tapTime[numTaps++] = now;
if(numTaps <= 1)
return;
// Now apply least squares to tap history
double sum = 0.0, weightedSum = 0.0;
for(uint32 i = 0; i < numTaps; i++)
{
const double tapMs = tapTime[i] / 1000.0;
sum += tapMs;
weightedSum += i * tapMs;
}
const double lengthSum = numTaps * (numTaps - 1) / 2;
const double lengthSumSum = lengthSum * (2 * numTaps - 1) / 3.0;
const double secondsPerBeat = (numTaps * weightedSum - lengthSum * sum) / (lengthSumSum * numTaps - lengthSum * lengthSum);
double newTempo = 60.0 / secondsPerBeat;
if(m_sndFile.m_nTempoMode != TempoMode::Modern)
newTempo *= (m_sndFile.m_nDefaultSpeed * m_sndFile.m_nDefaultRowsPerBeat) / 24.0;
if(!m_sndFile.GetModSpecifications().hasFractionalTempo)
newTempo = std::round(newTempo);
TEMPO t(newTempo);
Limit(t, m_tempoMin, m_tempoMax);
m_EditTempo.SetTempoValue(t);
}
void CCtrlGeneral::UpdateView(UpdateHint hint, CObject *pHint)
{
if (pHint == this) return;
FlagSet<HintType> hintType = hint.GetType();
const bool updateAll = hintType[HINT_MODTYPE];
const auto resamplingModes = Resampling::AllModes();
if (hintType == HINT_MPTOPTIONS || updateAll)
{
CString defaultResampler;
if(m_sndFile.m_SongFlags[SONG_ISAMIGA] && TrackerSettings::Instance().ResamplerEmulateAmiga != Resampling::AmigaFilter::Off)
defaultResampler = _T("Amiga Resampler");
else
defaultResampler = CTrackApp::GetResamplingModeName(TrackerSettings::Instance().ResamplerMode, 1, false);
m_CbnResampling.ResetContent();
m_CbnResampling.SetItemData(m_CbnResampling.AddString(_T("Default (") + defaultResampler + _T(")")), SRCMODE_DEFAULT);
for(auto mode : resamplingModes)
{
m_CbnResampling.SetItemData(m_CbnResampling.AddString(CTrackApp::GetResamplingModeName(mode, 2, true)), mode);
}
m_CbnResampling.Invalidate(FALSE);
}
if(updateAll)
{
const auto &specs = m_sndFile.GetModSpecifications();
// S3M HACK: ST3 will ignore speed 255, even though it can be used with Axx.
if(m_sndFile.GetType() == MOD_TYPE_S3M)
m_SpinSpeed.SetRange32(1, 254);
else
m_SpinSpeed.SetRange32(specs.speedMin, specs.speedMax);
m_tempoMin = specs.GetTempoMin();
m_tempoMax = specs.GetTempoMax();
// IT Hack: There are legacy OpenMPT-made ITs out there which use a higher default speed than 255.
// Changing the upper tempo limit in the mod specs would break them, so do it here instead.
if(m_sndFile.GetType() == MOD_TYPE_IT && m_sndFile.m_nDefaultTempo <= TEMPO(255, 0))
m_tempoMax.Set(255);
// Lower resolution for BPM above 256
if(m_tempoMax >= TEMPO_SPLIT_THRESHOLD)
m_SliderTempo.SetRange(0, TempoSliderRange().GetInt());
else
m_SliderTempo.SetRange(0, m_tempoMax.GetInt() - m_tempoMin.GetInt());
m_EditTempo.AllowFractions(specs.hasFractionalTempo);
const BOOL bIsNotMOD = (m_sndFile.GetType() != MOD_TYPE_MOD);
const BOOL bIsNotMOD_XM = ((bIsNotMOD) && (m_sndFile.GetType() != MOD_TYPE_XM));
m_EditArtist.EnableWindow(specs.hasArtistName);
m_EditTempo.EnableWindow(bIsNotMOD);
m_SpinTempo.EnableWindow(bIsNotMOD);
GetDlgItem(IDC_BUTTON1)->EnableWindow(bIsNotMOD);
m_SliderTempo.EnableWindow(bIsNotMOD);
m_EditSpeed.EnableWindow(bIsNotMOD);
m_SpinSpeed.EnableWindow(bIsNotMOD);
const BOOL globalVol = bIsNotMOD_XM || m_sndFile.m_nDefaultGlobalVolume != MAX_GLOBAL_VOLUME;
m_SliderGlobalVol.EnableWindow(globalVol);
m_EditGlobalVol.EnableWindow(globalVol);
m_SpinGlobalVol.EnableWindow(globalVol);
m_EditSamplePA.EnableWindow(bIsNotMOD);
m_SpinSamplePA.EnableWindow(bIsNotMOD);
m_SliderVSTiVol.EnableWindow(bIsNotMOD);
m_EditVSTiVol.EnableWindow(bIsNotMOD);
m_SpinVSTiVol.EnableWindow(bIsNotMOD);
m_EditRestartPos.EnableWindow((specs.hasRestartPos || m_sndFile.Order().GetRestartPos() != 0));
m_SpinRestartPos.EnableWindow(m_EditRestartPos.IsWindowEnabled());
//Note: Sample volume slider is not disabled for MOD
//on purpose (can be used to control play volume)
}
if(updateAll || (hint.GetCategory() == HINTCAT_GLOBAL && hintType[HINT_MODCHANNELS]))
{
// MOD Type
mpt::ustring modType;
switch(m_sndFile.GetType())
{
case MOD_TYPE_MOD: modType = U_("MOD (ProTracker)"); break;
case MOD_TYPE_S3M: modType = U_("S3M (Scream Tracker)"); break;
case MOD_TYPE_XM: modType = U_("XM (FastTracker 2)"); break;
case MOD_TYPE_IT: modType = U_("IT (Impulse Tracker)"); break;
case MOD_TYPE_MPT: modType = U_("MPTM (OpenMPT)"); break;
default: modType = MPT_UFORMAT("{} ({})")(mpt::ToUpperCase(m_sndFile.m_modFormat.type), m_sndFile.m_modFormat.formatName); break;
}
CString s;
s.Format(_T("%s, %u channel%s"), mpt::ToCString(modType).GetString(), m_sndFile.GetNumChannels(), (m_sndFile.GetNumChannels() != 1) ? _T("s") : _T(""));
m_BtnModType.SetWindowText(s);
}
if (updateAll || (hint.GetCategory() == HINTCAT_SEQUENCE && hintType[HINT_MODSEQUENCE | HINT_RESTARTPOS]))
{
// Set max valid restart position
m_SpinRestartPos.SetRange32(0, std::max(m_sndFile.Order().GetRestartPos(), static_cast<ORDERINDEX>(m_sndFile.Order().GetLengthTailTrimmed() - 1)));
SetDlgItemInt(IDC_EDIT_RESTARTPOS, m_sndFile.Order().GetRestartPos(), FALSE);
}
if (updateAll || (hint.GetCategory() == HINTCAT_GENERAL && hintType[HINT_MODGENERAL]))
{
if (!m_editsLocked)
{
m_EditTitle.SetWindowText(mpt::ToCString(m_sndFile.GetCharsetInternal(), m_sndFile.GetTitle()));
m_EditArtist.SetWindowText(mpt::ToCString(m_sndFile.m_songArtist));
m_EditTempo.SetTempoValue(m_sndFile.m_nDefaultTempo);
SetDlgItemInt(IDC_EDIT_SPEED, m_sndFile.m_nDefaultSpeed, FALSE);
SetDlgItemInt(IDC_EDIT_GLOBALVOL, m_sndFile.m_nDefaultGlobalVolume / GetGlobalVolumeFactor(), FALSE);
SetDlgItemInt(IDC_EDIT_VSTIVOL, m_sndFile.m_nVSTiVolume, FALSE);
SetDlgItemInt(IDC_EDIT_SAMPLEPA, m_sndFile.m_nSamplePreAmp, FALSE);
}
m_SliderGlobalVol.SetPos(MAX_SLIDER_GLOBAL_VOL - m_sndFile.m_nDefaultGlobalVolume);
m_SliderVSTiVol.SetPos(MAX_SLIDER_VSTI_VOL - m_sndFile.m_nVSTiVolume);
m_SliderSamplePreAmp.SetPos(MAX_SLIDER_SAMPLE_VOL - m_sndFile.m_nSamplePreAmp);
m_SliderTempo.SetPos(TempoToSlider(m_sndFile.m_nDefaultTempo));
}
if(updateAll || hintType == HINT_MPTOPTIONS || (hint.GetCategory() == HINTCAT_GENERAL && hintType[HINT_MODGENERAL]))
{
for(int i = 0; i < m_CbnResampling.GetCount(); ++i)
{
if(m_sndFile.m_nResampling == static_cast<ResamplingMode>(m_CbnResampling.GetItemData(i)))
{
m_CbnResampling.SetCurSel(i);
break;
}
}
}
CheckDlgButton(IDC_CHECK_LOOPSONG, (TrackerSettings::Instance().gbLoopSong) ? TRUE : FALSE);
if (hintType[HINT_MPTOPTIONS])
{
m_VuMeterLeft.InvalidateRect(NULL, FALSE);
m_VuMeterRight.InvalidateRect(NULL, FALSE);
}
}
void CCtrlGeneral::OnVScroll(UINT code, UINT pos, CScrollBar *pscroll)
{
CDialog::OnVScroll(code, pos, pscroll);
if (m_bInitialized)
{
CSliderCtrl* pSlider = (CSliderCtrl*) pscroll;
if (pSlider == &m_SliderTempo)
{
const TEMPO tempo = SliderToTempo(m_SliderTempo.GetPos());
if ((tempo >= m_sndFile.GetModSpecifications().GetTempoMin()) && (tempo <= m_sndFile.GetModSpecifications().GetTempoMax()) && (tempo != m_sndFile.m_nDefaultTempo))
{
m_sndFile.m_nDefaultTempo = m_sndFile.m_PlayState.m_nMusicTempo = tempo;
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
m_EditTempo.SetTempoValue(tempo);
}
}
else if (pSlider == &m_SliderGlobalVol)
{
const UINT gv = MAX_SLIDER_GLOBAL_VOL - m_SliderGlobalVol.GetPos();
if ((gv >= 0) && (gv <= MAX_SLIDER_GLOBAL_VOL) && (gv != m_sndFile.m_nDefaultGlobalVolume))
{
m_sndFile.m_PlayState.m_nGlobalVolume = gv;
m_sndFile.m_nDefaultGlobalVolume = gv;
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
SetDlgItemInt(IDC_EDIT_GLOBALVOL, m_sndFile.m_nDefaultGlobalVolume / GetGlobalVolumeFactor(), FALSE);
}
}
else if (pSlider == &m_SliderSamplePreAmp)
{
const UINT spa = MAX_SLIDER_SAMPLE_VOL - m_SliderSamplePreAmp.GetPos();
if ((spa >= 0) && (spa <= MAX_SLIDER_SAMPLE_VOL) && (spa != m_sndFile.m_nSamplePreAmp))
{
m_sndFile.m_nSamplePreAmp = spa;
if(m_sndFile.GetType() != MOD_TYPE_MOD)
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
SetDlgItemInt(IDC_EDIT_SAMPLEPA, m_sndFile.m_nSamplePreAmp, FALSE);
}
}
else if (pSlider == &m_SliderVSTiVol)
{
const UINT vv = MAX_SLIDER_VSTI_VOL - m_SliderVSTiVol.GetPos();
if ((vv >= 0) && (vv <= MAX_SLIDER_VSTI_VOL) && (vv != m_sndFile.m_nVSTiVolume))
{
m_sndFile.m_nVSTiVolume = vv;
m_sndFile.RecalculateGainForAllPlugs();
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
SetDlgItemInt(IDC_EDIT_VSTIVOL, m_sndFile.m_nVSTiVolume, FALSE);
}
}
else if(pSlider == (CSliderCtrl*)&m_SpinTempo)
{
int pos32 = m_SpinTempo.GetPos32();
if(pos32 != 0)
{
TEMPO newTempo;
if(m_sndFile.GetModSpecifications().hasFractionalTempo)
{
pos32 *= TEMPO::fractFact;
if(CMainFrame::GetMainFrame()->GetInputHandler()->CtrlPressed())
pos32 /= 100;
else if(CMainFrame::GetMainFrame()->GetInputHandler()->ShiftPressed())
pos32 /= 10;
newTempo.SetRaw(pos32);
} else
{
newTempo = TEMPO(pos32, 0);
}
newTempo += m_sndFile.m_nDefaultTempo;
Limit(newTempo, m_tempoMin, m_tempoMax);
m_sndFile.m_nDefaultTempo = m_sndFile.m_PlayState.m_nMusicTempo = newTempo;
m_modDoc.SetModified();
LockControls();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
UnlockControls();
m_SliderTempo.SetPos(TempoToSlider(newTempo));
m_EditTempo.SetTempoValue(newTempo);
}
m_SpinTempo.SetPos(0);
}
}
}
void CCtrlGeneral::OnTitleChanged()
{
if (!m_EditTitle.m_hWnd || !m_EditTitle.GetModify()) return;
CString title;
m_EditTitle.GetWindowText(title);
if(m_sndFile.SetTitle(mpt::ToCharset(m_sndFile.GetCharsetInternal(), title)))
{
m_EditTitle.SetModify(FALSE);
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
}
}
void CCtrlGeneral::OnArtistChanged()
{
if (!m_EditArtist.m_hWnd || !m_EditArtist.GetModify()) return;
mpt::ustring artist = GetWindowTextUnicode(m_EditArtist);
if(artist != m_sndFile.m_songArtist)
{
m_EditArtist.SetModify(FALSE);
m_sndFile.m_songArtist = artist;
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(NULL, GeneralHint().General(), this);
}
}
void CCtrlGeneral::OnTempoChanged()
{
if (m_bInitialized && m_EditTempo.GetWindowTextLength() > 0)
{
TEMPO tempo = m_EditTempo.GetTempoValue();
Limit(tempo, m_tempoMin, m_tempoMax);
if(!m_sndFile.GetModSpecifications().hasFractionalTempo) tempo.Set(tempo.GetInt());
if (tempo != m_sndFile.m_nDefaultTempo)
{
m_editsLocked = true;
m_EditTempo.SetModify(FALSE);
m_sndFile.m_nDefaultTempo = tempo;
m_sndFile.m_PlayState.m_nMusicTempo = tempo;
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General());
m_editsLocked = false;
}
}
}
void CCtrlGeneral::OnSpeedChanged()
{
TCHAR s[16];
if(m_bInitialized)
{
m_EditSpeed.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
if (s[0])
{
UINT n = ConvertStrTo<UINT>(s);
n = Clamp(n, m_sndFile.GetModSpecifications().speedMin, m_sndFile.GetModSpecifications().speedMax);
if (n != m_sndFile.m_nDefaultSpeed)
{
m_editsLocked = true;
m_EditSpeed.SetModify(FALSE);
m_sndFile.m_nDefaultSpeed = n;
m_sndFile.m_PlayState.m_nMusicSpeed = n;
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
// Update envelope grid view
m_modDoc.UpdateAllViews(nullptr, InstrumentHint().Envelope(), this);
m_editsLocked = false;
}
}
}
}
void CCtrlGeneral::OnVSTiVolChanged()
{
TCHAR s[16];
if (m_bInitialized)
{
m_EditVSTiVol.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
if (s[0])
{
UINT n = ConvertStrTo<UINT>(s);
Limit(n, 0u, 2000u);
if (n != m_sndFile.m_nVSTiVolume)
{
m_editsLocked = true;
m_sndFile.m_nVSTiVolume = n;
m_sndFile.RecalculateGainForAllPlugs();
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
UpdateView(GeneralHint().General());
m_editsLocked = false;
}
}
}
}
void CCtrlGeneral::OnSamplePAChanged()
{
TCHAR s[16];
if(m_bInitialized)
{
m_EditSamplePA.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
if (s[0])
{
UINT n = ConvertStrTo<UINT>(s);
Limit(n, 0u, 2000u);
if (n != m_sndFile.m_nSamplePreAmp)
{
m_editsLocked = true;
m_sndFile.m_nSamplePreAmp = n;
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
UpdateView(GeneralHint().General());
m_editsLocked = false;
}
}
}
}
void CCtrlGeneral::OnGlobalVolChanged()
{
TCHAR s[16];
if(m_bInitialized)
{
m_EditGlobalVol.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
if (s[0])
{
UINT n = ConvertStrTo<ORDERINDEX>(s) * GetGlobalVolumeFactor();
Limit(n, 0u, 256u);
if (n != m_sndFile.m_nDefaultGlobalVolume)
{
m_editsLocked = true;
m_EditGlobalVol.SetModify(FALSE);
m_sndFile.m_nDefaultGlobalVolume = n;
m_sndFile.m_PlayState.m_nGlobalVolume = n;
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
UpdateView(GeneralHint().General());
m_editsLocked = false;
}
}
}
}
void CCtrlGeneral::OnRestartPosChanged()
{
if(!m_bInitialized)
return;
TCHAR s[32];
m_EditRestartPos.GetWindowText(s, mpt::saturate_cast<int>(std::size(s)));
if(!s[0])
return;
ORDERINDEX n = ConvertStrTo<ORDERINDEX>(s);
LimitMax(n, m_sndFile.Order().GetLastIndex());
while(n > 0 && n < m_sndFile.Order().GetLastIndex() && !m_sndFile.Order().IsValidPat(n))
n++;
if(n == m_sndFile.Order().GetRestartPos())
return;
m_EditRestartPos.SetModify(FALSE);
m_sndFile.Order().SetRestartPos(n);
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, SequenceHint(m_sndFile.Order.GetCurrentSequenceIndex()).RestartPos(), this);
}
void CCtrlGeneral::OnRestartPosDone()
{
if(m_bInitialized)
SetDlgItemInt(IDC_EDIT_RESTARTPOS, m_sndFile.Order().GetRestartPos());
}
void CCtrlGeneral::OnSongProperties()
{
m_modDoc.OnSongProperties();
}
void CCtrlGeneral::OnLoopSongChanged()
{
m_modDoc.SetLoopSong(IsDlgButtonChecked(IDC_CHECK_LOOPSONG) != BST_UNCHECKED);
}
LRESULT CCtrlGeneral::OnUpdatePosition(WPARAM, LPARAM lParam)
{
Notification *pnotify = (Notification *)lParam;
if (pnotify)
{
m_VuMeterLeft.SetVuMeter(pnotify->masterVUout[0] & (~Notification::ClipVU), pnotify->type[Notification::Stop]);
m_VuMeterRight.SetVuMeter(pnotify->masterVUout[1] & (~Notification::ClipVU), pnotify->type[Notification::Stop]);
}
return 0;
}
BOOL CCtrlGeneral::GetToolTipText(UINT uId, LPTSTR pszText)
{
const TCHAR moreRecentMixModeNote[] = _T("Use a more recent mixmode to see dB offsets.");
if ((pszText) && (uId))
{
const bool displayDBValues = m_sndFile.GetPlayConfig().getDisplayDBValues();
const CWnd *wnd = GetDlgItem(uId);
const bool isEnabled = wnd ? (wnd->IsWindowEnabled() != FALSE) : true; // nullptr check is for a Wine bug workaround (https://bugs.openmpt.org/view.php?id=1553)
mpt::tstring notAvailable;
if(!isEnabled)
notAvailable = MPT_TFORMAT("Feature is not available in the {} format.")(mpt::ToWin(mpt::Charset::ASCII, mpt::ToUpperCaseAscii(m_sndFile.GetModSpecifications().fileExtension)));
switch(uId)
{
case IDC_BUTTON_MODTYPE:
_tcscpy(pszText, _T("Song Properties"));
{
const auto keyText = CMainFrame::GetInputHandler()->m_activeCommandSet->GetKeyTextFromCommand(kcViewSongProperties, 0);
if (!keyText.IsEmpty())
_tcscat(pszText, MPT_TFORMAT(" ({})")(keyText).c_str());
}
return TRUE;
case IDC_BUTTON1:
if(isEnabled)
_tcscpy(pszText, _T("Click button multiple times to tap in the desired tempo."));
else
_tcscpy(pszText, notAvailable.c_str());
return TRUE;
case IDC_SLIDER_SAMPLEPREAMP:
_tcscpy(pszText, displayDBValues ? CModDoc::LinearToDecibels(m_sndFile.m_nSamplePreAmp, m_sndFile.GetPlayConfig().getNormalSamplePreAmp()).GetString() : moreRecentMixModeNote);
return TRUE;
case IDC_SLIDER_VSTIVOL:
if(isEnabled)
_tcscpy(pszText, displayDBValues ? CModDoc::LinearToDecibels(m_sndFile.m_nVSTiVolume, m_sndFile.GetPlayConfig().getNormalVSTiVol()).GetString() : moreRecentMixModeNote);
else
_tcscpy(pszText, notAvailable.c_str());
return TRUE;
case IDC_SLIDER_GLOBALVOL:
if(isEnabled)
_tcscpy(pszText, displayDBValues ? CModDoc::LinearToDecibels(m_sndFile.m_PlayState.m_nGlobalVolume, m_sndFile.GetPlayConfig().getNormalGlobalVol()).GetString() : moreRecentMixModeNote);
else
_tcscpy(pszText, notAvailable.c_str());
return TRUE;
case IDC_SLIDER_SONGTEMPO:
case IDC_EDIT_ARTIST:
case IDC_EDIT_TEMPO:
case IDC_EDIT_SPEED:
case IDC_EDIT_RESTARTPOS:
case IDC_EDIT_GLOBALVOL:
case IDC_EDIT_VSTIVOL:
if(isEnabled)
break;
_tcscpy(pszText, notAvailable.c_str());
return TRUE;
}
}
return FALSE;
}
void CCtrlGeneral::OnEnSetfocusEditSongtitle()
{
m_EditTitle.SetLimitText(m_sndFile.GetModSpecifications().modNameLengthMax);
}
void CCtrlGeneral::OnResamplingChanged()
{
int sel = m_CbnResampling.GetCurSel();
if(sel >= 0)
{
m_sndFile.m_nResampling = static_cast<ResamplingMode>(m_CbnResampling.GetItemData(sel));
if(m_sndFile.GetModSpecifications().hasDefaultResampling)
{
m_modDoc.SetModified();
m_modDoc.UpdateAllViews(nullptr, GeneralHint().General(), this);
}
}
}
////////////////////////////////////////////////////////////////////////////////
//
// CVuMeter
//
BEGIN_MESSAGE_MAP(CVuMeter, CWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
void CVuMeter::OnPaint()
{
CRect rect;
CPaintDC dc(this);
GetClientRect(&rect);
dc.FillSolidRect(rect.left, rect.top, rect.Width(), rect.Height(), RGB(0,0,0));
m_lastDisplayedLevel = -1;
DrawVuMeter(dc, true);
}
void CVuMeter::SetVuMeter(int level, bool force)
{
level >>= 8;
if (level != m_lastLevel)
{
DWORD curTime = timeGetTime();
if(curTime - m_lastVuUpdateTime >= TrackerSettings::Instance().VuMeterUpdateInterval || force)
{
m_lastLevel = level;
CClientDC dc(this);
DrawVuMeter(dc);
m_lastVuUpdateTime = curTime;
}
}
}
void CVuMeter::DrawVuMeter(CDC &dc, bool /*redraw*/)
{
CRect rect;
GetClientRect(&rect);
int vu = (m_lastLevel * (rect.bottom-rect.top)) >> 8;
int cy = rect.bottom - rect.top;
if (cy < 1) cy = 1;
for (int ry=rect.bottom-1; ry>rect.top; ry-=2)
{
int y0 = rect.bottom - ry;
int n = Clamp((y0 * NUM_VUMETER_PENS) / cy, 0, NUM_VUMETER_PENS - 1);
if (vu < y0)
n += NUM_VUMETER_PENS;
dc.FillSolidRect(rect.left, ry, rect.Width(), 1, CMainFrame::gcolrefVuMeter[n]);
}
m_lastDisplayedLevel = m_lastLevel;
}
OPENMPT_NAMESPACE_END