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