1 /*
2 * Sndfile.cpp
3 * -----------
4 * Purpose: Core class of the playback engine. Every song is represented by a CSoundFile object.
5 * Notes : (currently none)
6 * Authors: Olivier Lapicque
7 * OpenMPT Devs
8 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
9 */
10
11
12 #include "stdafx.h"
13 #ifdef MODPLUG_TRACKER
14 #include "../mptrack/Mptrack.h" // For CTrackApp::OpenURL
15 #include "../mptrack/TrackerSettings.h"
16 #include "../mptrack/Moddoc.h"
17 #include "../mptrack/Reporting.h"
18 #include "../mptrack/Mainfrm.h"
19 #endif // MODPLUG_TRACKER
20 #ifdef MPT_EXTERNAL_SAMPLES
21 #include "../common/mptFileIO.h"
22 #endif // MPT_EXTERNAL_SAMPLES
23 #include "../common/version.h"
24 #include "../soundlib/AudioCriticalSection.h"
25 #include "../common/serialization_utils.h"
26 #include "Sndfile.h"
27 #include "Tables.h"
28 #include "mod_specifications.h"
29 #include "tuningcollection.h"
30 #include "plugins/PluginManager.h"
31 #include "plugins/PlugInterface.h"
32 #include "../common/mptStringBuffer.h"
33 #include "../common/FileReader.h"
34 #include "Container.h"
35 #include "OPL.h"
36 #include "mpt/io/io.hpp"
37 #include "mpt/io/io_stdstream.hpp"
38
39 #ifndef NO_ARCHIVE_SUPPORT
40 #include "../unarchiver/unarchiver.h"
41 #endif // NO_ARCHIVE_SUPPORT
42
43
44 OPENMPT_NAMESPACE_BEGIN
45
46
SettingCacheCompleteFileBeforeLoading()47 bool SettingCacheCompleteFileBeforeLoading()
48 {
49 #ifdef MODPLUG_TRACKER
50 return TrackerSettings::Instance().MiscCacheCompleteFileBeforeLoading;
51 #else
52 return false;
53 #endif
54 }
55
56
AsISO8601() const57 mpt::ustring FileHistory::AsISO8601() const
58 {
59 tm date = loadDate;
60 if(openTime > 0)
61 {
62 // Calculate the date when editing finished.
63 double openSeconds = static_cast<double>(openTime) / HISTORY_TIMER_PRECISION;
64 tm tmpLoadDate = loadDate;
65 int64 loadDateSinceEpoch = mpt::Date::Unix::FromUTC(tmpLoadDate);
66 int64 saveDateSinceEpoch = loadDateSinceEpoch + mpt::saturate_round<int64>(openSeconds);
67 date = mpt::Date::Unix(saveDateSinceEpoch).AsUTC();
68 }
69 return mpt::Date::ToShortenedISO8601(date);
70 }
71
72
73 //////////////////////////////////////////////////////////
74 // CSoundFile
75
76 #ifdef MODPLUG_TRACKER
77 const NoteName *CSoundFile::m_NoteNames = NoteNamesFlat;
78 #endif
79
CSoundFile()80 CSoundFile::CSoundFile() :
81 #ifndef MODPLUG_TRACKER
82 m_NoteNames(NoteNamesSharp),
83 #endif
84 m_pModSpecs(&ModSpecs::itEx),
85 m_nType(MOD_TYPE_NONE),
86 Patterns(*this),
87 #ifdef MODPLUG_TRACKER
88 m_MIDIMapper(*this),
89 #endif
90 Order(*this),
91 m_PRNG(mpt::make_prng<mpt::fast_prng>(mpt::global_prng())),
92 m_visitedRows(*this)
93 {
94 MemsetZero(MixSoundBuffer);
95 MemsetZero(MixRearBuffer);
96 MemsetZero(MixFloatBuffer);
97
98 #ifdef MODPLUG_TRACKER
99 m_bChannelMuteTogglePending.reset();
100
101 m_nDefaultRowsPerBeat = m_PlayState.m_nCurrentRowsPerBeat = (TrackerSettings::Instance().m_nRowHighlightBeats) ? TrackerSettings::Instance().m_nRowHighlightBeats : 4;
102 m_nDefaultRowsPerMeasure = m_PlayState.m_nCurrentRowsPerMeasure = (TrackerSettings::Instance().m_nRowHighlightMeasures >= m_nDefaultRowsPerBeat) ? TrackerSettings::Instance().m_nRowHighlightMeasures : m_nDefaultRowsPerBeat * 4;
103 #else
104 m_nDefaultRowsPerBeat = m_PlayState.m_nCurrentRowsPerBeat = 4;
105 m_nDefaultRowsPerMeasure = m_PlayState.m_nCurrentRowsPerMeasure = 16;
106 #endif // MODPLUG_TRACKER
107
108 MemsetZero(Instruments);
109 Clear(m_szNames);
110
111 m_pTuningsTuneSpecific = new CTuningCollection();
112 }
113
114
~CSoundFile()115 CSoundFile::~CSoundFile()
116 {
117 Destroy();
118 delete m_pTuningsTuneSpecific;
119 m_pTuningsTuneSpecific = nullptr;
120 }
121
122
AddToLog(LogLevel level,const mpt::ustring & text) const123 void CSoundFile::AddToLog(LogLevel level, const mpt::ustring &text) const
124 {
125 if(m_pCustomLog)
126 {
127 m_pCustomLog->AddToLog(level, text);
128 } else
129 {
130 #ifdef MODPLUG_TRACKER
131 if(GetpModDoc()) GetpModDoc()->AddToLog(level, text);
132 #else
133 MPT_LOG_GLOBAL(level, "soundlib", text);
134 #endif
135 }
136 }
137
138
139 // Global variable initializer for loader functions
InitializeGlobals(MODTYPE type)140 void CSoundFile::InitializeGlobals(MODTYPE type)
141 {
142 // Do not add or change any of these values! And if you do, review each and every loader to check if they require these defaults!
143 m_nType = type;
144
145 MODTYPE bestType = GetBestSaveFormat();
146 m_playBehaviour = GetDefaultPlaybackBehaviour(bestType);
147 SetModSpecsPointer(m_pModSpecs, bestType);
148
149 // Delete instruments in case some previously called loader already created them.
150 for(INSTRUMENTINDEX i = 1; i <= m_nInstruments; i++)
151 {
152 delete Instruments[i];
153 Instruments[i] = nullptr;
154 }
155
156 m_ContainerType = MOD_CONTAINERTYPE_NONE;
157 m_nChannels = 0;
158 m_nInstruments = 0;
159 m_nSamples = 0;
160 m_nSamplePreAmp = 48;
161 m_nVSTiVolume = 48;
162 m_OPLVolumeFactor = m_OPLVolumeFactorScale;
163 m_nDefaultSpeed = 6;
164 m_nDefaultTempo.Set(125);
165 m_nDefaultGlobalVolume = MAX_GLOBAL_VOLUME;
166 m_SongFlags.reset();
167 m_nMinPeriod = 16;
168 m_nMaxPeriod = 32767;
169 m_nResampling = SRCMODE_DEFAULT;
170 m_dwLastSavedWithVersion = Version(0);
171 m_dwCreatedWithVersion = Version(0);
172
173 SetMixLevels(MixLevels::Compatible);
174
175 Patterns.ClearPatterns();
176 Order.Initialize();
177
178 m_songName.clear();
179 m_songArtist.clear();
180 m_songMessage.clear();
181 m_modFormat = ModFormatDetails();
182 m_FileHistory.clear();
183 m_tempoSwing.clear();
184 #ifdef MPT_EXTERNAL_SAMPLES
185 m_samplePaths.clear();
186 #endif // MPT_EXTERNAL_SAMPLES
187
188 // Note: we do not use the Amiga resampler for DBM as it's a multichannel format and can make use of higher-quality Amiga soundcards instead of Paula.
189 if(GetType() & (/*MOD_TYPE_DBM | */MOD_TYPE_DIGI | MOD_TYPE_MED | MOD_TYPE_MOD | MOD_TYPE_OKT | MOD_TYPE_SFX | MOD_TYPE_STP))
190 {
191 m_SongFlags.set(SONG_ISAMIGA);
192 }
193 }
194
195
InitializeChannels()196 void CSoundFile::InitializeChannels()
197 {
198 for(CHANNELINDEX nChn = 0; nChn < MAX_BASECHANNELS; nChn++)
199 {
200 InitChannel(nChn);
201 }
202 }
203
204
205 struct FileFormatLoader
206 {
207 decltype(CSoundFile::ProbeFileHeaderXM) *prober;
208 decltype(&CSoundFile::ReadXM) loader;
209 };
210
211 #ifdef MODPLUG_TRACKER
212 #define MPT_DECLARE_FORMAT(format) { nullptr, &CSoundFile::Read ## format }
213 #else
214 #define MPT_DECLARE_FORMAT(format) { CSoundFile::ProbeFileHeader ## format, &CSoundFile::Read ## format }
215 #endif
216
217 // All module format loaders, in the order they should be executed.
218 // This order matters, depending on the format, due to some unfortunate
219 // clashes or lack of magic bytes that can lead to mis-detection of some formats.
220 // Apart from that, more common formats with sane magic bytes are also found
221 // at the top of the list to match the most common cases more quickly.
222 static constexpr FileFormatLoader ModuleFormatLoaders[] =
223 {
224 MPT_DECLARE_FORMAT(XM),
225 MPT_DECLARE_FORMAT(IT),
226 MPT_DECLARE_FORMAT(S3M),
227 MPT_DECLARE_FORMAT(STM),
228 MPT_DECLARE_FORMAT(MED),
229 MPT_DECLARE_FORMAT(MTM),
230 MPT_DECLARE_FORMAT(MDL),
231 MPT_DECLARE_FORMAT(DBM),
232 MPT_DECLARE_FORMAT(FAR),
233 MPT_DECLARE_FORMAT(AMS),
234 MPT_DECLARE_FORMAT(AMS2),
235 MPT_DECLARE_FORMAT(OKT),
236 MPT_DECLARE_FORMAT(PTM),
237 MPT_DECLARE_FORMAT(ULT),
238 MPT_DECLARE_FORMAT(DMF),
239 MPT_DECLARE_FORMAT(DSM),
240 MPT_DECLARE_FORMAT(AMF_Asylum),
241 MPT_DECLARE_FORMAT(AMF_DSMI),
242 MPT_DECLARE_FORMAT(PSM),
243 MPT_DECLARE_FORMAT(PSM16),
244 MPT_DECLARE_FORMAT(MT2),
245 MPT_DECLARE_FORMAT(ITP),
246 #if defined(MODPLUG_TRACKER) || defined(MPT_FUZZ_TRACKER)
247 // These make little sense for a module player library
248 MPT_DECLARE_FORMAT(UAX),
249 MPT_DECLARE_FORMAT(WAV),
250 MPT_DECLARE_FORMAT(MID),
251 #endif // MODPLUG_TRACKER || MPT_FUZZ_TRACKER
252 MPT_DECLARE_FORMAT(GDM),
253 MPT_DECLARE_FORMAT(IMF),
254 MPT_DECLARE_FORMAT(DIGI),
255 MPT_DECLARE_FORMAT(DTM),
256 MPT_DECLARE_FORMAT(PLM),
257 MPT_DECLARE_FORMAT(AM),
258 MPT_DECLARE_FORMAT(J2B),
259 MPT_DECLARE_FORMAT(PT36),
260 MPT_DECLARE_FORMAT(SymMOD),
261 MPT_DECLARE_FORMAT(MUS_KM),
262 MPT_DECLARE_FORMAT(FMT),
263 MPT_DECLARE_FORMAT(SFX),
264 MPT_DECLARE_FORMAT(STP),
265 MPT_DECLARE_FORMAT(DSym),
266 MPT_DECLARE_FORMAT(STX),
267 MPT_DECLARE_FORMAT(MOD),
268 MPT_DECLARE_FORMAT(ICE),
269 MPT_DECLARE_FORMAT(669),
270 MPT_DECLARE_FORMAT(C67),
271 MPT_DECLARE_FORMAT(MO3),
272 MPT_DECLARE_FORMAT(M15),
273 };
274
275 #undef MPT_DECLARE_FORMAT
276
277
ProbeAdditionalSize(MemoryFileReader & file,const uint64 * pfilesize,uint64 minimumAdditionalSize)278 CSoundFile::ProbeResult CSoundFile::ProbeAdditionalSize(MemoryFileReader &file, const uint64 *pfilesize, uint64 minimumAdditionalSize)
279 {
280 const uint64 availableFileSize = file.GetLength();
281 const uint64 fileSize = (pfilesize ? *pfilesize : file.GetLength());
282 //const uint64 validFileSize = std::min(fileSize, static_cast<uint64>(ProbeRecommendedSize));
283 const uint64 goalSize = file.GetPosition() + minimumAdditionalSize;
284 //const uint64 goalMinimumSize = std::min(goalSize, static_cast<uint64>(ProbeRecommendedSize));
285 if(pfilesize)
286 {
287 if(availableFileSize < std::min(fileSize, static_cast<uint64>(ProbeRecommendedSize)))
288 {
289 if(availableFileSize < goalSize)
290 {
291 return ProbeWantMoreData;
292 }
293 } else
294 {
295 if(fileSize < goalSize)
296 {
297 return ProbeFailure;
298 }
299 }
300 return ProbeSuccess;
301 }
302 return ProbeSuccess;
303 }
304
305
306 #define MPT_DO_PROBE( storedResult , call ) \
307 do { \
308 ProbeResult lastResult = call ; \
309 if(lastResult == ProbeSuccess) { \
310 return ProbeSuccess; \
311 } else if(lastResult == ProbeWantMoreData) { \
312 storedResult = ProbeWantMoreData; \
313 } \
314 } while(0) \
315 /**/
316
317
Probe(ProbeFlags flags,mpt::span<const std::byte> data,const uint64 * pfilesize)318 CSoundFile::ProbeResult CSoundFile::Probe(ProbeFlags flags, mpt::span<const std::byte> data, const uint64 *pfilesize)
319 {
320 ProbeResult result = ProbeFailure;
321 if(pfilesize && (*pfilesize < data.size()))
322 {
323 throw std::out_of_range("");
324 }
325 if(!data.data())
326 {
327 throw std::invalid_argument("");
328 }
329 MemoryFileReader file(data);
330 if(flags & ProbeContainers)
331 {
332 #if !defined(MPT_WITH_ANCIENT)
333 MPT_DO_PROBE(result, ProbeFileHeaderMMCMP(file, pfilesize));
334 MPT_DO_PROBE(result, ProbeFileHeaderPP20(file, pfilesize));
335 MPT_DO_PROBE(result, ProbeFileHeaderXPK(file, pfilesize));
336 #endif // !MPT_WITH_ANCIENT
337 MPT_DO_PROBE(result, ProbeFileHeaderUMX(file, pfilesize));
338 }
339 if(flags & ProbeModules)
340 {
341 for(const auto &format : ModuleFormatLoaders)
342 {
343 if(format.prober != nullptr)
344 {
345 MPT_DO_PROBE(result, format.prober(file, pfilesize));
346 }
347 }
348 }
349 if(pfilesize)
350 {
351 if((result == ProbeWantMoreData) && (mpt::saturate_cast<std::size_t>(*pfilesize) <= data.size()))
352 {
353 // If the prober wants more data but we already reached EOF,
354 // probing must fail.
355 result = ProbeFailure;
356 }
357 } else
358 {
359 if((result == ProbeWantMoreData) && (data.size() >= ProbeRecommendedSize))
360 {
361 // If the prober wants more daat but we already provided the recommended required maximum,
362 // just return success as this is the best we can do for the suggestesd probing size.
363 result = ProbeSuccess;
364 }
365 }
366 return result;
367 }
368
369
370 #ifdef MODPLUG_TRACKER
Create(FileReader file,ModLoadingFlags loadFlags,CModDoc * pModDoc)371 bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags, CModDoc *pModDoc)
372 {
373 m_pModDoc = pModDoc;
374 #else
375 bool CSoundFile::Create(FileReader file, ModLoadingFlags loadFlags)
376 {
377 #endif // MODPLUG_TRACKER
378
379 m_nMixChannels = 0;
380 #ifndef MODPLUG_TRACKER
381 m_nFreqFactor = m_nTempoFactor = 65536;
382 #endif
383
384 MemsetZero(Instruments);
385 Clear(m_szNames);
386 #ifndef NO_PLUGINS
387 std::fill(std::begin(m_MixPlugins), std::end(m_MixPlugins), SNDMIXPLUGIN());
388 #endif // NO_PLUGINS
389
390 if(file.IsValid())
391 {
392
393 {
394
395 #ifndef NO_ARCHIVE_SUPPORT
396 CUnarchiver unarchiver(file);
397 if(!(loadFlags & skipContainer))
398 {
399 if (unarchiver.ExtractBestFile(GetSupportedExtensions(true)))
400 {
401 file = unarchiver.GetOutputFile();
402 }
403 }
404 #endif
405
406 std::vector<ContainerItem> containerItems;
407 MODCONTAINERTYPE packedContainerType = MOD_CONTAINERTYPE_NONE;
408 if(!(loadFlags & skipContainer))
409 {
410 ContainerLoadingFlags containerLoadFlags = (loadFlags == onlyVerifyHeader) ? ContainerOnlyVerifyHeader : ContainerUnwrapData;
411 #if !defined(MPT_WITH_ANCIENT)
412 if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackXPK(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_XPK;
413 if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackPP20(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_PP20;
414 if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackMMCMP(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_MMCMP;
415 #endif // !MPT_WITH_ANCIENT
416 if(packedContainerType == MOD_CONTAINERTYPE_NONE && UnpackUMX(containerItems, file, containerLoadFlags)) packedContainerType = MOD_CONTAINERTYPE_UMX;
417 if(packedContainerType != MOD_CONTAINERTYPE_NONE)
418 {
419 if(loadFlags == onlyVerifyHeader)
420 {
421 return true;
422 }
423 if(!containerItems.empty())
424 {
425 // cppcheck false-positive
426 // cppcheck-suppress containerOutOfBounds
427 file = containerItems[0].file;
428 }
429 }
430 }
431
432 if(loadFlags & skipModules)
433 {
434 return false;
435 }
436
437 // Try all module format loaders
438 bool loaderSuccess = false;
439 for(const auto &format : ModuleFormatLoaders)
440 {
441 loaderSuccess = (this->*(format.loader))(file, loadFlags);
442 if(loaderSuccess)
443 break;
444 }
445
446 if(!loaderSuccess)
447 {
448 m_nType = MOD_TYPE_NONE;
449 m_ContainerType = MOD_CONTAINERTYPE_NONE;
450 }
451 if(loadFlags == onlyVerifyHeader)
452 {
453 return loaderSuccess;
454 }
455
456 if(packedContainerType != MOD_CONTAINERTYPE_NONE && m_ContainerType == MOD_CONTAINERTYPE_NONE)
457 {
458 m_ContainerType = packedContainerType;
459 }
460
461 #ifndef NO_ARCHIVE_SUPPORT
462 // Read archive comment if there is no song comment
463 if(m_songMessage.empty())
464 {
465 m_songMessage.assign(mpt::ToCharset(mpt::Charset::Locale, unarchiver.GetComment()));
466 }
467 #endif
468
469 m_visitedRows.Initialize(true);
470
471 }
472 } else
473 {
474 // New song
475 InitializeGlobals();
476 m_visitedRows.Initialize(true);
477 m_dwCreatedWithVersion = Version::Current();
478 }
479
480 // Adjust channels
481 const auto muteFlag = GetChannelMuteFlag();
482 for(CHANNELINDEX chn = 0; chn < MAX_BASECHANNELS; chn++)
483 {
484 LimitMax(ChnSettings[chn].nVolume, uint16(64));
485 if(ChnSettings[chn].nPan > 256)
486 ChnSettings[chn].nPan = 128;
487 if(ChnSettings[chn].nMixPlugin > MAX_MIXPLUGINS)
488 ChnSettings[chn].nMixPlugin = 0;
489 m_PlayState.Chn[chn].Reset(ModChannel::resetTotal, *this, chn, muteFlag);
490 }
491
492 // Checking samples, load external samples
493 for(SAMPLEINDEX nSmp = 1; nSmp <= m_nSamples; nSmp++)
494 {
495 ModSample &sample = Samples[nSmp];
496
497 #ifdef MPT_EXTERNAL_SAMPLES
498 if(SampleHasPath(nSmp))
499 {
500 mpt::PathString filename = GetSamplePath(nSmp);
501 if(file.GetOptionalFileName())
502 {
503 filename = filename.RelativePathToAbsolute(file.GetOptionalFileName()->GetPath());
504 } else if(GetpModDoc() != nullptr)
505 {
506 filename = filename.RelativePathToAbsolute(GetpModDoc()->GetPathNameMpt().GetPath());
507 }
508 filename = filename.Simplify();
509 if(!LoadExternalSample(nSmp, filename))
510 {
511 #ifndef MODPLUG_TRACKER
512 // OpenMPT has its own way of reporting this error in CModDoc.
513 AddToLog(LogError, MPT_UFORMAT("Unable to load sample {}: {}")(i, filename.ToUnicode()));
514 #endif // MODPLUG_TRACKER
515 }
516 } else
517 {
518 sample.uFlags.reset(SMP_KEEPONDISK);
519 }
520 #endif // MPT_EXTERNAL_SAMPLES
521
522 if(sample.HasSampleData())
523 {
524 sample.PrecomputeLoops(*this, false);
525 } else if(!sample.uFlags[SMP_KEEPONDISK])
526 {
527 sample.nLength = 0;
528 sample.nLoopStart = 0;
529 sample.nLoopEnd = 0;
530 sample.nSustainStart = 0;
531 sample.nSustainEnd = 0;
532 sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN);
533 }
534 if(sample.nGlobalVol > 64) sample.nGlobalVol = 64;
535 if(sample.uFlags[CHN_ADLIB] && m_opl == nullptr) InitOPL();
536 }
537 // Check invalid instruments
538 INSTRUMENTINDEX maxInstr = 0;
539 for(INSTRUMENTINDEX i = 0; i <= m_nInstruments; i++)
540 {
541 if(Instruments[i] != nullptr)
542 {
543 maxInstr = i;
544 Instruments[i]->Sanitize(GetType());
545 }
546 }
547 m_nInstruments = maxInstr;
548
549 // Set default play state values
550 if(!m_nDefaultTempo.GetInt())
551 m_nDefaultTempo.Set(125);
552 else
553 LimitMax(m_nDefaultTempo, TEMPO(uint16_max, 0));
554 if(!m_nDefaultSpeed)
555 m_nDefaultSpeed = 6;
556
557 if(m_nDefaultRowsPerMeasure < m_nDefaultRowsPerBeat)
558 m_nDefaultRowsPerMeasure = m_nDefaultRowsPerBeat;
559 LimitMax(m_nDefaultRowsPerBeat, MAX_ROWS_PER_BEAT);
560 LimitMax(m_nDefaultRowsPerMeasure, MAX_ROWS_PER_BEAT);
561 LimitMax(m_nDefaultGlobalVolume, MAX_GLOBAL_VOLUME);
562 if(!m_tempoSwing.empty())
563 m_tempoSwing.resize(m_nDefaultRowsPerBeat);
564
565 m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
566 m_PlayState.m_nMusicTempo = m_nDefaultTempo;
567 m_PlayState.m_nCurrentRowsPerBeat = m_nDefaultRowsPerBeat;
568 m_PlayState.m_nCurrentRowsPerMeasure = m_nDefaultRowsPerMeasure;
569 m_PlayState.m_nGlobalVolume = static_cast<int32>(m_nDefaultGlobalVolume);
570 m_PlayState.ResetGlobalVolumeRamping();
571 m_PlayState.m_nNextOrder = 0;
572 m_PlayState.m_nCurrentOrder = 0;
573 m_PlayState.m_nPattern = 0;
574 m_PlayState.m_nBufferCount = 0;
575 m_PlayState.m_dBufferDiff = 0;
576 m_PlayState.m_nTickCount = TICKS_ROW_FINISHED;
577 m_PlayState.m_nNextRow = 0;
578 m_PlayState.m_nRow = 0;
579 m_PlayState.m_nPatternDelay = 0;
580 m_PlayState.m_nFrameDelay = 0;
581 m_PlayState.m_nextPatStartRow = 0;
582 m_PlayState.m_nSeqOverride = ORDERINDEX_INVALID;
583
584 if(UseFinetuneAndTranspose())
585 m_playBehaviour.reset(kPeriodsAreHertz);
586
587 m_nMaxOrderPosition = 0;
588
589 RecalculateSamplesPerTick();
590
591 for(auto &order : Order)
592 {
593 order.Shrink();
594 if(order.GetRestartPos() >= order.size())
595 {
596 order.SetRestartPos(0);
597 }
598 }
599
600 if(GetType() == MOD_TYPE_NONE)
601 {
602 return false;
603 }
604
605 SetModSpecsPointer(m_pModSpecs, GetBestSaveFormat());
606
607 // When reading a file made with an older version of MPT, it might be necessary to upgrade some settings automatically.
608 if(m_dwLastSavedWithVersion)
609 {
610 UpgradeModule();
611 }
612
613 #ifndef NO_PLUGINS
614 // Load plugins
615 #ifdef MODPLUG_TRACKER
616 mpt::ustring notFoundText;
617 #endif // MODPLUG_TRACKER
618 std::vector<const SNDMIXPLUGININFO *> notFoundIDs;
619
620 if((loadFlags & (loadPluginData | loadPluginInstance)) == (loadPluginData | loadPluginInstance))
621 {
622 for(PLUGINDEX plug = 0; plug < MAX_MIXPLUGINS; plug++)
623 {
624 auto &plugin = m_MixPlugins[plug];
625 if(plugin.IsValidPlugin())
626 {
627 #ifdef MODPLUG_TRACKER
628 // Provide some visual feedback
629 {
630 mpt::ustring s = MPT_UFORMAT("Loading Plugin FX{}: {} ({})")(
631 mpt::ufmt::dec0<2>(plug + 1),
632 mpt::ToUnicode(mpt::Charset::UTF8, plugin.Info.szLibraryName),
633 mpt::ToUnicode(mpt::Charset::Locale, plugin.Info.szName));
634 CMainFrame::GetMainFrame()->SetHelpText(mpt::ToCString(s));
635 }
636 #endif // MODPLUG_TRACKER
637 CreateMixPluginProc(plugin, *this);
638 if(plugin.pMixPlugin)
639 {
640 // Plugin was found
641 plugin.pMixPlugin->RestoreAllParameters(plugin.defaultProgram);
642 } else
643 {
644 // Plugin not found - add to list
645 bool found = std::find_if(notFoundIDs.cbegin(), notFoundIDs.cend(),
646 [&plugin](const SNDMIXPLUGININFO *info) { return info->dwPluginId2 == plugin.Info.dwPluginId2 && info->dwPluginId1 == plugin.Info.dwPluginId1; }) != notFoundIDs.cend();
647
648 if(!found)
649 {
650 notFoundIDs.push_back(&plugin.Info);
651 #ifdef MODPLUG_TRACKER
652 notFoundText.append(plugin.GetLibraryName());
653 notFoundText.append(UL_("\n"));
654 #else
655 AddToLog(LogWarning, U_("Plugin not found: ") + plugin.GetLibraryName());
656 #endif // MODPLUG_TRACKER
657 }
658 }
659 }
660 }
661 }
662
663 // Set up mix levels (also recalculates plugin mix levels - must be done after plugins were loaded)
664 SetMixLevels(m_nMixLevels);
665
666 #ifdef MODPLUG_TRACKER
667 // Display a nice message so the user sees which plugins are missing
668 // TODO: Use IDD_MODLOADING_WARNINGS dialog (NON-MODAL!) to display all warnings that are encountered when loading a module.
669 if(!notFoundIDs.empty())
670 {
671 if(notFoundIDs.size() == 1)
672 {
673 notFoundText = UL_("The following plugin has not been found:\n\n") + notFoundText + UL_("\nDo you want to search for it online?");
674 } else
675 {
676 notFoundText = UL_("The following plugins have not been found:\n\n") + notFoundText + UL_("\nDo you want to search for them online?");
677 }
678 if(Reporting::Confirm(notFoundText, U_("OpenMPT - Plugins missing"), false, true) == cnfYes)
679 {
680 mpt::ustring url = U_("https://resources.openmpt.org/plugins/search.php?p=");
681 for(const auto &id : notFoundIDs)
682 {
683 url += mpt::ufmt::HEX0<8>(id->dwPluginId2.get());
684 url += mpt::ToUnicode(mpt::Charset::UTF8, id->szLibraryName);
685 url += UL_("%0a");
686 }
687 CTrackApp::OpenURL(mpt::PathString::FromUnicode(url));
688 }
689 }
690 #endif // MODPLUG_TRACKER
691 #endif // NO_PLUGINS
692
693 return true;
694 }
695
696
697 bool CSoundFile::Destroy()
698 {
699 for(auto &chn : m_PlayState.Chn)
700 {
701 chn.pModInstrument = nullptr;
702 chn.pModSample = nullptr;
703 chn.pCurrentSample = nullptr;
704 chn.nLength = 0;
705 }
706
707 Patterns.DestroyPatterns();
708
709 m_songName.clear();
710 m_songArtist.clear();
711 m_songMessage.clear();
712 m_FileHistory.clear();
713 #ifdef MPT_EXTERNAL_SAMPLES
714 m_samplePaths.clear();
715 #endif // MPT_EXTERNAL_SAMPLES
716
717 for(auto &smp : Samples)
718 {
719 smp.FreeSample();
720 }
721 for(auto &ins : Instruments)
722 {
723 delete ins;
724 ins = nullptr;
725 }
726 #ifndef NO_PLUGINS
727 for(auto &plug : m_MixPlugins)
728 {
729 plug.Destroy();
730 }
731 #endif // NO_PLUGINS
732
733 m_nType = MOD_TYPE_NONE;
734 m_ContainerType = MOD_CONTAINERTYPE_NONE;
735 m_nChannels = m_nSamples = m_nInstruments = 0;
736 return true;
737 }
738
739
740 //////////////////////////////////////////////////////////////////////////
741 // Misc functions
742
743
744 void CSoundFile::SetDspEffects(uint32 DSPMask)
745 {
746 m_MixerSettings.DSPMask = DSPMask;
747 InitPlayer(false);
748 }
749
750
751 void CSoundFile::SetPreAmp(uint32 nVol)
752 {
753 if (nVol < 1) nVol = 1;
754 if (nVol > 0x200) nVol = 0x200; // x4 maximum
755 #ifndef NO_AGC
756 if ((nVol < m_MixerSettings.m_nPreAmp) && (nVol) && (m_MixerSettings.DSPMask & SNDDSP_AGC))
757 {
758 m_AGC.Adjust(m_MixerSettings.m_nPreAmp, nVol);
759 }
760 #endif
761 m_MixerSettings.m_nPreAmp = nVol;
762 }
763
764
765 double CSoundFile::GetCurrentBPM() const
766 {
767 double bpm;
768
769 if (m_nTempoMode == TempoMode::Modern)
770 {
771 // With modern mode, we trust that true bpm is close enough to what user chose.
772 // This avoids oscillation due to tick-to-tick corrections.
773 bpm = m_PlayState.m_nMusicTempo.ToDouble();
774 } else
775 {
776 //with other modes, we calculate it:
777 double ticksPerBeat = m_PlayState.m_nMusicSpeed * m_PlayState.m_nCurrentRowsPerBeat; //ticks/beat = ticks/row * rows/beat
778 double samplesPerBeat = m_PlayState.m_nSamplesPerTick * ticksPerBeat; //samps/beat = samps/tick * ticks/beat
779 bpm = m_MixerSettings.gdwMixingFreq / samplesPerBeat * 60; //beats/sec = samps/sec / samps/beat
780 } //beats/min = beats/sec * 60
781
782 return bpm;
783 }
784
785
786 void CSoundFile::ResetPlayPos()
787 {
788 const auto muteFlag = GetChannelMuteFlag();
789 for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++)
790 m_PlayState.Chn[i].Reset(ModChannel::resetSetPosFull, *this, i, muteFlag);
791
792 m_visitedRows.Initialize(true);
793 m_SongFlags.reset(SONG_FADINGSONG | SONG_ENDREACHED);
794
795 m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume;
796 m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
797 m_PlayState.m_nMusicTempo = m_nDefaultTempo;
798
799 // Do not ramp global volume when starting playback
800 m_PlayState.ResetGlobalVolumeRamping();
801
802 m_PlayState.m_nNextOrder = 0;
803 m_PlayState.m_nNextRow = 0;
804 m_PlayState.m_nTickCount = TICKS_ROW_FINISHED;
805 m_PlayState.m_nBufferCount = 0;
806 m_PlayState.m_nPatternDelay = 0;
807 m_PlayState.m_nFrameDelay = 0;
808 m_PlayState.m_nextPatStartRow = 0;
809 m_PlayState.m_lTotalSampleCount = 0;
810 }
811
812
813
814 void CSoundFile::SetCurrentOrder(ORDERINDEX nOrder)
815 {
816 while(nOrder < Order().size() && !Order().IsValidPat(nOrder))
817 nOrder++;
818 if(nOrder >= Order().size())
819 return;
820
821 for(auto &chn : m_PlayState.Chn)
822 {
823 chn.nPeriod = 0;
824 chn.nNote = NOTE_NONE;
825 chn.nPortamentoDest = 0;
826 chn.nCommand = 0;
827 chn.nPatternLoopCount = 0;
828 chn.nPatternLoop = 0;
829 chn.nVibratoPos = chn.nTremoloPos = chn.nPanbrelloPos = 0;
830 //IT compatibility 15. Retrigger
831 if(m_playBehaviour[kITRetrigger])
832 {
833 chn.nRetrigCount = 0;
834 chn.nRetrigParam = 1;
835 }
836 chn.nTremorCount = 0;
837 }
838
839 #ifndef NO_PLUGINS
840 // Stop hanging notes from VST instruments as well
841 StopAllVsti();
842 #endif // NO_PLUGINS
843
844 if (!nOrder)
845 {
846 ResetPlayPos();
847 } else
848 {
849 m_PlayState.m_nNextOrder = nOrder;
850 m_PlayState.m_nRow = m_PlayState.m_nNextRow = 0;
851 m_PlayState.m_nPattern = 0;
852 m_PlayState.m_nTickCount = TICKS_ROW_FINISHED;
853 m_PlayState.m_nBufferCount = 0;
854 m_PlayState.m_nPatternDelay = 0;
855 m_PlayState.m_nFrameDelay = 0;
856 m_PlayState.m_nextPatStartRow = 0;
857 }
858
859 m_SongFlags.reset(SONG_FADINGSONG | SONG_ENDREACHED);
860 }
861
862 void CSoundFile::SuspendPlugins()
863 {
864 #ifndef NO_PLUGINS
865 for(auto &plug : m_MixPlugins)
866 {
867 IMixPlugin *pPlugin = plug.pMixPlugin;
868 if(pPlugin != nullptr && pPlugin->IsResumed())
869 {
870 pPlugin->NotifySongPlaying(false);
871 pPlugin->HardAllNotesOff();
872 pPlugin->Suspend();
873 }
874 }
875 #endif // NO_PLUGINS
876 }
877
878 void CSoundFile::ResumePlugins()
879 {
880 #ifndef NO_PLUGINS
881 for(auto &plugin : m_MixPlugins)
882 {
883 IMixPlugin *pPlugin = plugin.pMixPlugin;
884 if(pPlugin != nullptr && !pPlugin->IsResumed())
885 {
886 pPlugin->NotifySongPlaying(true);
887 pPlugin->Resume();
888 }
889 }
890 #endif // NO_PLUGINS
891 }
892
893
894 void CSoundFile::StopAllVsti()
895 {
896 #ifndef NO_PLUGINS
897 for(auto &plugin : m_MixPlugins)
898 {
899 IMixPlugin *pPlugin = plugin.pMixPlugin;
900 if(pPlugin != nullptr && pPlugin->IsResumed())
901 {
902 pPlugin->HardAllNotesOff();
903 }
904 }
905 #endif // NO_PLUGINS
906 }
907
908
909 void CSoundFile::SetMixLevels(MixLevels levels)
910 {
911 m_nMixLevels = levels;
912 m_PlayConfig.SetMixLevels(m_nMixLevels);
913 RecalculateGainForAllPlugs();
914 }
915
916
917 void CSoundFile::RecalculateGainForAllPlugs()
918 {
919 #ifndef NO_PLUGINS
920 for(auto &plugin : m_MixPlugins)
921 {
922 if(plugin.pMixPlugin != nullptr)
923 plugin.pMixPlugin->RecalculateGain();
924 }
925 #endif // NO_PLUGINS
926 }
927
928
929 void CSoundFile::ResetChannels()
930 {
931 m_SongFlags.reset(SONG_FADINGSONG | SONG_ENDREACHED);
932 m_PlayState.m_nBufferCount = 0;
933 for(auto &chn : m_PlayState.Chn)
934 {
935 chn.nROfs = chn.nLOfs = 0;
936 chn.nLength = 0;
937 if(chn.dwFlags[CHN_ADLIB] && m_opl)
938 {
939 CHANNELINDEX c = static_cast<CHANNELINDEX>(std::distance(std::begin(m_PlayState.Chn), &chn));
940 m_opl->NoteCut(c);
941 }
942 }
943 }
944
945
946 #ifdef MODPLUG_TRACKER
947
948 void CSoundFile::PatternTranstionChnSolo(const CHANNELINDEX chnIndex)
949 {
950 if(chnIndex >= m_nChannels)
951 return;
952
953 for(CHANNELINDEX i = 0; i < m_nChannels; i++)
954 {
955 m_bChannelMuteTogglePending[i] = !ChnSettings[i].dwFlags[CHN_MUTE];
956 }
957 m_bChannelMuteTogglePending[chnIndex] = ChnSettings[chnIndex].dwFlags[CHN_MUTE];
958 }
959
960
961 void CSoundFile::PatternTransitionChnUnmuteAll()
962 {
963 for(CHANNELINDEX i = 0; i < m_nChannels; i++)
964 {
965 m_bChannelMuteTogglePending[i] = ChnSettings[i].dwFlags[CHN_MUTE];
966 }
967 }
968
969 #endif // MODPLUG_TRACKER
970
971
972 void CSoundFile::LoopPattern(PATTERNINDEX nPat, ROWINDEX nRow)
973 {
974 if(!Patterns.IsValidPat(nPat))
975 {
976 m_SongFlags.reset(SONG_PATTERNLOOP);
977 } else
978 {
979 if(nRow >= Patterns[nPat].GetNumRows()) nRow = 0;
980 m_PlayState.m_nPattern = nPat;
981 m_PlayState.m_nRow = m_PlayState.m_nNextRow = nRow;
982 m_PlayState.m_nTickCount = TICKS_ROW_FINISHED;
983 m_PlayState.m_nPatternDelay = 0;
984 m_PlayState.m_nFrameDelay = 0;
985 m_PlayState.m_nextPatStartRow = 0;
986 m_SongFlags.set(SONG_PATTERNLOOP);
987 }
988 m_PlayState.m_nBufferCount = 0;
989 }
990
991
992 void CSoundFile::DontLoopPattern(PATTERNINDEX nPat, ROWINDEX nRow)
993 {
994 if(!Patterns.IsValidPat(nPat)) nPat = 0;
995 if(nRow >= Patterns[nPat].GetNumRows()) nRow = 0;
996 m_PlayState.m_nPattern = nPat;
997 m_PlayState.m_nRow = m_PlayState.m_nNextRow = nRow;
998 m_PlayState.m_nTickCount = TICKS_ROW_FINISHED;
999 m_PlayState.m_nPatternDelay = 0;
1000 m_PlayState.m_nFrameDelay = 0;
1001 m_PlayState.m_nBufferCount = 0;
1002 m_PlayState.m_nextPatStartRow = 0;
1003 m_SongFlags.reset(SONG_PATTERNLOOP);
1004 }
1005
1006
1007 void CSoundFile::SetDefaultPlaybackBehaviour(MODTYPE type)
1008 {
1009 m_playBehaviour = GetDefaultPlaybackBehaviour(type);
1010 }
1011
1012
1013 PlayBehaviourSet CSoundFile::GetSupportedPlaybackBehaviour(MODTYPE type)
1014 {
1015 PlayBehaviourSet playBehaviour;
1016 switch(type)
1017 {
1018 case MOD_TYPE_MPT:
1019 case MOD_TYPE_IT:
1020 playBehaviour.set(MSF_COMPATIBLE_PLAY);
1021 playBehaviour.set(kPeriodsAreHertz);
1022 playBehaviour.set(kTempoClamp);
1023 playBehaviour.set(kPerChannelGlobalVolSlide);
1024 playBehaviour.set(kPanOverride);
1025 playBehaviour.set(kITInstrWithoutNote);
1026 playBehaviour.set(kITVolColFinePortamento);
1027 playBehaviour.set(kITArpeggio);
1028 playBehaviour.set(kITOutOfRangeDelay);
1029 playBehaviour.set(kITPortaMemoryShare);
1030 playBehaviour.set(kITPatternLoopTargetReset);
1031 playBehaviour.set(kITFT2PatternLoop);
1032 playBehaviour.set(kITPingPongNoReset);
1033 playBehaviour.set(kITEnvelopeReset);
1034 playBehaviour.set(kITClearOldNoteAfterCut);
1035 playBehaviour.set(kITVibratoTremoloPanbrello);
1036 playBehaviour.set(kITTremor);
1037 playBehaviour.set(kITRetrigger);
1038 playBehaviour.set(kITMultiSampleBehaviour);
1039 playBehaviour.set(kITPortaTargetReached);
1040 playBehaviour.set(kITPatternLoopBreak);
1041 playBehaviour.set(kITOffset);
1042 playBehaviour.set(kITSwingBehaviour);
1043 playBehaviour.set(kITNNAReset);
1044 playBehaviour.set(kITSCxStopsSample);
1045 playBehaviour.set(kITEnvelopePositionHandling);
1046 playBehaviour.set(kITPortamentoInstrument);
1047 playBehaviour.set(kITPingPongMode);
1048 playBehaviour.set(kITRealNoteMapping);
1049 playBehaviour.set(kITHighOffsetNoRetrig);
1050 playBehaviour.set(kITFilterBehaviour);
1051 playBehaviour.set(kITNoSurroundPan);
1052 playBehaviour.set(kITShortSampleRetrig);
1053 playBehaviour.set(kITPortaNoNote);
1054 playBehaviour.set(kITFT2DontResetNoteOffOnPorta);
1055 playBehaviour.set(kITVolColMemory);
1056 playBehaviour.set(kITPortamentoSwapResetsPos);
1057 playBehaviour.set(kITEmptyNoteMapSlot);
1058 playBehaviour.set(kITFirstTickHandling);
1059 playBehaviour.set(kITSampleAndHoldPanbrello);
1060 playBehaviour.set(kITClearPortaTarget);
1061 playBehaviour.set(kITPanbrelloHold);
1062 playBehaviour.set(kITPanningReset);
1063 playBehaviour.set(kITPatternLoopWithJumps);
1064 playBehaviour.set(kITInstrWithNoteOff);
1065 playBehaviour.set(kITMultiSampleInstrumentNumber);
1066 playBehaviour.set(kRowDelayWithNoteDelay);
1067 playBehaviour.set(kITInstrWithNoteOffOldEffects);
1068 playBehaviour.set(kITDoNotOverrideChannelPan);
1069 playBehaviour.set(kITDCTBehaviour);
1070 playBehaviour.set(kITPitchPanSeparation);
1071 if(type == MOD_TYPE_MPT)
1072 {
1073 playBehaviour.set(kOPLFlexibleNoteOff);
1074 playBehaviour.set(kOPLwithNNA);
1075 playBehaviour.set(kOPLNoteOffOnNoteChange);
1076 }
1077 break;
1078
1079 case MOD_TYPE_XM:
1080 playBehaviour.set(MSF_COMPATIBLE_PLAY);
1081 playBehaviour.set(kFT2VolumeRamping);
1082 playBehaviour.set(kTempoClamp);
1083 playBehaviour.set(kPerChannelGlobalVolSlide);
1084 playBehaviour.set(kPanOverride);
1085 playBehaviour.set(kITFT2PatternLoop);
1086 playBehaviour.set(kITFT2DontResetNoteOffOnPorta);
1087 playBehaviour.set(kFT2Arpeggio);
1088 playBehaviour.set(kFT2Retrigger);
1089 playBehaviour.set(kFT2VolColVibrato);
1090 playBehaviour.set(kFT2PortaNoNote);
1091 playBehaviour.set(kFT2KeyOff);
1092 playBehaviour.set(kFT2PanSlide);
1093 playBehaviour.set(kFT2ST3OffsetOutOfRange);
1094 playBehaviour.set(kFT2RestrictXCommand);
1095 playBehaviour.set(kFT2RetrigWithNoteDelay);
1096 playBehaviour.set(kFT2SetPanEnvPos);
1097 playBehaviour.set(kFT2PortaIgnoreInstr);
1098 playBehaviour.set(kFT2VolColMemory);
1099 playBehaviour.set(kFT2LoopE60Restart);
1100 playBehaviour.set(kFT2ProcessSilentChannels);
1101 playBehaviour.set(kFT2ReloadSampleSettings);
1102 playBehaviour.set(kFT2PortaDelay);
1103 playBehaviour.set(kFT2Transpose);
1104 playBehaviour.set(kFT2PatternLoopWithJumps);
1105 playBehaviour.set(kFT2PortaTargetNoReset);
1106 playBehaviour.set(kFT2EnvelopeEscape);
1107 playBehaviour.set(kFT2Tremor);
1108 playBehaviour.set(kFT2OutOfRangeDelay);
1109 playBehaviour.set(kFT2Periods);
1110 playBehaviour.set(kFT2PanWithDelayedNoteOff);
1111 playBehaviour.set(kFT2VolColDelay);
1112 playBehaviour.set(kFT2FinetunePrecision);
1113 playBehaviour.set(kFT2NoteOffFlags);
1114 playBehaviour.set(kRowDelayWithNoteDelay);
1115 playBehaviour.set(kFT2MODTremoloRampWaveform);
1116 playBehaviour.set(kFT2PortaUpDownMemory);
1117 playBehaviour.set(kFT2PanSustainRelease);
1118 playBehaviour.set(kFT2NoteDelayWithoutInstr);
1119 playBehaviour.set(kFT2PortaResetDirection);
1120 break;
1121
1122 case MOD_TYPE_S3M:
1123 playBehaviour.set(MSF_COMPATIBLE_PLAY);
1124 playBehaviour.set(kTempoClamp);
1125 playBehaviour.set(kPanOverride);
1126 playBehaviour.set(kITPanbrelloHold);
1127 playBehaviour.set(kFT2ST3OffsetOutOfRange);
1128 playBehaviour.set(kST3NoMutedChannels);
1129 playBehaviour.set(kST3PortaSampleChange);
1130 playBehaviour.set(kST3EffectMemory);
1131 playBehaviour.set(kST3VibratoMemory);
1132 playBehaviour.set(KST3PortaAfterArpeggio);
1133 playBehaviour.set(kRowDelayWithNoteDelay);
1134 playBehaviour.set(kST3OffsetWithoutInstrument);
1135 playBehaviour.set(kST3RetrigAfterNoteCut);
1136 playBehaviour.set(kST3SampleSwap);
1137 playBehaviour.set(kOPLNoteOffOnNoteChange);
1138 playBehaviour.set(kApplyUpperPeriodLimit);
1139 break;
1140
1141 case MOD_TYPE_MOD:
1142 playBehaviour.set(kMODVBlankTiming);
1143 playBehaviour.set(kMODOneShotLoops);
1144 playBehaviour.set(kMODIgnorePanning);
1145 playBehaviour.set(kMODSampleSwap);
1146 playBehaviour.set(kMODOutOfRangeNoteDelay);
1147 playBehaviour.set(kMODTempoOnSecondTick);
1148 playBehaviour.set(kRowDelayWithNoteDelay);
1149 playBehaviour.set(kFT2MODTremoloRampWaveform);
1150 break;
1151
1152 default:
1153 playBehaviour.set(MSF_COMPATIBLE_PLAY);
1154 playBehaviour.set(kPeriodsAreHertz);
1155 playBehaviour.set(kTempoClamp);
1156 playBehaviour.set(kPanOverride);
1157 break;
1158 }
1159 return playBehaviour;
1160 }
1161
1162
1163 PlayBehaviourSet CSoundFile::GetDefaultPlaybackBehaviour(MODTYPE type)
1164 {
1165 PlayBehaviourSet playBehaviour;
1166 switch(type)
1167 {
1168 case MOD_TYPE_MPT:
1169 playBehaviour.set(kPeriodsAreHertz);
1170 playBehaviour.set(kPerChannelGlobalVolSlide);
1171 playBehaviour.set(kPanOverride);
1172 playBehaviour.set(kITArpeggio);
1173 playBehaviour.set(kITPortaMemoryShare);
1174 playBehaviour.set(kITPatternLoopTargetReset);
1175 playBehaviour.set(kITFT2PatternLoop);
1176 playBehaviour.set(kITPingPongNoReset);
1177 playBehaviour.set(kITClearOldNoteAfterCut);
1178 playBehaviour.set(kITVibratoTremoloPanbrello);
1179 playBehaviour.set(kITMultiSampleBehaviour);
1180 playBehaviour.set(kITPortaTargetReached);
1181 playBehaviour.set(kITPatternLoopBreak);
1182 playBehaviour.set(kITSwingBehaviour);
1183 playBehaviour.set(kITSCxStopsSample);
1184 playBehaviour.set(kITEnvelopePositionHandling);
1185 playBehaviour.set(kITPingPongMode);
1186 playBehaviour.set(kITRealNoteMapping);
1187 playBehaviour.set(kITPortaNoNote);
1188 playBehaviour.set(kITVolColMemory);
1189 playBehaviour.set(kITFirstTickHandling);
1190 playBehaviour.set(kITClearPortaTarget);
1191 playBehaviour.set(kITSampleAndHoldPanbrello);
1192 playBehaviour.set(kITPanbrelloHold);
1193 playBehaviour.set(kITPanningReset);
1194 playBehaviour.set(kITInstrWithNoteOff);
1195 playBehaviour.set(kOPLFlexibleNoteOff);
1196 playBehaviour.set(kITDoNotOverrideChannelPan);
1197 playBehaviour.set(kITDCTBehaviour);
1198 playBehaviour.set(kOPLwithNNA);
1199 playBehaviour.set(kITPitchPanSeparation);
1200 break;
1201
1202 case MOD_TYPE_S3M:
1203 playBehaviour = GetSupportedPlaybackBehaviour(type);
1204 // Default behaviour was chosen to follow GUS, so kST3PortaSampleChange is enabled and kST3SampleSwap is disabled.
1205 // For SoundBlaster behaviour, those two flags would need to be swapped.
1206 playBehaviour.reset(kST3SampleSwap);
1207 break;
1208
1209 case MOD_TYPE_XM:
1210 playBehaviour = GetSupportedPlaybackBehaviour(type);
1211 // Only set this explicitely for FT2-made XMs.
1212 playBehaviour.reset(kFT2VolumeRamping);
1213 break;
1214
1215 case MOD_TYPE_MOD:
1216 playBehaviour.set(kRowDelayWithNoteDelay);
1217 break;
1218
1219 default:
1220 playBehaviour = GetSupportedPlaybackBehaviour(type);
1221 break;
1222 }
1223 return playBehaviour;
1224 }
1225
1226
1227 MODTYPE CSoundFile::GetBestSaveFormat() const
1228 {
1229 switch(GetType())
1230 {
1231 case MOD_TYPE_MOD:
1232 case MOD_TYPE_S3M:
1233 case MOD_TYPE_XM:
1234 case MOD_TYPE_IT:
1235 case MOD_TYPE_MPT:
1236 return GetType();
1237 case MOD_TYPE_AMF0:
1238 case MOD_TYPE_DIGI:
1239 case MOD_TYPE_SFX:
1240 case MOD_TYPE_STP:
1241 return MOD_TYPE_MOD;
1242 case MOD_TYPE_MED:
1243 if(!m_nInstruments)
1244 {
1245 for(const auto &pat : Patterns)
1246 {
1247 if(pat.IsValid() && pat.GetNumRows() != 64)
1248 return MOD_TYPE_XM;
1249 }
1250 return MOD_TYPE_MOD;
1251 }
1252 return MOD_TYPE_XM;
1253 case MOD_TYPE_PSM:
1254 if(GetNumChannels() > 16)
1255 return MOD_TYPE_IT;
1256 for(CHANNELINDEX i = 0; i < GetNumChannels(); i++)
1257 {
1258 if(ChnSettings[i].dwFlags[CHN_SURROUND] || ChnSettings[i].nVolume != 64)
1259 {
1260 return MOD_TYPE_IT;
1261 break;
1262 }
1263 }
1264 return MOD_TYPE_S3M;
1265 case MOD_TYPE_669:
1266 case MOD_TYPE_FAR:
1267 case MOD_TYPE_STM:
1268 case MOD_TYPE_DSM:
1269 case MOD_TYPE_AMF:
1270 case MOD_TYPE_MTM:
1271 return MOD_TYPE_S3M;
1272 case MOD_TYPE_AMS:
1273 case MOD_TYPE_DMF:
1274 case MOD_TYPE_DBM:
1275 case MOD_TYPE_IMF:
1276 case MOD_TYPE_J2B:
1277 case MOD_TYPE_ULT:
1278 case MOD_TYPE_OKT:
1279 case MOD_TYPE_MT2:
1280 case MOD_TYPE_MDL:
1281 case MOD_TYPE_PTM:
1282 case MOD_TYPE_DTM:
1283 default:
1284 return MOD_TYPE_IT;
1285 case MOD_TYPE_MID:
1286 return MOD_TYPE_MPT;
1287 }
1288 }
1289
1290
1291 const char *CSoundFile::GetSampleName(SAMPLEINDEX nSample) const
1292 {
1293 MPT_ASSERT(nSample <= GetNumSamples());
1294 if (nSample < MAX_SAMPLES)
1295 {
1296 return m_szNames[nSample].buf;
1297 } else
1298 {
1299 return "";
1300 }
1301 }
1302
1303
1304 const char *CSoundFile::GetInstrumentName(INSTRUMENTINDEX nInstr) const
1305 {
1306 if((nInstr >= MAX_INSTRUMENTS) || (!Instruments[nInstr]))
1307 return "";
1308
1309 MPT_ASSERT(nInstr <= GetNumInstruments());
1310 return Instruments[nInstr]->name.buf;
1311 }
1312
1313
1314 bool CSoundFile::InitChannel(CHANNELINDEX nChn)
1315 {
1316 if(nChn >= MAX_BASECHANNELS)
1317 return true;
1318
1319 ChnSettings[nChn].Reset();
1320 m_PlayState.Chn[nChn].Reset(ModChannel::resetTotal, *this, nChn, GetChannelMuteFlag());
1321
1322 #ifdef MODPLUG_TRACKER
1323 if(GetpModDoc() != nullptr)
1324 {
1325 GetpModDoc()->SetChannelRecordGroup(nChn, RecordGroup::NoGroup);
1326 }
1327 #endif // MODPLUG_TRACKER
1328
1329 #ifdef MODPLUG_TRACKER
1330 m_bChannelMuteTogglePending[nChn] = false;
1331 #endif // MODPLUG_TRACKER
1332
1333 return false;
1334 }
1335
1336
1337 void CSoundFile::InitAmigaResampler()
1338 {
1339 if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off)
1340 {
1341 const Paula::State defaultState(GetSampleRate());
1342 for(auto &chn : m_PlayState.Chn)
1343 {
1344 chn.paulaState = defaultState;
1345 }
1346 }
1347 }
1348
1349
1350 void CSoundFile::InitOPL()
1351 {
1352 if(!m_opl)
1353 m_opl = std::make_unique<OPL>(m_MixerSettings.gdwMixingFreq);
1354 }
1355
1356
1357 // Detect samples that are referenced by an instrument, but actually not used in a song.
1358 // Only works in instrument mode. Unused samples are marked as false in the vector.
1359 SAMPLEINDEX CSoundFile::DetectUnusedSamples(std::vector<bool> &sampleUsed) const
1360 {
1361 sampleUsed.assign(GetNumSamples() + 1, false);
1362
1363 if(GetNumInstruments() == 0)
1364 {
1365 return 0;
1366 }
1367 SAMPLEINDEX unused = 0;
1368 std::vector<ModCommand::INSTR> lastIns;
1369
1370 for(const auto &pat : Patterns) if(pat.IsValid())
1371 {
1372 lastIns.assign(GetNumChannels(), 0);
1373 auto p = pat.cbegin();
1374 for(ROWINDEX row = 0; row < pat.GetNumRows(); row++)
1375 {
1376 for(CHANNELINDEX c = 0; c < GetNumChannels(); c++, p++)
1377 {
1378 if(p->IsNote())
1379 {
1380 ModCommand::INSTR instr = p->instr;
1381 if(!p->instr)
1382 instr = lastIns[c];
1383 INSTRUMENTINDEX minInstr = 1, maxInstr = GetNumInstruments();
1384 if(instr > 0)
1385 {
1386 if(instr <= GetNumInstruments())
1387 {
1388 minInstr = maxInstr = instr;
1389 }
1390 lastIns[c] = instr;
1391 } else
1392 {
1393 // No idea which instrument this note belongs to, so mark it used in any instruments.
1394 }
1395 for(INSTRUMENTINDEX i = minInstr; i <= maxInstr; i++)
1396 {
1397 if(const auto *pIns = Instruments[i]; pIns != nullptr)
1398 {
1399 SAMPLEINDEX n = pIns->Keyboard[p->note - NOTE_MIN];
1400 if(n <= GetNumSamples())
1401 sampleUsed[n] = true;
1402 }
1403 }
1404 }
1405 }
1406 }
1407 }
1408 for (SAMPLEINDEX ichk = GetNumSamples(); ichk >= 1; ichk--)
1409 {
1410 if ((!sampleUsed[ichk]) && (Samples[ichk].HasSampleData())) unused++;
1411 }
1412
1413 return unused;
1414 }
1415
1416
1417 // Destroy samples where keepSamples index is false. First sample is keepSamples[1]!
1418 SAMPLEINDEX CSoundFile::RemoveSelectedSamples(const std::vector<bool> &keepSamples)
1419 {
1420 if(keepSamples.empty())
1421 {
1422 return 0;
1423 }
1424
1425 SAMPLEINDEX nRemoved = 0;
1426 for(SAMPLEINDEX nSmp = std::min(GetNumSamples(), static_cast<SAMPLEINDEX>(keepSamples.size() - 1)); nSmp >= 1; nSmp--)
1427 {
1428 if(!keepSamples[nSmp])
1429 {
1430 CriticalSection cs;
1431
1432 #ifdef MODPLUG_TRACKER
1433 if(GetpModDoc())
1434 {
1435 GetpModDoc()->GetSampleUndo().PrepareUndo(nSmp, sundo_replace, "Remove Sample");
1436 }
1437 #endif // MODPLUG_TRACKER
1438
1439 if(DestroySample(nSmp))
1440 {
1441 m_szNames[nSmp] = "";
1442 nRemoved++;
1443 }
1444 if((nSmp == GetNumSamples()) && (nSmp > 1)) m_nSamples--;
1445 }
1446 }
1447 return nRemoved;
1448 }
1449
1450
1451 bool CSoundFile::DestroySample(SAMPLEINDEX nSample)
1452 {
1453 if(!nSample || nSample >= MAX_SAMPLES)
1454 {
1455 return false;
1456 }
1457 if(!Samples[nSample].HasSampleData())
1458 {
1459 return true;
1460 }
1461
1462 ModSample &sample = Samples[nSample];
1463
1464 for(auto &chn : m_PlayState.Chn)
1465 {
1466 if(chn.pModSample == &sample)
1467 {
1468 chn.position.Set(0);
1469 chn.nLength = 0;
1470 chn.pCurrentSample = nullptr;
1471 }
1472 }
1473
1474 sample.FreeSample();
1475 sample.nLength = 0;
1476 sample.uFlags.reset(CHN_16BIT | CHN_STEREO);
1477 sample.SetAdlib(false);
1478
1479 #ifdef MODPLUG_TRACKER
1480 ResetSamplePath(nSample);
1481 #endif
1482 return true;
1483 }
1484
1485
1486 bool CSoundFile::DestroySampleThreadsafe(SAMPLEINDEX nSample)
1487 {
1488 CriticalSection cs;
1489 return DestroySample(nSample);
1490 }
1491
1492
1493 std::unique_ptr<CTuning> CSoundFile::CreateTuning12TET(const mpt::ustring &name)
1494 {
1495 std::unique_ptr<CTuning> pT = CTuning::CreateGeometric(name, 12, 2, 15);
1496 for(ModCommand::NOTE note = 0; note < 12; ++note)
1497 {
1498 pT->SetNoteName(note, mpt::ustring(NoteNamesSharp[note]));
1499 }
1500 return pT;
1501 }
1502
1503
1504 mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const INSTRUMENTINDEX inst) const
1505 {
1506 // For MPTM instruments with custom tuning, find the appropriate note name. Else, use default note names.
1507 if(ModCommand::IsNote(note) && GetType() == MOD_TYPE_MPT && inst >= 1 && inst <= GetNumInstruments() && Instruments[inst] && Instruments[inst]->pTuning)
1508 {
1509 return Instruments[inst]->pTuning->GetNoteName(note - NOTE_MIDDLEC);
1510 } else
1511 {
1512 return GetNoteName(note);
1513 }
1514 }
1515
1516
1517 mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note) const
1518 {
1519 return GetNoteName(note, m_NoteNames);
1520 }
1521
1522
1523 mpt::ustring CSoundFile::GetNoteName(const ModCommand::NOTE note, const NoteName *noteNames)
1524 {
1525 if(ModCommand::IsSpecialNote(note))
1526 {
1527 // cppcheck false-positive
1528 // cppcheck-suppress constStatement
1529 const mpt::uchar specialNoteNames[][4] = { UL_("PCs"), UL_("PC "), UL_("~~~"), UL_("^^^"), UL_("===") };
1530 static_assert(mpt::array_size<decltype(specialNoteNames)>::size == NOTE_MAX_SPECIAL - NOTE_MIN_SPECIAL + 1);
1531 return specialNoteNames[note - NOTE_MIN_SPECIAL];
1532 } else if(ModCommand::IsNote(note))
1533 {
1534 return mpt::ustring()
1535 .append(noteNames[(note - NOTE_MIN) % 12])
1536 .append(1, UC_('0') + (note - NOTE_MIN) / 12)
1537 ; // e.g. "C#" + "5"
1538 } else if(note == NOTE_NONE)
1539 {
1540 return UL_("...");
1541 }
1542 return UL_("???");
1543 }
1544
1545
1546 #ifdef MODPLUG_TRACKER
1547
1548 void CSoundFile::SetDefaultNoteNames()
1549 {
1550 m_NoteNames = TrackerSettings::Instance().accidentalFlats ? NoteNamesFlat : NoteNamesSharp;
1551 }
1552
1553 const NoteName *CSoundFile::GetDefaultNoteNames()
1554 {
1555 return m_NoteNames;
1556 }
1557
1558 #endif // MODPLUG_TRACKER
1559
1560
1561 void CSoundFile::SetModSpecsPointer(const CModSpecifications*& pModSpecs, const MODTYPE type)
1562 {
1563 switch(type)
1564 {
1565 case MOD_TYPE_MPT:
1566 pModSpecs = &ModSpecs::mptm;
1567 break;
1568
1569 case MOD_TYPE_IT:
1570 pModSpecs = &ModSpecs::itEx;
1571 break;
1572
1573 case MOD_TYPE_XM:
1574 pModSpecs = &ModSpecs::xmEx;
1575 break;
1576
1577 case MOD_TYPE_S3M:
1578 pModSpecs = &ModSpecs::s3mEx;
1579 break;
1580
1581 case MOD_TYPE_MOD:
1582 default:
1583 pModSpecs = &ModSpecs::mod;
1584 break;
1585 }
1586 }
1587
1588
1589 void CSoundFile::SetType(MODTYPE type)
1590 {
1591 m_nType = type;
1592 m_playBehaviour = GetDefaultPlaybackBehaviour(GetBestSaveFormat());
1593 SetModSpecsPointer(m_pModSpecs, GetBestSaveFormat());
1594 }
1595
1596
1597 #ifdef MODPLUG_TRACKER
1598
1599 void CSoundFile::ChangeModTypeTo(const MODTYPE newType, bool adjust)
1600 {
1601 const MODTYPE oldType = GetType();
1602 m_nType = newType;
1603 SetModSpecsPointer(m_pModSpecs, m_nType);
1604
1605 if(oldType == newType || !adjust)
1606 return;
1607
1608 SetupMODPanning(); // Setup LRRL panning scheme if needed
1609
1610 // Only keep supported play behaviour flags
1611 PlayBehaviourSet oldAllowedFlags = GetSupportedPlaybackBehaviour(oldType);
1612 PlayBehaviourSet newAllowedFlags = GetSupportedPlaybackBehaviour(newType);
1613 PlayBehaviourSet newDefaultFlags = GetDefaultPlaybackBehaviour(newType);
1614 for(size_t i = 0; i < m_playBehaviour.size(); i++)
1615 {
1616 // If a flag is supported in both formats, keep its status
1617 if(m_playBehaviour[i]) m_playBehaviour.set(i, newAllowedFlags[i]);
1618 // Set allowed flags to their defaults if they were not supported in the old format
1619 if(!oldAllowedFlags[i]) m_playBehaviour.set(i, newDefaultFlags[i]);
1620 }
1621 // Special case for OPL behaviour when converting from S3M to MPTM to retain S3M-like note-off behaviour
1622 if(oldType == MOD_TYPE_S3M && newType == MOD_TYPE_MPT && m_opl)
1623 m_playBehaviour.reset(kOPLFlexibleNoteOff);
1624
1625 Order.OnModTypeChanged(oldType);
1626 Patterns.OnModTypeChanged(oldType);
1627
1628 m_modFormat.type = mpt::ToUnicode(mpt::Charset::UTF8, GetModSpecifications().fileExtension);
1629 }
1630
1631 #endif // MODPLUG_TRACKER
1632
1633
1634 ModMessageHeuristicOrder CSoundFile::GetMessageHeuristic() const
1635 {
1636 ModMessageHeuristicOrder result = ModMessageHeuristicOrder::Default;
1637 switch(GetType())
1638 {
1639 case MOD_TYPE_MPT:
1640 result = ModMessageHeuristicOrder::Samples;
1641 break;
1642 case MOD_TYPE_IT:
1643 result = ModMessageHeuristicOrder::Samples;
1644 break;
1645 case MOD_TYPE_XM:
1646 result = ModMessageHeuristicOrder::InstrumentsSamples;
1647 break;
1648 case MOD_TYPE_MDL:
1649 result = ModMessageHeuristicOrder::InstrumentsSamples;
1650 break;
1651 case MOD_TYPE_IMF:
1652 result = ModMessageHeuristicOrder::InstrumentsSamples;
1653 break;
1654 default:
1655 result = ModMessageHeuristicOrder::Default;
1656 break;
1657 }
1658 return result;
1659 }
1660
1661
1662 bool CSoundFile::SetTitle(const std::string &newTitle)
1663 {
1664 if(m_songName != newTitle)
1665 {
1666 m_songName = newTitle;
1667 return true;
1668 }
1669 return false;
1670 }
1671
1672
1673 double CSoundFile::GetPlaybackTimeAt(ORDERINDEX ord, ROWINDEX row, bool updateVars, bool updateSamplePos)
1674 {
1675 const GetLengthType t = GetLength(updateVars ? (updateSamplePos ? eAdjustSamplePositions : eAdjust) : eNoAdjust, GetLengthTarget(ord, row)).back();
1676 if(t.targetReached) return t.duration;
1677 else return -1; //Given position not found from play sequence.
1678 }
1679
1680
1681 std::vector<SubSong> CSoundFile::GetAllSubSongs()
1682 {
1683 std::vector<SubSong> subSongs;
1684 for(SEQUENCEINDEX seq = 0; seq < Order.GetNumSequences(); seq++)
1685 {
1686 const auto subSongsSeq = GetLength(eNoAdjust, GetLengthTarget(true).StartPos(seq, 0, 0));
1687 subSongs.reserve(subSongs.size() + subSongsSeq.size());
1688 for(const auto &song : subSongsSeq)
1689 {
1690 subSongs.push_back({song.duration, song.startRow, song.endRow, song.lastRow, song.startOrder, song.endOrder, song.lastOrder, seq});
1691 }
1692 }
1693 return subSongs;
1694 }
1695
1696
1697 // Calculate the length of a tick, depending on the tempo mode.
1698 // This differs from GetTickDuration() by not accumulating errors
1699 // because this is not called once per tick but in unrelated
1700 // circumstances. So this should not update error accumulation.
1701 void CSoundFile::RecalculateSamplesPerTick()
1702 {
1703 switch(m_nTempoMode)
1704 {
1705 case TempoMode::Classic:
1706 default:
1707 m_PlayState.m_nSamplesPerTick = Util::muldiv(m_MixerSettings.gdwMixingFreq, 5 * TEMPO::fractFact, std::max(TEMPO::store_t(1), m_PlayState.m_nMusicTempo.GetRaw() << 1));
1708 break;
1709
1710 case TempoMode::Modern:
1711 m_PlayState.m_nSamplesPerTick = static_cast<uint32>((Util::mul32to64_unsigned(m_MixerSettings.gdwMixingFreq, 60 * TEMPO::fractFact) / std::max(uint64(1), Util::mul32to64_unsigned(m_PlayState.m_nMusicSpeed, m_PlayState.m_nCurrentRowsPerBeat) * m_PlayState.m_nMusicTempo.GetRaw())));
1712 break;
1713
1714 case TempoMode::Alternative:
1715 m_PlayState.m_nSamplesPerTick = Util::muldiv(m_MixerSettings.gdwMixingFreq, TEMPO::fractFact, std::max(TEMPO::store_t(1), m_PlayState.m_nMusicTempo.GetRaw()));
1716 break;
1717 }
1718 #ifndef MODPLUG_TRACKER
1719 m_PlayState.m_nSamplesPerTick = Util::muldivr(m_PlayState.m_nSamplesPerTick, m_nTempoFactor, 65536);
1720 #endif // !MODPLUG_TRACKER
1721 if(!m_PlayState.m_nSamplesPerTick)
1722 m_PlayState.m_nSamplesPerTick = 1;
1723 }
1724
1725
1726 // Get length of a tick in sample, with tick-to-tick tempo correction in modern tempo mode.
1727 // This has to be called exactly once per tick because otherwise the error accumulation
1728 // goes wrong.
1729 uint32 CSoundFile::GetTickDuration(PlayState &playState) const
1730 {
1731 uint32 retval = 0;
1732 switch(m_nTempoMode)
1733 {
1734 case TempoMode::Classic:
1735 default:
1736 retval = Util::muldiv(m_MixerSettings.gdwMixingFreq, 5 * TEMPO::fractFact, std::max(TEMPO::store_t(1), playState.m_nMusicTempo.GetRaw() << 1));
1737 break;
1738
1739 case TempoMode::Alternative:
1740 retval = Util::muldiv(m_MixerSettings.gdwMixingFreq, TEMPO::fractFact, std::max(TEMPO::store_t(1), playState.m_nMusicTempo.GetRaw()));
1741 break;
1742
1743 case TempoMode::Modern:
1744 {
1745 double accurateBufferCount = static_cast<double>(m_MixerSettings.gdwMixingFreq) * (60.0 / (playState.m_nMusicTempo.ToDouble() * Util::mul32to64_unsigned(playState.m_nMusicSpeed, playState.m_nCurrentRowsPerBeat)));
1746 const TempoSwing &swing = (Patterns.IsValidPat(playState.m_nPattern) && Patterns[playState.m_nPattern].HasTempoSwing())
1747 ? Patterns[playState.m_nPattern].GetTempoSwing()
1748 : m_tempoSwing;
1749 if(!swing.empty())
1750 {
1751 // Apply current row's tempo swing factor
1752 TempoSwing::value_type swingFactor = swing[playState.m_nRow % swing.size()];
1753 accurateBufferCount = accurateBufferCount * swingFactor / double(TempoSwing::Unity);
1754 }
1755 uint32 bufferCount = static_cast<int>(accurateBufferCount);
1756 playState.m_dBufferDiff += accurateBufferCount - bufferCount;
1757
1758 //tick-to-tick tempo correction:
1759 if(playState.m_dBufferDiff >= 1)
1760 {
1761 bufferCount++;
1762 playState.m_dBufferDiff--;
1763 } else if(m_PlayState.m_dBufferDiff <= -1)
1764 {
1765 bufferCount--;
1766 playState.m_dBufferDiff++;
1767 }
1768 MPT_ASSERT(std::abs(playState.m_dBufferDiff) < 1.0);
1769 retval = bufferCount;
1770 }
1771 break;
1772 }
1773 #ifndef MODPLUG_TRACKER
1774 // when the user modifies the tempo, we do not really care about accurate tempo error accumulation
1775 retval = Util::muldivr_unsigned(retval, m_nTempoFactor, 65536);
1776 #endif // !MODPLUG_TRACKER
1777 if(!retval)
1778 retval = 1;
1779 return retval;
1780 }
1781
1782
1783 // Get the duration of a row in milliseconds, based on the current rows per beat and given speed and tempo settings.
1784 double CSoundFile::GetRowDuration(TEMPO tempo, uint32 speed) const
1785 {
1786 switch(m_nTempoMode)
1787 {
1788 case TempoMode::Classic:
1789 default:
1790 return static_cast<double>(2500 * speed) / tempo.ToDouble();
1791
1792 case TempoMode::Modern:
1793 {
1794 // If there are any row delay effects, the row length factor compensates for those.
1795 return 60000.0 / tempo.ToDouble() / static_cast<double>(m_PlayState.m_nCurrentRowsPerBeat);
1796 }
1797
1798 case TempoMode::Alternative:
1799 return static_cast<double>(1000 * speed) / tempo.ToDouble();
1800 }
1801 }
1802
1803
1804 const CModSpecifications& CSoundFile::GetModSpecifications(const MODTYPE type)
1805 {
1806 const CModSpecifications* p = nullptr;
1807 SetModSpecsPointer(p, type);
1808 return *p;
1809 }
1810
1811
1812 ChannelFlags CSoundFile::GetChannelMuteFlag()
1813 {
1814 #ifdef MODPLUG_TRACKER
1815 return (TrackerSettings::Instance().m_dwPatternSetup & PATTERN_SYNCMUTE) ? CHN_SYNCMUTE : CHN_MUTE;
1816 #else
1817 return CHN_SYNCMUTE;
1818 #endif
1819 }
1820
1821
1822 // Resolve note/instrument combination to real sample index. Return value is guaranteed to be in [0, GetNumSamples()].
1823 SAMPLEINDEX CSoundFile::GetSampleIndex(ModCommand::NOTE note, uint32 instr) const noexcept
1824 {
1825 SAMPLEINDEX smp = 0;
1826 if(GetNumInstruments())
1827 {
1828 if(ModCommand::IsNote(note) && instr <= GetNumInstruments() && Instruments[instr] != nullptr)
1829 smp = Instruments[instr]->Keyboard[note - NOTE_MIN];
1830 } else
1831 {
1832 smp = static_cast<SAMPLEINDEX>(instr);
1833 }
1834 if(smp <= GetNumSamples())
1835 return smp;
1836 else
1837 return 0;
1838 }
1839
1840
1841 // Find an unused sample slot. If it is going to be assigned to an instrument, targetInstrument should be specified.
1842 // SAMPLEINDEX_INVLAID is returned if no free sample slot could be found.
1843 SAMPLEINDEX CSoundFile::GetNextFreeSample(INSTRUMENTINDEX targetInstrument, SAMPLEINDEX start) const
1844 {
1845 // Find empty slot in two passes - in the first pass, we only search for samples with empty sample names,
1846 // in the second pass we check all samples with non-empty sample names.
1847 for(int passes = 0; passes < 2; passes++)
1848 {
1849 for(SAMPLEINDEX i = start; i <= GetModSpecifications().samplesMax; i++)
1850 {
1851 // Early exit for FM instruments
1852 if(Samples[i].uFlags[CHN_ADLIB] && (targetInstrument == INSTRUMENTINDEX_INVALID || !IsSampleReferencedByInstrument(i, targetInstrument)))
1853 continue;
1854
1855 // When loading into an instrument, ignore non-empty sample names. Else, only use this slot if the sample name is empty or we're in second pass.
1856 if((i > GetNumSamples() && passes == 1)
1857 || (!Samples[i].HasSampleData() && (!m_szNames[i][0] || passes == 1 || targetInstrument != INSTRUMENTINDEX_INVALID))
1858 || (targetInstrument != INSTRUMENTINDEX_INVALID && IsSampleReferencedByInstrument(i, targetInstrument))) // Not empty, but already used by this instrument. XXX this should only be done when replacing an instrument with a single sample! Otherwise it will use an inconsistent sample map!
1859 {
1860 // Empty slot, so it's a good candidate already.
1861
1862 // In instrument mode, check whether any instrument references this sample slot. If that is the case, we won't use it as it could lead to unwanted conflicts.
1863 // If we are loading the sample *into* an instrument, we should also not consider that instrument's sample map, since it might be inconsistent at this time.
1864 bool isReferenced = false;
1865 for(INSTRUMENTINDEX ins = 1; ins <= GetNumInstruments(); ins++)
1866 {
1867 if(ins == targetInstrument)
1868 {
1869 continue;
1870 }
1871 if(IsSampleReferencedByInstrument(i, ins))
1872 {
1873 isReferenced = true;
1874 break;
1875 }
1876 }
1877 if(!isReferenced)
1878 {
1879 return i;
1880 }
1881 }
1882 }
1883 }
1884
1885 return SAMPLEINDEX_INVALID;
1886 }
1887
1888
1889 // Find an unused instrument slot.
1890 // INSTRUMENTINDEX_INVALID is returned if no free instrument slot could be found.
1891 INSTRUMENTINDEX CSoundFile::GetNextFreeInstrument(INSTRUMENTINDEX start) const
1892 {
1893 for(INSTRUMENTINDEX i = start; i <= GetModSpecifications().instrumentsMax; i++)
1894 {
1895 if(Instruments[i] == nullptr)
1896 {
1897 return i;
1898 }
1899 }
1900
1901 return INSTRUMENTINDEX_INVALID;
1902 }
1903
1904
1905 // Check whether a given sample is used by a given instrument.
1906 bool CSoundFile::IsSampleReferencedByInstrument(SAMPLEINDEX sample, INSTRUMENTINDEX instr) const
1907 {
1908 if(instr < 1 || instr > GetNumInstruments())
1909 return false;
1910
1911 const ModInstrument *targetIns = Instruments[instr];
1912 if(targetIns == nullptr)
1913 return false;
1914
1915 return mpt::contains(mpt::as_span(targetIns->Keyboard).first(NOTE_MAX), sample);
1916 }
1917
1918
1919 ModInstrument *CSoundFile::AllocateInstrument(INSTRUMENTINDEX instr, SAMPLEINDEX assignedSample)
1920 {
1921 if(instr == 0 || instr >= MAX_INSTRUMENTS)
1922 {
1923 return nullptr;
1924 }
1925
1926 ModInstrument *ins = Instruments[instr];
1927 if(ins != nullptr)
1928 {
1929 // Re-initialize instrument
1930 *ins = ModInstrument(assignedSample);
1931 } else
1932 {
1933 // Create new instrument
1934 Instruments[instr] = ins = new (std::nothrow) ModInstrument(assignedSample);
1935 }
1936 if(ins != nullptr)
1937 {
1938 m_nInstruments = std::max(m_nInstruments, instr);
1939 }
1940 return ins;
1941 }
1942
1943
1944 void CSoundFile::PrecomputeSampleLoops(bool updateChannels)
1945 {
1946 for(SAMPLEINDEX i = 1; i <= GetNumSamples(); i++)
1947 {
1948 Samples[i].PrecomputeLoops(*this, updateChannels);
1949 }
1950 }
1951
1952
1953 #ifdef MPT_EXTERNAL_SAMPLES
1954 // Load external waveform, but keep sample properties like frequency, panning, etc...
1955 // Returns true if the file could be loaded.
1956 bool CSoundFile::LoadExternalSample(SAMPLEINDEX smp, const mpt::PathString &filename)
1957 {
1958 bool ok = false;
1959 InputFile f(filename, SettingCacheCompleteFileBeforeLoading());
1960
1961 if(f.IsValid())
1962 {
1963 const ModSample origSample = Samples[smp];
1964 mpt::charbuf<MAX_SAMPLENAME> origName;
1965 origName = m_szNames[smp];
1966
1967 FileReader file = GetFileReader(f);
1968 ok = ReadSampleFromFile(smp, file, false);
1969 if(ok)
1970 {
1971 // Copy over old attributes, but keep new sample data
1972 ModSample &sample = GetSample(smp);
1973 SmpLength newLength = sample.nLength;
1974 void *newData = sample.samplev();
1975 SampleFlags newFlags = sample.uFlags;
1976
1977 sample = origSample;
1978 sample.nLength = newLength;
1979 sample.pData.pSample = newData;
1980 sample.uFlags.set(CHN_16BIT, newFlags[CHN_16BIT]);
1981 sample.uFlags.set(CHN_STEREO, newFlags[CHN_STEREO]);
1982 sample.uFlags.reset(SMP_MODIFIED);
1983 sample.SanitizeLoops();
1984 }
1985 m_szNames[smp] = origName;
1986 }
1987 SetSamplePath(smp, filename);
1988 return ok;
1989 }
1990 #endif // MPT_EXTERNAL_SAMPLES
1991
1992
1993 // Set up channel panning and volume suitable for MOD + similar files. If the current mod type is not MOD, bForceSetup has to be set to true.
1994 void CSoundFile::SetupMODPanning(bool bForceSetup)
1995 {
1996 // Setup LRRL panning, max channel volume
1997 if(!(GetType() & MOD_TYPE_MOD) && bForceSetup == false) return;
1998
1999 for(CHANNELINDEX nChn = 0; nChn < MAX_BASECHANNELS; nChn++)
2000 {
2001 ChnSettings[nChn].nVolume = 64;
2002 ChnSettings[nChn].dwFlags.reset(CHN_SURROUND);
2003 if(m_MixerSettings.MixerFlags & SNDMIX_MAXDEFAULTPAN)
2004 ChnSettings[nChn].nPan = (((nChn & 3) == 1) || ((nChn & 3) == 2)) ? 256 : 0;
2005 else
2006 ChnSettings[nChn].nPan = (((nChn & 3) == 1) || ((nChn & 3) == 2)) ? 0xC0 : 0x40;
2007 }
2008 }
2009
2010
2011 void CSoundFile::PropagateXMAutoVibrato(INSTRUMENTINDEX ins, VibratoType type, uint8 sweep, uint8 depth, uint8 rate)
2012 {
2013 if(ins > m_nInstruments || Instruments[ins] == nullptr)
2014 return;
2015 const std::set<SAMPLEINDEX> referencedSamples = Instruments[ins]->GetSamples();
2016
2017 // Propagate changes to all samples that belong to this instrument.
2018 for(auto sample : referencedSamples)
2019 {
2020 if(sample <= m_nSamples)
2021 {
2022 Samples[sample].nVibDepth = depth;
2023 Samples[sample].nVibType = type;
2024 Samples[sample].nVibRate = rate;
2025 Samples[sample].nVibSweep = sweep;
2026 }
2027 }
2028 }
2029
2030
2031 // Normalize the tempo swing coefficients so that they add up to exactly the specified tempo again
2032 void TempoSwing::Normalize()
2033 {
2034 if(empty()) return;
2035 uint64 sum = 0;
2036 for(auto &i : *this)
2037 {
2038 Limit(i, Unity / 4u, Unity * 4u);
2039 sum += i;
2040 }
2041 sum /= size();
2042 int64 remain = Unity * size();
2043 for(auto &i : *this)
2044 {
2045 i = Util::muldivr_unsigned(i, Unity, static_cast<int32>(sum));
2046 remain -= i;
2047 }
2048 //MPT_ASSERT(static_cast<uint32>(std::abs(static_cast<int32>(remain))) <= size());
2049 at(0) += static_cast<int32>(remain);
2050 }
2051
2052
2053 void TempoSwing::Serialize(std::ostream &oStrm, const TempoSwing &swing)
2054 {
2055 mpt::IO::WriteIntLE<uint16>(oStrm, static_cast<uint16>(swing.size()));
2056 for(std::size_t i = 0; i < swing.size(); i++)
2057 {
2058 mpt::IO::WriteIntLE<uint32>(oStrm, swing[i]);
2059 }
2060 }
2061
2062
2063 void TempoSwing::Deserialize(std::istream &iStrm, TempoSwing &swing, const size_t)
2064 {
2065 uint16 numEntries;
2066 mpt::IO::ReadIntLE<uint16>(iStrm, numEntries);
2067 swing.resize(numEntries);
2068 for(uint16 i = 0; i < numEntries; i++)
2069 {
2070 mpt::IO::ReadIntLE<uint32>(iStrm, swing[i]);
2071 }
2072 swing.Normalize();
2073 }
2074
2075
2076 OPENMPT_NAMESPACE_END
2077