1 /*
2 * SampleFormats.cpp
3 * -----------------
4 * Purpose: Code for loading various more or less common sample and instrument formats.
5 * Notes : (currently none)
6 * Authors: OpenMPT Devs
7 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8 */
9
10
11 #include "stdafx.h"
12 #include "Sndfile.h"
13 #include "mod_specifications.h"
14 #ifdef MODPLUG_TRACKER
15 #include "../mptrack/Moddoc.h"
16 #include "Dlsbank.h"
17 #endif // MODPLUG_TRACKER
18 #include "../soundlib/AudioCriticalSection.h"
19 #ifndef MODPLUG_NO_FILESAVE
20 #include "mpt/io/base.hpp"
21 #include "mpt/io/io.hpp"
22 #include "mpt/io/io_stdstream.hpp"
23 #include "../common/mptFileIO.h"
24 #endif // !MODPLUG_NO_FILESAVE
25 #include "../common/misc_util.h"
26 #include "openmpt/base/Endian.hpp"
27 #include "Tagging.h"
28 #include "ITTools.h"
29 #include "XMTools.h"
30 #include "S3MTools.h"
31 #include "WAVTools.h"
32 #include "../common/version.h"
33 #include "Loaders.h"
34 #include "../common/FileReader.h"
35 #include "../soundlib/ModSampleCopy.h"
36 #include <functional>
37 #include <map>
38
39
40 OPENMPT_NAMESPACE_BEGIN
41
42
43 using namespace mpt::uuid_literals;
44
45
ReadSampleFromFile(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize,bool includeInstrumentFormats)46 bool CSoundFile::ReadSampleFromFile(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize, bool includeInstrumentFormats)
47 {
48 if(!nSample || nSample >= MAX_SAMPLES) return false;
49 if(!ReadWAVSample(nSample, file, mayNormalize)
50 && !(includeInstrumentFormats && ReadXISample(nSample, file))
51 && !(includeInstrumentFormats && ReadITISample(nSample, file))
52 && !ReadW64Sample(nSample, file)
53 && !ReadCAFSample(nSample, file)
54 && !ReadAIFFSample(nSample, file, mayNormalize)
55 && !ReadITSSample(nSample, file)
56 && !(includeInstrumentFormats && ReadPATSample(nSample, file))
57 && !ReadIFFSample(nSample, file)
58 && !ReadS3ISample(nSample, file)
59 && !ReadSBISample(nSample, file)
60 && !ReadAUSample(nSample, file, mayNormalize)
61 && !ReadBRRSample(nSample, file)
62 && !ReadFLACSample(nSample, file)
63 && !ReadOpusSample(nSample, file)
64 && !ReadVorbisSample(nSample, file)
65 && !ReadMP3Sample(nSample, file, false)
66 && !ReadMediaFoundationSample(nSample, file)
67 )
68 {
69 return false;
70 }
71
72 if(nSample > GetNumSamples())
73 {
74 m_nSamples = nSample;
75 }
76 if(Samples[nSample].uFlags[CHN_ADLIB])
77 {
78 InitOPL();
79 }
80 return true;
81 }
82
83
ReadInstrumentFromFile(INSTRUMENTINDEX nInstr,FileReader & file,bool mayNormalize)84 bool CSoundFile::ReadInstrumentFromFile(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize)
85 {
86 if ((!nInstr) || (nInstr >= MAX_INSTRUMENTS)) return false;
87 if(!ReadITIInstrument(nInstr, file)
88 && !ReadXIInstrument(nInstr, file)
89 && !ReadPATInstrument(nInstr, file)
90 && !ReadSFZInstrument(nInstr, file)
91 // Generic read
92 && !ReadSampleAsInstrument(nInstr, file, mayNormalize))
93 {
94 bool ok = false;
95 #ifdef MODPLUG_TRACKER
96 CDLSBank bank;
97 if(bank.Open(file))
98 {
99 ok = bank.ExtractInstrument(*this, nInstr, 0, 0);
100 }
101 #endif // MODPLUG_TRACKER
102 if(!ok) return false;
103 }
104
105 if(nInstr > GetNumInstruments()) m_nInstruments = nInstr;
106 return true;
107 }
108
109
ReadSampleAsInstrument(INSTRUMENTINDEX nInstr,FileReader & file,bool mayNormalize)110 bool CSoundFile::ReadSampleAsInstrument(INSTRUMENTINDEX nInstr, FileReader &file, bool mayNormalize)
111 {
112 // Scanning free sample
113 SAMPLEINDEX nSample = GetNextFreeSample(nInstr); // may also return samples which are only referenced by the current instrument
114 if(nSample == SAMPLEINDEX_INVALID)
115 {
116 return false;
117 }
118
119 // Loading Instrument
120 ModInstrument *pIns = new (std::nothrow) ModInstrument(nSample);
121 if(pIns == nullptr)
122 {
123 return false;
124 }
125 if(!ReadSampleFromFile(nSample, file, mayNormalize, false))
126 {
127 delete pIns;
128 return false;
129 }
130
131 // Remove all samples which are only referenced by the old instrument, except for the one we just loaded our new sample into.
132 RemoveInstrumentSamples(nInstr, nSample);
133
134 // Replace the instrument
135 DestroyInstrument(nInstr, doNoDeleteAssociatedSamples);
136 Instruments[nInstr] = pIns;
137
138 #if defined(MPT_ENABLE_FILEIO) && defined(MPT_EXTERNAL_SAMPLES)
139 SetSamplePath(nSample, file.GetOptionalFileName().value_or(P_("")));
140 #endif
141
142 return true;
143 }
144
145
DestroyInstrument(INSTRUMENTINDEX nInstr,deleteInstrumentSamples removeSamples)146 bool CSoundFile::DestroyInstrument(INSTRUMENTINDEX nInstr, deleteInstrumentSamples removeSamples)
147 {
148 if(nInstr == 0 || nInstr >= MAX_INSTRUMENTS || !Instruments[nInstr]) return true;
149
150 if(removeSamples == deleteAssociatedSamples)
151 {
152 RemoveInstrumentSamples(nInstr);
153 }
154
155 CriticalSection cs;
156
157 ModInstrument *pIns = Instruments[nInstr];
158 Instruments[nInstr] = nullptr;
159 for(auto &chn : m_PlayState.Chn)
160 {
161 if(chn.pModInstrument == pIns)
162 chn.pModInstrument = nullptr;
163 }
164 delete pIns;
165 return true;
166 }
167
168
169 // Remove all unused samples from the given nInstr and keep keepSample if provided
RemoveInstrumentSamples(INSTRUMENTINDEX nInstr,SAMPLEINDEX keepSample)170 bool CSoundFile::RemoveInstrumentSamples(INSTRUMENTINDEX nInstr, SAMPLEINDEX keepSample)
171 {
172 if(Instruments[nInstr] == nullptr)
173 {
174 return false;
175 }
176
177 std::vector<bool> keepSamples(GetNumSamples() + 1, true);
178
179 // Check which samples are used by the instrument we are going to nuke.
180 auto referencedSamples = Instruments[nInstr]->GetSamples();
181 for(auto sample : referencedSamples)
182 {
183 if(sample <= GetNumSamples())
184 {
185 keepSamples[sample] = false;
186 }
187 }
188
189 // If we want to keep a specific sample, do so.
190 if(keepSample != SAMPLEINDEX_INVALID)
191 {
192 if(keepSample <= GetNumSamples())
193 {
194 keepSamples[keepSample] = true;
195 }
196 }
197
198 // Check if any of those samples are referenced by other instruments as well, in which case we want to keep them of course.
199 for(INSTRUMENTINDEX nIns = 1; nIns <= GetNumInstruments(); nIns++) if (Instruments[nIns] != nullptr && nIns != nInstr)
200 {
201 Instruments[nIns]->GetSamples(keepSamples);
202 }
203
204 // Now nuke the selected samples.
205 RemoveSelectedSamples(keepSamples);
206 return true;
207 }
208
209 ////////////////////////////////////////////////////////////////////////////////
210 //
211 // I/O From another song
212 //
213
ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr,const CSoundFile & srcSong,INSTRUMENTINDEX sourceInstr)214 bool CSoundFile::ReadInstrumentFromSong(INSTRUMENTINDEX targetInstr, const CSoundFile &srcSong, INSTRUMENTINDEX sourceInstr)
215 {
216 if ((!sourceInstr) || (sourceInstr > srcSong.GetNumInstruments())
217 || (targetInstr >= MAX_INSTRUMENTS) || (!srcSong.Instruments[sourceInstr]))
218 {
219 return false;
220 }
221 if (m_nInstruments < targetInstr) m_nInstruments = targetInstr;
222
223 ModInstrument *pIns = new (std::nothrow) ModInstrument();
224 if(pIns == nullptr)
225 {
226 return false;
227 }
228
229 DestroyInstrument(targetInstr, deleteAssociatedSamples);
230
231 Instruments[targetInstr] = pIns;
232 *pIns = *srcSong.Instruments[sourceInstr];
233
234 std::vector<SAMPLEINDEX> sourceSample; // Sample index in source song
235 std::vector<SAMPLEINDEX> targetSample; // Sample index in target song
236 SAMPLEINDEX targetIndex = 0; // Next index for inserting sample
237
238 for(auto &sample : pIns->Keyboard)
239 {
240 const SAMPLEINDEX sourceIndex = sample;
241 if(sourceIndex > 0 && sourceIndex <= srcSong.GetNumSamples())
242 {
243 const auto entry = std::find(sourceSample.cbegin(), sourceSample.cend(), sourceIndex);
244 if(entry == sourceSample.end())
245 {
246 // Didn't consider this sample yet, so add it to our map.
247 targetIndex = GetNextFreeSample(targetInstr, targetIndex + 1);
248 if(targetIndex <= GetModSpecifications().samplesMax)
249 {
250 sourceSample.push_back(sourceIndex);
251 targetSample.push_back(targetIndex);
252 sample = targetIndex;
253 } else
254 {
255 sample = 0;
256 }
257 } else
258 {
259 // Sample reference has already been created, so only need to update the sample map.
260 sample = *(entry - sourceSample.begin() + targetSample.begin());
261 }
262 } else
263 {
264 // Invalid or no source sample
265 sample = 0;
266 }
267 }
268
269 #ifdef MODPLUG_TRACKER
270 if(pIns->filename.empty() && srcSong.GetpModDoc() != nullptr && &srcSong != this)
271 {
272 pIns->filename = srcSong.GetpModDoc()->GetPathNameMpt().GetFullFileName().ToLocale();
273 }
274 #endif
275 pIns->Convert(srcSong.GetType(), GetType());
276
277 // Copy all referenced samples over
278 for(size_t i = 0; i < targetSample.size(); i++)
279 {
280 ReadSampleFromSong(targetSample[i], srcSong, sourceSample[i]);
281 }
282
283 return true;
284 }
285
286
ReadSampleFromSong(SAMPLEINDEX targetSample,const CSoundFile & srcSong,SAMPLEINDEX sourceSample)287 bool CSoundFile::ReadSampleFromSong(SAMPLEINDEX targetSample, const CSoundFile &srcSong, SAMPLEINDEX sourceSample)
288 {
289 if(!sourceSample
290 || sourceSample > srcSong.GetNumSamples()
291 || (targetSample >= GetModSpecifications().samplesMax && targetSample > GetNumSamples()))
292 {
293 return false;
294 }
295
296 DestroySampleThreadsafe(targetSample);
297
298 const ModSample &sourceSmp = srcSong.GetSample(sourceSample);
299 ModSample &targetSmp = GetSample(targetSample);
300
301 if(GetNumSamples() < targetSample) m_nSamples = targetSample;
302 targetSmp = sourceSmp;
303 m_szNames[targetSample] = srcSong.m_szNames[sourceSample];
304
305 if(sourceSmp.HasSampleData())
306 {
307 if(targetSmp.CopyWaveform(sourceSmp))
308 targetSmp.PrecomputeLoops(*this, false);
309 // Remember on-disk path (for MPTM files), but don't implicitely enable on-disk storage
310 // (we really don't want this for e.g. duplicating samples or splitting stereo samples)
311 #ifdef MPT_EXTERNAL_SAMPLES
312 SetSamplePath(targetSample, srcSong.GetSamplePath(sourceSample));
313 #endif
314 targetSmp.uFlags.reset(SMP_KEEPONDISK);
315 }
316
317 #ifdef MODPLUG_TRACKER
318 if((targetSmp.filename.empty()) && srcSong.GetpModDoc() != nullptr && &srcSong != this)
319 {
320 targetSmp.filename = mpt::ToCharset(GetCharsetInternal(), srcSong.GetpModDoc()->GetTitle());
321 }
322 #endif
323
324 if(targetSmp.uFlags[CHN_ADLIB] && !SupportsOPL())
325 {
326 AddToLog(LogInformation, U_("OPL instruments are not supported by this format."));
327 }
328 targetSmp.Convert(srcSong.GetType(), GetType());
329 if(targetSmp.uFlags[CHN_ADLIB])
330 {
331 InitOPL();
332 }
333 return true;
334 }
335
336
337 ////////////////////////////////////////////////////////////////////////
338 // IMA ADPCM Support for WAV files
339
340
IMAADPCMUnpack16(int16 * target,SmpLength sampleLen,FileReader file,uint16 blockAlign,uint32 numChannels)341 static bool IMAADPCMUnpack16(int16 *target, SmpLength sampleLen, FileReader file, uint16 blockAlign, uint32 numChannels)
342 {
343 static constexpr int8 IMAIndexTab[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
344 static constexpr int16 IMAUnpackTable[90] =
345 {
346 7, 8, 9, 10, 11, 12, 13, 14,
347 16, 17, 19, 21, 23, 25, 28, 31,
348 34, 37, 41, 45, 50, 55, 60, 66,
349 73, 80, 88, 97, 107, 118, 130, 143,
350 157, 173, 190, 209, 230, 253, 279, 307,
351 337, 371, 408, 449, 494, 544, 598, 658,
352 724, 796, 876, 963, 1060, 1166, 1282, 1411,
353 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
354 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
355 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
356 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
357 32767, 0
358 };
359
360 if(target == nullptr || blockAlign < 4u * numChannels)
361 return false;
362
363 SmpLength samplePos = 0;
364 sampleLen *= numChannels;
365 while(file.CanRead(4u * numChannels) && samplePos < sampleLen)
366 {
367 FileReader block = file.ReadChunk(blockAlign);
368 FileReader::PinnedView blockView = block.GetPinnedView();
369 const std::byte *data = blockView.data();
370 const uint32 blockSize = static_cast<uint32>(blockView.size());
371
372 for(uint32 chn = 0; chn < numChannels; chn++)
373 {
374 // Block header
375 int32 value = block.ReadInt16LE();
376 int32 nIndex = block.ReadUint8();
377 Limit(nIndex, 0, 89);
378 block.Skip(1);
379
380 SmpLength smpPos = samplePos + chn;
381 uint32 dataPos = (numChannels + chn) * 4;
382 // Block data
383 while(smpPos <= (sampleLen - 8) && dataPos <= (blockSize - 4))
384 {
385 for(uint32 i = 0; i < 8; i++)
386 {
387 uint8 delta = mpt::byte_cast<uint8>(data[dataPos]);
388 if(i & 1)
389 {
390 delta >>= 4;
391 dataPos++;
392 } else
393 {
394 delta &= 0x0F;
395 }
396 int32 v = IMAUnpackTable[nIndex] >> 3;
397 if (delta & 1) v += IMAUnpackTable[nIndex] >> 2;
398 if (delta & 2) v += IMAUnpackTable[nIndex] >> 1;
399 if (delta & 4) v += IMAUnpackTable[nIndex];
400 if (delta & 8) value -= v; else value += v;
401 nIndex += IMAIndexTab[delta & 7];
402 Limit(nIndex, 0, 88);
403 Limit(value, -32768, 32767);
404 target[smpPos] = static_cast<int16>(value);
405 smpPos += numChannels;
406 }
407 dataPos += (numChannels - 1) * 4u;
408 }
409 }
410 samplePos += ((blockSize - (numChannels * 4u)) * 2u);
411 }
412
413 return true;
414 }
415
416
417 ////////////////////////////////////////////////////////////////////////////////
418 // WAV Open
419
ReadWAVSample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize,FileReader * wsmpChunk)420 bool CSoundFile::ReadWAVSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize, FileReader *wsmpChunk)
421 {
422 WAVReader wavFile(file);
423
424 static constexpr WAVFormatChunk::SampleFormats SupportedFormats[] = {WAVFormatChunk::fmtPCM, WAVFormatChunk::fmtFloat, WAVFormatChunk::fmtIMA_ADPCM, WAVFormatChunk::fmtMP3, WAVFormatChunk::fmtALaw, WAVFormatChunk::fmtULaw};
425 if(!wavFile.IsValid()
426 || wavFile.GetNumChannels() == 0
427 || wavFile.GetNumChannels() > 2
428 || (wavFile.GetBitsPerSample() == 0 && wavFile.GetSampleFormat() != WAVFormatChunk::fmtMP3)
429 || (wavFile.GetBitsPerSample() < 32 && wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
430 || (wavFile.GetBitsPerSample() > 64)
431 || !mpt::contains(SupportedFormats, wavFile.GetSampleFormat()))
432 {
433 return false;
434 }
435
436 DestroySampleThreadsafe(nSample);
437 m_szNames[nSample] = "";
438 ModSample &sample = Samples[nSample];
439 sample.Initialize();
440 sample.nLength = wavFile.GetSampleLength();
441 sample.nC5Speed = wavFile.GetSampleRate();
442 wavFile.ApplySampleSettings(sample, GetCharsetInternal(), m_szNames[nSample]);
443
444 FileReader sampleChunk = wavFile.GetSampleData();
445
446 SampleIO sampleIO(
447 SampleIO::_8bit,
448 (wavFile.GetNumChannels() > 1) ? SampleIO::stereoInterleaved : SampleIO::mono,
449 SampleIO::littleEndian,
450 SampleIO::signedPCM);
451
452 if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtIMA_ADPCM && wavFile.GetNumChannels() <= 2)
453 {
454 // IMA ADPCM 4:1
455 LimitMax(sample.nLength, MAX_SAMPLE_LENGTH);
456 sample.uFlags.set(CHN_16BIT);
457 sample.uFlags.set(CHN_STEREO, wavFile.GetNumChannels() == 2);
458 if(!sample.AllocateSample())
459 {
460 return false;
461 }
462 IMAADPCMUnpack16(sample.sample16(), sample.nLength, sampleChunk, wavFile.GetBlockAlign(), wavFile.GetNumChannels());
463 sample.PrecomputeLoops(*this, false);
464 } else if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtMP3)
465 {
466 // MP3 in WAV
467 bool loadedMP3 = ReadMP3Sample(nSample, sampleChunk, false, true) || ReadMediaFoundationSample(nSample, sampleChunk, true);
468 if(!loadedMP3)
469 {
470 return false;
471 }
472 } else if(!wavFile.IsExtensibleFormat() && wavFile.MayBeCoolEdit16_8() && wavFile.GetSampleFormat() == WAVFormatChunk::fmtPCM && wavFile.GetBitsPerSample() == 32 && wavFile.GetBlockAlign() == wavFile.GetNumChannels() * 4)
473 {
474 // Syntrillium Cool Edit hack to store IEEE 32bit floating point
475 // Format is described as 32bit integer PCM contained in 32bit blocks and an WAVEFORMATEX extension size of 2 which contains a single 16 bit little endian value of 1.
476 // (This is parsed in WAVTools.cpp and returned via MayBeCoolEdit16_8()).
477 // The data actually stored in this case is little endian 32bit floating point PCM with 2**15 full scale.
478 // Cool Edit calls this format "16.8 float".
479 sampleIO |= SampleIO::_32bit;
480 sampleIO |= SampleIO::floatPCM15;
481 sampleIO.ReadSample(sample, sampleChunk);
482 } else if(!wavFile.IsExtensibleFormat() && wavFile.GetSampleFormat() == WAVFormatChunk::fmtPCM && wavFile.GetBitsPerSample() == 24 && wavFile.GetBlockAlign() == wavFile.GetNumChannels() * 4)
483 {
484 // Syntrillium Cool Edit hack to store IEEE 32bit floating point
485 // Format is described as 24bit integer PCM contained in 32bit blocks.
486 // The data actually stored in this case is little endian 32bit floating point PCM with 2**23 full scale.
487 // Cool Edit calls this format "24.0 float".
488 sampleIO |= SampleIO::_32bit;
489 sampleIO |= SampleIO::floatPCM23;
490 sampleIO.ReadSample(sample, sampleChunk);
491 } else if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtALaw || wavFile.GetSampleFormat() == WAVFormatChunk::fmtULaw)
492 {
493 // a-law / u-law
494 sampleIO |= SampleIO::_16bit;
495 sampleIO |= wavFile.GetSampleFormat() == WAVFormatChunk::fmtALaw ? SampleIO::aLaw : SampleIO::uLaw;
496 sampleIO.ReadSample(sample, sampleChunk);
497 } else
498 {
499 // PCM / Float
500 SampleIO::Bitdepth bitDepth;
501 switch((wavFile.GetBitsPerSample() - 1) / 8u)
502 {
503 default:
504 case 0: bitDepth = SampleIO::_8bit; break;
505 case 1: bitDepth = SampleIO::_16bit; break;
506 case 2: bitDepth = SampleIO::_24bit; break;
507 case 3: bitDepth = SampleIO::_32bit; break;
508 case 7: bitDepth = SampleIO::_64bit; break;
509 }
510
511 sampleIO |= bitDepth;
512 if(wavFile.GetBitsPerSample() <= 8)
513 sampleIO |= SampleIO::unsignedPCM;
514
515 if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
516 sampleIO |= SampleIO::floatPCM;
517
518 if(mayNormalize)
519 sampleIO.MayNormalize();
520
521 sampleIO.ReadSample(sample, sampleChunk);
522 }
523
524 if(wsmpChunk != nullptr)
525 {
526 // DLS WSMP chunk
527 *wsmpChunk = wavFile.GetWsmpChunk();
528 }
529
530 sample.Convert(MOD_TYPE_IT, GetType());
531 sample.PrecomputeLoops(*this, false);
532
533 return true;
534 }
535
536
537 ///////////////////////////////////////////////////////////////
538 // Save WAV
539
540
541 #ifndef MODPLUG_NO_FILESAVE
SaveWAVSample(SAMPLEINDEX nSample,std::ostream & f) const542 bool CSoundFile::SaveWAVSample(SAMPLEINDEX nSample, std::ostream &f) const
543 {
544 const ModSample &sample = Samples[nSample];
545 if(sample.uFlags[CHN_ADLIB])
546 return false;
547
548 mpt::IO::OFile<std::ostream> ff(f);
549 WAVWriter file(ff);
550
551 file.WriteFormat(sample.GetSampleRate(GetType()), sample.GetElementarySampleSize() * 8, sample.GetNumChannels(), WAVFormatChunk::fmtPCM);
552
553 // Write sample data
554 file.StartChunk(RIFFChunk::iddata);
555 file.Skip(SampleIO(
556 sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
557 sample.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved : SampleIO::mono,
558 SampleIO::littleEndian,
559 sample.uFlags[CHN_16BIT] ? SampleIO::signedPCM : SampleIO::unsignedPCM)
560 .WriteSample(f, sample));
561
562 file.WriteLoopInformation(sample);
563 file.WriteExtraInformation(sample, GetType());
564 if(sample.HasCustomCuePoints())
565 {
566 file.WriteCueInformation(sample);
567 }
568
569 FileTags tags;
570 tags.SetEncoder();
571 tags.title = mpt::ToUnicode(GetCharsetInternal(), m_szNames[nSample]);
572 file.WriteMetatags(tags);
573 file.Finalize();
574
575 return true;
576 }
577
578 #endif // MODPLUG_NO_FILESAVE
579
580
581
582 /////////////////
583 // Sony Wave64 //
584
585
586 struct Wave64FileHeader
587 {
588 mpt::GUIDms GuidRIFF;
589 uint64le FileSize;
590 mpt::GUIDms GuidWAVE;
591 };
592
593 MPT_BINARY_STRUCT(Wave64FileHeader, 40)
594
595
596 struct Wave64ChunkHeader
597 {
598 mpt::GUIDms GuidChunk;
599 uint64le Size;
600 };
601
602 MPT_BINARY_STRUCT(Wave64ChunkHeader, 24)
603
604
605 struct Wave64Chunk
606 {
607 Wave64ChunkHeader header;
608
GetLengthWave64Chunk609 FileReader::off_t GetLength() const
610 {
611 uint64 length = header.Size;
612 if(length < sizeof(Wave64ChunkHeader))
613 {
614 length = 0;
615 } else
616 {
617 length -= sizeof(Wave64ChunkHeader);
618 }
619 return mpt::saturate_cast<FileReader::off_t>(length);
620 }
621
GetIDWave64Chunk622 mpt::UUID GetID() const
623 {
624 return mpt::UUID(header.GuidChunk);
625 }
626 };
627
628 MPT_BINARY_STRUCT(Wave64Chunk, 24)
629
630
Wave64TagFromLISTINFO(mpt::ustring & dst,uint16 codePage,const FileReader::ChunkList<RIFFChunk> & infoChunk,RIFFChunk::ChunkIdentifiers id)631 static void Wave64TagFromLISTINFO(mpt::ustring & dst, uint16 codePage, const FileReader::ChunkList<RIFFChunk> & infoChunk, RIFFChunk::ChunkIdentifiers id)
632 {
633 if(!infoChunk.ChunkExists(id))
634 {
635 return;
636 }
637 FileReader textChunk = infoChunk.GetChunk(id);
638 if(!textChunk.IsValid())
639 {
640 return;
641 }
642 std::string str;
643 textChunk.ReadString<mpt::String::maybeNullTerminated>(str, textChunk.GetLength());
644 str = mpt::replace(str, std::string("\r\n"), std::string("\n"));
645 str = mpt::replace(str, std::string("\r"), std::string("\n"));
646 dst = mpt::ToUnicode(codePage, mpt::Charset::Windows1252, str);
647 }
648
649
ReadW64Sample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize)650 bool CSoundFile::ReadW64Sample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize)
651 {
652 file.Rewind();
653
654 constexpr mpt::UUID guidRIFF = "66666972-912E-11CF-A5D6-28DB04C10000"_uuid;
655 constexpr mpt::UUID guidWAVE = "65766177-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
656
657 constexpr mpt::UUID guidLIST = "7473696C-912F-11CF-A5D6-28DB04C10000"_uuid;
658 constexpr mpt::UUID guidFMT = "20746D66-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
659 //constexpr mpt::UUID guidFACT = "74636166-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
660 constexpr mpt::UUID guidDATA = "61746164-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
661 //constexpr mpt::UUID guidLEVL = "6C76656C-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
662 //constexpr mpt::UUID guidJUNK = "6b6E756A-ACF3-11D3-8CD1-00C04f8EDB8A"_uuid;
663 //constexpr mpt::UUID guidBEXT = "74786562-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
664 //constexpr mpt::UUID guiMARKER = "ABF76256-392D-11D2-86C7-00C04F8EDB8A"_uuid;
665 //constexpr mpt::UUID guiSUMMARYLIST = "925F94BC-525A-11D2-86DC-00C04F8EDB8A"_uuid;
666
667 constexpr mpt::UUID guidCSET = "54455343-ACF3-11D3-8CD1-00C04F8EDB8A"_uuid;
668
669 Wave64FileHeader fileHeader;
670 if(!file.ReadStruct(fileHeader))
671 {
672 return false;
673 }
674 if(mpt::UUID(fileHeader.GuidRIFF) != guidRIFF)
675 {
676 return false;
677 }
678 if(mpt::UUID(fileHeader.GuidWAVE) != guidWAVE)
679 {
680 return false;
681 }
682 if(fileHeader.FileSize != file.GetLength())
683 {
684 return false;
685 }
686
687 FileReader chunkFile = file;
688 auto chunkList = chunkFile.ReadChunks<Wave64Chunk>(8);
689
690 if(!chunkList.ChunkExists(guidFMT))
691 {
692 return false;
693 }
694 FileReader formatChunk = chunkList.GetChunk(guidFMT);
695 WAVFormatChunk format;
696 if(!formatChunk.ReadStruct(format))
697 {
698 return false;
699 }
700 uint16 sampleFormat = format.format;
701 if(format.format == WAVFormatChunk::fmtExtensible)
702 {
703 WAVFormatChunkExtension formatExt;
704 if(!formatChunk.ReadStruct(formatExt))
705 {
706 return false;
707 }
708 sampleFormat = static_cast<uint16>(mpt::UUID(formatExt.subFormat).GetData1());
709 }
710 if(format.sampleRate == 0)
711 {
712 return false;
713 }
714 if(format.numChannels == 0)
715 {
716 return false;
717 }
718 if(format.numChannels > 2)
719 {
720 return false;
721 }
722 if(sampleFormat != WAVFormatChunk::fmtPCM && sampleFormat != WAVFormatChunk::fmtFloat)
723 {
724 return false;
725 }
726 if(sampleFormat == WAVFormatChunk::fmtFloat && format.bitsPerSample != 32 && format.bitsPerSample != 64)
727 {
728 return false;
729 }
730 if(sampleFormat == WAVFormatChunk::fmtPCM && format.bitsPerSample > 64)
731 {
732 return false;
733 }
734
735 SampleIO::Bitdepth bitDepth;
736 switch((format.bitsPerSample - 1) / 8u)
737 {
738 default:
739 case 0: bitDepth = SampleIO::_8bit ; break;
740 case 1: bitDepth = SampleIO::_16bit; break;
741 case 2: bitDepth = SampleIO::_24bit; break;
742 case 3: bitDepth = SampleIO::_32bit; break;
743 case 7: bitDepth = SampleIO::_64bit; break;
744 }
745 SampleIO sampleIO(
746 bitDepth,
747 (format.numChannels > 1) ? SampleIO::stereoInterleaved : SampleIO::mono,
748 SampleIO::littleEndian,
749 (sampleFormat == WAVFormatChunk::fmtFloat) ? SampleIO::floatPCM : SampleIO::signedPCM);
750 if(format.bitsPerSample <= 8)
751 {
752 sampleIO |= SampleIO::unsignedPCM;
753 }
754 if(mayNormalize)
755 {
756 sampleIO.MayNormalize();
757 }
758
759 FileTags tags;
760
761 uint16 codePage = 28591; // mpt::Charset::ISO8859_1
762 FileReader csetChunk = chunkList.GetChunk(guidCSET);
763 if(csetChunk.IsValid())
764 {
765 if(csetChunk.CanRead(2))
766 {
767 codePage = csetChunk.ReadUint16LE();
768 }
769 }
770
771 if(chunkList.ChunkExists(guidLIST))
772 {
773 FileReader listChunk = chunkList.GetChunk(guidLIST);
774 if(listChunk.ReadMagic("INFO"))
775 {
776 auto infoChunk = listChunk.ReadChunks<RIFFChunk>(2);
777 Wave64TagFromLISTINFO(tags.title, codePage, infoChunk, RIFFChunk::idINAM);
778 Wave64TagFromLISTINFO(tags.encoder, codePage, infoChunk, RIFFChunk::idISFT);
779 //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idICOP);
780 Wave64TagFromLISTINFO(tags.artist, codePage, infoChunk, RIFFChunk::idIART);
781 Wave64TagFromLISTINFO(tags.album, codePage, infoChunk, RIFFChunk::idIPRD);
782 Wave64TagFromLISTINFO(tags.comments, codePage, infoChunk, RIFFChunk::idICMT);
783 //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idIENG);
784 //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idISBJ);
785 Wave64TagFromLISTINFO(tags.genre, codePage, infoChunk, RIFFChunk::idIGNR);
786 //Wave64TagFromLISTINFO(void, codePage, infoChunk, RIFFChunk::idICRD);
787 Wave64TagFromLISTINFO(tags.year, codePage, infoChunk, RIFFChunk::idYEAR);
788 Wave64TagFromLISTINFO(tags.trackno, codePage, infoChunk, RIFFChunk::idTRCK);
789 Wave64TagFromLISTINFO(tags.url, codePage, infoChunk, RIFFChunk::idTURL);
790 //Wave64TagFromLISTINFO(tags.bpm, codePage, infoChunk, void);
791 }
792 }
793
794 if(!chunkList.ChunkExists(guidDATA))
795 {
796 return false;
797 }
798 FileReader audioData = chunkList.GetChunk(guidDATA);
799
800 SmpLength length = mpt::saturate_cast<SmpLength>(audioData.GetLength() / (sampleIO.GetEncodedBitsPerSample()/8));
801
802 ModSample &mptSample = Samples[nSample];
803 DestroySampleThreadsafe(nSample);
804 mptSample.Initialize();
805 mptSample.nLength = length;
806 mptSample.nC5Speed = format.sampleRate;
807
808 sampleIO.ReadSample(mptSample, audioData);
809
810 m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags));
811
812 mptSample.Convert(MOD_TYPE_IT, GetType());
813 mptSample.PrecomputeLoops(*this, false);
814
815 return true;
816
817 }
818
819
820
821 #ifndef MODPLUG_NO_FILESAVE
822
823 ///////////////////////////////////////////////////////////////
824 // Save RAW
825
SaveRAWSample(SAMPLEINDEX nSample,std::ostream & f) const826 bool CSoundFile::SaveRAWSample(SAMPLEINDEX nSample, std::ostream &f) const
827 {
828 const ModSample &sample = Samples[nSample];
829 SampleIO(
830 sample.uFlags[CHN_16BIT] ? SampleIO::_16bit : SampleIO::_8bit,
831 sample.uFlags[CHN_STEREO] ? SampleIO::stereoInterleaved : SampleIO::mono,
832 SampleIO::littleEndian,
833 SampleIO::signedPCM)
834 .WriteSample(f, sample);
835
836 return true;
837 }
838
839 #endif // MODPLUG_NO_FILESAVE
840
841 /////////////////////////////////////////////////////////////
842 // GUS Patches
843
844 struct GF1PatchFileHeader
845 {
846 char magic[8]; // "GF1PATCH"
847 char version[4]; // "100", or "110"
848 char id[10]; // "ID#000002"
849 char copyright[60]; // Copyright
850 uint8le numInstr; // Number of instruments in patch
851 uint8le voices; // Number of voices, usually 14
852 uint8le channels; // Number of wav channels that can be played concurently to the patch
853 uint16le numSamples; // Total number of waveforms for all the .PAT
854 uint16le volume; // Master volume
855 uint32le dataSize;
856 char reserved2[36];
857 };
858
859 MPT_BINARY_STRUCT(GF1PatchFileHeader, 129)
860
861
862 struct GF1Instrument
863 {
864 uint16le id; // Instrument id: 0-65535
865 char name[16]; // Name of instrument. Gravis doesn't seem to use it
866 uint32le size; // Number of bytes for the instrument with header. (To skip to next instrument)
867 uint8 layers; // Number of layers in instrument: 1-4
868 char reserved[40];
869 };
870
871 MPT_BINARY_STRUCT(GF1Instrument, 63)
872
873
874 struct GF1SampleHeader
875 {
876 char name[7]; // null terminated string. name of the wave.
877 uint8le fractions; // Start loop point fraction in 4 bits + End loop point fraction in the 4 other bits.
878 uint32le length; // total size of wavesample. limited to 65535 now by the drivers, not the card.
879 uint32le loopstart; // start loop position in the wavesample
880 uint32le loopend; // end loop position in the wavesample
881 uint16le freq; // Rate at which the wavesample has been sampled
882 uint32le low_freq; // check note.h for the correspondance.
883 uint32le high_freq; // check note.h for the correspondance.
884 uint32le root_freq; // check note.h for the correspondance.
885 int16le finetune; // fine tune. -512 to +512, EXCLUDING 0 cause it is a multiplier. 512 is one octave off, and 1 is a neutral value
886 uint8le balance; // Balance: 0-15. 0=full left, 15 = full right
887 uint8le env_rate[6]; // attack rates
888 uint8le env_volume[6]; // attack volumes
889 uint8le tremolo_sweep, tremolo_rate, tremolo_depth;
890 uint8le vibrato_sweep, vibrato_rate, vibrato_depth;
891 uint8le flags;
892 int16le scale_frequency; // Note
893 uint16le scale_factor; // 0...2048 (1024 is normal) or 0...2
894 char reserved[36];
895 };
896
897 MPT_BINARY_STRUCT(GF1SampleHeader, 96)
898
899 // -- GF1 Envelopes --
900 //
901 // It can be represented like this (the envelope is totally bogus, it is
902 // just to show the concept):
903 //
904 // |
905 // | /----` | |
906 // | /------/ `\ | | | | |
907 // | / \ | | | | |
908 // | / \ | | | | |
909 // |/ \ | | | | |
910 // ---------------------------- | | | | | |
911 // <---> attack rate 0 0 1 2 3 4 5 amplitudes
912 // <----> attack rate 1
913 // <> attack rate 2
914 // <--> attack rate 3
915 // <> attack rate 4
916 // <-----> attack rate 5
917 //
918 // -- GF1 Flags --
919 //
920 // bit 0: 8/16 bit
921 // bit 1: Signed/Unsigned
922 // bit 2: off/on looping
923 // bit 3: off/on bidirectionnal looping
924 // bit 4: off/on backward looping
925 // bit 5: off/on sustaining (3rd point in env.)
926 // bit 6: off/on envelopes
927 // bit 7: off/on clamped release (6th point, env)
928
929
930 struct GF1Layer
931 {
932 uint8le previous; // If !=0 the wavesample to use is from the previous layer. The waveheader is still needed
933 uint8le id; // Layer id: 0-3
934 uint32le size; // data size in bytes in the layer, without the header. to skip to next layer for example:
935 uint8le samples; // number of wavesamples
936 char reserved[40];
937 };
938
939 MPT_BINARY_STRUCT(GF1Layer, 47)
940
941
PatchFreqToNote(uint32 nFreq)942 static double PatchFreqToNote(uint32 nFreq)
943 {
944 return std::log(nFreq / 2044.0) * (12.0 * 1.44269504088896340736); // 1.0/std::log(2.0)
945 }
946
947
PatchFreqToNoteInt(uint32 nFreq)948 static int32 PatchFreqToNoteInt(uint32 nFreq)
949 {
950 return mpt::saturate_round<int32>(PatchFreqToNote(nFreq));
951 }
952
953
PatchToSample(CSoundFile * that,SAMPLEINDEX nSample,GF1SampleHeader & sampleHeader,FileReader & file)954 static void PatchToSample(CSoundFile *that, SAMPLEINDEX nSample, GF1SampleHeader &sampleHeader, FileReader &file)
955 {
956 ModSample &sample = that->GetSample(nSample);
957
958 file.ReadStruct(sampleHeader);
959
960 sample.Initialize();
961 if(sampleHeader.flags & 4) sample.uFlags.set(CHN_LOOP);
962 if(sampleHeader.flags & 8) sample.uFlags.set(CHN_PINGPONGLOOP);
963 if(sampleHeader.flags & 16) sample.uFlags.set(CHN_REVERSE);
964 sample.nLength = sampleHeader.length;
965 sample.nLoopStart = sampleHeader.loopstart;
966 sample.nLoopEnd = sampleHeader.loopend;
967 sample.nC5Speed = sampleHeader.freq;
968 sample.nPan = (sampleHeader.balance * 256 + 8) / 15;
969 if(sample.nPan > 256) sample.nPan = 128;
970 else sample.uFlags.set(CHN_PANNING);
971 sample.nVibType = VIB_SINE;
972 sample.nVibSweep = sampleHeader.vibrato_sweep;
973 sample.nVibDepth = sampleHeader.vibrato_depth;
974 sample.nVibRate = sampleHeader.vibrato_rate / 4;
975 if(sampleHeader.scale_factor)
976 {
977 sample.Transpose((84.0 - PatchFreqToNote(sampleHeader.root_freq)) / 12.0);
978 }
979
980 SampleIO sampleIO(
981 SampleIO::_8bit,
982 SampleIO::mono,
983 SampleIO::littleEndian,
984 (sampleHeader.flags & 2) ? SampleIO::unsignedPCM : SampleIO::signedPCM);
985
986 if(sampleHeader.flags & 1)
987 {
988 sampleIO |= SampleIO::_16bit;
989 sample.nLength /= 2;
990 sample.nLoopStart /= 2;
991 sample.nLoopEnd /= 2;
992 }
993 sampleIO.ReadSample(sample, file);
994 sample.Convert(MOD_TYPE_IT, that->GetType());
995 sample.PrecomputeLoops(*that, false);
996
997 that->m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, sampleHeader.name);
998 }
999
1000
ReadPATSample(SAMPLEINDEX nSample,FileReader & file)1001 bool CSoundFile::ReadPATSample(SAMPLEINDEX nSample, FileReader &file)
1002 {
1003 file.Rewind();
1004 GF1PatchFileHeader fileHeader;
1005 GF1Instrument instrHeader; // We only support one instrument
1006 GF1Layer layerHeader;
1007 if(!file.ReadStruct(fileHeader)
1008 || memcmp(fileHeader.magic, "GF1PATCH", 8)
1009 || (memcmp(fileHeader.version, "110\0", 4) && memcmp(fileHeader.version, "100\0", 4))
1010 || memcmp(fileHeader.id, "ID#000002\0", 10)
1011 || !fileHeader.numInstr || !fileHeader.numSamples
1012 || !file.ReadStruct(instrHeader)
1013 //|| !instrHeader.layers // DOO.PAT has 0 layers
1014 || !file.ReadStruct(layerHeader)
1015 || !layerHeader.samples)
1016 {
1017 return false;
1018 }
1019
1020 DestroySampleThreadsafe(nSample);
1021 GF1SampleHeader sampleHeader;
1022 PatchToSample(this, nSample, sampleHeader, file);
1023
1024 if(instrHeader.name[0] > ' ')
1025 {
1026 m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name);
1027 }
1028 return true;
1029 }
1030
1031
1032 // PAT Instrument
ReadPATInstrument(INSTRUMENTINDEX nInstr,FileReader & file)1033 bool CSoundFile::ReadPATInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
1034 {
1035 file.Rewind();
1036 GF1PatchFileHeader fileHeader;
1037 GF1Instrument instrHeader; // We only support one instrument
1038 GF1Layer layerHeader;
1039 if(!file.ReadStruct(fileHeader)
1040 || memcmp(fileHeader.magic, "GF1PATCH", 8)
1041 || (memcmp(fileHeader.version, "110\0", 4) && memcmp(fileHeader.version, "100\0", 4))
1042 || memcmp(fileHeader.id, "ID#000002\0", 10)
1043 || !fileHeader.numInstr || !fileHeader.numSamples
1044 || !file.ReadStruct(instrHeader)
1045 //|| !instrHeader.layers // DOO.PAT has 0 layers
1046 || !file.ReadStruct(layerHeader)
1047 || !layerHeader.samples)
1048 {
1049 return false;
1050 }
1051
1052 ModInstrument *pIns = new (std::nothrow) ModInstrument();
1053 if(pIns == nullptr)
1054 {
1055 return false;
1056 }
1057
1058 DestroyInstrument(nInstr, deleteAssociatedSamples);
1059 if (nInstr > m_nInstruments) m_nInstruments = nInstr;
1060 Instruments[nInstr] = pIns;
1061
1062 pIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name);
1063 pIns->nFadeOut = 2048;
1064 if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
1065 {
1066 pIns->nNNA = NewNoteAction::NoteOff;
1067 pIns->nDNA = DuplicateNoteAction::NoteFade;
1068 }
1069
1070 SAMPLEINDEX nextSample = 0;
1071 int32 nMinSmpNote = 0xFF;
1072 SAMPLEINDEX nMinSmp = 0;
1073 for(uint8 smp = 0; smp < layerHeader.samples; smp++)
1074 {
1075 // Find a free sample
1076 nextSample = GetNextFreeSample(nInstr, nextSample + 1);
1077 if(nextSample == SAMPLEINDEX_INVALID) break;
1078 if(m_nSamples < nextSample) m_nSamples = nextSample;
1079 if(!nMinSmp) nMinSmp = nextSample;
1080 // Load it
1081 GF1SampleHeader sampleHeader;
1082 PatchToSample(this, nextSample, sampleHeader, file);
1083 int32 nMinNote = (sampleHeader.low_freq > 100) ? PatchFreqToNoteInt(sampleHeader.low_freq) : 0;
1084 int32 nMaxNote = (sampleHeader.high_freq > 100) ? PatchFreqToNoteInt(sampleHeader.high_freq) : static_cast<uint8>(NOTE_MAX);
1085 int32 nBaseNote = (sampleHeader.root_freq > 100) ? PatchFreqToNoteInt(sampleHeader.root_freq) : -1;
1086 if(!sampleHeader.scale_factor && layerHeader.samples == 1) { nMinNote = 0; nMaxNote = NOTE_MAX; }
1087 // Fill Note Map
1088 for(int32 k = 0; k < NOTE_MAX; k++)
1089 {
1090 if(k == nBaseNote || (!pIns->Keyboard[k] && k >= nMinNote && k <= nMaxNote))
1091 {
1092 if(!sampleHeader.scale_factor)
1093 pIns->NoteMap[k] = NOTE_MIDDLEC;
1094
1095 pIns->Keyboard[k] = nextSample;
1096 if(k < nMinSmpNote)
1097 {
1098 nMinSmpNote = k;
1099 nMinSmp = nextSample;
1100 }
1101 }
1102 }
1103 }
1104 if(nMinSmp)
1105 {
1106 // Fill note map and missing samples
1107 for(uint8 k = 0; k < NOTE_MAX; k++)
1108 {
1109 if(!pIns->NoteMap[k]) pIns->NoteMap[k] = k + 1;
1110 if(!pIns->Keyboard[k])
1111 {
1112 pIns->Keyboard[k] = nMinSmp;
1113 } else
1114 {
1115 nMinSmp = pIns->Keyboard[k];
1116 }
1117 }
1118 }
1119
1120 pIns->Sanitize(MOD_TYPE_IT);
1121 pIns->Convert(MOD_TYPE_IT, GetType());
1122 return true;
1123 }
1124
1125
1126 /////////////////////////////////////////////////////////////
1127 // S3I Samples
1128
1129
ReadS3ISample(SAMPLEINDEX nSample,FileReader & file)1130 bool CSoundFile::ReadS3ISample(SAMPLEINDEX nSample, FileReader &file)
1131 {
1132 file.Rewind();
1133
1134 S3MSampleHeader sampleHeader;
1135 if(!file.ReadStruct(sampleHeader)
1136 || (sampleHeader.sampleType != S3MSampleHeader::typePCM && sampleHeader.sampleType != S3MSampleHeader::typeAdMel)
1137 || (memcmp(sampleHeader.magic, "SCRS", 4) && memcmp(sampleHeader.magic, "SCRI", 4))
1138 || !file.Seek(sampleHeader.GetSampleOffset()))
1139 {
1140 return false;
1141 }
1142
1143 DestroySampleThreadsafe(nSample);
1144
1145 ModSample &sample = Samples[nSample];
1146 sampleHeader.ConvertToMPT(sample);
1147 m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::nullTerminated, sampleHeader.name);
1148
1149 if(sampleHeader.sampleType < S3MSampleHeader::typeAdMel)
1150 sampleHeader.GetSampleFormat(false).ReadSample(sample, file);
1151 else if(SupportsOPL())
1152 InitOPL();
1153 else
1154 AddToLog(LogInformation, U_("OPL instruments are not supported by this format."));
1155
1156 sample.Convert(MOD_TYPE_S3M, GetType());
1157 sample.PrecomputeLoops(*this, false);
1158 return true;
1159 }
1160
1161 #ifndef MODPLUG_NO_FILESAVE
1162
SaveS3ISample(SAMPLEINDEX smp,std::ostream & f) const1163 bool CSoundFile::SaveS3ISample(SAMPLEINDEX smp, std::ostream &f) const
1164 {
1165 const ModSample &sample = Samples[smp];
1166 S3MSampleHeader sampleHeader;
1167 MemsetZero(sampleHeader);
1168 SmpLength length = sampleHeader.ConvertToS3M(sample);
1169 mpt::String::WriteBuf(mpt::String::nullTerminated, sampleHeader.name) = m_szNames[smp];
1170 mpt::String::WriteBuf(mpt::String::maybeNullTerminated, sampleHeader.reserved2) = mpt::ToCharset(mpt::Charset::UTF8, Version::Current().GetOpenMPTVersionString());
1171 if(length)
1172 sampleHeader.dataPointer[1] = sizeof(S3MSampleHeader) >> 4;
1173 mpt::IO::Write(f, sampleHeader);
1174 if(length)
1175 sampleHeader.GetSampleFormat(false).WriteSample(f, sample, length);
1176
1177 return true;
1178 }
1179
1180 #endif // MODPLUG_NO_FILESAVE
1181
1182
1183 /////////////////////////////////////////////////////////////
1184 // SBI OPL patch files
1185
ReadSBISample(SAMPLEINDEX sample,FileReader & file)1186 bool CSoundFile::ReadSBISample(SAMPLEINDEX sample, FileReader &file)
1187 {
1188 file.Rewind();
1189 if(!file.ReadMagic("SBI\x1A")
1190 || !file.CanRead(32 + sizeof(OPLPatch))
1191 || file.CanRead(64)) // Arbitrary threshold to reject files that are unlikely to be SBI files
1192 return false;
1193
1194 if(!SupportsOPL())
1195 {
1196 AddToLog(LogInformation, U_("OPL instruments are not supported by this format."));
1197 return true;
1198 }
1199
1200 DestroySampleThreadsafe(sample);
1201 InitOPL();
1202
1203 ModSample &mptSmp = Samples[sample];
1204 mptSmp.Initialize(MOD_TYPE_S3M);
1205 file.ReadString<mpt::String::nullTerminated>(m_szNames[sample], 32);
1206 OPLPatch patch;
1207 file.ReadArray(patch);
1208 mptSmp.SetAdlib(true, patch);
1209
1210 mptSmp.Convert(MOD_TYPE_S3M, GetType());
1211 return true;
1212 }
1213
1214
1215
1216 /////////////////////////////////////////////////////////////
1217 // XI Instruments
1218
1219
ReadXIInstrument(INSTRUMENTINDEX nInstr,FileReader & file)1220 bool CSoundFile::ReadXIInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
1221 {
1222 file.Rewind();
1223
1224 XIInstrumentHeader fileHeader;
1225 if(!file.ReadStruct(fileHeader)
1226 || memcmp(fileHeader.signature, "Extended Instrument: ", 21)
1227 || fileHeader.version != XIInstrumentHeader::fileVersion
1228 || fileHeader.eof != 0x1A)
1229 {
1230 return false;
1231 }
1232
1233 ModInstrument *pIns = new (std::nothrow) ModInstrument();
1234 if(pIns == nullptr)
1235 {
1236 return false;
1237 }
1238
1239 DestroyInstrument(nInstr, deleteAssociatedSamples);
1240 if(nInstr > m_nInstruments)
1241 {
1242 m_nInstruments = nInstr;
1243 }
1244 Instruments[nInstr] = pIns;
1245
1246 fileHeader.ConvertToMPT(*pIns);
1247
1248 // Translate sample map and find available sample slots
1249 std::vector<SAMPLEINDEX> sampleMap(fileHeader.numSamples);
1250 SAMPLEINDEX maxSmp = 0;
1251
1252 for(size_t i = 0 + 12; i < 96 + 12; i++)
1253 {
1254 if(pIns->Keyboard[i] >= fileHeader.numSamples)
1255 {
1256 continue;
1257 }
1258
1259 if(sampleMap[pIns->Keyboard[i]] == 0)
1260 {
1261 // Find slot for this sample
1262 maxSmp = GetNextFreeSample(nInstr, maxSmp + 1);
1263 if(maxSmp != SAMPLEINDEX_INVALID)
1264 {
1265 sampleMap[pIns->Keyboard[i]] = maxSmp;
1266 }
1267 }
1268 pIns->Keyboard[i] = sampleMap[pIns->Keyboard[i]];
1269 }
1270
1271 if(m_nSamples < maxSmp)
1272 {
1273 m_nSamples = maxSmp;
1274 }
1275
1276 std::vector<SampleIO> sampleFlags(fileHeader.numSamples);
1277
1278 // Read sample headers
1279 for(SAMPLEINDEX i = 0; i < fileHeader.numSamples; i++)
1280 {
1281 XMSample sampleHeader;
1282 if(!file.ReadStruct(sampleHeader)
1283 || !sampleMap[i])
1284 {
1285 continue;
1286 }
1287
1288 ModSample &mptSample = Samples[sampleMap[i]];
1289 sampleHeader.ConvertToMPT(mptSample);
1290 fileHeader.instrument.ApplyAutoVibratoToMPT(mptSample);
1291 mptSample.Convert(MOD_TYPE_XM, GetType());
1292 if(GetType() != MOD_TYPE_XM && fileHeader.numSamples == 1)
1293 {
1294 // No need to pan that single sample, thank you...
1295 mptSample.uFlags &= ~CHN_PANNING;
1296 }
1297
1298 mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
1299 m_szNames[sampleMap[i]] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
1300
1301 sampleFlags[i] = sampleHeader.GetSampleFormat();
1302 }
1303
1304 // Read sample data
1305 for(SAMPLEINDEX i = 0; i < fileHeader.numSamples; i++)
1306 {
1307 if(sampleMap[i])
1308 {
1309 sampleFlags[i].ReadSample(Samples[sampleMap[i]], file);
1310 Samples[sampleMap[i]].PrecomputeLoops(*this, false);
1311 }
1312 }
1313
1314 // Read MPT crap
1315 ReadExtendedInstrumentProperties(pIns, file);
1316 pIns->Convert(MOD_TYPE_XM, GetType());
1317 pIns->Sanitize(GetType());
1318 return true;
1319 }
1320
1321
1322 #ifndef MODPLUG_NO_FILESAVE
1323
SaveXIInstrument(INSTRUMENTINDEX nInstr,std::ostream & f) const1324 bool CSoundFile::SaveXIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f) const
1325 {
1326 ModInstrument *pIns = Instruments[nInstr];
1327 if(pIns == nullptr)
1328 {
1329 return false;
1330 }
1331
1332 // Create file header
1333 XIInstrumentHeader header;
1334 header.ConvertToXM(*pIns, false);
1335
1336 const std::vector<SAMPLEINDEX> samples = header.instrument.GetSampleList(*pIns, false);
1337 if(samples.size() > 0 && samples[0] <= GetNumSamples())
1338 {
1339 // Copy over auto-vibrato settings of first sample
1340 header.instrument.ApplyAutoVibratoToXM(Samples[samples[0]], GetType());
1341 }
1342
1343 mpt::IO::Write(f, header);
1344
1345 std::vector<SampleIO> sampleFlags(samples.size());
1346
1347 // XI Sample Headers
1348 for(SAMPLEINDEX i = 0; i < samples.size(); i++)
1349 {
1350 XMSample xmSample;
1351 if(samples[i] <= GetNumSamples())
1352 {
1353 xmSample.ConvertToXM(Samples[samples[i]], GetType(), false);
1354 } else
1355 {
1356 MemsetZero(xmSample);
1357 }
1358 sampleFlags[i] = xmSample.GetSampleFormat();
1359
1360 mpt::String::WriteBuf(mpt::String::spacePadded, xmSample.name) = m_szNames[samples[i]];
1361
1362 mpt::IO::Write(f, xmSample);
1363 }
1364
1365 // XI Sample Data
1366 for(SAMPLEINDEX i = 0; i < samples.size(); i++)
1367 {
1368 if(samples[i] <= GetNumSamples())
1369 {
1370 sampleFlags[i].WriteSample(f, Samples[samples[i]]);
1371 }
1372 }
1373
1374 // Write 'MPTX' extension tag
1375 mpt::IO::WriteText(f, "XTPM");
1376 WriteInstrumentHeaderStructOrField(pIns, f); // Write full extended header.
1377
1378 return true;
1379 }
1380
1381 #endif // MODPLUG_NO_FILESAVE
1382
1383
1384 // Read first sample from XI file into a sample slot
ReadXISample(SAMPLEINDEX nSample,FileReader & file)1385 bool CSoundFile::ReadXISample(SAMPLEINDEX nSample, FileReader &file)
1386 {
1387 file.Rewind();
1388
1389 XIInstrumentHeader fileHeader;
1390 if(!file.ReadStruct(fileHeader)
1391 || !file.CanRead(sizeof(XMSample))
1392 || memcmp(fileHeader.signature, "Extended Instrument: ", 21)
1393 || fileHeader.version != XIInstrumentHeader::fileVersion
1394 || fileHeader.eof != 0x1A
1395 || fileHeader.numSamples == 0)
1396 {
1397 return false;
1398 }
1399
1400 if(m_nSamples < nSample)
1401 {
1402 m_nSamples = nSample;
1403 }
1404
1405 uint16 numSamples = fileHeader.numSamples;
1406 FileReader::off_t samplePos = sizeof(XIInstrumentHeader) + numSamples * sizeof(XMSample);
1407 // Preferrably read the middle-C sample
1408 auto sample = fileHeader.instrument.sampleMap[48];
1409 if(sample >= fileHeader.numSamples)
1410 sample = 0;
1411 XMSample sampleHeader;
1412 while(sample--)
1413 {
1414 file.ReadStruct(sampleHeader);
1415 samplePos += sampleHeader.length;
1416 }
1417 file.ReadStruct(sampleHeader);
1418 // Gotta skip 'em all!
1419 file.Seek(samplePos);
1420
1421 DestroySampleThreadsafe(nSample);
1422
1423 ModSample &mptSample = Samples[nSample];
1424 sampleHeader.ConvertToMPT(mptSample);
1425 if(GetType() != MOD_TYPE_XM)
1426 {
1427 // No need to pan that single sample, thank you...
1428 mptSample.uFlags.reset(CHN_PANNING);
1429 }
1430 fileHeader.instrument.ApplyAutoVibratoToMPT(mptSample);
1431 mptSample.Convert(MOD_TYPE_XM, GetType());
1432
1433 mptSample.filename = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
1434 m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePadded, sampleHeader.name);
1435
1436 // Read sample data
1437 sampleHeader.GetSampleFormat().ReadSample(Samples[nSample], file);
1438 Samples[nSample].PrecomputeLoops(*this, false);
1439
1440 return true;
1441 }
1442
1443
1444 ///////////////
1445 // Apple CAF //
1446
1447
1448 struct CAFFileHeader
1449 {
1450 uint32be mFileType;
1451 uint16be mFileVersion;
1452 uint16be mFileFlags;
1453 };
1454
1455 MPT_BINARY_STRUCT(CAFFileHeader, 8)
1456
1457
1458 struct CAFChunkHeader
1459 {
1460 uint32be mChunkType;
1461 int64be mChunkSize;
1462 };
1463
1464 MPT_BINARY_STRUCT(CAFChunkHeader, 12)
1465
1466
1467 struct CAFChunk
1468 {
1469 enum ChunkIdentifiers
1470 {
1471 iddesc = MagicBE("desc"),
1472 iddata = MagicBE("data"),
1473 idstrg = MagicBE("strg"),
1474 idinfo = MagicBE("info")
1475 };
1476
1477 CAFChunkHeader header;
1478
GetLengthCAFChunk1479 FileReader::off_t GetLength() const
1480 {
1481 int64 length = header.mChunkSize;
1482 if(length == -1)
1483 {
1484 length = std::numeric_limits<int64>::max(); // spec
1485 }
1486 if(length < 0)
1487 {
1488 length = std::numeric_limits<int64>::max(); // heuristic
1489 }
1490 return mpt::saturate_cast<FileReader::off_t>(length);
1491 }
1492
GetIDCAFChunk1493 ChunkIdentifiers GetID() const
1494 {
1495 return static_cast<ChunkIdentifiers>(header.mChunkType.get());
1496 }
1497 };
1498
1499 MPT_BINARY_STRUCT(CAFChunk, 12)
1500
1501
1502 enum {
1503 CAFkAudioFormatLinearPCM = MagicBE("lpcm"),
1504 CAFkAudioFormatAppleIMA4 = MagicBE("ima4"),
1505 CAFkAudioFormatMPEG4AAC = MagicBE("aac "),
1506 CAFkAudioFormatMACE3 = MagicBE("MAC3"),
1507 CAFkAudioFormatMACE6 = MagicBE("MAC6"),
1508 CAFkAudioFormatULaw = MagicBE("ulaw"),
1509 CAFkAudioFormatALaw = MagicBE("alaw"),
1510 CAFkAudioFormatMPEGLayer1 = MagicBE(".mp1"),
1511 CAFkAudioFormatMPEGLayer2 = MagicBE(".mp2"),
1512 CAFkAudioFormatMPEGLayer3 = MagicBE(".mp3"),
1513 CAFkAudioFormatAppleLossless = MagicBE("alac")
1514 };
1515
1516
1517 enum {
1518 CAFkCAFLinearPCMFormatFlagIsFloat = (1L << 0),
1519 CAFkCAFLinearPCMFormatFlagIsLittleEndian = (1L << 1)
1520 };
1521
1522
1523 struct CAFAudioFormat
1524 {
1525 float64be mSampleRate;
1526 uint32be mFormatID;
1527 uint32be mFormatFlags;
1528 uint32be mBytesPerPacket;
1529 uint32be mFramesPerPacket;
1530 uint32be mChannelsPerFrame;
1531 uint32be mBitsPerChannel;
1532 };
1533
1534 MPT_BINARY_STRUCT(CAFAudioFormat, 32)
1535
1536
CAFSetTagFromInfoKey(mpt::ustring & dst,const std::map<std::string,std::string> & infoMap,const std::string & key)1537 static void CAFSetTagFromInfoKey(mpt::ustring & dst, const std::map<std::string,std::string> & infoMap, const std::string & key)
1538 {
1539 auto item = infoMap.find(key);
1540 if(item == infoMap.end())
1541 {
1542 return;
1543 }
1544 if(item->second.empty())
1545 {
1546 return;
1547 }
1548 dst = mpt::ToUnicode(mpt::Charset::UTF8, item->second);
1549 }
1550
1551
ReadCAFSample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize)1552 bool CSoundFile::ReadCAFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize)
1553 {
1554 file.Rewind();
1555
1556 CAFFileHeader fileHeader;
1557 if(!file.ReadStruct(fileHeader))
1558 {
1559 return false;
1560 }
1561 if(fileHeader.mFileType != MagicBE("caff"))
1562 {
1563 return false;
1564 }
1565 if(fileHeader.mFileVersion != 1)
1566 {
1567 return false;
1568 }
1569
1570 auto chunkList = file.ReadChunks<CAFChunk>(0);
1571
1572 CAFAudioFormat audioFormat;
1573 if(!chunkList.GetChunk(CAFChunk::iddesc).ReadStruct(audioFormat))
1574 {
1575 return false;
1576 }
1577 if(audioFormat.mSampleRate <= 0.0)
1578 {
1579 return false;
1580 }
1581 if(audioFormat.mChannelsPerFrame == 0)
1582 {
1583 return false;
1584 }
1585 if(audioFormat.mChannelsPerFrame > 2)
1586 {
1587 return false;
1588 }
1589
1590 if(!mpt::in_range<uint32>(mpt::saturate_round<int64>(audioFormat.mSampleRate)))
1591 {
1592 return false;
1593 }
1594 uint32 sampleRate = static_cast<uint32>(mpt::saturate_round<int64>(audioFormat.mSampleRate));
1595 if(sampleRate <= 0)
1596 {
1597 return false;
1598 }
1599
1600 SampleIO sampleIO;
1601 if(audioFormat.mFormatID == CAFkAudioFormatLinearPCM)
1602 {
1603 if(audioFormat.mFramesPerPacket != 1)
1604 {
1605 return false;
1606 }
1607 if(audioFormat.mBytesPerPacket == 0)
1608 {
1609 return false;
1610 }
1611 if(audioFormat.mBitsPerChannel == 0)
1612 {
1613 return false;
1614 }
1615 if(audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsFloat)
1616 {
1617 if(audioFormat.mBitsPerChannel != 32 && audioFormat.mBitsPerChannel != 64)
1618 {
1619 return false;
1620 }
1621 if(audioFormat.mBytesPerPacket != audioFormat.mChannelsPerFrame * audioFormat.mBitsPerChannel/8)
1622 {
1623 return false;
1624 }
1625 }
1626 if(audioFormat.mBytesPerPacket % audioFormat.mChannelsPerFrame != 0)
1627 {
1628 return false;
1629 }
1630 if(audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 1
1631 && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 2
1632 && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 3
1633 && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 4
1634 && audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame != 8
1635 )
1636 {
1637 return false;
1638 }
1639 SampleIO::Channels channels = (audioFormat.mChannelsPerFrame == 2) ? SampleIO::stereoInterleaved : SampleIO::mono;
1640 SampleIO::Endianness endianness = (audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsLittleEndian) ? SampleIO::littleEndian : SampleIO::bigEndian;
1641 SampleIO::Encoding encoding = (audioFormat.mFormatFlags & CAFkCAFLinearPCMFormatFlagIsFloat) ? SampleIO::floatPCM : SampleIO::signedPCM;
1642 SampleIO::Bitdepth bitdepth = static_cast<SampleIO::Bitdepth>((audioFormat.mBytesPerPacket / audioFormat.mChannelsPerFrame) * 8);
1643 sampleIO = SampleIO(bitdepth, channels, endianness, encoding);
1644 } else
1645 {
1646 return false;
1647 }
1648
1649 if(mayNormalize)
1650 {
1651 sampleIO.MayNormalize();
1652 }
1653
1654 /*
1655 std::map<uint32, std::string> stringMap; // UTF-8
1656 if(chunkList.ChunkExists(CAFChunk::idstrg))
1657 {
1658 FileReader stringsChunk = chunkList.GetChunk(CAFChunk::idstrg);
1659 uint32 numEntries = stringsChunk.ReadUint32BE();
1660 if(stringsChunk.Skip(12 * numEntries))
1661 {
1662 FileReader stringData = stringsChunk.ReadChunk(stringsChunk.BytesLeft());
1663 stringsChunk.Seek(4);
1664 for(uint32 entry = 0; entry < numEntries && stringsChunk.CanRead(12); entry++)
1665 {
1666 uint32 stringID = stringsChunk.ReadUint32BE();
1667 int64 offset = stringsChunk.ReadIntBE<int64>();
1668 if(offset >= 0 && mpt::in_range<FileReader::off_t>(offset))
1669 {
1670 stringData.Seek(mpt::saturate_cast<FileReader::off_t>(offset));
1671 std::string str;
1672 if(stringData.ReadNullString(str))
1673 {
1674 stringMap[stringID] = str;
1675 }
1676 }
1677 }
1678 }
1679 }
1680 */
1681
1682 std::map<std::string, std::string> infoMap; // UTF-8
1683 if(chunkList.ChunkExists(CAFChunk::idinfo))
1684 {
1685 FileReader informationChunk = chunkList.GetChunk(CAFChunk::idinfo);
1686 uint32 numEntries = informationChunk.ReadUint32BE();
1687 for(uint32 entry = 0; entry < numEntries && informationChunk.CanRead(2); entry++)
1688 {
1689 std::string key;
1690 std::string value;
1691 if(!informationChunk.ReadNullString(key))
1692 {
1693 break;
1694 }
1695 if(!informationChunk.ReadNullString(value))
1696 {
1697 break;
1698 }
1699 if(!key.empty() && !value.empty())
1700 {
1701 infoMap[key] = value;
1702 }
1703 }
1704 }
1705 FileTags tags;
1706 CAFSetTagFromInfoKey(tags.bpm, infoMap, "tempo");
1707 //CAFSetTagFromInfoKey(void, infoMap, "key signature");
1708 //CAFSetTagFromInfoKey(void, infoMap, "time signature");
1709 CAFSetTagFromInfoKey(tags.artist, infoMap, "artist");
1710 CAFSetTagFromInfoKey(tags.album, infoMap, "album");
1711 CAFSetTagFromInfoKey(tags.trackno, infoMap, "track number");
1712 CAFSetTagFromInfoKey(tags.year, infoMap, "year");
1713 //CAFSetTagFromInfoKey(void, infoMap, "composer");
1714 //CAFSetTagFromInfoKey(void, infoMap, "lyricist");
1715 CAFSetTagFromInfoKey(tags.genre, infoMap, "genre");
1716 CAFSetTagFromInfoKey(tags.title, infoMap, "title");
1717 //CAFSetTagFromInfoKey(void, infoMap, "recorded date");
1718 CAFSetTagFromInfoKey(tags.comments, infoMap, "comments");
1719 //CAFSetTagFromInfoKey(void, infoMap, "copyright");
1720 //CAFSetTagFromInfoKey(void, infoMap, "source encoder");
1721 CAFSetTagFromInfoKey(tags.encoder, infoMap, "encoding application");
1722 //CAFSetTagFromInfoKey(void, infoMap, "nominal bit rate");
1723 //CAFSetTagFromInfoKey(void, infoMap, "channel layout");
1724 //CAFSetTagFromInfoKey(tags.url, infoMap, void);
1725
1726 if(!chunkList.ChunkExists(CAFChunk::iddata))
1727 {
1728 return false;
1729 }
1730 FileReader dataChunk = chunkList.GetChunk(CAFChunk::iddata);
1731 dataChunk.Skip(4); // edit count
1732 FileReader audioData = dataChunk.ReadChunk(dataChunk.BytesLeft());
1733
1734 SmpLength length = mpt::saturate_cast<SmpLength>((audioData.GetLength() / audioFormat.mBytesPerPacket) * audioFormat.mFramesPerPacket);
1735
1736 ModSample &mptSample = Samples[nSample];
1737 DestroySampleThreadsafe(nSample);
1738 mptSample.Initialize();
1739 mptSample.nLength = length;
1740 mptSample.nC5Speed = sampleRate;
1741
1742 sampleIO.ReadSample(mptSample, audioData);
1743
1744 m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags));
1745
1746 mptSample.Convert(MOD_TYPE_IT, GetType());
1747 mptSample.PrecomputeLoops(*this, false);
1748
1749 return true;
1750
1751 }
1752
1753
1754
1755 /////////////////////////////////////////////////////////////////////////////////////////
1756 // AIFF File I/O
1757
1758 // AIFF header
1759 struct AIFFHeader
1760 {
1761 char magic[4]; // FORM
1762 uint32be length; // Size of the file, not including magic and length
1763 char type[4]; // AIFF or AIFC
1764 };
1765
1766 MPT_BINARY_STRUCT(AIFFHeader, 12)
1767
1768
1769 // General IFF Chunk header
1770 struct AIFFChunk
1771 {
1772 // 32-Bit chunk identifiers
1773 enum ChunkIdentifiers
1774 {
1775 idCOMM = MagicBE("COMM"),
1776 idSSND = MagicBE("SSND"),
1777 idINST = MagicBE("INST"),
1778 idMARK = MagicBE("MARK"),
1779 idNAME = MagicBE("NAME"),
1780 };
1781
1782 uint32be id; // See ChunkIdentifiers
1783 uint32be length; // Chunk size without header
1784
GetLengthAIFFChunk1785 size_t GetLength() const
1786 {
1787 return length;
1788 }
1789
GetIDAIFFChunk1790 ChunkIdentifiers GetID() const
1791 {
1792 return static_cast<ChunkIdentifiers>(id.get());
1793 }
1794 };
1795
1796 MPT_BINARY_STRUCT(AIFFChunk, 8)
1797
1798
1799 // "Common" chunk (in AIFC, a compression ID and compression name follows this header, but apart from that it's identical)
1800 struct AIFFCommonChunk
1801 {
1802 uint16be numChannels;
1803 uint32be numSampleFrames;
1804 uint16be sampleSize;
1805 uint8be sampleRate[10]; // Sample rate in 80-Bit floating point
1806
1807 // Convert sample rate to integer
GetSampleRateAIFFCommonChunk1808 uint32 GetSampleRate() const
1809 {
1810 uint32 mantissa = (sampleRate[2] << 24) | (sampleRate[3] << 16) | (sampleRate[4] << 8) | (sampleRate[5] << 0);
1811 uint32 last = 0;
1812 uint8 exp = 30 - sampleRate[1];
1813
1814 while(exp--)
1815 {
1816 last = mantissa;
1817 mantissa >>= 1;
1818 }
1819 if(last & 1) mantissa++;
1820 return mantissa;
1821 }
1822 };
1823
1824 MPT_BINARY_STRUCT(AIFFCommonChunk, 18)
1825
1826
1827 // Sound chunk
1828 struct AIFFSoundChunk
1829 {
1830 uint32be offset;
1831 uint32be blockSize;
1832 };
1833
1834 MPT_BINARY_STRUCT(AIFFSoundChunk, 8)
1835
1836
1837 // Marker
1838 struct AIFFMarker
1839 {
1840 uint16be id;
1841 uint32be position; // Position in sample
1842 uint8be nameLength; // Not counting eventually existing padding byte in name string
1843 };
1844
1845 MPT_BINARY_STRUCT(AIFFMarker, 7)
1846
1847
1848 // Instrument loop
1849 struct AIFFInstrumentLoop
1850 {
1851 enum PlayModes
1852 {
1853 noLoop = 0,
1854 loopNormal = 1,
1855 loopBidi = 2,
1856 };
1857
1858 uint16be playMode;
1859 uint16be beginLoop; // Marker index
1860 uint16be endLoop; // Marker index
1861 };
1862
1863 MPT_BINARY_STRUCT(AIFFInstrumentLoop, 6)
1864
1865
1866 struct AIFFInstrumentChunk
1867 {
1868 uint8be baseNote;
1869 uint8be detune;
1870 uint8be lowNote;
1871 uint8be highNote;
1872 uint8be lowVelocity;
1873 uint8be highVelocity;
1874 uint16be gain;
1875 AIFFInstrumentLoop sustainLoop;
1876 AIFFInstrumentLoop releaseLoop;
1877 };
1878
1879 MPT_BINARY_STRUCT(AIFFInstrumentChunk, 20)
1880
1881
ReadAIFFSample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize)1882 bool CSoundFile::ReadAIFFSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize)
1883 {
1884 file.Rewind();
1885
1886 // Verify header
1887 AIFFHeader fileHeader;
1888 if(!file.ReadStruct(fileHeader)
1889 || memcmp(fileHeader.magic, "FORM", 4)
1890 || (memcmp(fileHeader.type, "AIFF", 4) && memcmp(fileHeader.type, "AIFC", 4)))
1891 {
1892 return false;
1893 }
1894
1895 auto chunks = file.ReadChunks<AIFFChunk>(2);
1896
1897 // Read COMM chunk
1898 FileReader commChunk(chunks.GetChunk(AIFFChunk::idCOMM));
1899 AIFFCommonChunk sampleInfo;
1900 if(!commChunk.ReadStruct(sampleInfo))
1901 {
1902 return false;
1903 }
1904
1905 // Is this a proper sample?
1906 if(sampleInfo.numSampleFrames == 0
1907 || sampleInfo.numChannels < 1 || sampleInfo.numChannels > 2
1908 || sampleInfo.sampleSize < 1 || sampleInfo.sampleSize > 64)
1909 {
1910 return false;
1911 }
1912
1913 // Read compression type in AIFF-C files.
1914 uint8 compression[4] = { 'N', 'O', 'N', 'E' };
1915 SampleIO::Endianness endian = SampleIO::bigEndian;
1916 if(!memcmp(fileHeader.type, "AIFC", 4))
1917 {
1918 if(!commChunk.ReadArray(compression))
1919 {
1920 return false;
1921 }
1922 if(!memcmp(compression, "twos", 4))
1923 {
1924 endian = SampleIO::littleEndian;
1925 }
1926 }
1927
1928 // Read SSND chunk
1929 FileReader soundChunk(chunks.GetChunk(AIFFChunk::idSSND));
1930 AIFFSoundChunk sampleHeader;
1931 if(!soundChunk.ReadStruct(sampleHeader))
1932 {
1933 return false;
1934 }
1935
1936 SampleIO::Bitdepth bitDepth;
1937 switch((sampleInfo.sampleSize - 1) / 8)
1938 {
1939 default:
1940 case 0: bitDepth = SampleIO::_8bit; break;
1941 case 1: bitDepth = SampleIO::_16bit; break;
1942 case 2: bitDepth = SampleIO::_24bit; break;
1943 case 3: bitDepth = SampleIO::_32bit; break;
1944 case 7: bitDepth = SampleIO::_64bit; break;
1945 }
1946
1947 SampleIO sampleIO(bitDepth,
1948 (sampleInfo.numChannels == 2) ? SampleIO::stereoInterleaved : SampleIO::mono,
1949 endian,
1950 SampleIO::signedPCM);
1951
1952 if(!memcmp(compression, "fl32", 4) || !memcmp(compression, "FL32", 4) || !memcmp(compression, "fl64", 4) || !memcmp(compression, "FL64", 4))
1953 {
1954 sampleIO |= SampleIO::floatPCM;
1955 } else if(!memcmp(compression, "alaw", 4) || !memcmp(compression, "ALAW", 4))
1956 {
1957 sampleIO |= SampleIO::aLaw;
1958 sampleIO |= SampleIO::_16bit;
1959 } else if(!memcmp(compression, "ulaw", 4) || !memcmp(compression, "ULAW", 4))
1960 {
1961 sampleIO |= SampleIO::uLaw;
1962 sampleIO |= SampleIO::_16bit;
1963 } else if(!memcmp(compression, "raw ", 4))
1964 {
1965 sampleIO |= SampleIO::unsignedPCM;
1966 }
1967
1968 if(mayNormalize)
1969 {
1970 sampleIO.MayNormalize();
1971 }
1972
1973 if(soundChunk.CanRead(sampleHeader.offset))
1974 {
1975 soundChunk.Skip(sampleHeader.offset);
1976 }
1977
1978 ModSample &mptSample = Samples[nSample];
1979 DestroySampleThreadsafe(nSample);
1980 mptSample.Initialize();
1981 mptSample.nLength = sampleInfo.numSampleFrames;
1982 mptSample.nC5Speed = sampleInfo.GetSampleRate();
1983
1984 sampleIO.ReadSample(mptSample, soundChunk);
1985
1986 // Read MARK and INST chunk to extract sample loops
1987 FileReader markerChunk(chunks.GetChunk(AIFFChunk::idMARK));
1988 AIFFInstrumentChunk instrHeader;
1989 if(markerChunk.IsValid() && chunks.GetChunk(AIFFChunk::idINST).ReadStruct(instrHeader))
1990 {
1991 uint16 numMarkers = markerChunk.ReadUint16BE();
1992
1993 std::vector<AIFFMarker> markers;
1994 markers.reserve(numMarkers);
1995 for(size_t i = 0; i < numMarkers; i++)
1996 {
1997 AIFFMarker marker;
1998 if(!markerChunk.ReadStruct(marker))
1999 {
2000 break;
2001 }
2002 markers.push_back(marker);
2003 markerChunk.Skip(marker.nameLength + ((marker.nameLength % 2u) == 0 ? 1 : 0));
2004 }
2005
2006 if(instrHeader.sustainLoop.playMode != AIFFInstrumentLoop::noLoop)
2007 {
2008 mptSample.uFlags.set(CHN_SUSTAINLOOP);
2009 mptSample.uFlags.set(CHN_PINGPONGSUSTAIN, instrHeader.sustainLoop.playMode == AIFFInstrumentLoop::loopBidi);
2010 }
2011
2012 if(instrHeader.releaseLoop.playMode != AIFFInstrumentLoop::noLoop)
2013 {
2014 mptSample.uFlags.set(CHN_LOOP);
2015 mptSample.uFlags.set(CHN_PINGPONGLOOP, instrHeader.releaseLoop.playMode == AIFFInstrumentLoop::loopBidi);
2016 }
2017
2018 // Read markers
2019 for(const auto &m : markers)
2020 {
2021 if(m.id == instrHeader.sustainLoop.beginLoop)
2022 mptSample.nSustainStart = m.position;
2023 if(m.id == instrHeader.sustainLoop.endLoop)
2024 mptSample.nSustainEnd = m.position;
2025 if(m.id == instrHeader.releaseLoop.beginLoop)
2026 mptSample.nLoopStart = m.position;
2027 if(m.id == instrHeader.releaseLoop.endLoop)
2028 mptSample.nLoopEnd = m.position;
2029 }
2030 mptSample.SanitizeLoops();
2031 }
2032
2033 // Extract sample name
2034 FileReader nameChunk(chunks.GetChunk(AIFFChunk::idNAME));
2035 if(nameChunk.IsValid())
2036 {
2037 nameChunk.ReadString<mpt::String::spacePadded>(m_szNames[nSample], nameChunk.GetLength());
2038 } else
2039 {
2040 m_szNames[nSample] = "";
2041 }
2042
2043 mptSample.Convert(MOD_TYPE_IT, GetType());
2044 mptSample.PrecomputeLoops(*this, false);
2045 return true;
2046 }
2047
2048
AUIsAnnotationLineWithField(const std::string & line)2049 static bool AUIsAnnotationLineWithField(const std::string &line)
2050 {
2051 std::size_t pos = line.find('=');
2052 if(pos == std::string::npos)
2053 {
2054 return false;
2055 }
2056 if(pos == 0)
2057 {
2058 return false;
2059 }
2060 const auto field = std::string_view(line).substr(0, pos);
2061 // Scan for invalid chars
2062 for(auto c : field)
2063 {
2064 if(!mpt::is_in_range(c, 'a', 'z') && !mpt::is_in_range(c, 'A', 'Z') && !mpt::is_in_range(c, '0', '9') && c != '-' && c != '_')
2065 {
2066 return false;
2067 }
2068 }
2069 return true;
2070 }
2071
AUTrimFieldFromAnnotationLine(const std::string & line)2072 static std::string AUTrimFieldFromAnnotationLine(const std::string &line)
2073 {
2074 if(!AUIsAnnotationLineWithField(line))
2075 {
2076 return line;
2077 }
2078 std::size_t pos = line.find('=');
2079 return line.substr(pos + 1);
2080 }
2081
AUGetAnnotationFieldFromLine(const std::string & line)2082 static std::string AUGetAnnotationFieldFromLine(const std::string &line)
2083 {
2084 if(!AUIsAnnotationLineWithField(line))
2085 {
2086 return std::string();
2087 }
2088 std::size_t pos = line.find('=');
2089 return line.substr(0, pos);
2090 }
2091
ReadAUSample(SAMPLEINDEX nSample,FileReader & file,bool mayNormalize)2092 bool CSoundFile::ReadAUSample(SAMPLEINDEX nSample, FileReader &file, bool mayNormalize)
2093 {
2094 file.Rewind();
2095
2096 // Verify header
2097 const auto magic = file.ReadArray<char, 4>();
2098 const bool bigEndian = !std::memcmp(magic.data(), ".snd", 4);
2099 const bool littleEndian = !std::memcmp(magic.data(), "dns.", 4);
2100 if(!bigEndian && !littleEndian)
2101 return false;
2102
2103 auto readUint32 = std::bind(bigEndian ? &FileReader::ReadUint32BE : &FileReader::ReadUint32LE, file);
2104
2105 uint32 dataOffset = readUint32(); // must be divisible by 8 according to spec, however, there are files that ignore this requirement
2106 uint32 dataSize = readUint32();
2107 uint32 encoding = readUint32();
2108 uint32 sampleRate = readUint32();
2109 uint32 channels = readUint32();
2110
2111 // According to spec, a minimum 8 byte annotation field after the header fields is required,
2112 // however, there are files in the wild that violate this requirement.
2113 // Thus, check for 24 instead of 32 here.
2114 if(dataOffset < 24) // data offset points inside header
2115 {
2116 return false;
2117 }
2118
2119 if(channels < 1 || channels > 2)
2120 return false;
2121
2122 SampleIO sampleIO(SampleIO::_8bit, channels == 1 ? SampleIO::mono : SampleIO::stereoInterleaved, bigEndian ? SampleIO::bigEndian : SampleIO::littleEndian, SampleIO::signedPCM);
2123 switch(encoding)
2124 {
2125 case 1: sampleIO |= SampleIO::_16bit; // u-law
2126 sampleIO |= SampleIO::uLaw; break;
2127 case 2: break; // 8-bit linear PCM
2128 case 3: sampleIO |= SampleIO::_16bit; break; // 16-bit linear PCM
2129 case 4: sampleIO |= SampleIO::_24bit; break; // 24-bit linear PCM
2130 case 5: sampleIO |= SampleIO::_32bit; break; // 32-bit linear PCM
2131 case 6: sampleIO |= SampleIO::_32bit; // 32-bit IEEE floating point
2132 sampleIO |= SampleIO::floatPCM;
2133 break;
2134 case 7: sampleIO |= SampleIO::_64bit; // 64-bit IEEE floating point
2135 sampleIO |= SampleIO::floatPCM;
2136 break;
2137 case 27: sampleIO |= SampleIO::_16bit; // a-law
2138 sampleIO |= SampleIO::aLaw; break;
2139 default: return false;
2140 }
2141
2142 if(!file.LengthIsAtLeast(dataOffset))
2143 {
2144 return false;
2145 }
2146
2147 FileTags tags;
2148
2149 // This reads annotation metadata as written by OpenMPT, sox, ffmpeg.
2150 // Additionally, we fall back to just reading the whole field as a single comment.
2151 // We only read up to the first \0 byte.
2152 file.Seek(24);
2153 std::string annotation;
2154 file.ReadString<mpt::String::maybeNullTerminated>(annotation, dataOffset - 24);
2155 annotation = mpt::replace(annotation, std::string("\r\n"), std::string("\n"));
2156 annotation = mpt::replace(annotation, std::string("\r"), std::string("\n"));
2157 mpt::Charset charset = mpt::IsUTF8(annotation) ? mpt::Charset::UTF8 : mpt::Charset::ISO8859_1;
2158 const auto lines = mpt::String::Split<std::string>(annotation, "\n");
2159 bool hasFields = false;
2160 for(const auto &line : lines)
2161 {
2162 if(AUIsAnnotationLineWithField(line))
2163 {
2164 hasFields = true;
2165 break;
2166 }
2167 }
2168 if(hasFields)
2169 {
2170 std::map<std::string, std::vector<std::string>> linesPerField;
2171 std::string lastField = "comment";
2172 for(const auto &line : lines)
2173 {
2174 if(AUIsAnnotationLineWithField(line))
2175 {
2176 lastField = mpt::ToLowerCaseAscii(mpt::trim(AUGetAnnotationFieldFromLine(line)));
2177 }
2178 linesPerField[lastField].push_back(AUTrimFieldFromAnnotationLine(line));
2179 }
2180 tags.title = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["title" ], std::string("\n")));
2181 tags.artist = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["artist" ], std::string("\n")));
2182 tags.album = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["album" ], std::string("\n")));
2183 tags.trackno = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["track" ], std::string("\n")));
2184 tags.genre = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["genre" ], std::string("\n")));
2185 tags.comments = mpt::ToUnicode(charset, mpt::String::Combine(linesPerField["comment"], std::string("\n")));
2186 } else
2187 {
2188 // Most applications tend to write their own name here,
2189 // thus there is little use in interpreting the string as a title.
2190 annotation = mpt::trim_right(annotation, std::string("\r\n"));
2191 tags.comments = mpt::ToUnicode(charset, annotation);
2192 }
2193
2194 file.Seek(dataOffset);
2195
2196 ModSample &mptSample = Samples[nSample];
2197 DestroySampleThreadsafe(nSample);
2198 mptSample.Initialize();
2199 SmpLength length = mpt::saturate_cast<SmpLength>(file.BytesLeft());
2200 if(dataSize != 0xFFFFFFFF)
2201 LimitMax(length, dataSize);
2202 mptSample.nLength = (length * 8u) / (sampleIO.GetEncodedBitsPerSample() * channels);
2203 mptSample.nC5Speed = sampleRate;
2204 m_szNames[nSample] = mpt::ToCharset(GetCharsetInternal(), GetSampleNameFromTags(tags));
2205
2206 if(mayNormalize)
2207 {
2208 sampleIO.MayNormalize();
2209 }
2210
2211 sampleIO.ReadSample(mptSample, file);
2212
2213 mptSample.Convert(MOD_TYPE_IT, GetType());
2214 mptSample.PrecomputeLoops(*this, false);
2215 return true;
2216 }
2217
2218
2219 /////////////////////////////////////////////////////////////////////////////////////////
2220 // ITS Samples
2221
2222
ReadITSSample(SAMPLEINDEX nSample,FileReader & file,bool rewind)2223 bool CSoundFile::ReadITSSample(SAMPLEINDEX nSample, FileReader &file, bool rewind)
2224 {
2225 if(rewind)
2226 {
2227 file.Rewind();
2228 }
2229
2230 ITSample sampleHeader;
2231 if(!file.ReadStruct(sampleHeader)
2232 || memcmp(sampleHeader.id, "IMPS", 4))
2233 {
2234 return false;
2235 }
2236 DestroySampleThreadsafe(nSample);
2237
2238 ModSample &sample = Samples[nSample];
2239 file.Seek(sampleHeader.ConvertToMPT(sample));
2240 m_szNames[nSample] = mpt::String::ReadBuf(mpt::String::spacePaddedNull, sampleHeader.name);
2241
2242 if(sample.uFlags[CHN_ADLIB])
2243 {
2244 OPLPatch patch;
2245 file.ReadArray(patch);
2246 sample.SetAdlib(true, patch);
2247 InitOPL();
2248 if(!SupportsOPL())
2249 {
2250 AddToLog(LogInformation, U_("OPL instruments are not supported by this format."));
2251 }
2252 } else if(!sample.uFlags[SMP_KEEPONDISK])
2253 {
2254 sampleHeader.GetSampleFormat().ReadSample(sample, file);
2255 } else
2256 {
2257 // External sample
2258 size_t strLen;
2259 file.ReadVarInt(strLen);
2260 #ifdef MPT_EXTERNAL_SAMPLES
2261 std::string filenameU8;
2262 file.ReadString<mpt::String::maybeNullTerminated>(filenameU8, strLen);
2263 mpt::PathString filename = mpt::PathString::FromUTF8(filenameU8);
2264
2265 if(!filename.empty())
2266 {
2267 if(file.GetOptionalFileName())
2268 {
2269 filename = filename.RelativePathToAbsolute(file.GetOptionalFileName()->GetPath());
2270 }
2271 if(!LoadExternalSample(nSample, filename))
2272 {
2273 AddToLog(LogWarning, U_("Unable to load sample: ") + filename.ToUnicode());
2274 }
2275 } else
2276 {
2277 sample.uFlags.reset(SMP_KEEPONDISK);
2278 }
2279 #else
2280 file.Skip(strLen);
2281 #endif // MPT_EXTERNAL_SAMPLES
2282 }
2283
2284 sample.Convert(MOD_TYPE_IT, GetType());
2285 sample.PrecomputeLoops(*this, false);
2286 return true;
2287 }
2288
2289
ReadITISample(SAMPLEINDEX nSample,FileReader & file)2290 bool CSoundFile::ReadITISample(SAMPLEINDEX nSample, FileReader &file)
2291 {
2292 ITInstrument instrumentHeader;
2293
2294 file.Rewind();
2295 if(!file.ReadStruct(instrumentHeader)
2296 || memcmp(instrumentHeader.id, "IMPI", 4))
2297 {
2298 return false;
2299 }
2300 file.Rewind();
2301 ModInstrument dummy;
2302 ITInstrToMPT(file, dummy, instrumentHeader.trkvers);
2303 // Old SchismTracker versions set nos=0
2304 const SAMPLEINDEX nsamples = std::max(static_cast<SAMPLEINDEX>(instrumentHeader.nos), *std::max_element(std::begin(dummy.Keyboard), std::end(dummy.Keyboard)));
2305 if(!nsamples)
2306 return false;
2307
2308 // Preferrably read the middle-C sample
2309 auto sample = dummy.Keyboard[NOTE_MIDDLEC - NOTE_MIN];
2310 if(sample > 0)
2311 sample--;
2312 else
2313 sample = 0;
2314 file.Seek(file.GetPosition() + sample * sizeof(ITSample));
2315 return ReadITSSample(nSample, file, false);
2316 }
2317
2318
ReadITIInstrument(INSTRUMENTINDEX nInstr,FileReader & file)2319 bool CSoundFile::ReadITIInstrument(INSTRUMENTINDEX nInstr, FileReader &file)
2320 {
2321 ITInstrument instrumentHeader;
2322 SAMPLEINDEX smp = 0;
2323
2324 file.Rewind();
2325 if(!file.ReadStruct(instrumentHeader)
2326 || memcmp(instrumentHeader.id, "IMPI", 4))
2327 {
2328 return false;
2329 }
2330 if(nInstr > GetNumInstruments()) m_nInstruments = nInstr;
2331
2332 ModInstrument *pIns = new (std::nothrow) ModInstrument();
2333 if(pIns == nullptr)
2334 {
2335 return false;
2336 }
2337
2338 DestroyInstrument(nInstr, deleteAssociatedSamples);
2339
2340 Instruments[nInstr] = pIns;
2341 file.Rewind();
2342 ITInstrToMPT(file, *pIns, instrumentHeader.trkvers);
2343 // Old SchismTracker versions set nos=0
2344 const SAMPLEINDEX nsamples = std::max(static_cast<SAMPLEINDEX>(instrumentHeader.nos), *std::max_element(std::begin(pIns->Keyboard), std::end(pIns->Keyboard)));
2345
2346 // In order to properly compute the position, in file, of eventual extended settings
2347 // such as "attack" we need to keep the "real" size of the last sample as those extra
2348 // setting will follow this sample in the file
2349 FileReader::off_t extraOffset = file.GetPosition();
2350
2351 // Reading Samples
2352 std::vector<SAMPLEINDEX> samplemap(nsamples, 0);
2353 for(SAMPLEINDEX i = 0; i < nsamples; i++)
2354 {
2355 smp = GetNextFreeSample(nInstr, smp + 1);
2356 if(smp == SAMPLEINDEX_INVALID) break;
2357 samplemap[i] = smp;
2358 const FileReader::off_t offset = file.GetPosition();
2359 if(!ReadITSSample(smp, file, false))
2360 smp--;
2361 extraOffset = std::max(extraOffset, file.GetPosition());
2362 file.Seek(offset + sizeof(ITSample));
2363 }
2364 if(GetNumSamples() < smp) m_nSamples = smp;
2365
2366 // Adjust sample assignment
2367 for(auto &sample : pIns->Keyboard)
2368 {
2369 if(sample > 0 && sample <= nsamples)
2370 {
2371 sample = samplemap[sample - 1];
2372 }
2373 }
2374
2375 if(file.Seek(extraOffset))
2376 {
2377 // Read MPT crap
2378 ReadExtendedInstrumentProperties(pIns, file);
2379 }
2380
2381 pIns->Convert(MOD_TYPE_IT, GetType());
2382 pIns->Sanitize(GetType());
2383
2384 return true;
2385 }
2386
2387
2388 #ifndef MODPLUG_NO_FILESAVE
2389
SaveITIInstrument(INSTRUMENTINDEX nInstr,std::ostream & f,const mpt::PathString & filename,bool compress,bool allowExternal) const2390 bool CSoundFile::SaveITIInstrument(INSTRUMENTINDEX nInstr, std::ostream &f, const mpt::PathString &filename, bool compress, bool allowExternal) const
2391 {
2392 ITInstrument iti;
2393 ModInstrument *pIns = Instruments[nInstr];
2394
2395 if((!pIns) || (filename.empty() && allowExternal)) return false;
2396
2397 auto instSize = iti.ConvertToIT(*pIns, false, *this);
2398
2399 // Create sample assignment table
2400 std::vector<SAMPLEINDEX> smptable;
2401 std::vector<uint8> smpmap(GetNumSamples(), 0);
2402 for(size_t i = 0; i < NOTE_MAX; i++)
2403 {
2404 const SAMPLEINDEX smp = pIns->Keyboard[i];
2405 if(smp && smp <= GetNumSamples())
2406 {
2407 if(!smpmap[smp - 1])
2408 {
2409 // We haven't considered this sample yet.
2410 smptable.push_back(smp);
2411 smpmap[smp - 1] = static_cast<uint8>(smptable.size());
2412 }
2413 iti.keyboard[i * 2 + 1] = smpmap[smp - 1];
2414 } else
2415 {
2416 iti.keyboard[i * 2 + 1] = 0;
2417 }
2418 }
2419 iti.nos = static_cast<uint8>(smptable.size());
2420 smpmap.clear();
2421
2422 uint32 filePos = instSize;
2423 mpt::IO::WritePartial(f, iti, instSize);
2424
2425 filePos += mpt::saturate_cast<uint32>(smptable.size() * sizeof(ITSample));
2426
2427 // Writing sample headers + data
2428 std::vector<SampleIO> sampleFlags;
2429 for(auto smp : smptable)
2430 {
2431 ITSample itss;
2432 itss.ConvertToIT(Samples[smp], GetType(), compress, compress, allowExternal);
2433 const bool isExternal = itss.cvt == ITSample::cvtExternalSample;
2434
2435 mpt::String::WriteBuf(mpt::String::nullTerminated, itss.name) = m_szNames[smp];
2436
2437 itss.samplepointer = filePos;
2438 mpt::IO::Write(f, itss);
2439
2440 // Write sample
2441 auto curPos = mpt::IO::TellWrite(f);
2442 mpt::IO::SeekAbsolute(f, filePos);
2443 if(!isExternal)
2444 {
2445 filePos += mpt::saturate_cast<uint32>(itss.GetSampleFormat(0x0214).WriteSample(f, Samples[smp]));
2446 } else
2447 {
2448 #ifdef MPT_EXTERNAL_SAMPLES
2449 const std::string filenameU8 = GetSamplePath(smp).AbsolutePathToRelative(filename.GetPath()).ToUTF8();
2450 const size_t strSize = filenameU8.size();
2451 size_t intBytes = 0;
2452 if(mpt::IO::WriteVarInt(f, strSize, &intBytes))
2453 {
2454 filePos += mpt::saturate_cast<uint32>(intBytes + strSize);
2455 mpt::IO::WriteRaw(f, filenameU8.data(), strSize);
2456 }
2457 #endif // MPT_EXTERNAL_SAMPLES
2458 }
2459 mpt::IO::SeekAbsolute(f, curPos);
2460 }
2461
2462 mpt::IO::SeekEnd(f);
2463 // Write 'MPTX' extension tag
2464 mpt::IO::WriteRaw(f, "XTPM", 4);
2465 WriteInstrumentHeaderStructOrField(pIns, f); // Write full extended header.
2466
2467 return true;
2468 }
2469
2470 #endif // MODPLUG_NO_FILESAVE
2471
2472
2473 ///////////////////////////////////////////////////////////////////////////////////////////////////
2474 // 8SVX / 16SVX / MAUD Samples
2475
2476 // IFF File Header
2477 struct IFFHeader
2478 {
2479 char form[4]; // "FORM"
2480 uint32be size;
2481 char magic[4]; // "8SVX", "16SV", "MAUD"
2482 };
2483
2484 MPT_BINARY_STRUCT(IFFHeader, 12)
2485
2486
2487 // General IFF Chunk header
2488 struct IFFChunk
2489 {
2490 // 32-Bit chunk identifiers
2491 enum ChunkIdentifiers
2492 {
2493 // 8SVX / 16SV
2494 idVHDR = MagicBE("VHDR"),
2495 idBODY = MagicBE("BODY"),
2496 idCHAN = MagicBE("CHAN"),
2497
2498 // MAUD
2499 idMHDR = MagicBE("MHDR"),
2500 idMDAT = MagicBE("MDAT"),
2501
2502 idNAME = MagicBE("NAME"),
2503 };
2504
2505 uint32be id; // See ChunkIdentifiers
2506 uint32be length; // Chunk size without header
2507
GetLengthIFFChunk2508 size_t GetLength() const
2509 {
2510 if(length == 0) // Broken files
2511 return std::numeric_limits<size_t>::max();
2512 return length;
2513 }
2514
GetIDIFFChunk2515 ChunkIdentifiers GetID() const
2516 {
2517 return static_cast<ChunkIdentifiers>(id.get());
2518 }
2519 };
2520
2521 MPT_BINARY_STRUCT(IFFChunk, 8)
2522
2523
2524 struct IFFSampleHeader
2525 {
2526 uint32be oneShotHiSamples; // Samples in the high octave 1-shot part
2527 uint32be repeatHiSamples; // Samples in the high octave repeat part
2528 uint32be samplesPerHiCycle; // Samples/cycle in high octave, else 0
2529 uint16be samplesPerSec; // Data sampling rate
2530 uint8be octave; // Octaves of waveforms
2531 uint8be compression; // Data compression technique used
2532 uint32be volume;
2533 };
2534
2535 MPT_BINARY_STRUCT(IFFSampleHeader, 20)
2536
2537
ReadIFFSample(SAMPLEINDEX nSample,FileReader & file)2538 bool CSoundFile::ReadIFFSample(SAMPLEINDEX nSample, FileReader &file)
2539 {
2540 file.Rewind();
2541
2542 IFFHeader fileHeader;
2543 if(!file.ReadStruct(fileHeader)
2544 || memcmp(fileHeader.form, "FORM", 4)
2545 || (memcmp(fileHeader.magic, "8SVX", 4) && memcmp(fileHeader.magic, "16SV", 4) && memcmp(fileHeader.magic, "MAUD", 4)))
2546 {
2547 return false;
2548 }
2549
2550 const auto chunks = file.ReadChunks<IFFChunk>(2);
2551 FileReader sampleData;
2552
2553 SampleIO sampleIO(SampleIO::_8bit, SampleIO::mono, SampleIO::bigEndian, SampleIO::signedPCM);
2554 uint32 numSamples = 0, sampleRate = 0, loopStart = 0, loopLength = 0, volume = 0;
2555
2556 if(!memcmp(fileHeader.magic, "MAUD", 4))
2557 {
2558 FileReader mhdrChunk = chunks.GetChunk(IFFChunk::idMHDR);
2559 sampleData = chunks.GetChunk(IFFChunk::idMDAT);
2560 if(!mhdrChunk.LengthIs(32)
2561 || !sampleData.IsValid())
2562 {
2563 return false;
2564 }
2565
2566 numSamples = mhdrChunk.ReadUint32BE();
2567 const uint16 bitsPerSample = mhdrChunk.ReadUint16BE();
2568 mhdrChunk.Skip(2); // bits per sample after decompression
2569 sampleRate = mhdrChunk.ReadUint32BE();
2570 const auto [clockDivide, channelInformation, numChannels, compressionType] = mhdrChunk.ReadArray<uint16be, 4>();
2571 if(!clockDivide)
2572 return false;
2573 else
2574 sampleRate /= clockDivide;
2575
2576 if(numChannels != (channelInformation + 1))
2577 return false;
2578 if(numChannels == 2)
2579 sampleIO |= SampleIO::stereoInterleaved;
2580
2581 if(bitsPerSample == 8 && compressionType == 0)
2582 sampleIO |= SampleIO::unsignedPCM;
2583 else if(bitsPerSample == 8 && compressionType == 2)
2584 sampleIO |= SampleIO::aLaw;
2585 else if(bitsPerSample == 8 && compressionType == 3)
2586 sampleIO |= SampleIO::uLaw;
2587 else if(bitsPerSample == 16 && compressionType == 0)
2588 sampleIO |= SampleIO::_16bit;
2589 else
2590 return false;
2591 } else
2592 {
2593 FileReader vhdrChunk = chunks.GetChunk(IFFChunk::idVHDR);
2594 FileReader chanChunk = chunks.GetChunk(IFFChunk::idCHAN);
2595 sampleData = chunks.GetChunk(IFFChunk::idBODY);
2596 IFFSampleHeader sampleHeader;
2597 if(!sampleData.IsValid()
2598 || !vhdrChunk.IsValid()
2599 || !vhdrChunk.ReadStruct(sampleHeader))
2600 {
2601 return false;
2602 }
2603
2604 const uint8 bytesPerSample = memcmp(fileHeader.magic, "8SVX", 4) ? 2 : 1;
2605 const uint8 numChannels = chanChunk.ReadUint32BE() == 6 ? 2 : 1;
2606 const uint8 bytesPerFrame = bytesPerSample * numChannels;
2607
2608 // While this is an Amiga format, the 16SV version appears to be only used on PC, and only with little-endian sample data.
2609 if(bytesPerSample == 2)
2610 sampleIO = SampleIO(SampleIO::_16bit, SampleIO::mono, SampleIO::littleEndian, SampleIO::signedPCM);
2611 if(numChannels == 2)
2612 sampleIO |= SampleIO::stereoSplit;
2613
2614 loopStart = sampleHeader.oneShotHiSamples / bytesPerFrame;
2615 loopLength = sampleHeader.repeatHiSamples / bytesPerFrame;
2616 sampleRate = sampleHeader.samplesPerSec;
2617 volume = sampleHeader.volume;
2618 numSamples = mpt::saturate_cast<SmpLength>(sampleData.GetLength() / bytesPerFrame);
2619 }
2620
2621 DestroySampleThreadsafe(nSample);
2622 ModSample &sample = Samples[nSample];
2623 sample.Initialize();
2624 sample.nLength = numSamples;
2625 sample.nLoopStart = loopStart;
2626 sample.nLoopEnd = sample.nLoopStart + loopLength;
2627 if((sample.nLoopStart + 4 < sample.nLoopEnd) && (sample.nLoopEnd <= sample.nLength))
2628 sample.uFlags.set(CHN_LOOP);
2629
2630 sample.nC5Speed = sampleRate;
2631 if(!sample.nC5Speed)
2632 sample.nC5Speed = 22050;
2633
2634 sample.nVolume = static_cast<uint16>(volume / 256);
2635 if(!sample.nVolume || sample.nVolume > 256)
2636 sample.nVolume = 256;
2637
2638 sample.Convert(MOD_TYPE_IT, GetType());
2639
2640 FileReader nameChunk = chunks.GetChunk(IFFChunk::idNAME);
2641 if(nameChunk.IsValid())
2642 nameChunk.ReadString<mpt::String::maybeNullTerminated>(m_szNames[nSample], nameChunk.GetLength());
2643 else
2644 m_szNames[nSample] = "";
2645
2646 sampleIO.ReadSample(sample, sampleData);
2647 sample.PrecomputeLoops(*this, false);
2648
2649 return true;
2650 }
2651
2652
2653 OPENMPT_NAMESPACE_END
2654