Skip to content

Commit

Permalink
[Mod] Change NOTE_MAX so that there are 128 valid notes instead of 12…
Browse files Browse the repository at this point in the history
…0, like in the instrument keyboard mapping. Fixes bad notes in some Future Composer files (https://www.un4seen.com/forum/?topic=15448.msg144041#msg144041).

[Var] Update MPTM/XM tests to verify correct writing of special notes and notes at the upper/lower note limit.

git-svn-id: https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@22093 56274372-70c3-4bfc-bfc3-4c3a0b034d27
  • Loading branch information
sagamusix committed Nov 4, 2024
1 parent 464464f commit f5e896e
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 46 deletions.
53 changes: 34 additions & 19 deletions mptrack/Ctrl_ins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ void CNoteMapWnd::OnPaint()
dc.IntersectClipRect(&rcClient);

const CSoundFile &sndFile = m_modDoc.GetSoundFile();
const auto &modSpecs = sndFile.GetModSpecifications();
int noteMin = 0, noteMax = NOTE_MAX - NOTE_MIN;
if(modSpecs.instrumentsMax)
{
noteMin = modSpecs.noteMin - NOTE_MIN;
noteMax = modSpecs.noteMax - NOTE_MIN;
}
if (m_cxFont > 0 && m_cyFont > 0)
{
const bool focus = (::GetFocus() == m_hWnd);
Expand All @@ -185,11 +192,11 @@ void CNoteMapWnd::OnPaint()
int nPos = m_nNote - (nNotes/2);
int ypaint = 0;
mpt::winstring s;
for (int ynote=0; ynote<nNotes; ynote++, ypaint+=m_cyFont, nPos++)
for(int ynote = 0; ynote < nNotes; ynote++, ypaint += m_cyFont, nPos++)
{
// Note
bool isValidPos = (nPos >= 0) && (nPos < NOTE_MAX - NOTE_MIN + 1);
if (isValidPos)
const bool isValidPos = mpt::is_in_range(nPos, noteMin, noteMax);
if(isValidPos)
{
s = mpt::ToWin(sndFile.GetNoteName(static_cast<ModCommand::NOTE>(nPos + 1), m_nInstrument));
s.resize(4);
Expand Down Expand Up @@ -230,7 +237,7 @@ void CNoteMapWnd::OnPaint()
rect.left = rcClient.left + m_cxFont * 2 + 3;
rect.right = rcClient.right;
s = _T(" ..");
if(pIns && nPos >= 0 && nPos < NOTE_MAX && pIns->Keyboard[nPos])
if(pIns && isValidPos && pIns->Keyboard[nPos])
{
s = mpt::tfmt::right(3, mpt::tfmt::dec(pIns->Keyboard[nPos]));
}
Expand Down Expand Up @@ -755,40 +762,48 @@ bool CNoteMapWnd::HandleNav(WPARAM k)
bool redraw = false;

//HACK: handle numpad (convert numpad number key to normal number key)
if ((k >= VK_NUMPAD0) && (k <= VK_NUMPAD9)) return HandleChar(k-VK_NUMPAD0+'0');
if ((k >= VK_NUMPAD0) && (k <= VK_NUMPAD9))
return HandleChar(k-VK_NUMPAD0+'0');

const CSoundFile &sndFile = m_modDoc.GetSoundFile();
const auto &modSpecs = sndFile.GetModSpecifications();
UINT noteMin = 0, noteMax = NOTE_MAX - NOTE_MIN;
if(modSpecs.instrumentsMax)
{
noteMin = modSpecs.noteMin - NOTE_MIN;
noteMax = modSpecs.noteMax - NOTE_MIN;
}

switch(k)
{
case VK_RIGHT:
if (!m_bIns) { m_bIns = true; redraw = true; } else
if (m_nNote < NOTE_MAX - NOTE_MIN) { m_nNote++; m_bIns = false; redraw = true; }
if (!m_bIns) { m_bIns = true; redraw = true; }
else if (m_nNote < noteMax) { m_nNote++; m_bIns = false; redraw = true; }
break;
case VK_LEFT:
if (m_bIns) { m_bIns = false; redraw = true; } else
if (m_nNote) { m_nNote--; m_bIns = true; redraw = true; }
if (m_bIns) { m_bIns = false; redraw = true; }
else if (m_nNote > noteMin) { m_nNote--; m_bIns = true; redraw = true; }
break;
case VK_UP:
if (m_nNote > 0) { m_nNote--; redraw = true; }
if (m_nNote > noteMin) { m_nNote--; redraw = true; }
break;
case VK_DOWN:
if (m_nNote < NOTE_MAX - 1) { m_nNote++; redraw = true; }
if (m_nNote < noteMax) { m_nNote++; redraw = true; }
break;
case VK_PRIOR:
if (m_nNote > 3) { m_nNote -= 3; redraw = true; } else
if (m_nNote > 0) { m_nNote = 0; redraw = true; }
if (m_nNote > noteMin + 3) { m_nNote -= 3; redraw = true; }
else if (m_nNote > noteMin) { m_nNote = noteMin; redraw = true; }
break;
case VK_NEXT:
if (m_nNote+3 < NOTE_MAX) { m_nNote += 3; redraw = true; } else
if (m_nNote < NOTE_MAX - NOTE_MIN) { m_nNote = NOTE_MAX - NOTE_MIN; redraw = true; }
if (m_nNote + 3 < noteMax) { m_nNote += 3; redraw = true; }
else if (m_nNote < noteMax) { m_nNote = noteMax; redraw = true; }
break;
case VK_HOME:
if(m_nNote > 0) { m_nNote = 0; redraw = true; }
if(m_nNote > noteMin) { m_nNote = noteMin; redraw = true; }
break;
case VK_END:
if(m_nNote < NOTE_MAX - NOTE_MIN) { m_nNote = NOTE_MAX - NOTE_MIN; redraw = true; }
if(m_nNote < noteMax) { m_nNote = noteMax; redraw = true; }
break;
// case VK_TAB:
// return true;
case VK_RETURN:
{
ModInstrument *pIns = m_modDoc.GetSoundFile().Instruments[m_nInstrument];
Expand Down
2 changes: 1 addition & 1 deletion soundlib/InstrumentSynth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ void InstrumentSynth::States::State::ApplyChannelState(ModChannel &chn, int32 &p
{
uint8 fcNote = static_cast<uint8>(m_fcPitch >= 0 ? m_fcPitch + chn.nLastNote - NOTE_MIN : m_fcPitch) & 0x7F;
static_assert(mpt::array_size<decltype(chn.pModInstrument->NoteMap)>::size > 0x7F);
if(m_fcPitch)
if(m_fcPitch && ModCommand::IsNote(chn.nLastNote))
period += (sndFile.GetPeriodFromNote(chn.pModInstrument->NoteMap[fcNote], chn.nFineTune, chn.nC5Speed) - sndFile.GetPeriodFromNote(chn.pModInstrument->NoteMap[chn.nLastNote - NOTE_MIN], chn.nFineTune, chn.nC5Speed));

if(doVibratoFC)
Expand Down
1 change: 1 addition & 0 deletions soundlib/Load_fc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ bool CSoundFile::ReadFC(FileReader &file, ModLoadingFlags loadFlags)
if(!instr)
return false;

static_assert(NOTE_MAX - NOTE_MIN + 1 >= 128, "Need at least a note range of 128 for correct emulation of note wrap-around logic in Future Composer");
for(uint8 note = 0; note < static_cast<uint8>(instr->NoteMap.size()); note++)
{
if(note < 48)
Expand Down
37 changes: 25 additions & 12 deletions soundlib/Load_it.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1098,11 +1098,14 @@ bool CSoundFile::ReadIT(FileReader &file, ModLoadingFlags loadFlags)
uint8 note = patternData.ReadUint8();
if(note < 0x80)
note += NOTE_MIN;
if(!(GetType() & MOD_TYPE_MPT))
{
if(note > NOTE_MAX && note < 0xFD) note = NOTE_FADE;
else if(note == 0xFD) note = NOTE_NONE;
}
else if(note == 0xFF)
note = NOTE_KEYOFF;
else if(note == 0xFE)
note = NOTE_NOTECUT;
else if(note == 0xFD && GetType() != MOD_TYPE_MPT)
note = NOTE_NONE; // Note: in MPTM format, NOTE_FADE is written as 0xFD to preserve compatibility with older OpenMPT versions.
else
note = NOTE_FADE;
m.note = lastValue[ch].note = note;
}
if(chnMask[ch] & 2)
Expand Down Expand Up @@ -1769,14 +1772,23 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
}

auto &chnState = chnStates[ch];
uint8 b = 0;
uint8 b = 1;
uint8 vol = 0xFF;
uint8 note = m->note;
if (note != NOTE_NONE) b |= 1;
if (m->IsNote()) note -= NOTE_MIN;
if (note == NOTE_FADE && GetType() != MOD_TYPE_MPT) note = 0xF6;
if (m->instr) b |= 2;
if (m->volcmd != VOLCMD_NONE)
if(note >= NOTE_MIN && note <= NOTE_MIN + 119)
note = m->note - NOTE_MIN;
else if(note == NOTE_FADE)
note = (GetType() == MOD_TYPE_MPT) ? 0xFD : 0xF6;
else if(note == NOTE_NOTECUT)
note = 0xFE;
else if(note == NOTE_KEYOFF)
note = 0xFF;
else
b = 0;

if(m->instr)
b |= 2;
if(m->volcmd != VOLCMD_NONE)
{
vol = std::min(m->vol, uint8(9));
switch(m->volcmd)
Expand All @@ -1801,7 +1813,8 @@ bool CSoundFile::SaveIT(std::ostream &f, const mpt::PathString &filename, bool c
default: vol = 0xFF;
}
}
if (vol != 0xFF) b |= 4;
if(vol != 0xFF)
b |= 4;
uint8 command = 0, param = 0;
if(m->command == CMD_VOLUME && vol == 0xFF)
{
Expand Down
13 changes: 7 additions & 6 deletions soundlib/Load_s3m.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,22 +969,23 @@ bool CSoundFile::SaveS3M(std::ostream &f) const
{
info |= s3mNotePresent;

if(note == NOTE_NONE)
{
note = s3mNoteNone;
} else if(ModCommand::IsSpecialNote(note))
if(ModCommand::IsSpecialNote(note))
{
// Note Cut
note = s3mNoteOff;
} else if(note < 12 + NOTE_MIN)
} else if(note < NOTE_MIN + 12)
{
// Too low
note = 0;
} else if(note <= NOTE_MAX)
} else if(note <= NOTE_MIN + 107)
{
note -= (12 + NOTE_MIN);
note = static_cast<uint8>((note % 12) + ((note / 12) << 4));
}
else
{
note = s3mNoteNone;
}

if(m.instr > 0 && m.instr <= GetNumSamples())
{
Expand Down
9 changes: 6 additions & 3 deletions soundlib/Load_xm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1262,9 +1262,12 @@ bool CSoundFile::SaveXM(std::ostream &f, bool compatibilityExport)
uint8 note = p->note, command = 0, param = 0;
ModSaveCommand(*p, command, param, true, compatibilityExport);

if (note >= NOTE_MIN_SPECIAL) note = 97; else
if ((note <= 12) || (note > 96+12)) note = 0; else
note -= 12;
if(note >= NOTE_MIN_SPECIAL)
note = 97;
else if(note < NOTE_MIN + 12 || note >= NOTE_MIN + 12 + 96)
note = 0;
else
note -= 12;
uint8 vol = 0;
if (p->volcmd != VOLCMD_NONE)
{
Expand Down
5 changes: 3 additions & 2 deletions soundlib/Sndfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1643,9 +1643,10 @@ mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const NoteName
return specialNoteNames[note - NOTE_MIN_SPECIAL];
} else if(ModCommand::IsNote(note))
{
const int octave = (note - NOTE_MIN) / 12;
return mpt::ustring()
.append(noteNames[(note - NOTE_MIN) % 12])
.append(1, static_cast<mpt::uchar>(UC_('0') + ((note - NOTE_MIN) / 12)))
.append(1, static_cast<mpt::uchar>((octave <= 9 ? UC_('0') : UC_('A') - 10) + octave))
; // e.g. "C#" + "5"
} else if(note == NOTE_NONE)
{
Expand Down Expand Up @@ -2014,7 +2015,7 @@ bool CSoundFile::IsSampleReferencedByInstrument(SAMPLEINDEX sample, INSTRUMENTIN
if(targetIns == nullptr)
return false;

return mpt::contains(mpt::as_span(targetIns->Keyboard).first(NOTE_MAX), sample);
return mpt::contains(mpt::as_span(targetIns->Keyboard).first(NOTE_MAX - NOTE_MIN + 1), sample);
}


Expand Down
2 changes: 1 addition & 1 deletion soundlib/mod_specifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ constexpr CModSpecifications mptm_ =
MOD_TYPE_MPT, // Internal MODTYPE value
"mptm", // File extension
NOTE_MIN, // Minimum note index
NOTE_MAX, // Maximum note index
NOTE_MIN + 119, // Maximum note index
4000, // Pattern max.
4000, // Order max.
MAX_SEQUENCES, // Sequences max
Expand Down
2 changes: 1 addition & 1 deletion soundlib/modcommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ enum : uint8 // ModCommand::NOTE
{
NOTE_NONE = 0, // Empty note cell
NOTE_MIN = 1, // Minimum note value
NOTE_MAX = 120, // Maximum note value
NOTE_MAX = 128, // Maximum note value
NOTE_MIDDLEC = (5 * 12 + NOTE_MIN),
NOTE_KEYOFF = 0xFF, // === (Note Off, releases envelope / fades samples, stops plugin note)
NOTE_NOTECUT = 0xFE, // ^^^ (Cuts sample / stops all plugin notes)
Expand Down
16 changes: 15 additions & 1 deletion test/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2863,6 +2863,9 @@ static void TestLoadXMFile(const CSoundFile &sndFile)
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->instr, 0);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->volcmd, VOLCMD_VIBRATOSPEED);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->vol, 15);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 1)->note, NOTE_MIN + 12);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(1, 1)->note, NOTE_MIN + 12 + 95);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(2, 1)->note, NOTE_KEYOFF);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 0)->IsEmpty(), true);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsEmpty(), false);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsPcNote(), false);
Expand Down Expand Up @@ -3128,7 +3131,7 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile)
VERIFY_EQUAL_NONCONT(pIns->pluginVelocityHandling, PLUGIN_VELOCITYHANDLING_VOLUME);
VERIFY_EQUAL_NONCONT(pIns->pluginVolumeHandling, PLUGIN_VOLUMEHANDLING_MIDI);

