project include files from ~60 messier squashed commits

adpcm
Dimitri Diakopoulos 11 years ago
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…
Cancel
Save