1 /* 2 * ModInstrument.h 3 * --------------- 4 * Purpose: Module Instrument header class and helpers 5 * Notes : (currently none) 6 * Authors: OpenMPT Devs 7 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. 8 */ 9 10 11 #pragma once 12 13 #include "openmpt/all/BuildSettings.hpp" 14 15 #include "modcommand.h" 16 #include "tuningbase.h" 17 #include "Snd_defs.h" 18 #include "openmpt/base/FlagSet.hpp" 19 #include "../common/misc_util.h" 20 #include <set> 21 22 OPENMPT_NAMESPACE_BEGIN 23 24 class CSoundFile; 25 26 // Instrument Nodes 27 struct EnvelopeNode 28 { 29 using tick_t = uint16 ; 30 using value_t = uint8; 31 32 tick_t tick = 0; // Envelope node position (x axis) 33 value_t value = 0; // Envelope node value (y axis) 34 EnvelopeNodeEnvelopeNode35 EnvelopeNode() { } EnvelopeNodeEnvelopeNode36 EnvelopeNode(tick_t tick, value_t value) : tick(tick), value(value) { } 37 38 bool operator== (const EnvelopeNode &other) const { return tick == other.tick && value == other.value; } 39 }; 40 41 // Instrument Envelopes 42 struct InstrumentEnvelope : public std::vector<EnvelopeNode> 43 { 44 FlagSet<EnvelopeFlags> dwFlags; // Envelope flags 45 uint8 nLoopStart = 0; // Loop start node 46 uint8 nLoopEnd = 0; // Loop end node 47 uint8 nSustainStart = 0; // Sustain start node 48 uint8 nSustainEnd = 0; // Sustain end node 49 uint8 nReleaseNode = ENV_RELEASE_NODE_UNSET; // Release node 50 51 // Convert envelope data between various formats. 52 void Convert(MODTYPE fromType, MODTYPE toType); 53 54 // Get envelope value at a given tick. Assumes that the envelope data is in rage [0, rangeIn], 55 // returns value in range [0, rangeOut]. 56 int32 GetValueFromPosition(int position, int32 rangeOut, int32 rangeIn = ENVELOPE_MAX) const; 57 58 // Ensure that ticks are ordered in increasing order and values are within the allowed range. 59 void Sanitize(uint8 maxValue = ENVELOPE_MAX); 60 sizeInstrumentEnvelope61 uint32 size() const { return static_cast<uint32>(std::vector<EnvelopeNode>::size()); } 62 63 using std::vector<EnvelopeNode>::push_back; push_backInstrumentEnvelope64 void push_back(EnvelopeNode::tick_t tick, EnvelopeNode::value_t value) { emplace_back(tick, value); } 65 }; 66 67 // Instrument Struct 68 struct ModInstrument 69 { 70 uint32 nFadeOut = 256; // Instrument fadeout speed 71 uint32 nGlobalVol = 64; // Global volume (0...64, all sample volumes are multiplied with this - TODO: This is 0...128 in Impulse Tracker) 72 uint32 nPan = 32 * 4; // Default pan (0...256), if the appropriate flag is set. Sample panning overrides instrument panning. 73 74 uint16 nVolRampUp = 0; // Default sample ramping up, 0 = use global default 75 76 ResamplingMode resampling = SRCMODE_DEFAULT; // Resampling mode 77 78 FlagSet<InstrumentFlags> dwFlags; // Instrument flags 79 NewNoteAction nNNA = NewNoteAction::NoteCut; // New note action 80 DuplicateCheckType nDCT = DuplicateCheckType::None; // Duplicate check type (i.e. which condition will trigger the duplicate note action) 81 DuplicateNoteAction nDNA = DuplicateNoteAction::NoteCut; // Duplicate note action 82 83 uint8 nPanSwing = 0; // Random panning factor (0...64) 84 uint8 nVolSwing = 0; // Random volume factor (0...100) 85 86 uint8 nIFC = 0; // Default filter cutoff (0...127). Used if the high bit is set 87 uint8 nIFR = 0; // Default filter resonance (0...127). Used if the high bit is set 88 uint8 nCutSwing = 0; // Random cutoff factor (0...64) 89 uint8 nResSwing = 0; // Random resonance factor (0...64) 90 FilterMode filterMode = FilterMode::Unchanged; // Default filter mode 91 92 int8 nPPS = 0; // Pitch/Pan separation (i.e. how wide the panning spreads, -32...32) 93 uint8 nPPC = NOTE_MIDDLEC - NOTE_MIN; // Pitch/Pan centre (zero-based) 94 95 uint16 wMidiBank = 0; // MIDI Bank (1...16384). 0 = Don't send. 96 uint8 nMidiProgram = 0; // MIDI Program (1...128). 0 = Don't send. 97 uint8 nMidiChannel = 0; // MIDI Channel (1...16). 0 = Don't send. 17 = Mapped (Send to tracker channel modulo 16). 98 uint8 nMidiDrumKey = 0; // Drum set note mapping (currently only used by the .MID loader) 99 int8 midiPWD = 2; // MIDI Pitch Wheel Depth and CMD_FINETUNE depth in semitones 100 PLUGINDEX nMixPlug = 0; // Plugin assigned to this instrument (0 = no plugin, 1 = first plugin) 101 102 PlugVelocityHandling pluginVelocityHandling = PLUGIN_VELOCITYHANDLING_CHANNEL; // How to deal with plugin velocity 103 PlugVolumeHandling pluginVolumeHandling = PLUGIN_VOLUMEHANDLING_IGNORE; // How to deal with plugin volume 104 105 TEMPO pitchToTempoLock; // BPM at which the samples assigned to this instrument loop correctly (0 = unset) 106 CTuning *pTuning = nullptr; // sample tuning assigned to this instrument 107 108 InstrumentEnvelope VolEnv; // Volume envelope data 109 InstrumentEnvelope PanEnv; // Panning envelope data 110 InstrumentEnvelope PitchEnv; // Pitch / filter envelope data 111 112 std::array<uint8, 128> NoteMap; // Note mapping, e.g. C-5 => D-5 113 std::array<SAMPLEINDEX, 128> Keyboard; // Sample mapping, e.g. C-5 => Sample 1 114 115 mpt::charbuf<MAX_INSTRUMENTNAME> name; 116 mpt::charbuf<MAX_INSTRUMENTFILENAME> filename; 117 GetNameModInstrument118 std::string GetName() const { return name; } GetFilenameModInstrument119 std::string GetFilename() const { return filename; } 120 121 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 122 // WHEN adding new members here, ALSO update InstrumentExtensions.cpp 123 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 124 125 ModInstrument(SAMPLEINDEX sample = 0); 126 127 // Assign all notes to a given sample. AssignSampleModInstrument128 void AssignSample(SAMPLEINDEX sample) 129 { 130 Keyboard.fill(sample); 131 } 132 133 // Reset note mapping (i.e. every note is mapped to itself) ResetNoteMapModInstrument134 void ResetNoteMap() 135 { 136 for(size_t n = 0; n < std::size(NoteMap); n++) 137 { 138 NoteMap[n] = static_cast<uint8>(n + 1); 139 } 140 } 141 142 // Transpose entire note mapping by given number of semitones 143 void Transpose(int8 amount); 144 IsCutoffEnabledModInstrument145 bool IsCutoffEnabled() const { return (nIFC & 0x80) != 0; } IsResonanceEnabledModInstrument146 bool IsResonanceEnabled() const { return (nIFR & 0x80) != 0; } GetCutoffModInstrument147 uint8 GetCutoff() const { return (nIFC & 0x7F); } GetResonanceModInstrument148 uint8 GetResonance() const { return (nIFR & 0x7F); } SetCutoffModInstrument149 void SetCutoff(uint8 cutoff, bool enable) { nIFC = std::min(cutoff, uint8(0x7F)) | (enable ? 0x80 : 0x00); } SetResonanceModInstrument150 void SetResonance(uint8 resonance, bool enable) { nIFR = std::min(resonance, uint8(0x7F)) | (enable ? 0x80 : 0x00); } 151 HasValidMIDIChannelModInstrument152 bool HasValidMIDIChannel() const { return (nMidiChannel >= 1 && nMidiChannel <= 17); } 153 uint8 GetMIDIChannel(const CSoundFile &sndFile, CHANNELINDEX chn) const; 154 SetTuningModInstrument155 void SetTuning(CTuning *pT) 156 { 157 pTuning = pT; 158 } 159 160 // Get a reference to a specific envelope of this instrument GetEnvelopeModInstrument161 const InstrumentEnvelope &GetEnvelope(EnvelopeType envType) const 162 { 163 switch(envType) 164 { 165 case ENV_VOLUME: 166 default: 167 return VolEnv; 168 case ENV_PANNING: 169 return PanEnv; 170 case ENV_PITCH: 171 return PitchEnv; 172 } 173 } 174 GetEnvelopeModInstrument175 InstrumentEnvelope &GetEnvelope(EnvelopeType envType) 176 { 177 return const_cast<InstrumentEnvelope &>(static_cast<const ModInstrument &>(*this).GetEnvelope(envType)); 178 } 179 180 // Get a set of all samples referenced by this instrument 181 std::set<SAMPLEINDEX> GetSamples() const; 182 183 // Write sample references into a bool vector. If a sample is referenced by this instrument, true is written. 184 // The caller has to initialize the vector. 185 void GetSamples(std::vector<bool> &referencedSamples) const; 186 187 // Translate instrument properties between two given formats. 188 void Convert(MODTYPE fromType, MODTYPE toType); 189 190 // Sanitize all instrument data. 191 void Sanitize(MODTYPE modType); 192 193 }; 194 195 OPENMPT_NAMESPACE_END 196