aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/soundlib/ModChannel.h
blob: b829395914aa84e38210f9858a787fb41d7e224d (plain) (blame)
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
/*
 * ModChannel.h
 * ------------
 * Purpose: Module Channel header class and helpers
 * Notes  : (currently none)
 * Authors: 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 "ModSample.h"
#include "ModInstrument.h"
#include "modcommand.h"
#include "Paula.h"
#include "tuningbase.h"

OPENMPT_NAMESPACE_BEGIN

class CSoundFile;

// Mix Channel Struct
struct ModChannel
{
	// Envelope playback info
	struct EnvInfo
	{
		uint32 nEnvPosition = 0;
		int16 nEnvValueAtReleaseJump = NOT_YET_RELEASED;
		FlagSet<EnvelopeFlags> flags;

		void Reset()
		{
			nEnvPosition = 0;
			nEnvValueAtReleaseJump = NOT_YET_RELEASED;
		}
	};

	// Information used in the mixer (should be kept tight for better caching)
	SamplePosition position;     // Current play position (fixed point)
	SamplePosition increment;    // Sample speed relative to mixing frequency (fixed point)
	const void *pCurrentSample;  // Currently playing sample (nullptr if no sample is playing)
	int32 leftVol;               // 0...4096 (12 bits, since 16 bits + 12 bits = 28 bits = 0dB in integer mixer, see MIXING_ATTENUATION)
	int32 rightVol;              // Ditto
	int32 leftRamp;              // Ramping delta, 20.12 fixed point (see VOLUMERAMPPRECISION)
	int32 rightRamp;             // Ditto
	int32 rampLeftVol;           // Current ramping volume, 20.12 fixed point (see VOLUMERAMPPRECISION)
	int32 rampRightVol;          // Ditto
	mixsample_t nFilter_Y[2][2]; // Filter memory - two history items per sample channel
	mixsample_t nFilter_A0, nFilter_B0, nFilter_B1; // Filter coeffs
	mixsample_t nFilter_HP;

	SmpLength nLength;
	SmpLength nLoopStart;
	SmpLength nLoopEnd;
	FlagSet<ChannelFlags> dwFlags;
	mixsample_t nROfs, nLOfs;
	uint32 nRampLength;

	const ModSample *pModSample;  // Currently assigned sample slot (may already be stopped)
	Paula::State paulaState;

	// Information not used in the mixer
	const ModInstrument *pModInstrument;  // Currently assigned instrument slot
	SmpLength prevNoteOffset;             // Offset for instrument-less notes for ProTracker/ScreamTracker
	SmpLength oldOffset;                  // Offset command memory
	FlagSet<ChannelFlags> dwOldFlags;     // Flags from previous tick
	int32 newLeftVol, newRightVol;
	int32 nRealVolume, nRealPan;
	int32 nVolume, nPan, nFadeOutVol;
	int32 nPeriod;  // Frequency in Hz if CSoundFile::PeriodsAreFrequencies() or using custom tuning, 4x Amiga periods otherwise
	int32 nC5Speed, nPortamentoDest;
	int32 cachedPeriod, glissandoPeriod;
	int32 nCalcVolume;                 // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros
	EnvInfo VolEnv, PanEnv, PitchEnv;  // Envelope playback info
	int32 nGlobalVol;                  // Channel volume (CV in ITTECH.TXT) 0...64
	int32 nInsVol;                     // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64
	int32 nAutoVibDepth;
	uint32 nEFxOffset;  // Offset memory for Invert Loop (EFx, .MOD only)
	ROWINDEX nPatternLoop;
	uint16 portamentoSlide;
	int16 nTranspose;
	int16 nFineTune;
	int16 microTuning;  // Micro-tuning / MIDI pitch wheel command
	int16 nVolSwing, nPanSwing;
	int16 nCutSwing, nResSwing;
	uint16 nRestorePanOnNewNote;  // If > 0, nPan should be set to nRestorePanOnNewNote - 1 on new note. Used to recover from pan swing and IT sample / instrument panning. High bit set = surround
	CHANNELINDEX nMasterChn;
	ModCommand rowCommand;
	// 8-bit members
	ResamplingMode resamplingMode;
	uint8 nRestoreResonanceOnNewNote;  // See nRestorePanOnNewNote
	uint8 nRestoreCutoffOnNewNote;     // ditto
	uint8 nNote;
	NewNoteAction nNNA;
	uint8 nLastNote;  // Last note, ignoring note offs and cuts - for MIDI macros
	uint8 nArpeggioLastNote, nArpeggioBaseNote;  // For plugin arpeggio
	uint8 nNewNote, nNewIns, nOldIns, nCommand, nArpeggio;
	uint8 nRetrigParam, nRetrigCount;
	uint8 nOldVolumeSlide, nOldFineVolUpDown;
	uint8 nOldPortaUp, nOldPortaDown, nOldFinePortaUpDown, nOldExtraFinePortaUpDown;
	uint8 nOldPanSlide, nOldChnVolSlide;
	uint8 nOldGlobalVolSlide;
	uint8 nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos;
	uint8 nVibratoType, nVibratoSpeed, nVibratoDepth;
	uint8 nTremoloType, nTremoloSpeed, nTremoloDepth;
	uint8 nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth;
	int8  nPanbrelloOffset, nPanbrelloRandomMemory;
	uint8 nOldCmdEx, nOldVolParam, nOldTempo;
	uint8 nOldHiOffset;
	uint8 nCutOff, nResonance;
	uint8 nTremorCount, nTremorParam;
	uint8 nPatternLoopCount;
	uint8 nLeftVU, nRightVU;
	uint8 nActiveMacro;
	FilterMode nFilterMode;
	uint8 nEFxSpeed, nEFxDelay;              // memory for Invert Loop (EFx, .MOD only)
	uint8 noteSlideParam, noteSlideCounter;  // IMF / PTM Note Slide
	uint8 lastZxxParam;                      // Memory for \xx slides
	bool isFirstTick : 1;                    // Execute tick-0 effects on this channel? (condition differs between formats due to Pattern Delay commands)
	bool triggerNote : 1;                    // Trigger note on this tick on this channel if there is one?
	bool isPreviewNote : 1;                  // Notes preview in editor
	bool isPaused : 1;                       // Don't mix or increment channel position, but keep the note alive
	bool portaTargetReached : 1;             // Tone portamento is finished

	//-->Variables used to make user-definable tuning modes work with pattern effects.
	//If true, freq should be recalculated in ReadNote() on first tick.
	//Currently used only for vibrato things - using in other context might be
	//problematic.
	bool m_ReCalculateFreqOnFirstTick : 1;

	//To tell whether to calculate frequency.
	bool m_CalculateFreq : 1;

	int32 m_PortamentoFineSteps, m_PortamentoTickSlide;

	//NOTE_PCs memory.
	float m_plugParamValueStep, m_plugParamTargetValue;
	uint16 m_RowPlugParam;
	PLUGINDEX m_RowPlug;

	void ClearRowCmd() { rowCommand = ModCommand(); }

	// Get a reference to a specific envelope of this channel
	const EnvInfo &GetEnvelope(EnvelopeType envType) const
	{
		switch(envType)
		{
		case ENV_VOLUME:
		default:
			return VolEnv;
		case ENV_PANNING:
			return PanEnv;
		case ENV_PITCH:
			return PitchEnv;
		}
	}

	EnvInfo &GetEnvelope(EnvelopeType envType)
	{
		return const_cast<EnvInfo &>(static_cast<const ModChannel *>(this)->GetEnvelope(envType));
	}

	void ResetEnvelopes()
	{
		VolEnv.Reset();
		PanEnv.Reset();
		PitchEnv.Reset();
	}

	enum ResetFlags
	{
		resetChannelSettings = 1,  // Reload initial channel settings
		resetSetPosBasic     = 2,  // Reset basic runtime channel attributes
		resetSetPosAdvanced  = 4,  // Reset more runtime channel attributes
		resetSetPosFull      = resetSetPosBasic | resetSetPosAdvanced | resetChannelSettings,  // Reset all runtime channel attributes
		resetTotal           = resetSetPosFull,
	};

	void Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel, ChannelFlags muteFlag);
	void Stop();

	bool IsSamplePlaying() const noexcept { return !increment.IsZero(); }

	uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; }

	ModCommand::NOTE GetPluginNote(bool realNoteMapping) const;

	// Check if the channel has a valid MIDI output. A return value of true implies that pModInstrument != nullptr.
	bool HasMIDIOutput() const noexcept { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); }
	// Check if the channel uses custom tuning. A return value of true implies that pModInstrument != nullptr.
	bool HasCustomTuning() const noexcept { return pModInstrument != nullptr && pModInstrument->pTuning != nullptr; }

	// Check if currently processed loop is a sustain loop. pModSample is not checked for validity!
	bool InSustainLoop() const noexcept { return (dwFlags & (CHN_LOOP | CHN_KEYOFF)) == CHN_LOOP && pModSample->uFlags[CHN_SUSTAINLOOP]; }

	void UpdateInstrumentVolume(const ModSample *smp, const ModInstrument *ins);

	void SetInstrumentPan(int32 pan, const CSoundFile &sndFile);
	void RestorePanAndFilter();

	void RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile);

	// IT command S73-S7E
	void InstrumentControl(uint8 param, const CSoundFile &sndFile);

	int32 GetMIDIPitchBend() const noexcept { return (static_cast<int32>(microTuning) + 0x8000) / 4; }
};


// Default pattern channel settings
struct ModChannelSettings
{
#ifdef MODPLUG_TRACKER
	static constexpr uint32 INVALID_COLOR = 0xFFFFFFFF;
	uint32 color = INVALID_COLOR;   // For pattern editor
#endif                              // MODPLUG_TRACKER
	FlagSet<ChannelFlags> dwFlags;  // Channel flags
	uint16 nPan = 128;              // Initial pan (0...256)
	uint16 nVolume = 64;            // Initial channel volume (0...64)
	PLUGINDEX nMixPlugin = 0;       // Assigned plugin

	mpt::charbuf<MAX_CHANNELNAME> szName;  // Channel name

	void Reset()
	{
		*this = {};
	}
};

OPENMPT_NAMESPACE_END