diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/ModConvert.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/ModConvert.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/ModConvert.cpp | 675 |
1 files changed, 675 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/ModConvert.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/ModConvert.cpp new file mode 100644 index 00000000..37757b7d --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/ModConvert.cpp @@ -0,0 +1,675 @@ +/* + * 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 |