Index: build/android_ndk/Android-minimp3-stbvorbis.mk
===================================================================
--- build/android_ndk/Android-minimp3-stbvorbis.mk (revision 6473)
+++ build/android_ndk/Android-minimp3-stbvorbis.mk (working copy)
@@ -75,6 +75,7 @@
soundlib/Load_s3m.cpp \
soundlib/Load_sfx.cpp \
soundlib/Load_stm.cpp \
+ soundlib/Load_stp.cpp \
soundlib/Load_ult.cpp \
soundlib/Load_umx.cpp \
soundlib/Load_wav.cpp \
Index: build/android_ndk/Android-mpg123-vorbis.mk
===================================================================
--- build/android_ndk/Android-mpg123-vorbis.mk (revision 6473)
+++ build/android_ndk/Android-mpg123-vorbis.mk (working copy)
@@ -73,6 +73,7 @@
soundlib/Load_s3m.cpp \
soundlib/Load_sfx.cpp \
soundlib/Load_stm.cpp \
+ soundlib/Load_stp.cpp \
soundlib/Load_ult.cpp \
soundlib/Load_umx.cpp \
soundlib/Load_wav.cpp \
Index: build/android_ndk/Android-unmo3.mk
===================================================================
--- build/android_ndk/Android-unmo3.mk (revision 6473)
+++ build/android_ndk/Android-unmo3.mk (working copy)
@@ -106,6 +106,7 @@
soundlib/Load_s3m.cpp \
soundlib/Load_sfx.cpp \
soundlib/Load_stm.cpp \
+ soundlib/Load_stp.cpp \
soundlib/Load_ult.cpp \
soundlib/Load_umx.cpp \
soundlib/Load_wav.cpp \
Index: build/android_ndk/Android.mk
===================================================================
--- build/android_ndk/Android.mk (revision 6473)
+++ build/android_ndk/Android.mk (working copy)
@@ -74,6 +74,7 @@
soundlib/Load_s3m.cpp \
soundlib/Load_sfx.cpp \
soundlib/Load_stm.cpp \
+ soundlib/Load_stp.cpp \
soundlib/Load_ult.cpp \
soundlib/Load_umx.cpp \
soundlib/Load_wav.cpp \
Index: build/autotools/Makefile.am
===================================================================
--- build/autotools/Makefile.am (revision 6473)
+++ build/autotools/Makefile.am (working copy)
@@ -186,6 +186,7 @@
libopenmpt_la_SOURCES += soundlib/Load_s3m.cpp
libopenmpt_la_SOURCES += soundlib/Load_sfx.cpp
libopenmpt_la_SOURCES += soundlib/Load_stm.cpp
+libopenmpt_la_SOURCES += soundlib/Load_stp.cpp
libopenmpt_la_SOURCES += soundlib/Load_ult.cpp
libopenmpt_la_SOURCES += soundlib/Load_umx.cpp
libopenmpt_la_SOURCES += soundlib/Load_wav.cpp
@@ -432,6 +433,7 @@
libopenmpttest_SOURCES += soundlib/Load_s3m.cpp
libopenmpttest_SOURCES += soundlib/Load_sfx.cpp
libopenmpttest_SOURCES += soundlib/Load_stm.cpp
+libopenmpttest_SOURCES += soundlib/Load_stp.cpp
libopenmpttest_SOURCES += soundlib/Load_ult.cpp
libopenmpttest_SOURCES += soundlib/Load_umx.cpp
libopenmpttest_SOURCES += soundlib/Load_wav.cpp
Index: libopenmpt/foo_openmpt.cpp
===================================================================
--- libopenmpt/foo_openmpt.cpp (revision 6473)
+++ libopenmpt/foo_openmpt.cpp (working copy)
@@ -319,6 +319,7 @@
"*.imf" ";"
"*.j2b" ";"
"*.plm" ";"
+ "*.stp" ";"
"*.sfx" ";"
"*.sfx2" ";"
"*.mms" ";"
Index: mptrack/Mptrack.cpp
===================================================================
--- mptrack/Mptrack.cpp (revision 6473)
+++ mptrack/Mptrack.cpp (working copy)
@@ -1412,7 +1412,7 @@
"FastTracker Modules (*.xm)|*.xm;*.xmz|"
"Impulse Tracker Modules (*.it)|*.it;*.itz|"
"OpenMPT Modules (*.mptm)|*.mptm;*.mptmz|"
- "Other Modules (mtm,okt,mdl,669,far,...)|*.mtm;*.669;*.ult;*.wow;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.med;*.ams;*.dbm;*.digi;*.dsm;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf;*.itp;*.j2b;*.ice;*.st26;*.plm;*.sfx;*.sfx2;*.mms|"
+ "Other Modules (mtm,okt,mdl,669,far,...)|*.mtm;*.669;*.ult;*.wow;*.far;*.mdl;*.okt;*.dmf;*.ptm;*.med;*.ams;*.dbm;*.digi;*.dsm;*.umx;*.amf;*.psm;*.mt2;*.gdm;*.imf;*.itp;*.j2b;*.ice;*.st26;*.plm;*.stp;*.sfx;*.sfx2;*.mms|"
"Wave Files (*.wav)|*.wav|"
"MIDI Files (*.mid,*.rmi)|*.mid;*.rmi;*.smf|"
"All Files (*.*)|*.*||")
Index: mptrack/mptrack_10.vcxproj
===================================================================
--- mptrack/mptrack_10.vcxproj (revision 6473)
+++ mptrack/mptrack_10.vcxproj (working copy)
@@ -785,6 +785,7 @@
+
Index: mptrack/mptrack_10.vcxproj.filters
===================================================================
--- mptrack/mptrack_10.vcxproj.filters (revision 6473)
+++ mptrack/mptrack_10.vcxproj.filters (working copy)
@@ -643,6 +643,9 @@
Source Files\soundlib\Module Loaders
+
+ Source Files\soundlib\Module Loaders
+
Index: soundlib/Load_stp.cpp
===================================================================
--- soundlib/Load_stp.cpp (nonexistent)
+++ soundlib/Load_stp.cpp (working copy)
@@ -0,0 +1,951 @@
+/*
+ * 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"
+#include "Tables.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+#ifdef NEEDS_PRAGMA_PACK
+#pragma pack(push, 1)
+#endif
+
+// File header (except for "STP3" magic)
+struct PACKED STPFileHeader
+{
+ uint16 version;
+ uint8 numOrders;
+ uint8 patternLength;
+ uint8 orderList[128];
+ uint16 speed;
+ uint16 speedFrac;
+ uint16 timerCount;
+ uint16 flags;
+ uint32 reserved;
+ uint16 midiCount; // always 50
+ uint8 midi[50];
+ uint16 numSamples;
+ uint16 sampleStructSize;
+
+ void ConvertEndianness()
+ {
+ SwapBytesBE(version);
+ SwapBytesBE(speed);
+ SwapBytesBE(speedFrac);
+ SwapBytesBE(timerCount);
+ SwapBytesBE(flags);
+ SwapBytesBE(midiCount);
+ SwapBytesBE(numSamples);
+ SwapBytesBE(sampleStructSize);
+ }
+};
+
+STATIC_ASSERT(sizeof(STPFileHeader) == 200);
+
+
+// Sample header (versions 0 and 1)
+struct PACKED STPSampleHeaderOld
+{
+ char pathName[31];
+ uint8 flags;
+ char fileName[30];
+ uint32 length;
+ uint8 volume;
+ uint8 reserved1;
+ uint32 loopStart;
+ uint32 loopLength;
+ uint16 defaultCmd;
+ uint32 reserved2;
+
+ void ConvertEndianness()
+ {
+ SwapBytesBE(length);
+ SwapBytesBE(loopStart);
+ SwapBytesBE(loopLength);
+ SwapBytesBE(defaultCmd);
+ }
+
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.Initialize(MOD_TYPE_MOD);
+ mptSmp.nLength = length;
+ mptSmp.nVolume = 4u * std::min(volume, 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);
+ }
+ }
+
+ void Read(FileReader &file)
+ {
+ file.ReadConvertEndianness(*this);
+ }
+};
+
+STATIC_ASSERT(sizeof(STPSampleHeaderOld) == 82);
+
+#ifdef NEEDS_PRAGMA_PACK
+#pragma pack(pop)
+#endif
+
+// Sample header (version 2), not packed due to variable string length and alignment in file
+struct STPSampleHeader
+{
+ //char pathName[256];
+ uint8 flags;
+ char fileName[30];
+ uint32 length;
+ uint8 volume;
+ uint8 reserved1;
+ uint32 loopStart;
+ uint32 loopLength;
+ uint16 defaultCmd;
+ uint16 defaultPeriod;
+ uint8 finetune;
+ uint8 reserved2;
+
+ void ConvertToMPT(ModSample &mptSmp) const
+ {
+ mptSmp.Initialize(MOD_TYPE_MOD);
+ mptSmp.nLength = length;
+ mptSmp.nFineTune = static_cast(finetune << 3);
+ mptSmp.nVolume = 4 * MIN(volume, 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.cues[0] = mptSmp.nLoopStart;
+ mptSmp.uFlags.set(CHN_LOOP);
+ }
+ }
+
+ void Read(FileReader &file)
+ {
+ std::string str;
+
+ file.ReadNullString(str, 255);
+ //mpt::String::Copy(pathName, str);
+
+ flags = file.ReadUint8();
+
+ file.ReadNullString(str, 29);
+ mpt::String::Copy(fileName, str);
+
+ // seek to even boundary
+ if(file.GetPosition() % 2u)
+ file.Skip(1);
+
+ length = file.ReadUint32BE();
+ volume = file.ReadUint8();
+ reserved1 = file.ReadUint8();
+
+ loopStart = file.ReadUint32BE();
+ loopLength = file.ReadUint32BE();
+
+ defaultCmd = file.ReadUint16BE();
+ defaultPeriod = file.ReadUint16BE();
+
+ finetune = file.ReadUint8();
+ reserved2 = file.ReadUint8();
+ }
+};
+
+struct STPLoopInfo
+{
+ SmpLength loopStart;
+ SmpLength loopLength;
+ SAMPLEINDEX looped;
+ SAMPLEINDEX nonLooped;
+};
+
+typedef std::vector STPLoopList;
+
+template
+static void ReadSample(FileReader &file, T &sampleHeader, ModSample &sample, char (&sampleName)[MAX_SAMPLENAME])
+//--------------------------------------------------------------------------------------------------------------
+{
+ sampleHeader.Read(file);
+ sampleHeader.ConvertToMPT(sample);
+
+ mpt::String::Read(sampleName, sampleHeader.fileName);
+}
+
+
+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()) return;
+
+ dest.FreeSample();
+
+ dest = src;
+ dest.nLength = len;
+ dest.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);
+
+ memcpy(dest.pSample8, src.pSample8 + start, len);
+ if(loop)
+ {
+ dest.nLoopStart = 0;
+ dest.nLoopEnd = len;
+ dest.uFlags.set(CHN_LOOP);
+ } else
+ {
+ dest.nLoopStart = 0;
+ dest.nLoopEnd = 0;
+ dest.uFlags.reset(CHN_LOOP);
+ }
+}
+
+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.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((newSmp.nLength + info.loopLength > MAX_SAMPLE_LENGTH) ||
+ (info.loopLength > MAX_SAMPLE_LENGTH) ||
+ (info.loopStart + info.loopLength > smp.nLength))
+ {
+ 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.pSample8 + start, smp.pSample8 + info.loopStart, info.loopLength);
+
+ // update loop info based on position in edited sample
+ info.loopStart = start;
+ if(i > 0 && i <= CountOf(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);
+}
+
+
+bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
+//-------------------------------------------------------------------
+{
+ file.Rewind();
+ if(!file.ReadMagic("STP3"))
+ return false;
+
+ STPFileHeader fileHeader;
+ file.ReadConvertEndianness(fileHeader);
+ if(fileHeader.version > 2 ||
+ fileHeader.numOrders > 128 || fileHeader.numSamples >= MAX_SAMPLES ||
+ fileHeader.midiCount != 50)
+ return false;
+
+ if(loadFlags == onlyVerifyHeader)
+ return true;
+
+ InitializeGlobals(MOD_TYPE_STP);
+
+ m_nChannels = 4;
+ m_nSamples = 0;
+ m_nInstruments = 0;
+
+ m_nDefaultSpeed = fileHeader.speed;
+ m_nDefaultTempo = ConvertTempo(fileHeader.timerCount);
+
+ m_nMinPeriod = 14 * 4;
+ m_nMaxPeriod = 3424 * 4;
+
+ Order.ReadFromArray(fileHeader.orderList, fileHeader.numOrders);
+
+ std::vector loopInfo;
+ // non-looped versions of samples with loopes (when needed)
+ std::vector nonLooped;
+
+ // Load sample headers
+ SAMPLEINDEX samplesInFile = 0;
+
+ for(SAMPLEINDEX smp = 1; smp <= fileHeader.numSamples; smp++)
+ {
+ // this is 1-based the same as smp
+ SAMPLEINDEX actualSmp = file.ReadUint16BE();
+ if(actualSmp >= MAX_SAMPLES)
+ return false;
+
+ samplesInFile = m_nSamples = MAX(m_nSamples, actualSmp);
+
+ ModSample &thisSmp = Samples[actualSmp];
+
+ if(fileHeader.version == 2)
+ {
+ STPSampleHeader sampleHeader;
+ uint32 headerSize = file.ReadUint32BE();
+
+ ReadSample(file, sampleHeader, thisSmp, m_szNames[actualSmp]);
+ // TODO: verify string lengths against headerSize?
+ MPT_UNUSED_VARIABLE(headerSize);
+ } else
+ {
+ STPSampleHeaderOld sampleHeaderOld;
+ ReadSample(file, sampleHeaderOld, thisSmp, m_szNames[actualSmp]);
+ }
+
+ STPLoopList loopList;
+
+ if(fileHeader.version >= 1)
+ {
+ uint16 numLoops = file.ReadUint16BE();
+
+ STPLoopInfo loop;
+ loop.looped = loop.nonLooped = 0;
+
+ if(numLoops == 0 && thisSmp.uFlags[CHN_LOOP])
+ {
+ loop.loopStart = thisSmp.nLoopStart;
+ loop.loopLength = thisSmp.nLoopEnd - thisSmp.nLoopStart;
+ loopList.push_back(loop);
+
+ } else for(SAMPLEINDEX i = 0; i < numLoops; i++)
+ {
+ loop.loopStart = file.ReadUint32BE();
+ loop.loopLength = file.ReadUint32BE();
+ loopList.push_back(loop);
+ }
+ }
+
+ nonLooped.resize(actualSmp);
+ loopInfo.resize(actualSmp);
+ loopInfo[actualSmp - 1] = loopList;
+ }
+
+ // 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();
+ m_nChannels = std::max(m_nChannels, channels);
+
+ file.Skip(channels * patternLength * 4u);
+ }
+ file.Seek(patOffset);
+ if(m_nChannels > MAX_BASECHANNELS)
+ return false;
+ }
+
+ uint8 globalVolSlide = 0;
+ std::vector autoFinePorta(channels, 0);
+ std::vector autoPortaUp(channels, 0);
+ std::vector autoPortaDown(channels, 0);
+ std::vector autoVolSlide(channels, 0);
+ std::vector autoVibrato(channels, 0);
+ std::vector vibratoMem(channels, 0);
+ std::vector autoTremolo(channels, 0);
+ std::vector autoTonePorta(channels, 0);
+ std::vector tonePortaMem(channels, 0);
+
+ 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(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
+ {
+ file.Skip(channels * patternLength * 4u);
+ continue;
+ }
+
+ for(ROWINDEX row = 0; row < patternLength; row++)
+ {
+ PatternRow rowBase = Patterns[actualPat].GetpModCommand(row, 0);
+
+ bool didGlobalVolSlide = false;
+ bool shouldDelay = false;
+
+ // if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere
+ switch(fileHeader.speedFrac & 3)
+ {
+ // 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++)
+ {
+ ModCommand &m = rowBase[chn];
+ uint8 data[4];
+ file.ReadArray(data);
+
+ m.instr = data[0];
+ m.note = data[1];
+ m.command = data[2];
+ m.param = data[3];
+
+ if(m.note)
+ {
+ m.note += 24 + NOTE_MIN;
+
+ autoFinePorta[chn] = 0;
+ autoPortaUp[chn] = 0;
+ autoPortaDown[chn] = 0;
+ autoVolSlide[chn] = 0;
+ autoVibrato[chn] = vibratoMem[chn] = 0;
+ autoTremolo[chn] = 0;
+ autoTonePorta[chn] = tonePortaMem[chn] = 0;
+ }
+
+ // this is a nibble-swapped param value used for auto fine volside
+ // and auto global fine volside
+ uint8 swap = (m.param >> 4) | (m.param << 4);
+
+ if((m.command & 0xF0) == 0xF0)
+ {
+ m.param = mpt::saturate_cast(Util::Round(ConvertTempo(m.param | (((uint16)m.command & 0xF) << 8)).ToDouble()));
+ m.command = CMD_TEMPO;
+ } else switch(m.command)
+ {
+ case 0x00: // arpeggio
+ if(m.param)
+ m.command = CMD_ARPEGGIO;
+ 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
+ autoFinePorta[chn] = 0x10 | std::min(m.param, ModCommand::PARAM(15));
+ autoPortaUp[chn] = 0;
+ autoPortaDown[chn] = 0;
+ autoTonePorta[chn] = 0;
+
+ m.command = m.param = 0;
+ break;
+
+ case 0x04: // auto fine portamento down
+ autoFinePorta[chn] = 0x20 | std::min(m.param, ModCommand::PARAM(15));
+ autoPortaUp[chn] = 0;
+ autoPortaDown[chn] = 0;
+ autoTonePorta[chn] = 0;
+
+ m.command = m.param = 0;
+ break;
+
+ case 0x05: // auto portamento up
+ autoFinePorta[chn] = 0;
+ autoPortaUp[chn] = m.param;
+ autoPortaDown[chn] = 0;
+ autoTonePorta[chn] = 0;
+
+ m.command = m.param = 0;
+ break;
+
+ case 0x06: // auto portamento down
+ autoFinePorta[chn] = 0;
+ autoPortaUp[chn] = 0;
+ autoPortaDown[chn] = m.param;
+ autoTonePorta[chn] = 0;
+
+ m.command = m.param = 0;
+ break;
+
+ case 0x07: // set global volume
+ m.command = CMD_GLOBALVOLUME;
+ globalVolSlide = 0;
+ break;
+
+ case 0x08: // auto global fine volume slide
+ globalVolSlide = swap;
+ m.command = m.param = 0;
+ 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
+ autoVolSlide[chn] = swap;
+ m.command = m.param = 0;
+ break;
+
+ case 0x0C: // set volume
+ m.volcmd = VOLCMD_VOLUME;
+ m.vol = m.param;
+ autoVolSlide[chn] = 0;
+ m.command = m.param = 0;
+ 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;
+ }
+ autoVolSlide[chn] = 0;
+ m.command = m.param = 0;
+ 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;
+ fileHeader.speedFrac = m.param & 0xF;
+ m.param >>= 4;
+ break;
+
+ case 0x10: // auto vibrato
+ autoVibrato[chn] = m.param;
+ vibratoMem[chn] = 0;
+ m.command = m.param = 0;
+ break;
+
+ case 0x11: // auto tremolo
+ if(m.param & 0xF)
+ autoTremolo[chn] = m.param;
+ else
+ autoTremolo[chn] = 0;
+ m.command = m.param = 0;
+ break;
+
+ case 0x12: // pattern break
+ m.command = CMD_PATTERNBREAK;
+ break;
+
+ case 0x13: // auto tone portamento
+ autoFinePorta[chn] = 0;
+ autoPortaUp[chn] = 0;
+ autoPortaDown[chn] = 0;
+ autoTonePorta[chn] = m.param;
+
+ tonePortaMem[chn] = 0;
+ m.command = m.param = 0;
+ 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(CountOf(ModSample().cues), loopList.size()))
+ {
+ m.volcmd = VOLCMD_OFFSET;
+ m.vol = m.param;
+ }
+ }
+
+ m.command = m.param = 0;
+ 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 && m_nSamples < MAX_SAMPLES - 1)
+ loopList[m.param].looped = ++m_nSamples;
+ m.instr = static_cast(loopList[m.param].looped);
+ }
+ }
+
+ m.command = m.param = 0;
+ 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(CountOf(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] && m_nSamples < MAX_SAMPLES - 1)
+ nonLooped[m.instr - 1] = ++m_nSamples;
+ m.instr = static_cast(nonLooped[m.instr - 1]);
+ }
+
+ m.command = m.param = 0;
+ 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 && m_nSamples < MAX_SAMPLES-1)
+ loopList[m.param].nonLooped = ++m_nSamples;
+ m.instr = static_cast(loopList[m.param].nonLooped);
+ }
+ }
+
+ m.command = m.param = 0;
+ break;
+
+ case 0x1D: // fine volume slide (nibble order also swapped)
+ m.command = CMD_VOLUMESLIDE;
+ m.param = swap;
+ 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)
+ {
+ autoVolSlide[chn] = m.param >> 4;
+ m.command = m.param = 0;
+ } else
+ {
+ m.command = CMD_MODCMDEX;
+ m.param = 0xC0 | (m.param & 0xF);
+ }
+ break;
+
+ case 0x21: // note delay
+ m.command = CMD_MODCMDEX;
+ m.param = 0xD0 | MIN(m.param, 15);
+ break;
+
+ case 0x22: // retrigger note
+ m.command = CMD_RETRIG;
+ m.param = MIN(m.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 = m.param = 0;
+ break;
+
+ case 0x4F: // set speed/tempo
+ if(m.param < 0x20)
+ {
+ m.command = CMD_SPEED;
+ fileHeader.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(autoVolSlide[chn] && !m.volcmd)
+ {
+ if(autoVolSlide[chn] & 0xF0)
+ {
+ m.volcmd = VOLCMD_FINEVOLUP;
+ m.vol = autoVolSlide[chn] >> 4;
+ } else
+ {
+ m.volcmd = VOLCMD_FINEVOLDOWN;
+ m.vol = autoVolSlide[chn] & 0xF;
+ }
+ didVolSlide = true;
+ }
+
+ // try to place/combine all remaining running effects.
+ if(!m.command)
+ {
+ if(autoPortaUp[chn])
+ {
+ m.command = CMD_PORTAMENTOUP;
+ m.param = autoPortaUp[chn];
+
+ } else if(autoPortaDown[chn])
+ {
+ m.command = CMD_PORTAMENTODOWN;
+ m.param = autoPortaDown[chn];
+
+ } else if(autoFinePorta[chn])
+ {
+ m.command = CMD_MODCMDEX;
+ m.param = autoFinePorta[chn];
+
+ } else if(autoTonePorta[chn])
+ {
+ m.command = CMD_TONEPORTAMENTO;
+ m.param = tonePortaMem[chn] = autoTonePorta[chn];
+
+ } else if(autoVibrato[chn])
+ {
+ m.command = CMD_VIBRATO;
+ m.param = vibratoMem[chn] = autoVibrato[chn];
+
+ } else if(!didVolSlide && autoVolSlide[chn])
+ {
+ m.command = CMD_VOLUMESLIDE;
+ m.param = autoVolSlide[chn];
+ // 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;
+
+ } else if(autoTremolo[chn])
+ {
+ m.command = CMD_TREMOLO;
+ m.param = autoTremolo[chn];
+
+ } 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)
+ {
+ uint16 scriptNum;
+ uint32 length;
+
+ while(file.CanRead(2))
+ {
+ scriptNum = file.ReadUint16BE();
+ if(scriptNum == 0xFFFF)
+ break;
+
+ file.Skip(2);
+ 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);
+
+ 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(SAMPLEINDEX loop = 0; loop < loopInfo[smp-1].size(); loop++)
+ {
+ STPLoopInfo &info = loopInfo[smp-1][loop];
+
+ // 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
Property changes on: soundlib/Load_stp.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/x-c++src
\ No newline at end of property
Index: soundlib/Snd_defs.h
===================================================================
--- soundlib/Snd_defs.h (revision 6473)
+++ soundlib/Snd_defs.h (working copy)
@@ -98,6 +98,7 @@
MOD_TYPE_UAX = 0x10000000, // sampleset as module
MOD_TYPE_PLM = 0x20000000,
MOD_TYPE_SFX = 0x40000000,
+ MOD_TYPE_STP = 0x80000000,
};
DECLARE_FLAGSET(MODTYPE)
Index: soundlib/Snd_fx.cpp
===================================================================
--- soundlib/Snd_fx.cpp (revision 6473)
+++ soundlib/Snd_fx.cpp (working copy)
@@ -3358,7 +3358,7 @@
else
param = pChn->nOldPortaUpDown;
- const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+ const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
// Process MIDI pitch bend for instrument plugins
MidiPortamento(nChn, param, doFineSlides);
@@ -3417,7 +3417,7 @@
else
param = pChn->nOldPortaUpDown;
- const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI));
+ const bool doFineSlides = !doFinePortamentoAsRegular && !(GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_AMF0 | MOD_TYPE_DIGI | MOD_TYPE_STP));
// Process MIDI pitch bend for instrument plugins
MidiPortamento(nChn, -static_cast(param), doFineSlides);
@@ -3895,7 +3895,7 @@
else
param = pChn->nOldVolumeSlide;
- if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI)))
+ if((GetType() & (MOD_TYPE_MOD | MOD_TYPE_XM | MOD_TYPE_MT2 | MOD_TYPE_MED | MOD_TYPE_DIGI | MOD_TYPE_STP)))
{
// MOD / XM nibble priority
if((param & 0xF0) != 0)
@@ -5468,7 +5468,7 @@
//------------------------------------------------------------------------------------------
{
if (!period) return 0;
- if (GetType() & (MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_MTM | MOD_TYPE_AMF0 | MOD_TYPE_SFX))
+ if (GetType() & (MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_DIGI | MOD_TYPE_MTM | MOD_TYPE_AMF0 | MOD_TYPE_SFX | MOD_TYPE_STP))
{
return ((3546895L * 4) << FREQ_FRACBITS) / period;
} else if (GetType() == MOD_TYPE_XM)
Index: soundlib/Sndfile.cpp
===================================================================
--- soundlib/Sndfile.cpp (revision 6473)
+++ soundlib/Sndfile.cpp (working copy)
@@ -312,6 +312,7 @@
&& !ReadJ2B(file, loadFlags)
&& !ReadPT36(file, loadFlags)
&& !ReadSFX(file, loadFlags)
+ && !ReadSTP(file, loadFlags)
&& !ReadMod(file, loadFlags)
&& !ReadICE(file, loadFlags)
&& !Read669(file, loadFlags)
@@ -1058,6 +1059,7 @@
case MOD_TYPE_AMF0:
case MOD_TYPE_DIGI:
case MOD_TYPE_SFX:
+ case MOD_TYPE_STP:
return MOD_TYPE_MOD;
case MOD_TYPE_MED:
if(m_nDefaultTempo == TEMPO(125, 0) && m_nDefaultSpeed == 6 && !m_nInstruments)
Index: soundlib/Sndfile.h
===================================================================
--- soundlib/Sndfile.h (revision 6473)
+++ soundlib/Sndfile.h (working copy)
@@ -668,6 +668,7 @@
bool ReadPLM(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
bool ReadMID(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
bool ReadSFX(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
+ bool ReadSTP(FileReader &file, ModLoadingFlags loadFlags = loadCompleteModule);
static std::vector GetSupportedExtensions(bool otherFormats);
static mpt::Charset GetCharsetFromModType(MODTYPE modtype);
Index: soundlib/Tables.cpp
===================================================================
--- soundlib/Tables.cpp (revision 6473)
+++ soundlib/Tables.cpp (working copy)
@@ -90,6 +90,7 @@
{ MOD_TYPE_SFX, "SoundFX", "sfx" },
{ MOD_TYPE_SFX, "SoundFX", "sfx2" },
{ MOD_TYPE_SFX, "MultiMedia Sound", "mms" },
+ { MOD_TYPE_STP, "Soundtracker Pro II", "stp" },
#ifndef NO_ARCHIVE_SUPPORT
// Compressed modules
@@ -151,6 +152,7 @@
{ MOD_TYPE_DBM , mpt::CharsetISO8859_1 },
{ MOD_TYPE_DIGI, mpt::CharsetISO8859_1 },
{ MOD_TYPE_SFX , mpt::CharsetISO8859_1 },
+ { MOD_TYPE_STP, mpt::CharsetISO8859_1 },
// Amiga // DOS
{ MOD_TYPE_MOD , mpt::CharsetISO8859_1 },
{ MOD_TYPE_MED , mpt::CharsetISO8859_1 },