#include "stdafx.h"
#include "Loaders.h"
#include "ITTools.h"
#include "Tables.h"
#include "../common/mptStringBuffer.h"
#include "../common/version.h"
OPENMPT_NAMESPACE_BEGIN
void ITEnvelope::ConvertToIT(const InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 envDefault)
{
if(mptEnv.dwFlags[ENV_ENABLED]) flags |= ITEnvelope::envEnabled;
if(mptEnv.dwFlags[ENV_LOOP]) flags |= ITEnvelope::envLoop;
if(mptEnv.dwFlags[ENV_SUSTAIN]) flags |= ITEnvelope::envSustain;
if(mptEnv.dwFlags[ENV_CARRY]) flags |= ITEnvelope::envCarry;
num = (uint8)std::min(mptEnv.size(), uint32(25));
lpb = (uint8)mptEnv.nLoopStart;
lpe = (uint8)mptEnv.nLoopEnd;
slb = (uint8)mptEnv.nSustainStart;
sle = (uint8)mptEnv.nSustainEnd;
MemsetZero(data);
if(!mptEnv.empty())
{
for(uint32 ev = 0; ev < num; ev++)
{
data[ev].value = static_cast<int8>(mptEnv[ev].value) - envOffset;
data[ev].tick = mptEnv[ev].tick;
}
} else
{
num = 2;
data[0].value = data[1].value = envDefault - envOffset;
data[1].tick = 10;
}
}
void ITEnvelope::ConvertToMPT(InstrumentEnvelope &mptEnv, uint8 envOffset, uint8 maxNodes) const
{
mptEnv.dwFlags.set(ENV_ENABLED, (flags & ITEnvelope::envEnabled) != 0);
mptEnv.dwFlags.set(ENV_LOOP, (flags & ITEnvelope::envLoop) != 0);
mptEnv.dwFlags.set(ENV_SUSTAIN, (flags & ITEnvelope::envSustain) != 0);
mptEnv.dwFlags.set(ENV_CARRY, (flags & ITEnvelope::envCarry) != 0);
mptEnv.resize(std::min(num, maxNodes));
mptEnv.nLoopStart = std::min(lpb, maxNodes);
mptEnv.nLoopEnd = Clamp(lpe, mptEnv.nLoopStart, maxNodes);
mptEnv.nSustainStart = std::min(slb, maxNodes);
mptEnv.nSustainEnd = Clamp(sle, mptEnv.nSustainStart, maxNodes);
for(uint32 ev = 0; ev < std::min<uint32>(25, num); ev++)
{
mptEnv[ev].value = Clamp<int8, int8>(data[ev].value + envOffset, 0, 64);
mptEnv[ev].tick = data[ev].tick;
if(ev > 0 && ev < num && mptEnv[ev].tick < mptEnv[ev - 1].tick)
{
mptEnv[ev].tick &= 0xFF;
mptEnv[ev].tick |= (mptEnv[ev].tick & ~0xFF);
if(mptEnv[ev].tick < mptEnv[ev - 1].tick)
{
mptEnv[ev].tick += 0x100;
}
}
}
}
void ITOldInstrument::ConvertToMPT(ModInstrument &mptIns) const
{
if(memcmp(id, "IMPI", 4))
{
return;
}
mpt::String::Read<mpt::String::spacePadded>(mptIns.name, name);
mpt::String::Read<mpt::String::nullTerminated>(mptIns.filename, filename);
mptIns.nFadeOut = fadeout << 6;
mptIns.nGlobalVol = 64;
mptIns.nPan = 128;
mptIns.nNNA = static_cast<NewNoteAction>(nna.get());
mptIns.nDCT = static_cast<DuplicateCheckType>(dnc.get());
for(size_t i = 0; i < 120; i++)
{
uint8 note = keyboard[i * 2];
SAMPLEINDEX ins = keyboard[i * 2 + 1];
if(ins < MAX_SAMPLES)
{
mptIns.Keyboard[i] = ins;
}
if(note < 120)
{
mptIns.NoteMap[i] = note + 1u;
} else
{
mptIns.NoteMap[i] = static_cast<uint8>(i + 1);
}
}
mptIns.VolEnv.dwFlags.set(ENV_ENABLED, (flags & ITOldInstrument::envEnabled) != 0);
mptIns.VolEnv.dwFlags.set(ENV_LOOP, (flags & ITOldInstrument::envLoop) != 0);
mptIns.VolEnv.dwFlags.set(ENV_SUSTAIN, (flags & ITOldInstrument::envSustain) != 0);
mptIns.VolEnv.nLoopStart = vls;
mptIns.VolEnv.nLoopEnd = vle;
mptIns.VolEnv.nSustainStart = sls;
mptIns.VolEnv.nSustainEnd = sle;
mptIns.VolEnv.resize(25);
for(uint32 i = 0; i < 25; i++)
{
if((mptIns.VolEnv[i].tick = nodes[i * 2]) == 0xFF)
{
mptIns.VolEnv.resize(i);
break;
}
mptIns.VolEnv[i].value = nodes[i * 2 + 1];
}
if(std::max(mptIns.VolEnv.nLoopStart, mptIns.VolEnv.nLoopEnd) >= mptIns.VolEnv.size()) mptIns.VolEnv.dwFlags.reset(ENV_LOOP);
if(std::max(mptIns.VolEnv.nSustainStart, mptIns.VolEnv.nSustainEnd) >= mptIns.VolEnv.size()) mptIns.VolEnv.dwFlags.reset(ENV_SUSTAIN);
}
uint32 ITInstrument::ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile)
{
MemsetZero(*this);
memcpy(id, "IMPI", 4);
trkvers = 0x5000 | static_cast<uint16>(Version::Current().GetRawVersion() >> 16);
mpt::String::Write<mpt::String::nullTerminated>(filename, mptIns.filename);
mpt::String::Write<mpt::String::nullTerminated>(name, mptIns.name);
fadeout = static_cast<uint16>(std::min<uint32>(mptIns.nFadeOut >> 5, 256u));
gbv = static_cast<uint8>(std::min<uint32>(mptIns.nGlobalVol * 2u, 128u));
dfp = static_cast<uint8>(std::min<uint32>(mptIns.nPan / 4u, 64u));
if(!mptIns.dwFlags[INS_SETPANNING]) dfp |= ITInstrument::ignorePanning;
rv = std::min(mptIns.nVolSwing, uint8(100));
rp = std::min(mptIns.nPanSwing, uint8(64));
nna = mptIns.nNNA;
dct = (mptIns.nDCT < DCT_PLUGIN || !compatExport) ? mptIns.nDCT : DCT_NONE;
dca = mptIns.nDNA;
pps = mptIns.nPPS;
ppc = mptIns.nPPC;
ifc = mptIns.GetCutoff() | (mptIns.IsCutoffEnabled() ? ITInstrument::enableCutoff : 0x00);
ifr = mptIns.GetResonance() | (mptIns.IsResonanceEnabled() ? ITInstrument::enableResonance : 0x00);
if(mptIns.nMidiProgram > 0)
mpr = mptIns.nMidiProgram - 1u;
else
mpr = 0xFF;
if(mptIns.wMidiBank > 0)
{
mbank[0] = static_cast<uint8>((mptIns.wMidiBank - 1) & 0x7F);
mbank[1] = static_cast<uint8>((mptIns.wMidiBank - 1) >> 7);
} else
{
mbank[0] = 0xFF;
mbank[1] = 0xFF;
}
if(mptIns.nMidiChannel != MidiNoChannel || mptIns.nMixPlug == 0 || mptIns.nMixPlug > 127 || compatExport)
{
mch = mptIns.nMidiChannel;
} else
{
mch = mptIns.nMixPlug + 128;
}
nos = 0; std::vector<bool> smpCount(sndFile.GetNumSamples(), false);
for(int i = 0; i < 120; i++)
{
keyboard[i * 2] = (mptIns.NoteMap[i] >= NOTE_MIN && mptIns.NoteMap[i] <= NOTE_MAX) ? (mptIns.NoteMap[i] - NOTE_MIN) : static_cast<uint8>(i);
const SAMPLEINDEX smp = mptIns.Keyboard[i];
if(smp < MAX_SAMPLES && smp < 256)
{
keyboard[i * 2 + 1] = static_cast<uint8>(smp);
if(smp && smp <= sndFile.GetNumSamples() && !smpCount[smp - 1])
{
smpCount[smp - 1] = true;
nos++;
}
}
}
volenv.ConvertToIT(mptIns.VolEnv, 0, 64);
panenv.ConvertToIT(mptIns.PanEnv, 32, 32);
pitchenv.ConvertToIT(mptIns.PitchEnv, 32, 32);
if(mptIns.PitchEnv.dwFlags[ENV_FILTER]) pitchenv.flags |= ITEnvelope::envFilter;
return sizeof(ITInstrument);
}
uint32 ITInstrument::ConvertToMPT(ModInstrument &mptIns, MODTYPE modFormat) const
{
if(memcmp(id, "IMPI", 4))
{
return 0;
}
mpt::String::Read<mpt::String::spacePadded>(mptIns.name, name);
mpt::String::Read<mpt::String::nullTerminated>(mptIns.filename, filename);
mptIns.nFadeOut = fadeout << 5;
mptIns.nGlobalVol = gbv / 2;
LimitMax(mptIns.nGlobalVol, 64u);
mptIns.nPan = (dfp & 0x7F) * 4;
if(mptIns.nPan > 256) mptIns.nPan = 128;
mptIns.dwFlags.set(INS_SETPANNING, !(dfp & ITInstrument::ignorePanning));
mptIns.nVolSwing = std::min<uint8>(rv, 100);
mptIns.nPanSwing = std::min<uint8>(rp, 64);
mptIns.nNNA = static_cast<NewNoteAction>(nna.get());
mptIns.nDCT = static_cast<DuplicateCheckType>(dct.get());
mptIns.nDNA = static_cast<DuplicateNoteAction>(dca.get());
mptIns.nPPS = pps;
mptIns.nPPC = ppc;
mptIns.SetCutoff(ifc & 0x7F, (ifc & ITInstrument::enableCutoff) != 0);
mptIns.SetResonance(ifr & 0x7F, (ifr & ITInstrument::enableResonance) != 0);
if((trkvers == 0x0202 || trkvers == 0x0211 || trkvers == 0x0220 || trkvers == 0x0214) && mpr != 0xFF)
{
if(mpr <= 128)
{
mptIns.nMidiProgram = mpr;
}
uint16 bank = mbank[0] | (mbank[1] << 8);
if(bank <= 128)
{
mptIns.wMidiBank = bank;
}
} else
{
if(mpr < 128)
{
mptIns.nMidiProgram = mpr + 1;
}
uint16 bank = 0;
if(mbank[0] < 128)
bank = mbank[0] + 1;
if(mbank[1] < 128)
bank += (mbank[1] << 7);
mptIns.wMidiBank = bank;
}
mptIns.nMidiChannel = mch;
if(mptIns.nMidiChannel >= 128)
{
mptIns.nMixPlug = mptIns.nMidiChannel - 128;
mptIns.nMidiChannel = 0;
}
const uint8 maxNodes = (modFormat & MOD_TYPE_MPT) ? MAX_ENVPOINTS : 25;
volenv.ConvertToMPT(mptIns.VolEnv, 0, maxNodes);
panenv.ConvertToMPT(mptIns.PanEnv, 32, maxNodes);
pitchenv.ConvertToMPT(mptIns.PitchEnv, 32, maxNodes);
mptIns.PitchEnv.dwFlags.set(ENV_FILTER, (pitchenv.flags & ITEnvelope::envFilter) != 0);
for(int i = 0; i < 120; i++)
{
uint8 note = keyboard[i * 2];
SAMPLEINDEX ins = keyboard[i * 2 + 1];
if(ins < MAX_SAMPLES)
{
mptIns.Keyboard[i] = ins;
}
if(note < 120)
{
mptIns.NoteMap[i] = note + NOTE_MIN;
} else
{
mptIns.NoteMap[i] = static_cast<uint8>(i + NOTE_MIN);
}
}
return sizeof(ITInstrument);
}
uint32 ITInstrumentEx::ConvertToIT(const ModInstrument &mptIns, bool compatExport, const CSoundFile &sndFile)
{
uint32 instSize = iti.ConvertToIT(mptIns, compatExport, sndFile);
if(compatExport)
{
return instSize;
}
bool usedExtension = false;
iti.nos = 0;
std::vector<bool> smpCount(sndFile.GetNumSamples(), false);
for(int i = 0; i < 120; i++)
{
const SAMPLEINDEX smp = mptIns.Keyboard[i];
keyboardhi[i] = 0;
if(smp < MAX_SAMPLES)
{
if(smp >= 256)
{
iti.keyboard[i * 2 + 1] = static_cast<uint8>(smp & 0xFF);
keyboardhi[i] = static_cast<uint8>(smp >> 8);
usedExtension = true;
}
if(smp && smp <= sndFile.GetNumSamples() && !smpCount[smp - 1])
{
smpCount[smp - 1] = true;
iti.nos++;
}
}
}
if(usedExtension)
{
memcpy(iti.dummy, "XTPM", 4);
instSize = sizeof(ITInstrumentEx);
}
return instSize;
}
uint32 ITInstrumentEx::ConvertToMPT(ModInstrument &mptIns, MODTYPE fromType) const
{
uint32 insSize = iti.ConvertToMPT(mptIns, fromType);
if(insSize == 0 || (memcmp(iti.dummy, "MPTX", 4) && memcmp(iti.dummy, "XTPM", 4)))
{
return insSize;
}
for(int i = 0; i < 120; i++)
{
mptIns.Keyboard[i] |= ((SAMPLEINDEX)keyboardhi[i] << 8);
}
return sizeof(ITInstrumentEx);
}
void ITSample::ConvertToIT(const ModSample &mptSmp, MODTYPE fromType, bool compress, bool compressIT215, bool allowExternal)
{
MemsetZero(*this);
memcpy(id, "IMPS", 4);
mpt::String::Write<mpt::String::nullTerminated>(filename, mptSmp.filename);
gvl = static_cast<uint8>(mptSmp.nGlobalVol);
vol = static_cast<uint8>(mptSmp.nVolume / 4);
dfp = static_cast<uint8>(mptSmp.nPan / 4);
if(mptSmp.uFlags[CHN_PANNING]) dfp |= ITSample::enablePanning;
if(mptSmp.HasSampleData())
{
flags = ITSample::sampleDataPresent;
if(mptSmp.uFlags[CHN_LOOP]) flags |= ITSample::sampleLoop;
if(mptSmp.uFlags[CHN_SUSTAINLOOP]) flags |= ITSample::sampleSustain;
if(mptSmp.uFlags[CHN_PINGPONGLOOP]) flags |= ITSample::sampleBidiLoop;
if(mptSmp.uFlags[CHN_PINGPONGSUSTAIN]) flags |= ITSample::sampleBidiSustain;
if(mptSmp.uFlags[CHN_STEREO])
{
flags |= ITSample::sampleStereo;
}
if(mptSmp.uFlags[CHN_16BIT])
{
flags |= ITSample::sample16Bit;
}
cvt = ITSample::cvtSignedSample;
if(compress)
{
flags |= ITSample::sampleCompressed;
if(compressIT215)
{
cvt |= ITSample::cvtDelta;
}
}
} else
{
flags = 0x00;
}
C5Speed = mptSmp.nC5Speed ? mptSmp.nC5Speed : 8363;
length = mpt::saturate_cast<uint32>(mptSmp.nLength);
loopbegin = mpt::saturate_cast<uint32>(mptSmp.nLoopStart);
loopend = mpt::saturate_cast<uint32>(mptSmp.nLoopEnd);
susloopbegin = mpt::saturate_cast<uint32>(mptSmp.nSustainStart);
susloopend = mpt::saturate_cast<uint32>(mptSmp.nSustainEnd);
vit = AutoVibratoXM2IT[mptSmp.nVibType & 7];
vis = std::min(mptSmp.nVibRate, uint8(64));
vid = std::min(mptSmp.nVibDepth, uint8(32));
vir = std::min(mptSmp.nVibSweep, uint8(255));
if((vid | vis) != 0 && (fromType & MOD_TYPE_XM))
{
vir = 255 - vir;
}
if(mptSmp.uFlags[CHN_ADLIB])
{
length = 12;
cvt = ITSample::cvtOPLInstrument;
} else if(mptSmp.uFlags[SMP_KEEPONDISK])
{
#ifndef MPT_EXTERNAL_SAMPLES
MPT_UNREFERENCED_PARAMETER(allowExternal);
#else
if(allowExternal && mptSmp.HasSampleData())
{
cvt = ITSample::cvtExternalSample;
} else
#endif {
length = loopbegin = loopend = susloopbegin = susloopend = 0;
}
}
}
uint32 ITSample::ConvertToMPT(ModSample &mptSmp) const
{
if(memcmp(id, "IMPS", 4))
{
return 0;
}
mptSmp.Initialize(MOD_TYPE_IT);
mpt::String::Read<mpt::String::nullTerminated>(mptSmp.filename, filename);
mptSmp.nVolume = vol * 4;
LimitMax(mptSmp.nVolume, uint16(256));
mptSmp.nGlobalVol = gvl;
LimitMax(mptSmp.nGlobalVol, uint16(64));
mptSmp.nPan = (dfp & 0x7F) * 4;
LimitMax(mptSmp.nPan, uint16(256));
if(dfp & ITSample::enablePanning) mptSmp.uFlags.set(CHN_PANNING);
if(flags & ITSample::sampleLoop) mptSmp.uFlags.set(CHN_LOOP);
if(flags & ITSample::sampleSustain) mptSmp.uFlags.set(CHN_SUSTAINLOOP);
if(flags & ITSample::sampleBidiLoop) mptSmp.uFlags.set(CHN_PINGPONGLOOP);
if(flags & ITSample::sampleBidiSustain) mptSmp.uFlags.set(CHN_PINGPONGSUSTAIN);
mptSmp.nC5Speed = C5Speed;
if(!mptSmp.nC5Speed) mptSmp.nC5Speed = 8363;
if(mptSmp.nC5Speed < 256) mptSmp.nC5Speed = 256;
mptSmp.nLength = length;
mptSmp.nLoopStart = loopbegin;
mptSmp.nLoopEnd = loopend;
mptSmp.nSustainStart = susloopbegin;
mptSmp.nSustainEnd = susloopend;
mptSmp.SanitizeLoops();
mptSmp.nVibType = static_cast<VibratoType>(AutoVibratoIT2XM[vit & 7]);
mptSmp.nVibRate = vis;
mptSmp.nVibDepth = vid & 0x7F;
mptSmp.nVibSweep = vir;
if(cvt == ITSample::cvtOPLInstrument)
{
mptSmp.uFlags.set(CHN_ADLIB);
} else if(cvt == ITSample::cvtExternalSample)
{
mptSmp.uFlags.set(SMP_KEEPONDISK);
}
return samplepointer;
}
SampleIO ITSample::GetSampleFormat(uint16 cwtv) const
{
SampleIO sampleIO(
(flags & ITSample::sample16Bit) ? SampleIO::_16bit : SampleIO::_8bit,
SampleIO::mono,
SampleIO::littleEndian,
(cvt & ITSample::cvtSignedSample) ? SampleIO::signedPCM: SampleIO::unsignedPCM);
if((flags & ITSample::sampleStereo) && cwtv >= 0x214)
{
sampleIO |= SampleIO::stereoSplit;
}
if(flags & ITSample::sampleCompressed)
{
sampleIO |= (cvt & ITSample::cvtDelta) ? SampleIO::IT215 : SampleIO::IT214;
} else
{
if(!(flags & ITSample::sample16Bit) && cvt == ITSample::cvtADPCMSample)
{
sampleIO |= SampleIO::ADPCM;
} else
{
if(cvt & ITSample::cvtBigEndian)
{
sampleIO |= SampleIO::bigEndian;
}
if(cvt & ITSample::cvtDelta)
{
sampleIO |= SampleIO::deltaPCM;
}
if((cvt & ITSample::cvtPTM8to16) && (flags & ITSample::sample16Bit))
{
sampleIO |= SampleIO::PTM8Dto16;
}
}
}
return sampleIO;
}
void ITHistoryStruct::ConvertToMPT(FileHistory &mptHistory) const
{
MemsetZero(mptHistory.loadDate);
mptHistory.loadDate.tm_year = ((fatdate >> 9) & 0x7F) + 80;
mptHistory.loadDate.tm_mon = Clamp((fatdate >> 5) & 0x0F, 1, 12) - 1;
mptHistory.loadDate.tm_mday = Clamp(fatdate & 0x1F, 1, 31);
mptHistory.loadDate.tm_hour = Clamp((fattime >> 11) & 0x1F, 0, 23);
mptHistory.loadDate.tm_min = Clamp((fattime >> 5) & 0x3F, 0, 59);
mptHistory.loadDate.tm_sec = Clamp((fattime & 0x1F) * 2, 0, 59);
mptHistory.openTime = static_cast<uint32>(runtime * (HISTORY_TIMER_PRECISION / 18.2));
}
void ITHistoryStruct::ConvertToIT(const FileHistory &mptHistory)
{
fatdate = static_cast<uint16>(mptHistory.loadDate.tm_mday | ((mptHistory.loadDate.tm_mon + 1) << 5) | ((mptHistory.loadDate.tm_year - 80) << 9));
fattime = static_cast<uint16>((mptHistory.loadDate.tm_sec / 2) | (mptHistory.loadDate.tm_min << 5) | (mptHistory.loadDate.tm_hour << 11));
runtime = static_cast<uint32>(mptHistory.openTime * (18.2 / HISTORY_TIMER_PRECISION));
}
OPENMPT_NAMESPACE_END