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