123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- /*
- * ModConvert.cpp
- * --------------
- * Purpose: Converting between various module formats.
- * Notes : Incomplete list of MPTm-only features and extensions in the old formats:
- * Features only available for MPTm:
- * - User definable tunings.
- * - Extended pattern range
- * - Extended sequence
- * - Multiple sequences ("songs")
- * - Pattern-specific time signatures
- * - Pattern effects :xy, S7D, S7E
- * - Long instrument envelopes
- * - Envelope release node (this was previously also usable in the IT format, but is now deprecated in that format)
- * - Fractional tempo
- * - Song-specific resampling
- * - Alternative tempo modes (only usable in legacy XM / IT files)
- *
- * Extended features in IT/XM/S3M (not all listed below are available in all of those formats):
- * - Plugins
- * - Extended ranges for
- * - Sample count
- * - Instrument count
- * - Pattern count
- * - Sequence size
- * - Row count
- * - Channel count
- * - Tempo limits
- * - Extended sample/instrument properties.
- * - MIDI mapping directives
- * - Version info
- * - Channel names
- * - Pattern names
- * - For more info, see e.g. SaveExtendedSongProperties(), SaveExtendedInstrumentProperties()
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Moddoc.h"
- #include "Mainfrm.h"
- #include "InputHandler.h"
- #include "../tracklib/SampleEdit.h"
- #include "../soundlib/modsmp_ctrl.h"
- #include "../soundlib/mod_specifications.h"
- #include "ModConvert.h"
- OPENMPT_NAMESPACE_BEGIN
- // Trim envelopes and remove release nodes.
- static void UpdateEnvelopes(InstrumentEnvelope &mptEnv, const CModSpecifications &specs, std::bitset<wNumWarnings> &warnings)
- {
- // shorten instrument envelope if necessary (for mod conversion)
- const uint8 envMax = specs.envelopePointsMax;
- #define TRIMENV(envLen) if(envLen >= envMax) { envLen = envMax - 1; warnings.set(wTrimmedEnvelopes); }
- if(mptEnv.size() > envMax)
- {
- mptEnv.resize(envMax);
- warnings.set(wTrimmedEnvelopes);
- }
- TRIMENV(mptEnv.nLoopStart);
- TRIMENV(mptEnv.nLoopEnd);
- TRIMENV(mptEnv.nSustainStart);
- TRIMENV(mptEnv.nSustainEnd);
- if(mptEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET)
- {
- if(specs.hasReleaseNode)
- {
- TRIMENV(mptEnv.nReleaseNode);
- } else
- {
- mptEnv.nReleaseNode = ENV_RELEASE_NODE_UNSET;
- warnings.set(wReleaseNode);
- }
- }
- #undef TRIMENV
- }
- bool CModDoc::ChangeModType(MODTYPE nNewType)
- {
- std::bitset<wNumWarnings> warnings;
- warnings.reset();
- PATTERNINDEX nResizedPatterns = 0;
- const MODTYPE nOldType = m_SndFile.GetType();
-
- if(nNewType == nOldType)
- return true;
- const bool oldTypeIsXM = (nOldType == MOD_TYPE_XM),
- oldTypeIsS3M = (nOldType == MOD_TYPE_S3M), oldTypeIsIT = (nOldType == MOD_TYPE_IT),
- oldTypeIsMPT = (nOldType == MOD_TYPE_MPT),
- oldTypeIsS3M_IT_MPT = (oldTypeIsS3M || oldTypeIsIT || oldTypeIsMPT),
- oldTypeIsIT_MPT = (oldTypeIsIT || oldTypeIsMPT);
- const bool newTypeIsMOD = (nNewType == MOD_TYPE_MOD), newTypeIsXM = (nNewType == MOD_TYPE_XM),
- newTypeIsS3M = (nNewType == MOD_TYPE_S3M), newTypeIsIT = (nNewType == MOD_TYPE_IT),
- newTypeIsMPT = (nNewType == MOD_TYPE_MPT), newTypeIsMOD_XM = (newTypeIsMOD || newTypeIsXM),
- newTypeIsIT_MPT = (newTypeIsIT || newTypeIsMPT);
- const CModSpecifications &specs = m_SndFile.GetModSpecifications(nNewType);
- // Check if conversion to 64 rows is necessary
- for(const auto &pat : m_SndFile.Patterns)
- {
- if(pat.IsValid() && pat.GetNumRows() != 64)
- nResizedPatterns++;
- }
- if((m_SndFile.GetNumInstruments() || nResizedPatterns) && (nNewType & (MOD_TYPE_MOD|MOD_TYPE_S3M)))
- {
- if(Reporting::Confirm(
- "This operation will convert all instruments to samples,\n"
- "and resize all patterns to 64 rows.\n"
- "Do you want to continue?", "Warning") != cnfYes) return false;
- BeginWaitCursor();
- CriticalSection cs;
- // Converting instruments to samples
- if(m_SndFile.GetNumInstruments())
- {
- ConvertInstrumentsToSamples();
- warnings.set(wInstrumentsToSamples);
- }
- // Resizing all patterns to 64 rows
- for(auto &pat : m_SndFile.Patterns) if(pat.IsValid() && pat.GetNumRows() != 64)
- {
- ROWINDEX origRows = pat.GetNumRows();
- pat.Resize(64);
- if(origRows < 64)
- {
- // Try to save short patterns by inserting a pattern break.
- pat.WriteEffect(EffectWriter(CMD_PATTERNBREAK, 0).Row(origRows - 1).RetryNextRow());
- }
- warnings.set(wResizedPatterns);
- }
- // Removing all instrument headers from channels
- for(auto &chn : m_SndFile.m_PlayState.Chn)
- {
- chn.pModInstrument = nullptr;
- }
- for(INSTRUMENTINDEX nIns = 0; nIns <= m_SndFile.GetNumInstruments(); nIns++)
- {
- delete m_SndFile.Instruments[nIns];
- m_SndFile.Instruments[nIns] = nullptr;
- }
- m_SndFile.m_nInstruments = 0;
- EndWaitCursor();
- } //End if (((m_SndFile.m_nInstruments) || (b64)) && (nNewType & (MOD_TYPE_MOD|MOD_TYPE_S3M)))
- BeginWaitCursor();
- /////////////////////////////
- // Converting pattern data
- // When converting to MOD, get the new sample transpose setting right here so that we can compensate notes in the pattern.
- if(newTypeIsMOD && !oldTypeIsXM)
- {
- for(SAMPLEINDEX smp = 1; smp <= m_SndFile.GetNumSamples(); smp++)
- {
- m_SndFile.GetSample(smp).FrequencyToTranspose();
- }
- }
- bool onlyAmigaNotes = true;
- for(auto &pat : m_SndFile.Patterns) if(pat.IsValid())
- {
- // This is used for -> MOD/XM conversion
- std::vector<std::array<ModCommand::PARAM, MAX_EFFECTS>> effMemory(GetNumChannels());
- std::vector<ModCommand::VOL> volMemory(GetNumChannels(), 0);
- std::vector<ModCommand::INSTR> instrMemory(GetNumChannels(), 0);
- bool addBreak = false; // When converting to XM, avoid the E60 bug.
- CHANNELINDEX chn = 0;
- ROWINDEX row = 0;
- for(auto m = pat.begin(); m != pat.end(); m++, chn++)
- {
- if(chn >= GetNumChannels())
- {
- chn = 0;
- row++;
- }
- ModCommand::INSTR instr = m->instr;
- if(m->instr) instrMemory[chn] = instr;
- else instr = instrMemory[chn];
- // Deal with volume column slide memory (it's not shared with the effect column)
- if(oldTypeIsIT_MPT && (newTypeIsMOD_XM || newTypeIsS3M))
- {
- switch(m->volcmd)
- {
- case VOLCMD_VOLSLIDEUP:
- case VOLCMD_VOLSLIDEDOWN:
- case VOLCMD_FINEVOLUP:
- case VOLCMD_FINEVOLDOWN:
- if(m->vol == 0)
- m->vol = volMemory[chn];
- else
- volMemory[chn] = m->vol;
- break;
- }
- }
- // Deal with MOD/XM commands without effect memory
- if(oldTypeIsS3M_IT_MPT && newTypeIsMOD_XM)
- {
- switch(m->command)
- {
- // No effect memory in XM / MOD
- case CMD_ARPEGGIO:
- case CMD_S3MCMDEX:
- case CMD_MODCMDEX:
- // These have effect memory in XM, but it is spread over several commands (for fine and extra-fine slides), so the easiest way to fix this is to just always use the previous value.
- case CMD_PORTAMENTOUP:
- case CMD_PORTAMENTODOWN:
- case CMD_VOLUMESLIDE:
- if(m->param == 0)
- m->param = effMemory[chn][m->command];
- else
- effMemory[chn][m->command] = m->param;
- break;
- }
- }
- // Adjust effect memory for MOD files
- if(newTypeIsMOD)
- {
- switch(m->command)
- {
- case CMD_PORTAMENTOUP:
- case CMD_PORTAMENTODOWN:
- case CMD_TONEPORTAVOL:
- case CMD_VIBRATOVOL:
- case CMD_VOLUMESLIDE:
- // ProTracker doesn't have effect memory for these commands, so let's try to fix them
- if(m->param == 0)
- m->param = effMemory[chn][m->command];
- else
- effMemory[chn][m->command] = m->param;
- break;
- }
- // Compensate for loss of transpose information
- if(m->IsNote() && instr && instr <= GetNumSamples())
- {
- const int newNote = m->note + m_SndFile.GetSample(instr).RelativeTone;
- m->note = static_cast<ModCommand::NOTE>(Clamp(newNote, specs.noteMin, specs.noteMax));
- }
- if(!m->IsAmigaNote())
- {
- onlyAmigaNotes = false;
- }
- }
- m->Convert(nOldType, nNewType, m_SndFile);
- // When converting to XM, avoid the E60 bug.
- if(newTypeIsXM)
- {
- switch(m->command)
- {
- case CMD_MODCMDEX:
- if(m->param == 0x60 && row > 0)
- {
- addBreak = true;
- }
- break;
- case CMD_POSITIONJUMP:
- case CMD_PATTERNBREAK:
- addBreak = false;
- break;
- }
- }
- // Fix Row Delay commands when converting between MOD/XM and S3M/IT.
- // FT2 only considers the rightmost command, ST3/IT only the leftmost...
- if((nOldType & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT)) && (nNewType & (MOD_TYPE_MOD | MOD_TYPE_XM))
- && m->command == CMD_MODCMDEX && (m->param & 0xF0) == 0xE0)
- {
- if(oldTypeIsIT_MPT || m->param != 0xE0)
- {
- // If the leftmost row delay command is SE0, ST3 ignores it, IT doesn't.
- // Delete all commands right of the first command
- auto p = m + 1;
- for(CHANNELINDEX c = chn + 1; c < m_SndFile.GetNumChannels(); c++, p++)
- {
- if(p->command == CMD_S3MCMDEX && (p->param & 0xF0) == 0xE0)
- {
- p->command = CMD_NONE;
- }
- }
- }
- } else if((nOldType & (MOD_TYPE_MOD | MOD_TYPE_XM)) && (nNewType & (MOD_TYPE_S3M | MOD_TYPE_IT | MOD_TYPE_MPT))
- && m->command == CMD_S3MCMDEX && (m->param & 0xF0) == 0xE0)
- {
- // Delete all commands left of the last command
- auto p = m - 1;
- for(CHANNELINDEX c = 0; c < chn; c++, p--)
- {
- if(p->command == CMD_S3MCMDEX && (p->param & 0xF0) == 0xE0)
- {
- p->command = CMD_NONE;
- }
- }
- }
- }
- if(addBreak)
- {
- pat.WriteEffect(EffectWriter(CMD_PATTERNBREAK, 0).Row(pat.GetNumRows() - 1));
- }
- }
- ////////////////////////////////////////////////
- // Converting instrument / sample / etc. data
- // Do some sample conversion
- const bool newTypeHasPingPongLoops = !(newTypeIsMOD || newTypeIsS3M);
- for(SAMPLEINDEX smp = 1; smp <= m_SndFile.GetNumSamples(); smp++)
- {
- ModSample &sample = m_SndFile.GetSample(smp);
- GetSampleUndo().PrepareUndo(smp, sundo_none, "Song Conversion");
- // Too many samples? Only 31 samples allowed in MOD format...
- if(newTypeIsMOD && smp > 31 && sample.nLength > 0)
- {
- warnings.set(wMOD31Samples);
- }
- // No auto-vibrato in MOD/S3M
- if((newTypeIsMOD || newTypeIsS3M) && (sample.nVibDepth | sample.nVibRate | sample.nVibSweep) != 0)
- {
- warnings.set(wSampleAutoVibrato);
- }
- // No sustain loops for MOD/S3M/XM
- bool ignoreLoopConversion = false;
- if(newTypeIsMOD_XM || newTypeIsS3M)
- {
- // Sustain loops - convert to normal loops
- if(sample.uFlags[CHN_SUSTAINLOOP])
- {
- warnings.set(wSampleSustainLoops);
- // Prepare conversion to regular loop
- if(!newTypeHasPingPongLoops)
- {
- ignoreLoopConversion = true;
- if(!SampleEdit::ConvertPingPongLoop(sample, m_SndFile, true))
- warnings.set(wSampleBidiLoops);
- }
- }
- }
- // No ping-pong loops in MOD/S3M
- if(!ignoreLoopConversion && !newTypeHasPingPongLoops && sample.HasPingPongLoop())
- {
- if(!SampleEdit::ConvertPingPongLoop(sample, m_SndFile, false))
- warnings.set(wSampleBidiLoops);
- }
- if(newTypeIsMOD && sample.RelativeTone != 0)
- {
- warnings.set(wMODSampleFrequency);
- }
- if(!CSoundFile::SupportsOPL(nNewType) && sample.uFlags[CHN_ADLIB])
- {
- warnings.set(wAdlibInstruments);
- }
- sample.Convert(nOldType, nNewType);
- }
- for(INSTRUMENTINDEX ins = 1; ins <= m_SndFile.GetNumInstruments(); ins++)
- {
- ModInstrument *pIns = m_SndFile.Instruments[ins];
- if(pIns == nullptr)
- {
- continue;
- }
- // Convert IT/MPT to XM (fix instruments)
- if(newTypeIsXM)
- {
- for(size_t i = 0; i < std::size(pIns->NoteMap); i++)
- {
- if (pIns->NoteMap[i] && pIns->NoteMap[i] != (i + 1))
- {
- warnings.set(wBrokenNoteMap);
- break;
- }
- }
- // Convert sustain loops to sustain "points"
- if(pIns->VolEnv.nSustainStart != pIns->VolEnv.nSustainEnd)
- {
- warnings.set(wInstrumentSustainLoops);
- }
- if(pIns->PanEnv.nSustainStart != pIns->PanEnv.nSustainEnd)
- {
- warnings.set(wInstrumentSustainLoops);
- }
- }
- // Convert MPT to anything - remove instrument tunings, Pitch/Tempo Lock, filter variation
- if(oldTypeIsMPT)
- {
- if(pIns->pTuning != nullptr)
- {
- warnings.set(wInstrumentTuning);
- }
- if(pIns->pitchToTempoLock.GetRaw() != 0)
- {
- warnings.set(wPitchToTempoLock);
- }
- if((pIns->nCutSwing | pIns->nResSwing) != 0)
- {
- warnings.set(wFilterVariation);
- }
- }
- pIns->Convert(nOldType, nNewType);
- }
- if(newTypeIsMOD)
- {
- // Not supported in MOD format
- auto firstPat = std::find_if(m_SndFile.Order().cbegin(), m_SndFile.Order().cend(), [this](PATTERNINDEX pat) { return m_SndFile.Patterns.IsValidPat(pat); });
- bool firstPatValid = firstPat != m_SndFile.Order().cend();
- bool lossy = false;
- if(m_SndFile.m_nDefaultSpeed != 6)
- {
- if(firstPatValid)
- {
- m_SndFile.Patterns[*firstPat].WriteEffect(EffectWriter(CMD_SPEED, ModCommand::PARAM(m_SndFile.m_nDefaultSpeed)).RetryNextRow());
- }
- m_SndFile.m_nDefaultSpeed = 6;
- lossy = true;
- }
- if(m_SndFile.m_nDefaultTempo != TEMPO(125, 0))
- {
- if(firstPatValid)
- {
- m_SndFile.Patterns[*firstPat].WriteEffect(EffectWriter(CMD_TEMPO, ModCommand::PARAM(m_SndFile.m_nDefaultTempo.GetInt())).RetryNextRow());
- }
- m_SndFile.m_nDefaultTempo.Set(125);
- lossy = true;
- }
- if(m_SndFile.m_nDefaultGlobalVolume != MAX_GLOBAL_VOLUME || m_SndFile.m_nSamplePreAmp != 48 || m_SndFile.m_nVSTiVolume != 48)
- {
- m_SndFile.m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
- m_SndFile.m_nSamplePreAmp = 48;
- m_SndFile.m_nVSTiVolume = 48;
- lossy = true;
- }
- if(lossy)
- {
- warnings.set(wMODGlobalVars);
- }
- }
- // Is the "restart position" value allowed in this format?
- for(SEQUENCEINDEX seq = 0; seq < m_SndFile.Order.GetNumSequences(); seq++)
- {
- if(m_SndFile.Order(seq).GetRestartPos() > 0 && !specs.hasRestartPos)
- {
- // Try to fix it by placing a pattern jump command in the pattern.
- if(!m_SndFile.Order.RestartPosToPattern(seq))
- {
- // Couldn't fix it! :(
- warnings.set(wRestartPos);
- }
- }
- }
- // Fix channel settings (pan/vol)
- for(CHANNELINDEX nChn = 0; nChn < GetNumChannels(); nChn++)
- {
- if(newTypeIsMOD_XM || newTypeIsS3M)
- {
- if(m_SndFile.ChnSettings[nChn].nVolume != 64 || m_SndFile.ChnSettings[nChn].dwFlags[CHN_SURROUND])
- {
- m_SndFile.ChnSettings[nChn].nVolume = 64;
- m_SndFile.ChnSettings[nChn].dwFlags.reset(CHN_SURROUND);
- warnings.set(wChannelVolSurround);
- }
- }
- if(newTypeIsXM)
- {
- if(m_SndFile.ChnSettings[nChn].nPan != 128)
- {
- m_SndFile.ChnSettings[nChn].nPan = 128;
- warnings.set(wChannelPanning);
- }
- }
- }
- // Check for patterns with custom time signatures (fixing will be applied in the pattern container)
- if(!specs.hasPatternSignatures)
- {
- for(const auto &pat: m_SndFile.Patterns)
- {
- if(pat.GetOverrideSignature())
- {
- warnings.set(wPatternSignatures);
- break;
- }
- }
- }
- // Check whether the new format supports embedding the edit history in the file.
- if(oldTypeIsIT_MPT && !newTypeIsIT_MPT && GetSoundFile().GetFileHistory().size() > 0)
- {
- warnings.set(wEditHistory);
- }
- if((nOldType & MOD_TYPE_XM) && m_SndFile.m_playBehaviour[kFT2VolumeRamping])
- {
- warnings.set(wVolRamp);
- }
- CriticalSection cs;
- m_SndFile.ChangeModTypeTo(nNewType);
- // In case we need to update IT bidi loop handling pre-computation or loops got changed...
- m_SndFile.PrecomputeSampleLoops(false);
- // Song flags
- if(!(specs.songFlags & SONG_LINEARSLIDES) && m_SndFile.m_SongFlags[SONG_LINEARSLIDES])
- {
- warnings.set(wLinearSlides);
- }
- if(oldTypeIsXM && newTypeIsIT_MPT)
- {
- m_SndFile.m_SongFlags.set(SONG_ITCOMPATGXX);
- } else if(newTypeIsMOD && GetNumChannels() == 4 && onlyAmigaNotes)
- {
- m_SndFile.m_SongFlags.set(SONG_ISAMIGA);
- m_SndFile.InitAmigaResampler();
- }
- m_SndFile.m_SongFlags &= (specs.songFlags | SONG_PLAY_FLAGS);
- // Adjust mix levels
- if(newTypeIsMOD || newTypeIsS3M)
- {
- m_SndFile.SetMixLevels(MixLevels::Compatible);
- }
- if(oldTypeIsMPT && m_SndFile.GetMixLevels() != MixLevels::Compatible && m_SndFile.GetMixLevels() != MixLevels::CompatibleFT2)
- {
- warnings.set(wMixmode);
- }
- if(!specs.hasFractionalTempo && m_SndFile.m_nDefaultTempo.GetFract() != 0)
- {
- m_SndFile.m_nDefaultTempo.Set(m_SndFile.m_nDefaultTempo.GetInt(), 0);
- warnings.set(wFractionalTempo);
- }
- ChangeFileExtension(nNewType);
- // Check mod specifications
- Limit(m_SndFile.m_nDefaultTempo, specs.GetTempoMin(), specs.GetTempoMax());
- Limit(m_SndFile.m_nDefaultSpeed, specs.speedMin, specs.speedMax);
- for(INSTRUMENTINDEX i = 1; i <= m_SndFile.GetNumInstruments(); i++) if(m_SndFile.Instruments[i] != nullptr)
- {
- UpdateEnvelopes(m_SndFile.Instruments[i]->VolEnv, specs, warnings);
- UpdateEnvelopes(m_SndFile.Instruments[i]->PanEnv, specs, warnings);
- UpdateEnvelopes(m_SndFile.Instruments[i]->PitchEnv, specs, warnings);
- }
- // XM requires instruments, so we create them right away.
- if(newTypeIsXM && GetNumInstruments() == 0)
- {
- ConvertSamplesToInstruments();
- }
- // XM has no global volume
- if(newTypeIsXM && m_SndFile.m_nDefaultGlobalVolume != MAX_GLOBAL_VOLUME)
- {
- if(!GlobalVolumeToPattern())
- {
- warnings.set(wGlobalVolumeNotSupported);
- }
- }
- // Resampling is only saved in MPTM
- if(!newTypeIsMPT && m_SndFile.m_nResampling != SRCMODE_DEFAULT)
- {
- warnings.set(wResamplingMode);
- m_SndFile.m_nResampling = SRCMODE_DEFAULT;
- }
- cs.Leave();
- if(warnings[wResizedPatterns])
- {
- AddToLog(LogInformation, MPT_UFORMAT("{} patterns have been resized to 64 rows")(nResizedPatterns));
- }
- static constexpr struct
- {
- ConversionWarning warning;
- const char *mesage;
- } messages[] =
- {
- // Pattern warnings
- { wRestartPos, "Restart position is not supported by the new format." },
- { wPatternSignatures, "Pattern-specific time signatures are not supported by the new format." },
- { wChannelVolSurround, "Channel volume and surround are not supported by the new format." },
- { wChannelPanning, "Channel panning is not supported by the new format." },
- // Sample warnings
- { wSampleBidiLoops, "Sample bidi loops are not supported by the new format." },
- { wSampleSustainLoops, "New format doesn't support sample sustain loops." },
- { wSampleAutoVibrato, "New format doesn't support sample autovibrato." },
- { wMODSampleFrequency, "Sample C-5 frequencies will be lost." },
- { wMOD31Samples, "Samples above 31 will be lost when saving as MOD. Consider rearranging samples if there are unused slots available." },
- { wAdlibInstruments, "OPL instruments are not supported by this format." },
- // Instrument warnings
- { wInstrumentsToSamples, "All instruments have been converted to samples." },
- { wTrimmedEnvelopes, "Instrument envelopes have been shortened." },
- { wInstrumentSustainLoops, "Sustain loops were converted to sustain points." },
- { wInstrumentTuning, "Instrument tunings will be lost." },
- { wPitchToTempoLock, "Pitch / Tempo Lock instrument property is not supported by the new format." },
- { wBrokenNoteMap, "Instrument Note Mapping is not supported by the new format." },
- { wReleaseNode, "Instrument envelope release nodes are not supported by the new format." },
- { wFilterVariation, "Random filter variation is not supported by the new format." },
- // General warnings
- { wMODGlobalVars, "Default speed, tempo and global volume will be lost." },
- { wLinearSlides, "Linear Frequency Slides not supported by the new format." },
- { wEditHistory, "Edit history will not be saved in the new format." },
- { wMixmode, "Consider setting the mix levels to \"Compatible\" in the song properties when working with legacy formats." },
- { wVolRamp, "Fasttracker 2 compatible super soft volume ramping gets lost when converting XM files to another type." },
- { wGlobalVolumeNotSupported, "Default global volume is not supported by the new format." },
- { wResamplingMode, "Song-specific resampling mode is not supported by the new format." },
- { wFractionalTempo, "Fractional tempo is not supported by the new format." },
- };
- for(const auto &msg : messages)
- {
- if(warnings[msg.warning])
- AddToLog(LogInformation, mpt::ToUnicode(mpt::Charset::UTF8, msg.mesage));
- }
- SetModified();
- GetPatternUndo().ClearUndo();
- UpdateAllViews(nullptr, GeneralHint().General().ModType());
- EndWaitCursor();
- // Update effect key commands
- CMainFrame::GetInputHandler()->SetEffectLetters(m_SndFile.GetModSpecifications());
- return true;
- }
- OPENMPT_NAMESPACE_END
|