123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892 |
- /*
- * Load_stp.cpp
- * ------------
- * Purpose: STP (Soundtracker Pro II) module loader
- * Notes : A few exotic effects aren't supported.
- * Multiple sample loops are supported, but only the first 10 can be used as cue points
- * (with 16xx and 18xx).
- * Fractional speed values and combined auto effects are handled whenever possible,
- * but some effects may be omitted (and there may be tempo accuracy issues).
- * Authors: Devin Acker
- * OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- *
- * Wisdom from the Soundtracker Pro II manual:
- * "To create shorter patterns, simply create shorter patterns."
- */
- #include "stdafx.h"
- #include "Loaders.h"
- OPENMPT_NAMESPACE_BEGIN
- // File header
- struct STPFileHeader
- {
- char magic[4];
- uint16be version;
- uint8be numOrders;
- uint8be patternLength;
- uint8be orderList[128];
- uint16be speed;
- uint16be speedFrac;
- uint16be timerCount;
- uint16be flags;
- uint32be reserved;
- uint16be midiCount; // always 50
- uint8be midi[50];
- uint16be numSamples;
- uint16be sampleStructSize;
- };
- MPT_BINARY_STRUCT(STPFileHeader, 204)
- // Sample header (common part between all versions)
- struct STPSampleHeader
- {
- uint32be length;
- uint8be volume;
- uint8be reserved1;
- uint32be loopStart;
- uint32be loopLength;
- uint16be defaultCommand; // Default command to put next to note when editing patterns; not relevant for playback
- // The following 4 bytes are reserved in version 0 and 1.
- uint16be defaultPeriod;
- uint8be finetune;
- uint8be reserved2;
- void ConvertToMPT(ModSample &mptSmp) const
- {
- mptSmp.nLength = length;
- mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64));
- mptSmp.nLoopStart = loopStart;
- mptSmp.nLoopEnd = loopStart + loopLength;
- if(mptSmp.nLoopStart >= mptSmp.nLength)
- {
- mptSmp.nLoopStart = mptSmp.nLength - 1;
- }
- if(mptSmp.nLoopEnd > mptSmp.nLength)
- {
- mptSmp.nLoopEnd = mptSmp.nLength;
- }
- if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
- {
- mptSmp.nLoopStart = 0;
- mptSmp.nLoopEnd = 0;
- } else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
- {
- mptSmp.uFlags.set(CHN_LOOP);
- mptSmp.cues[0] = mptSmp.nLoopStart;
- }
- }
- };
- MPT_BINARY_STRUCT(STPSampleHeader, 20)
- struct STPLoopInfo
- {
- SmpLength loopStart;
- SmpLength loopLength;
- SAMPLEINDEX looped;
- SAMPLEINDEX nonLooped;
- };
- typedef std::vector<STPLoopInfo> STPLoopList;
- static TEMPO ConvertTempo(uint16 ciaSpeed)
- {
- // 3546 is the resulting CIA timer value when using 4F7D (tempo 125 bpm) command in STProII
- return TEMPO((125.0 * 3546.0) / ciaSpeed);
- }
- static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, SmpLength len, bool loop)
- {
- if(!src.HasSampleData()
- || start >= src.nLength
- || src.nLength - start < len)
- {
- return;
- }
- dest.FreeSample();
- dest = src;
- dest.nLength = len;
- dest.pData.pSample = nullptr;
- if(!dest.AllocateSample())
- {
- return;
- }
- // only preserve cue points if the target sample length is the same
- if(len != src.nLength)
- MemsetZero(dest.cues);
- std::memcpy(dest.sampleb(), src.sampleb() + start, len);
- dest.uFlags.set(CHN_LOOP, loop);
- if(loop)
- {
- dest.nLoopStart = 0;
- dest.nLoopEnd = len;
- } else
- {
- dest.nLoopStart = 0;
- dest.nLoopEnd = 0;
- }
- }
- static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList)
- {
- // This should only modify a sample if it has more than one loop
- // (otherwise, it behaves like a normal sample loop)
- if(!smp.HasSampleData() || loopList.size() < 2) return;
- ModSample newSmp = smp;
- newSmp.nLength = 0;
- newSmp.pData.pSample = nullptr;
- size_t numLoops = loopList.size();
- // Get the total length of the sample after combining all looped sections
- for(size_t i = 0; i < numLoops; i++)
- {
- STPLoopInfo &info = loopList[i];
- // If adding this loop would cause the sample length to exceed maximum,
- // then limit and bail out
- if(info.loopStart >= smp.nLength
- || smp.nLength - info.loopStart < info.loopLength
- || newSmp.nLength > MAX_SAMPLE_LENGTH - info.loopLength)
- {
- numLoops = i;
- break;
- }
- newSmp.nLength += info.loopLength;
- }
- if(!newSmp.AllocateSample())
- {
- return;
- }
- // start copying the looped sample data parts
- SmpLength start = 0;
- for(size_t i = 0; i < numLoops; i++)
- {
- STPLoopInfo &info = loopList[i];
- memcpy(newSmp.sampleb() + start, smp.sampleb() + info.loopStart, info.loopLength);
- // update loop info based on position in edited sample
- info.loopStart = start;
- if(i > 0 && i <= std::size(newSmp.cues))
- {
- newSmp.cues[i - 1] = start;
- }
- start += info.loopLength;
- }
- // replace old sample with new one
- smp.FreeSample();
- smp = newSmp;
- smp.nLoopStart = 0;
- smp.nLoopEnd = smp.nLength;
- smp.uFlags.set(CHN_LOOP);
- }
- static bool ValidateHeader(const STPFileHeader &fileHeader)
- {
- if(std::memcmp(fileHeader.magic, "STP3", 4)
- || fileHeader.version > 2
- || fileHeader.numOrders > 128
- || fileHeader.numSamples >= MAX_SAMPLES
- || fileHeader.timerCount == 0
- || fileHeader.midiCount != 50)
- {
- return false;
- }
- return true;
- }
- CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize)
- {
- STPFileHeader fileHeader;
- if(!file.ReadStruct(fileHeader))
- {
- return ProbeWantMoreData;
- }
- if(!ValidateHeader(fileHeader))
- {
- return ProbeFailure;
- }
- MPT_UNREFERENCED_PARAMETER(pfilesize);
- return ProbeSuccess;
- }
- bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
- {
- file.Rewind();
- STPFileHeader fileHeader;
- if(!file.ReadStruct(fileHeader))
- {
- return false;
- }
- if(!ValidateHeader(fileHeader))
- {
- return false;
- }
- if(loadFlags == onlyVerifyHeader)
- {
- return true;
- }
- InitializeGlobals(MOD_TYPE_STP);
- m_modFormat.formatName = MPT_UFORMAT("Soundtracker Pro II v{}")(fileHeader.version);
- m_modFormat.type = U_("stp");
- m_modFormat.charset = mpt::Charset::Amiga_no_C1;
- m_nChannels = 4;
- m_nSamples = 0;
- m_nDefaultSpeed = fileHeader.speed;
- m_nDefaultTempo = ConvertTempo(fileHeader.timerCount);
- m_nMinPeriod = 14 * 4;
- m_nMaxPeriod = 3424 * 4;
- ReadOrderFromArray(Order(), fileHeader.orderList, fileHeader.numOrders);
- std::vector<STPLoopList> loopInfo;
- // Non-looped versions of samples with loops (when needed)
- std::vector<SAMPLEINDEX> nonLooped;
- // Load sample headers
- SAMPLEINDEX samplesInFile = 0;
- for(SAMPLEINDEX smp = 0; smp < fileHeader.numSamples; smp++)
- {
- SAMPLEINDEX actualSmp = file.ReadUint16BE();
- if(actualSmp == 0 || actualSmp >= MAX_SAMPLES)
- return false;
- uint32 chunkSize = fileHeader.sampleStructSize;
- if(fileHeader.version == 2)
- chunkSize = file.ReadUint32BE() - 2;
- FileReader chunk = file.ReadChunk(chunkSize);
- samplesInFile = m_nSamples = std::max(m_nSamples, actualSmp);
- ModSample &mptSmp = Samples[actualSmp];
- mptSmp.Initialize(MOD_TYPE_MOD);
- if(fileHeader.version < 2)
- {
- // Read path
- chunk.ReadString<mpt::String::maybeNullTerminated>(mptSmp.filename, 31);
- // Ignore flags, they are all not relevant for us
- chunk.Skip(1);
- // Read filename / sample text
- chunk.ReadString<mpt::String::maybeNullTerminated>(m_szNames[actualSmp], 30);
- } else
- {
- std::string str;
- // Read path
- chunk.ReadNullString(str, 257);
- mptSmp.filename = str;
- // Ignore flags, they are all not relevant for us
- chunk.Skip(1);
- // Read filename / sample text
- chunk.ReadNullString(str, 31);
- m_szNames[actualSmp] = str;
- // Seek to even boundary
- if(chunk.GetPosition() % 2u)
- chunk.Skip(1);
- }
- STPSampleHeader sampleHeader;
- chunk.ReadStruct(sampleHeader);
- sampleHeader.ConvertToMPT(mptSmp);
- if(fileHeader.version == 2)
- {
- mptSmp.nFineTune = static_cast<int8>(sampleHeader.finetune << 3);
- }
- if(fileHeader.version >= 1)
- {
- nonLooped.resize(samplesInFile);
- loopInfo.resize(samplesInFile);
- STPLoopList &loopList = loopInfo[actualSmp - 1];
- loopList.clear();
- const uint16 numLoops = file.ReadUint16BE();
- if(!file.CanRead(numLoops * 8u))
- return false;
- loopList.reserve(numLoops);
- STPLoopInfo loop;
- loop.looped = loop.nonLooped = 0;
- if(numLoops == 0 && mptSmp.uFlags[CHN_LOOP])
- {
- loop.loopStart = mptSmp.nLoopStart;
- loop.loopLength = mptSmp.nLoopEnd - mptSmp.nLoopStart;
- loopList.push_back(loop);
- } else for(uint16 i = 0; i < numLoops; i++)
- {
- loop.loopStart = file.ReadUint32BE();
- loop.loopLength = file.ReadUint32BE();
- loopList.push_back(loop);
- }
- }
- }
- // Load patterns
- uint16 numPatterns = 128;
- if(fileHeader.version == 0)
- numPatterns = file.ReadUint16BE();
- uint16 patternLength = fileHeader.patternLength;
- CHANNELINDEX channels = 4;
- if(fileHeader.version > 0)
- {
- // Scan for total number of channels
- FileReader::off_t patOffset = file.GetPosition();
- for(uint16 pat = 0; pat < numPatterns; pat++)
- {
- PATTERNINDEX actualPat = file.ReadUint16BE();
- if(actualPat == 0xFFFF)
- break;
- patternLength = file.ReadUint16BE();
- channels = file.ReadUint16BE();
- if(channels > MAX_BASECHANNELS)
- return false;
- m_nChannels = std::max(m_nChannels, channels);
- file.Skip(channels * patternLength * 4u);
- }
- file.Seek(patOffset);
- }
- struct ChannelMemory
- {
- uint8 autoFinePorta, autoPortaUp, autoPortaDown, autoVolSlide, autoVibrato;
- uint8 vibratoMem, autoTremolo, autoTonePorta, tonePortaMem;
- };
- std::vector<ChannelMemory> channelMemory(m_nChannels);
- uint8 globalVolSlide = 0;
- uint8 speedFrac = static_cast<uint8>(fileHeader.speedFrac);
- for(uint16 pat = 0; pat < numPatterns; pat++)
- {
- PATTERNINDEX actualPat = pat;
- if(fileHeader.version > 0)
- {
- actualPat = file.ReadUint16BE();
- if(actualPat == 0xFFFF)
- break;
- patternLength = file.ReadUint16BE();
- channels = file.ReadUint16BE();
- }
- if(!file.CanRead(channels * patternLength * 4u))
- break;
- if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
- {
- file.Skip(channels * patternLength * 4u);
- continue;
- }
- for(ROWINDEX row = 0; row < patternLength; row++)
- {
- auto rowBase = Patterns[actualPat].GetRow(row);
- bool didGlobalVolSlide = false;
- // if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere
- bool shouldDelay;
- switch(speedFrac & 3)
- {
- default: shouldDelay = false; break;
- // 1/4
- case 1: shouldDelay = (row & 3) == 0; break;
- // 1/2
- case 2: shouldDelay = (row & 1) == 0; break;
- // 3/4
- case 3: shouldDelay = (row & 3) != 3; break;
- }
- for(CHANNELINDEX chn = 0; chn < channels; chn++)
- {
- ChannelMemory &chnMem = channelMemory[chn];
- ModCommand &m = rowBase[chn];
- const auto [instr, note, command, param] = file.ReadArray<uint8, 4>();
- m.instr = instr;
- m.note = note;
- m.param = param;
- if(m.note)
- {
- m.note += 24 + NOTE_MIN;
- chnMem = ChannelMemory();
- }
- // this is a nibble-swapped param value used for auto fine volside
- // and auto global fine volside
- uint8 swapped = (m.param >> 4) | (m.param << 4);
- if((command & 0xF0) == 0xF0)
- {
- // 12-bit CIA tempo
- uint16 ciaTempo = (static_cast<uint16>(command & 0x0F) << 8) | m.param;
- if(ciaTempo)
- {
- m.param = mpt::saturate_round<ModCommand::PARAM>(ConvertTempo(ciaTempo).ToDouble());
- m.command = CMD_TEMPO;
- } else
- {
- m.command = CMD_NONE;
- }
- } else switch(command)
- {
- case 0x00: // arpeggio
- if(m.param)
- m.command = CMD_ARPEGGIO;
- else
- m.command = CMD_NONE;
- break;
- case 0x01: // portamento up
- m.command = CMD_PORTAMENTOUP;
- break;
- case 0x02: // portamento down
- m.command = CMD_PORTAMENTODOWN;
- break;
- case 0x03: // auto fine portamento up
- chnMem.autoFinePorta = 0x10 | std::min(m.param, ModCommand::PARAM(15));
- chnMem.autoPortaUp = 0;
- chnMem.autoPortaDown = 0;
- chnMem.autoTonePorta = 0;
- m.command = CMD_NONE;
- break;
- case 0x04: // auto fine portamento down
- chnMem.autoFinePorta = 0x20 | std::min(m.param, ModCommand::PARAM(15));
- chnMem.autoPortaUp = 0;
- chnMem.autoPortaDown = 0;
- chnMem.autoTonePorta = 0;
- m.command = CMD_NONE;
- break;
- case 0x05: // auto portamento up
- chnMem.autoFinePorta = 0;
- chnMem.autoPortaUp = m.param;
- chnMem.autoPortaDown = 0;
- chnMem.autoTonePorta = 0;
- m.command = CMD_NONE;
- break;
- case 0x06: // auto portamento down
- chnMem.autoFinePorta = 0;
- chnMem.autoPortaUp = 0;
- chnMem.autoPortaDown = m.param;
- chnMem.autoTonePorta = 0;
- m.command = CMD_NONE;
- break;
- case 0x07: // set global volume
- m.command = CMD_GLOBALVOLUME;
- globalVolSlide = 0;
- break;
- case 0x08: // auto global fine volume slide
- globalVolSlide = swapped;
- m.command = CMD_NONE;
- break;
- case 0x09: // fine portamento up
- m.command = CMD_MODCMDEX;
- m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15));
- break;
- case 0x0A: // fine portamento down
- m.command = CMD_MODCMDEX;
- m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15));
- break;
- case 0x0B: // auto fine volume slide
- chnMem.autoVolSlide = swapped;
- m.command = CMD_NONE;
- break;
- case 0x0C: // set volume
- m.volcmd = VOLCMD_VOLUME;
- m.vol = m.param;
- chnMem.autoVolSlide = 0;
- m.command = CMD_NONE;
- break;
- case 0x0D: // volume slide (param is swapped compared to .mod)
- if(m.param & 0xF0)
- {
- m.volcmd = VOLCMD_VOLSLIDEDOWN;
- m.vol = m.param >> 4;
- } else if(m.param & 0x0F)
- {
- m.volcmd = VOLCMD_VOLSLIDEUP;
- m.vol = m.param & 0xF;
- }
- chnMem.autoVolSlide = 0;
- m.command = CMD_NONE;
- break;
- case 0x0E: // set filter (also uses opposite value compared to .mod)
- m.command = CMD_MODCMDEX;
- m.param = 1 ^ (m.param ? 1 : 0);
- break;
- case 0x0F: // set speed
- m.command = CMD_SPEED;
- speedFrac = m.param & 0x0F;
- m.param >>= 4;
- break;
- case 0x10: // auto vibrato
- chnMem.autoVibrato = m.param;
- chnMem.vibratoMem = 0;
- m.command = CMD_NONE;
- break;
- case 0x11: // auto tremolo
- if(m.param & 0xF)
- chnMem.autoTremolo = m.param;
- else
- chnMem.autoTremolo = 0;
- m.command = CMD_NONE;
- break;
- case 0x12: // pattern break
- m.command = CMD_PATTERNBREAK;
- break;
- case 0x13: // auto tone portamento
- chnMem.autoFinePorta = 0;
- chnMem.autoPortaUp = 0;
- chnMem.autoPortaDown = 0;
- chnMem.autoTonePorta = m.param;
- chnMem.tonePortaMem = 0;
- m.command = CMD_NONE;
- break;
- case 0x14: // position jump
- m.command = CMD_POSITIONJUMP;
- break;
- case 0x16: // start loop sequence
- if(m.instr && m.instr <= loopInfo.size())
- {
- STPLoopList &loopList = loopInfo[m.instr - 1];
- m.param--;
- if(m.param < std::min(std::size(ModSample().cues), loopList.size()))
- {
- m.volcmd = VOLCMD_OFFSET;
- m.vol = m.param;
- }
- }
- m.command = CMD_NONE;
- break;
- case 0x17: // play only loop nn
- if(m.instr && m.instr <= loopInfo.size())
- {
- STPLoopList &loopList = loopInfo[m.instr - 1];
- m.param--;
- if(m.param < loopList.size())
- {
- if(!loopList[m.param].looped && CanAddMoreSamples())
- loopList[m.param].looped = ++m_nSamples;
- m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
- }
- }
- m.command = CMD_NONE;
- break;
- case 0x18: // play sequence without loop
- if(m.instr && m.instr <= loopInfo.size())
- {
- STPLoopList &loopList = loopInfo[m.instr - 1];
- m.param--;
- if(m.param < std::min(std::size(ModSample().cues), loopList.size()))
- {
- m.volcmd = VOLCMD_OFFSET;
- m.vol = m.param;
- }
- // switch to non-looped version of sample and create it if needed
- if(!nonLooped[m.instr - 1] && CanAddMoreSamples())
- nonLooped[m.instr - 1] = ++m_nSamples;
- m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
- }
- m.command = CMD_NONE;
- break;
- case 0x19: // play only loop nn without loop
- if(m.instr && m.instr <= loopInfo.size())
- {
- STPLoopList &loopList = loopInfo[m.instr - 1];
- m.param--;
- if(m.param < loopList.size())
- {
- if(!loopList[m.param].nonLooped && CanAddMoreSamples())
- loopList[m.param].nonLooped = ++m_nSamples;
- m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].nonLooped);
- }
- }
- m.command = CMD_NONE;
- break;
- case 0x1D: // fine volume slide (nibble order also swapped)
- m.command = CMD_VOLUMESLIDE;
- m.param = swapped;
- if(m.param & 0xF0) // slide down
- m.param |= 0x0F;
- else if(m.param & 0x0F)
- m.param |= 0xF0;
- break;
- case 0x20: // "delayed fade"
- // just behave like either a normal fade or a notecut
- // depending on the speed
- if(m.param & 0xF0)
- {
- chnMem.autoVolSlide = m.param >> 4;
- m.command = CMD_NONE;
- } else
- {
- m.command = CMD_MODCMDEX;
- m.param = 0xC0 | (m.param & 0xF);
- }
- break;
- case 0x21: // note delay
- m.command = CMD_MODCMDEX;
- m.param = 0xD0 | std::min(m.param, ModCommand::PARAM(15));
- break;
- case 0x22: // retrigger note
- m.command = CMD_MODCMDEX;
- m.param = 0x90 | std::min(m.param, ModCommand::PARAM(15));
- break;
- case 0x49: // set sample offset
- m.command = CMD_OFFSET;
- break;
- case 0x4E: // other protracker commands (pattern loop / delay)
- if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
- m.command = CMD_MODCMDEX;
- else
- m.command = CMD_NONE;
- break;
- case 0x4F: // set speed/tempo
- if(m.param < 0x20)
- {
- m.command = CMD_SPEED;
- speedFrac = 0;
- } else
- {
- m.command = CMD_TEMPO;
- }
- break;
- default:
- m.command = CMD_NONE;
- break;
- }
- bool didVolSlide = false;
- // try to put volume slide in volume command
- if(chnMem.autoVolSlide && m.volcmd == VOLCMD_NONE)
- {
- if(chnMem.autoVolSlide & 0xF0)
- {
- m.volcmd = VOLCMD_FINEVOLUP;
- m.vol = chnMem.autoVolSlide >> 4;
- } else
- {
- m.volcmd = VOLCMD_FINEVOLDOWN;
- m.vol = chnMem.autoVolSlide & 0xF;
- }
- didVolSlide = true;
- }
- // try to place/combine all remaining running effects.
- if(m.command == CMD_NONE)
- {
- if(chnMem.autoPortaUp)
- {
- m.command = CMD_PORTAMENTOUP;
- m.param = chnMem.autoPortaUp;
- } else if(chnMem.autoPortaDown)
- {
- m.command = CMD_PORTAMENTODOWN;
- m.param = chnMem.autoPortaDown;
- } else if(chnMem.autoFinePorta)
- {
- m.command = CMD_MODCMDEX;
- m.param = chnMem.autoFinePorta;
- } else if(chnMem.autoTonePorta)
- {
- m.command = CMD_TONEPORTAMENTO;
- m.param = chnMem.tonePortaMem = chnMem.autoTonePorta;
- } else if(chnMem.autoVibrato)
- {
- m.command = CMD_VIBRATO;
- m.param = chnMem.vibratoMem = chnMem.autoVibrato;
- } else if(!didVolSlide && chnMem.autoVolSlide)
- {
- m.command = CMD_VOLUMESLIDE;
- m.param = chnMem.autoVolSlide;
- // convert to a "fine" value by setting the other nibble to 0xF
- if(m.param & 0x0F)
- m.param |= 0xF0;
- else if(m.param & 0xF0)
- m.param |= 0x0F;
- didVolSlide = true;
- MPT_UNUSED(didVolSlide);
- } else if(chnMem.autoTremolo)
- {
- m.command = CMD_TREMOLO;
- m.param = chnMem.autoTremolo;
- } else if(shouldDelay)
- {
- // insert a fine pattern delay here
- m.command = CMD_S3MCMDEX;
- m.param = 0x61;
- shouldDelay = false;
- } else if(!didGlobalVolSlide && globalVolSlide)
- {
- m.command = CMD_GLOBALVOLSLIDE;
- m.param = globalVolSlide;
- // convert to a "fine" value by setting the other nibble to 0xF
- if(m.param & 0x0F)
- m.param |= 0xF0;
- else if(m.param & 0xF0)
- m.param |= 0x0F;
- didGlobalVolSlide = true;
- }
- }
- }
- // TODO: create/use extra channels for global volslide/delay if needed
- }
- }
- // after we know how many channels there really are...
- m_nSamplePreAmp = 256 / m_nChannels;
- // Setup channel pan positions and volume
- SetupMODPanning(true);
- // Skip over scripts and drumpad info
- if(fileHeader.version > 0)
- {
- while(file.CanRead(2))
- {
- uint16 scriptNum = file.ReadUint16BE();
- if(scriptNum == 0xFFFF)
- break;
- file.Skip(2);
- uint32 length = file.ReadUint32BE();
- file.Skip(length);
- }
- // Skip drumpad stuff
- file.Skip(17 * 2);
- }
- // Reading samples
- if(loadFlags & loadSampleData)
- {
- for(SAMPLEINDEX smp = 1; smp <= samplesInFile; smp++) if(Samples[smp].nLength)
- {
- SampleIO(
- SampleIO::_8bit,
- SampleIO::mono,
- SampleIO::littleEndian,
- SampleIO::signedPCM)
- .ReadSample(Samples[smp], file);
- if(smp > loopInfo.size())
- continue;
- ConvertLoopSequence(Samples[smp], loopInfo[smp - 1]);
- // make a non-looping duplicate of this sample if needed
- if(nonLooped[smp - 1])
- {
- ConvertLoopSlice(Samples[smp], Samples[nonLooped[smp - 1]], 0, Samples[smp].nLength, false);
- }
- for(const auto &info : loopInfo[smp - 1])
- {
- // make duplicate samples for this individual section if needed
- if(info.looped)
- {
- ConvertLoopSlice(Samples[smp], Samples[info.looped], info.loopStart, info.loopLength, true);
- }
- if(info.nonLooped)
- {
- ConvertLoopSlice(Samples[smp], Samples[info.nonLooped], info.loopStart, info.loopLength, false);
- }
- }
- }
- }
- return true;
- }
- OPENMPT_NAMESPACE_END
|