1 /*
2  * ModChannel.h
3  * ------------
4  * Purpose: Module Channel header class and helpers
5  * Notes  : (currently none)
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 
11 #pragma once
12 
13 #include "openmpt/all/BuildSettings.hpp"
14 
15 #include "ModSample.h"
16 #include "ModInstrument.h"
17 #include "modcommand.h"
18 #include "Paula.h"
19 #include "tuningbase.h"
20 
21 OPENMPT_NAMESPACE_BEGIN
22 
23 class CSoundFile;
24 
25 // Mix Channel Struct
26 struct ModChannel
27 {
28 	// Envelope playback info
29 	struct EnvInfo
30 	{
31 		uint32 nEnvPosition = 0;
32 		int16 nEnvValueAtReleaseJump = NOT_YET_RELEASED;
33 		FlagSet<EnvelopeFlags> flags;
34 
ResetModChannel::EnvInfo35 		void Reset()
36 		{
37 			nEnvPosition = 0;
38 			nEnvValueAtReleaseJump = NOT_YET_RELEASED;
39 		}
40 	};
41 
42 	// Information used in the mixer (should be kept tight for better caching)
43 	SamplePosition position;     // Current play position (fixed point)
44 	SamplePosition increment;    // Sample speed relative to mixing frequency (fixed point)
45 	const void *pCurrentSample;  // Currently playing sample (nullptr if no sample is playing)
46 	int32 leftVol;               // 0...4096 (12 bits, since 16 bits + 12 bits = 28 bits = 0dB in integer mixer, see MIXING_ATTENUATION)
47 	int32 rightVol;              // Ditto
48 	int32 leftRamp;              // Ramping delta, 20.12 fixed point (see VOLUMERAMPPRECISION)
49 	int32 rightRamp;             // Ditto
50 	int32 rampLeftVol;           // Current ramping volume, 20.12 fixed point (see VOLUMERAMPPRECISION)
51 	int32 rampRightVol;          // Ditto
52 	mixsample_t nFilter_Y[2][2]; // Filter memory - two history items per sample channel
53 	mixsample_t nFilter_A0, nFilter_B0, nFilter_B1; // Filter coeffs
54 	mixsample_t nFilter_HP;
55 
56 	SmpLength nLength;
57 	SmpLength nLoopStart;
58 	SmpLength nLoopEnd;
59 	FlagSet<ChannelFlags> dwFlags;
60 	mixsample_t nROfs, nLOfs;
61 	uint32 nRampLength;
62 
63 	const ModSample *pModSample;  // Currently assigned sample slot (may already be stopped)
64 	Paula::State paulaState;
65 
66 	// Information not used in the mixer
67 	const ModInstrument *pModInstrument;  // Currently assigned instrument slot
68 	SmpLength prevNoteOffset;             // Offset for instrument-less notes for ProTracker/ScreamTracker
69 	SmpLength oldOffset;                  // Offset command memory
70 	FlagSet<ChannelFlags> dwOldFlags;     // Flags from previous tick
71 	int32 newLeftVol, newRightVol;
72 	int32 nRealVolume, nRealPan;
73 	int32 nVolume, nPan, nFadeOutVol;
74 	int32 nPeriod;  // Frequency in Hz if CSoundFile::PeriodsAreFrequencies() or using custom tuning, 4x Amiga periods otherwise
75 	int32 nC5Speed, nPortamentoDest;
76 	int32 cachedPeriod, glissandoPeriod;
77 	int32 nCalcVolume;                 // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros
78 	EnvInfo VolEnv, PanEnv, PitchEnv;  // Envelope playback info
79 	int32 nGlobalVol;                  // Channel volume (CV in ITTECH.TXT) 0...64
80 	int32 nInsVol;                     // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64
81 	int32 nAutoVibDepth;
82 	uint32 nEFxOffset;  // Offset memory for Invert Loop (EFx, .MOD only)
83 	ROWINDEX nPatternLoop;
84 	uint16 portamentoSlide;
85 	int16 nTranspose;
86 	int16 nFineTune;
87 	int16 microTuning;  // Micro-tuning / MIDI pitch wheel command
88 	int16 nVolSwing, nPanSwing;
89 	int16 nCutSwing, nResSwing;
90 	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
91 	CHANNELINDEX nMasterChn;
92 	ModCommand rowCommand;
93 	// 8-bit members
94 	ResamplingMode resamplingMode;
95 	uint8 nRestoreResonanceOnNewNote;  // See nRestorePanOnNewNote
96 	uint8 nRestoreCutoffOnNewNote;     // ditto
97 	uint8 nNote;
98 	NewNoteAction nNNA;
99 	uint8 nLastNote;  // Last note, ignoring note offs and cuts - for MIDI macros
100 	uint8 nArpeggioLastNote, nArpeggioBaseNote;  // For plugin arpeggio
101 	uint8 nNewNote, nNewIns, nOldIns, nCommand, nArpeggio;
102 	uint8 nRetrigParam, nRetrigCount;
103 	uint8 nOldVolumeSlide, nOldFineVolUpDown;
104 	uint8 nOldPortaUp, nOldPortaDown, nOldFinePortaUpDown, nOldExtraFinePortaUpDown;
105 	uint8 nOldPanSlide, nOldChnVolSlide;
106 	uint8 nOldGlobalVolSlide;
107 	uint8 nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos;
108 	uint8 nVibratoType, nVibratoSpeed, nVibratoDepth;
109 	uint8 nTremoloType, nTremoloSpeed, nTremoloDepth;
110 	uint8 nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth;
111 	int8  nPanbrelloOffset, nPanbrelloRandomMemory;
112 	uint8 nOldCmdEx, nOldVolParam, nOldTempo;
113 	uint8 nOldHiOffset;
114 	uint8 nCutOff, nResonance;
115 	uint8 nTremorCount, nTremorParam;
116 	uint8 nPatternLoopCount;
117 	uint8 nLeftVU, nRightVU;
118 	uint8 nActiveMacro;
119 	FilterMode nFilterMode;
120 	uint8 nEFxSpeed, nEFxDelay;              // memory for Invert Loop (EFx, .MOD only)
121 	uint8 noteSlideParam, noteSlideCounter;  // IMF / PTM Note Slide
122 	uint8 lastZxxParam;                      // Memory for \xx slides
123 	bool isFirstTick : 1;                    // Execute tick-0 effects on this channel? (condition differs between formats due to Pattern Delay commands)
124 	bool triggerNote : 1;                    // Trigger note on this tick on this channel if there is one?
125 	bool isPreviewNote : 1;                  // Notes preview in editor
126 	bool isPaused : 1;                       // Don't mix or increment channel position, but keep the note alive
127 	bool portaTargetReached : 1;             // Tone portamento is finished
128 
129 	//-->Variables used to make user-definable tuning modes work with pattern effects.
130 	//If true, freq should be recalculated in ReadNote() on first tick.
131 	//Currently used only for vibrato things - using in other context might be
132 	//problematic.
133 	bool m_ReCalculateFreqOnFirstTick : 1;
134 
135 	//To tell whether to calculate frequency.
136 	bool m_CalculateFreq : 1;
137 
138 	int32 m_PortamentoFineSteps, m_PortamentoTickSlide;
139 
140 	//NOTE_PCs memory.
141 	float m_plugParamValueStep, m_plugParamTargetValue;
142 	uint16 m_RowPlugParam;
143 	PLUGINDEX m_RowPlug;
144 
ClearRowCmdModChannel145 	void ClearRowCmd() { rowCommand = ModCommand(); }
146 
147 	// Get a reference to a specific envelope of this channel
GetEnvelopeModChannel148 	const EnvInfo &GetEnvelope(EnvelopeType envType) const
149 	{
150 		switch(envType)
151 		{
152 		case ENV_VOLUME:
153 		default:
154 			return VolEnv;
155 		case ENV_PANNING:
156 			return PanEnv;
157 		case ENV_PITCH:
158 			return PitchEnv;
159 		}
160 	}
161 
GetEnvelopeModChannel162 	EnvInfo &GetEnvelope(EnvelopeType envType)
163 	{
164 		return const_cast<EnvInfo &>(static_cast<const ModChannel *>(this)->GetEnvelope(envType));
165 	}
166 
ResetEnvelopesModChannel167 	void ResetEnvelopes()
168 	{
169 		VolEnv.Reset();
170 		PanEnv.Reset();
171 		PitchEnv.Reset();
172 	}
173 
174 	enum ResetFlags
175 	{
176 		resetChannelSettings = 1,  // Reload initial channel settings
177 		resetSetPosBasic     = 2,  // Reset basic runtime channel attributes
178 		resetSetPosAdvanced  = 4,  // Reset more runtime channel attributes
179 		resetSetPosFull      = resetSetPosBasic | resetSetPosAdvanced | resetChannelSettings,  // Reset all runtime channel attributes
180 		resetTotal           = resetSetPosFull,
181 	};
182 
183 	void Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel, ChannelFlags muteFlag);
184 	void Stop();
185 
IsSamplePlayingModChannel186 	bool IsSamplePlaying() const noexcept { return !increment.IsZero(); }
187 
GetVSTVolumeModChannel188 	uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; }
189 
190 	ModCommand::NOTE GetPluginNote(bool realNoteMapping) const;
191 
192 	// Check if the channel has a valid MIDI output. A return value of true implies that pModInstrument != nullptr.
HasMIDIOutputModChannel193 	bool HasMIDIOutput() const noexcept { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); }
194 	// Check if the channel uses custom tuning. A return value of true implies that pModInstrument != nullptr.
HasCustomTuningModChannel195 	bool HasCustomTuning() const noexcept { return pModInstrument != nullptr && pModInstrument->pTuning != nullptr; }
196 
197 	// Check if currently processed loop is a sustain loop. pModSample is not checked for validity!
InSustainLoopModChannel198 	bool InSustainLoop() const noexcept { return (dwFlags & (CHN_LOOP | CHN_KEYOFF)) == CHN_LOOP && pModSample->uFlags[CHN_SUSTAINLOOP]; }
199 
200 	void UpdateInstrumentVolume(const ModSample *smp, const ModInstrument *ins);
201 
202 	void SetInstrumentPan(int32 pan, const CSoundFile &sndFile);
203 
204 	void RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile);
205 
206 	// IT command S73-S7E
207 	void InstrumentControl(uint8 param, const CSoundFile &sndFile);
208 
GetMIDIPitchBendModChannel209 	int32 GetMIDIPitchBend() const noexcept { return (static_cast<int32>(microTuning) + 0x8000) / 4; }
210 };
211 
212 
213 // Default pattern channel settings
214 struct ModChannelSettings
215 {
216 #ifdef MODPLUG_TRACKER
217 	static constexpr uint32 INVALID_COLOR = 0xFFFFFFFF;
218 	uint32 color = INVALID_COLOR;   // For pattern editor
219 #endif                              // MODPLUG_TRACKER
220 	FlagSet<ChannelFlags> dwFlags;  // Channel flags
221 	uint16 nPan = 128;              // Initial pan (0...256)
222 	uint16 nVolume = 64;            // Initial channel volume (0...64)
223 	PLUGINDEX nMixPlugin = 0;       // Assigned plugin
224 
225 	mpt::charbuf<MAX_CHANNELNAME> szName;  // Channel name
226 
ResetModChannelSettings227 	void Reset()
228 	{
229 		*this = {};
230 	}
231 };
232 
233 OPENMPT_NAMESPACE_END
234