From: <sv...@op...> - 2024-12-11 17:58:44
|
Author: sagamusix Date: Wed Dec 11 18:58:26 2024 New Revision: 22513 URL: https://source.openmpt.org/browse/openmpt/?op=revision&rev=22513 Log: Merged revision(s) 22512 from trunk/OpenMPT: [Imp] MPT Hacks: Warn when an XM instrument uses more than 16 samples (https://bugs.openmpt.org/view.php?id=1846). [Imp] XM: Warn when saving an instrument that references more than 16/32 samples (https://bugs.openmpt.org/view.php?id=1846). ........ Modified: branches/OpenMPT-1.31/ (props changed) branches/OpenMPT-1.31/mptrack/MPTHacks.cpp branches/OpenMPT-1.31/soundlib/Load_xm.cpp branches/OpenMPT-1.31/soundlib/SampleFormats.cpp branches/OpenMPT-1.31/soundlib/XMTools.cpp branches/OpenMPT-1.31/soundlib/XMTools.h Modified: branches/OpenMPT-1.31/mptrack/MPTHacks.cpp ============================================================================== --- branches/OpenMPT-1.31/mptrack/MPTHacks.cpp Wed Dec 11 18:56:57 2024 (r22512) +++ branches/OpenMPT-1.31/mptrack/MPTHacks.cpp Wed Dec 11 18:58:26 2024 (r22513) @@ -299,6 +299,7 @@ // Check for instrument extensions foundHere = false; bool foundEnvelopes = false; + INSTRUMENTINDEX instrWithTooManySmps = 0; for(INSTRUMENTINDEX i = 1; i <= m_SndFile.GetNumInstruments(); i++) { ModInstrument *instr = m_SndFile.Instruments[i]; @@ -334,6 +335,9 @@ instr->nFadeOut = ((instr->nFadeOut + 16) / 32) * 32; } + if(modType == MOD_TYPE_XM && instr->GetSamples().size() > 16) + instrWithTooManySmps++; + // Incompatible envelope shape foundEnvelopes |= FindIncompatibleEnvelopes(instr->VolEnv, autofix); foundEnvelopes |= FindIncompatibleEnvelopes(instr->PanEnv, autofix); @@ -344,6 +348,8 @@ AddToLog("Found MPT instrument extensions"); if(foundEnvelopes) AddToLog("Two envelope points may not share the same tick."); + if(instrWithTooManySmps) + AddToLog(MPT_AFORMAT("{} instruments use too many samples (16 allowed)")(instrWithTooManySmps)); // Check for too many orders if(m_SndFile.Order().GetLengthTailTrimmed() > originalSpecs->ordersMax) Modified: branches/OpenMPT-1.31/soundlib/Load_xm.cpp ============================================================================== --- branches/OpenMPT-1.31/soundlib/Load_xm.cpp Wed Dec 11 18:56:57 2024 (r22512) +++ branches/OpenMPT-1.31/soundlib/Load_xm.cpp Wed Dec 11 18:58:26 2024 (r22513) @@ -1389,9 +1389,11 @@ if(Instruments[ins] != nullptr) { // Convert instrument - insHeader.ConvertToXM(*Instruments[ins], compatibilityExport); + auto sampleList = insHeader.ConvertToXM(*Instruments[ins], compatibilityExport); + samples = std::move(sampleList.samples); + if(sampleList.tooManySamples) + AddToLog(LogInformation, MPT_UFORMAT("Instrument {} references too many samples, only the first {} will be exported.")(ins, samples.size())); - samples = insHeader.instrument.GetSampleList(*Instruments[ins], compatibilityExport); if(samples.size() > 0 && samples[0] <= GetNumSamples()) { // Copy over auto-vibrato settings of first sample Modified: branches/OpenMPT-1.31/soundlib/SampleFormats.cpp ============================================================================== --- branches/OpenMPT-1.31/soundlib/SampleFormats.cpp Wed Dec 11 18:56:57 2024 (r22512) +++ branches/OpenMPT-1.31/soundlib/SampleFormats.cpp Wed Dec 11 18:58:26 2024 (r22513) @@ -1360,9 +1360,11 @@ // Create file header XIInstrumentHeader header; - header.ConvertToXM(*pIns, false); + const auto sampleList = header.ConvertToXM(*pIns, false); + const auto &samples = sampleList.samples; + if(sampleList.tooManySamples) + AddToLog(LogInformation, MPT_UFORMAT("This instrument references too many samples, only the first {} will be exported.")(samples.size())); - const std::vector<SAMPLEINDEX> samples = header.instrument.GetSampleList(*pIns, false); if(samples.size() > 0 && samples[0] <= GetNumSamples()) { // Copy over auto-vibrato settings of first sample Modified: branches/OpenMPT-1.31/soundlib/XMTools.cpp ============================================================================== --- branches/OpenMPT-1.31/soundlib/XMTools.cpp Wed Dec 11 18:56:57 2024 (r22512) +++ branches/OpenMPT-1.31/soundlib/XMTools.cpp Wed Dec 11 18:58:26 2024 (r22513) @@ -54,7 +54,7 @@ // Convert OpenMPT's internal sample representation to an XMInstrument. -uint16 XMInstrument::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) +XMInstrument::SampleList XMInstrument::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) { MemsetZero(*this); @@ -74,53 +74,49 @@ pitchWheelRange = std::min(mptIns.midiPWD, int8(36)); // Create sample assignment table - auto sampleList = GetSampleList(mptIns, compatibilityExport); + const auto sampleList = GetSampleList(mptIns, compatibilityExport); for(std::size_t i = 0; i < std::size(sampleMap); i++) { if(mptIns.Keyboard[i + 12] > 0) { - auto sample = std::find(sampleList.begin(), sampleList.end(), mptIns.Keyboard[i + 12]); - if(sample != sampleList.end()) + auto sample = std::find(sampleList.samples.begin(), sampleList.samples.end(), mptIns.Keyboard[i + 12]); + if(sample != sampleList.samples.end()) { // Yep, we want to export this sample. - sampleMap[i] = static_cast<uint8>(sample - sampleList.begin()); + sampleMap[i] = static_cast<uint8>(std::distance(sampleList.samples.begin(), sample)); } } } - return static_cast<uint16>(sampleList.size()); + return sampleList; } // Get a list of samples that should be written to the file. -std::vector<SAMPLEINDEX> XMInstrument::GetSampleList(const ModInstrument &mptIns, bool compatibilityExport) const +XMInstrument::SampleList XMInstrument::GetSampleList(const ModInstrument &mptIns, bool compatibilityExport) const { - std::vector<SAMPLEINDEX> sampleList; // List of samples associated with this instrument - std::vector<bool> addedToList; // Which samples did we already add to the sample list? + SampleList sampleList; // List of samples associated with this instrument + std::vector<bool> addedToList; // Which samples did we already add to the sample list? - uint8 numSamples = 0; for(std::size_t i = 0; i < std::size(sampleMap); i++) { const SAMPLEINDEX smp = mptIns.Keyboard[i + 12]; - if(smp > 0) - { - if(smp > addedToList.size()) - { - addedToList.resize(smp, false); - } - - if(!addedToList[smp - 1] && numSamples < (compatibilityExport ? 16 : 32)) - { - // We haven't considered this sample yet. - addedToList[smp - 1] = true; - numSamples++; - sampleList.push_back(smp); - } - } + if(smp == 0) + continue; + if(smp > addedToList.size()) + addedToList.resize(smp, false); + if(addedToList[smp - 1]) + continue; + // We haven't considered this sample yet. + addedToList[smp - 1] = true; + if(sampleList.samples.size() < (compatibilityExport ? 16 : 32)) + sampleList.samples.push_back(smp); + else + sampleList.tooManySamples = true; } // FT2 completely ignores MIDI settings (and also the less important stuff) if not at least one (empty) sample is assigned to this instrument! - if(sampleList.empty() && compatibilityExport && midiEnabled) - sampleList.assign(1, 0); + if(sampleList.samples.empty() && compatibilityExport && midiEnabled) + sampleList.samples.assign(1, 0); return sampleList; } @@ -247,12 +243,14 @@ // Convert OpenMPT's internal sample representation to an XMInstrumentHeader. -void XMInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) +XMInstrument::SampleList XMInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) { - numSamples = instrument.ConvertToXM(mptIns, compatibilityExport); + const auto sampleList = instrument.ConvertToXM(mptIns, compatibilityExport); + numSamples = static_cast<uint16>(sampleList.samples.size()); mpt::String::WriteBuf(mpt::String::spacePadded, name) = mptIns.name; type = mptIns.nMidiProgram; // If FT2 writes crap here, we can do so, too! (we probably shouldn't, though. This is just for backwards compatibility with old MPT versions.) + return sampleList; } @@ -284,9 +282,10 @@ // Convert OpenMPT's internal sample representation to an XIInstrumentHeader. -void XIInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) +XMInstrument::SampleList XIInstrumentHeader::ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport) { - numSamples = instrument.ConvertToXM(mptIns, compatibilityExport); + const auto sampleList = instrument.ConvertToXM(mptIns, compatibilityExport); + numSamples = static_cast<uint16>(sampleList.samples.size()); memcpy(signature, "Extended Instrument: ", 21); mpt::String::WriteBuf(mpt::String::spacePadded, name) = mptIns.name; @@ -296,6 +295,7 @@ mpt::String::WriteBuf(mpt::String::spacePadded, trackerName) = openMptTrackerName; version = 0x102; + return sampleList; } Modified: branches/OpenMPT-1.31/soundlib/XMTools.h ============================================================================== --- branches/OpenMPT-1.31/soundlib/XMTools.h Wed Dec 11 18:56:57 2024 (r22512) +++ branches/OpenMPT-1.31/soundlib/XMTools.h Wed Dec 11 18:58:26 2024 (r22513) @@ -28,7 +28,7 @@ }; char signature[17]; // "Extended Module: " - char songName[20]; // Song Name, not null-terminated (any nulls are treated as spaces) + char songName[20]; // Song Name, space-padded uint8le eof; // DOS EOF Character (0x1A) char trackerName[20]; // Software that was used to create the XM file uint16le version; // File version (1.02 - 1.04 are supported) @@ -92,8 +92,14 @@ // Convert XM envelope data to an OpenMPT's internal envelope representation. void ConvertEnvelopeToMPT(InstrumentEnvelope &mptEnv, uint8 numPoints, uint8 flags, uint8 sustain, uint8 loopStart, uint8 loopEnd, EnvType env) const; + struct SampleList + { + std::vector<SAMPLEINDEX> samples; // The list of samples to write to the file + bool tooManySamples; // Does the source instrument contain more samples than what we can write? + }; + // Convert OpenMPT's internal sample representation to an XMInstrument. - uint16 ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); + SampleList ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); // Convert an XMInstrument to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns) const; // Apply auto-vibrato settings from sample to file. @@ -102,7 +108,7 @@ void ApplyAutoVibratoToMPT(ModSample &mptSmp) const; // Get a list of samples that should be written to the file. - std::vector<SAMPLEINDEX> GetSampleList(const ModInstrument &mptIns, bool compatibilityExport) const; + SampleList GetSampleList(const ModInstrument &mptIns, bool compatibilityExport) const; }; MPT_BINARY_STRUCT(XMInstrument, 230) @@ -112,8 +118,8 @@ struct XMInstrumentHeader { uint32le size; // Size of XMInstrumentHeader + XMInstrument - char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces) - uint8le type; // Instrument Type (Apparently FT2 writes some crap here, but it's the same crap for all instruments of the same module!) + char name[22]; // Instrument Name, space-padded + uint8le type; // Instrument Type (FT2 does not initialize this field properly, so it contains a random value, but it's the same random value for all instruments of the same module!) uint16le numSamples; // Number of Samples associated with instrument uint32le sampleHeaderSize; // Size of XMSample XMInstrument instrument; @@ -122,7 +128,7 @@ void Finalise(); // Convert OpenMPT's internal sample representation to an XMInstrument. - void ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); + XMInstrument::SampleList ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); // Convert an XMInstrument to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns) const; }; @@ -139,7 +145,7 @@ }; char signature[21]; // "Extended Instrument: " - char name[22]; // Instrument Name, not null-terminated (any nulls are treated as spaces) + char name[22]; // Instrument Name, space-padded uint8le eof; // DOS EOF Character (0x1A) char trackerName[20]; // Software that was used to create the XI file uint16le version; // File Version (1.02) @@ -147,7 +153,7 @@ uint16le numSamples; // Number of embedded sample headers + samples // Convert OpenMPT's internal sample representation to an XIInstrumentHeader. - void ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); + XMInstrument::SampleList ConvertToXM(const ModInstrument &mptIns, bool compatibilityExport); // Convert an XIInstrumentHeader to OpenMPT's internal instrument representation. void ConvertToMPT(ModInstrument &mptIns) const; }; @@ -176,7 +182,7 @@ uint8le pan; // Sample Panning int8le relnote; // Sample Transpose uint8le reserved; // Reserved (abused for ModPlug's ADPCM compression) - char name[22]; // Sample Name, not null-terminated (any nulls are treated as spaces) + char name[22]; // Sample Name, space-padded // Convert OpenMPT's internal sample representation to an XMSample. void ConvertToXM(const ModSample &mptSmp, MODTYPE fromType, bool compatibilityExport); |