260 lines
6.7 KiB
C
260 lines
6.7 KiB
C
|
/*
|
||
|
* SampleIO.h
|
||
|
* ----------
|
||
|
* Purpose: Central code for reading and writing samples. Create your SampleIO object and have a go at the ReadSample and WriteSample functions!
|
||
|
* Notes : Not all combinations of possible sample format combinations are implemented, especially for WriteSample.
|
||
|
* Using the existing generic sample conversion functors in SampleFormatConverters.h, it should be quite easy to extend the code, though.
|
||
|
* Authors: Olivier Lapicque
|
||
|
* OpenMPT Devs
|
||
|
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "BuildSettings.h"
|
||
|
|
||
|
|
||
|
#include "../common/FileReaderFwd.h"
|
||
|
|
||
|
|
||
|
OPENMPT_NAMESPACE_BEGIN
|
||
|
|
||
|
|
||
|
struct ModSample;
|
||
|
|
||
|
// Sample import / export formats
|
||
|
class SampleIO
|
||
|
{
|
||
|
public:
|
||
|
// Bits per sample
|
||
|
enum Bitdepth : uint8
|
||
|
{
|
||
|
_8bit = 8,
|
||
|
_16bit = 16,
|
||
|
_24bit = 24,
|
||
|
_32bit = 32,
|
||
|
_64bit = 64,
|
||
|
};
|
||
|
|
||
|
// Number of channels + channel format
|
||
|
enum Channels : uint8
|
||
|
{
|
||
|
mono = 1,
|
||
|
stereoInterleaved, // LRLRLR...
|
||
|
stereoSplit, // LLL...RRR...
|
||
|
};
|
||
|
|
||
|
// Sample byte order
|
||
|
enum Endianness : uint8
|
||
|
{
|
||
|
littleEndian = 0,
|
||
|
bigEndian = 1,
|
||
|
};
|
||
|
|
||
|
// Sample encoding
|
||
|
enum Encoding : uint8
|
||
|
{
|
||
|
signedPCM = 0, // Integer PCM, signed
|
||
|
unsignedPCM, // Integer PCM, unsigned
|
||
|
deltaPCM, // Integer PCM, delta-encoded
|
||
|
floatPCM, // Floating point PCM
|
||
|
IT214, // Impulse Tracker 2.14 compressed
|
||
|
IT215, // Impulse Tracker 2.15 compressed
|
||
|
AMS, // AMS / Velvet Studio packed
|
||
|
DMF, // DMF Huffman compression
|
||
|
MDL, // MDL Huffman compression
|
||
|
PTM8Dto16, // PTM 8-Bit delta value -> 16-Bit sample
|
||
|
PCM7to8, // 8-Bit sample data with unused high bit
|
||
|
ADPCM, // 4-Bit ADPCM-packed
|
||
|
MT2, // MadTracker 2 stereo delta encoding
|
||
|
floatPCM15, // Floating point PCM with 2^15 full scale
|
||
|
floatPCM23, // Floating point PCM with 2^23 full scale
|
||
|
floatPCMnormalize, // Floating point PCM and data will be normalized while reading
|
||
|
signedPCMnormalize, // Integer PCM and data will be normalized while reading
|
||
|
uLaw, // 8-to-16 bit G.711 u-law compression
|
||
|
aLaw, // 8-to-16 bit G.711 a-law compression
|
||
|
};
|
||
|
|
||
|
protected:
|
||
|
Bitdepth m_bitdepth;
|
||
|
Channels m_channels;
|
||
|
Endianness m_endianness;
|
||
|
Encoding m_encoding;
|
||
|
|
||
|
public:
|
||
|
constexpr SampleIO(Bitdepth bits = _8bit, Channels channels = mono, Endianness endianness = littleEndian, Encoding encoding = signedPCM)
|
||
|
: m_bitdepth(bits), m_channels(channels), m_endianness(endianness), m_encoding(encoding)
|
||
|
{ }
|
||
|
|
||
|
bool operator== (const SampleIO &other) const
|
||
|
{
|
||
|
return memcmp(this, &other, sizeof(*this)) == 0;
|
||
|
}
|
||
|
|
||
|
bool operator!= (const SampleIO &other) const
|
||
|
{
|
||
|
return memcmp(this, &other, sizeof(*this)) != 0;
|
||
|
}
|
||
|
|
||
|
void operator|= (Bitdepth bits)
|
||
|
{
|
||
|
m_bitdepth = bits;
|
||
|
}
|
||
|
|
||
|
void operator|= (Channels channels)
|
||
|
{
|
||
|
m_channels = channels;
|
||
|
}
|
||
|
|
||
|
void operator|= (Endianness endianness)
|
||
|
{
|
||
|
m_endianness = endianness;
|
||
|
}
|
||
|
|
||
|
void operator|= (Encoding encoding)
|
||
|
{
|
||
|
m_encoding = encoding;
|
||
|
}
|
||
|
|
||
|
void MayNormalize()
|
||
|
{
|
||
|
if(GetBitDepth() >= 24)
|
||
|
{
|
||
|
if(GetEncoding() == SampleIO::signedPCM)
|
||
|
{
|
||
|
m_encoding = SampleIO::signedPCMnormalize;
|
||
|
} else if(GetEncoding() == SampleIO::floatPCM)
|
||
|
{
|
||
|
m_encoding = SampleIO::floatPCMnormalize;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Return 0 in case of variable-length encoded samples.
|
||
|
MPT_CONSTEXPR14_FUN uint8 GetEncodedBitsPerSample() const
|
||
|
{
|
||
|
switch(GetEncoding())
|
||
|
{
|
||
|
case signedPCM: // Integer PCM, signed
|
||
|
case unsignedPCM: //Integer PCM, unsigned
|
||
|
case deltaPCM: // Integer PCM, delta-encoded
|
||
|
case floatPCM: // Floating point PCM
|
||
|
case MT2: // MadTracker 2 stereo delta encoding
|
||
|
case floatPCM15: // Floating point PCM with 2^15 full scale
|
||
|
case floatPCM23: // Floating point PCM with 2^23 full scale
|
||
|
case floatPCMnormalize: // Floating point PCM and data will be normalized while reading
|
||
|
case signedPCMnormalize: // Integer PCM and data will be normalized while reading
|
||
|
return GetBitDepth();
|
||
|
|
||
|
case IT214: // Impulse Tracker 2.14 compressed
|
||
|
case IT215: // Impulse Tracker 2.15 compressed
|
||
|
case AMS: // AMS / Velvet Studio packed
|
||
|
case DMF: // DMF Huffman compression
|
||
|
case MDL: // MDL Huffman compression
|
||
|
return 0; // variable-length compressed
|
||
|
|
||
|
case PTM8Dto16: // PTM 8-Bit delta value -> 16-Bit sample
|
||
|
return 16;
|
||
|
case PCM7to8: // 8-Bit sample data with unused high bit
|
||
|
return 8;
|
||
|
case ADPCM: // 4-Bit ADPCM-packed
|
||
|
return 4;
|
||
|
case uLaw: // G.711 u-law
|
||
|
return 8;
|
||
|
case aLaw: // G.711 a-law
|
||
|
return 8;
|
||
|
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Return the static header size additional to the raw encoded sample data.
|
||
|
MPT_CONSTEXPR14_FUN std::size_t GetEncodedHeaderSize() const
|
||
|
{
|
||
|
switch(GetEncoding())
|
||
|
{
|
||
|
case ADPCM:
|
||
|
return 16;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Returns true if the encoded size cannot be calculated apriori from the encoding format and the sample length.
|
||
|
MPT_CONSTEXPR14_FUN bool IsVariableLengthEncoded() const
|
||
|
{
|
||
|
return GetEncodedBitsPerSample() == 0;
|
||
|
}
|
||
|
|
||
|
// Returns true if the decoder for a given format uses FileReader interface and thus do not need to call GetPinnedRawDataView()
|
||
|
MPT_CONSTEXPR14_FUN bool UsesFileReaderForDecoding() const
|
||
|
{
|
||
|
switch(GetEncoding())
|
||
|
{
|
||
|
case IT214:
|
||
|
case IT215:
|
||
|
case AMS:
|
||
|
case DMF:
|
||
|
case MDL:
|
||
|
return true;
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get bits per sample
|
||
|
constexpr uint8 GetBitDepth() const
|
||
|
{
|
||
|
return static_cast<uint8>(m_bitdepth);
|
||
|
}
|
||
|
// Get channel layout
|
||
|
constexpr Channels GetChannelFormat() const
|
||
|
{
|
||
|
return m_channels;
|
||
|
}
|
||
|
// Get number of channels
|
||
|
constexpr uint8 GetNumChannels() const
|
||
|
{
|
||
|
return GetChannelFormat() == mono ? 1u : 2u;
|
||
|
}
|
||
|
// Get sample byte order
|
||
|
constexpr Endianness GetEndianness() const
|
||
|
{
|
||
|
return m_endianness;
|
||
|
}
|
||
|
// Get sample format / encoding
|
||
|
constexpr Encoding GetEncoding() const
|
||
|
{
|
||
|
return m_encoding;
|
||
|
}
|
||
|
|
||
|
// Returns the encoded size of the sample. In case of variable-length encoding returns 0.
|
||
|
std::size_t CalculateEncodedSize(SmpLength length) const
|
||
|
{
|
||
|
if(IsVariableLengthEncoded())
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
uint8 bps = GetEncodedBitsPerSample();
|
||
|
if(bps % 8u != 0)
|
||
|
{
|
||
|
MPT_ASSERT(GetEncoding() == ADPCM && bps == 4);
|
||
|
return GetEncodedHeaderSize() + (((length + 1) / 2) * GetNumChannels()); // round up
|
||
|
}
|
||
|
return GetEncodedHeaderSize() + (length * (bps / 8) * GetNumChannels());
|
||
|
}
|
||
|
|
||
|
// Read a sample from memory
|
||
|
size_t ReadSample(ModSample &sample, FileReader &file) const;
|
||
|
|
||
|
#ifndef MODPLUG_NO_FILESAVE
|
||
|
// Write a sample to file
|
||
|
size_t WriteSample(std::ostream &f, const ModSample &sample, SmpLength maxSamples = 0) const;
|
||
|
#endif // MODPLUG_NO_FILESAVE
|
||
|
};
|
||
|
|
||
|
|
||
|
OPENMPT_NAMESPACE_END
|