From: <sag...@us...> - 2015-06-13 12:47:50
|
Revision: 5300 http://sourceforge.net/p/modplug/code/5300 Author: saga-games Date: 2015-06-13 12:47:42 +0000 (Sat, 13 Jun 2015) Log Message: ----------- [New] MPTM: In modern tempo mode, a swing factor can now be configured which determines how much every row of a beat contributes to the beat. This is done by increasing or decreasing the length of each tick as required. [Mod] OpenMPT: Version is now 1.25.00.14 Modified Paths: -------------- trunk/OpenMPT/common/versionNumber.h trunk/OpenMPT/mptrack/AppendModule.cpp trunk/OpenMPT/mptrack/PatternEditorDialogs.cpp trunk/OpenMPT/mptrack/PatternEditorDialogs.h trunk/OpenMPT/mptrack/dlg_misc.cpp trunk/OpenMPT/mptrack/dlg_misc.h trunk/OpenMPT/mptrack/mptrack.rc trunk/OpenMPT/mptrack/resource.h trunk/OpenMPT/soundlib/Load_it.cpp trunk/OpenMPT/soundlib/Snd_defs.h trunk/OpenMPT/soundlib/Sndfile.cpp trunk/OpenMPT/soundlib/Sndfile.h trunk/OpenMPT/soundlib/SoundFilePlayConfig.h trunk/OpenMPT/soundlib/pattern.cpp trunk/OpenMPT/soundlib/pattern.h trunk/OpenMPT/soundlib/patternContainer.cpp Modified: trunk/OpenMPT/common/versionNumber.h =================================================================== --- trunk/OpenMPT/common/versionNumber.h 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/common/versionNumber.h 2015-06-13 12:47:42 UTC (rev 5300) @@ -19,7 +19,7 @@ #define VER_MAJORMAJOR 1 #define VER_MAJOR 25 #define VER_MINOR 00 -#define VER_MINORMINOR 13 +#define VER_MINORMINOR 14 //Version string. For example "1.17.02.28" #define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR) Modified: trunk/OpenMPT/mptrack/AppendModule.cpp =================================================================== --- trunk/OpenMPT/mptrack/AppendModule.cpp 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/mptrack/AppendModule.cpp 2015-06-13 12:47:42 UTC (rev 5300) @@ -168,6 +168,7 @@ { SEQUENCEINDEX newSeq = m_SndFile.Order.AddSequence(false); m_SndFile.Order.SetSequence(newSeq); + m_SndFile.Order.GetSequence(newSeq).SetName(source.Order.GetSequence(seq).GetName()); insertPos = 0; } else { @@ -264,6 +265,18 @@ targetPat.SetSignature(source.m_nDefaultRowsPerBeat, source.m_nDefaultRowsPerMeasure); } } + if(m_SndFile.m_nTempoMode == tempoModeModern) + { + // Swing only works in modern tempo mode + if(sourcePat.HasTempoSwing()) + { + targetPat.SetTempoSwing(sourcePat.GetTempoSwing()); + } else if(source.m_tempoSwing != m_SndFile.m_tempoSwing) + { + // Try fixing differing swing settings by copying them to the newly created patterns + targetPat.SetTempoSwing(source.m_tempoSwing); + } + } const ROWINDEX copyRows = std::min(sourcePat.GetNumRows(), targetPat.GetNumRows()); for(ROWINDEX row = 0; row < copyRows; row++) Modified: trunk/OpenMPT/mptrack/PatternEditorDialogs.cpp =================================================================== --- trunk/OpenMPT/mptrack/PatternEditorDialogs.cpp 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/mptrack/PatternEditorDialogs.cpp 2015-06-13 12:47:42 UTC (rev 5300) @@ -39,7 +39,7 @@ while(nCmdRow >= 0) { const ModCommand *m = sndFile.Patterns[nPat].GetpModCommand(nCmdRow, nChannel); - if(m->command == CMD_OFFSET || m->command == CMD_PATTERNBREAK || m->command == CMD_PATTERNBREAK) + if(m->command == CMD_OFFSET || m->command == CMD_PATTERNBREAK || m->command == CMD_TEMPO) break; if(m->command != CMD_XPARAM) { @@ -470,6 +470,7 @@ ON_COMMAND(IDC_BUTTON_HALF, OnHalfRowNumber) ON_COMMAND(IDC_BUTTON_DOUBLE, OnDoubleRowNumber) ON_COMMAND(IDC_CHECK1, OnOverrideSignature) + ON_COMMAND(IDC_BUTTON1, OnTempoSwing) END_MESSAGE_MAP() BOOL CPatternPropertiesDlg::OnInitDialog() @@ -483,7 +484,8 @@ if(m_nPattern < sndFile.Patterns.Size() && combo) { TCHAR s[256]; - UINT nrows = sndFile.Patterns[m_nPattern].GetNumRows(); + const CPattern &pattern = sndFile.Patterns[m_nPattern]; + ROWINDEX nrows = pattern.GetNumRows(); const CModSpecifications& specs = sndFile.GetModSpecifications(); combo->SetRedraw(FALSE); @@ -496,13 +498,13 @@ combo->SetRedraw(TRUE); _stprintf(s, _T("Pattern #%d: %d row%s (%dK)"), m_nPattern, - sndFile.Patterns[m_nPattern].GetNumRows(), - (sndFile.Patterns[m_nPattern].GetNumRows() == 1) ? _T("") : _T("s"), - (sndFile.Patterns[m_nPattern].GetNumRows() * sndFile.GetNumChannels() * sizeof(ModCommand)) / 1024); + pattern.GetNumRows(), + (pattern.GetNumRows() == 1) ? _T("") : _T("s"), + (pattern.GetNumRows() * sndFile.GetNumChannels() * sizeof(ModCommand)) / 1024); SetDlgItemText(IDC_TEXT1, s); // Window title - const CString patternName = sndFile.Patterns[m_nPattern].GetName().c_str(); + const CString patternName = pattern.GetName().c_str(); _stprintf(s, _T("Pattern Properties for Pattern #%d"), m_nPattern); if(!patternName.IsEmpty()) { @@ -513,15 +515,17 @@ SetWindowText(s); // pattern time signature - const bool bOverride = sndFile.Patterns[m_nPattern].GetOverrideSignature(); - UINT nRPB = sndFile.Patterns[m_nPattern].GetRowsPerBeat(), nRPM = sndFile.Patterns[m_nPattern].GetRowsPerMeasure(); + const bool bOverride = pattern.GetOverrideSignature(); + ROWINDEX nRPB = pattern.GetRowsPerBeat(), nRPM = pattern.GetRowsPerMeasure(); if(nRPB == 0 || !bOverride) nRPB = sndFile.m_nDefaultRowsPerBeat; if(nRPM == 0 || !bOverride) nRPM = sndFile.m_nDefaultRowsPerMeasure; + m_tempoSwing = pattern.HasTempoSwing() ? pattern.GetTempoSwing() : sndFile.m_tempoSwing; + GetDlgItem(IDC_CHECK1)->EnableWindow(sndFile.GetModSpecifications().hasPatternSignatures ? TRUE : FALSE); CheckDlgButton(IDC_CHECK1, bOverride ? MF_CHECKED : MF_UNCHECKED); - SetDlgItemInt(IDC_EDIT_ROWSPERBEAT, nRPB, FALSE); - SetDlgItemInt(IDC_EDIT_ROWSPERMEASURE, nRPM, FALSE); + SetDlgItemInt(IDC_ROWSPERBEAT, nRPB, FALSE); + SetDlgItemInt(IDC_ROWSPERMEASURE, nRPM, FALSE); OnOverrideSignature(); } return TRUE; @@ -556,41 +560,59 @@ void CPatternPropertiesDlg::OnOverrideSignature() //----------------------------------------------- -{ - GetDlgItem(IDC_EDIT_ROWSPERBEAT)->EnableWindow(IsDlgButtonChecked(IDC_CHECK1)); - GetDlgItem(IDC_EDIT_ROWSPERMEASURE)->EnableWindow(IsDlgButtonChecked(IDC_CHECK1)); +{ + GetDlgItem(IDC_ROWSPERBEAT)->EnableWindow(IsDlgButtonChecked(IDC_CHECK1)); + GetDlgItem(IDC_ROWSPERMEASURE)->EnableWindow(IsDlgButtonChecked(IDC_CHECK1)); + GetDlgItem(IDC_BUTTON1)->EnableWindow(IsDlgButtonChecked(IDC_CHECK1) && modDoc.GetrSoundFile().m_nTempoMode == tempoModeModern); } +void CPatternPropertiesDlg::OnTempoSwing() +//---------------------------------------- +{ + m_tempoSwing.resize(GetDlgItemInt(IDC_ROWSPERBEAT), TempoSwing::Unity); + CTempoSwingDlg dlg(this, m_tempoSwing); + if(dlg.DoModal() == IDOK) + { + m_tempoSwing = dlg.m_tempoSwing; + } +} + + void CPatternPropertiesDlg::OnOK() //-------------------------------- { CSoundFile &sndFile = modDoc.GetrSoundFile(); + CPattern &pattern = sndFile.Patterns[m_nPattern]; // Update pattern signature if necessary if(sndFile.GetModSpecifications().hasPatternSignatures) { if(IsDlgButtonChecked(IDC_CHECK1)) // Enable signature { - ROWINDEX nNewBeat = (ROWINDEX)GetDlgItemInt(IDC_EDIT_ROWSPERBEAT, NULL, FALSE), nNewMeasure = (ROWINDEX)GetDlgItemInt(IDC_EDIT_ROWSPERMEASURE, NULL, FALSE); - if(nNewBeat != sndFile.Patterns[m_nPattern].GetRowsPerBeat() || nNewMeasure != sndFile.Patterns[m_nPattern].GetRowsPerMeasure()) + ROWINDEX nNewBeat = (ROWINDEX)GetDlgItemInt(IDC_ROWSPERBEAT, NULL, FALSE), nNewMeasure = (ROWINDEX)GetDlgItemInt(IDC_ROWSPERMEASURE, NULL, FALSE); + if(nNewBeat != pattern.GetRowsPerBeat() || nNewMeasure != pattern.GetRowsPerMeasure() || m_tempoSwing != pattern.GetTempoSwing()) { - if(!sndFile.Patterns[m_nPattern].SetSignature(nNewBeat, nNewMeasure)) + if(!pattern.SetSignature(nNewBeat, nNewMeasure)) { Reporting::Error("Invalid time signature!", "Pattern Properties"); - GetDlgItem(IDC_EDIT_ROWSPERBEAT)->SetFocus(); + GetDlgItem(IDC_ROWSPERBEAT)->SetFocus(); return; } + m_tempoSwing.resize(nNewBeat, TempoSwing::Unity); + pattern.SetTempoSwing(m_tempoSwing); modDoc.SetModified(); } } else // Disable signature { - if(sndFile.Patterns[m_nPattern].GetOverrideSignature()) + if(pattern.GetOverrideSignature() || pattern.HasTempoSwing()) { - sndFile.Patterns[m_nPattern].RemoveSignature(); + pattern.RemoveSignature(); + pattern.RemoveTempoSwing(); modDoc.SetModified(); } } } + const ROWINDEX newSize = (ROWINDEX)GetDlgItemInt(IDC_COMBO1, NULL, FALSE); Modified: trunk/OpenMPT/mptrack/PatternEditorDialogs.h =================================================================== --- trunk/OpenMPT/mptrack/PatternEditorDialogs.h 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/mptrack/PatternEditorDialogs.h 2015-06-13 12:47:42 UTC (rev 5300) @@ -96,10 +96,15 @@ { protected: CModDoc &modDoc; + TempoSwing m_tempoSwing; PATTERNINDEX m_nPattern; public: - CPatternPropertiesDlg(CModDoc &modParent, PATTERNINDEX nPat, CWnd *parent=NULL):CDialog(IDD_PATTERN_PROPERTIES, parent), modDoc(modParent), m_nPattern(nPat) { } + CPatternPropertiesDlg(CModDoc &modParent, PATTERNINDEX nPat, CWnd *parent=NULL) + : CDialog(IDD_PATTERN_PROPERTIES, parent) + , modDoc(modParent) + , m_nPattern(nPat) + { } protected: virtual BOOL OnInitDialog(); @@ -107,6 +112,7 @@ afx_msg void OnHalfRowNumber(); afx_msg void OnDoubleRowNumber(); afx_msg void OnOverrideSignature(); + afx_msg void OnTempoSwing(); DECLARE_MESSAGE_MAP() }; Modified: trunk/OpenMPT/mptrack/dlg_misc.cpp =================================================================== --- trunk/OpenMPT/mptrack/dlg_misc.cpp 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/mptrack/dlg_misc.cpp 2015-06-13 12:47:42 UTC (rev 5300) @@ -31,8 +31,10 @@ BEGIN_MESSAGE_MAP(CModTypeDlg, CDialog) //{{AFX_MSG_MAP(CModTypeDlg) - ON_CBN_SELCHANGE(IDC_COMBO1, UpdateDialog) - ON_COMMAND(IDC_CHECK_PT1X, OnPTModeChanged) + ON_CBN_SELCHANGE(IDC_COMBO1, UpdateDialog) + ON_CBN_SELCHANGE(IDC_COMBO_TEMPOMODE, OnTempoModeChanged) + ON_COMMAND(IDC_CHECK_PT1X, OnPTModeChanged) + ON_COMMAND(IDC_BUTTON1, OnTempoSwing) ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipNotify) @@ -69,6 +71,7 @@ CDialog::OnInitDialog(); m_nType = sndFile.GetType(); m_nChannels = sndFile.GetNumChannels(); + m_tempoSwing = sndFile.m_tempoSwing; initialized = false; // Mod types @@ -220,6 +223,7 @@ break; } } + OnTempoModeChanged(); // Mix levels const MixLevels oldMixLevels = initialized ? static_cast<MixLevels>(m_PlugMixBox.GetItemData(m_PlugMixBox.GetCurSel())) : sndFile.GetMixLevels(); @@ -270,6 +274,25 @@ } +void CModTypeDlg::OnTempoModeChanged() +//------------------------------------ +{ + GetDlgItem(IDC_BUTTON1)->EnableWindow(m_TempoModeBox.GetItemData(m_TempoModeBox.GetCurSel()) == tempoModeModern); +} + + +void CModTypeDlg::OnTempoSwing() +//------------------------------ +{ + m_tempoSwing.resize(GetDlgItemInt(IDC_ROWSPERBEAT), TempoSwing::Unity); + CTempoSwingDlg dlg(this, m_tempoSwing); + if(dlg.DoModal() == IDOK) + { + m_tempoSwing = dlg.m_tempoSwing; + } +} + + bool CModTypeDlg::VerifyData() //---------------------------- { @@ -332,11 +355,23 @@ m_nChannels = static_cast<CHANNELINDEX>(m_ChannelsBox.GetItemData(sel)); } + sndFile.m_nDefaultRowsPerBeat = GetDlgItemInt(IDC_ROWSPERBEAT); + sndFile.m_nDefaultRowsPerMeasure = GetDlgItemInt(IDC_ROWSPERMEASURE); + sel = m_TempoModeBox.GetCurSel(); if (sel >= 0) { sndFile.m_nTempoMode = static_cast<TempoMode>(m_TempoModeBox.GetItemData(sel)); } + if(sndFile.m_nTempoMode == tempoModeModern) + { + sndFile.m_tempoSwing = m_tempoSwing; + sndFile.m_tempoSwing.resize(sndFile.m_nDefaultRowsPerBeat, TempoSwing::Unity); + sndFile.m_tempoSwing.Normalize(); + } else + { + sndFile.m_tempoSwing.clear(); + } sel = m_PlugMixBox.GetCurSel(); if(sel >= 0) @@ -354,9 +389,6 @@ if(IsDlgButtonChecked(IDC_CHK_FT2VOLRAMP)) sndFile.SetModFlag(MSF_VOLRAMP, true); } - sndFile.m_nDefaultRowsPerBeat = GetDlgItemInt(IDC_ROWSPERBEAT); - sndFile.m_nDefaultRowsPerMeasure = GetDlgItemInt(IDC_ROWSPERMEASURE); - if(CChannelManagerDlg::sharedInstance(FALSE) && CChannelManagerDlg::sharedInstance()->IsDisplayed()) CChannelManagerDlg::sharedInstance()->Update(); CDialog::OnOK(); @@ -479,10 +511,10 @@ if (m_nRemove > 0) { - wsprintf(label, "Select %d channel%s to remove:", m_nRemove, (m_nRemove != 1) ? "s" : ""); + wsprintf(label, "Select %u channel%s to remove:", m_nRemove, (m_nRemove != 1) ? "s" : ""); } else { - wsprintf(label, "Select channels to remove (the minimum number of remaining channels is %d)", sndFile.GetModSpecifications().channelsMin); + wsprintf(label, "Select channels to remove (the minimum number of remaining channels is %u)", sndFile.GetModSpecifications().channelsMin); } SetDlgItemText(IDC_QUESTION1, label); @@ -1160,6 +1192,186 @@ } + +///////////////////////////////////////////////////////////////////////// +// Tempo swing dialog + + +BEGIN_MESSAGE_MAP(CTempoSwingDlg, CDialog) + //{{AFX_MSG_MAP(CTempoSwingDlg) + ON_WM_HSCROLL() + ON_COMMAND(IDC_BUTTON1, OnReset) + ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipNotify) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +BOOL CTempoSwingDlg::OnInitDialog() +//----------------------------------- +{ + struct Measurements + { + enum + { + edRowLabelWidth = 64, // Label "Row 999:" + edSliderWidth = 220, // Setting slider + edSliderHeight = 20, // Setting slider + edValueLabelWidth = 64, // Label "100%" + edPaddingX = 8, // Spacing between elements + edPaddingY = 4, // Spacing between elements + edPaddingTop = 35, // Topping from top of dialog + edTotalHeight = edSliderHeight + edPaddingY, // Height of one set of controls + edFooterHeight = 32, // Buttons + }; + + const int rowLabelWidth; + const int sliderWidth; + const int sliderHeight; + const int valueLabelWidth; + const int paddingX; + const int paddingY; + const int paddingTop; + const int totalHeight; + const int footerHeight; + + Measurements(HWND hWnd) + : rowLabelWidth(Util::ScalePixels(edRowLabelWidth, hWnd)) + , sliderWidth(Util::ScalePixels(edSliderWidth, hWnd)) + , sliderHeight(Util::ScalePixels(edSliderHeight, hWnd)) + , valueLabelWidth(Util::ScalePixels(edValueLabelWidth, hWnd)) + , paddingX(Util::ScalePixels(edPaddingX, hWnd)) + , paddingY(Util::ScalePixels(edPaddingY, hWnd)) + , paddingTop(Util::ScalePixels(edPaddingTop, hWnd)) + , totalHeight(Util::ScalePixels(edTotalHeight, hWnd)) + , footerHeight(Util::ScalePixels(edFooterHeight, hWnd)) + { } + }; + + CDialog::OnInitDialog(); + Measurements m(m_hWnd); + CRect windowRect, rect; + GetWindowRect(windowRect); + GetClientRect(rect); + windowRect.bottom = windowRect.top + windowRect.Height() - rect.Height(); + rect.DeflateRect(m.paddingX, m.paddingTop, m.paddingX, 0); + m_controls.resize(m_tempoSwing.size()); + for(size_t i = 0; i < m_controls.size(); i++) + { + RowCtls *r = m_controls[i] = new RowCtls; + // Parameter name + TCHAR s[16]; + wsprintf(s, "Row %u:", i + 1); + r->rowLabel.Create(s, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, CRect(rect.left, rect.top, rect.left + m.rowLabelWidth, rect.top + m.sliderHeight), this); + r->rowLabel.SetFont(GetFont()); + + // Parameter value as reported by the plugin + r->valueLabel.Create(_T("100%"), WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, CRect(rect.right - m.valueLabelWidth, rect.top, rect.right, rect.top + m.sliderHeight), this); + r->valueLabel.SetFont(GetFont()); + + // Parameter value slider + r->valueSlider.Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_TOOLTIPS | TBS_AUTOTICKS, CRect(rect.left + m.rowLabelWidth, rect.top, rect.right - m.valueLabelWidth, rect.top + m.sliderHeight), this, ID_PLUGINEDITOR_SLIDERS_BASE + i); + r->valueSlider.SetFont(GetFont()); + r->valueSlider.SetRange(-SliderResolution / 2, SliderResolution / 2); + r->valueSlider.SetTicFreq(SliderResolution / 8); + r->valueSlider.SetPageSize(SliderResolution / 8); + int32 val = Util::muldivr(static_cast<int32>(m_tempoSwing[i]) - TempoSwing::Unity, SliderUnity, TempoSwing::Unity); + r->valueSlider.SetPos(val); + rect.MoveToY(rect.top + m.totalHeight); + } + OnHScroll(0, 0, nullptr); + rect.MoveToY(rect.top + m.paddingY); + { + CRect buttonRect; + GetDlgItem(IDOK)->GetWindowRect(buttonRect); + GetDlgItem(IDOK)->SetWindowPos(nullptr, buttonRect.left - windowRect.left - GetSystemMetrics(SM_CXEDGE), rect.top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER); + GetDlgItem(IDCANCEL)->GetWindowRect(buttonRect); + GetDlgItem(IDCANCEL)->SetWindowPos(nullptr, buttonRect.left - windowRect.left - GetSystemMetrics(SM_CXEDGE), rect.top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER); + } + + windowRect.bottom += rect.top + m.footerHeight; + SetWindowPos(nullptr, 0, 0, windowRect.Width(), windowRect.Height(), SWP_NOMOVE | SWP_NOOWNERZORDER); + EnableToolTips(); + + return TRUE; +} + + +void CTempoSwingDlg::OnOK() +//--------------------------- +{ + CDialog::OnOK(); + // If this is the default setup, just clear the vector. + if(static_cast<size_t>(std::count(m_tempoSwing.begin(), m_tempoSwing.end(), TempoSwing::Unity)) == m_tempoSwing.size()) + { + m_tempoSwing.clear(); + } + OnClose(); +} + + +void CTempoSwingDlg::OnCancel() +//--------------------------- +{ + CDialog::OnCancel(); + OnClose(); +} + + +void CTempoSwingDlg::OnClose() +//------------------------------ +{ + for(size_t i = 0; i < m_controls.size(); i++) + { + delete m_controls[i]; + } +} + + +void CTempoSwingDlg::OnReset() +//------------------------------ +{ + for(size_t i = 0; i < m_controls.size(); i++) + { + m_controls[i]->valueSlider.SetPos(0); + } + OnHScroll(0, 0, nullptr); +} + + +void CTempoSwingDlg::OnHScroll(UINT /*nSBCode*/, UINT /*nPos*/, CScrollBar* /*pScrollBar*/) +//------------------------------------------------------------------------------------------- +{ + for(size_t i = 0; i < m_controls.size(); i++) + { + m_tempoSwing[i] = Util::muldivr(m_controls[i]->valueSlider.GetPos(), TempoSwing::Unity, SliderUnity) + TempoSwing::Unity; + } + m_tempoSwing.Normalize(); + for(size_t i = 0; i < m_tempoSwing.size(); i++) + { + TCHAR s[32]; + wsprintf(s, _T("%u%%"), Util::muldivr(m_tempoSwing[i], 100, TempoSwing::Unity)); + m_controls[i]->valueLabel.SetWindowText(s); + } +} + + +BOOL CTempoSwingDlg::OnToolTipNotify(UINT, NMHDR *pNMHDR, LRESULT *) +//-------------------------------------------------------------------- +{ + TOOLTIPTEXT *pTTT = (TOOLTIPTEXTA*)pNMHDR; + for(size_t i = 0; i < m_controls.size(); i++) + { + if((HWND)pNMHDR->idFrom == m_controls[i]->valueSlider.m_hWnd) + { + int32 val = Util::muldivr(m_tempoSwing[i], 100, TempoSwing::Unity) - 100; + wsprintf(pTTT->szText, _T("%s%d"), val > 0 ? _T("+") : _T(""), val); + return TRUE; + } + } + return FALSE; +} + + /////////////////////////////////////////////////////////////////////////////////////// // Messagebox with 'don't show again'-option. Modified: trunk/OpenMPT/mptrack/dlg_misc.h =================================================================== --- trunk/OpenMPT/mptrack/dlg_misc.h 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/mptrack/dlg_misc.h 2015-06-13 12:47:42 UTC (rev 5300) @@ -26,6 +26,7 @@ CComboBox m_TypeBox, m_ChannelsBox, m_TempoModeBox, m_PlugMixBox; CButton m_CheckBox1, m_CheckBox2, m_CheckBox3, m_CheckBox4, m_CheckBox5, m_CheckBoxPT1x, m_CheckBoxFt2VolRamp, m_CheckBoxAmigaLimits; CSoundFile &sndFile; + TempoSwing m_tempoSwing; CHANNELINDEX m_nChannels; MODTYPE m_nType; bool initialized; @@ -35,6 +36,8 @@ bool VerifyData(); void UpdateDialog(); void OnPTModeChanged(); + void OnTempoModeChanged(); + void OnTempoSwing(); protected: void UpdateChannelCBox(); @@ -196,7 +199,7 @@ protected: virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog(); - virtual VOID OnOK(); + virtual void OnOK(); afx_msg void OnUpdateSamples(); afx_msg void OnUpdateKeyboard(); afx_msg void OnUpdateOctave(); @@ -222,7 +225,7 @@ protected: virtual BOOL OnInitDialog(); - virtual VOID OnOK(); + virtual void OnOK(); afx_msg void OnClearHistory(); DECLARE_MESSAGE_MAP() }; @@ -261,6 +264,39 @@ ///////////////////////////////////////////////////////////////////////// +// Tempo swing dialog + +//================================== +class CTempoSwingDlg: public CDialog +//================================== +{ +protected: + enum { SliderResolution = 1000, SliderUnity = SliderResolution / 2 }; + struct RowCtls + { + CStatic rowLabel, valueLabel; + CSliderCtrl valueSlider; + }; + std::vector<RowCtls *> m_controls; +public: + TempoSwing m_tempoSwing; + +public: + CTempoSwingDlg(CWnd *parent, const TempoSwing &tempoSwing) : CDialog(IDD_TEMPO_SWING, parent), m_tempoSwing(tempoSwing) { } + +protected: + virtual BOOL OnInitDialog(); + virtual void OnOK(); + virtual void OnCancel(); + void OnClose(); + afx_msg void OnReset(); + afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); + afx_msg BOOL OnToolTipNotify(UINT, NMHDR *pNMHDR, LRESULT *); + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////// // Messagebox with 'don't show again'-option. // Enums for message entries. See dlg_misc.cpp for the array of entries. Modified: trunk/OpenMPT/mptrack/mptrack.rc =================================================================== --- trunk/OpenMPT/mptrack/mptrack.rc 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/mptrack/mptrack.rc 2015-06-13 12:47:42 UTC (rev 5300) @@ -361,6 +361,18 @@ END +IDD_TEMPO_SWING DIALOGEX 0, 0, 246, 143 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Tempo Swing Settings" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + DEFPUSHBUTTON "OK",IDOK,132,120,50,14 + PUSHBUTTON "Cancel",IDCANCEL,186,120,50,14 + LTEXT "Configure how much each row contributes to a beat:",IDC_STATIC,6,9,174,8 + PUSHBUTTON "Reset",IDC_BUTTON1,186,6,50,14 +END + + ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO @@ -520,6 +532,14 @@ TOPMARGIN, 7 BOTTOMMARGIN, 130 END + + IDD_TEMPO_SWING, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 239 + TOPMARGIN, 7 + BOTTOMMARGIN, 136 + END END #endif // APSTUDIO_INVOKED @@ -1108,12 +1128,13 @@ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,156,222,12 CONTROL "Plugin volume command &bug emulation",IDC_CHK_MIDICCBUG, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,167,222,12 - LTEXT "&Tempo Mode:",IDC_TEXT_TEMPOMODE,12,204,44,8 - COMBOBOX IDC_COMBO_TEMPOMODE,12,215,108,77,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Rows per beat",IDC_TEXT_ROWSPERBEAT,162,206,84,8 - EDITTEXT IDC_ROWSPERBEAT,132,204,24,12,ES_AUTOHSCROLL | ES_NUMBER - LTEXT "Rows per measure",IDC_TEXT_ROWSPERMEASURE,162,223,84,8 - EDITTEXT IDC_ROWSPERMEASURE,132,222,24,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "&Tempo Mode:",IDC_TEXT_TEMPOMODE,12,206,44,8 + COMBOBOX IDC_COMBO_TEMPOMODE,62,204,82,77,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure S&wing",IDC_BUTTON1,62,222,82,12 + LTEXT "Rows per beat",IDC_TEXT_ROWSPERBEAT,180,206,66,8 + EDITTEXT IDC_ROWSPERBEAT,150,204,24,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Rows per measure",IDC_TEXT_ROWSPERMEASURE,180,223,66,8 + EDITTEXT IDC_ROWSPERMEASURE,150,222,24,12,ES_AUTOHSCROLL | ES_NUMBER RTEXT "Created with:",IDC_TEXT_CREATEDWITH,12,260,60,8 EDITTEXT IDC_EDIT_CREATEDWITH,78,258,166,13,ES_AUTOHSCROLL | ES_READONLY,WS_EX_STATICEDGE RTEXT "Last saved with:",IDC_TEXT_SAVEDWITH,12,276,60,8 @@ -1300,18 +1321,19 @@ CAPTION "Pattern Properties" FONT 8, "MS Shell Dlg", 0, 0, 0x1 BEGIN + DEFPUSHBUTTON "&OK",IDOK,132,6,50,14 + PUSHBUTTON "&Cancel",IDCANCEL,132,24,50,14 + LTEXT "Rows:",IDC_STATIC,6,6,108,8 COMBOBOX IDC_COMBO1,6,18,48,93,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "&/2",IDC_BUTTON_HALF,60,18,18,12 PUSHBUTTON "&x2",IDC_BUTTON_DOUBLE,84,18,18,12 - CONTROL "Override &song signature:",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,48,174,12 - EDITTEXT IDC_EDIT_ROWSPERBEAT,18,66,30,12,ES_AUTOHSCROLL | ES_NUMBER - EDITTEXT IDC_EDIT_ROWSPERMEASURE,18,84,30,12,ES_AUTOHSCROLL | ES_NUMBER - DEFPUSHBUTTON "&OK",IDOK,132,6,50,14 - PUSHBUTTON "&Cancel",IDCANCEL,132,24,50,14 - LTEXT "Rows:",IDC_STATIC,6,6,108,8 CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,6,42,174,1 + CONTROL "Override &song signature:",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,48,108,12 + PUSHBUTTON "Configure S&wing",IDC_BUTTON1,114,48,66,12 LTEXT "Rows per beat",IDC_STATIC,54,68,126,8 + EDITTEXT IDC_ROWSPERBEAT,18,66,30,12,ES_AUTOHSCROLL | ES_NUMBER LTEXT "Rows per measure",IDC_STATIC,54,86,126,8 + EDITTEXT IDC_ROWSPERMEASURE,18,84,30,12,ES_AUTOHSCROLL | ES_NUMBER CONTROL "",IDC_STATIC,"Static",SS_ETCHEDFRAME,6,102,174,1 LTEXT "Pattern Info",IDC_TEXT1,6,108,174,14 END Modified: trunk/OpenMPT/mptrack/resource.h =================================================================== --- trunk/OpenMPT/mptrack/resource.h 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/mptrack/resource.h 2015-06-13 12:47:42 UTC (rev 5300) @@ -133,6 +133,7 @@ #define IDD_RESAMPLE 537 #define IDD_MISSINGSAMPLES 538 #define IDD_WECLOME 539 +#define IDD_TEMPO_SWING 540 #define IDC_BUTTON1 1001 #define IDC_BUTTON2 1002 #define IDC_BUTTON3 1003 @@ -911,8 +912,6 @@ #define IDC_LIST_SAMPLEGEN_PRESETS 2425 #define IDC_CHECK_UNDO 2426 #define IDC_CHK_REMEMBERSETTINGS 2427 -#define IDC_EDIT_ROWSPERBEAT 2428 -#define IDC_EDIT_ROWSPERMEASURE 2429 #define IDC_BTN_CLEAR 2430 #define IDC_TOTAL_EDIT_TIME 2431 #define IDC_EDIT_HISTORY 2432 @@ -1257,7 +1256,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_3D_CONTROLS 1 -#define _APS_NEXT_RESOURCE_VALUE 540 +#define _APS_NEXT_RESOURCE_VALUE 541 #define _APS_NEXT_COMMAND_VALUE 44645 #define _APS_NEXT_CONTROL_VALUE 2483 #define _APS_NEXT_SYMED_VALUE 901 Modified: trunk/OpenMPT/soundlib/Load_it.cpp =================================================================== --- trunk/OpenMPT/soundlib/Load_it.cpp 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/soundlib/Load_it.cpp 2015-06-13 12:47:42 UTC (rev 5300) @@ -1072,7 +1072,7 @@ { size_t num = pSndFile->GetFileHistory().size(); #ifdef MODPLUG_TRACKER - CModDoc *pModDoc = pSndFile->GetpModDoc(); + const CModDoc *pModDoc = pSndFile->GetpModDoc(); num += (pModDoc != nullptr) ? 1 : 0; // + 1 for this session #endif // MODPLUG_TRACKER @@ -2024,6 +2024,17 @@ } } + // Tempo Swing Factors + if(!m_tempoSwing.empty()) + { + std::ostringstream oStrm; + TempoSwing::Serialize(oStrm, m_tempoSwing); + std::string data = oStrm.str(); + uint16 length = mpt::saturate_cast<uint16>(data.size()); + WRITEMODULARHEADER(MAGIC4LE('S','W','N','G'), length); + mpt::IO::WriteRaw(f, &data[0], length); + } + // Additional flags for XM/IT/MPTM if(m_ModFlags) { @@ -2182,25 +2193,35 @@ } } break; + + case MAGIC4LE('S','W','N','G'): + // Tempo Swing Factors + if(size > 2) + { + std::string data(chunk.GetLength(), '\0'); + chunk.ReadRaw(&data[0], data.size()); + std::istringstream iStrm(data); + TempoSwing::Deserialize(iStrm, m_tempoSwing, data.size()); + } + break; } } // Validate read values. Limit(m_nDefaultTempo, GetModSpecifications().tempoMin, GetModSpecifications().tempoMax); - //m_nRowsPerBeat - //m_nRowsPerMeasure + if(m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat) m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat; Limit(m_nChannels, CHANNELINDEX(1), GetModSpecifications().channelsMax); - //m_nTempoMode - //m_nMixLevels + if(m_nTempoMode >= tempoModeMax) m_nTempoMode = tempoModeClassic; + if(m_nMixLevels >= mixLevelsMax) m_nMixLevels = mixLevelsOriginal; //m_dwCreatedWithVersion //m_dwLastSavedWithVersion //m_nSamplePreAmp //m_nVSTiVolume - //m_nDefaultGlobalVolume); + //m_nDefaultGlobalVolume + LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME); //m_nRestartPos //m_ModFlags - } @@ -2209,6 +2230,18 @@ size_t CSoundFile::SaveModularInstrumentData(FILE *f, const ModInstrument *pIns) const //------------------------------------------------------------------------------------ { + // Yes, it is completely idiotic that we have both SaveModularInstrumentData and SaveExtendedInstrumentProperties. + // And they are all insane in their own way: + // - SaveExtendedInstrumentProperties is tacked SOMEWHERE AFTER THE SAMPLE CHUNK, which is horrible in case of + // compressed samples because you don't know where to start reading without actually unpacking the sample. + // this mechanism has broken a gazillion times in the past due to simple code changes. Luckily we have unit + // tests which can detect this most of the time. + // - SaveModularInstrumentData follows the instrument data as it should, but is used for only one chunk, and + // it doesn't actually specify the chunk's size. You have to hardcode chunk size or else you're lost. + // That's totally awesome if you have to process unknown chunks. NOT. + // Oh, and of course they all use backwards FOURCCs because they originally used the non-standard '1234' + // multi-char MSVC extension. It's one big clusterfuck. + // As the only stuff that is actually written here is the plugin ID, // we can actually chicken out if there's no plugin. if(!pIns->nMixPlug) @@ -2218,30 +2251,18 @@ uint32 modularInstSize = 0; uint32 id = MAGIC4BE('I','N','S','M'); - SwapBytesLE(id); - fwrite(&id, 1, sizeof(id), f); // mark this as an instrument with modular extensions - long sizePos = ftell(f); // we will want to write the modular data's total size here - fwrite(&modularInstSize, 1, sizeof(modularInstSize), f); // write a DUMMY size, just to move file pointer by a long + mpt::IO::WriteIntLE<uint32>(f, id); + long sizePos = ftell(f); // we will want to write the modular data's total size here + mpt::IO::WriteIntLE<uint32>(f, 0); // write a DUMMY size, just to move file pointer by a long // Write chunks { //VST Slot chunk: - id = MAGIC4BE('P','L','U','G'); - SwapBytesLE(id); - fwrite(&id, 1, sizeof(uint32), f); - fwrite(&(pIns->nMixPlug), 1, sizeof(uint8), f); + mpt::IO::WriteIntLE<uint32>(f, MAGIC4BE('P','L','U','G')); + mpt::IO::WriteIntLE<uint8>(f, pIns->nMixPlug); modularInstSize += sizeof(uint32) + sizeof(uint8); } - //How to save your own modular instrument chunk: -/* { - ID='MYID'; - fwrite(&ID, 1, sizeof(int), f); - instModularDataSize+=sizeof(int); + // If you really need to add more chunks here, please don't repeat history (see above) and *do* add a size field for your chunk, mmmkay? - //You can save your chunk size somwhere here if you need variable chunk size. - fwrite(myData, 1, myDataSize, f); - instModularDataSize+=myDataSize; - } -*/ //write modular data's total size long curPos = ftell(f); // remember current pos fseek(f, sizePos, SEEK_SET); // go back to sizePos @@ -2272,18 +2293,20 @@ while(modularData.AreBytesLeft()) { const uint32 chunkID = modularData.ReadUint32LE(); + uint16 chunkSize; + // Legacy chunk + if(chunkID == MAGIC4BE('P','L','U','G')) + chunkSize = 1; + else + chunkSize = modularData.ReadUint16LE(); + FileReader chunkData = modularData.ReadChunk(chunkSize); switch (chunkID) { case MAGIC4BE('P','L','U','G'): - // Chunks don't tell us their length - stupid! - ins.nMixPlug = modularData.ReadUint8(); + ins.nMixPlug = chunkData.ReadUint8(); break; - default: - // move forward one byte and try to recognize again. - modularData.Skip(1); - } } Modified: trunk/OpenMPT/soundlib/Snd_defs.h =================================================================== --- trunk/OpenMPT/soundlib/Snd_defs.h 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/soundlib/Snd_defs.h 2015-06-13 12:47:42 UTC (rev 5300) @@ -363,7 +363,22 @@ }; +// Tempo swing determines how much every row in modern tempo mode contributes to a beat. +class TempoSwing : public std::vector<uint32> +{ +public: + enum { Unity = 1u << 24 }; + // Normalize the tempo swing coefficients so that they add up to exactly the specified tempo again + void Normalize(); + + static void Serialize(std::ostream &oStrm, const TempoSwing &swing); + static void Deserialize(std::istream& iStrm, TempoSwing &swing, const size_t); +}; + + // Fixed-point type, e.g. used for fractional tempos +// Note that this doesn't use classical bit shifting for the fixed point part. +// This is mostly for the clarity of stored values and to be able to represent any value .0000 to .9999 properly. template<size_t FFact, typename T> struct FPInt { Modified: trunk/OpenMPT/soundlib/Sndfile.cpp =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.cpp 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/soundlib/Sndfile.cpp 2015-06-13 12:47:42 UTC (rev 5300) @@ -96,15 +96,12 @@ gnDryROfsVol = 0; m_nType = MOD_TYPE_NONE; m_ContainerType = MOD_CONTAINERTYPE_NONE; - m_nChannels = 0; m_nMixChannels = 0; m_nSamples = 0; m_nInstruments = 0; #ifndef MODPLUG_TRACKER m_nFreqFactor = m_nTempoFactor = 65536; #endif - m_nMinPeriod = 32; - m_nMaxPeriod = 0x7FFF; m_nRepeatCount = 0; m_PlayState.m_nSeqOverride = ORDERINDEX_INVALID; m_PlayState.m_bPatternTransitionOccurred = false; @@ -123,9 +120,6 @@ m_nDefaultRowsPerMeasure = m_PlayState.m_nCurrentRowsPerMeasure = 16; #endif // MODPLUG_TRACKER - m_dwLastSavedWithVersion=0; - m_dwCreatedWithVersion=0; - MemsetZero(m_PlayState.ChnMix); MemsetZero(Instruments); MemsetZero(m_szNames); @@ -197,6 +191,7 @@ songMessage.clear(); madeWithTracker.clear(); m_FileHistory.clear(); + m_tempoSwing.clear(); } @@ -1358,6 +1353,13 @@ case tempoModeModern: { double accurateBufferCount = static_cast<double>(m_MixerSettings.gdwMixingFreq) * (60.0 / playState.m_nMusicTempo.ToDouble() / (static_cast<double>(playState.m_nMusicSpeed * playState.m_nCurrentRowsPerBeat))); + const TempoSwing &swing = Patterns[playState.m_nPattern].HasTempoSwing() ? Patterns[playState.m_nPattern].GetTempoSwing() : m_tempoSwing; + if(!swing.empty()) + { + // Apply current row's tempo swing factor + TempoSwing::value_type swingFactor = swing[playState.m_nRow % m_tempoSwing.size()]; + accurateBufferCount = accurateBufferCount * swingFactor / double(TempoSwing::Unity); + } uint32 bufferCount = static_cast<int>(accurateBufferCount); playState.m_dBufferDiff += accurateBufferCount - bufferCount; @@ -1592,4 +1594,52 @@ } +// Normalize the tempo swing coefficients so that they add up to exactly the specified tempo again +void TempoSwing::Normalize() +//-------------------------- +{ + if(empty()) return; + int64 sum = 0, remain = Unity * size(); + for(iterator i = begin(); i != end(); i++) + { + Limit(*i, Unity / 4u, Unity * 4u); + sum += *i; + } + sum = sum / size(); + for(iterator i = begin(); i != end(); i++) + { + *i = Util::muldivr(*i, Unity, static_cast<int32>(sum)); + remain -= *i; + } + MPT_ASSERT(abs(remain) <= size()); + at(0) += static_cast<int32>(remain); +} + + +void TempoSwing::Serialize(std::ostream &oStrm, const TempoSwing &swing) +//---------------------------------------------------------------------- +{ + mpt::IO::WriteIntLE<uint16>(oStrm, static_cast<uint16>(swing.size())); + for(std::size_t i = 0; i < swing.size(); i++) + { + mpt::IO::WriteIntLE<uint32>(oStrm, swing[i]); + } +} + + +void TempoSwing::Deserialize(std::istream& iStrm, TempoSwing &swing, const size_t) +//-------------------------------------------------------------------------------- +{ + uint16 numEntries; + mpt::IO::ReadIntLE<uint16>(iStrm, numEntries); + swing.resize(numEntries); + for(uint16 i = 0; i < numEntries; i++) + { + mpt::IO::ReadIntLE<uint32>(iStrm, swing[i]); + } + swing.Normalize(); +} + + + OPENMPT_NAMESPACE_END Modified: trunk/OpenMPT/soundlib/Sndfile.h =================================================================== --- trunk/OpenMPT/soundlib/Sndfile.h 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/soundlib/Sndfile.h 2015-06-13 12:47:42 UTC (rev 5300) @@ -428,6 +428,9 @@ uint32 m_nTempoFactor; // Tempo factor (65536 = no tempo adjustment). Only used in libopenmpt (openmpt::ext::interactive::set_tempo_factor) #endif + // Row swing factors for modern tempo mode + TempoSwing m_tempoSwing; + // Min Period = highest possible frequency, Max Period = lowest possible frequency for current format // Note: Period is an Amiga metric that is inverse to frequency. // Periods in MPT are 4 times as fine as Amiga periods because of extra fine frequency slides (introduced in the S3M format). Modified: trunk/OpenMPT/soundlib/SoundFilePlayConfig.h =================================================================== --- trunk/OpenMPT/soundlib/SoundFilePlayConfig.h 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/soundlib/SoundFilePlayConfig.h 2015-06-13 12:47:42 UTC (rev 5300) @@ -3,8 +3,7 @@ * --------------------- * Purpose: Configuration of sound levels, pan laws, etc... for various mix configurations. * Notes : (currently none) - * Authors: Olivier Lapicque - * OpenMPT Devs + * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ @@ -23,6 +22,7 @@ tempoModeClassic = 0, tempoModeAlternative = 1, tempoModeModern = 2, + tempoModeMax }; enum MixLevels @@ -33,6 +33,7 @@ mixLevels1_17RC3 = 3, mixLevelsCompatible = 4, mixLevelsCompatibleFT2 = 5, + mixLevelsMax }; enum ForcePanningMode Modified: trunk/OpenMPT/soundlib/pattern.cpp =================================================================== --- trunk/OpenMPT/soundlib/pattern.cpp 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/soundlib/pattern.cpp 2015-06-13 12:47:42 UTC (rev 5300) @@ -494,6 +494,10 @@ ssb.WriteItem<uint32>(pat.GetRowsPerBeat(), "RPB."); ssb.WriteItem<uint32>(pat.GetRowsPerMeasure(), "RPM."); } + if(pat.HasTempoSwing()) + { + ssb.WriteItem<TempoSwing>(pat.GetTempoSwing(), "SWNG", TempoSwing::Serialize); + } ssb.FinishWrite(); } @@ -511,6 +515,9 @@ ssb.ReadItem<uint32>(nRPB, "RPB."); ssb.ReadItem<uint32>(nRPM, "RPM."); pat.SetSignature(nRPB, nRPM); + TempoSwing swing; + ssb.ReadItem<TempoSwing>(swing, "SWNG", TempoSwing::Deserialize); + pat.SetTempoSwing(swing); } Modified: trunk/OpenMPT/soundlib/pattern.h =================================================================== --- trunk/OpenMPT/soundlib/pattern.h 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/soundlib/pattern.h 2015-06-13 12:47:42 UTC (rev 5300) @@ -92,6 +92,11 @@ bool SetSignature(const ROWINDEX rowsPerBeat, const ROWINDEX rowsPerMeasure); void RemoveSignature() { m_RowsPerBeat = m_RowsPerMeasure = 0; } + bool HasTempoSwing() const { return !m_tempoSwing.empty(); } + const TempoSwing& GetTempoSwing() const { return m_tempoSwing; } + void SetTempoSwing(const TempoSwing &swing) { m_tempoSwing = swing; m_tempoSwing.Normalize(); } + void RemoveTempoSwing() { m_tempoSwing.clear(); } + // Pattern name functions - bool functions return true on success. bool SetName(const std::string &newName); bool SetName(const char *newName, size_t maxChars = MAX_PATTERNNAME); @@ -151,6 +156,7 @@ ROWINDEX m_Rows; ROWINDEX m_RowsPerBeat; // patterns-specific time signature. if != 0, this is implicitely set. ROWINDEX m_RowsPerMeasure; // ditto + TempoSwing m_tempoSwing; std::string m_PatternName; CPatternContainer& m_rPatternContainer; //END: DATA Modified: trunk/OpenMPT/soundlib/patternContainer.cpp =================================================================== --- trunk/OpenMPT/soundlib/patternContainer.cpp 2015-06-13 12:42:43 UTC (rev 5299) +++ trunk/OpenMPT/soundlib/patternContainer.cpp 2015-06-13 12:47:42 UTC (rev 5300) @@ -147,6 +147,7 @@ for(PATTERNINDEX nPat = 0; nPat < m_Patterns.size(); nPat++) { m_Patterns[nPat].RemoveSignature(); + m_Patterns[nPat].RemoveTempoSwing(); } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |