1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
|
/*
* Snd_Defs.h
* ----------
* Purpose: Basic definitions of data types, enums, etc. for the playback engine core.
* Notes : (currently none)
* Authors: Olivier Lapicque
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "openmpt/all/BuildSettings.hpp"
#include "openmpt/base/FlagSet.hpp"
OPENMPT_NAMESPACE_BEGIN
using ROWINDEX = uint32;
inline constexpr ROWINDEX ROWINDEX_INVALID = uint32_max;
using CHANNELINDEX = uint16;
inline constexpr CHANNELINDEX CHANNELINDEX_INVALID = uint16_max;
using ORDERINDEX = uint16;
inline constexpr ORDERINDEX ORDERINDEX_INVALID = uint16_max;
inline constexpr ORDERINDEX ORDERINDEX_MAX = uint16_max - 1;
using PATTERNINDEX = uint16;
inline constexpr PATTERNINDEX PATTERNINDEX_INVALID = uint16_max;
using PLUGINDEX = uint8;
inline constexpr PLUGINDEX PLUGINDEX_INVALID = uint8_max;
using SAMPLEINDEX = uint16;
inline constexpr SAMPLEINDEX SAMPLEINDEX_INVALID = uint16_max;
using INSTRUMENTINDEX = uint16;
inline constexpr INSTRUMENTINDEX INSTRUMENTINDEX_INVALID = uint16_max;
using SEQUENCEINDEX = uint8;
inline constexpr SEQUENCEINDEX SEQUENCEINDEX_INVALID = uint8_max;
using SmpLength = uint32;
inline constexpr SmpLength MAX_SAMPLE_LENGTH = 0x10000000; // Sample length in frames. Sample size in bytes can be more than this (= 256 MB).
inline constexpr ROWINDEX MAX_PATTERN_ROWS = 1024;
inline constexpr ROWINDEX MAX_ROWS_PER_BEAT = 65536;
inline constexpr ORDERINDEX MAX_ORDERS = ORDERINDEX_MAX + 1;
inline constexpr PATTERNINDEX MAX_PATTERNS = 4000;
inline constexpr SAMPLEINDEX MAX_SAMPLES = 4000;
inline constexpr INSTRUMENTINDEX MAX_INSTRUMENTS = 256;
inline constexpr PLUGINDEX MAX_MIXPLUGINS = 250;
inline constexpr SEQUENCEINDEX MAX_SEQUENCES = 50;
inline constexpr CHANNELINDEX MAX_BASECHANNELS = 127; // Maximum pattern channels.
inline constexpr CHANNELINDEX MAX_CHANNELS = 256; // Maximum number of mixing channels.
enum { FREQ_FRACBITS = 4 }; // Number of fractional bits in return value of CSoundFile::GetFreqFromPeriod()
// String lengths (including trailing null char)
enum
{
MAX_SAMPLENAME = 32,
MAX_SAMPLEFILENAME = 22,
MAX_INSTRUMENTNAME = 32,
MAX_INSTRUMENTFILENAME = 32,
MAX_PATTERNNAME = 32,
MAX_CHANNELNAME = 20,
};
enum MODTYPE
{
MOD_TYPE_NONE = 0x00,
MOD_TYPE_MOD = 0x01,
MOD_TYPE_S3M = 0x02,
MOD_TYPE_XM = 0x04,
MOD_TYPE_MED = 0x08,
MOD_TYPE_MTM = 0x10,
MOD_TYPE_IT = 0x20,
MOD_TYPE_669 = 0x40,
MOD_TYPE_ULT = 0x80,
MOD_TYPE_STM = 0x100,
MOD_TYPE_FAR = 0x200,
MOD_TYPE_DTM = 0x400,
MOD_TYPE_AMF = 0x800,
MOD_TYPE_AMS = 0x1000,
MOD_TYPE_DSM = 0x2000,
MOD_TYPE_MDL = 0x4000,
MOD_TYPE_OKT = 0x8000,
MOD_TYPE_MID = 0x10000,
MOD_TYPE_DMF = 0x20000,
MOD_TYPE_PTM = 0x40000,
MOD_TYPE_DBM = 0x80000,
MOD_TYPE_MT2 = 0x100000,
MOD_TYPE_AMF0 = 0x200000,
MOD_TYPE_PSM = 0x400000,
MOD_TYPE_J2B = 0x800000,
MOD_TYPE_MPT = 0x1000000,
MOD_TYPE_IMF = 0x2000000,
MOD_TYPE_DIGI = 0x4000000,
MOD_TYPE_STP = 0x8000000,
MOD_TYPE_PLM = 0x10000000,
MOD_TYPE_SFX = 0x20000000,
};
DECLARE_FLAGSET(MODTYPE)
enum MODCONTAINERTYPE
{
MOD_CONTAINERTYPE_NONE = 0x0,
MOD_CONTAINERTYPE_UMX = 0x3,
MOD_CONTAINERTYPE_XPK = 0x4,
MOD_CONTAINERTYPE_PP20 = 0x5,
MOD_CONTAINERTYPE_MMCMP= 0x6,
MOD_CONTAINERTYPE_WAV = 0x7, // WAV as module
MOD_CONTAINERTYPE_UAX = 0x8, // Unreal sample set as module
};
// Module channel / sample flags
enum ChannelFlags
{
// Sample Flags
CHN_16BIT = 0x01, // 16-bit sample
CHN_LOOP = 0x02, // Looped sample
CHN_PINGPONGLOOP = 0x04, // Bidi-looped sample
CHN_SUSTAINLOOP = 0x08, // Sample with sustain loop
CHN_PINGPONGSUSTAIN = 0x10, // Sample with bidi sustain loop
CHN_PANNING = 0x20, // Sample with forced panning
CHN_STEREO = 0x40, // Stereo sample
CHN_REVERSE = 0x80, // Start sample playback from sample / loop end (Velvet Studio feature)
CHN_SURROUND = 0x100, // Use surround channel
CHN_ADLIB = 0x200, // Adlib / OPL instrument is active on this channel
// Channel Flags
CHN_PINGPONGFLAG = 0x80, // When flag is on, sample is processed backwards - this is intentionally the same flag as CHN_REVERSE.
CHN_MUTE = 0x400, // Muted channel
CHN_KEYOFF = 0x800, // Exit sustain
CHN_NOTEFADE = 0x1000, // Fade note (instrument mode)
CHN_WRAPPED_LOOP = 0x2000, // Loop just wrapped around to loop start (required for correct interpolation around loop points)
CHN_AMIGAFILTER = 0x4000, // Apply Amiga low-pass filter
CHN_FILTER = 0x8000, // Apply resonant filter on sample
CHN_VOLUMERAMP = 0x10000, // Apply volume ramping
CHN_VIBRATO = 0x20000, // Apply vibrato
CHN_TREMOLO = 0x40000, // Apply tremolo
CHN_PORTAMENTO = 0x80000, // Apply portamento
CHN_GLISSANDO = 0x100000, // Glissando (force portamento to semitones) mode
CHN_FASTVOLRAMP = 0x200000, // Force usage of global ramping settings instead of ramping over the complete render buffer length
CHN_EXTRALOUD = 0x400000, // Force sample to play at 0dB
CHN_REVERB = 0x800000, // Apply reverb on this channel
CHN_NOREVERB = 0x1000000, // Disable reverb on this channel
CHN_SOLO = 0x2000000, // Solo channel
CHN_NOFX = 0x4000000, // Dry channel (no plugins)
CHN_SYNCMUTE = 0x8000000, // Keep sample sync on mute
// Sample flags (only present in ModSample::uFlags, may overlap with CHN_CHANNELFLAGS)
SMP_MODIFIED = 0x2000, // Sample data has been edited in the tracker
SMP_KEEPONDISK = 0x4000, // Sample is not saved to file, data is restored from original sample file
SMP_NODEFAULTVOLUME = 0x8000, // Ignore default volume setting
};
DECLARE_FLAGSET(ChannelFlags)
#define CHN_SAMPLEFLAGS (CHN_16BIT | CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN | CHN_PANNING | CHN_STEREO | CHN_PINGPONGFLAG | CHN_REVERSE | CHN_SURROUND | CHN_ADLIB)
#define CHN_CHANNELFLAGS (~CHN_SAMPLEFLAGS | CHN_SURROUND)
// Sample flags fit into the first 16 bits, and with the current memory layout, storing them as a 16-bit integer packs struct ModSample nicely.
using SampleFlags = FlagSet<ChannelFlags, uint16>;
// Instrument envelope-specific flags
enum EnvelopeFlags : uint8
{
ENV_ENABLED = 0x01, // env is enabled
ENV_LOOP = 0x02, // env loop
ENV_SUSTAIN = 0x04, // env sustain
ENV_CARRY = 0x08, // env carry
ENV_FILTER = 0x10, // filter env enabled (this has to be combined with ENV_ENABLED in the pitch envelope's flags)
};
DECLARE_FLAGSET(EnvelopeFlags)
// Envelope value boundaries
#define ENVELOPE_MIN 0 // Vertical min value of a point
#define ENVELOPE_MID 32 // Vertical middle line
#define ENVELOPE_MAX 64 // Vertical max value of a point
#define MAX_ENVPOINTS 240 // Maximum length of each instrument envelope
// Instrument-specific flags
enum InstrumentFlags : uint8
{
INS_SETPANNING = 0x01, // Panning enabled
INS_MUTE = 0x02, // Instrument is muted
};
DECLARE_FLAGSET(InstrumentFlags)
// envelope types in instrument editor
enum EnvelopeType : uint8
{
ENV_VOLUME = 0,
ENV_PANNING,
ENV_PITCH,
ENV_MAXTYPES
};
// Filter Modes
enum class FilterMode : uint8
{
Unchanged = 0xFF,
LowPass = 0,
HighPass = 1,
};
// NNA types (New Note Action)
enum class NewNoteAction : uint8
{
NoteCut = 0,
Continue = 1,
NoteOff = 2,
NoteFade = 3,
};
// DCT types (Duplicate Check Types)
enum class DuplicateCheckType : uint8
{
None = 0,
Note = 1,
Sample = 2,
Instrument = 3,
Plugin = 4,
};
// DNA types (Duplicate Note Action)
enum class DuplicateNoteAction : uint8
{
NoteCut = 0,
NoteOff = 1,
NoteFade = 2,
};
// Module flags - contains both song configuration and playback state... Use SONG_FILE_FLAGS and SONG_PLAY_FLAGS distinguish between the two.
enum SongFlags
{
SONG_FASTVOLSLIDES = 0x02, // Old Scream Tracker 3.0 volume slides
SONG_ITOLDEFFECTS = 0x04, // Old Impulse Tracker effect implementations
SONG_ITCOMPATGXX = 0x08, // IT "Compatible Gxx" (IT's flag to behave more like other trackers w/r/t portamento effects)
SONG_LINEARSLIDES = 0x10, // Linear slides vs. Amiga slides
SONG_PATTERNLOOP = 0x20, // Loop current pattern (pattern editor)
SONG_STEP = 0x40, // Song is in "step" mode (pattern editor)
SONG_PAUSED = 0x80, // Song is paused (no tick processing, just rendering audio)
SONG_FADINGSONG = 0x0100, // Song is fading out
SONG_ENDREACHED = 0x0200, // Song is finished
SONG_FIRSTTICK = 0x1000, // Is set when the current tick is the first tick of the row
SONG_MPTFILTERMODE = 0x2000, // Local filter mode (reset filter on each note)
SONG_SURROUNDPAN = 0x4000, // Pan in the rear channels
SONG_EXFILTERRANGE = 0x8000, // Cutoff Filter has double frequency range (up to ~10Khz)
SONG_AMIGALIMITS = 0x1'0000, // Enforce amiga frequency limits
SONG_S3MOLDVIBRATO = 0x2'0000, // ScreamTracker 2 vibrato in S3M files
SONG_BREAKTOROW = 0x8'0000, // Break to row command encountered (internal flag, do not touch)
SONG_POSJUMP = 0x10'0000, // Position jump encountered (internal flag, do not touch)
SONG_PT_MODE = 0x20'0000, // ProTracker 1/2 playback mode
SONG_PLAYALLSONGS = 0x40'0000, // Play all subsongs consecutively (libopenmpt)
SONG_ISAMIGA = 0x80'0000, // Is an Amiga module and thus qualifies to be played using the Paula BLEP resampler
SONG_IMPORTED = 0x100'0000, // Song type does not represent actual module format / was imported from a different format (OpenMPT)
};
DECLARE_FLAGSET(SongFlags)
#define SONG_FILE_FLAGS (SONG_FASTVOLSLIDES|SONG_ITOLDEFFECTS|SONG_ITCOMPATGXX|SONG_LINEARSLIDES|SONG_EXFILTERRANGE|SONG_AMIGALIMITS|SONG_S3MOLDVIBRATO|SONG_PT_MODE|SONG_ISAMIGA|SONG_IMPORTED)
#define SONG_PLAY_FLAGS (~SONG_FILE_FLAGS)
// Global Options (Renderer)
#ifndef NO_AGC
#define SNDDSP_AGC 0x40 // Automatic gain control
#endif // ~NO_AGC
#ifndef NO_DSP
#define SNDDSP_MEGABASS 0x02 // Bass expansion
#define SNDDSP_SURROUND 0x08 // Surround mix
#define SNDDSP_BITCRUSH 0x01
#endif // NO_DSP
#ifndef NO_REVERB
#define SNDDSP_REVERB 0x20 // Apply reverb
#endif // NO_REVERB
#ifndef NO_EQ
#define SNDDSP_EQ 0x80 // Apply EQ
#endif // NO_EQ
#define SNDMIX_SOFTPANNING 0x10 // Soft panning mode (this is forced with mixmode RC3 and later)
// Misc Flags (can safely be turned on or off)
#define SNDMIX_MAXDEFAULTPAN 0x80000 // Currently unused (should be used by Amiga MOD loaders)
#define SNDMIX_MUTECHNMODE 0x100000 // Notes are not played on muted channels
#define MAX_GLOBAL_VOLUME 256u
// Resampling modes
enum ResamplingMode : uint8
{
// ATTENTION: Do not change ANY of these values, as they get written out to files in per instrument interpolation settings
// and old files have these exact values in them which should not change meaning.
SRCMODE_NEAREST = 0, // 1 tap, no AA
SRCMODE_LINEAR = 1, // 2 tap, no AA
SRCMODE_CUBIC = 2, // 4 tap, no AA
SRCMODE_SINC8 = 4, // 8 tap, no AA (yes, index 4) (XMMS-ModPlug)
SRCMODE_SINC8LP = 3, // 8 tap, with AA (yes, index 3) (Polyphase)
SRCMODE_DEFAULT = 5, // Only used for instrument settings, not used inside the mixer
SRCMODE_AMIGA = 0xFF, // Not explicitely user-selectable
};
namespace Resampling
{
enum class AmigaFilter
{
Off = 0,
A500 = 1,
A1200 = 2,
Unfiltered = 3,
};
inline std::array<ResamplingMode, 5> AllModes() noexcept { return { { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_CUBIC, SRCMODE_SINC8, SRCMODE_SINC8LP } }; }
inline std::array<ResamplingMode, 6> AllModesWithDefault() noexcept { return { { SRCMODE_NEAREST, SRCMODE_LINEAR, SRCMODE_CUBIC, SRCMODE_SINC8, SRCMODE_SINC8LP, SRCMODE_DEFAULT } }; }
constexpr ResamplingMode Default() noexcept { return SRCMODE_SINC8LP; }
constexpr bool IsKnownMode(int mode) noexcept { return (mode >= 0) && (mode < SRCMODE_DEFAULT); }
constexpr ResamplingMode ToKnownMode(int mode) noexcept
{
return Resampling::IsKnownMode(mode) ? static_cast<ResamplingMode>(mode)
: (mode == SRCMODE_AMIGA) ? SRCMODE_LINEAR
: Resampling::Default();
}
constexpr int Length(ResamplingMode mode) noexcept
{
return mode == SRCMODE_NEAREST ? 1
: mode == SRCMODE_LINEAR ? 2
: mode == SRCMODE_CUBIC ? 4
: mode == SRCMODE_SINC8 ? 8
: mode == SRCMODE_SINC8LP ? 8
: 0;
}
constexpr bool HasAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP); }
constexpr ResamplingMode AddAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8) ? SRCMODE_SINC8LP : mode; }
constexpr ResamplingMode RemoveAA(ResamplingMode mode) noexcept { return (mode == SRCMODE_SINC8LP) ? SRCMODE_SINC8 : mode; }
}
// Release node defines
#define ENV_RELEASE_NODE_UNSET 0xFF
#define NOT_YET_RELEASED (-1)
static_assert(ENV_RELEASE_NODE_UNSET > MAX_ENVPOINTS);
enum PluginPriority
{
ChannelOnly,
InstrumentOnly,
PrioritiseInstrument,
PrioritiseChannel,
};
enum PluginMutePriority
{
EvenIfMuted,
RespectMutes,
};
// Plugin velocity handling options
enum PlugVelocityHandling : uint8
{
PLUGIN_VELOCITYHANDLING_CHANNEL = 0,
PLUGIN_VELOCITYHANDLING_VOLUME
};
// Plugin volumecommand handling options
enum PlugVolumeHandling : uint8
{
PLUGIN_VOLUMEHANDLING_MIDI = 0,
PLUGIN_VOLUMEHANDLING_DRYWET,
PLUGIN_VOLUMEHANDLING_IGNORE,
PLUGIN_VOLUMEHANDLING_CUSTOM,
PLUGIN_VOLUMEHANDLING_MAX,
};
enum MidiChannel : uint8
{
MidiNoChannel = 0,
MidiFirstChannel = 1,
MidiLastChannel = 16,
MidiMappedChannel = 17,
};
// Vibrato Types
enum VibratoType : uint8
{
VIB_SINE = 0,
VIB_SQUARE,
VIB_RAMP_UP,
VIB_RAMP_DOWN,
VIB_RANDOM
};
// Tracker-specific playback behaviour
// Note: The index of every flag has to be fixed, so do not remove flags. Always add new flags at the end!
enum PlayBehaviour
{
MSF_COMPATIBLE_PLAY, // No-op - only used during loading (Old general compatibility flag for IT/MPT/XM)
kMPTOldSwingBehaviour, // MPT 1.16 swing behaviour (IT/MPT, deprecated)
kMIDICCBugEmulation, // Emulate broken volume MIDI CC behaviour (IT/MPT/XM, deprecated)
kOldMIDIPitchBends, // Old VST MIDI pitch bend behaviour (IT/MPT/XM, deprecated)
kFT2VolumeRamping, // Smooth volume ramping like in FT2 (XM)
kMODVBlankTiming, // F21 and above set speed instead of tempo
kSlidesAtSpeed1, // Execute normal slides at speed 1 as if they were fine slides
kPeriodsAreHertz, // Compute note frequency in Hertz rather than periods
kTempoClamp, // Clamp tempo to 32-255 range.
kPerChannelGlobalVolSlide, // Global volume slide memory is per-channel
kPanOverride, // Panning commands override surround and random pan variation
kITInstrWithoutNote, // Avoid instrument handling if there is no note
kITVolColFinePortamento, // Volume column portamento never does fine portamento
kITArpeggio, // IT arpeggio algorithm
kITOutOfRangeDelay, // Out-of-range delay command behaviour in IT
kITPortaMemoryShare, // Gxx shares memory with Exx and Fxx
kITPatternLoopTargetReset, // After finishing a pattern loop, set the pattern loop target to the next row
kITFT2PatternLoop, // Nested pattern loop behaviour
kITPingPongNoReset, // Don't reset ping pong direction with instrument numbers
kITEnvelopeReset, // IT envelope reset behaviour
kITClearOldNoteAfterCut, // Forget the previous note after cutting it
kITVibratoTremoloPanbrello, // More IT-like Hxx / hx, Rxx, Yxx and autovibrato handling, including more precise LUTs
kITTremor, // Ixx behaves like in IT
kITRetrigger, // Qxx behaves like in IT
kITMultiSampleBehaviour, // Properly update C-5 frequency when changing in multisampled instrument
kITPortaTargetReached, // Clear portamento target after it has been reached
kITPatternLoopBreak, // Don't reset loop count on pattern break.
kITOffset, // IT-style Oxx edge case handling
kITSwingBehaviour, // IT's swing behaviour
kITNNAReset, // NNA is reset on every note change, not every instrument change
kITSCxStopsSample, // SCx really stops the sample and does not just mute it
kITEnvelopePositionHandling, // IT-style envelope position advance + enable/disable behaviour
kITPortamentoInstrument, // No sample changes during portamento with Compatible Gxx enabled, instrument envelope reset with portamento
kITPingPongMode, // Don't repeat last sample point in ping pong loop, like IT's software mixer
kITRealNoteMapping, // Use triggered note rather than translated note for PPS and other effects
kITHighOffsetNoRetrig, // SAx should not apply an offset effect to a note next to it
kITFilterBehaviour, // User IT's filter coefficients (unless extended filter range is used)
kITNoSurroundPan, // Panning and surround are mutually exclusive
kITShortSampleRetrig, // Don't retrigger already stopped channels
kITPortaNoNote, // Don't apply any portamento if no previous note is playing
kITFT2DontResetNoteOffOnPorta, // Only reset note-off status on portamento in IT Compatible Gxx mode
kITVolColMemory, // IT volume column effects share their memory with the effect column
kITPortamentoSwapResetsPos, // Portamento with sample swap plays the new sample from the beginning
kITEmptyNoteMapSlot, // IT ignores instrument note map entries with no note completely
kITFirstTickHandling, // IT-style first tick handling
kITSampleAndHoldPanbrello, // IT-style sample&hold panbrello waveform
kITClearPortaTarget, // New notes reset portamento target in IT
kITPanbrelloHold, // Don't reset panbrello effect until next note or panning effect
kITPanningReset, // Sample and instrument panning is only applied on note change, not instrument change
kITPatternLoopWithJumpsOld, // Bxx on the same row as SBx terminates the loop in IT (old implementation of kITPatternLoopWithJumps)
kITInstrWithNoteOff, // Instrument number with note-off recalls default volume
kFT2Arpeggio, // FT2 arpeggio algorithm
kFT2Retrigger, // Rxx behaves like in FT2
kFT2VolColVibrato, // Vibrato depth in volume column does not actually execute the vibrato effect
kFT2PortaNoNote, // Don't play portamento-ed note if no previous note is playing
kFT2KeyOff, // FT2-style Kxx handling
kFT2PanSlide, // Volume-column pan slides should be handled like fine slides
kFT2ST3OffsetOutOfRange, // Offset past sample end stops the note
kFT2RestrictXCommand, // Don't allow MPT extensions to Xxx command in XM
kFT2RetrigWithNoteDelay, // Retrigger envelopes if there is a note delay with no note
kFT2SetPanEnvPos, // Lxx only sets the pan env position if the volume envelope's sustain flag is set
kFT2PortaIgnoreInstr, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself.
kFT2VolColMemory, // No volume column memory in FT2
kFT2LoopE60Restart, // Next pattern starts on the same row as the last E60 command
kFT2ProcessSilentChannels, // Keep processing silent channels for later 3xx pickup
kFT2ReloadSampleSettings, // Reload sample settings even if a note-off is placed next to an instrument number
kFT2PortaDelay, // Portamento with note delay next to it is ignored in FT2
kFT2Transpose, // Out-of-range transposed notes in FT2
kFT2PatternLoopWithJumps, // Bxx or Dxx on the same row as E6x terminates the loop in FT2
kFT2PortaTargetNoReset, // Portamento target is not reset with new notes in FT2
kFT2EnvelopeEscape, // FT2 sustain point at end of envelope
kFT2Tremor, // Txx behaves like in FT2
kFT2OutOfRangeDelay, // Out-of-range delay command behaviour in FT2
kFT2Periods, // Use FT2's broken period handling
kFT2PanWithDelayedNoteOff, // Pan command with delayed note-off
kFT2VolColDelay, // FT2-style volume column handling if there is a note delay
kFT2FinetunePrecision, // Only take the upper 4 bits of sample finetune.
kST3NoMutedChannels, // Don't process any effects on muted S3M channels
kST3EffectMemory, // Most effects share the same memory in ST3
kST3PortaSampleChange, // Portamento plus instrument number applies the volume settings of the new sample, but not the new sample itself (GUS behaviour).
kST3VibratoMemory, // Do not remember vibrato type in effect memory
kST3LimitPeriod, // Cut note instead of limiting final period (ModPlug Tracker style)
KST3PortaAfterArpeggio, // Portamento after arpeggio continues at the note where the arpeggio left off
kMODOneShotLoops, // Allow ProTracker-like oneshot loops
kMODIgnorePanning, // Do not process any panning commands
kMODSampleSwap, // On-the-fly sample swapping
kFT2NoteOffFlags, // Set and reset the correct fade/key-off flags with note-off and instrument number after note-off
kITMultiSampleInstrumentNumber, // After portamento to different sample within multi-sampled instrument, lone instrument numbers in patterns always recall the new sample's default settings
kRowDelayWithNoteDelay, // Retrigger note delays on every reptition of a row
kFT2MODTremoloRampWaveform, // FT2-/ProTracker-compatible tremolo ramp down / triangle waveform
kFT2PortaUpDownMemory, // Portamento up and down have separate memory
kMODOutOfRangeNoteDelay, // ProTracker behaviour for out-of-range note delays
kMODTempoOnSecondTick, // ProTracker sets tempo after the first tick
kFT2PanSustainRelease, // If the sustain point of a panning envelope is reached before key-off, FT2 does not escape it anymore
kLegacyReleaseNode, // Legacy release node volume processing
kOPLBeatingOscillators, // Emulate beating FM oscillators from CDFM / Composer 670
kST3OffsetWithoutInstrument, // Note without instrument uses same offset as previous note
kReleaseNodePastSustainBug, // OpenMPT 1.23.01.02 / r4009 broke release nodes past the sustain point, fixed in OpenMPT 1.28
kFT2NoteDelayWithoutInstr, // Sometime between OpenMPT 1.18.03.00 and 1.19.01.00, delayed instrument-less notes in XM started recalling the default sample volume and panning
kOPLFlexibleNoteOff, // Full control after note-off over OPL voices, ^^^ sends note cut instead of just note-off
kITInstrWithNoteOffOldEffects, // Instrument number with note-off recalls default volume - special cases with Old Effects enabled
kMIDIVolumeOnNoteOffBug, // Update MIDI channel volume on note-off (legacy bug emulation)
kITDoNotOverrideChannelPan, // Sample / instrument pan does not override channel pan for following samples / instruments that are not panned
kITPatternLoopWithJumps, // Bxx right of SBx terminates the loop in IT
kITDCTBehaviour, // DCT="Sample" requires sample instrument, DCT="Note" checks old pattern note against new pattern note (previously was checking old pattern note against new translated note)
kOPLwithNNA, // NNA note-off / fade are applied to OPL channels
kST3RetrigAfterNoteCut, // Qxy does not retrigger note after it has been cut with ^^^ or SCx
kST3SampleSwap, // On-the-fly sample swapping (SoundBlaster behaviour)
kOPLRealRetrig, // Retrigger effect (Qxy) restarts OPL notes
kOPLNoResetAtEnvelopeEnd, // Do not reset OPL channel status at end of envelope (OpenMPT 1.28 inconsistency with samples)
kOPLNoteStopWith0Hz, // Set note frequency to 0 Hz to "stop" OPL notes
kOPLNoteOffOnNoteChange, // Send note-off events for old note on every note change
kFT2PortaResetDirection, // Reset portamento direction when reaching portamento target from below
kApplyUpperPeriodLimit, // Enforce m_nMaxPeriod
kApplyOffsetWithoutNote, // Offset commands even work when there's no note next to them (e.g. DMF, MDL, PLM formats)
kITPitchPanSeparation, // Pitch/Pan Separation can be overridden by panning commands (this also fixes a bug where any "special" notes affect PPS)
kImprecisePingPongLoops, // Use old (less precise) ping-pong overshoot calculation
// Add new play behaviours here.
kMaxPlayBehaviours,
};
// Tempo swing determines how much every row in modern tempo mode contributes to a beat.
class TempoSwing : public std::vector<uint32>
{
public:
static constexpr uint32 Unity = 1u << 24;
// Normalize the tempo swing coefficients so that they add up to exactly the specified tempo again
void Normalize();
void resize(size_type newSize, value_type val = Unity) { std::vector<uint32>::resize(newSize, val); Normalize(); }
static void Serialize(std::ostream &oStrm, const TempoSwing &swing);
static void Deserialize(std::istream &iStrm, TempoSwing &swing, const size_t);
};
// Sample position and sample position increment value
struct SamplePosition
{
using value_t = int64;
using unsigned_value_t = uint64;
protected:
value_t v = 0;
public:
static constexpr uint32 fractMax = 0xFFFFFFFFu;
MPT_CONSTEXPRINLINE SamplePosition() { }
MPT_CONSTEXPRINLINE explicit SamplePosition(value_t pos) : v(pos) { }
MPT_CONSTEXPRINLINE SamplePosition(int32 intPart, uint32 fractPart) : v((static_cast<value_t>(intPart) * (1ll << 32)) | fractPart) { }
static SamplePosition Ratio(uint32 dividend, uint32 divisor) { return SamplePosition((static_cast<int64>(dividend) << 32) / divisor); }
static SamplePosition FromDouble(double pos) { return SamplePosition(static_cast<value_t>(pos * 4294967296.0)); }
double ToDouble() const { return v / 4294967296.0; }
// Set integer and fractional part
MPT_CONSTEXPRINLINE SamplePosition &Set(int32 intPart, uint32 fractPart = 0) { v = (static_cast<int64>(intPart) << 32) | fractPart; return *this; }
// Set integer part, keep fractional part
MPT_CONSTEXPRINLINE SamplePosition &SetInt(int32 intPart) { v = (static_cast<value_t>(intPart) << 32) | GetFract(); return *this; }
// Get integer part (as sample length / position)
MPT_CONSTEXPRINLINE SmpLength GetUInt() const { return static_cast<SmpLength>(static_cast<unsigned_value_t>(v) >> 32); }
// Get integer part
MPT_CONSTEXPRINLINE int32 GetInt() const { return static_cast<int32>(static_cast<unsigned_value_t>(v) >> 32); }
// Get fractional part
MPT_CONSTEXPRINLINE uint32 GetFract() const { return static_cast<uint32>(v); }
// Get the inverted fractional part
MPT_CONSTEXPRINLINE SamplePosition GetInvertedFract() const { return SamplePosition(0x100000000ll - GetFract()); }
// Get the raw fixed-point value
MPT_CONSTEXPRINLINE int64 GetRaw() const { return v; }
// Negate the current value
MPT_CONSTEXPRINLINE SamplePosition &Negate() { v = -v; return *this; }
// Multiply and divide by given integer scalars
MPT_CONSTEXPRINLINE SamplePosition &MulDiv(uint32 mul, uint32 div) { v = (v * mul) / div; return *this; }
// Removes the integer part, only keeping fractions
MPT_CONSTEXPRINLINE SamplePosition &RemoveInt() { v &= fractMax; return *this; }
// Check if value is 1.0
MPT_CONSTEXPRINLINE bool IsUnity() const { return v == 0x100000000ll; }
// Check if value is 0
MPT_CONSTEXPRINLINE bool IsZero() const { return v == 0; }
// Check if value is > 0
MPT_CONSTEXPRINLINE bool IsPositive() const { return v > 0; }
// Check if value is < 0
MPT_CONSTEXPRINLINE bool IsNegative() const { return v < 0; }
// Addition / subtraction of another fixed-point number
SamplePosition operator+ (const SamplePosition &other) const { return SamplePosition(v + other.v); }
SamplePosition operator- (const SamplePosition &other) const { return SamplePosition(v - other.v); }
void operator+= (const SamplePosition &other) { v += other.v; }
void operator-= (const SamplePosition &other) { v -= other.v; }
// Multiplication with integer scalar
template<typename T>
SamplePosition operator* (T other) const { return SamplePosition(static_cast<value_t>(v * other)); }
template<typename T>
void operator*= (T other) { v = static_cast<value_t>(v *other); }
// Division by other fractional point number; returns scalar
value_t operator/ (SamplePosition other) const { return v / other.v; }
// Division by scalar; returns fractional point number
SamplePosition operator/ (int div) const { return SamplePosition(v / div); }
MPT_CONSTEXPRINLINE bool operator==(const SamplePosition &other) const { return v == other.v; }
MPT_CONSTEXPRINLINE bool operator!=(const SamplePosition &other) const { return v != other.v; }
MPT_CONSTEXPRINLINE bool operator<=(const SamplePosition &other) const { return v <= other.v; }
MPT_CONSTEXPRINLINE bool operator>=(const SamplePosition &other) const { return v >= other.v; }
MPT_CONSTEXPRINLINE bool operator<(const SamplePosition &other) const { return v < other.v; }
MPT_CONSTEXPRINLINE bool operator>(const SamplePosition &other) const { return v > other.v; }
};
// Aaaand another fixed-point type, e.g. used for fractional tempos
// Note that this doesn't use classical bit shifting for the fixed point part.
// This is mostly for the clarity of stored values and to be able to represent any value .0000 to .9999 properly.
// For easier debugging, use the Debugger Visualizers available in build/vs/debug/
// to easily display the stored values.
template<size_t FFact, typename T>
struct FPInt
{
protected:
T v;
MPT_CONSTEXPRINLINE FPInt(T rawValue) : v(rawValue) { }
public:
enum : size_t { fractFact = FFact };
using store_t = T;
MPT_CONSTEXPRINLINE FPInt() : v(0) { }
MPT_CONSTEXPRINLINE FPInt(T intPart, T fractPart) : v((intPart * fractFact) + (fractPart % fractFact)) { }
explicit MPT_CONSTEXPRINLINE FPInt(float f) : v(mpt::saturate_round<T>(f * float(fractFact))) { }
explicit MPT_CONSTEXPRINLINE FPInt(double f) : v(mpt::saturate_round<T>(f * double(fractFact))) { }
// Set integer and fractional part
MPT_CONSTEXPRINLINE FPInt<fractFact, T> &Set(T intPart, T fractPart = 0) { v = (intPart * fractFact) + (fractPart % fractFact); return *this; }
// Set raw internal representation directly
MPT_CONSTEXPRINLINE FPInt<fractFact, T> &SetRaw(T value) { v = value; return *this; }
// Retrieve the integer part of the stored value
MPT_CONSTEXPRINLINE T GetInt() const { return v / fractFact; }
// Retrieve the fractional part of the stored value
MPT_CONSTEXPRINLINE T GetFract() const { return v % fractFact; }
// Retrieve the raw internal representation of the stored value
MPT_CONSTEXPRINLINE T GetRaw() const { return v; }
// Formats the stored value as a floating-point value
MPT_CONSTEXPRINLINE double ToDouble() const { return v / double(fractFact); }
MPT_CONSTEXPRINLINE friend FPInt<fractFact, T> operator+ (const FPInt<fractFact, T> &a, const FPInt<fractFact, T> &b) noexcept { return FPInt<fractFact, T>(a.v + b.v); }
MPT_CONSTEXPRINLINE friend FPInt<fractFact, T> operator- (const FPInt<fractFact, T> &a, const FPInt<fractFact, T> &b) noexcept { return FPInt<fractFact, T>(a.v - b.v); }
MPT_CONSTEXPRINLINE FPInt<fractFact, T> operator+= (const FPInt<fractFact, T> &other) noexcept { v += other.v; return *this; }
MPT_CONSTEXPRINLINE FPInt<fractFact, T> operator-= (const FPInt<fractFact, T> &other) noexcept { v -= other.v; return *this; }
MPT_CONSTEXPRINLINE friend bool operator== (const FPInt<fractFact, T> &a, const FPInt<fractFact, T> &b) noexcept { return a.v == b.v; }
MPT_CONSTEXPRINLINE friend bool operator!= (const FPInt<fractFact, T> &a, const FPInt<fractFact, T> &b) noexcept { return a.v != b.v; }
MPT_CONSTEXPRINLINE friend bool operator<= (const FPInt<fractFact, T> &a, const FPInt<fractFact, T> &b) noexcept { return a.v <= b.v; }
MPT_CONSTEXPRINLINE friend bool operator>= (const FPInt<fractFact, T> &a, const FPInt<fractFact, T> &b) noexcept { return a.v >= b.v; }
MPT_CONSTEXPRINLINE friend bool operator< (const FPInt<fractFact, T> &a, const FPInt<fractFact, T> &b) noexcept { return a.v < b.v; }
MPT_CONSTEXPRINLINE friend bool operator> (const FPInt<fractFact, T> &a, const FPInt<fractFact, T> &b) noexcept { return a.v > b.v; }
};
using TEMPO = FPInt<10000, uint32>;
using OPLPatch = std::array<uint8, 12>;
OPENMPT_NAMESPACE_END
|