for(size_t i = 0; i < NOTE_MAX; i++)
for(size_t i = 0; i < 120; i++)
{
VERIFY_EQUAL_NONCONT(pIns->Keyboard[i], (i == NOTE_MIDDLEC - 1) ? (ins * 1111) : 1);
VERIFY_EQUAL_NONCONT(pIns->NoteMap[i], (i == NOTE_MIDDLEC - 1) ? (i + 13) : (i + 1));
Expand Down Expand Up @@ -3211,6 +3214,17 @@ static void TestLoadMPTMFile(const CSoundFile &sndFile)
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->instr, 99);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->GetValueVolCol(), 1);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 0)->GetValueEffectCol(), 200);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(1, 0)->IsPcNote(), true);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(1, 0)->note, NOTE_PCS);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(1, 0)->instr, 250);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(1, 0)->GetValueVolCol(), 999);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(1, 0)->GetValueEffectCol(), 999);

VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(0, 1)->note, NOTE_MIN);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(1, 1)->note, NOTE_MIN + 119);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(2, 1)->note, NOTE_KEYOFF);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(3, 1)->note, NOTE_NOTECUT);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(4, 1)->note, NOTE_FADE);

VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 0)->IsEmpty(), true);
VERIFY_EQUAL_NONCONT(sndFile.Patterns[1].GetpModCommand(31, 1)->IsEmpty(), false);
Expand Down
Binary file modified test/test.mptm
Binary file not shown.
Binary file modified test/test.xm
Binary file not shown.

0 comments on commit f5e896e

Please sign in to comment.