1 /*
2 * ModChannel.cpp
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 #include "stdafx.h"
12 #include "Sndfile.h"
13 #include "ModChannel.h"
14 #include "tuning.h"
15
16 OPENMPT_NAMESPACE_BEGIN
17
Reset(ResetFlags resetMask,const CSoundFile & sndFile,CHANNELINDEX sourceChannel,ChannelFlags muteFlag)18 void ModChannel::Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel, ChannelFlags muteFlag)
19 {
20 if(resetMask & resetSetPosBasic)
21 {
22 nNote = nNewNote = NOTE_NONE;
23 nNewIns = nOldIns = 0;
24 pModSample = nullptr;
25 pModInstrument = nullptr;
26 nPortamentoDest = 0;
27 nCommand = CMD_NONE;
28 nPatternLoopCount = 0;
29 nPatternLoop = 0;
30 nFadeOutVol = 0;
31 dwFlags.set(CHN_KEYOFF | CHN_NOTEFADE);
32 dwOldFlags.reset();
33 //IT compatibility 15. Retrigger
34 if(sndFile.m_playBehaviour[kITRetrigger])
35 {
36 nRetrigParam = 1;
37 nRetrigCount = 0;
38 }
39 microTuning = 0;
40 nTremorCount = 0;
41 nEFxSpeed = 0;
42 prevNoteOffset = 0;
43 lastZxxParam = 0xFF;
44 isFirstTick = false;
45 triggerNote = false;
46 isPreviewNote = false;
47 isPaused = false;
48 portaTargetReached = false;
49 rowCommand.Clear();
50 }
51
52 if(resetMask & resetSetPosAdvanced)
53 {
54 increment = SamplePosition(0);
55 nPeriod = 0;
56 position.Set(0);
57 nLength = 0;
58 nLoopStart = 0;
59 nLoopEnd = 0;
60 nROfs = nLOfs = 0;
61 pModSample = nullptr;
62 pModInstrument = nullptr;
63 nCutOff = 0x7F;
64 nResonance = 0;
65 nFilterMode = FilterMode::LowPass;
66 rightVol = leftVol = 0;
67 newRightVol = newLeftVol = 0;
68 rightRamp = leftRamp = 0;
69 nVolume = 0; // Needs to be 0 for SMP_NODEFAULTVOLUME flag
70 nVibratoPos = nTremoloPos = nPanbrelloPos = 0;
71 nOldHiOffset = 0;
72 nLeftVU = nRightVU = 0;
73
74 // Custom tuning related
75 m_ReCalculateFreqOnFirstTick = false;
76 m_CalculateFreq = false;
77 m_PortamentoFineSteps = 0;
78 m_PortamentoTickSlide = 0;
79 }
80
81 if(resetMask & resetChannelSettings)
82 {
83 if(sourceChannel < MAX_BASECHANNELS)
84 {
85 dwFlags = sndFile.ChnSettings[sourceChannel].dwFlags;
86 nPan = sndFile.ChnSettings[sourceChannel].nPan;
87 nGlobalVol = sndFile.ChnSettings[sourceChannel].nVolume;
88 if(dwFlags[CHN_MUTE])
89 {
90 dwFlags.reset(CHN_MUTE);
91 dwFlags.set(muteFlag);
92 }
93 } else
94 {
95 dwFlags.reset();
96 nPan = 128;
97 nGlobalVol = 64;
98 }
99 nRestorePanOnNewNote = 0;
100 nRestoreCutoffOnNewNote = 0;
101 nRestoreResonanceOnNewNote = 0;
102 }
103 }
104
105
Stop()106 void ModChannel::Stop()
107 {
108 nPeriod = 0;
109 increment.Set(0);
110 position.Set(0);
111 nLeftVU = nRightVU = 0;
112 nVolume = 0;
113 pCurrentSample = nullptr;
114 }
115
116
UpdateInstrumentVolume(const ModSample * smp,const ModInstrument * ins)117 void ModChannel::UpdateInstrumentVolume(const ModSample *smp, const ModInstrument *ins)
118 {
119 nInsVol = 64;
120 if(smp != nullptr)
121 nInsVol = smp->nGlobalVol;
122 if(ins != nullptr)
123 nInsVol = (nInsVol * ins->nGlobalVol) / 64;
124 }
125
126
GetPluginNote(bool realNoteMapping) const127 ModCommand::NOTE ModChannel::GetPluginNote(bool realNoteMapping) const
128 {
129 if(nArpeggioLastNote != NOTE_NONE)
130 {
131 // If an arpeggio is playing, this definitely the last playing note, which may be different from the arpeggio base note stored in nNote.
132 return nArpeggioLastNote;
133 }
134 ModCommand::NOTE plugNote = mpt::saturate_cast<ModCommand::NOTE>(nNote - nTranspose);
135 // Caution: When in compatible mode, ModChannel::nNote stores the "real" note, not the mapped note!
136 if(realNoteMapping && pModInstrument != nullptr && plugNote >= NOTE_MIN && plugNote < (std::size(pModInstrument->NoteMap) + NOTE_MIN))
137 {
138 plugNote = pModInstrument->NoteMap[plugNote - NOTE_MIN];
139 }
140 return plugNote;
141 }
142
143
SetInstrumentPan(int32 pan,const CSoundFile & sndFile)144 void ModChannel::SetInstrumentPan(int32 pan, const CSoundFile &sndFile)
145 {
146 // IT compatibility: Instrument and sample panning does not override channel panning
147 // Test case: PanResetInstr.it
148 if(sndFile.m_playBehaviour[kITDoNotOverrideChannelPan])
149 {
150 nRestorePanOnNewNote = static_cast<uint16>(nPan + 1);
151 if(dwFlags[CHN_SURROUND])
152 nRestorePanOnNewNote |= 0x8000;
153 }
154 nPan = pan;
155 }
156
157
RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor,Tuning::NOTEINDEXTYPE arpeggioSteps,const CSoundFile & sndFile)158 void ModChannel::RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile)
159 {
160 if(!HasCustomTuning())
161 return;
162
163 ModCommand::NOTE note = ModCommand::IsNote(nNote) ? nNote : nLastNote;
164
165 if(sndFile.m_playBehaviour[kITRealNoteMapping] && note >= NOTE_MIN && note <= NOTE_MAX)
166 note = pModInstrument->NoteMap[note - NOTE_MIN];
167
168 nPeriod = mpt::saturate_round<uint32>(nC5Speed * vibratoFactor * pModInstrument->pTuning->GetRatio(note - NOTE_MIDDLEC + arpeggioSteps, nFineTune + m_PortamentoFineSteps) * (1 << FREQ_FRACBITS));
169 }
170
171
172 // IT command S73-S7E
InstrumentControl(uint8 param,const CSoundFile & sndFile)173 void ModChannel::InstrumentControl(uint8 param, const CSoundFile &sndFile)
174 {
175 param &= 0x0F;
176 switch(param)
177 {
178 case 0x3: nNNA = NewNoteAction::NoteCut; break;
179 case 0x4: nNNA = NewNoteAction::Continue; break;
180 case 0x5: nNNA = NewNoteAction::NoteOff; break;
181 case 0x6: nNNA = NewNoteAction::NoteFade; break;
182 case 0x7: VolEnv.flags.reset(ENV_ENABLED); break;
183 case 0x8: VolEnv.flags.set(ENV_ENABLED); break;
184 case 0x9: PanEnv.flags.reset(ENV_ENABLED); break;
185 case 0xA: PanEnv.flags.set(ENV_ENABLED); break;
186 case 0xB: PitchEnv.flags.reset(ENV_ENABLED); break;
187 case 0xC: PitchEnv.flags.set(ENV_ENABLED); break;
188 case 0xD: // S7D: Enable pitch envelope, force to play as pitch envelope
189 case 0xE: // S7E: Enable pitch envelope, force to play as filter envelope
190 if(sndFile.GetType() == MOD_TYPE_MPT)
191 {
192 PitchEnv.flags.set(ENV_ENABLED);
193 PitchEnv.flags.set(ENV_FILTER, param != 0xD);
194 }
195 break;
196 }
197 }
198
199
200 OPENMPT_NAMESPACE_END
201