You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
295 lines
8.3 KiB
C++
295 lines
8.3 KiB
C++
/*
|
|
Copyright (c) 2015, Dimitri Diakopoulos All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef LIBNYQUIST_COMMON_H
|
|
#define LIBNYQUIST_COMMON_H
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <stdint.h>
|
|
#include <cassert>
|
|
#include <type_traits>
|
|
#include <numeric>
|
|
#include <array>
|
|
|
|
#include "PostProcess.h"
|
|
#include "Dither.h"
|
|
|
|
namespace nqr
|
|
{
|
|
|
|
/////////////////
|
|
// Util Macros //
|
|
/////////////////
|
|
|
|
#define F_ROUND(x) ((x) > 0 ? (x) + 0.5f : (x) - 0.5f)
|
|
#define D_ROUND(x) ((x) > 0 ? (x) + 0.5 : (x) - 0.5)
|
|
|
|
#define NO_COPY(C) C(const C &) = delete; C & operator = (const C &) = delete
|
|
#define NO_MOVE(C) NO_COPY(C); C(C &&) = delete; C & operator = (const C &&) = delete
|
|
|
|
///////////////////////
|
|
// Endian Operations //
|
|
///////////////////////
|
|
|
|
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
|
|
#define CPU_X86 1
|
|
#endif
|
|
|
|
#if defined(__arm__) || defined(_M_ARM)
|
|
#define CPU_ARM 1
|
|
#endif
|
|
|
|
#if defined(CPU_X86) && defined(CPU_ARM)
|
|
#error CPU_X86 and CPU_ARM both defined.
|
|
#endif
|
|
|
|
#if !defined(ARCH_CPU_BIG_ENDIAN) && !defined(ARCH_CPU_LITTLE_ENDIAN)
|
|
#if CPU_X86 || CPU_ARM || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
|
#define ARCH_CPU_LITTLE_ENDIAN
|
|
#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
|
#define ARCH_CPU_BIG_ENDIAN
|
|
#else
|
|
#error ARCH_CPU_BIG_ENDIAN or ARCH_CPU_LITTLE_ENDIAN should be defined.
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(ARCH_CPU_BIG_ENDIAN) && defined(ARCH_CPU_LITTLE_ENDIAN)
|
|
#error ARCH_CPU_BIG_ENDIAN and ARCH_CPU_LITTLE_ENDIAN both defined.
|
|
#endif
|
|
|
|
static inline uint16_t Swap16(uint16_t value)
|
|
{
|
|
return (uint16_t)((value >> 8) | (value << 8));
|
|
}
|
|
|
|
static inline uint32_t Swap24(uint32_t value)
|
|
{
|
|
return (((value & 0x00ff0000) >> 16) |
|
|
((value & 0x0000ff00)) |
|
|
((value & 0x000000ff) << 16)) & 0x00FFFFFF;
|
|
}
|
|
|
|
static inline uint32_t Swap32(uint32_t value)
|
|
{
|
|
return (((value & 0x000000ff) << 24) |
|
|
((value & 0x0000ff00) << 8) |
|
|
((value & 0x00ff0000) >> 8) |
|
|
((value & 0xff000000) >> 24));
|
|
}
|
|
|
|
static inline uint64_t Swap64(uint64_t value)
|
|
{
|
|
return (((value & 0x00000000000000ffLL) << 56) |
|
|
((value & 0x000000000000ff00LL) << 40) |
|
|
((value & 0x0000000000ff0000LL) << 24) |
|
|
((value & 0x00000000ff000000LL) << 8) |
|
|
((value & 0x000000ff00000000LL) >> 8) |
|
|
((value & 0x0000ff0000000000LL) >> 24) |
|
|
((value & 0x00ff000000000000LL) >> 40) |
|
|
((value & 0xff00000000000000LL) >> 56));
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool isOdd(const T x)
|
|
{
|
|
return (x & 0x1);
|
|
}
|
|
|
|
#ifdef ARCH_CPU_LITTLE_ENDIAN
|
|
#define Read16(n) (n)
|
|
#define Read24(n) (n)
|
|
#define Read32(n) (n)
|
|
#define Read64(n) (n)
|
|
#define Write16(n) (n)
|
|
#define Write24(n) (n)
|
|
#define Write32(n) (n)
|
|
#define Write64(n) (n)
|
|
#else
|
|
#define Read16(n) Swap16(n)
|
|
#define Read24(n) Swap24(n)
|
|
#define Read32(n) Swap32(n)
|
|
#define Read64(n) Swap64(n)
|
|
#define Write16(n) Swap16(n)
|
|
#define Write24(n) Swap24(n)
|
|
#define Write32(n) Swap32(n)
|
|
#define Write64(n) Swap64(n)
|
|
#endif
|
|
|
|
inline uint64_t Pack(uint32_t a, uint32_t b)
|
|
{
|
|
uint64_t tmp = (uint64_t) b << 32 | (uint64_t) a;
|
|
#ifdef ARCH_CPU_LITTLE_ENDIAN
|
|
return tmp;
|
|
#else
|
|
return Swap64(tmp);
|
|
#endif
|
|
}
|
|
|
|
inline uint32_t Pack(uint16_t a, uint16_t b)
|
|
{
|
|
uint32_t tmp = (uint32_t) b << 16 | (uint32_t) a;
|
|
#ifdef ARCH_CPU_LITTLE_ENDIAN
|
|
return tmp;
|
|
#else
|
|
return Swap32(tmp);
|
|
#endif
|
|
}
|
|
|
|
inline uint16_t Pack(uint8_t a, uint8_t b)
|
|
{
|
|
uint16_t tmp = (uint16_t) b << 8 | (uint16_t) a;
|
|
#ifdef ARCH_CPU_LITTLE_ENDIAN
|
|
return tmp;
|
|
#else
|
|
return Swap16(tmp);
|
|
#endif
|
|
}
|
|
|
|
// http://www.dsprelated.com/showthread/comp.dsp/136689-1.php
|
|
inline int32_t Pack(uint8_t a, uint8_t b, uint8_t c)
|
|
{
|
|
// uint32_t tmp = ((c & 0x80) ? (0xFF << 24) : 0x00 << 24) | (c << 16) | (b << 8) | (a << 0); // alternate method
|
|
int32_t x = (c << 16) | (b << 8) | (a << 0);
|
|
auto sign_extended = (x) | (!!((x) & 0x800000) * 0xff000000);
|
|
#ifdef ARCH_CPU_LITTLE_ENDIAN
|
|
return sign_extended;
|
|
#else
|
|
Swap32(sign_extended);
|
|
#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 //
|
|
//////////////////////////
|
|
|
|
// Signed maxes, defined for readabilty/convenience
|
|
#define NQR_INT16_MAX 32767.f
|
|
#define NQR_INT24_MAX 8388608.f
|
|
#define NQR_INT32_MAX 2147483648.f
|
|
|
|
static const float NQR_BYTE_2_FLT = 1.0f / 127.0f;
|
|
|
|
#define int8_to_float32(s) ((float) (s) * NQR_BYTE_2_FLT)
|
|
#define uint8_to_float32(s)(((float) (s) - 128) * NQR_BYTE_2_FLT)
|
|
#define int16_to_float32(s) ((float) (s) / NQR_INT16_MAX)
|
|
#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) (float) (s * 127.f)
|
|
#define float32_to_uint8(s) (float) ((s * 127.f) + 128)
|
|
#define float32_to_int16(s) (float) (s * NQR_INT16_MAX)
|
|
#define float32_to_int24(s) (float) (s * NQR_INT24_MAX)
|
|
#define float32_to_int32(s) (float) (s * NQR_INT32_MAX)
|
|
|
|
//@todo add 12, 20 for flac
|
|
enum PCMFormat
|
|
{
|
|
PCM_U8,
|
|
PCM_S8,
|
|
PCM_16,
|
|
PCM_24,
|
|
PCM_32,
|
|
PCM_64,
|
|
PCM_FLT,
|
|
PCM_DBL,
|
|
PCM_END
|
|
};
|
|
|
|
template<class T> T clamp(T a, T mn, T mx) { return std::max(std::min(a, mx), mn); }
|
|
|
|
// Src data is aligned to PCMFormat
|
|
// @todo normalize?
|
|
void ConvertToFloat32(float * dst, const uint8_t * src, const size_t N, PCMFormat f);
|
|
|
|
// Src data is always aligned to 4 bytes (WavPack, primarily)
|
|
void ConvertToFloat32(float * dst, const int32_t * src, const size_t N, PCMFormat f);
|
|
|
|
// Src data is always aligned to 2 bytes (IMA ADPCM, primarily)
|
|
void ConvertToFloat32(float * dst, const int16_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 //
|
|
//////////////////////////
|
|
|
|
struct AudioData
|
|
{
|
|
int channelCount;
|
|
int sampleRate;
|
|
double lengthSeconds;
|
|
size_t frameSize; // channels * bits per sample
|
|
std::vector<float> samples;
|
|
PCMFormat sourceFormat;
|
|
|
|
//@todo: add field: channel layout
|
|
//@todo: add field: lossy / lossless
|
|
//@todo: audio data loaded (for metadata only)
|
|
//@todo: bitrate (if applicable)
|
|
//@todo: original sample rate (if applicable)
|
|
};
|
|
|
|
struct StreamableAudioData : public AudioData
|
|
{
|
|
//@todo: add field: is this format streamable?
|
|
//@todo: hold file handle
|
|
};
|
|
|
|
struct NyquistFileBuffer
|
|
{
|
|
std::vector<uint8_t> buffer;
|
|
size_t size;
|
|
};
|
|
|
|
NyquistFileBuffer ReadFile(std::string pathToFile);
|
|
|
|
int GetFormatBitsPerSample(PCMFormat f);
|
|
PCMFormat MakeFormatForBits(int bits, bool floatingPt, bool isSigned);
|
|
|
|
} // end namespace nqr
|
|
|
|
#endif
|