project include files from ~60 messier squashed commits
parent
ca58cd0531
commit
c65a33fe6a
@ -0,0 +1,73 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef AUDIO_DECODER_H
|
||||
#define AUDIO_DECODER_H
|
||||
|
||||
#include "Common.h"
|
||||
#include <utility>
|
||||
#include <map>
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
// Individual decoder classes will throw std::exceptions for bad things,
|
||||
// but NyquistIO implements this enum for high-level error notifications.
|
||||
enum IOError
|
||||
{
|
||||
NoError,
|
||||
NoDecodersLoaded,
|
||||
ExtensionNotSupported,
|
||||
LoadPathNotImplemented,
|
||||
LoadBufferNotImplemented,
|
||||
UnknownError
|
||||
};
|
||||
|
||||
struct BaseDecoder
|
||||
{
|
||||
virtual int LoadFromPath(nqr::AudioData * data, const std::string & path) = 0;
|
||||
virtual int LoadFromBuffer(nqr::AudioData * data, const std::vector<uint8_t> & memory) = 0;
|
||||
virtual std::vector<std::string> GetSupportedFileExtensions() = 0;
|
||||
//@todo implement streaming helper methods here
|
||||
};
|
||||
|
||||
typedef std::pair<std::string, std::shared_ptr<nqr::BaseDecoder>> DecoderPair;
|
||||
|
||||
class NyquistIO
|
||||
{
|
||||
public:
|
||||
|
||||
NyquistIO();
|
||||
~NyquistIO();
|
||||
|
||||
int Load(AudioData * data, const std::string & path);
|
||||
|
||||
bool IsFileSupported(const std::string path) const;
|
||||
|
||||
private:
|
||||
|
||||
std::string ParsePathForExtension(const std::string & path) const;
|
||||
|
||||
std::shared_ptr<nqr::BaseDecoder> GetDecoderForExtension(const std::string ext);
|
||||
|
||||
void BuildDecoderTable();
|
||||
|
||||
template<typename T>
|
||||
void AddDecoderToTable(std::shared_ptr<T> decoder)
|
||||
{
|
||||
auto supportedExtensions = decoder->GetSupportedFileExtensions();
|
||||
|
||||
//@todo: basic sanity checking that the extension isn't already supported
|
||||
for (const auto ext : supportedExtensions)
|
||||
{
|
||||
decoderTable.insert(DecoderPair(ext, decoder));
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, std::shared_ptr<BaseDecoder>> decoderTable;
|
||||
|
||||
NO_MOVE(NyquistIO);
|
||||
};
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,72 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef AUDIO_DEVICE_H
|
||||
#define AUDIO_DEVICE_H
|
||||
|
||||
// This file implements a simple sound file player based on RtAudio for testing / example purposes.
|
||||
|
||||
#include "Common.h"
|
||||
#include "RingBuffer.h"
|
||||
|
||||
#include "rtaudio/RtAudio.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
const unsigned int FRAME_SIZE = 512;
|
||||
const int CHANNELS = 2;
|
||||
const int BUFFER_LENGTH = FRAME_SIZE * CHANNELS;
|
||||
|
||||
struct DeviceInfo
|
||||
{
|
||||
int id;
|
||||
int numChannels;
|
||||
int sampleRate;
|
||||
unsigned int frameSize;
|
||||
bool isPlaying = false;
|
||||
};
|
||||
|
||||
class AudioDevice
|
||||
{
|
||||
|
||||
NO_MOVE(AudioDevice);
|
||||
|
||||
std::unique_ptr<RtAudio> rtaudio;
|
||||
|
||||
public:
|
||||
|
||||
DeviceInfo info;
|
||||
|
||||
static void ListAudioDevices();
|
||||
|
||||
AudioDevice(int numChannels, int sampleRate, int deviceId = -1)
|
||||
{
|
||||
rtaudio = std::unique_ptr<RtAudio>(new RtAudio);
|
||||
info.id = deviceId != -1 ? deviceId : rtaudio->getDefaultOutputDevice();
|
||||
info.numChannels = numChannels;
|
||||
info.sampleRate = sampleRate;
|
||||
info.frameSize = FRAME_SIZE;
|
||||
}
|
||||
|
||||
~AudioDevice()
|
||||
{
|
||||
if (rtaudio)
|
||||
{
|
||||
rtaudio->stopStream();
|
||||
if (rtaudio->isStreamOpen())
|
||||
rtaudio->closeStream();
|
||||
}
|
||||
}
|
||||
|
||||
bool Open(const int deviceId);
|
||||
|
||||
bool Play(const std::vector<float> & data);
|
||||
|
||||
};
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,22 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef CAF_DECODER_H
|
||||
#define CAF_DECODER_H
|
||||
|
||||
#include "AudioDecoder.h"
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
struct CAFDecoder : public nqr::BaseDecoder
|
||||
{
|
||||
CAFDecoder() {};
|
||||
virtual ~CAFDecoder() {};
|
||||
virtual int LoadFromPath(nqr::AudioData * data, const std::string & path) override;
|
||||
virtual int LoadFromBuffer(nqr::AudioData * data, const std::vector<uint8_t> & memory) override;
|
||||
virtual std::vector<std::string> GetSupportedFileExtensions() override;
|
||||
};
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,330 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef LIBNYQUIST_COMMON_H
|
||||
#define LIBNYQUIST_COMMON_H
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
#include <numeric>
|
||||
#include "PostProcess.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));
|
||||
}
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
// Conversion Utilities //
|
||||
//////////////////////////
|
||||
|
||||
// Signed maxes, defined for readabilty/convenience
|
||||
#define NQR_INT16_MAX 32768.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)
|
||||
|
||||
//@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
|
||||
};
|
||||
|
||||
// Src data is aligned to PCMFormat
|
||||
// @todo normalize?
|
||||
inline void ConvertToFloat32(float * dst, const uint8_t * src, const size_t N, PCMFormat f)
|
||||
{
|
||||
assert(f != PCM_END);
|
||||
|
||||
if (f == PCM_U8)
|
||||
{
|
||||
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(src);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
dst[i] = uint8_to_float32(dataPtr[i]);
|
||||
}
|
||||
else if (f == PCM_S8)
|
||||
{
|
||||
const int8_t * dataPtr = reinterpret_cast<const int8_t *>(src);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
dst[i] = int8_to_float32(dataPtr[i]);
|
||||
}
|
||||
else if (f == PCM_16)
|
||||
{
|
||||
const int16_t * dataPtr = reinterpret_cast<const int16_t *>(src);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
dst[i] = int16_to_float32(Read16(dataPtr[i]));
|
||||
}
|
||||
else if (f == PCM_24)
|
||||
{
|
||||
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(src);
|
||||
size_t c = 0;
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
int32_t sample = Pack(dataPtr[c], dataPtr[c+1], dataPtr[c+2]);
|
||||
dst[i] = int24_to_float32(sample); // Packed types don't need addtional endian helpers
|
||||
c += 3;
|
||||
}
|
||||
}
|
||||
else if (f == PCM_32)
|
||||
{
|
||||
const int32_t * dataPtr = reinterpret_cast<const int32_t *>(src);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
dst[i] = int32_to_float32(Read32(dataPtr[i]));
|
||||
}
|
||||
|
||||
//@todo add int64 format
|
||||
|
||||
else if (f == PCM_FLT)
|
||||
{
|
||||
const float * dataPtr = reinterpret_cast<const float *>(src);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
dst[i] = (float) Read32(dataPtr[i]);
|
||||
}
|
||||
else if (f == PCM_DBL)
|
||||
{
|
||||
const double * dataPtr = reinterpret_cast<const double *>(src);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
dst[i] = (float) Read64(dataPtr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Src data is always aligned to 4 bytes (WavPack, primarily)
|
||||
inline void ConvertToFloat32(float * dst, const int32_t * src, const size_t N, PCMFormat f)
|
||||
{
|
||||
assert(f != PCM_END);
|
||||
|
||||
if (f == PCM_16)
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
dst[i] = int16_to_float32(Read32(src[i]));
|
||||
}
|
||||
else if (f == PCM_24)
|
||||
{
|
||||
const uint8_t * dataPtr = reinterpret_cast<const uint8_t *>(src);
|
||||
size_t c = 0;
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
int32_t sample = Pack(dataPtr[c], dataPtr[c+1], dataPtr[c+2]);
|
||||
dst[i] = int24_to_float32(sample);
|
||||
c += 4; // +4 for next 4 byte boundary
|
||||
}
|
||||
}
|
||||
else if (f == PCM_32)
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
dst[i] = int32_to_float32(Read32(src[i]));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// Chunk utilities (move somewhere else) //
|
||||
///////////////////////////////////////////
|
||||
|
||||
struct ChunkHeaderInfo
|
||||
{
|
||||
uint32_t offset; // Byte offset into chunk
|
||||
uint32_t size; // Size of the chunk in bytes
|
||||
};
|
||||
|
||||
inline uint32_t GenerateChunkCode(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
|
||||
{
|
||||
#ifdef ARCH_CPU_LITTLE_ENDIAN
|
||||
return ((uint32_t) ((a) | ((b) << 8) | ((c) << 16) | (((uint32_t) (d)) << 24)));
|
||||
#else
|
||||
return ((uint32_t) ((((uint32_t) (a)) << 24) | ((b) << 16) | ((c) << 8) | (d)));
|
||||
#endif
|
||||
}
|
||||
|
||||
ChunkHeaderInfo ScanForChunk(const std::vector<uint8_t> & fileData, uint32_t chunkMarker);
|
||||
|
||||
//////////////////////////
|
||||
// User Data + File Ops //
|
||||
//////////////////////////
|
||||
|
||||
struct AudioData
|
||||
{
|
||||
int channelCount;
|
||||
int sampleRate;
|
||||
int bitDepth;
|
||||
double lengthSeconds;
|
||||
size_t frameSize; // channels * bits per sample
|
||||
std::vector<float> samples;
|
||||
//@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);
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,15 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef DITHER_OPERATIONS_H
|
||||
#define DITHER_OPERATIONS_H
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
//@todo libnyquist will, at some point, allow conversion between arbitrary bit-depths
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,39 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef FLAC_DECODER_H
|
||||
#define FLAC_DECODER_H
|
||||
|
||||
#include "AudioDecoder.h"
|
||||
#include <map>
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
//@todo expose this in API
|
||||
inline std::map<int, std::string> GetQualityTable()
|
||||
{
|
||||
return {
|
||||
{ 0, "0 (Fastest)" },
|
||||
{ 1, "1" },
|
||||
{ 2, "2" },
|
||||
{ 3, "3" },
|
||||
{ 4, "4" },
|
||||
{ 5, "5 (Default)" },
|
||||
{ 6, "6" },
|
||||
{ 7, "7" },
|
||||
{ 8, "8 (Highest)" },
|
||||
};
|
||||
}
|
||||
|
||||
struct FlacDecoder : public nqr::BaseDecoder
|
||||
{
|
||||
FlacDecoder() {}
|
||||
virtual ~FlacDecoder() {}
|
||||
virtual int LoadFromPath(nqr::AudioData * data, const std::string & path) override;
|
||||
virtual int LoadFromBuffer(nqr::AudioData * data, const std::vector<uint8_t> & memory) override;
|
||||
virtual std::vector<std::string> GetSupportedFileExtensions() override;
|
||||
};
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,257 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
/*
|
||||
|
||||
AudioSampleBuffer can be backed by a memory store such as standard vector,
|
||||
|
||||
a file, such as NyquistFileBuffer, or some platform specific DMA backed
|
||||
|
||||
thingamajig, or a memmaped file.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
template <typename Buffer>
|
||||
|
||||
struct AudioSampleBuffer
|
||||
|
||||
{
|
||||
|
||||
// RAII object to temporarily map a data buffer into R/O memory
|
||||
|
||||
struct Data
|
||||
|
||||
{
|
||||
|
||||
Data(AudioSampleBuffer&); // map data into CPU memory
|
||||
|
||||
~Data(); // unmap data
|
||||
|
||||
|
||||
|
||||
void *data();
|
||||
|
||||
size_t size();
|
||||
|
||||
};
|
||||
|
||||
|
||||
// RAII object to temporarily map a data buffer into R/W memory
|
||||
|
||||
struct MutableData
|
||||
|
||||
{
|
||||
|
||||
MutableData(AudioSampleBuffer&); // map data into CPU memory
|
||||
|
||||
~MutableData(); // unmap data after copying contents back to backing store
|
||||
|
||||
|
||||
|
||||
void *data();
|
||||
|
||||
size_t size();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
AudioSampleBuffer(std::function<size_t(size_t, Buffer)> resize,
|
||||
|
||||
std::function<void()> dealloc)
|
||||
|
||||
: resize(resize), dealloc(dealloc)
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
~AudioSampleBuffer()
|
||||
|
||||
{
|
||||
|
||||
if (dealloc)
|
||||
|
||||
dealloc();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_t resize(size_t sz)
|
||||
|
||||
{
|
||||
|
||||
if (resize)
|
||||
|
||||
return resize(sz, &buffer);
|
||||
|
||||
|
||||
return buffer.size();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
AudioSampleBuffer() = 0;
|
||||
|
||||
|
||||
|
||||
Buffer buffer;
|
||||
|
||||
std::function<size_t(size_t, Buffer*)> _resize;
|
||||
|
||||
std::function<void()> _dealloc;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct AudioData
|
||||
|
||||
{
|
||||
|
||||
AudioData() : channelCount(0), sampleRate(0), bitDepth(0), lengthSeconds(0), frameSize(0) { }
|
||||
|
||||
|
||||
|
||||
AudioData(const AudioData& rhs)
|
||||
|
||||
: channelCount(rhs.channelCount), sampleRate(rhs.sampleRate), bitDepth(rhs.bitDepth)
|
||||
|
||||
, lengthInSeconds(rhs.lengthInSeconds), frameSize(rhs.frameSize), samples(rhs.samples) { }
|
||||
|
||||
|
||||
|
||||
int channelCount; // unsigned?
|
||||
|
||||
int sampleRate; // ditto?
|
||||
|
||||
int bitDepth; // ditto?
|
||||
|
||||
double lengthSeconds;
|
||||
|
||||
|
||||
|
||||
// since it does make sense to set this (does it?) it should probably be a function not a data value
|
||||
|
||||
size_t frameSize; // channels * bits per sample
|
||||
|
||||
|
||||
AudioSampleBuffer samples;
|
||||
|
||||
//@todo: add field: streamable
|
||||
|
||||
//@todo: add field: channel layout
|
||||
|
||||
//@todo: add field: lossy / lossless
|
||||
|
||||
|
||||
|
||||
void mixToChannels(int channelCount);
|
||||
|
||||
void resample(int sampleRate);
|
||||
|
||||
void setBitDepth(int bitDepth);
|
||||
|
||||
void setLength(double lengthInSeconds);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// in general nice to pass string ref
|
||||
|
||||
|
||||
|
||||
void LoadFile(AudioData * dataResult, const std::string & path)
|
||||
|
||||
{
|
||||
|
||||
string extension = path.substr(path.rfind('.') + 1, path.length());
|
||||
|
||||
if (!extension.length())
|
||||
|
||||
return;
|
||||
|
||||
|
||||
|
||||
if (extension == "ogg")
|
||||
|
||||
{
|
||||
|
||||
VorbisDecoder::LoadFile(dataResult, path);
|
||||
|
||||
}
|
||||
|
||||
else if (extension == "wav")
|
||||
|
||||
{
|
||||
|
||||
VorbisDecoder::LoadFile(dataResult, path);
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
// etc.
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void LoadFile(AudioData * dataResult, const std::string & path, bool conformToData)
|
||||
|
||||
{
|
||||
|
||||
if (!conformToData)
|
||||
|
||||
{
|
||||
|
||||
LoadFile(dataResult, path);
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
|
||||
{
|
||||
|
||||
AudioData * conform = new dataResult(dataResult); // add copy constructor
|
||||
|
||||
LoadFile(dataResult, path);
|
||||
|
||||
|
||||
if (conform->channelCount && (conform->channelCount != dataResult->channelCount))
|
||||
|
||||
dataResult->mixToChannels(conform->channelCount);
|
||||
|
||||
|
||||
|
||||
if (conform->sampleRate && (conform->sampleRate != dataResult->sampleRate))
|
||||
|
||||
dataResult->resample(conform->sampleRate);
|
||||
|
||||
|
||||
|
||||
if (conform->bitDepth && (conform->bitDepth != dataResult->bitDepth))
|
||||
|
||||
dataResult->setBitDepth(conform->bitDepth);
|
||||
|
||||
|
||||
|
||||
if (conform->lengthInSeconds > 0 && (conform->lengthSeconds != dataResult->lengthSeconds))
|
||||
|
||||
dataResult->setLength(conform->lengthSeconds);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef OPUS_DECODER_H
|
||||
#define OPUS_DECODER_H
|
||||
|
||||
#include "AudioDecoder.h"
|
||||
#include "opus/opusfile/include/opusfile.h"
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
// Opus is a general-purpose codec designed to replace Vorbis at some point. Primarily, it's a low
|
||||
// delay format making it suitable for high-quality, real time streaming. It's not really
|
||||
// an archival format or something designed for heavy DSP post-processing since
|
||||
// it's fundamentally limited to encode/decode at 48khz.
|
||||
// https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/index.html
|
||||
struct OpusDecoder : public nqr::BaseDecoder
|
||||
{
|
||||
OpusDecoder() {}
|
||||
virtual ~OpusDecoder() {}
|
||||
virtual int LoadFromPath(nqr::AudioData * data, const std::string & path) override;
|
||||
virtual int LoadFromBuffer(nqr::AudioData * data, const std::vector<uint8_t> & memory) override;
|
||||
virtual std::vector<std::string> GetSupportedFileExtensions() override;
|
||||
};
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,62 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef POSTPROCESS_H
|
||||
#define POSTPROCESS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
inline void DeinterleaveStereo(T * c1, T * c2, T const * src, size_t count)
|
||||
{
|
||||
auto src_end = src + count;
|
||||
while (src != src_end)
|
||||
{
|
||||
*c1 = src[0];
|
||||
*c2 = src[1];
|
||||
c1++;
|
||||
c2++;
|
||||
src += 2;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void InterleaveArbitrary(const T * src, T * dest, size_t numFramesPerChannel, size_t numChannels, size_t numCopyFrames)
|
||||
{
|
||||
for (size_t ch = 0; ch < numChannels; ch++)
|
||||
{
|
||||
size_t x = ch;
|
||||
const T * srcChannel = &src[ch * numFramesPerChannel];
|
||||
for(size_t i = 0; i < numCopyFrames; i++)
|
||||
{
|
||||
dest[x] = srcChannel[i];
|
||||
x += numChannels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void DeinterleaveArbitrary(const T * src, T * dest, size_t numFramesPerChannel, size_t numChannels, size_t numCopyFrames)
|
||||
{
|
||||
for(size_t ch = 0; ch < numChannels; ch++)
|
||||
{
|
||||
size_t x = ch;
|
||||
T *destChannel = &dest[ch * numFramesPerChannel];
|
||||
for (size_t i = 0; i < numCopyFrames; i++)
|
||||
{
|
||||
destChannel[i] = (T) src[x];
|
||||
x += numChannels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void TrimSilenceInterleaved(std::vector<float> & buffer, float v, bool fromFront, bool fromEnd)
|
||||
{
|
||||
//@todo implement me
|
||||
}
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright (c) 2014, The Cinder Project
|
||||
|
||||
This code is intended to be used with the Cinder C++ library, http://libcinder.org
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
//! \brief Ringbuffer (aka circular buffer) data structure for use in concurrent audio scenarios.
|
||||
//!
|
||||
//! Other than minor modifications, this ringbuffer is a copy of Tim Blechmann's fine work, found as the base
|
||||
//! structure of boost::lockfree::spsc_queue (ringbuffer_base). Whereas the boost::lockfree data structures
|
||||
//! are meant for a wide range of applications / archs, this version specifically caters to audio processing.
|
||||
//!
|
||||
//! The implementation remains lock-free and thread-safe within a single write thread / single read thread context.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <assert.h>
|
||||
|
||||
template <typename T>
|
||||
class RingBufferT {
|
||||
public:
|
||||
//! Constructs a RingBufferT with size = 0
|
||||
RingBufferT() : mData( nullptr ), mAllocatedSize( 0 ), mWriteIndex( 0 ), mReadIndex( 0 ) {}
|
||||
//! Constructs a RingBufferT with \a count maximum elements.
|
||||
RingBufferT( size_t count ) : mAllocatedSize( 0 )
|
||||
{
|
||||
resize( count );
|
||||
}
|
||||
|
||||
RingBufferT( RingBufferT &&other )
|
||||
: mData( other.mData ), mAllocatedSize( other.mAllocatedSize ), mWriteIndex( 0 ), mReadIndex( 0 )
|
||||
{
|
||||
other.mData = nullptr;
|
||||
other.mAllocatedSize = 0;
|
||||
}
|
||||
|
||||
~RingBufferT()
|
||||
{
|
||||
if( mData )
|
||||
free( mData );
|
||||
}
|
||||
//! Resizes the container to contain \a count maximum elements. Invalidates the internal buffer and resets read / write indices to 0. \note Must be synchronized with both read and write threads.
|
||||
void resize( size_t count )
|
||||
{
|
||||
size_t allocatedSize = count + 1; // one bin is used to distinguish between the read and write indices when full.
|
||||
|
||||
if( mAllocatedSize )
|
||||
mData = (T *)::realloc( mData, allocatedSize * sizeof( T ) );
|
||||
else
|
||||
mData = (T *)::calloc( allocatedSize, sizeof( T ) );
|
||||
|
||||
assert( mData );
|
||||
|
||||
mAllocatedSize = allocatedSize;
|
||||
clear();
|
||||
}
|
||||
//! Invalidates the internal buffer and resets read / write indices to 0. \note Must be synchronized with both read and write threads.
|
||||
void clear()
|
||||
{
|
||||
mWriteIndex = 0;
|
||||
mReadIndex = 0;
|
||||
}
|
||||
//! Returns the maximum number of elements.
|
||||
size_t getSize() const
|
||||
{
|
||||
return mAllocatedSize - 1;
|
||||
}
|
||||
//! Returns the number of elements available for wrtiing. \note Only safe to call from the write thread.
|
||||
size_t getAvailableWrite() const
|
||||
{
|
||||
return getAvailableWrite( mWriteIndex, mReadIndex );
|
||||
}
|
||||
//! Returns the number of elements available for wrtiing. \note Only safe to call from the read thread.
|
||||
size_t getAvailableRead() const
|
||||
{
|
||||
return getAvailableRead( mWriteIndex, mReadIndex );
|
||||
}
|
||||
|
||||
//! \brief Writes \a count elements into the internal buffer from \a array. \return `true` if all elements were successfully written, or `false` otherwise.
|
||||
//!
|
||||
//! \note only safe to call from the write thread.
|
||||
//! TODO: consider renaming this to writeAll / readAll, and having generic read / write that just does as much as it can
|
||||
bool write( const T *array, size_t count )
|
||||
{
|
||||
const size_t writeIndex = mWriteIndex.load( std::memory_order_relaxed );
|
||||
const size_t readIndex = mReadIndex.load( std::memory_order_acquire );
|
||||
|
||||
if( count > getAvailableWrite( writeIndex, readIndex ) )
|
||||
return false;
|
||||
|
||||
size_t writeIndexAfter = writeIndex + count;
|
||||
|
||||
if( writeIndex + count > mAllocatedSize ) {
|
||||
size_t countA = mAllocatedSize - writeIndex;
|
||||
size_t countB = count - countA;
|
||||
|
||||
std::memcpy( mData + writeIndex, array, countA * sizeof( T ) );
|
||||
std::memcpy( mData, array + countA, countB * sizeof( T ) );
|
||||
writeIndexAfter -= mAllocatedSize;
|
||||
}
|
||||
else {
|
||||
std::memcpy( mData + writeIndex, array, count * sizeof( T ) );
|
||||
if( writeIndexAfter == mAllocatedSize )
|
||||
writeIndexAfter = 0;
|
||||
}
|
||||
|
||||
mWriteIndex.store( writeIndexAfter, std::memory_order_release );
|
||||
return true;
|
||||
}
|
||||
//! \brief Reads \a count elements from the internal buffer into \a array. \return `true` if all elements were successfully read, or `false` otherwise.
|
||||
//!
|
||||
//! \note only safe to call from the read thread.
|
||||
bool read( T *array, size_t count )
|
||||
{
|
||||
const size_t writeIndex = mWriteIndex.load( std::memory_order_acquire );
|
||||
const size_t readIndex = mReadIndex.load( std::memory_order_relaxed );
|
||||
|
||||
if( count > getAvailableRead( writeIndex, readIndex ) )
|
||||
return false;
|
||||
|
||||
size_t readIndexAfter = readIndex + count;
|
||||
|
||||
if( readIndex + count > mAllocatedSize ) {
|
||||
size_t countA = mAllocatedSize - readIndex;
|
||||
size_t countB = count - countA;
|
||||
|
||||
std::memcpy( array, mData + readIndex, countA * sizeof( T ) );
|
||||
std::memcpy( array + countA, mData, countB * sizeof( T ) );
|
||||
|
||||
readIndexAfter -= mAllocatedSize;
|
||||
}
|
||||
else {
|
||||
std::memcpy( array, mData + readIndex, count * sizeof( T ) );
|
||||
if( readIndexAfter == mAllocatedSize )
|
||||
readIndexAfter = 0;
|
||||
}
|
||||
|
||||
mReadIndex.store( readIndexAfter, std::memory_order_release );
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
size_t getAvailableWrite( size_t writeIndex, size_t readIndex ) const
|
||||
{
|
||||
size_t result = readIndex - writeIndex - 1;
|
||||
if( writeIndex >= readIndex )
|
||||
result += mAllocatedSize;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t getAvailableRead( size_t writeIndex, size_t readIndex ) const
|
||||
{
|
||||
if( writeIndex >= readIndex )
|
||||
return writeIndex - readIndex;
|
||||
|
||||
return writeIndex + mAllocatedSize - readIndex;
|
||||
}
|
||||
|
||||
T *mData;
|
||||
size_t mAllocatedSize;
|
||||
std::atomic<size_t> mWriteIndex, mReadIndex;
|
||||
};
|
||||
|
||||
typedef RingBufferT<float> RingBuffer;
|
||||
@ -0,0 +1,22 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef VORBIS_DECODER_H
|
||||
#define VORBIS_DECODER_H
|
||||
|
||||
#include "AudioDecoder.h"
|
||||
#include "libvorbis/include/vorbis/vorbisfile.h"
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
struct VorbisDecoder : public nqr::BaseDecoder
|
||||
{
|
||||
VorbisDecoder() {}
|
||||
virtual ~VorbisDecoder() {}
|
||||
virtual int LoadFromPath(nqr::AudioData * data, const std::string & path) override;
|
||||
virtual int LoadFromBuffer(nqr::AudioData * data, const std::vector<uint8_t> & memory) override;
|
||||
virtual std::vector<std::string> GetSupportedFileExtensions() override;
|
||||
};
|
||||
|
||||
} // end namespace nqr
|
||||
#endif
|
||||
@ -0,0 +1,164 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef WAVE_DECODER_H
|
||||
#define WAVE_DECODER_H
|
||||
|
||||
#include "AudioDecoder.h"
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
enum WaveFormatCode
|
||||
{
|
||||
FORMAT_UNKNOWN = 0x0, // Unknown Wave Format
|
||||
FORMAT_PCM = 0x1, // PCM Format
|
||||
FORMAT_IEEE = 0x3, // IEEE float/double
|
||||
FORMAT_ALAW = 0x6, // 8-bit ITU-T G.711 A-law
|
||||
FORMAT_MULAW = 0x7, // 8-bit ITU-T G.711 µ-law
|
||||
FORMAT_EXT = 0xFFFE // Set via subformat
|
||||
};
|
||||
|
||||
struct RiffChunkHeader
|
||||
{
|
||||
uint32_t id_riff; // Chunk ID: 'RIFF'
|
||||
uint32_t file_size; // Entire file in bytes
|
||||
uint32_t id_wave; // Chunk ID: 'WAVE'
|
||||
};
|
||||
|
||||
struct WaveChunkHeader
|
||||
{
|
||||
uint32_t fmt_id; // Chunk ID: 'fmt '
|
||||
uint32_t chunk_size; // Size in bytes
|
||||
uint16_t format; // Format code
|
||||
uint16_t channel_count; // Num interleaved channels
|
||||
uint32_t sample_rate; // SR
|
||||
uint32_t data_rate; // Data rate
|
||||
uint16_t frame_size; // 1 frame = channels * bits per sample
|
||||
uint16_t bit_depth; // Bits per sample
|
||||
};
|
||||
|
||||
struct BextChunk
|
||||
{
|
||||
uint32_t fmt_id; // Chunk ID: 'bext'
|
||||
uint32_t chunk_size; // Size in bytes
|
||||
uint8_t description[256]; // Description of the sound (ascii)
|
||||
uint8_t origin[32]; // Name of the originator (ascii)
|
||||
uint8_t origin_ref[32]; // Reference of the originator (ascii)
|
||||
uint8_t orgin_date[10]; // yyyy-mm-dd (ascii)
|
||||
uint8_t origin_time[8]; // hh-mm-ss (ascii)
|
||||
uint64_t time_ref; // First sample count since midnight
|
||||
uint32_t version; // Version of the BWF
|
||||
uint8_t uimd[64]; // Byte 0 of SMPTE UMID
|
||||
uint8_t reserved[188]; // 190 bytes, reserved for future use & set to NULL
|
||||
};
|
||||
|
||||
struct FactChunk
|
||||
{
|
||||
uint32_t fact_id; // Chunk ID: 'fact'
|
||||
uint32_t chunk_size; // Size in bytes
|
||||
uint32_t sample_length; // number of samples per channel
|
||||
};
|
||||
|
||||
struct ExtensibleData
|
||||
{
|
||||
uint16_t size;
|
||||
uint16_t valid_bits_per_sample;
|
||||
uint32_t channel_mask;
|
||||
struct GUID
|
||||
{
|
||||
uint32_t data0;
|
||||
uint16_t data1;
|
||||
uint16_t data2;
|
||||
uint16_t data3;
|
||||
uint8_t data4[6];
|
||||
};
|
||||
};
|
||||
|
||||
template<class C, class R>
|
||||
std::basic_ostream<C,R> & operator << (std::basic_ostream<C,R> & a, const WaveChunkHeader & b)
|
||||
{
|
||||
return a <<
|
||||
"Format ID:\t\t" << b.fmt_id <<
|
||||
"\nChunk Size:\t\t" << b.chunk_size <<
|
||||
"\nFormat Code:\t\t" << b.format <<
|
||||
"\nChannels:\t\t" << b.channel_count <<
|
||||
"\nSample Rate:\t\t" << b.sample_rate <<
|
||||
"\nData Rate:\t\t" << b.data_rate <<
|
||||
"\nFrame Size:\t\t" << b.frame_size <<
|
||||
"\nBit Depth:\t\t" << b.bit_depth << std::endl;
|
||||
}
|
||||
|
||||
//@todo expose speaker/channel/layout masks in the API:
|
||||
|
||||
enum SpeakerChannelMask
|
||||
{
|
||||
SPEAKER_FRONT_LEFT = 0x00000001,
|
||||
SPEAKER_FRONT_RIGHT = 0x00000002,
|
||||
SPEAKER_FRONT_CENTER = 0x00000004,
|
||||
SPEAKER_LOW_FREQUENCY = 0x00000008,
|
||||
SPEAKER_BACK_LEFT = 0x00000010,
|
||||
SPEAKER_BACK_RIGHT = 0x00000020,
|
||||
SPEAKER_FRONT_LEFT_OF_CENTER = 0x00000040,
|
||||
SPEAKER_FRONT_RIGHT_OF_CENTER = 0x00000080,
|
||||
SPEAKER_BACK_CENTER = 0x00000100,
|
||||
SPEAKER_SIDE_LEFT = 0x00000200,
|
||||
SPEAKER_SIDE_RIGHT = 0x00000400,
|
||||
SPEAKER_TOP_CENTER = 0x00000800,
|
||||
SPEAKER_TOP_FRONT_LEFT = 0x00001000,
|
||||
SPEAKER_TOP_FRONT_CENTER = 0x00002000,
|
||||
SPEAKER_TOP_FRONT_RIGHT = 0x00004000,
|
||||
SPEAKER_TOP_BACK_LEFT = 0x00008000,
|
||||
SPEAKER_TOP_BACK_CENTER = 0x00010000,
|
||||
SPEAKER_TOP_BACK_RIGHT = 0x00020000,
|
||||
SPEAKER_RESERVED = 0x7FFC0000,
|
||||
SPEAKER_ALL = 0x80000000
|
||||
};
|
||||
|
||||
enum SpeakerLayoutMask
|
||||
{
|
||||
SPEAKER_MONO = (SPEAKER_FRONT_CENTER),
|
||||
SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT),
|
||||
SPEAKER_2POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY),
|
||||
SPEAKER_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER),
|
||||
SPEAKER_QUAD = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
|
||||
SPEAKER_4POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
|
||||
SPEAKER_5POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT),
|
||||
SPEAKER_7POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER),
|
||||
SPEAKER_5POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),
|
||||
SPEAKER_7POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),
|
||||
};
|
||||
|
||||
//@todo verify mask values
|
||||
inline int ComputeChannelMask(const size_t channels)
|
||||
{
|
||||
switch (channels)
|
||||
{
|
||||
case 1:
|
||||
return SPEAKER_MONO;
|
||||
case 2:
|
||||
return SPEAKER_STEREO;
|
||||
case 3:
|
||||
return SPEAKER_2POINT1;
|
||||
case 4:
|
||||
return SPEAKER_QUAD;
|
||||
case 5:
|
||||
return SPEAKER_4POINT1;
|
||||
case 6:
|
||||
return SPEAKER_5POINT1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
struct WavDecoder : public nqr::BaseDecoder
|
||||
{
|
||||
WavDecoder() {}
|
||||
virtual ~WavDecoder() {}
|
||||
virtual int LoadFromPath(nqr::AudioData * data, const std::string & path) override;
|
||||
virtual int LoadFromBuffer(nqr::AudioData * data, const std::vector<uint8_t> & memory) override;
|
||||
virtual std::vector<std::string> GetSupportedFileExtensions() override;
|
||||
};
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,22 @@
|
||||
#pragma comment(user, "license")
|
||||
|
||||
#ifndef WAVEPACK_DECODER_H
|
||||
#define WAVEPACK_DECODER_H
|
||||
|
||||
#include "AudioDecoder.h"
|
||||
|
||||
namespace nqr
|
||||
{
|
||||
|
||||
struct WavPackDecoder : public nqr::BaseDecoder
|
||||
{
|
||||
WavPackDecoder() {};
|
||||
virtual ~WavPackDecoder() {};
|
||||
virtual int LoadFromPath(nqr::AudioData * data, const std::string & path) override;
|
||||
virtual int LoadFromBuffer(nqr::AudioData * data, const std::vector<uint8_t> & memory) override;
|
||||
virtual std::vector<std::string> GetSupportedFileExtensions() override;
|
||||
};
|
||||
|
||||
} // end namespace nqr
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue