1 /* 2 * SampleIO.h 3 * ---------- 4 * Purpose: Central code for reading and writing samples. Create your SampleIO object and have a go at the ReadSample and WriteSample functions! 5 * Notes : Not all combinations of possible sample format combinations are implemented, especially for WriteSample. 6 * Using the existing generic sample conversion functors in SampleFormatConverters.h, it should be quite easy to extend the code, though. 7 * Authors: Olivier Lapicque 8 * OpenMPT Devs 9 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. 10 */ 11 12 13 #pragma once 14 15 #include "openmpt/all/BuildSettings.hpp" 16 17 18 #include "../common/FileReaderFwd.h" 19 20 21 OPENMPT_NAMESPACE_BEGIN 22 23 24 struct ModSample; 25 26 // Sample import / export formats 27 class SampleIO 28 { 29 public: 30 // Bits per sample 31 enum Bitdepth : uint8 32 { 33 _8bit = 8, 34 _16bit = 16, 35 _24bit = 24, 36 _32bit = 32, 37 _64bit = 64, 38 }; 39 40 // Number of channels + channel format 41 enum Channels : uint8 42 { 43 mono = 1, 44 stereoInterleaved, // LRLRLR... 45 stereoSplit, // LLL...RRR... 46 }; 47 48 // Sample byte order 49 enum Endianness : uint8 50 { 51 littleEndian = 0, 52 bigEndian = 1, 53 }; 54 55 // Sample encoding 56 enum Encoding : uint8 57 { 58 signedPCM = 0, // Integer PCM, signed 59 unsignedPCM, // Integer PCM, unsigned 60 deltaPCM, // Integer PCM, delta-encoded 61 floatPCM, // Floating point PCM 62 IT214, // Impulse Tracker 2.14 compressed 63 IT215, // Impulse Tracker 2.15 compressed 64 AMS, // AMS / Velvet Studio packed 65 DMF, // DMF Huffman compression 66 MDL, // MDL Huffman compression 67 PTM8Dto16, // PTM 8-Bit delta value -> 16-Bit sample 68 ADPCM, // 4-Bit ADPCM-packed 69 MT2, // MadTracker 2 stereo delta encoding 70 floatPCM15, // Floating point PCM with 2^15 full scale 71 floatPCM23, // Floating point PCM with 2^23 full scale 72 floatPCMnormalize, // Floating point PCM and data will be normalized while reading 73 signedPCMnormalize, // Integer PCM and data will be normalized while reading 74 uLaw, // 8-to-16 bit G.711 u-law compression 75 aLaw, // 8-to-16 bit G.711 a-law compression 76 }; 77 78 protected: 79 Bitdepth m_bitdepth; 80 Channels m_channels; 81 Endianness m_endianness; 82 Encoding m_encoding; 83 84 public: 85 constexpr SampleIO(Bitdepth bits = _8bit, Channels channels = mono, Endianness endianness = littleEndian, Encoding encoding = signedPCM) m_bitdepth(bits)86 : m_bitdepth(bits), m_channels(channels), m_endianness(endianness), m_encoding(encoding) 87 { } 88 89 bool operator== (const SampleIO &other) const 90 { 91 return memcmp(this, &other, sizeof(*this)) == 0; 92 } 93 94 bool operator!= (const SampleIO &other) const 95 { 96 return memcmp(this, &other, sizeof(*this)) != 0; 97 } 98 99 void operator|= (Bitdepth bits) 100 { 101 m_bitdepth = bits; 102 } 103 104 void operator|= (Channels channels) 105 { 106 m_channels = channels; 107 } 108 109 void operator|= (Endianness endianness) 110 { 111 m_endianness = endianness; 112 } 113 114 void operator|= (Encoding encoding) 115 { 116 m_encoding = encoding; 117 } 118 MayNormalize()119 void MayNormalize() 120 { 121 if(GetBitDepth() >= 24) 122 { 123 if(GetEncoding() == SampleIO::signedPCM) 124 { 125 m_encoding = SampleIO::signedPCMnormalize; 126 } else if(GetEncoding() == SampleIO::floatPCM) 127 { 128 m_encoding = SampleIO::floatPCMnormalize; 129 } 130 } 131 } 132 133 // Return 0 in case of variable-length encoded samples. GetEncodedBitsPerSample()134 MPT_CONSTEXPRINLINE uint8 GetEncodedBitsPerSample() const 135 { 136 switch(GetEncoding()) 137 { 138 case signedPCM: // Integer PCM, signed 139 case unsignedPCM: //Integer PCM, unsigned 140 case deltaPCM: // Integer PCM, delta-encoded 141 case floatPCM: // Floating point PCM 142 case MT2: // MadTracker 2 stereo delta encoding 143 case floatPCM15: // Floating point PCM with 2^15 full scale 144 case floatPCM23: // Floating point PCM with 2^23 full scale 145 case floatPCMnormalize: // Floating point PCM and data will be normalized while reading 146 case signedPCMnormalize: // Integer PCM and data will be normalized while reading 147 return GetBitDepth(); 148 149 case IT214: // Impulse Tracker 2.14 compressed 150 case IT215: // Impulse Tracker 2.15 compressed 151 case AMS: // AMS / Velvet Studio packed 152 case DMF: // DMF Huffman compression 153 case MDL: // MDL Huffman compression 154 return 0; // variable-length compressed 155 156 case PTM8Dto16: // PTM 8-Bit delta value -> 16-Bit sample 157 return 16; 158 case ADPCM: // 4-Bit ADPCM-packed 159 return 4; 160 case uLaw: // G.711 u-law 161 return 8; 162 case aLaw: // G.711 a-law 163 return 8; 164 165 default: 166 return 0; 167 } 168 } 169 170 // Return the static header size additional to the raw encoded sample data. GetEncodedHeaderSize()171 MPT_CONSTEXPRINLINE std::size_t GetEncodedHeaderSize() const 172 { 173 switch(GetEncoding()) 174 { 175 case ADPCM: 176 return 16; 177 default: 178 return 0; 179 } 180 } 181 182 // Returns true if the encoded size cannot be calculated apriori from the encoding format and the sample length. IsVariableLengthEncoded()183 MPT_CONSTEXPRINLINE bool IsVariableLengthEncoded() const 184 { 185 return GetEncodedBitsPerSample() == 0; 186 } 187 188 // Returns true if the decoder for a given format uses FileReader interface and thus do not need to call GetPinnedView() UsesFileReaderForDecoding()189 MPT_CONSTEXPRINLINE bool UsesFileReaderForDecoding() const 190 { 191 switch(GetEncoding()) 192 { 193 case IT214: 194 case IT215: 195 case AMS: 196 case DMF: 197 case MDL: 198 return true; 199 default: 200 return false; 201 } 202 } 203 204 // Get bits per sample GetBitDepth()205 constexpr uint8 GetBitDepth() const 206 { 207 return static_cast<uint8>(m_bitdepth); 208 } 209 // Get channel layout GetChannelFormat()210 constexpr Channels GetChannelFormat() const 211 { 212 return m_channels; 213 } 214 // Get number of channels GetNumChannels()215 constexpr uint8 GetNumChannels() const 216 { 217 return GetChannelFormat() == mono ? 1u : 2u; 218 } 219 // Get sample byte order GetEndianness()220 constexpr Endianness GetEndianness() const 221 { 222 return m_endianness; 223 } 224 // Get sample format / encoding GetEncoding()225 constexpr Encoding GetEncoding() const 226 { 227 return m_encoding; 228 } 229 230 // Returns the encoded size of the sample. In case of variable-length encoding returns 0. CalculateEncodedSize(SmpLength length)231 std::size_t CalculateEncodedSize(SmpLength length) const 232 { 233 if(IsVariableLengthEncoded()) 234 { 235 return 0; 236 } 237 uint8 bps = GetEncodedBitsPerSample(); 238 if(bps % 8u != 0) 239 { 240 MPT_ASSERT(GetEncoding() == ADPCM && bps == 4); 241 return GetEncodedHeaderSize() + (((length + 1) / 2) * GetNumChannels()); // round up 242 } 243 return GetEncodedHeaderSize() + (length * (bps / 8) * GetNumChannels()); 244 } 245 246 // Read a sample from memory 247 size_t ReadSample(ModSample &sample, FileReader &file) const; 248 249 #ifndef MODPLUG_NO_FILESAVE 250 // Write a sample to file 251 size_t WriteSample(std::ostream &f, const ModSample &sample, SmpLength maxSamples = 0) const; 252 #endif // MODPLUG_NO_FILESAVE 253 }; 254 255 256 OPENMPT_NAMESPACE_END 257