project source files from ~60 messier squashed commits

adpcm
Dimitri Diakopoulos 11 years ago
parent c65a33fe6a
commit c4bceacc5e

@ -0,0 +1,88 @@
#pragma comment(user, "license")
#include "AudioDecoder.h"
#include "WavDecoder.h"
#include "WavPackDecoder.h"
#include "FlacDecoder.h"
#include "VorbisDecoder.h"
#include "OpusDecoder.h"
#include "CafDecoder.h"
using namespace nqr;
NyquistIO::NyquistIO()
{
BuildDecoderTable();
}
NyquistIO::~NyquistIO()
{
}
int NyquistIO::Load(AudioData * data, const std::string & path)
{
if (IsFileSupported(path))
{
if (decoderTable.size() > 0)
{
auto fileExtension = ParsePathForExtension(path);
auto decoder = GetDecoderForExtension(fileExtension);
try
{
return decoder->LoadFromPath(data, path);
}
catch (std::exception e)
{
std::cerr << "Caught fatal exception: " << e.what() << std::endl;
}
}
return IOError::NoDecodersLoaded;
}
else
{
return IOError::ExtensionNotSupported;
}
// Should never be reached
return IOError::UnknownError;
}
bool NyquistIO::IsFileSupported(const std::string path) const
{
auto fileExtension = ParsePathForExtension(path);
if (decoderTable.find(fileExtension) == decoderTable.end())
{
return false;
}
else
{
return true;
}
}
std::string NyquistIO::ParsePathForExtension(const std::string & path) const
{
if (path.find_last_of(".") != std::string::npos)
return path.substr(path.find_last_of(".") + 1);
return std::string("");
}
std::shared_ptr<BaseDecoder> NyquistIO::GetDecoderForExtension(const std::string ext)
{
return decoderTable[ext];
}
void NyquistIO::BuildDecoderTable()
{
AddDecoderToTable(std::make_shared<WavDecoder>());
AddDecoderToTable(std::make_shared<WavPackDecoder>());
AddDecoderToTable(std::make_shared<FlacDecoder>());
AddDecoderToTable(std::make_shared<VorbisDecoder>());
AddDecoderToTable(std::make_shared<OpusDecoder>());
AddDecoderToTable(std::make_shared<CAFDecoder>());
}

@ -0,0 +1,81 @@
#pragma comment(user, "license")
#include "AudioDevice.h"
#include <cmath>
#include <algorithm>
#include <thread>
#include <chrono>
using namespace nqr;
static RingBufferT<float> buffer(BUFFER_LENGTH);
static int rt_callback(void * output_buffer, void * input_buffer, unsigned int num_bufferframes, double stream_time, RtAudioStreamStatus status, void * user_data)
{
if (status) std::cerr << "[rtaudio] Buffer over or underflow" << std::endl;
if (buffer.getAvailableRead())
{
buffer.read((float*) output_buffer, BUFFER_LENGTH);
}
else
{
memset(output_buffer, 0, BUFFER_LENGTH * sizeof(float));
}
return 0;
}
bool AudioDevice::Open(const int deviceId)
{
if (!rtaudio) throw std::runtime_error("rtaudio not created yet");
RtAudio::StreamParameters parameters;
parameters.deviceId = info.id;
parameters.nChannels = info.numChannels;
parameters.firstChannel = 0;
rtaudio->openStream(&parameters, NULL, RTAUDIO_FLOAT32, info.sampleRate, &info.frameSize, &rt_callback, (void*) & buffer);
if (rtaudio->isStreamOpen())
{
rtaudio->startStream();
return true;
}
return false;
}
void AudioDevice::ListAudioDevices()
{
std::unique_ptr<RtAudio> tempDevice(new RtAudio);
RtAudio::DeviceInfo info;
unsigned int devices = tempDevice->getDeviceCount();
std::cout << "[rtaudio] Found: " << devices << " device(s)\n";
for (unsigned int i = 0; i < devices; ++i)
{
info = tempDevice->getDeviceInfo(i);
std::cout << "\tDevice: " << i << " - " << info.name << std::endl;
}
std::cout << std::endl;
}
bool AudioDevice::Play(const std::vector<float> & data)
{
if (!rtaudio->isStreamOpen()) return false;
// Each frame is the (size/2) cause interleaved channels!
int sizeInFrames = ((int) data.size()) / (BUFFER_LENGTH);
int writeCount = 0;
while(writeCount < sizeInFrames)
{
bool status = buffer.write((data.data() + (writeCount * BUFFER_LENGTH)), BUFFER_LENGTH);
if (status) writeCount++;
}
return true;
}

@ -0,0 +1,24 @@
#pragma comment(user, "license")
#include "CafDecoder.h"
using namespace nqr;
//////////////////////
// Public Interface //
//////////////////////
int CAFDecoder::LoadFromPath(AudioData * data, const std::string & path)
{
return IOError::LoadPathNotImplemented;
}
int CAFDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & memory)
{
return IOError::LoadBufferNotImplemented;
}
std::vector<std::string> CAFDecoder::GetSupportedFileExtensions()
{
return {};
}

@ -0,0 +1,56 @@
#include "Common.h"
using namespace nqr;
ChunkHeaderInfo nqr::ScanForChunk(const std::vector<uint8_t> & fileData, uint32_t chunkMarker)
{
// D[n] aligned to 16 bytes now
const uint16_t * d = reinterpret_cast<const uint16_t *>(fileData.data());
for (size_t i = 0; i < fileData.size() / sizeof(uint16_t); i++)
{
// This will be in machine endianess
uint32_t m = Pack(Read16(d[i]), Read16(d[i+1]));
if (m == chunkMarker)
{
uint32_t cSz = Pack(Read16(d[i+2]), Read16(d[i+3]));
return {(uint32_t (i * sizeof(uint16_t))), cSz}; // return i in bytes to the start of the data
}
else continue;
}
return {0, 0};
};
NyquistFileBuffer nqr::ReadFile(std::string pathToFile)
{
std::cout << "[Debug] Open: " << pathToFile << std::endl;
FILE * audioFile = fopen(pathToFile.c_str(), "rb");
if (!audioFile)
{
throw std::runtime_error("file not found");
}
fseek(audioFile, 0, SEEK_END);
size_t lengthInBytes = ftell(audioFile);
fseek(audioFile, 0, SEEK_SET);
// Allocate temporary buffer
std::vector<uint8_t> fileBuffer(lengthInBytes);
size_t elementsRead = fread(fileBuffer.data(), 1, lengthInBytes, audioFile);
if (elementsRead == 0 || fileBuffer.size() < 64)
{
throw std::runtime_error("error reading file or file too small");
}
NyquistFileBuffer data = {std::move(fileBuffer), elementsRead};
fclose(audioFile);
// Copy out to user
return data;
}

