1 /*
2 * Load_stp.cpp
3 * ------------
4 * Purpose: STP (Soundtracker Pro II) module loader
5 * Notes : A few exotic effects aren't supported.
6 * Multiple sample loops are supported, but only the first 10 can be used as cue points
7 * (with 16xx and 18xx).
8 * Fractional speed values and combined auto effects are handled whenever possible,
9 * but some effects may be omitted (and there may be tempo accuracy issues).
10 * Authors: Devin Acker
11 * OpenMPT Devs
12 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
13 *
14 * Wisdom from the Soundtracker Pro II manual:
15 * "To create shorter patterns, simply create shorter patterns."
16 */
17
18 #include "stdafx.h"
19 #include "Loaders.h"
20
21 OPENMPT_NAMESPACE_BEGIN
22
23 // File header
24 struct STPFileHeader
25 {
26 char magic[4];
27 uint16be version;
28 uint8be numOrders;
29 uint8be patternLength;
30 uint8be orderList[128];
31 uint16be speed;
32 uint16be speedFrac;
33 uint16be timerCount;
34 uint16be flags;
35 uint32be reserved;
36 uint16be midiCount; // always 50
37 uint8be midi[50];
38 uint16be numSamples;
39 uint16be sampleStructSize;
40 };
41
42 MPT_BINARY_STRUCT(STPFileHeader, 204)
43
44
45 // Sample header (common part between all versions)
46 struct STPSampleHeader
47 {
48 uint32be length;
49 uint8be volume;
50 uint8be reserved1;
51 uint32be loopStart;
52 uint32be loopLength;
53 uint16be defaultCommand; // Default command to put next to note when editing patterns; not relevant for playback
54 // The following 4 bytes are reserved in version 0 and 1.
55 uint16be defaultPeriod;
56 uint8be finetune;
57 uint8be reserved2;
58
ConvertToMPTSTPSampleHeader59 void ConvertToMPT(ModSample &mptSmp) const
60 {
61 mptSmp.nLength = length;
62 mptSmp.nVolume = 4u * std::min(volume.get(), uint8(64));
63
64 mptSmp.nLoopStart = loopStart;
65 mptSmp.nLoopEnd = loopStart + loopLength;
66
67 if(mptSmp.nLoopStart >= mptSmp.nLength)
68 {
69 mptSmp.nLoopStart = mptSmp.nLength - 1;
70 }
71 if(mptSmp.nLoopEnd > mptSmp.nLength)
72 {
73 mptSmp.nLoopEnd = mptSmp.nLength;
74 }
75
76 if(mptSmp.nLoopStart > mptSmp.nLoopEnd)
77 {
78 mptSmp.nLoopStart = 0;
79 mptSmp.nLoopEnd = 0;
80 } else if(mptSmp.nLoopEnd > mptSmp.nLoopStart)
81 {
82 mptSmp.uFlags.set(CHN_LOOP);
83 mptSmp.cues[0] = mptSmp.nLoopStart;
84 }
85 }
86 };
87
88 MPT_BINARY_STRUCT(STPSampleHeader, 20)
89
90
91 struct STPLoopInfo
92 {
93 SmpLength loopStart;
94 SmpLength loopLength;
95 SAMPLEINDEX looped;
96 SAMPLEINDEX nonLooped;
97 };
98
99 typedef std::vector<STPLoopInfo> STPLoopList;
100
101
ConvertTempo(uint16 ciaSpeed)102 static TEMPO ConvertTempo(uint16 ciaSpeed)
103 {
104 // 3546 is the resulting CIA timer value when using 4F7D (tempo 125 bpm) command in STProII
105 return TEMPO((125.0 * 3546.0) / ciaSpeed);
106 }
107
108
ConvertLoopSlice(ModSample & src,ModSample & dest,SmpLength start,SmpLength len,bool loop)109 static void ConvertLoopSlice(ModSample &src, ModSample &dest, SmpLength start, SmpLength len, bool loop)
110 {
111 if(!src.HasSampleData()
112 || start >= src.nLength
113 || src.nLength - start < len)
114 {
115 return;
116 }
117
118 dest.FreeSample();
119 dest = src;
120 dest.nLength = len;
121 dest.pData.pSample = nullptr;
122
123 if(!dest.AllocateSample())
124 {
125 return;
126 }
127
128 // only preserve cue points if the target sample length is the same
129 if(len != src.nLength)
130 MemsetZero(dest.cues);
131
132 std::memcpy(dest.sampleb(), src.sampleb() + start, len);
133 dest.uFlags.set(CHN_LOOP, loop);
134 if(loop)
135 {
136 dest.nLoopStart = 0;
137 dest.nLoopEnd = len;
138 } else
139 {
140 dest.nLoopStart = 0;
141 dest.nLoopEnd = 0;
142 }
143 }
144
ConvertLoopSequence(ModSample & smp,STPLoopList & loopList)145 static void ConvertLoopSequence(ModSample &smp, STPLoopList &loopList)
146 {
147 // This should only modify a sample if it has more than one loop
148 // (otherwise, it behaves like a normal sample loop)
149 if(!smp.HasSampleData() || loopList.size() < 2) return;
150
151 ModSample newSmp = smp;
152 newSmp.nLength = 0;
153 newSmp.pData.pSample = nullptr;
154
155 size_t numLoops = loopList.size();
156
157 // Get the total length of the sample after combining all looped sections
158 for(size_t i = 0; i < numLoops; i++)
159 {
160 STPLoopInfo &info = loopList[i];
161
162 // If adding this loop would cause the sample length to exceed maximum,
163 // then limit and bail out
164 if(info.loopStart >= smp.nLength
165 || smp.nLength - info.loopStart < info.loopLength
166 || newSmp.nLength > MAX_SAMPLE_LENGTH - info.loopLength)
167 {
168 numLoops = i;
169 break;
170 }
171
172 newSmp.nLength += info.loopLength;
173 }
174
175 if(!newSmp.AllocateSample())
176 {
177 return;
178 }
179
180 // start copying the looped sample data parts
181 SmpLength start = 0;
182
183 for(size_t i = 0; i < numLoops; i++)
184 {
185 STPLoopInfo &info = loopList[i];
186
187 memcpy(newSmp.sampleb() + start, smp.sampleb() + info.loopStart, info.loopLength);
188
189 // update loop info based on position in edited sample
190 info.loopStart = start;
191 if(i > 0 && i <= std::size(newSmp.cues))
192 {
193 newSmp.cues[i - 1] = start;
194 }
195 start += info.loopLength;
196 }
197
198 // replace old sample with new one
199 smp.FreeSample();
200 smp = newSmp;
201
202 smp.nLoopStart = 0;
203 smp.nLoopEnd = smp.nLength;
204 smp.uFlags.set(CHN_LOOP);
205 }
206
207
ValidateHeader(const STPFileHeader & fileHeader)208 static bool ValidateHeader(const STPFileHeader &fileHeader)
209 {
210 if(std::memcmp(fileHeader.magic, "STP3", 4)
211 || fileHeader.version > 2
212 || fileHeader.numOrders > 128
213 || fileHeader.numSamples >= MAX_SAMPLES
214 || fileHeader.timerCount == 0
215 || fileHeader.midiCount != 50)
216 {
217 return false;
218 }
219 return true;
220 }
221
222
ProbeFileHeaderSTP(MemoryFileReader file,const uint64 * pfilesize)223 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderSTP(MemoryFileReader file, const uint64 *pfilesize)
224 {
225 STPFileHeader fileHeader;
226 if(!file.ReadStruct(fileHeader))
227 {
228 return ProbeWantMoreData;
229 }
230 if(!ValidateHeader(fileHeader))
231 {
232 return ProbeFailure;
233 }
234 MPT_UNREFERENCED_PARAMETER(pfilesize);
235 return ProbeSuccess;
236 }
237
238
ReadSTP(FileReader & file,ModLoadingFlags loadFlags)239 bool CSoundFile::ReadSTP(FileReader &file, ModLoadingFlags loadFlags)
240 {
241 file.Rewind();
242
243 STPFileHeader fileHeader;
244 if(!file.ReadStruct(fileHeader))
245 {
246 return false;
247 }
248 if(!ValidateHeader(fileHeader))
249 {
250 return false;
251 }
252 if(loadFlags == onlyVerifyHeader)
253 {
254 return true;
255 }
256
257 InitializeGlobals(MOD_TYPE_STP);
258
259 m_modFormat.formatName = MPT_UFORMAT("Soundtracker Pro II v{}")(fileHeader.version);
260 m_modFormat.type = U_("stp");
261 m_modFormat.charset = mpt::Charset::ISO8859_1;
262
263 m_nChannels = 4;
264 m_nSamples = 0;
265
266 m_nDefaultSpeed = fileHeader.speed;
267 m_nDefaultTempo = ConvertTempo(fileHeader.timerCount);
268
269 m_nMinPeriod = 14 * 4;
270 m_nMaxPeriod = 3424 * 4;
271
272 ReadOrderFromArray(Order(), fileHeader.orderList, fileHeader.numOrders);
273
274 std::vector<STPLoopList> loopInfo;
275 // Non-looped versions of samples with loops (when needed)
276 std::vector<SAMPLEINDEX> nonLooped;
277
278 // Load sample headers
279 SAMPLEINDEX samplesInFile = 0;
280
281 for(SAMPLEINDEX smp = 0; smp < fileHeader.numSamples; smp++)
282 {
283 SAMPLEINDEX actualSmp = file.ReadUint16BE();
284 if(actualSmp == 0 || actualSmp >= MAX_SAMPLES)
285 return false;
286 uint32 chunkSize = fileHeader.sampleStructSize;
287 if(fileHeader.version == 2)
288 chunkSize = file.ReadUint32BE() - 2;
289 FileReader chunk = file.ReadChunk(chunkSize);
290
291 samplesInFile = m_nSamples = std::max(m_nSamples, actualSmp);
292
293 ModSample &mptSmp = Samples[actualSmp];
294 mptSmp.Initialize(MOD_TYPE_MOD);
295
296 if(fileHeader.version < 2)
297 {
298 // Read path
299 chunk.ReadString<mpt::String::maybeNullTerminated>(mptSmp.filename, 31);
300 // Ignore flags, they are all not relevant for us
301 chunk.Skip(1);
302 // Read filename / sample text
303 chunk.ReadString<mpt::String::maybeNullTerminated>(m_szNames[actualSmp], 30);
304 } else
305 {
306 std::string str;
307 // Read path
308 chunk.ReadNullString(str, 257);
309 mptSmp.filename = str;
310 // Ignore flags, they are all not relevant for us
311 chunk.Skip(1);
312 // Read filename / sample text
313 chunk.ReadNullString(str, 31);
314 m_szNames[actualSmp] = str;
315 // Seek to even boundary
316 if(chunk.GetPosition() % 2u)
317 chunk.Skip(1);
318 }
319
320 STPSampleHeader sampleHeader;
321 chunk.ReadStruct(sampleHeader);
322 sampleHeader.ConvertToMPT(mptSmp);
323
324 if(fileHeader.version == 2)
325 {
326 mptSmp.nFineTune = static_cast<int8>(sampleHeader.finetune << 3);
327 }
328
329 if(fileHeader.version >= 1)
330 {
331 nonLooped.resize(samplesInFile);
332 loopInfo.resize(samplesInFile);
333 STPLoopList &loopList = loopInfo[actualSmp - 1];
334 loopList.clear();
335
336 const uint16 numLoops = file.ReadUint16BE();
337 if(!file.CanRead(numLoops * 8u))
338 return false;
339 loopList.reserve(numLoops);
340
341 STPLoopInfo loop;
342 loop.looped = loop.nonLooped = 0;
343
344 if(numLoops == 0 && mptSmp.uFlags[CHN_LOOP])
345 {
346 loop.loopStart = mptSmp.nLoopStart;
347 loop.loopLength = mptSmp.nLoopEnd - mptSmp.nLoopStart;
348 loopList.push_back(loop);
349 } else for(uint16 i = 0; i < numLoops; i++)
350 {
351 loop.loopStart = file.ReadUint32BE();
352 loop.loopLength = file.ReadUint32BE();
353 loopList.push_back(loop);
354 }
355 }
356 }
357
358 // Load patterns
359 uint16 numPatterns = 128;
360 if(fileHeader.version == 0)
361 numPatterns = file.ReadUint16BE();
362
363 uint16 patternLength = fileHeader.patternLength;
364 CHANNELINDEX channels = 4;
365 if(fileHeader.version > 0)
366 {
367 // Scan for total number of channels
368 FileReader::off_t patOffset = file.GetPosition();
369 for(uint16 pat = 0; pat < numPatterns; pat++)
370 {
371 PATTERNINDEX actualPat = file.ReadUint16BE();
372 if(actualPat == 0xFFFF)
373 break;
374
375 patternLength = file.ReadUint16BE();
376 channels = file.ReadUint16BE();
377 if(channels > MAX_BASECHANNELS)
378 return false;
379 m_nChannels = std::max(m_nChannels, channels);
380
381 file.Skip(channels * patternLength * 4u);
382 }
383 file.Seek(patOffset);
384 }
385
386 struct ChannelMemory
387 {
388 uint8 autoFinePorta, autoPortaUp, autoPortaDown, autoVolSlide, autoVibrato;
389 uint8 vibratoMem, autoTremolo, autoTonePorta, tonePortaMem;
390 };
391 std::vector<ChannelMemory> channelMemory(m_nChannels);
392 uint8 globalVolSlide = 0;
393 uint8 speedFrac = static_cast<uint8>(fileHeader.speedFrac);
394
395 for(uint16 pat = 0; pat < numPatterns; pat++)
396 {
397 PATTERNINDEX actualPat = pat;
398
399 if(fileHeader.version > 0)
400 {
401 actualPat = file.ReadUint16BE();
402 if(actualPat == 0xFFFF)
403 break;
404
405 patternLength = file.ReadUint16BE();
406 channels = file.ReadUint16BE();
407 }
408
409 if(!file.CanRead(channels * patternLength * 4u))
410 break;
411
412 if(!(loadFlags & loadPatternData) || !Patterns.Insert(actualPat, patternLength))
413 {
414 file.Skip(channels * patternLength * 4u);
415 continue;
416 }
417
418 for(ROWINDEX row = 0; row < patternLength; row++)
419 {
420 auto rowBase = Patterns[actualPat].GetRow(row);
421
422 bool didGlobalVolSlide = false;
423
424 // if a fractional speed value is in use then determine if we should stick a fine pattern delay somewhere
425 bool shouldDelay;
426 switch(speedFrac & 3)
427 {
428 default: shouldDelay = false; break;
429 // 1/4
430 case 1: shouldDelay = (row & 3) == 0; break;
431 // 1/2
432 case 2: shouldDelay = (row & 1) == 0; break;
433 // 3/4
434 case 3: shouldDelay = (row & 3) != 3; break;
435 }
436
437 for(CHANNELINDEX chn = 0; chn < channels; chn++)
438 {
439 ChannelMemory &chnMem = channelMemory[chn];
440 ModCommand &m = rowBase[chn];
441 const auto [instr, note, command, param] = file.ReadArray<uint8, 4>();
442
443 m.instr = instr;
444 m.note = note;
445 m.param = param;
446
447 if(m.note)
448 {
449 m.note += 24 + NOTE_MIN;
450 chnMem = ChannelMemory();
451 }
452
453 // this is a nibble-swapped param value used for auto fine volside
454 // and auto global fine volside
455 uint8 swapped = (m.param >> 4) | (m.param << 4);
456
457 if((command & 0xF0) == 0xF0)
458 {
459 // 12-bit CIA tempo
460 uint16 ciaTempo = (static_cast<uint16>(command & 0x0F) << 8) | m.param;
461 if(ciaTempo)
462 {
463 m.param = mpt::saturate_round<ModCommand::PARAM>(ConvertTempo(ciaTempo).ToDouble());
464 m.command = CMD_TEMPO;
465 } else
466 {
467 m.command = CMD_NONE;
468 }
469 } else switch(command)
470 {
471 case 0x00: // arpeggio
472 if(m.param)
473 m.command = CMD_ARPEGGIO;
474 else
475 m.command = CMD_NONE;
476 break;
477
478 case 0x01: // portamento up
479 m.command = CMD_PORTAMENTOUP;
480 break;
481
482 case 0x02: // portamento down
483 m.command = CMD_PORTAMENTODOWN;
484 break;
485
486 case 0x03: // auto fine portamento up
487 chnMem.autoFinePorta = 0x10 | std::min(m.param, ModCommand::PARAM(15));
488 chnMem.autoPortaUp = 0;
489 chnMem.autoPortaDown = 0;
490 chnMem.autoTonePorta = 0;
491
492 m.command = CMD_NONE;
493 break;
494
495 case 0x04: // auto fine portamento down
496 chnMem.autoFinePorta = 0x20 | std::min(m.param, ModCommand::PARAM(15));
497 chnMem.autoPortaUp = 0;
498 chnMem.autoPortaDown = 0;
499 chnMem.autoTonePorta = 0;
500
501 m.command = CMD_NONE;
502 break;
503
504 case 0x05: // auto portamento up
505 chnMem.autoFinePorta = 0;
506 chnMem.autoPortaUp = m.param;
507 chnMem.autoPortaDown = 0;
508 chnMem.autoTonePorta = 0;
509
510 m.command = CMD_NONE;
511 break;
512
513 case 0x06: // auto portamento down
514 chnMem.autoFinePorta = 0;
515 chnMem.autoPortaUp = 0;
516 chnMem.autoPortaDown = m.param;
517 chnMem.autoTonePorta = 0;
518
519 m.command = CMD_NONE;
520 break;
521
522 case 0x07: // set global volume
523 m.command = CMD_GLOBALVOLUME;
524 globalVolSlide = 0;
525 break;
526
527 case 0x08: // auto global fine volume slide
528 globalVolSlide = swapped;
529 m.command = CMD_NONE;
530 break;
531
532 case 0x09: // fine portamento up
533 m.command = CMD_MODCMDEX;
534 m.param = 0x10 | std::min(m.param, ModCommand::PARAM(15));
535 break;
536
537 case 0x0A: // fine portamento down
538 m.command = CMD_MODCMDEX;
539 m.param = 0x20 | std::min(m.param, ModCommand::PARAM(15));
540 break;
541
542 case 0x0B: // auto fine volume slide
543 chnMem.autoVolSlide = swapped;
544 m.command = CMD_NONE;
545 break;
546
547 case 0x0C: // set volume
548 m.volcmd = VOLCMD_VOLUME;
549 m.vol = m.param;
550 chnMem.autoVolSlide = 0;
551 m.command = CMD_NONE;
552 break;
553
554 case 0x0D: // volume slide (param is swapped compared to .mod)
555 if(m.param & 0xF0)
556 {
557 m.volcmd = VOLCMD_VOLSLIDEDOWN;
558 m.vol = m.param >> 4;
559 } else if(m.param & 0x0F)
560 {
561 m.volcmd = VOLCMD_VOLSLIDEUP;
562 m.vol = m.param & 0xF;
563 }
564 chnMem.autoVolSlide = 0;
565 m.command = CMD_NONE;
566 break;
567
568 case 0x0E: // set filter (also uses opposite value compared to .mod)
569 m.command = CMD_MODCMDEX;
570 m.param = 1 ^ (m.param ? 1 : 0);
571 break;
572
573 case 0x0F: // set speed
574 m.command = CMD_SPEED;
575 speedFrac = m.param & 0x0F;
576 m.param >>= 4;
577 break;
578
579 case 0x10: // auto vibrato
580 chnMem.autoVibrato = m.param;
581 chnMem.vibratoMem = 0;
582 m.command = CMD_NONE;
583 break;
584
585 case 0x11: // auto tremolo
586 if(m.param & 0xF)
587 chnMem.autoTremolo = m.param;
588 else
589 chnMem.autoTremolo = 0;
590 m.command = CMD_NONE;
591 break;
592
593 case 0x12: // pattern break
594 m.command = CMD_PATTERNBREAK;
595 break;
596
597 case 0x13: // auto tone portamento
598 chnMem.autoFinePorta = 0;
599 chnMem.autoPortaUp = 0;
600 chnMem.autoPortaDown = 0;
601 chnMem.autoTonePorta = m.param;
602
603 chnMem.tonePortaMem = 0;
604 m.command = CMD_NONE;
605 break;
606
607 case 0x14: // position jump
608 m.command = CMD_POSITIONJUMP;
609 break;
610
611 case 0x16: // start loop sequence
612 if(m.instr && m.instr <= loopInfo.size())
613 {
614 STPLoopList &loopList = loopInfo[m.instr - 1];
615
616 m.param--;
617 if(m.param < std::min(std::size(ModSample().cues), loopList.size()))
618 {
619 m.volcmd = VOLCMD_OFFSET;
620 m.vol = m.param;
621 }
622 }
623
624 m.command = CMD_NONE;
625 break;
626
627 case 0x17: // play only loop nn
628 if(m.instr && m.instr <= loopInfo.size())
629 {
630 STPLoopList &loopList = loopInfo[m.instr - 1];
631
632 m.param--;
633 if(m.param < loopList.size())
634 {
635 if(!loopList[m.param].looped && CanAddMoreSamples())
636 loopList[m.param].looped = ++m_nSamples;
637 m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].looped);
638 }
639 }
640
641 m.command = CMD_NONE;
642 break;
643
644 case 0x18: // play sequence without loop
645 if(m.instr && m.instr <= loopInfo.size())
646 {
647 STPLoopList &loopList = loopInfo[m.instr - 1];
648
649 m.param--;
650 if(m.param < std::min(std::size(ModSample().cues), loopList.size()))
651 {
652 m.volcmd = VOLCMD_OFFSET;
653 m.vol = m.param;
654 }
655 // switch to non-looped version of sample and create it if needed
656 if(!nonLooped[m.instr - 1] && CanAddMoreSamples())
657 nonLooped[m.instr - 1] = ++m_nSamples;
658 m.instr = static_cast<ModCommand::INSTR>(nonLooped[m.instr - 1]);
659 }
660
661 m.command = CMD_NONE;
662 break;
663
664 case 0x19: // play only loop nn without loop
665 if(m.instr && m.instr <= loopInfo.size())
666 {
667 STPLoopList &loopList = loopInfo[m.instr - 1];
668
669 m.param--;
670 if(m.param < loopList.size())
671 {
672 if(!loopList[m.param].nonLooped && CanAddMoreSamples())
673 loopList[m.param].nonLooped = ++m_nSamples;
674 m.instr = static_cast<ModCommand::INSTR>(loopList[m.param].nonLooped);
675 }
676 }
677
678 m.command = CMD_NONE;
679 break;
680
681 case 0x1D: // fine volume slide (nibble order also swapped)
682 m.command = CMD_VOLUMESLIDE;
683 m.param = swapped;
684 if(m.param & 0xF0) // slide down
685 m.param |= 0x0F;
686 else if(m.param & 0x0F)
687 m.param |= 0xF0;
688 break;
689
690 case 0x20: // "delayed fade"
691 // just behave like either a normal fade or a notecut
692 // depending on the speed
693 if(m.param & 0xF0)
694 {
695 chnMem.autoVolSlide = m.param >> 4;
696 m.command = CMD_NONE;
697 } else
698 {
699 m.command = CMD_MODCMDEX;
700 m.param = 0xC0 | (m.param & 0xF);
701 }
702 break;
703
704 case 0x21: // note delay
705 m.command = CMD_MODCMDEX;
706 m.param = 0xD0 | std::min(m.param, ModCommand::PARAM(15));
707 break;
708
709 case 0x22: // retrigger note
710 m.command = CMD_MODCMDEX;
711 m.param = 0x90 | std::min(m.param, ModCommand::PARAM(15));
712 break;
713
714 case 0x49: // set sample offset
715 m.command = CMD_OFFSET;
716 break;
717
718 case 0x4E: // other protracker commands (pattern loop / delay)
719 if((m.param & 0xF0) == 0x60 || (m.param & 0xF0) == 0xE0)
720 m.command = CMD_MODCMDEX;
721 else
722 m.command = CMD_NONE;
723 break;
724
725 case 0x4F: // set speed/tempo
726 if(m.param < 0x20)
727 {
728 m.command = CMD_SPEED;
729 speedFrac = 0;
730 } else
731 {
732 m.command = CMD_TEMPO;
733 }
734 break;
735
736 default:
737 m.command = CMD_NONE;
738 break;
739 }
740
741 bool didVolSlide = false;
742
743 // try to put volume slide in volume command
744 if(chnMem.autoVolSlide && m.volcmd == VOLCMD_NONE)
745 {
746 if(chnMem.autoVolSlide & 0xF0)
747 {
748 m.volcmd = VOLCMD_FINEVOLUP;
749 m.vol = chnMem.autoVolSlide >> 4;
750 } else
751 {
752 m.volcmd = VOLCMD_FINEVOLDOWN;
753 m.vol = chnMem.autoVolSlide & 0xF;
754 }
755 didVolSlide = true;
756 }
757
758 // try to place/combine all remaining running effects.
759 if(m.command == CMD_NONE)
760 {
761 if(chnMem.autoPortaUp)
762 {
763 m.command = CMD_PORTAMENTOUP;
764 m.param = chnMem.autoPortaUp;
765
766 } else if(chnMem.autoPortaDown)
767 {
768 m.command = CMD_PORTAMENTODOWN;
769 m.param = chnMem.autoPortaDown;
770 } else if(chnMem.autoFinePorta)
771 {
772 m.command = CMD_MODCMDEX;
773 m.param = chnMem.autoFinePorta;
774
775 } else if(chnMem.autoTonePorta)
776 {
777 m.command = CMD_TONEPORTAMENTO;
778 m.param = chnMem.tonePortaMem = chnMem.autoTonePorta;
779
780 } else if(chnMem.autoVibrato)
781 {
782 m.command = CMD_VIBRATO;
783 m.param = chnMem.vibratoMem = chnMem.autoVibrato;
784
785 } else if(!didVolSlide && chnMem.autoVolSlide)
786 {
787 m.command = CMD_VOLUMESLIDE;
788 m.param = chnMem.autoVolSlide;
789 // convert to a "fine" value by setting the other nibble to 0xF
790 if(m.param & 0x0F)
791 m.param |= 0xF0;
792 else if(m.param & 0xF0)
793 m.param |= 0x0F;
794 didVolSlide = true;
795
796 } else if(chnMem.autoTremolo)
797 {
798 m.command = CMD_TREMOLO;
799 m.param = chnMem.autoTremolo;
800
801 } else if(shouldDelay)
802 {
803 // insert a fine pattern delay here
804 m.command = CMD_S3MCMDEX;
805 m.param = 0x61;
806 shouldDelay = false;
807
808 } else if(!didGlobalVolSlide && globalVolSlide)
809 {
810 m.command = CMD_GLOBALVOLSLIDE;
811 m.param = globalVolSlide;
812 // convert to a "fine" value by setting the other nibble to 0xF
813 if(m.param & 0x0F)
814 m.param |= 0xF0;
815 else if(m.param & 0xF0)
816 m.param |= 0x0F;
817
818 didGlobalVolSlide = true;
819 }
820 }
821 }
822
823 // TODO: create/use extra channels for global volslide/delay if needed
824 }
825 }
826
827 // after we know how many channels there really are...
828 m_nSamplePreAmp = 256 / m_nChannels;
829 // Setup channel pan positions and volume
830 SetupMODPanning(true);
831
832 // Skip over scripts and drumpad info
833 if(fileHeader.version > 0)
834 {
835 while(file.CanRead(2))
836 {
837 uint16 scriptNum = file.ReadUint16BE();
838 if(scriptNum == 0xFFFF)
839 break;
840
841 file.Skip(2);
842 uint32 length = file.ReadUint32BE();
843 file.Skip(length);
844 }
845
846 // Skip drumpad stuff
847 file.Skip(17 * 2);
848 }
849
850 // Reading samples
851 if(loadFlags & loadSampleData)
852 {
853 for(SAMPLEINDEX smp = 1; smp <= samplesInFile; smp++) if(Samples[smp].nLength)
854 {
855 SampleIO(
856 SampleIO::_8bit,
857 SampleIO::mono,
858 SampleIO::littleEndian,
859 SampleIO::signedPCM)
860 .ReadSample(Samples[smp], file);
861
862 if(smp > loopInfo.size())
863 continue;
864
865 ConvertLoopSequence(Samples[smp], loopInfo[smp - 1]);
866
867 // make a non-looping duplicate of this sample if needed
868 if(nonLooped[smp - 1])
869 {
870 ConvertLoopSlice(Samples[smp], Samples[nonLooped[smp - 1]], 0, Samples[smp].nLength, false);
871 }
872
873 for(const auto &info : loopInfo[smp - 1])
874 {
875 // make duplicate samples for this individual section if needed
876 if(info.looped)
877 {
878 ConvertLoopSlice(Samples[smp], Samples[info.looped], info.loopStart, info.loopLength, true);
879 }
880 if(info.nonLooped)
881 {
882 ConvertLoopSlice(Samples[smp], Samples[info.nonLooped], info.loopStart, info.loopLength, false);
883 }
884 }
885 }
886 }
887
888 return true;
889 }
890
891 OPENMPT_NAMESPACE_END
892