/* * ModChannel.h * ------------ * Purpose: Module Channel header class and helpers * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "BuildSettings.h" #include "ModSample.h" #include "ModInstrument.h" #include "modcommand.h" #include "Paula.h" #include "tuningbase.h" OPENMPT_NAMESPACE_BEGIN class CSoundFile; // Mix Channel Struct struct ModChannel { // Envelope playback info struct EnvInfo { FlagSet flags; uint32 nEnvPosition = 0; int32 nEnvValueAtReleaseJump = NOT_YET_RELEASED; void Reset() { nEnvPosition = 0; nEnvValueAtReleaseJump = NOT_YET_RELEASED; } }; // Information used in the mixer (should be kept tight for better caching) SamplePosition position; // Current play position (fixed point) SamplePosition increment; // Sample speed relative to mixing frequency (fixed point) const void *pCurrentSample; // Currently playing sample (nullptr if no sample is playing) int32 leftVol; // 0...4096 (12 bits, since 16 bits + 12 bits = 28 bits = 0dB in integer mixer, see MIXING_ATTENUATION) int32 rightVol; // Ditto int32 leftRamp; // Ramping delta, 20.12 fixed point (see VOLUMERAMPPRECISION) int32 rightRamp; // Ditto int32 rampLeftVol; // Current ramping volume, 20.12 fixed point (see VOLUMERAMPPRECISION) int32 rampRightVol; // Ditto mixsample_t nFilter_Y[2][2]; // Filter memory - two history items per sample channel mixsample_t nFilter_A0, nFilter_B0, nFilter_B1; // Filter coeffs mixsample_t nFilter_HP; SmpLength nLength; SmpLength nLoopStart; SmpLength nLoopEnd; FlagSet dwFlags; mixsample_t nROfs, nLOfs; uint32 nRampLength; const ModSample *pModSample; // Currently assigned sample slot (may already be stopped) Paula::State paulaState; // Information not used in the mixer const ModInstrument *pModInstrument; // Currently assigned instrument slot SmpLength prevNoteOffset; // Offset for instrument-less notes for ProTracker/ScreamTracker SmpLength oldOffset; FlagSet dwOldFlags; // Flags from previous tick int32 newLeftVol, newRightVol; int32 nRealVolume, nRealPan; int32 nVolume, nPan, nFadeOutVol; int32 nPeriod; // Frequency in Hz if CSoundFile::PeriodsAreFrequencies() or using custom tuning, 4x Amiga periods otherwise int32 nC5Speed, nPortamentoDest; int32 cachedPeriod, glissandoPeriod; int32 nCalcVolume; // Calculated channel volume, 14-Bit (without global volume, pre-amp etc applied) - for MIDI macros EnvInfo VolEnv, PanEnv, PitchEnv; // Envelope playback info int32 nGlobalVol; // Channel volume (CV in ITTECH.TXT) 0...64 int32 nInsVol; // Sample / Instrument volume (SV * IV in ITTECH.TXT) 0...64 int32 nFineTune, nTranspose; int32 nPortamentoSlide, nAutoVibDepth; uint32 nEFxOffset; // Offset memory for Invert Loop (EFx, .MOD only) int16 nVolSwing, nPanSwing; int16 nCutSwing, nResSwing; uint16 nRestorePanOnNewNote; //If > 0, nPan should be set to nRestorePanOnNewNote - 1 on new note. Used to recover from pan swing and IT sample / instrument panning. High bit set = surround int16 nRetrigCount, nRetrigParam; ROWINDEX nPatternLoop; CHANNELINDEX nMasterChn; ModCommand rowCommand; // 8-bit members ResamplingMode resamplingMode; uint8 nRestoreResonanceOnNewNote; // See nRestorePanOnNewNote uint8 nRestoreCutoffOnNewNote; // ditto uint8 nNote; NewNoteAction nNNA; uint8 nLastNote; // Last note, ignoring note offs and cuts - for MIDI macros uint8 nArpeggioLastNote, nArpeggioBaseNote; // For plugin arpeggio uint8 nNewNote, nNewIns, nOldIns, nCommand, nArpeggio; uint8 nOldVolumeSlide, nOldFineVolUpDown; uint8 nOldPortaUp, nOldPortaDown, nOldFinePortaUpDown, nOldExtraFinePortaUpDown; uint8 nOldPanSlide, nOldChnVolSlide; uint8 nOldGlobalVolSlide; uint8 nAutoVibPos, nVibratoPos, nTremoloPos, nPanbrelloPos; uint8 nVibratoType, nVibratoSpeed, nVibratoDepth; uint8 nTremoloType, nTremoloSpeed, nTremoloDepth; uint8 nPanbrelloType, nPanbrelloSpeed, nPanbrelloDepth; int8 nPanbrelloOffset, nPanbrelloRandomMemory; uint8 nOldCmdEx, nOldVolParam, nOldTempo; uint8 nOldHiOffset; uint8 nCutOff, nResonance; uint8 nTremorCount, nTremorParam; uint8 nPatternLoopCount; uint8 nLeftVU, nRightVU; uint8 nActiveMacro; FilterMode nFilterMode; uint8 nEFxSpeed, nEFxDelay; // memory for Invert Loop (EFx, .MOD only) uint8 nNoteSlideCounter, nNoteSlideSpeed, nNoteSlideStep; // 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 //-->Variables used to make user-definable tuning modes work with pattern effects. //If true, freq should be recalculated in ReadNote() on first tick. //Currently used only for vibrato things - using in other context might be //problematic. bool m_ReCalculateFreqOnFirstTick : 1; //To tell whether to calculate frequency. bool m_CalculateFreq : 1; int32 m_PortamentoFineSteps, m_PortamentoTickSlide; //NOTE_PCs memory. float m_plugParamValueStep, m_plugParamTargetValue; uint16 m_RowPlugParam; PLUGINDEX m_RowPlug; void ClearRowCmd() { rowCommand = ModCommand(); } // Get a reference to a specific envelope of this channel const EnvInfo &GetEnvelope(EnvelopeType envType) const { switch(envType) { case ENV_VOLUME: default: return VolEnv; case ENV_PANNING: return PanEnv; case ENV_PITCH: return PitchEnv; } } EnvInfo &GetEnvelope(EnvelopeType envType) { return const_cast(static_cast(this)->GetEnvelope(envType)); } void ResetEnvelopes() { VolEnv.Reset(); PanEnv.Reset(); PitchEnv.Reset(); } enum ResetFlags { resetChannelSettings = 1, // Reload initial channel settings resetSetPosBasic = 2, // Reset basic runtime channel attributes resetSetPosAdvanced = 4, // Reset more runtime channel attributes resetSetPosFull = resetSetPosBasic | resetSetPosAdvanced | resetChannelSettings, // Reset all runtime channel attributes resetTotal = resetSetPosFull, }; void Reset(ResetFlags resetMask, const CSoundFile &sndFile, CHANNELINDEX sourceChannel, ChannelFlags muteFlag); void Stop(); bool IsSamplePlaying() const noexcept { return !increment.IsZero(); } uint32 GetVSTVolume() const noexcept { return (pModInstrument) ? pModInstrument->nGlobalVol * 4 : nVolume; } ModCommand::NOTE GetPluginNote(bool realNoteMapping) const; // Check if the channel has a valid MIDI output. A return value of true implies that pModInstrument != nullptr. bool HasMIDIOutput() const noexcept { return pModInstrument != nullptr && pModInstrument->HasValidMIDIChannel(); } // Check if the channel uses custom tuning. A return value of true implies that pModInstrument != nullptr. bool HasCustomTuning() const noexcept { return pModInstrument != nullptr && pModInstrument->pTuning != nullptr; } // Check if currently processed loop is a sustain loop. pModSample is not checked for validity! bool InSustainLoop() const noexcept { return (dwFlags & (CHN_LOOP | CHN_KEYOFF)) == CHN_LOOP && pModSample->uFlags[CHN_SUSTAINLOOP]; } void UpdateInstrumentVolume(const ModSample *smp, const ModInstrument *ins); void SetInstrumentPan(int32 pan, const CSoundFile &sndFile); void RestorePanAndFilter(); void RecalcTuningFreq(Tuning::RATIOTYPE vibratoFactor, Tuning::NOTEINDEXTYPE arpeggioSteps, const CSoundFile &sndFile); // IT command S73-S7E void InstrumentControl(uint8 param, const CSoundFile &sndFile); }; // Default pattern channel settings struct ModChannelSettings { FlagSet dwFlags; // Channel flags uint16 nPan; // Initial pan (0...256) uint16 nVolume; // Initial channel volume (0...64) PLUGINDEX nMixPlugin; // Assigned plugin mpt::charbuf szName; // Channel name ModChannelSettings() { Reset(); } void Reset() { dwFlags.reset(); nPan = 128; nVolume = 64; nMixPlugin = 0; szName = ""; } }; OPENMPT_NAMESPACE_END