@ -0,0 +1,177 @@
#pragma comment(user, "license")
#include "FlacDecoder.h"
#include "flac/all.h"
#include "AudioDecoder.h"
using namespace nqr;
class FlacDecoderInternal
{
public:
// N.B.: FLAC is a big-endian format. All values are unsigned.
FlacDecoderInternal(AudioData * d, std::string filepath) : d(d)
{
/////////////////////////////
// Initialize FLAC library //
/////////////////////////////
decoderInternal = FLAC__stream_decoder_new();
FLAC__stream_decoder_set_metadata_respond(decoderInternal, FLAC__METADATA_TYPE_STREAMINFO);
//@todo: check if OGG flac
bool initialized = FLAC__stream_decoder_init_file(decoderInternal,
filepath.c_str(),
s_writeCallback,
s_metadataCallback,
s_errorCallback,
this) == FLAC__STREAM_DECODER_INIT_STATUS_OK;
FLAC__stream_decoder_set_md5_checking(decoderInternal, true);
//////////////////////
// Read Stream Data //
/////////////////////
if (initialized)
{
// Find the size and allocate memory
FLAC__stream_decoder_process_until_end_of_metadata(decoderInternal);
// Read memory out into our temporary internalBuffer
FLAC__stream_decoder_process_until_end_of_stream(decoderInternal);
// Presently unneeded, but useful for reference
// FLAC__ChannelAssignment channelAssignment = FLAC__stream_decoder_get_channel_assignment(decoderInternal);
// Fill out remaining user data
d->lengthSeconds = (float) numSamples / (float) d->sampleRate;
auto totalSamples = numSamples * d->channelCount;
// N.B.: "Currently the reference encoder and decoders only support up to 24 bits per sample."
PCMFormat internalFmt = PCMFormat::PCM_END;
switch (d->bitDepth)
{
case 8:
internalFmt = PCMFormat::PCM_S8;
break;
case 16:
internalFmt = PCMFormat::PCM_16;
break;
case 24:
internalFmt = PCMFormat::PCM_24;
break;
default:
throw std::runtime_error("unsupported FLAC bit depth");
break;
}
// Next, process internal buffer into the user-visible samples array
ConvertToFloat32(d->samples.data(), internalBuffer.data(), totalSamples, internalFmt);
}
else
{
throw std::runtime_error("Unable to initialize FLAC decoder");
}
}
~FlacDecoderInternal()
{
if (decoderInternal)
{
FLAC__stream_decoder_finish(decoderInternal);
FLAC__stream_decoder_delete(decoderInternal);
}
}
void processMetadata(const FLAC__StreamMetadata_StreamInfo & info)
{
d->sampleRate = info.sample_rate;
d->channelCount = info.channels; // Assert 1 to 8
d->bitDepth = info.bits_per_sample; // Assert 4 to 32
d->frameSize = info.channels * info.bits_per_sample;
const size_t bytesPerSample = d->bitDepth / 8;
numSamples = (size_t) info.total_samples;
internalBuffer.resize(numSamples * info.channels * bytesPerSample); // as array of bytes
d->samples.resize(numSamples * info.channels); // as audio samples in float32
}
///////////////////////
// libflab callbacks //
///////////////////////
static FLAC__StreamDecoderWriteStatus s_writeCallback(const FLAC__StreamDecoder*, const FLAC__Frame* frame, const FLAC__int32* const buffer[], void* userPtr)
{
FlacDecoderInternal * decoder = reinterpret_cast<FlacDecoderInternal *>(userPtr);
const size_t bytesPerSample = decoder->d->bitDepth / 8;
auto dataPtr = decoder->internalBuffer.data();
for(unsigned int i = 0; i < frame->header.blocksize; i++)
{
for(int j = 0; j < decoder->d->channelCount; j++)
{
memcpy(dataPtr + decoder->bufferPosition, &buffer[j][i], bytesPerSample);
decoder->bufferPosition += bytesPerSample;
}
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
static void s_metadataCallback (const FLAC__StreamDecoder*, const FLAC__StreamMetadata* metadata, void* userPtr)
{
static_cast<FlacDecoderInternal*>(userPtr)->processMetadata(metadata->data.stream_info);
}
static void s_errorCallback (const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus status, void*)
{
std::cerr << "FLAC Decoder Error: " << FLAC__StreamDecoderErrorStatusString[status] << std::endl;
}
private:
NO_COPY(FlacDecoderInternal);
FLAC__StreamDecoder * decoderInternal;
size_t bufferPosition = 0;
size_t numSamples = 0;
AudioData * d;
std::vector<uint8_t> internalBuffer;
};
//////////////////////
// Public Interface //
//////////////////////
int FlacDecoder::LoadFromPath(AudioData * data, const std::string & path)
{
FlacDecoderInternal decoder(data, path);
return IOError::NoError;
}
int FlacDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & memory)
{
return IOError::LoadBufferNotImplemented;
}
std::vector<std::string> FlacDecoder::GetSupportedFileExtensions()
{
return {"flac"};
}

@ -0,0 +1,84 @@
#pragma comment(user, "license")
#undef VERSION
#define VERSION "1.3.1"
#define FLAC__NO_DLL 1
#if (_MSC_VER)
#pragma warning (push)
#pragma warning (disable: 181 111 4267 4996 4244 4701 4702 4133 4100 4127 4206 4312 4505 4365 4005 4013 4334)
#ifndef _WIN32
#define _WIN32
#endif
#endif
#if defined(__APPLE__) && defined(__MACH__)
#define FLAC__SYS_DARWIN 1
#endif
#ifndef SIZE_MAX
#define SIZE_MAX (size_t) (-1)
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic ignored "-Wdeprecated-register"
#endif
#if CPU_X86
#ifdef __i386__
#define FLAC__CPU_IA32 1
#endif
#ifdef __x86_64__
#define FLAC__CPU_X86_64 1
#endif
#define FLAC__HAS_X86INTRIN 1
#endif
// Ensure libflac can use non-standard <stdint> types
#undef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#if defined(__APPLE__) && defined(__MACH__)
#define flac_max(a,b) ((a) > (b) ? a : b)
#define flac_min(a,b) ((a) < (b) ? a : b)
#elif defined(_MSC_VER)
#include <stdlib.h>
#define flac_max(a,b) __max(a,b)
#define flac_min(a,b) __min(a,b)
#endif
#define HAVE_LROUND 1
#include "flac/all.h"
#if defined(_MSC_VER)
#include "flac/src/win_utf8_io.c"
#endif
#include "flac/src/bitmath.c"
#include "flac/src/bitreader.c"
#include "flac/src/bitwriter.c"
#include "flac/src/cpu.c"
#include "flac/src/crc.c"
#include "flac/src/fixed.c"
#include "flac/src/float.c"
#include "flac/src/format.c"
#include "flac/src/lpc.c"
#include "flac/src/md5.c"
#include "flac/src/memory.c"
#include "flac/src/stream_decoder.c"
#include "flac/src/window.c"
#undef VERSION
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#if (_MSC_VER)
#pragma warning (pop)
#endif

@ -0,0 +1,96 @@
#if defined(_MSC_VER)
#pragma comment(lib, "dsound.lib")
#endif
#include "AudioDevice.h"
#include "AudioDecoder.h"
#include <thread>
using namespace nqr;
int main()
{
AudioDevice::ListAudioDevices();
int desiredSampleRate = 44100;
AudioDevice myDevice(2, desiredSampleRate);
myDevice.Open(myDevice.info.id);
AudioData * fileData = new AudioData();
NyquistIO loader;
try
{
//auto result = loader.Load(fileData, "test_data/1ch/44100/8/test.wav");
//auto result = loader.Load(fileData, "test_data/1ch/44100/16/test.wav");
//auto result = loader.Load(fileData, "test_data/1ch/44100/24/test.wav");
//auto result = loader.Load(fileData, "test_data/1ch/44100/32/test.wav");
//auto result = loader.Load(fileData, "test_data/1ch/44100/64/test.wav");
//auto result = loader.Load(fileData, "test_data/2ch/44100/8/test.wav");
//auto result = loader.Load(fileData, "test_data/2ch/44100/16/test.wav");
//auto result = loader.Load(fileData, "test_data/2ch/44100/24/test.wav");
auto result = loader.Load(fileData, "test_data/2ch/44100/32/test.wav");
//auto result = loader.Load(fileData, "test_data/2ch/44100/64/test.wav");
//auto result = loader.Load(fileData, "test_data/ad_hoc/6_channel_44k_16b.wav");
//auto result = loader.Load(fileData, "test_data/ad_hoc/LR_Stereo.ogg");
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestLaugh_44k.ogg");
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat.ogg");
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeatMono.ogg");
//auto result = loader.Load(fileData, "test_data/ad_hoc/BlockWoosh_Stereo.ogg");
//auto result = loader.Load(fileData, "test_data/ad_hoc/KittyPurr8_Stereo_Dithered.flac");
//auto result = loader.Load(fileData, "test_data/ad_hoc/KittyPurr16_Stereo.flac");
//auto result = loader.Load(fileData, "test_data/ad_hoc/KittyPurr16_Mono.flac");
//auto result = loader.Load(fileData, "test_data/ad_hoc/KittyPurr24_Stereo.flac");
//auto result = loader.Load(fileData, "test_data/ad_hoc/detodos.opus"); // "Firefox: From All, To All"
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_Float32.wv");
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_Float32_Mono.wv");
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_Int16.wv");
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_Int24.wv");
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_Int32.wv");
//auto result = loader.Load(fileData, "test_data/ad_hoc/TestBeat_Int24_Mono.wv");
std::cout << "[Debug] Loader Status: " << result << std::endl;
}
catch (std::exception e)
{
std::cerr << "Caught: " << e.what() << std::endl;
std::exit(1);
}
// Libnyquist does not do sample rate conversion
if (fileData->sampleRate != desiredSampleRate)
{
std::cout << "[Warning - Sample Rate Mismatch] - file is sampled at " << fileData->sampleRate << " and output is " << desiredSampleRate << std::endl;
}
// Convert mono to stereo for testing playback
if (fileData->channelCount == 1)
{
std::vector<float> stereoCopy(fileData->samples.size() * 2);
int m = 0;
for (size_t i = 0; i < stereoCopy.size(); i+=2)
{
stereoCopy[i] = fileData->samples[m];
stereoCopy[i+1] = fileData->samples[m];
m++;
}
myDevice.Play(stereoCopy);
}
else
{
std::cout << "Playing for: " << fileData->lengthSeconds << " seconds..." << std::endl;
myDevice.Play(fileData->samples);
}
return 0;
}

@ -0,0 +1,156 @@
#pragma comment(user, "license")
#include "OpusDecoder.h"
using namespace nqr;
static const int OPUS_SAMPLE_RATE = 48000;
class OpusDecoderInternal
{
public:
OpusDecoderInternal(AudioData * d, const std::vector<uint8_t> & fileData) : d(d)
{
/* @todo proper steaming support + classes
const opus_callbacks = {
.read = s_readCallback,
.seek = s_seekCallback,
.tell = s_tellCallback,
.close = nullptr
};
*/
int err;
fileHandle = op_test_memory(fileData.data(), fileData.size(), &err);
if (!fileHandle)
{
std::cerr << errorAsString(err) << std::endl;
throw std::runtime_error("File is not a valid ogg vorbis file");
}
if (auto r = op_test_open(fileHandle) != 0)
{
std::cerr << errorAsString(r) << std::endl;
throw std::runtime_error("Could not open file");
}
const OpusHead *header = op_head(fileHandle, 0);
int originalSampleRate = header->input_sample_rate;
std::cout << "Original Sample Rate: " << originalSampleRate << std::endl;
d->sampleRate = OPUS_SAMPLE_RATE;
d->channelCount = (uint32_t) header->channel_count;
d->bitDepth = 32;
d->lengthSeconds = double(getLengthInSeconds());
d->frameSize = (uint32_t) header->channel_count * d->bitDepth;
// Samples in a single channel
auto totalSamples = size_t(getTotalSamples());
d->samples.resize(totalSamples * d->channelCount);
auto r = readInternal(totalSamples);
}
~OpusDecoderInternal()
{
op_free(fileHandle);
}
size_t readInternal(size_t requestedFrameCount, size_t frameOffset = 0)
{
float *buffer = (float *) d->samples.data();
size_t framesRemaining = requestedFrameCount;
size_t totalFramesRead = 0;
while(0 < framesRemaining)
{
int64_t framesRead = op_read_float(fileHandle, buffer, (int)(framesRemaining * d->channelCount), nullptr);
// EOF
if(!framesRead)
break;
if (framesRead < 0)
{
std::cerr << "Opus decode error: " << framesRead << std::endl;
return 0;
}
buffer += framesRead * d->channelCount;
totalFramesRead += framesRead;
framesRemaining -= framesRead;
}
return totalFramesRead;
}
std::string errorAsString(int opusErrorCode)
{
switch(opusErrorCode)
{
case OP_FALSE: return "A request did not succeed";
case OP_EOF: return "End of File Reached";
case OP_HOLE: return "There was a hole in the page sequence numbers (e.g., a page was corrupt or missing).";
case OP_EREAD: return "An underlying read, seek, or tell operation failed when it should have succeeded.";
case OP_EFAULT: return "A NULL pointer was passed where one was unexpected, or an internal memory allocation failed, or an internal library error was encountered.";
case OP_EIMPL: return "The stream used a feature that is not implemented, such as an unsupported channel family. ";
case OP_EINVAL: return "One or more parameters to a function were invalid. ";
case OP_ENOTFORMAT: return "A purported Ogg Opus stream did not begin with an Ogg page, a purported header packet did not start with one of the required strings";
case OP_EBADHEADER: return "A required header packet was not properly formatted, contained illegal values, or was missing altogether.";
case OP_EVERSION: return "The ID header contained an unrecognized version number.";
case OP_ENOTAUDIO: return "Not Audio";
case OP_EBADPACKET: return "An audio packet failed to decode properly.";
case OP_EBADLINK: return "We failed to find data we had seen before, or the bitstream structure was sufficiently malformed that seeking to the target destination was impossible.";
case OP_ENOSEEK: return "An operation that requires seeking was requested on an unseekable stream.";
case OP_EBADTIMESTAMP: return "The first or last granule position of a link failed basic validity checks.";
default: return "Unknown Error";
}
}
////////////////////
// opus callbacks //
////////////////////
private:
NO_MOVE(OpusDecoderInternal);
OggOpusFile * fileHandle;
AudioData * d;
inline int64_t getTotalSamples() const { return int64_t(op_pcm_total(const_cast<OggOpusFile *>(fileHandle), -1)); }
inline int64_t getLengthInSeconds() const { return uint64_t(getTotalSamples() / OPUS_SAMPLE_RATE); }
inline int64_t getCurrentSample() const { return int64_t(op_pcm_tell(const_cast<OggOpusFile *>(fileHandle))); }
};
//////////////////////
// Public Interface //
//////////////////////
int nqr::OpusDecoder::LoadFromPath(AudioData * data, const std::string & path)
{
auto fileBuffer = nqr::ReadFile(path);
OpusDecoderInternal decoder(data, fileBuffer.buffer);
return IOError::NoError;
}
int nqr::OpusDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & memory)
{
OpusDecoderInternal decoder(data, memory);
return IOError::NoError;
}
std::vector<std::string> nqr::OpusDecoder::GetSupportedFileExtensions()
{
return {"opus"};
}

