1 /*
2 * Sndmix.cpp
3 * -----------
4 * Purpose: Pattern playback, effect processing
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
14 #include "Sndfile.h"
15 #include "MixerLoops.h"
16 #include "MIDIEvents.h"
17 #include "Tables.h"
18 #ifdef MODPLUG_TRACKER
19 #include "../mptrack/TrackerSettings.h"
20 #endif // MODPLUG_TRACKER
21 #ifndef NO_PLUGINS
22 #include "plugins/PlugInterface.h"
23 #endif // NO_PLUGINS
24 #include "OPL.h"
25
26 OPENMPT_NAMESPACE_BEGIN
27
28 // Log tables for pre-amp
29 // Pre-amp (or more precisely: Pre-attenuation) depends on the number of channels,
30 // Which this table takes care of.
31 static constexpr uint8 PreAmpTable[16] =
32 {
33 0x60, 0x60, 0x60, 0x70, // 0-7
34 0x80, 0x88, 0x90, 0x98, // 8-15
35 0xA0, 0xA4, 0xA8, 0xAC, // 16-23
36 0xB0, 0xB4, 0xB8, 0xBC, // 24-31
37 };
38
39 #ifndef NO_AGC
40 static constexpr uint8 PreAmpAGCTable[16] =
41 {
42 0x60, 0x60, 0x60, 0x64,
43 0x68, 0x70, 0x78, 0x80,
44 0x84, 0x88, 0x8C, 0x90,
45 0x92, 0x94, 0x96, 0x98,
46 };
47 #endif
48
49
SetMixerSettings(const MixerSettings & mixersettings)50 void CSoundFile::SetMixerSettings(const MixerSettings &mixersettings)
51 {
52 SetPreAmp(mixersettings.m_nPreAmp); // adjust agc
53 bool reset = false;
54 if(
55 (mixersettings.gdwMixingFreq != m_MixerSettings.gdwMixingFreq)
56 ||
57 (mixersettings.gnChannels != m_MixerSettings.gnChannels)
58 ||
59 (mixersettings.MixerFlags != m_MixerSettings.MixerFlags))
60 reset = true;
61 m_MixerSettings = mixersettings;
62 InitPlayer(reset);
63 }
64
65
SetResamplerSettings(const CResamplerSettings & resamplersettings)66 void CSoundFile::SetResamplerSettings(const CResamplerSettings &resamplersettings)
67 {
68 m_Resampler.m_Settings = resamplersettings;
69 m_Resampler.UpdateTables();
70 InitAmigaResampler();
71 }
72
73
InitPlayer(bool bReset)74 void CSoundFile::InitPlayer(bool bReset)
75 {
76 if(bReset)
77 {
78 ResetMixStat();
79 m_dryLOfsVol = m_dryROfsVol = 0;
80 m_surroundLOfsVol = m_surroundROfsVol = 0;
81 InitAmigaResampler();
82 }
83 m_Resampler.UpdateTables();
84 #ifndef NO_REVERB
85 m_Reverb.Initialize(bReset, m_RvbROfsVol, m_RvbLOfsVol, m_MixerSettings.gdwMixingFreq);
86 #endif
87 #ifndef NO_DSP
88 m_Surround.Initialize(bReset, m_MixerSettings.gdwMixingFreq);
89 #endif
90 #ifndef NO_DSP
91 m_MegaBass.Initialize(bReset, m_MixerSettings.gdwMixingFreq);
92 #endif
93 #ifndef NO_EQ
94 m_EQ.Initialize(bReset, m_MixerSettings.gdwMixingFreq);
95 #endif
96 #ifndef NO_AGC
97 m_AGC.Initialize(bReset, m_MixerSettings.gdwMixingFreq);
98 #endif
99 #ifndef NO_DSP
100 m_BitCrush.Initialize(bReset, m_MixerSettings.gdwMixingFreq);
101 #endif
102 if(m_opl)
103 {
104 m_opl->Initialize(m_MixerSettings.gdwMixingFreq);
105 }
106 }
107
108
FadeSong(uint32 msec)109 bool CSoundFile::FadeSong(uint32 msec)
110 {
111 samplecount_t nsamples = Util::muldiv(msec, m_MixerSettings.gdwMixingFreq, 1000);
112 if (nsamples <= 0) return false;
113 if (nsamples > 0x100000) nsamples = 0x100000;
114 m_PlayState.m_nBufferCount = nsamples;
115 int32 nRampLength = static_cast<int32>(m_PlayState.m_nBufferCount);
116 // Ramp everything down
117 for (uint32 noff=0; noff < m_nMixChannels; noff++)
118 {
119 ModChannel &pramp = m_PlayState.Chn[m_PlayState.ChnMix[noff]];
120 pramp.newRightVol = pramp.newLeftVol = 0;
121 pramp.leftRamp = -pramp.leftVol * (1 << VOLUMERAMPPRECISION) / nRampLength;
122 pramp.rightRamp = -pramp.rightVol * (1 << VOLUMERAMPPRECISION) / nRampLength;
123 pramp.rampLeftVol = pramp.leftVol * (1 << VOLUMERAMPPRECISION);
124 pramp.rampRightVol = pramp.rightVol * (1 << VOLUMERAMPPRECISION);
125 pramp.nRampLength = nRampLength;
126 pramp.dwFlags.set(CHN_VOLUMERAMP);
127 }
128 return true;
129 }
130
131
132 // Apply stereo separation factor on an interleaved stereo/quad stream.
133 // count = Number of stereo sample pairs to process
134 // separation = -256...256 (negative values = swap L/R, 0 = mono, 128 = normal)
ApplyStereoSeparation(mixsample_t * mixBuf,std::size_t count,int32 separation)135 static void ApplyStereoSeparation(mixsample_t *mixBuf, std::size_t count, int32 separation)
136 {
137 #ifdef MPT_INTMIXER
138 const mixsample_t factor_num = separation; // 128 =^= 1.0f
139 const mixsample_t factor_den = MixerSettings::StereoSeparationScale; // 128
140 const mixsample_t normalize_den = 2; // mid/side pre/post normalization
141 const mixsample_t mid_den = normalize_den;
142 const mixsample_t side_num = factor_num;
143 const mixsample_t side_den = factor_den * normalize_den;
144 #else
145 const float normalize_factor = 0.5f; // cumulative mid/side normalization factor (1/sqrt(2))*(1/sqrt(2))
146 const float factor = static_cast<float>(separation) / static_cast<float>(MixerSettings::StereoSeparationScale); // sep / 128
147 const float mid_factor = normalize_factor;
148 const float side_factor = factor * normalize_factor;
149 #endif
150 for(std::size_t i = 0; i < count; i++)
151 {
152 mixsample_t l = mixBuf[0];
153 mixsample_t r = mixBuf[1];
154 mixsample_t m = l + r;
155 mixsample_t s = l - r;
156 #ifdef MPT_INTMIXER
157 m /= mid_den;
158 s = Util::muldiv(s, side_num, side_den);
159 #else
160 m *= mid_factor;
161 s *= side_factor;
162 #endif
163 l = m + s;
164 r = m - s;
165 mixBuf[0] = l;
166 mixBuf[1] = r;
167 mixBuf += 2;
168 }
169 }
170
171
ApplyStereoSeparation(mixsample_t * SoundFrontBuffer,mixsample_t * SoundRearBuffer,std::size_t channels,std::size_t countChunk,int32 separation)172 static void ApplyStereoSeparation(mixsample_t *SoundFrontBuffer, mixsample_t *SoundRearBuffer, std::size_t channels, std::size_t countChunk, int32 separation)
173 {
174 if(separation == MixerSettings::StereoSeparationScale)
175 { // identity
176 return;
177 }
178 if(channels >= 2) ApplyStereoSeparation(SoundFrontBuffer, countChunk, separation);
179 if(channels >= 4) ApplyStereoSeparation(SoundRearBuffer , countChunk, separation);
180 }
181
182
ProcessInputChannels(IAudioSource & source,std::size_t countChunk)183 void CSoundFile::ProcessInputChannels(IAudioSource &source, std::size_t countChunk)
184 {
185 for(std::size_t channel = 0; channel < NUMMIXINPUTBUFFERS; ++channel)
186 {
187 std::fill(&(MixInputBuffer[channel][0]), &(MixInputBuffer[channel][countChunk]), 0);
188 }
189 mixsample_t * buffers[NUMMIXINPUTBUFFERS];
190 for(std::size_t channel = 0; channel < NUMMIXINPUTBUFFERS; ++channel)
191 {
192 buffers[channel] = MixInputBuffer[channel];
193 }
194 source.Process(mpt::audio_span_planar(buffers, m_MixerSettings.NumInputChannels, countChunk));
195 }
196
197
198 // Read one tick but skip all expensive rendering options
ReadOneTick()199 CSoundFile::samplecount_t CSoundFile::ReadOneTick()
200 {
201 const auto origMaxMixChannels = m_MixerSettings.m_nMaxMixChannels;
202 m_MixerSettings.m_nMaxMixChannels = 0;
203 while(m_PlayState.m_nBufferCount)
204 {
205 auto framesToRender = std::min(m_PlayState.m_nBufferCount, samplecount_t(MIXBUFFERSIZE));
206 CreateStereoMix(framesToRender);
207 m_PlayState.m_nBufferCount -= framesToRender;
208 m_PlayState.m_lTotalSampleCount += framesToRender;
209 }
210 m_MixerSettings.m_nMaxMixChannels = origMaxMixChannels;
211 if(ReadNote())
212 return m_PlayState.m_nBufferCount;
213 else
214 return 0;
215 }
216
217
Read(samplecount_t count,IAudioTarget & target,IAudioSource & source,std::optional<std::reference_wrapper<IMonitorOutput>> outputMonitor,std::optional<std::reference_wrapper<IMonitorInput>> inputMonitor)218 CSoundFile::samplecount_t CSoundFile::Read(samplecount_t count, IAudioTarget &target, IAudioSource &source, std::optional<std::reference_wrapper<IMonitorOutput>> outputMonitor, std::optional<std::reference_wrapper<IMonitorInput>> inputMonitor)
219 {
220 MPT_ASSERT_ALWAYS(m_MixerSettings.IsValid());
221
222 samplecount_t countRendered = 0;
223 samplecount_t countToRender = count;
224
225 while(!m_SongFlags[SONG_ENDREACHED] && countToRender > 0)
226 {
227
228 // Update Channel Data
229 if(!m_PlayState.m_nBufferCount)
230 {
231 // Last tick or fade completely processed, find out what to do next
232
233 if(m_SongFlags[SONG_FADINGSONG])
234 {
235 // Song was faded out
236 m_SongFlags.set(SONG_ENDREACHED);
237 } else if(ReadNote())
238 {
239 // Render next tick (normal progress)
240 MPT_ASSERT(m_PlayState.m_nBufferCount > 0);
241 #ifdef MODPLUG_TRACKER
242 // Save pattern cue points for WAV rendering here (if we reached a new pattern, that is.)
243 if(m_PatternCuePoints != nullptr && (m_PatternCuePoints->empty() || m_PlayState.m_nCurrentOrder != m_PatternCuePoints->back().order))
244 {
245 PatternCuePoint cue;
246 cue.offset = countRendered;
247 cue.order = m_PlayState.m_nCurrentOrder;
248 cue.processed = false; // We don't know the base offset in the file here. It has to be added in the main conversion loop.
249 m_PatternCuePoints->push_back(cue);
250 }
251 #endif
252 } else
253 {
254 // No new pattern data
255 #ifdef MODPLUG_TRACKER
256 if((m_nMaxOrderPosition) && (m_PlayState.m_nCurrentOrder >= m_nMaxOrderPosition))
257 {
258 m_SongFlags.set(SONG_ENDREACHED);
259 }
260 #endif // MODPLUG_TRACKER
261 if(IsRenderingToDisc())
262 {
263 // Disable song fade when rendering or when requested in libopenmpt.
264 m_SongFlags.set(SONG_ENDREACHED);
265 } else
266 { // end of song reached, fade it out
267 if(FadeSong(FADESONGDELAY)) // sets m_nBufferCount xor returns false
268 { // FadeSong sets m_nBufferCount here
269 MPT_ASSERT(m_PlayState.m_nBufferCount > 0);
270 m_SongFlags.set(SONG_FADINGSONG);
271 } else
272 {
273 m_SongFlags.set(SONG_ENDREACHED);
274 }
275 }
276 }
277
278 }
279
280 if(m_SongFlags[SONG_ENDREACHED])
281 {
282 // Mix done.
283
284 // If we decide to continue the mix (possible in libopenmpt), the tick count
285 // is valid right now (0), meaning that no new row data will be processed.
286 // This would effectively prolong the last played row.
287 m_PlayState.m_nTickCount = m_PlayState.TicksOnRow();
288 break;
289 }
290
291 MPT_ASSERT(m_PlayState.m_nBufferCount > 0); // assert that we have actually something to do
292
293 const samplecount_t countChunk = std::min({ static_cast<samplecount_t>(MIXBUFFERSIZE), static_cast<samplecount_t>(m_PlayState.m_nBufferCount), static_cast<samplecount_t>(countToRender) });
294
295 if(m_MixerSettings.NumInputChannels > 0)
296 {
297 ProcessInputChannels(source, countChunk);
298 }
299
300 if(inputMonitor)
301 {
302 mixsample_t *buffers[NUMMIXINPUTBUFFERS];
303 for(std::size_t channel = 0; channel < NUMMIXINPUTBUFFERS; ++channel)
304 {
305 buffers[channel] = MixInputBuffer[channel];
306 }
307 inputMonitor->get().Process(mpt::audio_span_planar<const mixsample_t>(buffers, m_MixerSettings.NumInputChannels, countChunk));
308 }
309
310 CreateStereoMix(countChunk);
311
312 if(m_opl)
313 {
314 m_opl->Mix(MixSoundBuffer, countChunk, m_OPLVolumeFactor * m_nVSTiVolume / 48);
315 }
316
317 #ifndef NO_REVERB
318 m_Reverb.Process(MixSoundBuffer, ReverbSendBuffer, m_RvbROfsVol, m_RvbLOfsVol, countChunk);
319 #endif // NO_REVERB
320
321 #ifndef NO_PLUGINS
322 if(m_loadedPlugins)
323 {
324 ProcessPlugins(countChunk);
325 }
326 #endif // NO_PLUGINS
327
328 if(m_MixerSettings.gnChannels == 1)
329 {
330 MonoFromStereo(MixSoundBuffer, countChunk);
331 }
332
333 if(m_PlayConfig.getGlobalVolumeAppliesToMaster())
334 {
335 ProcessGlobalVolume(countChunk);
336 }
337
338 if(m_MixerSettings.m_nStereoSeparation != MixerSettings::StereoSeparationScale)
339 {
340 ProcessStereoSeparation(countChunk);
341 }
342
343 if(m_MixerSettings.DSPMask)
344 {
345 ProcessDSP(countChunk);
346 }
347
348 if(m_MixerSettings.gnChannels == 4)
349 {
350 InterleaveFrontRear(MixSoundBuffer, MixRearBuffer, countChunk);
351 }
352
353 if(outputMonitor)
354 {
355 outputMonitor->get().Process(mpt::audio_span_interleaved<const mixsample_t>(MixSoundBuffer, m_MixerSettings.gnChannels, countChunk));
356 }
357
358 target.Process(mpt::audio_span_interleaved<mixsample_t>(MixSoundBuffer, m_MixerSettings.gnChannels, countChunk));
359
360 // Buffer ready
361 countRendered += countChunk;
362 countToRender -= countChunk;
363 m_PlayState.m_nBufferCount -= countChunk;
364 m_PlayState.m_lTotalSampleCount += countChunk;
365
366 #ifdef MODPLUG_TRACKER
367 if(IsRenderingToDisc())
368 {
369 // Stop playback on F00 if no more voices are active.
370 // F00 sets the tick count to 65536 in FT2, so it just generates a reaaaally long row.
371 // Usually this command can be found at the end of a song to effectively stop playback.
372 // Since we don't want to render hours of silence, we are going to check if there are
373 // still any channels playing, and if that is no longer the case, we stop playback at
374 // the end of the next tick.
375 if(m_PlayState.m_nMusicSpeed == uint16_max && (m_nMixStat == 0 || m_PlayState.m_nGlobalVolume == 0) && GetType() == MOD_TYPE_XM && !m_PlayState.m_nBufferCount)
376 {
377 m_SongFlags.set(SONG_ENDREACHED);
378 }
379 }
380 #endif // MODPLUG_TRACKER
381 }
382
383 // mix done
384
385 return countRendered;
386
387 }
388
389
ProcessDSP(uint32 countChunk)390 void CSoundFile::ProcessDSP(uint32 countChunk)
391 {
392 #ifndef NO_DSP
393 if(m_MixerSettings.DSPMask & SNDDSP_SURROUND)
394 {
395 m_Surround.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels);
396 }
397 #endif // NO_DSP
398
399 #ifndef NO_DSP
400 if(m_MixerSettings.DSPMask & SNDDSP_MEGABASS)
401 {
402 m_MegaBass.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels);
403 }
404 #endif // NO_DSP
405
406 #ifndef NO_EQ
407 if(m_MixerSettings.DSPMask & SNDDSP_EQ)
408 {
409 m_EQ.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels);
410 }
411 #endif // NO_EQ
412
413 #ifndef NO_AGC
414 if(m_MixerSettings.DSPMask & SNDDSP_AGC)
415 {
416 m_AGC.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels);
417 }
418 #endif // NO_AGC
419
420 #ifndef NO_DSP
421 if(m_MixerSettings.DSPMask & SNDDSP_BITCRUSH)
422 {
423 m_BitCrush.Process(MixSoundBuffer, MixRearBuffer, countChunk, m_MixerSettings.gnChannels);
424 }
425 #endif // NO_DSP
426
427 #if defined(NO_DSP) && defined(NO_EQ) && defined(NO_AGC)
428 MPT_UNREFERENCED_PARAMETER(countChunk);
429 #endif
430 }
431
432
433 /////////////////////////////////////////////////////////////////////////////
434 // Handles navigation/effects
435
ProcessRow()436 bool CSoundFile::ProcessRow()
437 {
438 while(++m_PlayState.m_nTickCount >= m_PlayState.TicksOnRow())
439 {
440 const auto [ignoreRow, patternTransition] = NextRow(m_PlayState, m_SongFlags[SONG_BREAKTOROW]);
441
442 #ifdef MODPLUG_TRACKER
443 if(patternTransition)
444 {
445 HandlePatternTransitionEvents();
446 }
447 // "Lock row" editing feature
448 if(m_lockRowStart != ROWINDEX_INVALID && (m_PlayState.m_nRow < m_lockRowStart || m_PlayState.m_nRow > m_lockRowEnd) && !IsRenderingToDisc())
449 {
450 m_PlayState.m_nRow = m_lockRowStart;
451 }
452 // "Lock order" editing feature
453 if(Order().IsPositionLocked(m_PlayState.m_nCurrentOrder) && !IsRenderingToDisc())
454 {
455 m_PlayState.m_nCurrentOrder = m_lockOrderStart;
456 }
457 #else
458 MPT_UNUSED_VARIABLE(patternTransition);
459 #endif // MODPLUG_TRACKER
460
461 // Check if pattern is valid
462 if(!m_SongFlags[SONG_PATTERNLOOP])
463 {
464 m_PlayState.m_nPattern = (m_PlayState.m_nCurrentOrder < Order().size()) ? Order()[m_PlayState.m_nCurrentOrder] : Order.GetInvalidPatIndex();
465 if (m_PlayState.m_nPattern < Patterns.Size() && !Patterns[m_PlayState.m_nPattern].IsValid()) m_PlayState.m_nPattern = Order.GetIgnoreIndex();
466 while (m_PlayState.m_nPattern >= Patterns.Size())
467 {
468 // End of song?
469 if ((m_PlayState.m_nPattern == Order.GetInvalidPatIndex()) || (m_PlayState.m_nCurrentOrder >= Order().size()))
470 {
471 ORDERINDEX restartPosOverride = Order().GetRestartPos();
472 if(restartPosOverride == 0 && m_PlayState.m_nCurrentOrder <= Order().size() && m_PlayState.m_nCurrentOrder > 0)
473 {
474 // Subtune detection. Subtunes are separated by "---" order items, so if we're in a
475 // subtune and there's no restart position, we go to the first order of the subtune
476 // (i.e. the first order after the previous "---" item)
477 for(ORDERINDEX ord = m_PlayState.m_nCurrentOrder - 1; ord > 0; ord--)
478 {
479 if(Order()[ord] == Order.GetInvalidPatIndex())
480 {
481 // Jump back to first order of this subtune
482 restartPosOverride = ord + 1;
483 break;
484 }
485 }
486 }
487
488 // If channel resetting is disabled in MPT, we will emulate a pattern break (and we always do it if we're not in MPT)
489 #ifdef MODPLUG_TRACKER
490 if(!(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_RESETCHANNELS))
491 #endif // MODPLUG_TRACKER
492 {
493 m_SongFlags.set(SONG_BREAKTOROW);
494 }
495
496 if (restartPosOverride == 0 && !m_SongFlags[SONG_BREAKTOROW])
497 {
498 //rewbs.instroVSTi: stop all VSTi at end of song, if looping.
499 StopAllVsti();
500 m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
501 m_PlayState.m_nMusicTempo = m_nDefaultTempo;
502 m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume;
503 for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++)
504 {
505 auto &chn = m_PlayState.Chn[i];
506 if(chn.dwFlags[CHN_ADLIB] && m_opl)
507 {
508 m_opl->NoteCut(i);
509 }
510 chn.dwFlags.set(CHN_NOTEFADE | CHN_KEYOFF);
511 chn.nFadeOutVol = 0;
512
513 if(i < m_nChannels)
514 {
515 chn.nGlobalVol = ChnSettings[i].nVolume;
516 chn.nVolume = ChnSettings[i].nVolume;
517 chn.nPan = ChnSettings[i].nPan;
518 chn.nPanSwing = chn.nVolSwing = 0;
519 chn.nCutSwing = chn.nResSwing = 0;
520 chn.nOldVolParam = 0;
521 chn.oldOffset = 0;
522 chn.nOldHiOffset = 0;
523 chn.nPortamentoDest = 0;
524
525 if(!chn.nLength)
526 {
527 chn.dwFlags = ChnSettings[i].dwFlags;
528 chn.nLoopStart = 0;
529 chn.nLoopEnd = 0;
530 chn.pModInstrument = nullptr;
531 chn.pModSample = nullptr;
532 }
533 }
534 }
535 }
536
537 //Handle Repeat position
538 m_PlayState.m_nCurrentOrder = restartPosOverride;
539 m_SongFlags.reset(SONG_BREAKTOROW);
540 //If restart pos points to +++, move along
541 while(m_PlayState.m_nCurrentOrder < Order().size() && Order()[m_PlayState.m_nCurrentOrder] == Order.GetIgnoreIndex())
542 {
543 m_PlayState.m_nCurrentOrder++;
544 }
545 //Check for end of song or bad pattern
546 if (m_PlayState.m_nCurrentOrder >= Order().size()
547 || !Order().IsValidPat(m_PlayState.m_nCurrentOrder))
548 {
549 m_visitedRows.Initialize(true);
550 return false;
551 }
552 } else
553 {
554 m_PlayState.m_nCurrentOrder++;
555 }
556
557 if (m_PlayState.m_nCurrentOrder < Order().size())
558 m_PlayState.m_nPattern = Order()[m_PlayState.m_nCurrentOrder];
559 else
560 m_PlayState.m_nPattern = Order.GetInvalidPatIndex();
561
562 if (m_PlayState.m_nPattern < Patterns.Size() && !Patterns[m_PlayState.m_nPattern].IsValid())
563 m_PlayState.m_nPattern = Order.GetIgnoreIndex();
564 }
565 m_PlayState.m_nNextOrder = m_PlayState.m_nCurrentOrder;
566
567 #ifdef MODPLUG_TRACKER
568 if ((m_nMaxOrderPosition) && (m_PlayState.m_nCurrentOrder >= m_nMaxOrderPosition)) return false;
569 #endif // MODPLUG_TRACKER
570 }
571
572 // Weird stuff?
573 if (!Patterns.IsValidPat(m_PlayState.m_nPattern))
574 return false;
575 // Did we jump to an invalid row?
576 if (m_PlayState.m_nRow >= Patterns[m_PlayState.m_nPattern].GetNumRows()) m_PlayState.m_nRow = 0;
577
578 // Has this row been visited before? We might want to stop playback now.
579 // But: We will not mark the row as modified if the song is not in loop mode but
580 // the pattern loop (editor flag, not to be confused with the pattern loop effect)
581 // flag is set - because in that case, the module would stop after the first pattern loop...
582 const bool overrideLoopCheck = (m_nRepeatCount != -1) && m_SongFlags[SONG_PATTERNLOOP];
583 if(!overrideLoopCheck && m_visitedRows.Visit(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, m_PlayState.Chn, ignoreRow))
584 {
585 if(m_nRepeatCount)
586 {
587 // repeat count == -1 means repeat infinitely.
588 if(m_nRepeatCount > 0)
589 {
590 m_nRepeatCount--;
591 }
592 // Forget all but the current row.
593 m_visitedRows.Initialize(true);
594 m_visitedRows.Visit(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, m_PlayState.Chn, ignoreRow);
595 } else
596 {
597 #ifdef MODPLUG_TRACKER
598 // Let's check again if this really is the end of the song.
599 // The visited rows vector might have been screwed up while editing...
600 // This is of course not possible during rendering to WAV, so we ignore that case.
601 bool isReallyAtEnd = IsRenderingToDisc();
602 if(!isReallyAtEnd)
603 {
604 for(const auto &t : GetLength(eNoAdjust, GetLengthTarget(true)))
605 {
606 if(t.lastOrder == m_PlayState.m_nCurrentOrder && t.lastRow == m_PlayState.m_nRow)
607 {
608 isReallyAtEnd = true;
609 break;
610 }
611 }
612 }
613
614 if(isReallyAtEnd)
615 {
616 // This is really the song's end!
617 m_visitedRows.Initialize(true);
618 return false;
619 } else
620 {
621 // Ok, this is really dirty, but we have to update the visited rows vector...
622 GetLength(eAdjustOnlyVisitedRows, GetLengthTarget(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow));
623 }
624 #else
625 if(m_SongFlags[SONG_PLAYALLSONGS])
626 {
627 // When playing all subsongs consecutively, first search for any hidden subsongs...
628 if(!m_visitedRows.GetFirstUnvisitedRow(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, true))
629 {
630 // ...and then try the next sequence.
631 m_PlayState.m_nNextOrder = m_PlayState.m_nCurrentOrder = 0;
632 m_PlayState.m_nNextRow = m_PlayState.m_nRow = 0;
633 if(Order.GetCurrentSequenceIndex() >= Order.GetNumSequences() - 1)
634 {
635 Order.SetSequence(0);
636 m_visitedRows.Initialize(true);
637 return false;
638 }
639 Order.SetSequence(Order.GetCurrentSequenceIndex() + 1);
640 m_visitedRows.Initialize(true);
641 }
642 // When jumping to the next subsong, stop all playing notes from the previous song...
643 const auto muteFlag = CSoundFile::GetChannelMuteFlag();
644 for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++)
645 m_PlayState.Chn[i].Reset(ModChannel::resetSetPosFull, *this, i, muteFlag);
646 StopAllVsti();
647 // ...and the global playback information.
648 m_PlayState.m_nMusicSpeed = m_nDefaultSpeed;
649 m_PlayState.m_nMusicTempo = m_nDefaultTempo;
650 m_PlayState.m_nGlobalVolume = m_nDefaultGlobalVolume;
651
652 m_PlayState.m_nNextOrder = m_PlayState.m_nCurrentOrder;
653 m_PlayState.m_nNextRow = m_PlayState.m_nRow;
654 if(Order().size() > m_PlayState.m_nCurrentOrder)
655 m_PlayState.m_nPattern = Order()[m_PlayState.m_nCurrentOrder];
656 m_visitedRows.Visit(m_PlayState.m_nCurrentOrder, m_PlayState.m_nRow, m_PlayState.Chn, ignoreRow);
657 if (!Patterns.IsValidPat(m_PlayState.m_nPattern))
658 return false;
659 } else
660 {
661 m_visitedRows.Initialize(true);
662 return false;
663 }
664 #endif // MODPLUG_TRACKER
665 }
666 }
667
668 SetupNextRow(m_PlayState, m_SongFlags[SONG_PATTERNLOOP]);
669
670 // Reset channel values
671 ModCommand *m = Patterns[m_PlayState.m_nPattern].GetpModCommand(m_PlayState.m_nRow, 0);
672 for (ModChannel *pChn = m_PlayState.Chn, *pEnd = pChn + m_nChannels; pChn != pEnd; pChn++, m++)
673 {
674 // First, handle some quirks that happen after the last tick of the previous row...
675 if(m_playBehaviour[KST3PortaAfterArpeggio]
676 && pChn->nCommand == CMD_ARPEGGIO // Previous row state!
677 && (m->command == CMD_PORTAMENTOUP || m->command == CMD_PORTAMENTODOWN))
678 {
679 // In ST3, a portamento immediately following an arpeggio continues where the arpeggio left off.
680 // Test case: PortaAfterArp.s3m
681 pChn->nPeriod = GetPeriodFromNote(pChn->nArpeggioLastNote, pChn->nFineTune, pChn->nC5Speed);
682 }
683
684 if(m_playBehaviour[kMODOutOfRangeNoteDelay]
685 && !m->IsNote()
686 && pChn->rowCommand.IsNote()
687 && pChn->rowCommand.command == CMD_MODCMDEX && (pChn->rowCommand.param & 0xF0) == 0xD0
688 && (pChn->rowCommand.param & 0x0Fu) >= m_PlayState.m_nMusicSpeed)
689 {
690 // In ProTracker, a note triggered by an out-of-range note delay can be heard on the next row
691 // if there is no new note on that row.
692 // Test case: NoteDelay-NextRow.mod
693 pChn->nPeriod = GetPeriodFromNote(pChn->rowCommand.note, pChn->nFineTune, 0);
694 }
695 if(m_playBehaviour[kMODTempoOnSecondTick] && !m_playBehaviour[kMODVBlankTiming] && m_PlayState.m_nMusicSpeed == 1 && pChn->rowCommand.command == CMD_TEMPO)
696 {
697 // ProTracker sets the tempo after the first tick. This block handles the case of one tick per row.
698 // Test case: TempoChange.mod
699 m_PlayState.m_nMusicTempo = TEMPO(pChn->rowCommand.param, 0);
700 }
701
702 pChn->rowCommand = *m;
703
704 pChn->rightVol = pChn->newRightVol;
705 pChn->leftVol = pChn->newLeftVol;
706 pChn->dwFlags.reset(CHN_VIBRATO | CHN_TREMOLO);
707 if(!m_playBehaviour[kITVibratoTremoloPanbrello]) pChn->nPanbrelloOffset = 0;
708 pChn->nCommand = CMD_NONE;
709 pChn->m_plugParamValueStep = 0;
710 }
711
712 // Now that we know which pattern we're on, we can update time signatures (global or pattern-specific)
713 UpdateTimeSignature();
714
715 if(ignoreRow)
716 {
717 m_PlayState.m_nTickCount = m_PlayState.m_nMusicSpeed;
718 continue;
719 }
720 break;
721 }
722 // Should we process tick0 effects?
723 if (!m_PlayState.m_nMusicSpeed) m_PlayState.m_nMusicSpeed = 1;
724
725 //End of row? stop pattern step (aka "play row").
726 #ifdef MODPLUG_TRACKER
727 if (m_PlayState.m_nTickCount >= m_PlayState.TicksOnRow() - 1)
728 {
729 if(m_SongFlags[SONG_STEP])
730 {
731 m_SongFlags.reset(SONG_STEP);
732 m_SongFlags.set(SONG_PAUSED);
733 }
734 }
735 #endif // MODPLUG_TRACKER
736
737 if (m_PlayState.m_nTickCount)
738 {
739 m_SongFlags.reset(SONG_FIRSTTICK);
740 if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))
741 && (GetType() != MOD_TYPE_MOD || m_SongFlags[SONG_PT_MODE]) // Fix infinite loop in "GamerMan " by MrGamer, which was made with FT2
742 && m_PlayState.m_nTickCount < m_PlayState.TicksOnRow())
743 {
744 // Emulate first tick behaviour if Row Delay is set.
745 // Test cases: PatternDelaysRetrig.it, PatternDelaysRetrig.s3m, PatternDelaysRetrig.xm, PatternDelaysRetrig.mod
746 if(!(m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay)))
747 {
748 m_SongFlags.set(SONG_FIRSTTICK);
749 }
750 }
751 } else
752 {
753 m_SongFlags.set(SONG_FIRSTTICK);
754 m_SongFlags.reset(SONG_BREAKTOROW);
755 }
756
757 // Update Effects
758 return ProcessEffects();
759 }
760
761
NextRow(PlayState & playState,const bool breakRow) const762 std::pair<bool, bool> CSoundFile::NextRow(PlayState &playState, const bool breakRow) const
763 {
764 // When having an EEx effect on the same row as a Dxx jump, the target row is not played in ProTracker.
765 // Test case: DelayBreak.mod (based on condom_corruption by Travolta)
766 const bool ignoreRow = playState.m_nPatternDelay > 1 && breakRow && GetType() == MOD_TYPE_MOD;
767
768 // Done with the last row of the pattern or jumping somewhere else (could also be a result of pattern loop to row 0, but that doesn't matter here)
769 const bool patternTransition = playState.m_nNextRow == 0 || breakRow;
770 if(patternTransition && GetType() == MOD_TYPE_S3M)
771 {
772 // Reset pattern loop start
773 // Test case: LoopReset.s3m
774 for(CHANNELINDEX i = 0; i < GetNumChannels(); i++)
775 {
776 playState.Chn[i].nPatternLoop = 0;
777 }
778 }
779
780 playState.m_nPatternDelay = 0;
781 playState.m_nFrameDelay = 0;
782 playState.m_nTickCount = 0;
783 playState.m_nRow = playState.m_nNextRow;
784 playState.m_nCurrentOrder = playState.m_nNextOrder;
785
786 return {ignoreRow, patternTransition};
787 }
788
789
SetupNextRow(PlayState & playState,const bool patternLoop) const790 void CSoundFile::SetupNextRow(PlayState &playState, const bool patternLoop) const
791 {
792 playState.m_nNextRow = playState.m_nRow + 1;
793 if(playState.m_nNextRow >= Patterns[playState.m_nPattern].GetNumRows())
794 {
795 if(!patternLoop)
796 playState.m_nNextOrder = playState.m_nCurrentOrder + 1;
797 playState.m_nNextRow = 0;
798
799 // FT2 idiosyncrasy: When E60 is used on a pattern row x, the following pattern also starts from row x
800 // instead of the beginning of the pattern, unless there was a Bxx or Dxx effect.
801 if(m_playBehaviour[kFT2LoopE60Restart])
802 {
803 playState.m_nNextRow = playState.m_nextPatStartRow;
804 playState.m_nextPatStartRow = 0;
805 }
806 }
807 }
808
809
810 ////////////////////////////////////////////////////////////////////////////////////////////
811 // Channel effect processing
812
813
814 // Calculate delta for Vibrato / Tremolo / Panbrello effect
GetVibratoDelta(int type,int position) const815 int CSoundFile::GetVibratoDelta(int type, int position) const
816 {
817 // IT compatibility: IT has its own, more precise tables
818 if(m_playBehaviour[kITVibratoTremoloPanbrello])
819 {
820 position &= 0xFF;
821 switch(type & 0x03)
822 {
823 case 0: // Sine
824 default:
825 return ITSinusTable[position];
826 case 1: // Ramp down
827 return 64 - (position + 1) / 2;
828 case 2: // Square
829 return position < 128 ? 64 : 0;
830 case 3: // Random
831 return mpt::random<int, 7>(AccessPRNG()) - 0x40;
832 }
833 } else if(GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM))
834 {
835 // Other waveforms are not supported.
836 static constexpr int8 DBMSinus[] =
837 {
838 33, 52, 69, 84, 96, 107, 116, 122, 125, 127, 125, 122, 116, 107, 96, 84,
839 69, 52, 33, 13, -8, -31, -54, -79, -104,-128, -104, -79, -54, -31, -8, 13,
840 };
841 return DBMSinus[(position / 2u) & 0x1F];
842 } else
843 {
844 position &= 0x3F;
845 switch(type & 0x03)
846 {
847 case 0: // Sine
848 default:
849 return ModSinusTable[position];
850 case 1: // Ramp down
851 return (position < 32 ? 0 : 255) - position * 4;
852 case 2: // Square
853 return position < 32 ? 127 : -127;
854 case 3: // Random
855 return ModRandomTable[position];
856 }
857 }
858 }
859
860
ProcessVolumeSwing(ModChannel & chn,int & vol) const861 void CSoundFile::ProcessVolumeSwing(ModChannel &chn, int &vol) const
862 {
863 if(m_playBehaviour[kITSwingBehaviour])
864 {
865 vol += chn.nVolSwing;
866 Limit(vol, 0, 64);
867 } else if(m_playBehaviour[kMPTOldSwingBehaviour])
868 {
869 vol += chn.nVolSwing;
870 Limit(vol, 0, 256);
871 } else
872 {
873 chn.nVolume += chn.nVolSwing;
874 Limit(chn.nVolume, 0, 256);
875 vol = chn.nVolume;
876 chn.nVolSwing = 0;
877 }
878 }
879
880
ProcessPanningSwing(ModChannel & chn) const881 void CSoundFile::ProcessPanningSwing(ModChannel &chn) const
882 {
883 if(m_playBehaviour[kITSwingBehaviour] || m_playBehaviour[kMPTOldSwingBehaviour])
884 {
885 chn.nRealPan = chn.nPan + chn.nPanSwing;
886 Limit(chn.nRealPan, 0, 256);
887 } else
888 {
889 chn.nPan += chn.nPanSwing;
890 Limit(chn.nPan, 0, 256);
891 chn.nPanSwing = 0;
892 chn.nRealPan = chn.nPan;
893 }
894 }
895
896
ProcessTremolo(ModChannel & chn,int & vol) const897 void CSoundFile::ProcessTremolo(ModChannel &chn, int &vol) const
898 {
899 if (chn.dwFlags[CHN_TREMOLO])
900 {
901 if(m_SongFlags.test_all(SONG_FIRSTTICK | SONG_PT_MODE))
902 {
903 // ProTracker doesn't apply tremolo nor advance on the first tick.
904 // Test case: VibratoReset.mod
905 return;
906 }
907
908 // IT compatibility: Why would you not want to execute tremolo at volume 0?
909 if(vol > 0 || m_playBehaviour[kITVibratoTremoloPanbrello])
910 {
911 // IT compatibility: We don't need a different attenuation here because of the different tables we're going to use
912 const uint8 attenuation = ((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) || m_playBehaviour[kITVibratoTremoloPanbrello]) ? 5 : 6;
913
914 int delta = GetVibratoDelta(chn.nTremoloType, chn.nTremoloPos);
915 if((chn.nTremoloType & 0x03) == 1 && m_playBehaviour[kFT2MODTremoloRampWaveform])
916 {
917 // FT2 compatibility: Tremolo ramp down / triangle implementation is weird and affected by vibrato position (copypaste bug)
918 // Test case: TremoloWaveforms.xm, TremoloVibrato.xm
919 uint8 ramp = (chn.nTremoloPos * 4u) & 0x7F;
920 // Volume-colum vibrato gets executed first in FT2, so we may need to advance the vibrato position first
921 uint32 vibPos = chn.nVibratoPos;
922 if(!m_SongFlags[SONG_FIRSTTICK] && chn.dwFlags[CHN_VIBRATO])
923 vibPos += chn.nVibratoSpeed;
924 if((vibPos & 0x3F) >= 32)
925 ramp ^= 0x7F;
926 if((chn.nTremoloPos & 0x3F) >= 32)
927 delta = -ramp;
928 else
929 delta = ramp;
930 }
931 if(GetType() != MOD_TYPE_DMF)
932 {
933 vol += (delta * chn.nTremoloDepth) / (1 << attenuation);
934 } else
935 {
936 // Tremolo in DMF always attenuates by a percentage of the current note volume
937 vol -= (vol * chn.nTremoloDepth * (64 - delta)) / (128 * 64);
938 }
939 }
940 if(!m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS]))
941 {
942 // IT compatibility: IT has its own, more precise tables
943 if(m_playBehaviour[kITVibratoTremoloPanbrello])
944 chn.nTremoloPos += 4 * chn.nTremoloSpeed;
945 else
946 chn.nTremoloPos += chn.nTremoloSpeed;
947 }
948 }
949 }
950
951
ProcessTremor(CHANNELINDEX nChn,int & vol)952 void CSoundFile::ProcessTremor(CHANNELINDEX nChn, int &vol)
953 {
954 ModChannel &chn = m_PlayState.Chn[nChn];
955
956 if(m_playBehaviour[kFT2Tremor])
957 {
958 // FT2 Compatibility: Weird XM tremor.
959 // Test case: Tremor.xm
960 if(chn.nTremorCount & 0x80)
961 {
962 if(!m_SongFlags[SONG_FIRSTTICK] && chn.nCommand == CMD_TREMOR)
963 {
964 chn.nTremorCount &= ~0x20;
965 if(chn.nTremorCount == 0x80)
966 {
967 // Reached end of off-time
968 chn.nTremorCount = (chn.nTremorParam >> 4) | 0xC0;
969 } else if(chn.nTremorCount == 0xC0)
970 {
971 // Reached end of on-time
972 chn.nTremorCount = (chn.nTremorParam & 0x0F) | 0x80;
973 } else
974 {
975 chn.nTremorCount--;
976 }
977
978 chn.dwFlags.set(CHN_FASTVOLRAMP);
979 }
980
981 if((chn.nTremorCount & 0xE0) == 0x80)
982 {
983 vol = 0;
984 }
985 }
986 } else if(chn.nCommand == CMD_TREMOR)
987 {
988 // IT compatibility 12. / 13.: Tremor
989 if(m_playBehaviour[kITTremor])
990 {
991 if((chn.nTremorCount & 0x80) && chn.nLength)
992 {
993 if (chn.nTremorCount == 0x80)
994 chn.nTremorCount = (chn.nTremorParam >> 4) | 0xC0;
995 else if (chn.nTremorCount == 0xC0)
996 chn.nTremorCount = (chn.nTremorParam & 0x0F) | 0x80;
997 else
998 chn.nTremorCount--;
999 }
1000
1001 if((chn.nTremorCount & 0xC0) == 0x80)
1002 vol = 0;
1003 } else
1004 {
1005 uint8 ontime = chn.nTremorParam >> 4;
1006 uint8 n = ontime + (chn.nTremorParam & 0x0F); // Total tremor cycle time (On + Off)
1007 if ((!(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))) || m_SongFlags[SONG_ITOLDEFFECTS])
1008 {
1009 n += 2;
1010 ontime++;
1011 }
1012 uint8 tremcount = chn.nTremorCount;
1013 if(!(GetType() & MOD_TYPE_XM))
1014 {
1015 if (tremcount >= n) tremcount = 0;
1016 if (tremcount >= ontime) vol = 0;
1017 chn.nTremorCount = tremcount + 1;
1018 } else
1019 {
1020 if(m_SongFlags[SONG_FIRSTTICK])
1021 {
1022 // tremcount is only 0 on the first tremor tick after triggering a note.
1023 if(tremcount > 0)
1024 {
1025 tremcount--;
1026 }
1027 } else
1028 {
1029 chn.nTremorCount = tremcount + 1;
1030 }
1031 if (tremcount % n >= ontime) vol = 0;
1032 }
1033 }
1034 chn.dwFlags.set(CHN_FASTVOLRAMP);
1035 }
1036
1037 #ifndef NO_PLUGINS
1038 // Plugin tremor
1039 if(chn.nCommand == CMD_TREMOR && chn.pModInstrument && chn.pModInstrument->nMixPlug
1040 && !chn.pModInstrument->dwFlags[INS_MUTE]
1041 && !chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE]
1042 && ModCommand::IsNote(chn.nLastNote))
1043 {
1044 const ModInstrument *pIns = chn.pModInstrument;
1045 IMixPlugin *pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin;
1046 if(pPlugin)
1047 {
1048 const bool isPlaying = pPlugin->IsNotePlaying(chn.nLastNote, nChn);
1049 if(vol == 0 && isPlaying)
1050 pPlugin->MidiCommand(*pIns, chn.nLastNote + NOTE_MAX_SPECIAL, 0, nChn);
1051 else if(vol != 0 && !isPlaying)
1052 pPlugin->MidiCommand(*pIns, chn.nLastNote, static_cast<uint16>(chn.nVolume), nChn);
1053 }
1054 }
1055 #endif // NO_PLUGINS
1056 }
1057
1058
IsEnvelopeProcessed(const ModChannel & chn,EnvelopeType env) const1059 bool CSoundFile::IsEnvelopeProcessed(const ModChannel &chn, EnvelopeType env) const
1060 {
1061 if(chn.pModInstrument == nullptr)
1062 {
1063 return false;
1064 }
1065 const InstrumentEnvelope &insEnv = chn.pModInstrument->GetEnvelope(env);
1066
1067 // IT Compatibility: S77/S79/S7B do not disable the envelope, they just pause the counter
1068 // Test cases: s77.it, EnvLoops.xm, PanSustainRelease.xm
1069 bool playIfPaused = m_playBehaviour[kITEnvelopePositionHandling] || m_playBehaviour[kFT2PanSustainRelease];
1070 return ((chn.GetEnvelope(env).flags[ENV_ENABLED] || (insEnv.dwFlags[ENV_ENABLED] && playIfPaused))
1071 && !insEnv.empty());
1072 }
1073
1074
ProcessVolumeEnvelope(ModChannel & chn,int & vol) const1075 void CSoundFile::ProcessVolumeEnvelope(ModChannel &chn, int &vol) const
1076 {
1077 if(IsEnvelopeProcessed(chn, ENV_VOLUME))
1078 {
1079 const ModInstrument *pIns = chn.pModInstrument;
1080
1081 if(m_playBehaviour[kITEnvelopePositionHandling] && chn.VolEnv.nEnvPosition == 0)
1082 {
1083 // If the envelope is disabled at the very same moment as it is triggered, we do not process anything.
1084 return;
1085 }
1086 const int envpos = chn.VolEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0);
1087 // Get values in [0, 256]
1088 int envval = pIns->VolEnv.GetValueFromPosition(envpos, 256);
1089
1090 // if we are in the release portion of the envelope,
1091 // rescale envelope factor so that it is proportional to the release point
1092 // and release envelope beginning.
1093 if(pIns->VolEnv.nReleaseNode != ENV_RELEASE_NODE_UNSET
1094 && chn.VolEnv.nEnvValueAtReleaseJump != NOT_YET_RELEASED)
1095 {
1096 int envValueAtReleaseJump = chn.VolEnv.nEnvValueAtReleaseJump;
1097 int envValueAtReleaseNode = pIns->VolEnv[pIns->VolEnv.nReleaseNode].value * 4;
1098
1099 //If we have just hit the release node, force the current env value
1100 //to be that of the release node. This works around the case where
1101 // we have another node at the same position as the release node.
1102 if(envpos == pIns->VolEnv[pIns->VolEnv.nReleaseNode].tick)
1103 envval = envValueAtReleaseNode;
1104
1105 if(m_playBehaviour[kLegacyReleaseNode])
1106 {
1107 // Old, hard to grasp release node behaviour (additive)
1108 int relativeVolumeChange = (envval - envValueAtReleaseNode) * 2;
1109 envval = envValueAtReleaseJump + relativeVolumeChange;
1110 } else
1111 {
1112 // New behaviour, truly relative to release node
1113 if(envValueAtReleaseNode > 0)
1114 envval = envValueAtReleaseJump * envval / envValueAtReleaseNode;
1115 else
1116 envval = 0;
1117 }
1118 }
1119 vol = (vol * Clamp(envval, 0, 512)) / 256;
1120 }
1121
1122 }
1123
1124
ProcessPanningEnvelope(ModChannel & chn) const1125 void CSoundFile::ProcessPanningEnvelope(ModChannel &chn) const
1126 {
1127 if(IsEnvelopeProcessed(chn, ENV_PANNING))
1128 {
1129 const ModInstrument *pIns = chn.pModInstrument;
1130
1131 if(m_playBehaviour[kITEnvelopePositionHandling] && chn.PanEnv.nEnvPosition == 0)
1132 {
1133 // If the envelope is disabled at the very same moment as it is triggered, we do not process anything.
1134 return;
1135 }
1136
1137 const int envpos = chn.PanEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0);
1138 // Get values in [-32, 32]
1139 const int envval = pIns->PanEnv.GetValueFromPosition(envpos, 64) - 32;
1140
1141 int pan = chn.nRealPan;
1142 if(pan >= 128)
1143 {
1144 pan += (envval * (256 - pan)) / 32;
1145 } else
1146 {
1147 pan += (envval * (pan)) / 32;
1148 }
1149 chn.nRealPan = Clamp(pan, 0, 256);
1150
1151 }
1152 }
1153
1154
ProcessPitchFilterEnvelope(ModChannel & chn,int32 & period) const1155 int CSoundFile::ProcessPitchFilterEnvelope(ModChannel &chn, int32 &period) const
1156 {
1157 if(IsEnvelopeProcessed(chn, ENV_PITCH))
1158 {
1159 const ModInstrument *pIns = chn.pModInstrument;
1160
1161 if(m_playBehaviour[kITEnvelopePositionHandling] && chn.PitchEnv.nEnvPosition == 0)
1162 {
1163 // If the envelope is disabled at the very same moment as it is triggered, we do not process anything.
1164 return -1;
1165 }
1166
1167 const int envpos = chn.PitchEnv.nEnvPosition - (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0);
1168 // Get values in [-256, 256]
1169 #ifdef MODPLUG_TRACKER
1170 const int32 range = ENVELOPE_MAX;
1171 const int32 amp = 512;
1172 #else
1173 // TODO: AMS2 envelopes behave differently when linear slides are off - emulate with 15 * (-128...127) >> 6
1174 // Copy over vibrato behaviour for that?
1175 const int32 range = GetType() == MOD_TYPE_AMS ? uint8_max : ENVELOPE_MAX;
1176 int32 amp;
1177 switch(GetType())
1178 {
1179 case MOD_TYPE_AMS: amp = 64; break;
1180 case MOD_TYPE_MDL: amp = 192; break;
1181 default: amp = 512;
1182 }
1183 #endif
1184 const int envval = pIns->PitchEnv.GetValueFromPosition(envpos, amp, range) - amp / 2;
1185
1186 if(chn.PitchEnv.flags[ENV_FILTER])
1187 {
1188 // Filter Envelope: controls cutoff frequency
1189 return SetupChannelFilter(chn, !chn.dwFlags[CHN_FILTER], envval);
1190 } else
1191 {
1192 // Pitch Envelope
1193 if(chn.HasCustomTuning())
1194 {
1195 if(chn.nFineTune != envval)
1196 {
1197 chn.nFineTune = mpt::saturate_cast<int16>(envval);
1198 chn.m_CalculateFreq = true;
1199 //Preliminary tests indicated that this behavior
1200 //is very close to original(with 12TET) when finestep count
1201 //is 15.
1202 }
1203 } else //Original behavior
1204 {
1205 const bool useFreq = PeriodsAreFrequencies();
1206 const uint32 (&upTable)[256] = useFreq ? LinearSlideUpTable : LinearSlideDownTable;
1207 const uint32 (&downTable)[256] = useFreq ? LinearSlideDownTable : LinearSlideUpTable;
1208
1209 int l = envval;
1210 if(l < 0)
1211 {
1212 l = -l;
1213 LimitMax(l, 255);
1214 period = Util::muldiv(period, downTable[l], 65536);
1215 } else
1216 {
1217 LimitMax(l, 255);
1218 period = Util::muldiv(period, upTable[l], 65536);
1219 }
1220 } //End: Original behavior.
1221 }
1222 }
1223 return -1;
1224 }
1225
1226
IncrementEnvelopePosition(ModChannel & chn,EnvelopeType envType) const1227 void CSoundFile::IncrementEnvelopePosition(ModChannel &chn, EnvelopeType envType) const
1228 {
1229 ModChannel::EnvInfo &chnEnv = chn.GetEnvelope(envType);
1230
1231 if(chn.pModInstrument == nullptr || !chnEnv.flags[ENV_ENABLED])
1232 {
1233 return;
1234 }
1235
1236 // Increase position
1237 uint32 position = chnEnv.nEnvPosition + (m_playBehaviour[kITEnvelopePositionHandling] ? 0 : 1);
1238
1239 const InstrumentEnvelope &insEnv = chn.pModInstrument->GetEnvelope(envType);
1240 if(insEnv.empty())
1241 {
1242 return;
1243 }
1244
1245 bool endReached = false;
1246
1247 if(!m_playBehaviour[kITEnvelopePositionHandling])
1248 {
1249 // FT2-style envelope processing.
1250 if(insEnv.dwFlags[ENV_LOOP])
1251 {
1252 // Normal loop active
1253 uint32 end = insEnv[insEnv.nLoopEnd].tick;
1254 if(!(GetType() & (MOD_TYPE_XM | MOD_TYPE_MT2))) end++;
1255
1256 // FT2 compatibility: If the sustain point is at the loop end and the sustain loop has been released, don't loop anymore.
1257 // Test case: EnvLoops.xm
1258 const bool escapeLoop = (insEnv.nLoopEnd == insEnv.nSustainEnd && insEnv.dwFlags[ENV_SUSTAIN] && chn.dwFlags[CHN_KEYOFF] && m_playBehaviour[kFT2EnvelopeEscape]);
1259
1260 if(position == end && !escapeLoop)
1261 {
1262 position = insEnv[insEnv.nLoopStart].tick;
1263 }
1264 }
1265
1266 if(insEnv.dwFlags[ENV_SUSTAIN] && !chn.dwFlags[CHN_KEYOFF])
1267 {
1268 // Envelope sustained
1269 if(position == insEnv[insEnv.nSustainEnd].tick + 1u)
1270 {
1271 position = insEnv[insEnv.nSustainStart].tick;
1272 // FT2 compatibility: If the panning envelope reaches its sustain point before key-off, it stays there forever.
1273 // Test case: PanSustainRelease.xm
1274 if(m_playBehaviour[kFT2PanSustainRelease] && envType == ENV_PANNING && !chn.dwFlags[CHN_KEYOFF])
1275 {
1276 chnEnv.flags.reset(ENV_ENABLED);
1277 }
1278 }
1279 } else
1280 {
1281 // Limit to last envelope point
1282 if(position > insEnv.back().tick)
1283 {
1284 // Env of envelope
1285 position = insEnv.back().tick;
1286 endReached = true;
1287 }
1288 }
1289 } else
1290 {
1291 // IT envelope processing.
1292 // Test case: EnvLoops.it
1293 uint32 start, end;
1294
1295 // IT compatiblity: OpenMPT processes the key-off flag earlier than IT. Grab the flag from the previous tick instead.
1296 // Test case: EnvOffLength.it
1297 if(insEnv.dwFlags[ENV_SUSTAIN] && !chn.dwOldFlags[CHN_KEYOFF] && (chnEnv.nEnvValueAtReleaseJump == NOT_YET_RELEASED || m_playBehaviour[kReleaseNodePastSustainBug]))
1298 {
1299 // Envelope sustained
1300 start = insEnv[insEnv.nSustainStart].tick;
1301 end = insEnv[insEnv.nSustainEnd].tick + 1;
1302 } else if(insEnv.dwFlags[ENV_LOOP])
1303 {
1304 // Normal loop active
1305 start = insEnv[insEnv.nLoopStart].tick;
1306 end = insEnv[insEnv.nLoopEnd].tick + 1;
1307 } else
1308 {
1309 // Limit to last envelope point
1310 start = end = insEnv.back().tick;
1311 if(position > end)
1312 {
1313 // Env of envelope
1314 endReached = true;
1315 }
1316 }
1317
1318 if(position >= end)
1319 {
1320 position = start;
1321 }
1322 }
1323
1324 if(envType == ENV_VOLUME && endReached)
1325 {
1326 // Special handling for volume envelopes at end of envelope
1327 if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) || (chn.dwFlags[CHN_KEYOFF] && GetType() != MOD_TYPE_MDL))
1328 {
1329 chn.dwFlags.set(CHN_NOTEFADE);
1330 }
1331
1332 if(insEnv.back().value == 0 && (chn.nMasterChn > 0 || (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))))
1333 {
1334 // Stop channel if the last envelope node is silent anyway.
1335 chn.dwFlags.set(CHN_NOTEFADE);
1336 chn.nFadeOutVol = 0;
1337 chn.nRealVolume = 0;
1338 chn.nCalcVolume = 0;
1339 }
1340 }
1341
1342 chnEnv.nEnvPosition = position + (m_playBehaviour[kITEnvelopePositionHandling] ? 1 : 0);
1343
1344 }
1345
1346
IncrementEnvelopePositions(ModChannel & chn) const1347 void CSoundFile::IncrementEnvelopePositions(ModChannel &chn) const
1348 {
1349 if (chn.isFirstTick && GetType() == MOD_TYPE_MED)
1350 return;
1351 IncrementEnvelopePosition(chn, ENV_VOLUME);
1352 IncrementEnvelopePosition(chn, ENV_PANNING);
1353 IncrementEnvelopePosition(chn, ENV_PITCH);
1354 }
1355
1356
ProcessInstrumentFade(ModChannel & chn,int & vol) const1357 void CSoundFile::ProcessInstrumentFade(ModChannel &chn, int &vol) const
1358 {
1359 // FadeOut volume
1360 if(chn.dwFlags[CHN_NOTEFADE] && chn.pModInstrument != nullptr)
1361 {
1362 const ModInstrument *pIns = chn.pModInstrument;
1363
1364 uint32 fadeout = pIns->nFadeOut;
1365 if (fadeout)
1366 {
1367 chn.nFadeOutVol -= fadeout * 2;
1368 if (chn.nFadeOutVol <= 0) chn.nFadeOutVol = 0;
1369 vol = (vol * chn.nFadeOutVol) / 65536;
1370 } else if (!chn.nFadeOutVol)
1371 {
1372 vol = 0;
1373 }
1374 }
1375 }
1376
1377
ProcessPitchPanSeparation(int32 & pan,int note,const ModInstrument & instr)1378 void CSoundFile::ProcessPitchPanSeparation(int32 &pan, int note, const ModInstrument &instr)
1379 {
1380 if(!instr.nPPS || note == NOTE_NONE)
1381 return;
1382 // with PPS = 16 / PPC = C-5, E-6 will pan hard right (and D#6 will not)
1383 int32 delta = (note - instr.nPPC - NOTE_MIN) * instr.nPPS / 2;
1384 pan = Clamp(pan + delta, 0, 256);
1385 }
1386
1387
ProcessPanbrello(ModChannel & chn) const1388 void CSoundFile::ProcessPanbrello(ModChannel &chn) const
1389 {
1390 int pdelta = chn.nPanbrelloOffset;
1391 if(chn.rowCommand.command == CMD_PANBRELLO)
1392 {
1393 uint32 panpos;
1394 // IT compatibility: IT has its own, more precise tables
1395 if(m_playBehaviour[kITVibratoTremoloPanbrello])
1396 panpos = chn.nPanbrelloPos;
1397 else
1398 panpos = ((chn.nPanbrelloPos + 0x10) >> 2);
1399
1400 pdelta = GetVibratoDelta(chn.nPanbrelloType, panpos);
1401
1402 // IT compatibility: Sample-and-hold style random panbrello (tremolo and vibrato don't use this mechanism in IT)
1403 // Test case: RandomWaveform.it
1404 if(m_playBehaviour[kITSampleAndHoldPanbrello] && chn.nPanbrelloType == 3)
1405 {
1406 if(chn.nPanbrelloPos == 0 || chn.nPanbrelloPos >= chn.nPanbrelloSpeed)
1407 {
1408 chn.nPanbrelloPos = 0;
1409 chn.nPanbrelloRandomMemory = static_cast<int8>(pdelta);
1410 }
1411 chn.nPanbrelloPos++;
1412 pdelta = chn.nPanbrelloRandomMemory;
1413 } else
1414 {
1415 chn.nPanbrelloPos += chn.nPanbrelloSpeed;
1416 }
1417 // IT compatibility: Panbrello effect is active until next note or panning command.
1418 // Test case: PanbrelloHold.it
1419 if(m_playBehaviour[kITPanbrelloHold])
1420 {
1421 chn.nPanbrelloOffset = static_cast<int8>(pdelta);
1422 }
1423 }
1424 if(pdelta)
1425 {
1426 pdelta = ((pdelta * (int)chn.nPanbrelloDepth) + 2) / 8;
1427 pdelta += chn.nRealPan;
1428 chn.nRealPan = Clamp(pdelta, 0, 256);
1429 }
1430 }
1431
1432
ProcessArpeggio(CHANNELINDEX nChn,int32 & period,Tuning::NOTEINDEXTYPE & arpeggioSteps)1433 void CSoundFile::ProcessArpeggio(CHANNELINDEX nChn, int32 &period, Tuning::NOTEINDEXTYPE &arpeggioSteps)
1434 {
1435 ModChannel &chn = m_PlayState.Chn[nChn];
1436
1437 #ifndef NO_PLUGINS
1438 // Plugin arpeggio
1439 if(chn.pModInstrument && chn.pModInstrument->nMixPlug
1440 && !chn.pModInstrument->dwFlags[INS_MUTE]
1441 && !chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE])
1442 {
1443 const ModInstrument *pIns = chn.pModInstrument;
1444 IMixPlugin *pPlugin = m_MixPlugins[pIns->nMixPlug - 1].pMixPlugin;
1445 if(pPlugin)
1446 {
1447 uint8 step = 0;
1448 const bool arpOnRow = (chn.rowCommand.command == CMD_ARPEGGIO);
1449 const ModCommand::NOTE lastNote = ModCommand::IsNote(chn.nLastNote) ? static_cast<ModCommand::NOTE>(pIns->NoteMap[chn.nLastNote - NOTE_MIN]) : static_cast<ModCommand::NOTE>(NOTE_NONE);
1450 if(arpOnRow)
1451 {
1452 switch(m_PlayState.m_nTickCount % 3)
1453 {
1454 case 1: step = chn.nArpeggio >> 4; break;
1455 case 2: step = chn.nArpeggio & 0x0F; break;
1456 }
1457 chn.nArpeggioBaseNote = lastNote;
1458 }
1459
1460 // Trigger new note:
1461 // - If there's an arpeggio on this row and
1462 // - the note to trigger is not the same as the previous arpeggio note or
1463 // - a pattern note has just been triggered on this tick
1464 // - If there's no arpeggio
1465 // - but an arpeggio note is still active and
1466 // - there's no note stop or new note that would stop it anyway
1467 if((arpOnRow && chn.nArpeggioLastNote != chn.nArpeggioBaseNote + step && (!m_SongFlags[SONG_FIRSTTICK] || !chn.rowCommand.IsNote()))
1468 || (!arpOnRow && chn.rowCommand.note == NOTE_NONE && chn.nArpeggioLastNote != NOTE_NONE))
1469 SendMIDINote(nChn, chn.nArpeggioBaseNote + step, static_cast<uint16>(chn.nVolume));
1470 // Stop note:
1471 // - If some arpeggio note is still registered or
1472 // - When starting an arpeggio on a row with no other note on it, stop some possibly still playing note.
1473 if(chn.nArpeggioLastNote != NOTE_NONE)
1474 SendMIDINote(nChn, chn.nArpeggioLastNote + NOTE_MAX_SPECIAL, 0);
1475 else if(arpOnRow && m_SongFlags[SONG_FIRSTTICK] && !chn.rowCommand.IsNote() && ModCommand::IsNote(lastNote))
1476 SendMIDINote(nChn, lastNote + NOTE_MAX_SPECIAL, 0);
1477
1478 if(chn.rowCommand.command == CMD_ARPEGGIO)
1479 chn.nArpeggioLastNote = chn.nArpeggioBaseNote + step;
1480 else
1481 chn.nArpeggioLastNote = NOTE_NONE;
1482 }
1483 }
1484 #endif // NO_PLUGINS
1485
1486 if(chn.nCommand == CMD_ARPEGGIO)
1487 {
1488 if(chn.HasCustomTuning())
1489 {
1490 switch(m_PlayState.m_nTickCount % 3)
1491 {
1492 case 0: arpeggioSteps = 0; break;
1493 case 1: arpeggioSteps = chn.nArpeggio >> 4; break;
1494 case 2: arpeggioSteps = chn.nArpeggio & 0x0F; break;
1495 }
1496 chn.m_CalculateFreq = true;
1497 chn.m_ReCalculateFreqOnFirstTick = true;
1498 } else
1499 {
1500 if(GetType() == MOD_TYPE_MT2 && m_SongFlags[SONG_FIRSTTICK])
1501 {
1502 // MT2 resets any previous portamento when an arpeggio occurs.
1503 chn.nPeriod = period = GetPeriodFromNote(chn.nNote, chn.nFineTune, chn.nC5Speed);
1504 }
1505
1506 if(m_playBehaviour[kITArpeggio])
1507 {
1508 //IT playback compatibility 01 & 02
1509
1510 // Pattern delay restarts tick counting. Not quite correct yet!
1511 const uint32 tick = m_PlayState.m_nTickCount % (m_PlayState.m_nMusicSpeed + m_PlayState.m_nFrameDelay);
1512 if(chn.nArpeggio != 0)
1513 {
1514 uint32 arpRatio = 65536;
1515 switch(tick % 3)
1516 {
1517 case 1: arpRatio = LinearSlideUpTable[(chn.nArpeggio >> 4) * 16]; break;
1518 case 2: arpRatio = LinearSlideUpTable[(chn.nArpeggio & 0x0F) * 16]; break;
1519 }
1520 if(PeriodsAreFrequencies())
1521 period = Util::muldivr(period, arpRatio, 65536);
1522 else
1523 period = Util::muldivr(period, 65536, arpRatio);
1524 }
1525 } else if(m_playBehaviour[kFT2Arpeggio])
1526 {
1527 // FastTracker 2: Swedish tracker logic (TM) arpeggio
1528 if(!m_SongFlags[SONG_FIRSTTICK])
1529 {
1530 // Arpeggio is added on top of current note, but cannot do it the IT way because of
1531 // the behaviour in ArpeggioClamp.xm.
1532 // Test case: ArpSlide.xm
1533 uint32 note = 0;
1534
1535 // The fact that arpeggio behaves in a totally fucked up way at 16 ticks/row or more is that the arpeggio offset LUT only has 16 entries in FT2.
1536 // At more than 16 ticks/row, FT2 reads into the vibrato table, which is placed right after the arpeggio table.
1537 // Test case: Arpeggio.xm
1538 int arpPos = m_PlayState.m_nMusicSpeed - (m_PlayState.m_nTickCount % m_PlayState.m_nMusicSpeed);
1539 if(arpPos > 16)
1540 arpPos = 2;
1541 else if(arpPos == 16)
1542 arpPos = 0;
1543 else
1544 arpPos %= 3;
1545 switch(arpPos)
1546 {
1547 case 1: note = (chn.nArpeggio >> 4); break;
1548 case 2: note = (chn.nArpeggio & 0x0F); break;
1549 }
1550
1551 if(arpPos != 0)
1552 {
1553 // Arpeggio is added on top of current note, but cannot do it the IT way because of
1554 // the behaviour in ArpeggioClamp.xm.
1555 // Test case: ArpSlide.xm
1556 note += GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed);
1557
1558 period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed);
1559
1560 // FT2 compatibility: FT2 has a different note limit for Arpeggio.
1561 // Test case: ArpeggioClamp.xm
1562 if(note >= 108 + NOTE_MIN)
1563 {
1564 period = std::max(static_cast<uint32>(period), GetPeriodFromNote(108 + NOTE_MIN, 0, chn.nC5Speed));
1565 }
1566 }
1567 }
1568 }
1569 // Other trackers
1570 else
1571 {
1572 uint32 tick = m_PlayState.m_nTickCount;
1573
1574 // TODO other likely formats for MOD case: MED, OKT, etc
1575 uint8 note = (GetType() != MOD_TYPE_MOD) ? chn.nNote : static_cast<uint8>(GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed));
1576 if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI))
1577 tick += 2;
1578 switch(tick % 3)
1579 {
1580 case 1: note += (chn.nArpeggio >> 4); break;
1581 case 2: note += (chn.nArpeggio & 0x0F); break;
1582 }
1583 if(note != chn.nNote || (GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_STM)) || m_playBehaviour[KST3PortaAfterArpeggio])
1584 {
1585 if(m_SongFlags[SONG_PT_MODE])
1586 {
1587 // Weird arpeggio wrap-around in ProTracker.
1588 // Test case: ArpWraparound.mod, and the snare sound in "Jim is dead" by doh.
1589 if(note == NOTE_MIDDLEC + 24)
1590 {
1591 period = int32_max;
1592 return;
1593 } else if(note > NOTE_MIDDLEC + 24)
1594 {
1595 note -= 37;
1596 }
1597 }
1598 period = GetPeriodFromNote(note, chn.nFineTune, chn.nC5Speed);
1599
1600 if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_DIGI | MOD_TYPE_PSM | MOD_TYPE_STM | MOD_TYPE_OKT))
1601 {
1602 // The arpeggio note offset remains effective after the end of the current row in ScreamTracker 2.
1603 // This fixes the flute lead in MORPH.STM by Skaven, pattern 27.
1604 // Note that ScreamTracker 2.24 handles arpeggio slightly differently: It only considers the lower
1605 // nibble, and switches to that note halfway through the row.
1606 chn.nPeriod = period;
1607 } else if(m_playBehaviour[KST3PortaAfterArpeggio])
1608 {
1609 chn.nArpeggioLastNote = note;
1610 }
1611 }
1612 }
1613 }
1614 }
1615 }
1616
1617
ProcessVibrato(CHANNELINDEX nChn,int32 & period,Tuning::RATIOTYPE & vibratoFactor)1618 void CSoundFile::ProcessVibrato(CHANNELINDEX nChn, int32 &period, Tuning::RATIOTYPE &vibratoFactor)
1619 {
1620 ModChannel &chn = m_PlayState.Chn[nChn];
1621
1622 if(chn.dwFlags[CHN_VIBRATO])
1623 {
1624 const bool advancePosition = !m_SongFlags[SONG_FIRSTTICK] || ((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !(m_SongFlags[SONG_ITOLDEFFECTS]));
1625
1626 if(GetType() == MOD_TYPE_669)
1627 {
1628 if(chn.nVibratoPos % 2u)
1629 {
1630 period += chn.nVibratoDepth * 167; // Already multiplied by 4, and it seems like the real factor here is 669... how original =)
1631 }
1632 chn.nVibratoPos++;
1633 return;
1634 }
1635
1636 // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position
1637 if(advancePosition && m_playBehaviour[kITVibratoTremoloPanbrello])
1638 chn.nVibratoPos += 4 * chn.nVibratoSpeed;
1639
1640 int vdelta = GetVibratoDelta(chn.nVibratoType, chn.nVibratoPos);
1641
1642 if(chn.HasCustomTuning())
1643 {
1644 //Hack implementation: Scaling vibratofactor to [0.95; 1.05]
1645 //using figure from above tables and vibratodepth parameter
1646 vibratoFactor += 0.05f * (vdelta * chn.nVibratoDepth) / (128.0f * 60.0f);
1647 chn.m_CalculateFreq = true;
1648 chn.m_ReCalculateFreqOnFirstTick = false;
1649
1650 if(m_PlayState.m_nTickCount + 1 == m_PlayState.m_nMusicSpeed)
1651 chn.m_ReCalculateFreqOnFirstTick = true;
1652 } else
1653 {
1654 // Original behaviour
1655 if(m_SongFlags.test_all(SONG_FIRSTTICK | SONG_PT_MODE) || ((GetType() & (MOD_TYPE_DIGI | MOD_TYPE_DBM)) && m_SongFlags[SONG_FIRSTTICK]))
1656 {
1657 // ProTracker doesn't apply vibrato nor advance on the first tick.
1658 // Test case: VibratoReset.mod
1659 return;
1660 } else if((GetType() & (MOD_TYPE_XM | MOD_TYPE_MOD)) && (chn.nVibratoType & 0x03) == 1)
1661 {
1662 // FT2 compatibility: Vibrato ramp down table is upside down.
1663 // Test case: VibratoWaveforms.xm
1664 vdelta = -vdelta;
1665 }
1666
1667 uint32 vdepth;
1668 // IT compatibility: correct vibrato depth
1669 if(m_playBehaviour[kITVibratoTremoloPanbrello])
1670 {
1671 // Yes, vibrato goes backwards with old effects enabled!
1672 if(m_SongFlags[SONG_ITOLDEFFECTS])
1673 {
1674 // Test case: vibrato-oldfx.it
1675 vdepth = 5;
1676 } else
1677 {
1678 // Test case: vibrato.it
1679 vdepth = 6;
1680 vdelta = -vdelta;
1681 }
1682 } else
1683 {
1684 if(m_SongFlags[SONG_S3MOLDVIBRATO])
1685 vdepth = 5;
1686 else if(GetType() == MOD_TYPE_DTM)
1687 vdepth = 8;
1688 else if(GetType() & (MOD_TYPE_DBM | MOD_TYPE_MTM))
1689 vdepth = 7;
1690 else if((GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) && !m_SongFlags[SONG_ITOLDEFFECTS])
1691 vdepth = 7;
1692 else
1693 vdepth = 6;
1694
1695 // ST3 compatibility: Do not distinguish between vibrato types in effect memory
1696 // Test case: VibratoTypeChange.s3m
1697 if(m_playBehaviour[kST3VibratoMemory] && chn.rowCommand.command == CMD_FINEVIBRATO)
1698 vdepth += 2;
1699 }
1700
1701 vdelta = (-vdelta * static_cast<int>(chn.nVibratoDepth)) / (1 << vdepth);
1702
1703 DoFreqSlide(chn, period, vdelta);
1704
1705 // Process MIDI vibrato for plugins:
1706 #ifndef NO_PLUGINS
1707 IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn);
1708 if(plugin != nullptr)
1709 {
1710 // If the Pitch Wheel Depth is configured correctly (so it's the same as the plugin's PWD),
1711 // MIDI vibrato will sound identical to vibrato with linear slides enabled.
1712 int8 pwd = 2;
1713 if(chn.pModInstrument != nullptr)
1714 {
1715 pwd = chn.pModInstrument->midiPWD;
1716 }
1717 plugin->MidiVibrato(vdelta, pwd, nChn);
1718 }
1719 #endif // NO_PLUGINS
1720 }
1721
1722 // Advance vibrato position - IT updates on every tick, unless "old effects" are enabled (in this case it only updates on non-first ticks like other trackers)
1723 // IT compatibility: IT has its own, more precise tables and pre-increments the vibrato position
1724 if(advancePosition && !m_playBehaviour[kITVibratoTremoloPanbrello])
1725 chn.nVibratoPos += chn.nVibratoSpeed;
1726 } else if(chn.dwOldFlags[CHN_VIBRATO])
1727 {
1728 // Stop MIDI vibrato for plugins:
1729 #ifndef NO_PLUGINS
1730 IMixPlugin *plugin = GetChannelInstrumentPlugin(nChn);
1731 if(plugin != nullptr)
1732 {
1733 plugin->MidiVibrato(0, 0, nChn);
1734 }
1735 #endif // NO_PLUGINS
1736 }
1737 }
1738
1739
ProcessSampleAutoVibrato(ModChannel & chn,int32 & period,Tuning::RATIOTYPE & vibratoFactor,int & nPeriodFrac) const1740 void CSoundFile::ProcessSampleAutoVibrato(ModChannel &chn, int32 &period, Tuning::RATIOTYPE &vibratoFactor, int &nPeriodFrac) const
1741 {
1742 // Sample Auto-Vibrato
1743 if(chn.pModSample != nullptr && chn.pModSample->nVibDepth)
1744 {
1745 const ModSample *pSmp = chn.pModSample;
1746 const bool hasTuning = chn.HasCustomTuning();
1747
1748 // In IT compatible mode, we use always frequencies, otherwise we use periods, which are upside down.
1749 // In this context, the "up" tables refer to the tables that increase frequency, and the down tables are the ones that decrease frequency.
1750 const bool useFreq = PeriodsAreFrequencies();
1751 const uint32 (&upTable)[256] = useFreq ? LinearSlideUpTable : LinearSlideDownTable;
1752 const uint32 (&downTable)[256] = useFreq ? LinearSlideDownTable : LinearSlideUpTable;
1753 const uint32 (&fineUpTable)[16] = useFreq ? FineLinearSlideUpTable : FineLinearSlideDownTable;
1754 const uint32 (&fineDownTable)[16] = useFreq ? FineLinearSlideDownTable : FineLinearSlideUpTable;
1755
1756 // IT compatibility: Autovibrato is so much different in IT that I just put this in a separate code block, to get rid of a dozen IsCompatibilityMode() calls.
1757 if(m_playBehaviour[kITVibratoTremoloPanbrello] && !hasTuning && GetType() != MOD_TYPE_MT2)
1758 {
1759 if(!pSmp->nVibRate)
1760 return;
1761
1762 // Schism's autovibrato code
1763
1764 /*
1765 X86 Assembler from ITTECH.TXT:
1766 1) Mov AX, [SomeVariableNameRelatingToVibrato]
1767 2) Add AL, Rate
1768 3) AdC AH, 0
1769 4) AH contains the depth of the vibrato as a fine-linear slide.
1770 5) Mov [SomeVariableNameRelatingToVibrato], AX ; For the next cycle.
1771 */
1772 const int vibpos = chn.nAutoVibPos & 0xFF;
1773 int adepth = chn.nAutoVibDepth; // (1)
1774 adepth += pSmp->nVibSweep; // (2 & 3)
1775 LimitMax(adepth, static_cast<int>(pSmp->nVibDepth * 256u));
1776 chn.nAutoVibDepth = adepth; // (5)
1777 adepth /= 256; // (4)
1778
1779 chn.nAutoVibPos += pSmp->nVibRate;
1780
1781 int vdelta;
1782 switch(pSmp->nVibType)
1783 {
1784 case VIB_RANDOM:
1785 vdelta = mpt::random<int, 7>(AccessPRNG()) - 0x40;
1786 break;
1787 case VIB_RAMP_DOWN:
1788 vdelta = 64 - (vibpos + 1) / 2;
1789 break;
1790 case VIB_RAMP_UP:
1791 vdelta = ((vibpos + 1) / 2) - 64;
1792 break;
1793 case VIB_SQUARE:
1794 vdelta = vibpos < 128 ? 64 : 0;
1795 break;
1796 case VIB_SINE:
1797 default:
1798 vdelta = ITSinusTable[vibpos];
1799 break;
1800 }
1801
1802 vdelta = (vdelta * adepth) / 64;
1803 uint32 l = std::abs(vdelta);
1804 LimitMax(period, Util::MaxValueOfType(period) / 256);
1805 period *= 256;
1806 if(vdelta < 0)
1807 {
1808 vdelta = Util::muldiv(period, downTable[l / 4u], 0x10000) - period;
1809 if (l & 0x03)
1810 {
1811 vdelta += Util::muldiv(period, fineDownTable[l & 0x03], 0x10000) - period;
1812 }
1813 } else
1814 {
1815 vdelta = Util::muldiv(period, upTable[l / 4u], 0x10000) - period;
1816 if (l & 0x03)
1817 {
1818 vdelta += Util::muldiv(period, fineUpTable[l & 0x03], 0x10000) - period;
1819 }
1820 }
1821 period = (period + vdelta) / 256;
1822 nPeriodFrac = vdelta & 0xFF;
1823 } else
1824 {
1825 // MPT's autovibrato code
1826 if (pSmp->nVibSweep == 0 && !(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)))
1827 {
1828 chn.nAutoVibDepth = pSmp->nVibDepth * 256;
1829 } else
1830 {
1831 // Calculate current autovibrato depth using vibsweep
1832 if (GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
1833 {
1834 chn.nAutoVibDepth += pSmp->nVibSweep * 2u;
1835 } else
1836 {
1837 if(!chn.dwFlags[CHN_KEYOFF])
1838 {
1839 chn.nAutoVibDepth += (pSmp->nVibDepth * 256u) / pSmp->nVibSweep;
1840 }
1841 }
1842 LimitMax(chn.nAutoVibDepth, static_cast<int>(pSmp->nVibDepth * 256u));
1843 }
1844 chn.nAutoVibPos += pSmp->nVibRate;
1845 int vdelta;
1846 switch(pSmp->nVibType)
1847 {
1848 case VIB_RANDOM:
1849 vdelta = ModRandomTable[chn.nAutoVibPos & 0x3F];
1850 chn.nAutoVibPos++;
1851 break;
1852 case VIB_RAMP_DOWN:
1853 vdelta = ((0x40 - (chn.nAutoVibPos / 2u)) & 0x7F) - 0x40;
1854 break;
1855 case VIB_RAMP_UP:
1856 vdelta = ((0x40 + (chn.nAutoVibPos / 2u)) & 0x7F) - 0x40;
1857 break;
1858 case VIB_SQUARE:
1859 vdelta = (chn.nAutoVibPos & 128) ? +64 : -64;
1860 break;
1861 case VIB_SINE:
1862 default:
1863 if(GetType() != MOD_TYPE_MT2)
1864 {
1865 vdelta = -ITSinusTable[chn.nAutoVibPos & 0xFF];
1866 } else
1867 {
1868 // Fix flat-sounding pads in "another worlds" by Eternal Engine.
1869 // Vibrato starts at the maximum amplitude of the sine wave
1870 // and the vibrato frequency never decreases below the original note's frequency.
1871 vdelta = (-ITSinusTable[(chn.nAutoVibPos + 192) & 0xFF] + 64) / 2;
1872 }
1873 }
1874 int n = (vdelta * chn.nAutoVibDepth) / 256;
1875
1876 if(hasTuning)
1877 {
1878 //Vib sweep is not taken into account here.
1879 vibratoFactor += 0.05F * pSmp->nVibDepth * vdelta / 4096.0f; //4096 == 64^2
1880 //See vibrato for explanation.
1881 chn.m_CalculateFreq = true;
1882 /*
1883 Finestep vibrato:
1884 const float autoVibDepth = pSmp->nVibDepth * val / 4096.0f; //4096 == 64^2
1885 vibratoFineSteps += static_cast<CTuning::FINESTEPTYPE>(chn.pModInstrument->pTuning->GetFineStepCount() * autoVibDepth);
1886 chn.m_CalculateFreq = true;
1887 */
1888 }
1889 else //Original behavior
1890 {
1891 if (GetType() != MOD_TYPE_XM)
1892 {
1893 int df1, df2;
1894 if (n < 0)
1895 {
1896 n = -n;
1897 uint32 n1 = n / 256;
1898 df1 = downTable[n1];
1899 df2 = downTable[n1+1];
1900 } else
1901 {
1902 uint32 n1 = n / 256;
1903 df1 = upTable[n1];
1904 df2 = upTable[n1+1];
1905 }
1906 n /= 4;
1907 period = Util::muldiv(period, df1 + ((df2 - df1) * (n & 0x3F) / 64), 256);
1908 nPeriodFrac = period & 0xFF;
1909 period /= 256;
1910 } else
1911 {
1912 period += (n / 64);
1913 }
1914 } //Original MPT behavior
1915 }
1916 }
1917 }
1918
1919
ProcessRamping(ModChannel & chn) const1920 void CSoundFile::ProcessRamping(ModChannel &chn) const
1921 {
1922 chn.leftRamp = chn.rightRamp = 0;
1923 LimitMax(chn.newLeftVol, int32_max >> VOLUMERAMPPRECISION);
1924 LimitMax(chn.newRightVol, int32_max >> VOLUMERAMPPRECISION);
1925 if(chn.dwFlags[CHN_VOLUMERAMP] && (chn.leftVol != chn.newLeftVol || chn.rightVol != chn.newRightVol))
1926 {
1927 const bool rampUp = (chn.newLeftVol > chn.leftVol) || (chn.newRightVol > chn.rightVol);
1928 int32 rampLength, globalRampLength, instrRampLength = 0;
1929 rampLength = globalRampLength = (rampUp ? m_MixerSettings.GetVolumeRampUpSamples() : m_MixerSettings.GetVolumeRampDownSamples());
1930 //XXXih: add real support for bidi ramping here
1931
1932 if(m_playBehaviour[kFT2VolumeRamping] && (GetType() & MOD_TYPE_XM))
1933 {
1934 // apply FT2-style super-soft volume ramping (5ms), overriding openmpt settings
1935 rampLength = globalRampLength = Util::muldivr(5, m_MixerSettings.gdwMixingFreq, 1000);
1936 }
1937
1938 if(chn.pModInstrument != nullptr && rampUp)
1939 {
1940 instrRampLength = chn.pModInstrument->nVolRampUp;
1941 rampLength = instrRampLength ? (m_MixerSettings.gdwMixingFreq * instrRampLength / 100000) : globalRampLength;
1942 }
1943 const bool enableCustomRamp = (instrRampLength > 0);
1944
1945 if(!rampLength)
1946 {
1947 rampLength = 1;
1948 }
1949
1950 int32 leftDelta = ((chn.newLeftVol - chn.leftVol) * (1 << VOLUMERAMPPRECISION));
1951 int32 rightDelta = ((chn.newRightVol - chn.rightVol) * (1 << VOLUMERAMPPRECISION));
1952 if(!enableCustomRamp)
1953 {
1954 // Extra-smooth ramping, unless we're forced to use the default values
1955 if((chn.leftVol | chn.rightVol) && (chn.newLeftVol | chn.newRightVol) && !chn.dwFlags[CHN_FASTVOLRAMP])
1956 {
1957 rampLength = m_PlayState.m_nBufferCount;
1958 Limit(rampLength, globalRampLength, int32(1 << (VOLUMERAMPPRECISION - 1)));
1959 }
1960 }
1961
1962 chn.leftRamp = leftDelta / rampLength;
1963 chn.rightRamp = rightDelta / rampLength;
1964 chn.leftVol = chn.newLeftVol - ((chn.leftRamp * rampLength) / (1 << VOLUMERAMPPRECISION));
1965 chn.rightVol = chn.newRightVol - ((chn.rightRamp * rampLength) / (1 << VOLUMERAMPPRECISION));
1966
1967 if (chn.leftRamp|chn.rightRamp)
1968 {
1969 chn.nRampLength = rampLength;
1970 } else
1971 {
1972 chn.dwFlags.reset(CHN_VOLUMERAMP);
1973 chn.leftVol = chn.newLeftVol;
1974 chn.rightVol = chn.newRightVol;
1975 }
1976 } else
1977 {
1978 chn.dwFlags.reset(CHN_VOLUMERAMP);
1979 chn.leftVol = chn.newLeftVol;
1980 chn.rightVol = chn.newRightVol;
1981 }
1982 chn.rampLeftVol = chn.leftVol * (1 << VOLUMERAMPPRECISION);
1983 chn.rampRightVol = chn.rightVol * (1 << VOLUMERAMPPRECISION);
1984 chn.dwFlags.reset(CHN_FASTVOLRAMP);
1985 }
1986
1987
1988 // Returns channel increment and frequency with FREQ_FRACBITS fractional bits
GetChannelIncrement(const ModChannel & chn,uint32 period,int periodFrac) const1989 std::pair<SamplePosition, uint32> CSoundFile::GetChannelIncrement(const ModChannel &chn, uint32 period, int periodFrac) const
1990 {
1991 uint32 freq;
1992 if(!chn.HasCustomTuning())
1993 freq = GetFreqFromPeriod(period, chn.nC5Speed, periodFrac);
1994 else
1995 freq = chn.nPeriod;
1996
1997 const ModInstrument *ins = chn.pModInstrument;
1998
1999 if(int32 finetune = chn.microTuning; finetune != 0)
2000 {
2001 if(ins)
2002 finetune *= ins->midiPWD;
2003 if(finetune)
2004 freq = mpt::saturate_round<uint32>(freq * std::pow(2.0, finetune / (12.0 * 256.0 * 128.0)));
2005 }
2006
2007 // Applying Pitch/Tempo lock
2008 if(ins && ins->pitchToTempoLock.GetRaw())
2009 {
2010 freq = Util::muldivr(freq, m_PlayState.m_nMusicTempo.GetRaw(), ins->pitchToTempoLock.GetRaw());
2011 }
2012
2013 // Avoid increment to overflow and become negative with unrealisticly high frequencies.
2014 LimitMax(freq, uint32(int32_max));
2015 return {SamplePosition::Ratio(freq, m_MixerSettings.gdwMixingFreq << FREQ_FRACBITS), freq};
2016 }
2017
2018
2019 ////////////////////////////////////////////////////////////////////////////////////////////
2020 // Handles envelopes & mixer setup
2021
ReadNote()2022 bool CSoundFile::ReadNote()
2023 {
2024 #ifdef MODPLUG_TRACKER
2025 // Checking end of row ?
2026 if(m_SongFlags[SONG_PAUSED])
2027 {
2028 m_PlayState.m_nTickCount = 0;
2029 if (!m_PlayState.m_nMusicSpeed) m_PlayState.m_nMusicSpeed = 6;
2030 if (!m_PlayState.m_nMusicTempo.GetRaw()) m_PlayState.m_nMusicTempo.Set(125);
2031 } else
2032 #endif // MODPLUG_TRACKER
2033 {
2034 if(!ProcessRow())
2035 return false;
2036 }
2037 ////////////////////////////////////////////////////////////////////////////////////
2038 if (m_PlayState.m_nMusicTempo.GetRaw() == 0) return false;
2039
2040 m_PlayState.m_nSamplesPerTick = GetTickDuration(m_PlayState);
2041 m_PlayState.m_nBufferCount = m_PlayState.m_nSamplesPerTick;
2042
2043 // Master Volume + Pre-Amplification / Attenuation setup
2044 uint32 nMasterVol;
2045 {
2046 CHANNELINDEX nchn32 = Clamp(m_nChannels, CHANNELINDEX(1), CHANNELINDEX(31));
2047
2048 uint32 mastervol;
2049
2050 if (m_PlayConfig.getUseGlobalPreAmp())
2051 {
2052 int realmastervol = m_MixerSettings.m_nPreAmp;
2053 if (realmastervol > 0x80)
2054 {
2055 //Attenuate global pre-amp depending on num channels
2056 realmastervol = 0x80 + ((realmastervol - 0x80) * (nchn32 + 4)) / 16;
2057 }
2058 mastervol = (realmastervol * (m_nSamplePreAmp)) / 64;
2059 } else
2060 {
2061 //Preferred option: don't use global pre-amp at all.
2062 mastervol = m_nSamplePreAmp;
2063 }
2064
2065 if (m_PlayConfig.getUseGlobalPreAmp())
2066 {
2067 uint32 attenuation =
2068 #ifndef NO_AGC
2069 (m_MixerSettings.DSPMask & SNDDSP_AGC) ? PreAmpAGCTable[nchn32 / 2u] :
2070 #endif
2071 PreAmpTable[nchn32 / 2u];
2072 if(attenuation < 1) attenuation = 1;
2073 nMasterVol = (mastervol << 7) / attenuation;
2074 } else
2075 {
2076 nMasterVol = mastervol;
2077 }
2078 }
2079
2080 ////////////////////////////////////////////////////////////////////////////////////
2081 // Update channels data
2082 m_nMixChannels = 0;
2083 for (CHANNELINDEX nChn = 0; nChn < MAX_CHANNELS; nChn++)
2084 {
2085 ModChannel &chn = m_PlayState.Chn[nChn];
2086 // FT2 Compatibility: Prevent notes to be stopped after a fadeout. This way, a portamento effect can pick up a faded instrument which is long enough.
2087 // This occurs for example in the bassline (channel 11) of jt_burn.xm. I hope this won't break anything else...
2088 // I also suppose this could decrease mixing performance a bit, but hey, which CPU can't handle 32 muted channels these days... :-)
2089 if(chn.dwFlags[CHN_NOTEFADE] && (!(chn.nFadeOutVol|chn.leftVol|chn.rightVol)) && !m_playBehaviour[kFT2ProcessSilentChannels])
2090 {
2091 chn.nLength = 0;
2092 chn.nROfs = chn.nLOfs = 0;
2093 }
2094 // Check for unused channel
2095 if(chn.dwFlags[CHN_MUTE] || (nChn >= m_nChannels && !chn.nLength))
2096 {
2097 if(nChn < m_nChannels)
2098 {
2099 // Process MIDI macros on channels that are currently muted.
2100 ProcessMacroOnChannel(nChn);
2101 }
2102 chn.nLeftVU = chn.nRightVU = 0;
2103 continue;
2104 }
2105 // Reset channel data
2106 chn.increment = SamplePosition(0);
2107 chn.nRealVolume = 0;
2108 chn.nCalcVolume = 0;
2109
2110 chn.nRampLength = 0;
2111
2112 //Aux variables
2113 Tuning::RATIOTYPE vibratoFactor = 1;
2114 Tuning::NOTEINDEXTYPE arpeggioSteps = 0;
2115
2116 const ModInstrument *pIns = chn.pModInstrument;
2117
2118 // Calc Frequency
2119 int32 period = 0;
2120
2121 // Also process envelopes etc. when there's a plugin on this channel, for possible fake automation using volume and pan data.
2122 // We only care about master channels, though, since automation only "happens" on them.
2123 const bool samplePlaying = (chn.nPeriod && chn.nLength);
2124 const bool plugAssigned = (nChn < m_nChannels) && (ChnSettings[nChn].nMixPlugin || (chn.pModInstrument != nullptr && chn.pModInstrument->nMixPlug));
2125 if (samplePlaying || plugAssigned)
2126 {
2127 int vol = chn.nVolume;
2128 int insVol = chn.nInsVol; // This is the "SV * IV" value in ITTECH.TXT
2129
2130 ProcessVolumeSwing(chn, m_playBehaviour[kITSwingBehaviour] ? insVol : vol);
2131 ProcessPanningSwing(chn);
2132 ProcessTremolo(chn, vol);
2133 ProcessTremor(nChn, vol);
2134
2135 // Clip volume and multiply (extend to 14 bits)
2136 Limit(vol, 0, 256);
2137 vol <<= 6;
2138
2139 // Process Envelopes
2140 if (pIns)
2141 {
2142 if(m_playBehaviour[kITEnvelopePositionHandling])
2143 {
2144 // In IT compatible mode, envelope position indices are shifted by one for proper envelope pausing,
2145 // so we have to update the position before we actually process the envelopes.
2146 // When using MPT behaviour, we get the envelope position for the next tick while we are still calculating the current tick,
2147 // which then results in wrong position information when the envelope is paused on the next row.
2148 // Test cases: s77.it
2149 IncrementEnvelopePositions(chn);
2150 }
2151 ProcessVolumeEnvelope(chn, vol);
2152 ProcessInstrumentFade(chn, vol);
2153 ProcessPanningEnvelope(chn);
2154
2155 if(!m_playBehaviour[kITPitchPanSeparation] && chn.nNote != NOTE_NONE && chn.pModInstrument && chn.pModInstrument->nPPS != 0)
2156 ProcessPitchPanSeparation(chn.nRealPan, chn.nNote, *chn.pModInstrument);
2157 } else
2158 {
2159 // No Envelope: key off => note cut
2160 if(chn.dwFlags[CHN_NOTEFADE]) // 1.41-: CHN_KEYOFF|CHN_NOTEFADE
2161 {
2162 chn.nFadeOutVol = 0;
2163 vol = 0;
2164 }
2165 }
2166
2167 if(chn.isPaused)
2168 vol = 0;
2169
2170 // vol is 14-bits
2171 if (vol)
2172 {
2173 // IMPORTANT: chn.nRealVolume is 14 bits !!!
2174 // -> Util::muldiv( 14+8, 6+6, 18); => RealVolume: 14-bit result (22+12-20)
2175
2176 if(chn.dwFlags[CHN_SYNCMUTE])
2177 {
2178 chn.nRealVolume = 0;
2179 } else if (m_PlayConfig.getGlobalVolumeAppliesToMaster())
2180 {
2181 // Don't let global volume affect level of sample if
2182 // Global volume is going to be applied to master output anyway.
2183 chn.nRealVolume = Util::muldiv(vol * MAX_GLOBAL_VOLUME, chn.nGlobalVol * insVol, 1 << 20);
2184 } else
2185 {
2186 chn.nRealVolume = Util::muldiv(vol * m_PlayState.m_nGlobalVolume, chn.nGlobalVol * insVol, 1 << 20);
2187 }
2188 }
2189
2190 chn.nCalcVolume = vol; // Update calculated volume for MIDI macros
2191
2192 // ST3 only clamps the final output period, but never the channel's internal period.
2193 // Test case: PeriodLimit.s3m
2194 if (chn.nPeriod < m_nMinPeriod
2195 && GetType() != MOD_TYPE_S3M
2196 && !PeriodsAreFrequencies())
2197 {
2198 chn.nPeriod = m_nMinPeriod;
2199 } else if(chn.nPeriod >= m_nMaxPeriod && m_playBehaviour[kApplyUpperPeriodLimit] && !PeriodsAreFrequencies())
2200 {
2201 // ...but on the other hand, ST3's SoundBlaster driver clamps the maximum channel period.
2202 // Test case: PeriodLimitUpper.s3m
2203 chn.nPeriod = m_nMaxPeriod;
2204 }
2205 if(m_playBehaviour[kFT2Periods]) Clamp(chn.nPeriod, 1, 31999);
2206 period = chn.nPeriod;
2207
2208 // When glissando mode is set to semitones, clamp to the next halftone.
2209 if((chn.dwFlags & (CHN_GLISSANDO | CHN_PORTAMENTO)) == (CHN_GLISSANDO | CHN_PORTAMENTO)
2210 && (!m_SongFlags[SONG_PT_MODE] || (chn.rowCommand.IsPortamento() && !m_SongFlags[SONG_FIRSTTICK])))
2211 {
2212 if(period != chn.cachedPeriod)
2213 {
2214 // Only recompute this whole thing in case the base period has changed.
2215 chn.cachedPeriod = period;
2216 chn.glissandoPeriod = GetPeriodFromNote(GetNoteFromPeriod(period, chn.nFineTune, chn.nC5Speed), chn.nFineTune, chn.nC5Speed);
2217 }
2218 period = chn.glissandoPeriod;
2219 }
2220
2221 ProcessArpeggio(nChn, period, arpeggioSteps);
2222
2223 // Preserve Amiga freq limits.
2224 // In ST3, the frequency is always clamped to periods 113 to 856, while in ProTracker,
2225 // the limit is variable, depending on the finetune of the sample.
2226 // The int32_max test is for the arpeggio wrap-around in ProcessArpeggio().
2227 // Test case: AmigaLimits.s3m, AmigaLimitsFinetune.mod
2228 if(m_SongFlags[SONG_AMIGALIMITS | SONG_PT_MODE] && period != int32_max)
2229 {
2230 int limitLow = 113 * 4, limitHigh = 856 * 4;
2231 if(GetType() != MOD_TYPE_S3M)
2232 {
2233 const int tableOffset = XM2MODFineTune(chn.nFineTune) * 12;
2234 limitLow = ProTrackerTunedPeriods[tableOffset + 11] / 2;
2235 limitHigh = ProTrackerTunedPeriods[tableOffset] * 2;
2236 // Amiga cannot actually keep up with lower periods
2237 if(limitLow < 113 * 4) limitLow = 113 * 4;
2238 }
2239 Limit(period, limitLow, limitHigh);
2240 Limit(chn.nPeriod, limitLow, limitHigh);
2241 }
2242
2243 ProcessPanbrello(chn);
2244 }
2245
2246 // IT Compatibility: Ensure that there is no pan swing, panbrello, panning envelopes, etc. applied on surround channels.
2247 // Test case: surround-pan.it
2248 if(chn.dwFlags[CHN_SURROUND] && !m_SongFlags[SONG_SURROUNDPAN] && m_playBehaviour[kITNoSurroundPan])
2249 {
2250 chn.nRealPan = 128;
2251 }
2252
2253 // Now that all relevant envelopes etc. have been processed, we can parse the MIDI macro data.
2254 ProcessMacroOnChannel(nChn);
2255
2256 // After MIDI macros have been processed, we can also process the pitch / filter envelope and other pitch-related things.
2257 if(samplePlaying)
2258 {
2259 int cutoff = ProcessPitchFilterEnvelope(chn, period);
2260 if(cutoff >= 0 && chn.dwFlags[CHN_ADLIB] && m_opl)
2261 {
2262 // Cutoff doubles as modulator intensity for FM instruments
2263 m_opl->Volume(nChn, static_cast<uint8>(cutoff / 4), true);
2264 }
2265 }
2266
2267 if(chn.rowCommand.volcmd == VOLCMD_VIBRATODEPTH &&
2268 (chn.rowCommand.command == CMD_VIBRATO || chn.rowCommand.command == CMD_VIBRATOVOL || chn.rowCommand.command == CMD_FINEVIBRATO))
2269 {
2270 if(GetType() == MOD_TYPE_XM)
2271 {
2272 // XM Compatibility: Vibrato should be advanced twice (but not added up) if both volume-column and effect column vibrato is present.
2273 // Effect column vibrato parameter has precedence if non-zero.
2274 // Test case: VibratoDouble.xm
2275 if(!m_SongFlags[SONG_FIRSTTICK])
2276 chn.nVibratoPos += chn.nVibratoSpeed;
2277 } else if(GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT))
2278 {
2279 // IT Compatibility: Vibrato should be applied twice if both volume-colum and effect column vibrato is present.
2280 // Volume column vibrato parameter has precedence if non-zero.
2281 // Test case: VibratoDouble.it
2282 Vibrato(chn, chn.rowCommand.vol);
2283 ProcessVibrato(nChn, period, vibratoFactor);
2284 }
2285 }
2286 // Plugins may also receive vibrato
2287 ProcessVibrato(nChn, period, vibratoFactor);
2288
2289 if(samplePlaying)
2290 {
2291 int nPeriodFrac = 0;
2292 ProcessSampleAutoVibrato(chn, period, vibratoFactor, nPeriodFrac);
2293
2294 // Final Period
2295 // ST3 only clamps the final output period, but never the channel's internal period.
2296 // Test case: PeriodLimit.s3m
2297 if (period <= m_nMinPeriod)
2298 {
2299 if(m_playBehaviour[kST3LimitPeriod]) chn.nLength = 0; // Pattern 15 in watcha.s3m
2300 period = m_nMinPeriod;
2301 }
2302
2303 const bool hasTuning = chn.HasCustomTuning();
2304 if(hasTuning)
2305 {
2306 if(chn.m_CalculateFreq || (chn.m_ReCalculateFreqOnFirstTick && m_PlayState.m_nTickCount == 0))
2307 {
2308 chn.RecalcTuningFreq(vibratoFactor, arpeggioSteps, *this);
2309 if(!chn.m_CalculateFreq)
2310 chn.m_ReCalculateFreqOnFirstTick = false;
2311 else
2312 chn.m_CalculateFreq = false;
2313 }
2314 }
2315
2316 auto [ninc, freq] = GetChannelIncrement(chn, period, nPeriodFrac);
2317 #ifndef MODPLUG_TRACKER
2318 ninc.MulDiv(m_nFreqFactor, 65536);
2319 #endif // !MODPLUG_TRACKER
2320 if(ninc.IsZero())
2321 {
2322 ninc.Set(0, 1);
2323 }
2324 chn.increment = ninc;
2325
2326 if((chn.dwFlags & (CHN_ADLIB | CHN_MUTE | CHN_SYNCMUTE)) == CHN_ADLIB && m_opl)
2327 {
2328 const bool doProcess = m_playBehaviour[kOPLFlexibleNoteOff] || !chn.dwFlags[CHN_NOTEFADE] || GetType() == MOD_TYPE_S3M;
2329 if(doProcess && !(GetType() == MOD_TYPE_S3M && chn.dwFlags[CHN_KEYOFF]))
2330 {
2331 // In ST3, a sample rate of 8363 Hz is mapped to middle-C, which is 261.625 Hz in a tempered scale at A4 = 440.
2332 // Hence, we have to translate our "sample rate" into pitch.
2333 auto milliHertz = Util::muldivr_unsigned(freq, 261625, 8363 << FREQ_FRACBITS);
2334
2335 const bool keyOff = chn.dwFlags[CHN_KEYOFF] || (chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0);
2336 if(!m_playBehaviour[kOPLNoteStopWith0Hz] || !keyOff)
2337 m_opl->Frequency(nChn, milliHertz, keyOff, m_playBehaviour[kOPLBeatingOscillators]);
2338 }
2339 if(doProcess)
2340 {
2341 // Scale volume to OPL range (0...63).
2342 m_opl->Volume(nChn, static_cast<uint8>(Util::muldivr_unsigned(chn.nCalcVolume * chn.nGlobalVol * chn.nInsVol, 63, 1 << 26)), false);
2343 chn.nRealPan = m_opl->Pan(nChn, chn.nRealPan) * 128 + 128;
2344 }
2345
2346 // Deallocate OPL channels for notes that are most definitely never going to play again.
2347 if(const auto *ins = chn.pModInstrument; ins != nullptr
2348 && (ins->VolEnv.dwFlags & (ENV_ENABLED | ENV_LOOP | ENV_SUSTAIN)) == ENV_ENABLED
2349 && !ins->VolEnv.empty()
2350 && chn.GetEnvelope(ENV_VOLUME).nEnvPosition >= ins->VolEnv.back().tick
2351 && ins->VolEnv.back().value == 0)
2352 {
2353 m_opl->NoteCut(nChn);
2354 if(!m_playBehaviour[kOPLNoResetAtEnvelopeEnd])
2355 chn.dwFlags.reset(CHN_ADLIB);
2356 chn.dwFlags.set(CHN_NOTEFADE);
2357 chn.nFadeOutVol = 0;
2358 } else if(m_playBehaviour[kOPLFlexibleNoteOff] && chn.dwFlags[CHN_NOTEFADE] && chn.nFadeOutVol == 0)
2359 {
2360 m_opl->NoteCut(nChn);
2361 chn.dwFlags.reset(CHN_ADLIB);
2362 }
2363 }
2364 }
2365
2366 // Increment envelope positions
2367 if(pIns != nullptr && !m_playBehaviour[kITEnvelopePositionHandling])
2368 {
2369 // In IT and FT2 compatible mode, envelope positions are updated above.
2370 // Test cases: s77.it, EnvLoops.xm
2371 IncrementEnvelopePositions(chn);
2372 }
2373
2374 // Volume ramping
2375 chn.dwFlags.set(CHN_VOLUMERAMP, (chn.nRealVolume | chn.rightVol | chn.leftVol) != 0 && !chn.dwFlags[CHN_ADLIB]);
2376
2377 constexpr uint8 VUMETER_DECAY = 4;
2378 chn.nLeftVU = (chn.nLeftVU > VUMETER_DECAY) ? (chn.nLeftVU - VUMETER_DECAY) : 0;
2379 chn.nRightVU = (chn.nRightVU > VUMETER_DECAY) ? (chn.nRightVU - VUMETER_DECAY) : 0;
2380
2381 chn.newLeftVol = chn.newRightVol = 0;
2382 chn.pCurrentSample = (chn.pModSample && chn.pModSample->HasSampleData() && chn.nLength && chn.IsSamplePlaying()) ? chn.pModSample->samplev() : nullptr;
2383 if(chn.pCurrentSample || (chn.HasMIDIOutput() && !chn.dwFlags[CHN_KEYOFF | CHN_NOTEFADE]))
2384 {
2385 // Update VU-Meter (nRealVolume is 14-bit)
2386 uint32 vul = (chn.nRealVolume * (256-chn.nRealPan)) / (1 << 14);
2387 if (vul > 127) vul = 127;
2388 if (chn.nLeftVU > 127) chn.nLeftVU = (uint8)vul;
2389 vul /= 2;
2390 if (chn.nLeftVU < vul) chn.nLeftVU = (uint8)vul;
2391 uint32 vur = (chn.nRealVolume * chn.nRealPan) / (1 << 14);
2392 if (vur > 127) vur = 127;
2393 if (chn.nRightVU > 127) chn.nRightVU = (uint8)vur;
2394 vur /= 2;
2395 if (chn.nRightVU < vur) chn.nRightVU = (uint8)vur;
2396 } else
2397 {
2398 // Note change but no sample
2399 if (chn.nLeftVU > 128) chn.nLeftVU = 0;
2400 if (chn.nRightVU > 128) chn.nRightVU = 0;
2401 }
2402
2403 if (chn.pCurrentSample)
2404 {
2405 #ifdef MODPLUG_TRACKER
2406 const uint32 kChnMasterVol = chn.dwFlags[CHN_EXTRALOUD] ? (uint32)m_PlayConfig.getNormalSamplePreAmp() : nMasterVol;
2407 #else
2408 const uint32 kChnMasterVol = nMasterVol;
2409 #endif // MODPLUG_TRACKER
2410
2411 // Adjusting volumes
2412 {
2413 int32 pan = (m_MixerSettings.gnChannels >= 2) ? Clamp(chn.nRealPan, 0, 256) : 128;
2414
2415 int32 realvol;
2416 if(m_PlayConfig.getUseGlobalPreAmp())
2417 {
2418 realvol = (chn.nRealVolume * kChnMasterVol) / 128;
2419 } else
2420 {
2421 // Extra attenuation required here if we're bypassing pre-amp.
2422 realvol = (chn.nRealVolume * kChnMasterVol) / 256;
2423 }
2424
2425 const PanningMode panningMode = m_PlayConfig.getPanningMode();
2426 if(panningMode == PanningMode::SoftPanning || (panningMode == PanningMode::Undetermined && (m_MixerSettings.MixerFlags & SNDMIX_SOFTPANNING)))
2427 {
2428 if(pan < 128)
2429 {
2430 chn.newLeftVol = (realvol * 128) / 256;
2431 chn.newRightVol = (realvol * pan) / 256;
2432 } else
2433 {
2434 chn.newLeftVol = (realvol * (256 - pan)) / 256;
2435 chn.newRightVol = (realvol * 128) / 256;
2436 }
2437 } else if(panningMode == PanningMode::FT2Panning)
2438 {
2439 // FT2 uses square root panning. There is a 257-entry LUT for this,
2440 // but FT2's internal panning ranges from 0 to 255 only, meaning that
2441 // you can never truly achieve 100% right panning in FT2, only 100% left.
2442 // Test case: FT2PanLaw.xm
2443 LimitMax(pan, 255);
2444 const int panL = pan > 0 ? XMPanningTable[256 - pan] : 65536;
2445 const int panR = XMPanningTable[pan];
2446 chn.newLeftVol = (realvol * panL) / 65536;
2447 chn.newRightVol = (realvol * panR) / 65536;
2448 } else
2449 {
2450 chn.newLeftVol = (realvol * (256 - pan)) / 256;
2451 chn.newRightVol = (realvol * pan) / 256;
2452 }
2453 }
2454 // Clipping volumes
2455 //if (chn.nNewRightVol > 0xFFFF) chn.nNewRightVol = 0xFFFF;
2456 //if (chn.nNewLeftVol > 0xFFFF) chn.nNewLeftVol = 0xFFFF;
2457
2458 if(chn.pModInstrument && Resampling::IsKnownMode(chn.pModInstrument->resampling))
2459 {
2460 // For defined resampling modes, use per-instrument resampling mode if set
2461 chn.resamplingMode = chn.pModInstrument->resampling;
2462 } else if(Resampling::IsKnownMode(m_nResampling))
2463 {
2464 chn.resamplingMode = m_nResampling;
2465 } else if(m_SongFlags[SONG_ISAMIGA] && m_Resampler.m_Settings.emulateAmiga != Resampling::AmigaFilter::Off)
2466 {
2467 // Enforce Amiga resampler for Amiga modules
2468 chn.resamplingMode = SRCMODE_AMIGA;
2469 } else
2470 {
2471 // Default to global mixer settings
2472 chn.resamplingMode = m_Resampler.m_Settings.SrcMode;
2473 }
2474
2475 if(chn.increment.IsUnity() && !(chn.dwFlags[CHN_VIBRATO] || chn.nAutoVibDepth || chn.resamplingMode == SRCMODE_AMIGA))
2476 {
2477 // Exact sample rate match, do not interpolate at all
2478 // - unless vibrato is applied, because in this case the constant enabling and disabling
2479 // of resampling can introduce clicks (this is easily observable with a sine sample
2480 // played at the mix rate).
2481 chn.resamplingMode = SRCMODE_NEAREST;
2482 }
2483
2484 const int extraAttenuation = m_PlayConfig.getExtraSampleAttenuation();
2485 chn.newLeftVol /= (1 << extraAttenuation);
2486 chn.newRightVol /= (1 << extraAttenuation);
2487
2488 // Dolby Pro-Logic Surround
2489 if(chn.dwFlags[CHN_SURROUND] && m_MixerSettings.gnChannels == 2) chn.newRightVol = -chn.newRightVol;
2490
2491 // Checking Ping-Pong Loops
2492 if(chn.dwFlags[CHN_PINGPONGFLAG]) chn.increment.Negate();
2493
2494 // Setting up volume ramp
2495 ProcessRamping(chn);
2496
2497 // Adding the channel in the channel list
2498 if(!chn.dwFlags[CHN_ADLIB])
2499 {
2500 m_PlayState.ChnMix[m_nMixChannels++] = nChn;
2501 }
2502 } else
2503 {
2504 chn.rightVol = chn.leftVol = 0;
2505 chn.nLength = 0;
2506 // Put the channel back into the mixer for end-of-sample pop reduction
2507 if(chn.nLOfs || chn.nROfs)
2508 m_PlayState.ChnMix[m_nMixChannels++] = nChn;
2509 }
2510
2511 chn.dwOldFlags = chn.dwFlags;
2512 }
2513
2514 // If there are more channels being mixed than allowed, order them by volume and discard the most quiet ones
2515 if(m_nMixChannels >= m_MixerSettings.m_nMaxMixChannels)
2516 {
2517 std::partial_sort(std::begin(m_PlayState.ChnMix), std::begin(m_PlayState.ChnMix) + m_MixerSettings.m_nMaxMixChannels, std::begin(m_PlayState.ChnMix) + m_nMixChannels,
2518 [this](CHANNELINDEX i, CHANNELINDEX j) { return (m_PlayState.Chn[i].nRealVolume > m_PlayState.Chn[j].nRealVolume); });
2519 }
2520 return true;
2521 }
2522
2523
ProcessMacroOnChannel(CHANNELINDEX nChn)2524 void CSoundFile::ProcessMacroOnChannel(CHANNELINDEX nChn)
2525 {
2526 ModChannel &chn = m_PlayState.Chn[nChn];
2527 if(nChn < GetNumChannels())
2528 {
2529 // TODO evaluate per-plugin macros here
2530 //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_PAN]);
2531 //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_VOLUME]);
2532
2533 if((chn.rowCommand.command == CMD_MIDI && m_SongFlags[SONG_FIRSTTICK]) || chn.rowCommand.command == CMD_SMOOTHMIDI)
2534 {
2535 if(chn.rowCommand.param < 0x80)
2536 ProcessMIDIMacro(nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiSFXExt[chn.nActiveMacro], chn.rowCommand.param);
2537 else
2538 ProcessMIDIMacro(nChn, (chn.rowCommand.command == CMD_SMOOTHMIDI), m_MidiCfg.szMidiZXXExt[(chn.rowCommand.param & 0x7F)], 0);
2539 }
2540 }
2541 }
2542
2543
2544 #ifndef NO_PLUGINS
2545
ProcessMidiOut(CHANNELINDEX nChn)2546 void CSoundFile::ProcessMidiOut(CHANNELINDEX nChn)
2547 {
2548 ModChannel &chn = m_PlayState.Chn[nChn];
2549
2550 // Do we need to process MIDI?
2551 // For now there is no difference between mute and sync mute with VSTis.
2552 if(chn.dwFlags[CHN_MUTE | CHN_SYNCMUTE] || !chn.HasMIDIOutput()) return;
2553
2554 // Get instrument info and plugin reference
2555 const ModInstrument *pIns = chn.pModInstrument; // Can't be nullptr at this point, as we have valid MIDI output.
2556
2557 // No instrument or muted instrument?
2558 if(pIns->dwFlags[INS_MUTE])
2559 {
2560 return;
2561 }
2562
2563 // Check instrument plugins
2564 const PLUGINDEX nPlugin = GetBestPlugin(nChn, PrioritiseInstrument, RespectMutes);
2565 IMixPlugin *pPlugin = nullptr;
2566 if(nPlugin > 0 && nPlugin <= MAX_MIXPLUGINS)
2567 {
2568 pPlugin = m_MixPlugins[nPlugin - 1].pMixPlugin;
2569 }
2570
2571 // Couldn't find a valid plugin
2572 if(pPlugin == nullptr) return;
2573
2574 const ModCommand::NOTE note = chn.rowCommand.note;
2575 // Check for volume commands
2576 uint8 vol = 0xFF;
2577 if(chn.rowCommand.volcmd == VOLCMD_VOLUME)
2578 {
2579 vol = std::min(chn.rowCommand.vol, uint8(64));
2580 } else if(chn.rowCommand.command == CMD_VOLUME)
2581 {
2582 vol = std::min(chn.rowCommand.param, uint8(64));
2583 }
2584 const bool hasVolCommand = (vol != 0xFF);
2585
2586 if(m_playBehaviour[kMIDICCBugEmulation])
2587 {
2588 if(note != NOTE_NONE)
2589 {
2590 ModCommand::NOTE realNote = note;
2591 if(ModCommand::IsNote(note))
2592 realNote = pIns->NoteMap[note - NOTE_MIN];
2593 SendMIDINote(nChn, realNote, static_cast<uint16>(chn.nVolume));
2594 } else if(hasVolCommand)
2595 {
2596 pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Fine, vol, nChn);
2597 }
2598 return;
2599 }
2600
2601 const uint32 defaultVolume = pIns->nGlobalVol;
2602
2603 //If new note, determine notevelocity to use.
2604 if(note != NOTE_NONE)
2605 {
2606 int32 velocity = static_cast<int32>(4 * defaultVolume);
2607 switch(pIns->pluginVelocityHandling)
2608 {
2609 case PLUGIN_VELOCITYHANDLING_CHANNEL:
2610 velocity = chn.nVolume;
2611 break;
2612 default:
2613 break;
2614 }
2615
2616 int32 swing = chn.nVolSwing;
2617 if(m_playBehaviour[kITSwingBehaviour]) swing *= 4;
2618 velocity += swing;
2619 Limit(velocity, 0, 256);
2620
2621 ModCommand::NOTE realNote = note;
2622 if(ModCommand::IsNote(note))
2623 realNote = pIns->NoteMap[note - NOTE_MIN];
2624 // Experimental VST panning
2625 //ProcessMIDIMacro(nChn, false, m_MidiCfg.szMidiGlb[MIDIOUT_PAN], 0, nPlugin);
2626 SendMIDINote(nChn, realNote, static_cast<uint16>(velocity));
2627 }
2628
2629 const bool processVolumeAlsoOnNote = (pIns->pluginVelocityHandling == PLUGIN_VELOCITYHANDLING_VOLUME);
2630 const bool hasNote = m_playBehaviour[kMIDIVolumeOnNoteOffBug] ? (note != NOTE_NONE) : ModCommand::IsNote(note);
2631
2632 if((hasVolCommand && !hasNote) || (hasNote && processVolumeAlsoOnNote))
2633 {
2634 switch(pIns->pluginVolumeHandling)
2635 {
2636 case PLUGIN_VOLUMEHANDLING_DRYWET:
2637 if(hasVolCommand) pPlugin->SetDryRatio(2 * vol);
2638 else pPlugin->SetDryRatio(2 * defaultVolume);
2639 break;
2640 case PLUGIN_VOLUMEHANDLING_MIDI:
2641 if(hasVolCommand) pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, std::min(uint8(127), static_cast<uint8>(2 * vol)), nChn);
2642 else pPlugin->MidiCC(MIDIEvents::MIDICC_Volume_Coarse, static_cast<uint8>(std::min(uint32(127), static_cast<uint32>(2 * defaultVolume))), nChn);
2643 break;
2644 default:
2645 break;
2646 }
2647 }
2648 }
2649
2650 #endif // NO_PLUGINS
2651
2652
2653 template<int channels>
ApplyGlobalVolumeWithRamping(int32 * SoundBuffer,int32 * RearBuffer,int32 lCount,int32 m_nGlobalVolume,int32 step,int32 & m_nSamplesToGlobalVolRampDest,int32 & m_lHighResRampingGlobalVolume)2654 MPT_FORCEINLINE void ApplyGlobalVolumeWithRamping(int32 *SoundBuffer, int32 *RearBuffer, int32 lCount, int32 m_nGlobalVolume, int32 step, int32 &m_nSamplesToGlobalVolRampDest, int32 &m_lHighResRampingGlobalVolume)
2655 {
2656 const bool isStereo = (channels >= 2);
2657 const bool hasRear = (channels >= 4);
2658 for(int pos = 0; pos < lCount; ++pos)
2659 {
2660 if(m_nSamplesToGlobalVolRampDest > 0)
2661 {
2662 // Ramping required
2663 m_lHighResRampingGlobalVolume += step;
2664 SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION);
2665 if constexpr(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION);
2666 if constexpr(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer);
2667 if constexpr(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_lHighResRampingGlobalVolume, MAX_GLOBAL_VOLUME << VOLUMERAMPPRECISION); else MPT_UNUSED_VARIABLE(RearBuffer);
2668 m_nSamplesToGlobalVolRampDest--;
2669 } else
2670 {
2671 SoundBuffer[0] = Util::muldiv(SoundBuffer[0], m_nGlobalVolume, MAX_GLOBAL_VOLUME);
2672 if constexpr(isStereo) SoundBuffer[1] = Util::muldiv(SoundBuffer[1], m_nGlobalVolume, MAX_GLOBAL_VOLUME);
2673 if constexpr(hasRear) RearBuffer[0] = Util::muldiv(RearBuffer[0] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer);
2674 if constexpr(hasRear) RearBuffer[1] = Util::muldiv(RearBuffer[1] , m_nGlobalVolume, MAX_GLOBAL_VOLUME); else MPT_UNUSED_VARIABLE(RearBuffer);
2675 m_lHighResRampingGlobalVolume = m_nGlobalVolume << VOLUMERAMPPRECISION;
2676 }
2677 SoundBuffer += isStereo ? 2 : 1;
2678 if constexpr(hasRear) RearBuffer += 2;
2679 }
2680 }
2681
2682
ProcessGlobalVolume(long lCount)2683 void CSoundFile::ProcessGlobalVolume(long lCount)
2684 {
2685
2686 // should we ramp?
2687 if(IsGlobalVolumeUnset())
2688 {
2689 // do not ramp if no global volume was set before (which is the case at song start), to prevent audible glitches when default volume is > 0 and it is set to 0 in the first row
2690 m_PlayState.m_nGlobalVolumeDestination = m_PlayState.m_nGlobalVolume;
2691 m_PlayState.m_nSamplesToGlobalVolRampDest = 0;
2692 m_PlayState.m_nGlobalVolumeRampAmount = 0;
2693 } else if(m_PlayState.m_nGlobalVolumeDestination != m_PlayState.m_nGlobalVolume)
2694 {
2695 // User has provided new global volume
2696
2697 // m_nGlobalVolume: the last global volume which got set e.g. by a pattern command
2698 // m_nGlobalVolumeDestination: the current target of the ramping algorithm
2699 const bool rampUp = m_PlayState.m_nGlobalVolume > m_PlayState.m_nGlobalVolumeDestination;
2700
2701 m_PlayState.m_nGlobalVolumeDestination = m_PlayState.m_nGlobalVolume;
2702 m_PlayState.m_nSamplesToGlobalVolRampDest = m_PlayState.m_nGlobalVolumeRampAmount = rampUp ? m_MixerSettings.GetVolumeRampUpSamples() : m_MixerSettings.GetVolumeRampDownSamples();
2703 }
2704
2705 // calculate ramping step
2706 int32 step = 0;
2707 if (m_PlayState.m_nSamplesToGlobalVolRampDest > 0)
2708 {
2709
2710 // Still some ramping left to do.
2711 int32 highResGlobalVolumeDestination = static_cast<int32>(m_PlayState.m_nGlobalVolumeDestination) << VOLUMERAMPPRECISION;
2712
2713 const long delta = highResGlobalVolumeDestination - m_PlayState.m_lHighResRampingGlobalVolume;
2714 step = delta / static_cast<long>(m_PlayState.m_nSamplesToGlobalVolRampDest);
2715
2716 if(m_nMixLevels == MixLevels::v1_17RC2)
2717 {
2718 // Define max step size as some factor of user defined ramping value: the lower the value, the more likely the click.
2719 // If step is too big (might cause click), extend ramp length.
2720 // Warning: This increases the volume ramp length by EXTREME amounts (factors of 100 are easily reachable)
2721 // compared to the user-defined setting, so this really should not be used!
2722 int32 maxStep = std::max(int32(50), static_cast<int32>((10000 / (m_PlayState.m_nGlobalVolumeRampAmount + 1))));
2723 while(std::abs(step) > maxStep)
2724 {
2725 m_PlayState.m_nSamplesToGlobalVolRampDest += m_PlayState.m_nGlobalVolumeRampAmount;
2726 step = delta / static_cast<int32>(m_PlayState.m_nSamplesToGlobalVolRampDest);
2727 }
2728 }
2729 }
2730
2731 // apply volume and ramping
2732 if(m_MixerSettings.gnChannels == 1)
2733 {
2734 ApplyGlobalVolumeWithRamping<1>(MixSoundBuffer, MixRearBuffer, lCount, m_PlayState.m_nGlobalVolume, step, m_PlayState.m_nSamplesToGlobalVolRampDest, m_PlayState.m_lHighResRampingGlobalVolume);
2735 } else if(m_MixerSettings.gnChannels == 2)
2736 {
2737 ApplyGlobalVolumeWithRamping<2>(MixSoundBuffer, MixRearBuffer, lCount, m_PlayState.m_nGlobalVolume, step, m_PlayState.m_nSamplesToGlobalVolRampDest, m_PlayState.m_lHighResRampingGlobalVolume);
2738 } else if(m_MixerSettings.gnChannels == 4)
2739 {
2740 ApplyGlobalVolumeWithRamping<4>(MixSoundBuffer, MixRearBuffer, lCount, m_PlayState.m_nGlobalVolume, step, m_PlayState.m_nSamplesToGlobalVolRampDest, m_PlayState.m_lHighResRampingGlobalVolume);
2741 }
2742
2743 }
2744
2745
ProcessStereoSeparation(long countChunk)2746 void CSoundFile::ProcessStereoSeparation(long countChunk)
2747 {
2748 ApplyStereoSeparation(MixSoundBuffer, MixRearBuffer, m_MixerSettings.gnChannels, countChunk, m_MixerSettings.m_nStereoSeparation);
2749 }
2750
2751
2752 OPENMPT_NAMESPACE_END
2753