add conformance interface for wav encoding - bit depth supported, channels next, and later on sample rate

adpcm
Dimitri Diakopoulos 11 years ago
parent 8168b1fc92
commit a76b2fe90b

@ -98,7 +98,8 @@ int main()
}
// Test wav file encoder
encoder.WriteFile({2, PCM_FLT, DITHER_NONE}, fileData, "encoded.wav");
int encoderStatus = encoder.WriteFile({2, PCM_24, DITHER_NONE}, fileData, "encoded.wav");
std::cout << "Encoder Status: " << encoderStatus << std::endl;
return 0;
}

@ -35,7 +35,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cassert>
#include <type_traits>
#include <numeric>
#include <array>
#include "PostProcess.h"
#include "Dither.h"
namespace nqr
{
@ -117,7 +119,6 @@ inline bool isOdd(const uint32_t x)
return (x & 0x1);
}
#ifdef ARCH_CPU_LITTLE_ENDIAN
#define Read16(n) (n)
#define Read24(n) (n)
@ -181,6 +182,22 @@ inline int32_t Pack(uint8_t a, uint8_t b, uint8_t c)
#endif
}
inline std::array<uint8_t, 3> Unpack(uint32_t a)
{
static std::array<uint8_t, 3> output;
#ifdef ARCH_CPU_LITTLE_ENDIAN
output[0] = a >> 0;
output[1] = a >> 8;
output[2] = a >> 16;
#else
output[0] = a >> 16;
output[1] = a >> 8;
output[2] = a >> 0;
#endif
return output;
}
//////////////////////////
// Conversion Utilities //
//////////////////////////
@ -198,6 +215,12 @@ static const float NQR_BYTE_2_FLT = 1.0f / 127.0f;
#define int24_to_float32(s) ((float) (s) / NQR_INT24_MAX)
#define int32_to_float32(s) ((float) (s) / NQR_INT32_MAX)
#define float32_to_int8(s) (int8_t) (s * 127.f)
#define float32_to_uint8(s) (uint8_t)((s * 127.f) + 128)
#define float32_to_int16(s) (int16_t) (s * NQR_INT16_MAX)
#define float32_to_int24(s) (int32_t) (s * NQR_INT24_MAX)
#define float32_to_int32(s) (int32_t) (s * NQR_INT32_MAX)
//@todo add 12, 20 for flac
enum PCMFormat
{
@ -219,6 +242,8 @@ void ConvertToFloat32(float * dst, const uint8_t * src, const size_t N, PCMForma
// Src data is always aligned to 4 bytes (WavPack, primarily)
void ConvertToFloat32(float * dst, const int32_t * src, const size_t N, PCMFormat f);
void ConvertFromFloat32(uint8_t * dst, const float * src, const size_t N, PCMFormat f, DitherType t = DITHER_NONE);
//////////////////////////
// User Data + File Ops //
//////////////////////////

@ -26,9 +26,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef DITHER_OPERATIONS_H
#define DITHER_OPERATIONS_H
#include "Common.h"
//@todo libnyquist will, at some point, allow conversion between arbitrary bit-depths
#include <random>
namespace nqr
{
@ -36,8 +34,25 @@ namespace nqr
enum DitherType
{
DITHER_NONE,
DITHER_ONE,
DITHER_TWO
DITHER_TRIANGLE
};
class TriDither
{
std::uniform_real_distribution<float> distribution;
std::mt19937 rndGen;
float prev = 0.0f;
public:
TriDither() : distribution(-0.5f, +0.5f) {}
float operator()(float s)
{
const float value = distribution(rndGen);
s = s + value - prev;
prev = value;
return s;
}
};
} // end namespace nqr

@ -146,6 +146,52 @@ void nqr::ConvertToFloat32(float * dst, const int32_t * src, const size_t N, PCM
}
}
void nqr::ConvertFromFloat32(uint8_t * dst, const float * src, const size_t N, PCMFormat f, DitherType t)
{
//@todo implement dither
assert(f != PCM_END);
if (f == PCM_U8)
{
uint8_t * destPtr = reinterpret_cast<uint8_t *>(dst);
for (size_t i = 0; i < N; ++i)
destPtr[i] = float32_to_uint8(src[i]);
}
else if (f == PCM_S8)
{
int8_t * destPtr = reinterpret_cast<int8_t *>(dst);
for (size_t i = 0; i < N; ++i)
destPtr[i] = float32_to_int8(src[i]);
}
else if (f == PCM_16)
{
int16_t * destPtr = reinterpret_cast<int16_t *>(dst);
for (size_t i = 0; i < N; ++i)
destPtr[i] = float32_to_int16(src[i]);
}
else if (f == PCM_24)
{
uint8_t * destPtr = reinterpret_cast<uint8_t *>(dst);
size_t c = 0;
for (size_t i = 0; i < N; ++i)
{
auto sample = float32_to_int24(src[i]);
auto unpacked = Unpack(sample); // Handles endian swap
destPtr[c] = unpacked[0];
destPtr[c+1] = unpacked[1];
destPtr[c+2] = unpacked[2];
c += 3;
}
}
else if (f == PCM_32)
{
int32_t * destPtr = reinterpret_cast<int32_t *>(dst);
for (size_t i = 0; i < N; ++i)
destPtr[i] = float32_to_int32(src[i]);
}
}
int nqr::GetFormatBitsPerSample(PCMFormat f)
{
switch(f)

@ -47,8 +47,6 @@ ChunkHeaderInfo nqr::ScanForChunk(const std::vector<uint8_t> & fileData, uint32_
return {0, 0};
};
WaveChunkHeader nqr::MakeWaveHeader(const EncoderParams param, const int sampleRate)
{
WaveChunkHeader header;

@ -36,15 +36,9 @@ inline void toBytes(int value, char * arr)
arr[3] = (value >> 24) & 0xFF;
}
WavEncoder::WavEncoder()
{
WavEncoder::WavEncoder() {}
}
WavEncoder::~WavEncoder()
{
}
WavEncoder::~WavEncoder() {}
int WavEncoder::WriteFile(const EncoderParams p, const AudioData * d, const std::string & path)
{
@ -59,7 +53,7 @@ int WavEncoder::WriteFile(const EncoderParams p, const AudioData * d, const std:
}
auto maxFileSizeInBytes = std::numeric_limits<uint32_t>::max();
auto samplesSizeInBytes = d->samples.size() * sizeof(float);
auto samplesSizeInBytes = (d->samples.size() * GetFormatBitsPerSample(p.targetFormat)) / 8;
// 64 arbitrary
if ((samplesSizeInBytes - 64) >= maxFileSizeInBytes)
@ -82,7 +76,7 @@ int WavEncoder::WriteFile(const EncoderParams p, const AudioData * d, const std:
char * chunkSizeBuff = new char[4];
// Initial size
// Initial size (this is changed after we're done writing the file)
toBytes(36, chunkSizeBuff);
// RIFF file header
@ -98,14 +92,18 @@ int WavEncoder::WriteFile(const EncoderParams p, const AudioData * d, const std:
auto sourceBits = GetFormatBitsPerSample(d->sourceFormat);
auto targetBits = GetFormatBitsPerSample(p.targetFormat);
////////////////////////////
//@todo - channel mixing! //
////////////////////////////
// Write out fact chunk
if (p.targetFormat == PCM_FLT)
{
uint32_t four = 4;
uint32_t dataSz = int(d->samples.size() / d->channelCount);
fout.write(GenerateChunkCodeChar('f', 'a', 'c', 't'), 4);
fout.write(reinterpret_cast<const char *>(&four), sizeof(four));
fout.write(reinterpret_cast<const char *>(&dataSz), sizeof(dataSz)); // Number of samples (per channel)
fout.write(reinterpret_cast<const char *>(&four), 4);
fout.write(reinterpret_cast<const char *>(&dataSz), 4); // Number of samples (per channel)
}
// Data header
@ -115,13 +113,17 @@ int WavEncoder::WriteFile(const EncoderParams p, const AudioData * d, const std:
toBytes(int(samplesSizeInBytes), chunkSizeBuff);
fout.write(chunkSizeBuff, 4);
// Apply dithering
if (sourceBits != targetBits && p.dither != DITHER_NONE)
if (targetBits <= sourceBits && p.targetFormat != PCM_FLT)
{
// At most need this number of bytes in our copy
std::vector<uint8_t> samplesCopy(samplesSizeInBytes);
ConvertFromFloat32(samplesCopy.data(), d->samples.data(), d->samples.size(), p.targetFormat, p.dither);
fout.write(reinterpret_cast<const char*>(samplesCopy.data()), samplesSizeInBytes);
}
else
{
// Handle PCM_FLT. Coming in from AudioData ensures we start with 32 bits, so we're fine
// since we've also rejected formats with more than 32 bits above.
fout.write(reinterpret_cast<const char*>(d->samples.data()), samplesSizeInBytes);
}

Loading…
Cancel
Save