@ -0,0 +1,258 @@
#pragma comment(user, "license")
// https://dxr.mozilla.org/mozilla-central/source/media/libopus
#if (_MSC_VER)
#pragma warning (push)
#pragma warning (disable: 181 111 4267 4996 4244 4701 4702 4133 4100 4127 4206 4312 4505 4365 4005 4013 4334 4703)
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic ignored "-Wdeprecated-register"
#endif
#undef HAVE_CONFIG_H
#define USE_ALLOCA 1
#define OPUS_BUILD 1
/* Enable SSE functions, if compiled with SSE/SSE2 (note that AMD64 implies SSE2) */
#if defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))
#define __SSE__ 1
#endif
//////////
// CELT //
//////////
#include "opus/celt/arch.h"
#include "opus/celt/bands.h"
#include "opus/celt/celt.h"
#include "opus/celt/celt_lpc.h"
#include "opus/celt/cwrs.h"
#include "opus/celt/ecintrin.h"
#include "opus/celt/entcode.h"
#include "opus/celt/entdec.h"
#include "opus/celt/entenc.h"
#include "opus/celt/float_cast.h"
#include "opus/celt/kiss_fft.h"
#include "opus/celt/laplace.h"
#include "opus/celt/mathops.h"
#include "opus/celt/mdct.h"
#include "opus/celt/mfrngcod.h"
#include "opus/celt/modes.h"
#include "opus/celt/os_support.h"
#include "opus/celt/pitch.h"
#include "opus/celt/quant_bands.h"
#include "opus/celt/rate.h"
#include "opus/celt/stack_alloc.h"
#include "opus/celt/vq.h"
#include "opus/celt/_kiss_fft_guts.h"
#include "opus/celt/bands.c"
#include "opus/celt/celt.c"
#include "opus/celt/celt_lpc.c"
#include "opus/celt/cwrs.c"
#include "opus/celt/entcode.c"
#include "opus/celt/entdec.c"
#include "opus/celt/entenc.c"
#include "opus/celt/kiss_fft.c"
#include "opus/celt/laplace.c"
#include "opus/celt/mathops.c"
#include "opus/celt/mdct.c"
#include "opus/celt/modes.c"
#include "opus/celt/pitch.c"
#include "opus/celt/quant_bands.c"
#include "opus/celt/rate.c"
#include "opus/celt/vq.c"
// Disabled inline because of name clash of opus_custom_encoder_get_size.
//#include "opus/celt/celt_decoder.c"
//#include "opus/celt/celt_encoder.c"
/*
See celt/celt_decoder.c + celt/celt_encoder.c in the project browser.
These files need to be in separate translation units due to name clashes,
unfortunately.
*/
/////////////////
// SILK Common //
/////////////////
#include "opus_types.h"
#include "opus/silk/control.h"
#include "opus/silk/debug.h"
#include "opus/silk/define.h"
#include "opus/silk/errors.h"
#include "opus/silk/MacroCount.h"
#include "opus/silk/MacroDebug.h"
#include "opus/silk/macros.h"
#include "opus/silk/main.h"
#include "opus/silk/pitch_est_defines.h"
#include "opus/silk/PLC.h"
#include "opus/silk/resampler_private.h"
#include "opus/silk/resampler_rom.h"
#include "opus/silk/resampler_structs.h"
#include "opus/silk/API.h"
#include "opus/silk/SigProc_FIX.h"
#include "opus/silk/structs.h"
#include "opus/silk/tables.h"
#include "opus/silk/tuning_parameters.h"
#include "opus/silk/typedef.h"
#include "opus/silk/A2NLSF.c"
#include "opus/silk/ana_filt_bank_1.c"
#include "opus/silk/biquad_alt.c"
#include "opus/silk/bwexpander.c"
#include "opus/silk/bwexpander_32.c"
#include "opus/silk/check_control_input.c"
#include "opus/silk/CNG.c"
#include "opus/silk/code_signs.c"
#include "opus/silk/control_audio_bandwidth.c"
#include "opus/silk/control_codec.c"
#include "opus/silk/control_SNR.c"
#include "opus/silk/debug.c"
#include "opus/silk/decoder_set_fs.c"
#include "opus/silk/decode_core.c"
#include "opus/silk/decode_frame.c"
#include "opus/silk/decode_indices.c"
#include "opus/silk/decode_parameters.c"
#include "opus/silk/decode_pitch.c"
#include "opus/silk/decode_pulses.c"
#include "opus/silk/dec_API.c"
#include "opus/silk/encode_indices.c"
#include "opus/silk/encode_pulses.c"
#include "opus/silk/enc_API.c"
#include "opus/silk/gain_quant.c"
#include "opus/silk/HP_variable_cutoff.c"
#include "opus/silk/init_decoder.c"
#include "opus/silk/init_encoder.c"
#include "opus/silk/inner_prod_aligned.c"
#include "opus/silk/interpolate.c"
#include "opus/silk/lin2log.c"
#include "opus/silk/log2lin.c"
#include "opus/silk/LPC_analysis_filter.c"
#include "opus/silk/LPC_inv_pred_gain.c"
#include "opus/silk/LP_variable_cutoff.c"
#include "opus/silk/NLSF2A.c"
#include "opus/silk/NLSF_decode.c"
#include "opus/silk/NLSF_del_dec_quant.c"
#include "opus/silk/NLSF_encode.c"
#include "opus/silk/NLSF_stabilize.c"
#include "opus/silk/NLSF_unpack.c"
#include "opus/silk/NLSF_VQ.c"
#include "opus/silk/NLSF_VQ_weights_laroia.c"
#include "opus/silk/NSQ.c"
#include "opus/silk/NSQ_del_dec.c"
#include "opus/silk/pitch_est_tables.c"
#include "opus/silk/PLC.c"
#include "opus/silk/process_NLSFs.c"
#include "opus/silk/quant_LTP_gains.c"
#include "opus/silk/resampler.c"
#include "opus/silk/resampler_down2.c"
#include "opus/silk/resampler_down2_3.c"
#include "opus/silk/resampler_private_AR2.c"
#include "opus/silk/resampler_private_down_FIR.c"
#include "opus/silk/resampler_private_IIR_FIR.c"
#include "opus/silk/resampler_private_up2_HQ.c"
#include "opus/silk/resampler_rom.c"
#include "opus/silk/shell_coder.c"
#include "opus/silk/sigm_Q15.c"
#include "opus/silk/sort.c"
#include "opus/silk/stereo_decode_pred.c"
#include "opus/silk/stereo_encode_pred.c"
#include "opus/silk/stereo_find_predictor.c"
#include "opus/silk/stereo_LR_to_MS.c"
#include "opus/silk/stereo_MS_to_LR.c"
#include "opus/silk/stereo_quant_pred.c"
#include "opus/silk/sum_sqr_shift.c"
#include "opus/silk/tables_gain.c"
#include "opus/silk/tables_LTP.c"
#include "opus/silk/tables_NLSF_CB_NB_MB.c"
#include "opus/silk/tables_NLSF_CB_WB.c"
#include "opus/silk/tables_other.c"
#include "opus/silk/tables_pitch_lag.c"
#include "opus/silk/tables_pulses_per_block.c"
#include "opus/silk/table_LSF_cos.c"
#include "opus/silk/VAD.c"
#include "opus/silk/VQ_WMat_EC.c"
////////////////
// SILK Float //
////////////////
#include "opus/silk/float/main_FLP.h"
#include "opus/silk/float/SigProc_FLP.h"
#include "opus/silk/float/structs_FLP.h"
#include "opus/silk/float/apply_sine_window_FLP.c"
#include "opus/silk/float/autocorrelation_FLP.c"
#include "opus/silk/float/burg_modified_FLP.c"
#include "opus/silk/float/bwexpander_FLP.c"
#include "opus/silk/float/corrMatrix_FLP.c"
#include "opus/silk/float/encode_frame_FLP.c"
#include "opus/silk/float/energy_FLP.c"
#include "opus/silk/float/find_LPC_FLP.c"
#include "opus/silk/float/find_LTP_FLP.c"
#include "opus/silk/float/find_pitch_lags_FLP.c"
#include "opus/silk/float/find_pred_coefs_FLP.c"
#include "opus/silk/float/inner_product_FLP.c"
#include "opus/silk/float/k2a_FLP.c"
#include "opus/silk/float/levinsondurbin_FLP.c"
#include "opus/silk/float/LPC_analysis_filter_FLP.c"
#include "opus/silk/float/LPC_inv_pred_gain_FLP.c"
#include "opus/silk/float/LTP_analysis_filter_FLP.c"
#include "opus/silk/float/LTP_scale_ctrl_FLP.c"
#include "opus/silk/float/noise_shape_analysis_FLP.c"
#include "opus/silk/float/pitch_analysis_core_FLP.c"
#include "opus/silk/float/prefilter_FLP.c"
#include "opus/silk/float/process_gains_FLP.c"
#include "opus/silk/float/regularize_correlations_FLP.c"
#include "opus/silk/float/residual_energy_FLP.c"
#include "opus/silk/float/scale_copy_vector_FLP.c"
#include "opus/silk/float/scale_vector_FLP.c"
#include "opus/silk/float/schur_FLP.c"
#include "opus/silk/float/solve_LS_FLP.c"
#include "opus/silk/float/sort_FLP.c"
#include "opus/silk/float/warped_autocorrelation_FLP.c"
#include "opus/silk/float/wrappers_FLP.c"
/////////////
// LibOpus //
/////////////
#include "opus/libopus/src/opus.c"
#include "opus/libopus/src/opus_decoder.c"
#include "opus/libopus/src/opus_encoder.c"
#include "opus/libopus/src/opus_multistream.c"
#include "opus/libopus/src/opus_multistream_decoder.c"
#include "opus/libopus/src/opus_multistream_encoder.c"
#include "opus/libopus/src/repacketizer.c"
#include "opus/libopus/src/analysis.c"
#include "opus/libopus/src/mlp.c"
#include "opus/libopus/src/mlp_data.c"
//////////////
// Opusfile //
//////////////
#include "opus/opusfile/src/http.c"
#include "opus/opusfile/src/info.c"
#include "opus/opusfile/src/internal.c"
#include "opus/opusfile/src/opusfile.c"
#include "opus/opusfile/src/stream.c"
#include "opus/opusfile/src/wincerts.c"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#if (_MSC_VER)
#pragma warning (pop)
#endif

