Skip to content

Commit

Permalink
[Ref] Don't use fixed-size arrays for channel states in pattern edito…
Browse files Browse the repository at this point in the history
…r code.

git-svn-id: https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@21836 56274372-70c3-4bfc-bfc3-4c3a0b034d27
  • Loading branch information
sagamusix committed Oct 16, 2024
1 parent db92d71 commit c62ae23
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 54 deletions.
66 changes: 32 additions & 34 deletions mptrack/Draw_pat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,11 @@ enum
COLUMN_BITS_FXPARAM = 0x10,
COLUMN_BITS_FXCMDANDPARAM = 0x18,
COLUMN_BITS_ALLCOLUMNS = 0x1F,
COLUMN_BITS_UNKNOWN = 0x20, // Appears to be unused
COLUMN_BITS_ALL = 0x3F,
COLUMN_BITS_SKIP = 0x40,
COLUMN_BITS_INVISIBLE = 0x80,
};





/////////////////////////////////////////////////////////////////////////////
// Effect colour codes

Expand Down Expand Up @@ -526,7 +521,9 @@ void CViewPattern::OnDraw(CDC *pDC)

MPT_ASSERT(pDC);
UpdateSizes();
if ((pModDoc = GetDocument()) == nullptr) return;
if ((pModDoc = GetDocument()) == nullptr)
return;
m_chnState.resize(pModDoc->GetNumChannels());

const int vuHeight = MulDiv(VUMETERS_HEIGHT, m_nDPIy, 96);
const int colHeight = MulDiv(COLHDR_HEIGHT, m_nDPIy, 96);
Expand Down Expand Up @@ -760,7 +757,7 @@ void CViewPattern::OnDraw(CDC *pDC)

