817 lines
25 KiB
C++
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
|