@ -0,0 +1,174 @@
#pragma comment(user, "license")
#include "VorbisDecoder.h"
using namespace nqr;
//@todo: implement decoding from memory (c.f. http://stackoverflow.com/questions/13437422/libvorbis-audio-decode-from-memory-in-c)
class VorbisDecoderInternal
{
public:
VorbisDecoderInternal(AudioData * d, std::string filepath) : d(d)
{
fileHandle = new OggVorbis_File();
/* @todo proper steaming support + classes
const ov_callbacks callbacks =
{
.read_func = s_readCallback,
.seek_func = s_seekCallback,
.tell_func = s_tellCallback,
.close_func = nullptr
};
*/
FILE * f = fopen(filepath.c_str(), "rb");
if (!f)
throw std::runtime_error("Can't open file");
if (auto r = ov_test_callbacks(f, fileHandle, nullptr, 0, OV_CALLBACKS_DEFAULT) != 0)
{
std::cerr << errorAsString(r) << std::endl;
throw std::runtime_error("File is not a valid ogg vorbis file");
}
if (auto r = ov_test_open(fileHandle) != 0)
{
std::cerr << errorAsString(r) << std::endl;
throw std::runtime_error("ov_test_open failed");
}
// N.B.: Don't need to fclose() after an open -- vorbis does this internally
vorbis_info *ovInfo = ov_info(fileHandle, -1);
if (ovInfo == nullptr)
{
throw std::runtime_error("Reading metadata failed");
}
if (auto r = ov_streams(fileHandle) != 1)
{
std::cerr << errorAsString(r) << std::endl;
throw std::runtime_error( "Unsupported: file contains multiple bitstreams");
}
d->sampleRate = int(ovInfo->rate);
d->channelCount = ovInfo->channels;
d->bitDepth = 32; // float
d->lengthSeconds = double(getLengthInSeconds());
d->frameSize = ovInfo->channels * d->bitDepth;
// Samples in a single channel
auto totalSamples = size_t(getTotalSamples());
d->samples.resize(totalSamples * d->channelCount);
auto r = readInternal(totalSamples);
}
~VorbisDecoderInternal()
{
ov_clear(fileHandle);
}
size_t readInternal(size_t requestedFrameCount, size_t frameOffset = 0)
{
//@todo support offset
float **buffer = nullptr;
size_t framesRemaining = requestedFrameCount;
size_t totalFramesRead = 0;
int bitstream = 0;
while(0 < framesRemaining)
{
int64_t framesRead = ov_read_float(fileHandle, &buffer, std::min(2048, (int) framesRemaining), &bitstream);
// EOF
if(!framesRead)
break;
if (framesRead < 0)
{
// Probably OV_HOLE, OV_EBADLINK, OV_EINVAL. Log warning here.
continue;
}
for (int i = 0; i < framesRead; ++i)
{
for(int ch = 0; ch < d->channelCount; ch++)
{
d->samples[totalFramesRead] = buffer[ch][i];
totalFramesRead++;
}
}
}
return totalFramesRead;
}
std::string errorAsString(int ovErrorCode)
{
switch(ovErrorCode)
{
case OV_FALSE: return "OV_FALSE";
case OV_EOF: return "OV_EOF";
case OV_HOLE: return "OV_HOLE";
case OV_EREAD: return "OV_EREAD";
case OV_EFAULT: return "OV_EFAULT";
case OV_EIMPL: return "OV_EIMPL";
case OV_EINVAL: return "OV_EINVAL";
case OV_ENOTVORBIS: return "OV_ENOTVORBIS";
case OV_EBADHEADER: return "OV_EBADHEADER";
case OV_EVERSION: return "OV_EVERSION";
case OV_ENOTAUDIO: return "OV_ENOTAUDIO";
case OV_EBADPACKET: return "OV_EBADPACKET";
case OV_EBADLINK: return "OV_EBADLINK";
case OV_ENOSEEK: return "OV_ENOSEEK";
default: return "OV_UNKNOWN_ERROR";
}
}
//////////////////////
// vorbis callbacks //
//////////////////////
//@todo: implement streaming support
private:
NO_COPY(VorbisDecoderInternal);
OggVorbis_File * fileHandle;
AudioData * d;
inline int64_t getTotalSamples() const { return int64_t(ov_pcm_total(const_cast<OggVorbis_File *>(fileHandle), -1)); }
inline int64_t getLengthInSeconds() const { return int64_t(ov_time_total(const_cast<OggVorbis_File *>(fileHandle), -1)); }
inline int64_t getCurrentSample() const { return int64_t(ov_pcm_tell(const_cast<OggVorbis_File *>(fileHandle))); }
};
//////////////////////
// Public Interface //
//////////////////////
int VorbisDecoder::LoadFromPath(AudioData * data, const std::string & path)
{
VorbisDecoderInternal decoder(data, path);
return IOError::NoError;
}
int VorbisDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & memory)
{
return IOError::LoadBufferNotImplemented;
}
std::vector<std::string> VorbisDecoder::GetSupportedFileExtensions()
{
return {"ogg"};
}