if(m_Status[psShowVUMeters])
{
OldVUMeters[ncolhdr] = 0;
m_chnState[ncolhdr].vuMeterOld = 0;
DrawChannelVUMeter(hdc, rect.left, rect.bottom, ncolhdr);
rect.top += vuHeight;
rect.bottom += vuHeight;
Expand Down Expand Up @@ -809,35 +806,34 @@ static constexpr UINT EncodeRowColor(int rowBkCol, int rowCol, bool rowSelected)
void CViewPattern::DrawPatternData(HDC hdc, PATTERNINDEX nPattern, bool selEnable,
bool isPlaying, ROWINDEX startRow, ROWINDEX numRows, CHANNELINDEX startChan, CRect &rcClient, int *pypaint)
{
uint8 selectedCols[MAX_BASECHANNELS] = {}; // Bit mask of selected channel components
static_assert(1 << PatternCursor::lastColumn <= Util::MaxValueOfType(selectedCols[0]) , "Columns are used as bitmasks.");
static_assert(1 << PatternCursor::lastColumn <= Util::MaxValueOfType(ChannelState{}.selectedCols), "Columns are used as bitmasks");
static_assert(!((1 << PatternCursor::lastColumn) & (COLUMN_BITS_INVISIBLE | COLUMN_BITS_SKIP)), "Column bits and special bits overlap");

const CSoundFile &sndFile = GetDocument()->GetSoundFile();
if(!sndFile.Patterns.IsValidPat(nPattern))
{
return;
}
const CPattern &pattern = sndFile.Patterns[nPattern];
const auto patternSetupFlags = TrackerSettings::Instance().m_dwPatternSetup.Get();

const PATTERNFONT *pfnt = PatternFont::currentFont;
CRect rect;
int xpaint, ypaint = *pypaint;
UINT nColumnWidth;

CHANNELINDEX ncols = sndFile.GetNumChannels();
nColumnWidth = m_szCell.cx;
const CHANNELINDEX ncols = sndFile.GetNumChannels();
const UINT nColumnWidth = m_szCell.cx;
rect.SetRect(m_szHeader.cx, rcClient.top, m_szHeader.cx+nColumnWidth, rcClient.bottom);
for(CHANNELINDEX cmk = startChan; cmk < ncols; cmk++)
{
selectedCols[cmk] = selEnable ? m_Selection.GetSelectionBits(cmk) : 0;
if (!::RectVisible(hdc, &rect)) selectedCols[cmk] |= COLUMN_BITS_INVISIBLE;
m_chnState[cmk].selectedCols = selEnable ? m_Selection.GetSelectionBits(cmk) : 0;
if(!::RectVisible(hdc, &rect))
m_chnState[cmk].selectedCols |= COLUMN_BITS_INVISIBLE;
rect.left += nColumnWidth;
rect.right += nColumnWidth;
}
// Max Visible Column
CHANNELINDEX maxcol = ncols;
while ((maxcol > startChan) && (selectedCols[maxcol-1] & COLUMN_BITS_INVISIBLE)) maxcol--;
while((maxcol > startChan) && (m_chnState[maxcol -1].selectedCols & COLUMN_BITS_INVISIBLE))
maxcol--;
// Init bitmap border
{
UINT maxndx = sndFile.GetNumChannels() * m_szCell.cx;
Expand Down Expand Up @@ -878,7 +874,7 @@ void CViewPattern::DrawPatternData(HDC hdc, PATTERNINDEX nPattern, bool selEnabl
{
// No speedup for these columns next time
for(CHANNELINDEX iup = startChan; iup < maxcol; iup++)
selectedCols[iup] &= ~COLUMN_BITS_SKIP;
m_chnState[iup].selectedCols &= ~COLUMN_BITS_SKIP;
// skip row
ypaint += m_szCell.cy;
if(ypaint >= rcClient.bottom)
Expand All @@ -887,7 +883,7 @@ void CViewPattern::DrawPatternData(HDC hdc, PATTERNINDEX nPattern, bool selEnabl
}
rect.right = rect.left + m_szHeader.cx;

bool rowDisabled = sndFile.m_lockRowStart != ROWINDEX_INVALID && (row < sndFile.m_lockRowStart || row > sndFile.m_lockRowEnd);
const bool rowDisabled = sndFile.m_lockRowStart != ROWINDEX_INVALID && (row < sndFile.m_lockRowStart || row > sndFile.m_lockRowEnd);
TCHAR s[32];
if(hexNumbers)
wsprintf(s, _T("%s%02X"), compRow < 0 ? _T("-") : _T(""), std::abs(compRow));
Expand Down Expand Up @@ -948,14 +944,14 @@ void CViewPattern::DrawPatternData(HDC hdc, PATTERNINDEX nPattern, bool selEnabl
// Eliminate non-visible column
xpaint = m_szHeader.cx;
col = startChan;
while ((selectedCols[col] & COLUMN_BITS_INVISIBLE) && (col < maxcol))
while((m_chnState[col].selectedCols & COLUMN_BITS_INVISIBLE) && (col < maxcol))
{
selectedCols[col] &= ~COLUMN_BITS_SKIP;
m_chnState[col].selectedCols &= ~COLUMN_BITS_SKIP;
col++;
xpaint += nColumnWidth;
}
// Optimization: same row color ?
bool useSpeedUpMask = (oldrowcolor == EncodeRowColor(row_bkcol, row_col, bRowSel)) && !blendModeChanged;
const bool useSpeedUpMask = (oldrowcolor == EncodeRowColor(row_bkcol, row_col, bRowSel)) && !blendModeChanged;
xbmp = nbmp = 0;
do
{
Expand All @@ -967,7 +963,7 @@ void CViewPattern::DrawPatternData(HDC hdc, PATTERNINDEX nPattern, bool selEnabl
const bool drawDefaultVolume = DrawDefaultVolume(m);

DWORD dwSpeedUpMask = 0;
if (useSpeedUpMask && (selectedCols[col] & COLUMN_BITS_SKIP) && (row))
if(useSpeedUpMask && (m_chnState[col].selectedCols & COLUMN_BITS_SKIP) && (row))
{
const ModCommand *mold = m - ncols;
const bool drawOldDefaultVolume = DrawDefaultVolume(mold);
Expand All @@ -989,9 +985,9 @@ void CViewPattern::DrawPatternData(HDC hdc, PATTERNINDEX nPattern, bool selEnabl
}
if (dwSpeedUpMask == COLUMN_BITS_ALLCOLUMNS) goto DoBlit;
}
selectedCols[col] |= COLUMN_BITS_SKIP;
m_chnState[col].selectedCols |= COLUMN_BITS_SKIP;
col_sel = 0;
if (bRowSel) col_sel = selectedCols[col] & COLUMN_BITS_ALL;
if(bRowSel) col_sel = m_chnState[col].selectedCols & COLUMN_BITS_ALLCOLUMNS;
tx_col = row_col;
bk_col = row_bkcol;
if (col_sel)
Expand Down Expand Up @@ -1184,17 +1180,16 @@ void CViewPattern::DrawPatternData(HDC hdc, PATTERNINDEX nPattern, bool selEnabl
if (ypaint >= rcClient.bottom) break;
}
*pypaint = ypaint;

}


void CViewPattern::DrawChannelVUMeter(HDC hdc, int x, int y, UINT nChn)
{
if(ChnVUMeters[nChn] == OldVUMeters[nChn])
if(m_chnState[nChn].vuMeter == m_chnState[nChn].vuMeterOld)
return;

uint8 vuL = static_cast<uint8>((ChnVUMeters[nChn] & 0xFF00) >> 8);
uint8 vuR = static_cast<uint8>(ChnVUMeters[nChn] & 0xFF);
uint8 vuL = static_cast<uint8>((m_chnState[nChn].vuMeter & 0xFF00) >> 8);
uint8 vuR = static_cast<uint8>(m_chnState[nChn].vuMeter & 0xFF);
vuL /= 15;
vuR /= 15;
LimitMax(vuL, uint8(8));
Expand All @@ -1216,7 +1211,7 @@ void CViewPattern::DrawChannelVUMeter(HDC hdc, int x, int y, UINT nChn)
BitBlt(hdc, x + midSpacer / 2, y, barWidth, m_ledHeight, m_vuMeterDC, srcOffsetX + barWidth, vuR * m_ledHeight, SRCCOPY);
m_vuMeterDC.SelectObject(oldBitmap);

OldVUMeters[nChn] = ChnVUMeters[nChn];
m_chnState[nChn].vuMeterOld = m_chnState[nChn].vuMeter;
}


Expand Down Expand Up @@ -1799,19 +1794,22 @@ void CViewPattern::UpdateAllVUMeters(Notification *pnotify)
CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
const CModDoc *pModDoc = GetDocument();

if ((!pModDoc) || (!pMainFrm)) return;
if(!pModDoc || !pMainFrm)
return;
CRect rcClient;
GetClientRect(&rcClient);
int xofs = GetXScrollPos();
HDC hdc = ::GetDC(m_hWnd);
const bool isPlaying = (pMainFrm->GetFollowSong(pModDoc) == m_hWnd);
int x = m_szHeader.cx;
CHANNELINDEX nChn = static_cast<CHANNELINDEX>(xofs);
const CHANNELINDEX numChannels = std::min(pModDoc->GetNumChannels(), static_cast<CHANNELINDEX>(m_chnState.size()));
const int yPos = rcClient.top + MulDiv(COLHDR_HEIGHT, m_nDPIy, 96);
while ((nChn < pModDoc->GetNumChannels()) && (x < rcClient.right))
while(nChn < numChannels && x < rcClient.right)
{
ChnVUMeters[nChn] = static_cast<uint16>(pnotify->pos[nChn]);
if ((!isPlaying) || pnotify->type[Notification::Stop]) ChnVUMeters[nChn] = 0;
m_chnState[nChn].vuMeter = static_cast<uint16>(pnotify->pos[nChn]);
if(!isPlaying || pnotify->type[Notification::Stop])
m_chnState[nChn].vuMeter = 0;
DrawChannelVUMeter(hdc, x, rcClient.top + yPos, nChn);
nChn++;
x += m_szCell.cx;
Expand Down
24 changes: 13 additions & 11 deletions mptrack/View_pat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,7 @@ void CViewPattern::OnInitialUpdate()
{
CModScrollView::OnInitialUpdate();
EnableToolTips();
ChnVUMeters.fill(0);
OldVUMeters.fill(0);
m_previousNote.fill(NOTE_NONE);
m_chnState.assign(GetDocument()->GetNumChannels(), {});
m_splitActiveNoteChannel.fill(NOTE_CHANNEL_MAP_INVALID);
m_activeNoteChannel.fill(NOTE_CHANNEL_MAP_INVALID);
m_nPlayPat = PATTERNINDEX_INVALID;
Expand All @@ -203,7 +201,6 @@ void CViewPattern::OnInitialUpdate()
m_nLastPlayedRow = 0;
m_nLastPlayedOrder = ORDERINDEX_INVALID;
m_prevChordNote = NOTE_NONE;
m_previousPCevent.fill({PLUGINDEX_INVALID, 0});
}


Expand Down Expand Up @@ -3643,7 +3640,8 @@ LRESULT CViewPattern::OnPlayerNotify(Notification *pnotify)
if(pnotify->type[Notification::Stop])
{
m_baPlayingNote.reset();
ChnVUMeters.fill(0); // Also zero all non-visible VU meters
for(auto &chnState : m_chnState)
chnState.vuMeter = 0;
SetPlayCursor(PATTERNINDEX_INVALID, ROWINDEX_INVALID, 0);
}

Expand Down Expand Up @@ -3673,7 +3671,7 @@ CHANNELINDEX CViewPattern::GetRecordChannelForPCEvent(PLUGINDEX plugSlot, PlugPa
continue;

const ModCommand &m = *sndFile.Patterns[pattern].GetpModCommand(row, c);
if((m_previousPCevent[c] == plugParam && m.IsEmpty()) || (m.IsPcNote() && m.instr == plugSlot + 1 && m.GetValueVolCol() == paramIndex))
if((c < m_chnState.size() && m_chnState[c].previousPCevent == plugParam && m.IsEmpty()) || (m.IsPcNote() && m.instr == plugSlot + 1 && m.GetValueVolCol() == paramIndex))
{
return c;
break;
Expand Down Expand Up @@ -3725,7 +3723,8 @@ LRESULT CViewPattern::OnRecordPlugParamChange(WPARAM plugSlot, LPARAM paramIndex
if(m.IsEmpty() || m.IsPcNote())
{
m.Set(NOTE_PCS, static_cast<ModCommand::INSTR>(plugSlot + 1), static_cast<uint16>(paramIndex), static_cast<uint16>(pPlug->GetParameter(static_cast<PlugParamIndex>(paramIndex)) * ModCommand::maxColumnValue));
m_previousPCevent[chn] = plugParam;
m_chnState.resize(sndFile.GetNumChannels());
m_chnState[chn].previousPCevent = plugParam;
}
} else if(sndFile.GetModSpecifications().HasCommand(CMD_SMOOTHMIDI))
{
Expand Down Expand Up @@ -3942,7 +3941,8 @@ LRESULT CViewPattern::OnMidiMsg(WPARAM dwMidiDataParam, LPARAM)
ModCommand &m = GetModCommand(sndFile, editPos);
pModDoc->GetPatternUndo().PrepareUndo(editPos.pattern, editPos.channel, editPos.row, 1, 1, "MIDI Mapping Record");
m.Set(NOTE_PCS, mappedIndex, static_cast<uint16>(paramIndex), static_cast<uint16>((paramValue * ModCommand::maxColumnValue) / 16383));
m_previousPCevent[editPos.channel] = std::make_pair(static_cast<PLUGINDEX>(mappedIndex - 1), paramIndex);
m_chnState.resize(pModDoc->GetNumChannels());
m_chnState[editPos.channel].previousPCevent = std::make_pair(static_cast<PLUGINDEX>(mappedIndex - 1), paramIndex);
if(!liveRecord)
InvalidateRow(editPos.row);
pModDoc->SetModified();
Expand Down Expand Up @@ -5255,7 +5255,7 @@ void CViewPattern::TempStopNote(ModCommand::NOTE note, const bool fromMidi, bool
bool modified = false;
for(int i = 0; i < numNotes; i++)
{
if(m_previousNote[noteChannels[i]] != notes[i])
if(noteChannels[i] < m_chnState.size() && m_chnState[noteChannels[i]].previousNote != notes[i])
{
// This might be a note-off from a past note, but since we already hit a new note on this channel, we ignore it.
continue;
Expand Down Expand Up @@ -5667,7 +5667,8 @@ void CViewPattern::TempEnterNote(ModCommand::NOTE note, int vol, bool fromMidi)

if(newcmd.IsNote())
{
m_previousNote[nChn] = note;
m_chnState.resize(sndFile.GetNumChannels());
m_chnState[nChn].previousNote = note;
}

// -- if recording, handle post note entry behaviour (move cursor etc..)
Expand Down Expand Up @@ -5891,7 +5892,8 @@ void CViewPattern::TempEnterChord(ModCommand::NOTE note)
m_chordPatternChannels[i] = curChn;

ModCommand &m = newRow[curChn];
m_previousNote[curChn] = m.note = chordNotes[i];
m_chnState.resize(sndFile.GetNumChannels());
m_chnState[curChn].previousNote = m.note = chordNotes[i];
if(newRow[chn].instr)
{
m.instr = newRow[chn].instr;
Expand Down
21 changes: 12 additions & 9 deletions mptrack/View_pat.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,19 +211,24 @@ class CViewPattern final : public CModScrollView
std::array<std::vector<uint32>, 16> m_midiSustainBuffer;
std::bitset<16> m_midiSustainActive;

std::array<uint16, MAX_BASECHANNELS> ChnVUMeters;
std::array<uint16, MAX_BASECHANNELS> OldVUMeters;
struct ChannelState
{
uint16 vuMeter = 0;
uint16 vuMeterOld = 0;
std::pair<PLUGINDEX, PlugParamIndex> previousPCevent = {PLUGINDEX_INVALID, 0};
ModCommand::NOTE previousNote = NOTE_NONE;
uint8 selectedCols = 0;
};

std::vector<ChannelState> m_chnState;
std::bitset<128> m_baPlayingNote;
CModDoc::NoteToChannelMap m_noteChannel; // Note -> Preview channel assignment
std::array<ModCommand::NOTE, 10> m_octaveKeyMemory;
std::array<ModCommand::NOTE, MAX_BASECHANNELS> m_previousNote;
std::array<std::pair<PLUGINDEX, PlugParamIndex>, MAX_BASECHANNELS> m_previousPCevent; // For multichannel recording
std::array<ModCommand::NOTE, (NOTE_MAX - NOTE_MIN + 12) / 12> m_octaveKeyMemory;
std::array<uint8, NOTE_MAX + NOTE_MIN> m_activeNoteChannel;
std::array<uint8, NOTE_MAX + NOTE_MIN> m_splitActiveNoteChannel;
static constexpr uint8 NOTE_CHANNEL_MAP_INVALID = 0xFF;
static_assert(MAX_BASECHANNELS <= std::numeric_limits<decltype(m_activeNoteChannel)::value_type>::max());
static_assert(MAX_BASECHANNELS <= NOTE_CHANNEL_MAP_INVALID);
static_assert(MAX_BASECHANNELS - 1 <= std::numeric_limits<decltype(m_activeNoteChannel)::value_type>::max());
static_assert(MAX_BASECHANNELS - 1 < NOTE_CHANNEL_MAP_INVALID);

public:
std::unique_ptr<CEffectVis> m_pEffectVis;
Expand Down Expand Up @@ -488,8 +493,6 @@ class CViewPattern final : public CModScrollView
afx_msg void OnUpdateUndo(CCmdUI *pCmdUI);
afx_msg void OnUpdateRedo(CCmdUI *pCmdUI);
afx_msg void OnSelectPlugin(UINT nID);
// cppcheck-suppress duplInheritedMember
afx_msg LRESULT OnUpdatePosition(WPARAM nOrd, LPARAM nRow);
afx_msg LRESULT OnMidiMsg(WPARAM, LPARAM);
afx_msg LRESULT OnRecordPlugParamChange(WPARAM, LPARAM);
afx_msg LRESULT OnCustomKeyMsg(WPARAM, LPARAM);
Expand Down

0 comments on commit c62ae23

Please sign in to comment.