@ -0,0 +1,50 @@
#pragma comment(user, "license")
#if (_MSC_VER)
#pragma warning (push)
#pragma warning (disable: 181 111 4267 4996 4244 4701 4702 4133 4100 4127 4206 4312 4505 4365 4005 4013 4334)
#endif
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wshadow"
#pragma clang diagnostic ignored "-Wdeprecated-register"
#endif
#include "libvorbis/include/vorbis/vorbisenc.h"
#include "libvorbis/include/vorbis/codec.h"
#include "libvorbis/include/vorbis/vorbisfile.h"
#include "libogg/src/bitwise.c"
#include "libogg/src/framing.c"
#include "libvorbis/src/analysis.c"
#include "libvorbis/src/bitrate.c"
#include "libvorbis/src/block.c"
#include "libvorbis/src/codebook.c"
#include "libvorbis/src/envelope.c"
#include "libvorbis/src/floor0.c"
#include "libvorbis/src/floor1.c"
#include "libvorbis/src/info.c"
#include "libvorbis/src/lpc.c"
#include "libvorbis/src/lsp.c"
#include "libvorbis/src/mapping0.c"
#include "libvorbis/src/psy.c"
#include "libvorbis/src/registry.c"
#include "libvorbis/src/res0.c"
#include "libvorbis/src/sharedbook.c"
#include "libvorbis/src/smallft.c"
#include "libvorbis/src/synthesis.c"
#include "libvorbis/src/vorbisenc.c"
#include "libvorbis/src/vorbisfile.c"
#include "libvorbis/src/window.c"
#include "libvorbis/src/mdct.c"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#if (_MSC_VER)
#pragma warning (pop)
#endif

@ -0,0 +1,185 @@
#pragma comment(user, "license")
#include "WavDecoder.h"
using namespace nqr;
//////////////////////
// Public Interface //
//////////////////////
int WavDecoder::LoadFromPath(AudioData * data, const std::string & path)
{
auto fileBuffer = nqr::ReadFile(path);
return LoadFromBuffer(data, fileBuffer.buffer);
}
int WavDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & memory)
{
//////////////////////
// Read RIFF Header //
//////////////////////
//@todo swap methods for rifx
RiffChunkHeader riffHeader = {};
memcpy(&riffHeader, memory.data(), 12);
// Files should be 2-byte aligned
//@tofix: enforce this
bool usePaddingShort = ((riffHeader.file_size % sizeof(uint16_t)) == 1) ? true : false;
// Check RIFF
if (riffHeader.id_riff != GenerateChunkCode('R', 'I', 'F', 'F'))
{
// Check RIFX + FFIR
if (riffHeader.id_riff == GenerateChunkCode('R', 'I', 'F', 'X') || riffHeader.id_riff == GenerateChunkCode('F', 'F', 'I', 'R'))
{
// We're not RIFF, and we don't match RIFX or FFIR either
throw std::runtime_error("libnyquist doesn't support big endian files");
}
else
{
throw std::runtime_error("bad RIFF/RIFX/FFIR file header");
}
}
if (riffHeader.id_wave != GenerateChunkCode('W', 'A', 'V', 'E')) throw std::runtime_error("bad WAVE header");
if ((memory.size() - riffHeader.file_size) != sizeof(uint32_t) * 2)
{
throw std::runtime_error("declared size of file less than file size"); //@todo warning instead of runtime_error
}
//////////////////////
// Read WAVE Header //
//////////////////////
auto WaveChunkInfo = ScanForChunk(memory, GenerateChunkCode('f', 'm', 't', ' '));
if (WaveChunkInfo.offset == 0) throw std::runtime_error("couldn't find fmt chunk");
assert(WaveChunkInfo.size == 16 || WaveChunkInfo.size == 18 || WaveChunkInfo.size == 40);
WaveChunkHeader wavHeader = {};
memcpy(&wavHeader, memory.data() + WaveChunkInfo.offset, sizeof(WaveChunkHeader));
if (wavHeader.chunk_size < 16)
throw std::runtime_error("format chunk too small");
//@todo validate wav header (sane sample rate, bit depth, etc)
data->channelCount = wavHeader.channel_count;
data->sampleRate = wavHeader.sample_rate;
data->frameSize = wavHeader.frame_size;
std::cout << wavHeader << std::endl;
bool scanForFact = false;
bool grabExtensibleData = false;
if (wavHeader.format == WaveFormatCode::FORMAT_PCM)
{
std::cout << "[format id] pcm" << std::endl;
}
else if (wavHeader.format == WaveFormatCode::FORMAT_IEEE)
{
std::cout << "[format id] ieee" << std::endl;
scanForFact = true;
}
else if (wavHeader.format == WaveFormatCode::FORMAT_EXT)
{
// Used when (1) PCM data has more than 16 bits; (2) channels > 2; (3) bits/sample !== container size; (4) channel/speaker mapping specified
std::cout << "[format id] extended" << std::endl;
scanForFact = true;
grabExtensibleData = true;
}
else if (wavHeader.format == WaveFormatCode::FORMAT_UNKNOWN)
{
throw std::runtime_error("unknown wave format");
}
////////////////////////////
// Read Additional Chunks //
////////////////////////////
if (scanForFact)
{
auto FactChunkInfo = ScanForChunk(memory, GenerateChunkCode('f', 'a', 'c', 't'));
FactChunk factChunk = {};
if (FactChunkInfo.size)
{
memcpy(&factChunk, memory.data() + FactChunkInfo.offset, sizeof(FactChunk));
}
}
if (grabExtensibleData)
{
ExtensibleData extData = {};
memcpy(&extData, memory.data() + WaveChunkInfo.offset + sizeof(WaveChunkHeader), sizeof(ExtensibleData));
// extData can be compared against the multi-channel masks defined in the header
// eg. extData.channel_mask == SPEAKER_5POINT1
}
//@todo smpl chunk could be useful
/////////////////////
// Read Bext Chunk //
/////////////////////
auto BextChunkInfo = ScanForChunk(memory, GenerateChunkCode('b', 'e', 'x', 't'));
BextChunk bextChunk = {};
if (BextChunkInfo.size)
{
memcpy(&bextChunk, memory.data() + BextChunkInfo.offset, sizeof(BextChunk));
}
/////////////////////
// Read DATA Chunk //
/////////////////////
auto DataChunkInfo = ScanForChunk(memory, GenerateChunkCode('d', 'a', 't', 'a'));
if (DataChunkInfo.offset == 0) throw std::runtime_error("couldn't find data chunk");
DataChunkInfo.offset += 8; // ignore the header and size fields (2 uint32s)
data->lengthSeconds = ((float) DataChunkInfo.size / (float) wavHeader.sample_rate) / wavHeader.frame_size;
auto bit_depth = wavHeader.bit_depth;
size_t totalSamples = (DataChunkInfo.size / wavHeader.frame_size) * wavHeader.channel_count;
data->samples.resize(totalSamples);
PCMFormat internalFmt = PCMFormat::PCM_END;
switch (bit_depth)
{
case 8:
internalFmt = PCMFormat::PCM_U8;
break;
case 16:
internalFmt = PCMFormat::PCM_16;
break;
case 24:
internalFmt = PCMFormat::PCM_24;
break;
case 32:
internalFmt = (wavHeader.format == WaveFormatCode::FORMAT_IEEE) ? PCMFormat::PCM_FLT : PCMFormat::PCM_32;
break;
case 64:
internalFmt = (wavHeader.format == WaveFormatCode::FORMAT_IEEE) ? PCMFormat::PCM_DBL : PCMFormat::PCM_64;
break;
}
ConvertToFloat32(data->samples.data(), memory.data() + DataChunkInfo.offset, totalSamples, internalFmt);
return IOError::NoError;
}
std::vector<std::string> WavDecoder::GetSupportedFileExtensions()
{
return {"wav", "wave"};
}

@ -0,0 +1,152 @@
#pragma comment(user, "license")
#include "WavPackDecoder.h"
#include "wavpack.h"
using namespace nqr;
class WavPackInternal
{
public:
WavPackInternal(AudioData * d, const std::string path) : d(d)
{
char errorStr[128];
context = WavpackOpenFileInput(path.c_str(), errorStr, OPEN_WVC | OPEN_NORMALIZE, 0);
if (!context)
{
throw std::runtime_error("Not a WavPack file");
}
d->sampleRate = WavpackGetSampleRate(context);
d->channelCount = WavpackGetNumChannels(context);
d->bitDepth = WavpackGetBitsPerSample(context);
d->lengthSeconds = double(getLengthInSeconds());
d->frameSize = d->channelCount * d->bitDepth;
//@todo support channel masks
// WavpackGetChannelMask
auto totalSamples = size_t(getTotalSamples());
PCMFormat internalFmt = PCMFormat::PCM_END;
int mode = WavpackGetMode(context);
int isFloatingPoint = (MODE_FLOAT & mode);
switch (d->bitDepth)
{
case 16:
internalFmt = PCMFormat::PCM_16;
break;
case 24:
internalFmt = PCMFormat::PCM_24;
break;
case 32:
internalFmt = isFloatingPoint ? PCMFormat::PCM_FLT : PCMFormat::PCM_32;
break;
default:
throw std::runtime_error("unsupported WavPack bit depth");
break;
}
/* From the docs:
"... required memory at "buffer" is 4 * samples * num_channels bytes. The
audio data is returned right-justified in 32-bit longs in the endian
mode native to the executing processor."
*/
d->samples.resize(totalSamples * d->channelCount);
if (!isFloatingPoint)
internalBuffer.resize(totalSamples * d->channelCount);
auto r = readInternal(totalSamples);
// Next, process internal buffer into the user-visible samples array
if (!isFloatingPoint)
ConvertToFloat32(d->samples.data(), internalBuffer.data(), totalSamples * d->channelCount, internalFmt);
}
~WavPackInternal()
{
WavpackCloseFile(context);
}
size_t readInternal(size_t requestedFrameCount, size_t frameOffset = 0)
{
size_t framesRemaining = requestedFrameCount;
size_t totalFramesRead = 0;
// int frameSize = d->channelCount * WavpackGetBitsPerSample(context);
// The samples returned are handled differently based on the file's mode
int mode = WavpackGetMode(context);
while (0 < framesRemaining)
{
uint32_t framesRead = -1;
if (MODE_FLOAT & mode)
{
// Since it's float, we can decode directly into our buffer as a huge blob
framesRead = WavpackUnpackSamples(context, reinterpret_cast<int32_t*>(&d->samples.data()[0]), uint32_t(d->samples.size() / d->channelCount));
}
else if(MODE_LOSSLESS & mode)
{
// Lossless files will be handed off as integers
framesRead = WavpackUnpackSamples(context, internalBuffer.data(), uint32_t(internalBuffer.size() / d->channelCount));
}
// EOF
//if (framesRead == 0)
// break;
std::cout << framesRead << std::endl;
totalFramesRead += framesRead;
framesRemaining -= framesRead;
}
return totalFramesRead;
}
private:
NO_MOVE(WavPackInternal);
//WavpackStreamReader streamReader; //@todo: streaming support
WavpackContext * context; //@todo unique_ptr
AudioData * d;
std::vector<int32_t> internalBuffer;
inline int64_t getTotalSamples() const { return WavpackGetNumSamples(context); }
inline int64_t getLengthInSeconds() const { return getTotalSamples() / WavpackGetSampleRate(context); }
};
//////////////////////
// Public Interface //
//////////////////////
int WavPackDecoder::LoadFromPath(AudioData * data, const std::string & path)
{
WavPackInternal decoder(data, path);
return IOError::NoError;
}
int WavPackDecoder::LoadFromBuffer(AudioData * data, const std::vector<uint8_t> & memory)
{
return IOError::LoadBufferNotImplemented;
}
std::vector<std::string> WavPackDecoder::GetSupportedFileExtensions()
{
return {"wv"};
}
Loading…
Cancel
Save