diff --git a/examples/src/Main.cpp b/examples/src/Main.cpp index 33306f6..fbc3d3b 100644 --- a/examples/src/Main.cpp +++ b/examples/src/Main.cpp @@ -8,9 +8,8 @@ #include "AudioDevice.h" -#include "libnyquist/AudioDecoder.h" -#include "libnyquist/WavEncoder.h" -#include "libnyquist/PostProcess.h" +#include "libnyquist/Decoders.h" +#include "libnyquist/Encoders.h" #include diff --git a/include/libnyquist/AudioDecoder.h b/include/libnyquist/AudioDecoder.h deleted file mode 100644 index c5ed9ee..0000000 --- a/include/libnyquist/AudioDecoder.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef AUDIO_DECODER_H -#define AUDIO_DECODER_H - -#include "Common.h" -#include -#include -#include -#include - -namespace nqr -{ - -struct UnsupportedExtensionEx : public std::runtime_error -{ - UnsupportedExtensionEx() : std::runtime_error("Unsupported file extension") {} -}; - -struct LoadPathNotImplEx : public std::runtime_error -{ - LoadPathNotImplEx() : std::runtime_error("Loading from path not implemented") {} -}; - -struct LoadBufferNotImplEx : public std::runtime_error -{ - LoadBufferNotImplEx() : std::runtime_error("Loading from buffer not implemented") {} -}; - -struct BaseDecoder -{ - virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) = 0; - virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) = 0; - virtual std::vector GetSupportedFileExtensions() = 0; - virtual ~BaseDecoder() {} -}; - -typedef std::pair> DecoderPair; - -class NyquistIO -{ - std::string ParsePathForExtension(const std::string & path) const; - std::shared_ptr GetDecoderForExtension(const std::string & ext); - void BuildDecoderTable(); - void AddDecoderToTable(std::shared_ptr decoder); - std::map> decoderTable; - NO_MOVE(NyquistIO); - -public: - - NyquistIO(); - ~NyquistIO(); - void Load(AudioData * data, const std::string & path); - void Load(AudioData * data, const std::string & extension, const std::vector & buffer); - bool IsFileSupported(const std::string & path) const; -}; - -} // end namespace nqr - -#endif \ No newline at end of file diff --git a/include/libnyquist/Common.h b/include/libnyquist/Common.h index 1eda012..b47bc49 100644 --- a/include/libnyquist/Common.h +++ b/include/libnyquist/Common.h @@ -37,13 +37,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include - -#include "PostProcess.h" -#include "Dither.h" +#include +#include namespace nqr { - + ///////////////// // Util Macros // ///////////////// @@ -256,6 +255,35 @@ inline void hermite_resample(const double rate, const std::vector & input // Conversion Utilities // ////////////////////////// +enum DitherType +{ + DITHER_NONE, + DITHER_TRIANGLE +}; + +class Dither +{ + std::uniform_real_distribution distribution; + std::mt19937 gen; + float prev{ 0.0f }; + DitherType d; +public: + + Dither(DitherType d) : distribution(-0.5f, +0.5f), d(d) {} + + float operator()(float s) + { + if (d == DITHER_TRIANGLE) + { + const float value = distribution(gen); + s = s + value - prev; + prev = value; + return s; + } + else return s; + } +}; + // Signed maxes, defined for readabilty/convenience #define NQR_INT16_MAX 32767.f #define NQR_INT24_MAX 8388608.f @@ -303,6 +331,9 @@ void ConvertToFloat32(float * dst, const int16_t * src, const size_t N, PCMForma void ConvertFromFloat32(uint8_t * dst, const float * src, const size_t N, PCMFormat f, DitherType t = DITHER_NONE); +int GetFormatBitsPerSample(PCMFormat f); +PCMFormat MakeFormatForBits(int bits, bool floatingPt, bool isSigned); + ////////////////////////// // User Data + File Ops // ////////////////////////// @@ -336,9 +367,327 @@ struct NyquistFileBuffer }; NyquistFileBuffer ReadFile(const std::string & pathToFile); + +//////////////////// +// Encoding Utils // +//////////////////// + +struct EncoderParams +{ + int channelCount; + PCMFormat targetFormat; + DitherType dither; +}; + +enum EncoderError +{ + NoError, + InsufficientSampleData, + FileIOError, + UnsupportedSamplerate, + UnsupportedChannelConfiguration, + UnsupportedBitdepth, + UnsupportedChannelMix, + BufferTooBig, +}; + +////////////////////// +// Wav Format Utils // +////////////////////// + +enum WaveFormatCode +{ + FORMAT_UNKNOWN = 0x0, // Unknown Wave Format + FORMAT_PCM = 0x1, // PCM Format + FORMAT_ADPCM = 0x2, // Microsoft ADPCM 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_IMA_ADPCM = 0x11, // IMA ADPCM Format + 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 (also known as block align) + 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 +std::basic_ostream & operator << (std::basic_ostream & 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: -int GetFormatBitsPerSample(PCMFormat f); -PCMFormat MakeFormatForBits(int bits, bool floatingPt, bool isSigned); +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; + } +} + +///////////////////// +// Chunk utilities // +///////////////////// + +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 +} + +inline char * GenerateChunkCodeChar(uint8_t a, uint8_t b, uint8_t c, uint8_t d) +{ + auto chunk = GenerateChunkCode(a, b, c, d); + + char * outArr = new char[4]; + + uint32_t t = 0x000000FF; + + for (size_t i = 0; i < 4; i++) + { + outArr[i] = chunk & t; + chunk >>= 8; + } + return outArr; +} + +inline ChunkHeaderInfo ScanForChunk(const std::vector & fileData, uint32_t chunkMarker) +{ + // D[n] aligned to 16 bytes now + const uint16_t * d = reinterpret_cast(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 }; +}; + +inline WaveChunkHeader MakeWaveHeader(const EncoderParams param, const int sampleRate) +{ + WaveChunkHeader header; + + int bitdepth = GetFormatBitsPerSample(param.targetFormat); + + header.fmt_id = GenerateChunkCode('f', 'm', 't', ' '); + header.chunk_size = 16; + header.format = (param.targetFormat <= PCMFormat::PCM_32) ? WaveFormatCode::FORMAT_PCM : WaveFormatCode::FORMAT_IEEE; + header.channel_count = param.channelCount; + header.sample_rate = sampleRate; + header.data_rate = sampleRate * param.channelCount * (bitdepth / 8); + header.frame_size = param.channelCount * (bitdepth / 8); + header.bit_depth = bitdepth; + + return header; +} + +// @todo expose this in the FLAC API +inline std::map GetFlacQualityTable() +{ + return { + { 0, "0 (Fastest)" }, + { 1, "1" }, + { 2, "2" }, + { 3, "3" }, + { 4, "4" }, + { 5, "5 (Default)" }, + { 6, "6" }, + { 7, "7" }, + { 8, "8 (Highest)" }, + }; +} + +template +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 +void InterleaveChannels(const T * src, T * dest, size_t numFramesPerChannel, size_t numChannels, size_t N) +{ + for (size_t ch = 0; ch < numChannels; ch++) + { + size_t x = ch; + const T * srcChannel = &src[ch * numFramesPerChannel]; + for (size_t i = 0; i < N; i++) + { + dest[x] = srcChannel[i]; + x += numChannels; + } + } +} + +template +void DeinterleaveChannels(const T * src, T * dest, size_t numFramesPerChannel, size_t numChannels, size_t N) +{ + for (size_t ch = 0; ch < numChannels; ch++) + { + size_t x = ch; + T *destChannel = &dest[ch * numFramesPerChannel]; + for (size_t i = 0; i < N; i++) + { + destChannel[i] = (T)src[x]; + x += numChannels; + } + } +} + +template +void StereoToMono(const T * src, T * dest, size_t N) +{ + for (size_t i = 0, j = 0; i < N; i += 2, ++j) + { + dest[j] = (src[i] + src[i + 1]) / 2.0f; + } +} + +template +void MonoToStereo(const T * src, T * dest, size_t N) +{ + for (size_t i = 0, j = 0; i < N; ++i, j += 2) + { + dest[j] = src[i]; + dest[j + 1] = src[i]; + } +} + +inline void TrimSilenceInterleaved(std::vector & buffer, float v, bool fromFront, bool fromEnd) +{ + //@todo implement me! +} } // end namespace nqr diff --git a/include/libnyquist/Decoders.h b/include/libnyquist/Decoders.h new file mode 100644 index 0000000..a9b7c95 --- /dev/null +++ b/include/libnyquist/Decoders.h @@ -0,0 +1,135 @@ +/* +Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef AUDIO_DECODER_H +#define AUDIO_DECODER_H + +#include "Common.h" +#include +#include +#include +#include + +namespace nqr +{ + struct BaseDecoder + { + virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) = 0; + virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) = 0; + virtual std::vector GetSupportedFileExtensions() = 0; + virtual ~BaseDecoder() {} + }; + + typedef std::pair> DecoderPair; + + class NyquistIO + { + std::string ParsePathForExtension(const std::string & path) const; + std::shared_ptr GetDecoderForExtension(const std::string & ext); + void BuildDecoderTable(); + void AddDecoderToTable(std::shared_ptr decoder); + std::map> decoderTable; + + NO_MOVE(NyquistIO); + + public: + + NyquistIO(); + ~NyquistIO(); + void Load(AudioData * data, const std::string & path); + void Load(AudioData * data, const std::string & extension, const std::vector & buffer); + bool IsFileSupported(const std::string & path) const; + }; + + struct UnsupportedExtensionEx : public std::runtime_error { UnsupportedExtensionEx() : std::runtime_error("Unsupported file extension") {} }; + struct LoadPathNotImplEx : public std::runtime_error { LoadPathNotImplEx() : std::runtime_error("Loading from path not implemented") {} }; + struct LoadBufferNotImplEx : public std::runtime_error { LoadBufferNotImplEx() : std::runtime_error("Loading from buffer not implemented") {} }; + + struct WavDecoder final : public nqr::BaseDecoder + { + WavDecoder() = default; + virtual ~WavDecoder() {} + virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; + virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; + virtual std::vector GetSupportedFileExtensions() override final; + }; + + struct WavPackDecoder final : public nqr::BaseDecoder + { + WavPackDecoder() = default; + virtual ~WavPackDecoder() override {}; + virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; + virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; + virtual std::vector GetSupportedFileExtensions() override final; + }; + + struct VorbisDecoder final : public nqr::BaseDecoder + { + VorbisDecoder() = default; + virtual ~VorbisDecoder() override {} + virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; + virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; + virtual std::vector GetSupportedFileExtensions() override final; + }; + + struct OpusDecoder final : public nqr::BaseDecoder + { + OpusDecoder() = default; + virtual ~OpusDecoder() override {} + virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; + virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; + virtual std::vector GetSupportedFileExtensions() override final; + }; + + struct MusepackDecoder final : public nqr::BaseDecoder + { + MusepackDecoder() = default; + virtual ~MusepackDecoder() override {}; + virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; + virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; + virtual std::vector GetSupportedFileExtensions() override final; + }; + + struct Mp3Decoder final : public nqr::BaseDecoder + { + Mp3Decoder() = default; + virtual ~Mp3Decoder() override {}; + virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; + virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; + virtual std::vector GetSupportedFileExtensions() override final; + }; + + struct FlacDecoder final : public nqr::BaseDecoder + { + FlacDecoder() = default; + virtual ~FlacDecoder() override {} + virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; + virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; + virtual std::vector GetSupportedFileExtensions() override final; + }; + +} // end namespace nqr + +#endif diff --git a/include/libnyquist/Dither.h b/include/libnyquist/Dither.h deleted file mode 100644 index 81b26b1..0000000 --- a/include/libnyquist/Dither.h +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef DITHER_OPERATIONS_H -#define DITHER_OPERATIONS_H - -#include - -namespace nqr -{ - -enum DitherType -{ - DITHER_NONE, - DITHER_TRIANGLE -}; - -class Dither -{ - std::uniform_real_distribution distribution; - std::mt19937 rndGen; - float prev = 0.0f; - DitherType d; -public: - - Dither(DitherType d) : distribution(-0.5f, +0.5f), d(d) {} - - float operator()(float s) - { - if (d == DITHER_TRIANGLE) - { - const float value = distribution(rndGen); - s = s + value - prev; - prev = value; - return s; - } - else - { - return s; - } - - } -}; - -} // end namespace nqr - -#endif diff --git a/include/libnyquist/WavEncoder.h b/include/libnyquist/Encoders.h similarity index 86% rename from include/libnyquist/WavEncoder.h rename to include/libnyquist/Encoders.h index 9eb08a5..b8a028d 100644 --- a/include/libnyquist/WavEncoder.h +++ b/include/libnyquist/Encoders.h @@ -23,27 +23,13 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WAVE_ENCODER_H -#define WAVE_ENCODER_H +#ifndef NYQUIST_ENCODERS_H +#define NYQUIST_ENCODERS_H #include "Common.h" -#include "WavDecoder.h" -#include "RiffUtils.h" namespace nqr { - enum EncoderError - { - NoError, - InsufficientSampleData, - FileIOError, - UnsupportedSamplerate, - UnsupportedChannelConfiguration, - UnsupportedBitdepth, - UnsupportedChannelMix, - BufferTooBig, - }; - // A simplistic encoder that takes a buffer of audio, conforms it to the user's // EncoderParams preference, and writes to disk. Be warned, does not support resampling! // @todo support dithering, samplerate conversion, etc. @@ -55,4 +41,4 @@ namespace nqr } // end namespace nqr -#endif +#endif // end NYQUIST_ENCODERS_H diff --git a/include/libnyquist/FlacDecoder.h b/include/libnyquist/FlacDecoder.h deleted file mode 100644 index 9c657c1..0000000 --- a/include/libnyquist/FlacDecoder.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef FLAC_DECODER_H -#define FLAC_DECODER_H - -// http://lists.xiph.org/pipermail/flac-dev/2012-March/003276.html -#define FLAC__NO_DLL - -#include "AudioDecoder.h" -#include - -namespace nqr -{ - -//@todo expose this in API -inline std::map 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 final : public nqr::BaseDecoder -{ - FlacDecoder() = default; - virtual ~FlacDecoder() override {} - virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; - virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; - virtual std::vector GetSupportedFileExtensions() override final; -}; - -} // end namespace nqr - -#endif diff --git a/include/libnyquist/IMA4Util.h b/include/libnyquist/IMA4Util.h deleted file mode 100644 index 77acded..0000000 --- a/include/libnyquist/IMA4Util.h +++ /dev/null @@ -1,141 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef IMA4_UTIL_H -#define IMA4_UTIL_H - -#include "AudioDecoder.h" - -namespace nqr -{ - - struct ADPCMState - { - int frame_size; - int firstDataBlockByte; - int dataSize; - int currentByte; - const uint8_t * inBuffer; - }; - - static const int ima_index_table[16] = - { - -1, -1, -1, -1, // +0 / +3 : - the step - 2, 4, 6, 8, // +4 / +7 : + the step - -1, -1, -1, -1, // -0 / -3 : - the step - 2, 4, 6, 8, // -4 / -7 : + the step - }; - - static inline int ima_clamp_index(int index) - { - if (index < 0) return 0; - else if (index > 88) return 88; - return index; - } - - static inline int16_t ima_clamp_predict(int16_t predict) - { - if (predict < -32768) return -32768; - else if (predict > 32767) return 32767; - return predict; - } - - static const int ima_step_table[89] = - { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, - 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, - 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, - 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, - 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, - 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, - 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, - 27086, 29794, 32767 - }; - - // Decodes an IMA ADPCM nibble to a 16 bit pcm sample - static inline int16_t decode_nibble(uint8_t nibble, int16_t & p, int & s) - { - // Compute a delta to add to the predictor value - int diff = ima_step_table[s] >> 3; - if (nibble & 4) diff += ima_step_table[s]; - if (nibble & 2) diff += ima_step_table[s] >> 1; - if (nibble & 1) diff += ima_step_table[s] >> 2; - - // Sign - if (nibble & 8) diff = -diff; - - // Add delta - p += diff; - - s += ima_index_table[nibble]; - s = ima_clamp_index(s); - - return ima_clamp_predict(p); - } - - void decode_ima_adpcm(ADPCMState & state, int16_t * outBuffer, uint32_t num_channels) - { - const uint8_t * data = state.inBuffer; - - // Loop over the interleaved channels - for (uint32_t ch = 0; ch < num_channels; ch++) - { - const int byteOffset = ch * 4; - - // Header Structure: - // Byte0: packed low byte of the initial predictor - // Byte1: packed high byte of the initial predictor - // Byte2: initial step index - // Byte3: Reserved empty value - int16_t predictor = ((int16_t)data[byteOffset + 1] << 8) | data[byteOffset]; - int stepIndex = data[byteOffset + 2]; - - uint8_t reserved = data[byteOffset + 3]; - if (reserved != 0) throw std::runtime_error("adpcm decode error"); - - int byteIdx = num_channels * 4 + byteOffset; //the byte index of the first data word for this channel - int idx = ch; - - // Decode nibbles of the remaining data - while (byteIdx < state.frame_size) - { - for (int j = 0; j < 4; j++) - { - outBuffer[idx] = decode_nibble(data[byteIdx] & 0xf, predictor, stepIndex); // low nibble - idx += num_channels; - outBuffer[idx] = decode_nibble(data[byteIdx] >> 4, predictor, stepIndex); // high nibble - idx += num_channels; - byteIdx++; - } - byteIdx += (num_channels - 1) << 2; // Jump to the next data word for the current channel - } - - } - - } - -} // end namespace nqr - -#endif diff --git a/include/libnyquist/Mp3Decoder.h b/include/libnyquist/Mp3Decoder.h deleted file mode 100644 index d934250..0000000 --- a/include/libnyquist/Mp3Decoder.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef MP3_DECODER_H -#define MP3_DECODER_H - -#include "AudioDecoder.h" - -namespace nqr -{ - -struct Mp3Decoder final : public nqr::BaseDecoder -{ - Mp3Decoder() = default; - virtual ~Mp3Decoder() override {}; - virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; - virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; - virtual std::vector GetSupportedFileExtensions() override final; -}; - -} // end namespace nqr - -#endif \ No newline at end of file diff --git a/include/libnyquist/MusepackDecoder.h b/include/libnyquist/MusepackDecoder.h deleted file mode 100644 index 47e254f..0000000 --- a/include/libnyquist/MusepackDecoder.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef MUSEPACK_DECODER_H -#define MUSEPACK_DECODER_H - -#include "AudioDecoder.h" - -namespace nqr -{ - -struct MusepackDecoder final : public nqr::BaseDecoder -{ - MusepackDecoder() = default; - virtual ~MusepackDecoder() override {}; - virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; - virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; - virtual std::vector GetSupportedFileExtensions() override final; -}; - -} // end namespace nqr - -#endif \ No newline at end of file diff --git a/include/libnyquist/OpusDecoder.h b/include/libnyquist/OpusDecoder.h deleted file mode 100644 index 7f4de8b..0000000 --- a/include/libnyquist/OpusDecoder.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef OPUS_DECODER_H -#define OPUS_DECODER_H - -#include "AudioDecoder.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 final : public nqr::BaseDecoder -{ - OpusDecoder() = default; - virtual ~OpusDecoder() override {} - virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; - virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; - virtual std::vector GetSupportedFileExtensions() override final; -}; - -} // end namespace nqr - -#endif diff --git a/include/libnyquist/PostProcess.h b/include/libnyquist/PostProcess.h deleted file mode 100644 index 8bb6270..0000000 --- a/include/libnyquist/PostProcess.h +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef POSTPROCESS_H -#define POSTPROCESS_H - -#include - -namespace nqr -{ - -template -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 -void InterleaveChannels(const T * src, T * dest, size_t numFramesPerChannel, size_t numChannels, size_t N) -{ - for (size_t ch = 0; ch < numChannels; ch++) - { - size_t x = ch; - const T * srcChannel = &src[ch * numFramesPerChannel]; - for(size_t i = 0; i < N; i++) - { - dest[x] = srcChannel[i]; - x += numChannels; - } - } -} - -template -void DeinterleaveChannels(const T * src, T * dest, size_t numFramesPerChannel, size_t numChannels, size_t N) -{ - for(size_t ch = 0; ch < numChannels; ch++) - { - size_t x = ch; - T *destChannel = &dest[ch * numFramesPerChannel]; - for (size_t i = 0; i < N; i++) - { - destChannel[i] = (T) src[x]; - x += numChannels; - } - } -} - -template -void StereoToMono(const T * src, T * dest, size_t N) -{ - for (size_t i = 0, j = 0; i < N; i += 2, ++j) - { - dest[j] = (src[i] + src[i + 1]) / 2.0f; - } -} - -template -void MonoToStereo(const T * src, T * dest, size_t N) -{ - for(size_t i = 0, j = 0; i < N; ++i, j += 2) - { - dest[j] = src[i]; - dest[j + 1] = src[i]; - } -} - -inline void TrimSilenceInterleaved(std::vector & buffer, float v, bool fromFront, bool fromEnd) -{ - //@todo implement me! -} - -} // end namespace nqr - -#endif diff --git a/include/libnyquist/RiffUtils.h b/include/libnyquist/RiffUtils.h deleted file mode 100644 index e56b807..0000000 --- a/include/libnyquist/RiffUtils.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef RIFF_UTILS_H -#define RIFF_UTILS_H - -#include "Common.h" -#include "WavDecoder.h" -#include "Dither.h" - -namespace nqr -{ - -///////////////////// -// Chunk utilities // -///////////////////// - -struct EncoderParams -{ - int channelCount; - PCMFormat targetFormat; - DitherType dither; -}; - -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 -} - -inline char * GenerateChunkCodeChar(uint8_t a, uint8_t b, uint8_t c, uint8_t d) -{ -auto chunk = GenerateChunkCode(a, b, c, d); - - char * outArr = new char[4]; - - uint32_t t = 0x000000FF; - - for(size_t i = 0; i < 4; i++) - { - outArr[i] = chunk & t; - chunk >>= 8; - } - return outArr; -} - -ChunkHeaderInfo ScanForChunk(const std::vector & fileData, uint32_t chunkMarker); - -WaveChunkHeader MakeWaveHeader(const EncoderParams param, const int sampleRate); - -} // end namespace nqr - -#endif diff --git a/include/libnyquist/VorbisDecoder.h b/include/libnyquist/VorbisDecoder.h deleted file mode 100644 index 24d0173..0000000 --- a/include/libnyquist/VorbisDecoder.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef VORBIS_DECODER_H -#define VORBIS_DECODER_H - -#include "AudioDecoder.h" - -namespace nqr -{ - -struct VorbisDecoder final : public nqr::BaseDecoder -{ - VorbisDecoder() = default; - virtual ~VorbisDecoder() override {} - virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; - virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; - virtual std::vector GetSupportedFileExtensions() override final; -}; - -} // end namespace nqr -#endif \ No newline at end of file diff --git a/include/libnyquist/WavDecoder.h b/include/libnyquist/WavDecoder.h deleted file mode 100644 index bb519b4..0000000 --- a/include/libnyquist/WavDecoder.h +++ /dev/null @@ -1,182 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef 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_ADPCM = 0x2, // Microsoft ADPCM 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_IMA_ADPCM = 0x11, // IMA ADPCM Format - 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 (also known as block align) - 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 -std::basic_ostream & operator << (std::basic_ostream & 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 final : public nqr::BaseDecoder -{ - WavDecoder() = default; - virtual ~WavDecoder() {} - virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; - virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; - virtual std::vector GetSupportedFileExtensions() override final; -}; - -} // end namespace nqr - -#endif diff --git a/include/libnyquist/WavPackDecoder.h b/include/libnyquist/WavPackDecoder.h deleted file mode 100644 index 42498cf..0000000 --- a/include/libnyquist/WavPackDecoder.h +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef WAVEPACK_DECODER_H -#define WAVEPACK_DECODER_H - -#include "AudioDecoder.h" - -namespace nqr -{ - -struct WavPackDecoder final : public nqr::BaseDecoder -{ - WavPackDecoder() = default; - virtual ~WavPackDecoder() override {}; - virtual void LoadFromPath(nqr::AudioData * data, const std::string & path) override final; - virtual void LoadFromBuffer(nqr::AudioData * data, const std::vector & memory) override final; - virtual std::vector GetSupportedFileExtensions() override final; -}; - -} // end namespace nqr - -#endif diff --git a/src/AudioDecoder.cpp b/src/AudioDecoder.cpp index e33c58e..489d56b 100644 --- a/src/AudioDecoder.cpp +++ b/src/AudioDecoder.cpp @@ -23,15 +23,7 @@ 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. */ -#include "AudioDecoder.h" - -#include "WavDecoder.h" -#include "WavPackDecoder.h" -#include "FlacDecoder.h" -#include "VorbisDecoder.h" -#include "OpusDecoder.h" -#include "MusepackDecoder.h" -#include "Mp3Decoder.h" +#include "Decoders.h" using namespace nqr; diff --git a/src/Common.cpp b/src/Common.cpp index 404af9a..5204ada 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -24,10 +24,108 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "Common.h" +#include "Decoders.h" #include using namespace nqr; +NyquistIO::NyquistIO() { BuildDecoderTable(); } +NyquistIO::~NyquistIO() { } + +void NyquistIO::Load(AudioData * data, const std::string & path) +{ + if (IsFileSupported(path)) + { + if (decoderTable.size()) + { + auto fileExtension = ParsePathForExtension(path); + auto decoder = GetDecoderForExtension(fileExtension); + + try + { + decoder->LoadFromPath(data, path); + } + catch (const std::exception & e) + { + std::cerr << "Caught internal exception: " << e.what() << std::endl; + } + + } + else throw std::runtime_error("No available decoders."); + } + else + { + throw UnsupportedExtensionEx(); + } +} + +void NyquistIO::Load(AudioData * data, const std::string & extension, const std::vector & buffer) +{ + if (decoderTable.find(extension) == decoderTable.end()) + { + throw UnsupportedExtensionEx(); + } + + if (decoderTable.size()) + { + auto decoder = GetDecoderForExtension(extension); + try + { + decoder->LoadFromBuffer(data, buffer); + } + catch (const std::exception & e) + { + std::cerr << "Caught internal exception: " << e.what() << std::endl; + } + } + else + { + throw std::runtime_error("No available decoders."); + } +} + +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 NyquistIO::GetDecoderForExtension(const std::string & ext) +{ + if (decoderTable.size()) return decoderTable[ext]; + else throw std::runtime_error("No available decoders."); + return nullptr; +} + +void NyquistIO::AddDecoderToTable(std::shared_ptr decoder) +{ + auto supportedExtensions = decoder->GetSupportedFileExtensions(); + + for (const auto ext : supportedExtensions) + { + if (decoderTable.count(ext) >= 1) throw std::runtime_error("decoder already exists for extension."); + decoderTable.insert(DecoderPair(ext, decoder)); + } +} + +void NyquistIO::BuildDecoderTable() +{ + AddDecoderToTable(std::make_shared()); + AddDecoderToTable(std::make_shared()); + AddDecoderToTable(std::make_shared()); + AddDecoderToTable(std::make_shared()); + AddDecoderToTable(std::make_shared()); + AddDecoderToTable(std::make_shared()); + AddDecoderToTable(std::make_shared()); +} + NyquistFileBuffer nqr::ReadFile(const std::string & pathToFile) { //std::cout << "[Debug] Open: " << pathToFile << std::endl; @@ -105,7 +203,7 @@ void nqr::ConvertToFloat32(float * dst, const uint8_t * src, const size_t N, PCM else if (f == PCM_FLT) { - memcpy(dst, src, N * sizeof(float)); + std::memcpy(dst, src, N * sizeof(float)); /* const float * dataPtr = reinterpret_cast(src); for (size_t i = 0; i < N; ++i) dst[i] = (float) Read32(dataPtr[i]); */ diff --git a/src/WavEncoder.cpp b/src/Encoders.cpp similarity index 99% rename from src/WavEncoder.cpp rename to src/Encoders.cpp index 355af9e..c0a590c 100644 --- a/src/WavEncoder.cpp +++ b/src/Encoders.cpp @@ -23,7 +23,7 @@ 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. */ -#include "WavEncoder.h" +#include "Encoders.h" #include using namespace nqr; diff --git a/src/FlacDecoder.cpp b/src/FlacDecoder.cpp index 575cbb3..979e765 100644 --- a/src/FlacDecoder.cpp +++ b/src/FlacDecoder.cpp @@ -23,22 +23,24 @@ 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. */ -#include "FlacDecoder.h" +#include "Decoders.h" + +// http://lists.xiph.org/pipermail/flac-dev/2012-March/003276.html +#define FLAC__NO_DLL + #include "FLAC/all.h" #include "FLAC/stream_decoder.h" -#include "AudioDecoder.h" #include using namespace nqr; // FLAC is a big-endian format. All values are unsigned. - class FlacDecoderInternal { - + public: - + FlacDecoderInternal(AudioData * d, const std::string & filepath) : d(d) { decoderInternal = FLAC__stream_decoder_new(); diff --git a/src/Mp3Decoder.cpp b/src/Mp3Decoder.cpp index f4609ab..53957ad 100644 --- a/src/Mp3Decoder.cpp +++ b/src/Mp3Decoder.cpp @@ -23,7 +23,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "Mp3Decoder.h" +#include "Decoders.h" using namespace nqr; @@ -37,30 +37,22 @@ using namespace nqr; #include "minimp3/minimp3.h" #include "minimp3/minimp3_ex.h" -class Mp3Internal +void mp3_decode_internal(AudioData * d, const std::vector & fileData) { - NO_MOVE(Mp3Internal); - AudioData * d; + mp3dec_t mp3d; + mp3dec_file_info_t info; + mp3dec_load_buf(&mp3d, (const uint8_t*)fileData.data(), fileData.size(), &info, 0, 0); -public: - - Mp3Internal(AudioData * d, const std::vector & fileData) : d(d) - { - mp3dec_t mp3d; - mp3dec_file_info_t info; - mp3dec_load_buf(&mp3d, (const uint8_t*) fileData.data(), fileData.size(), &info, 0, 0); + d->sampleRate = info.hz; + d->channelCount = info.channels; + d->sourceFormat = MakeFormatForBits(32, true, false); + d->lengthSeconds = ((float)info.samples / (float)d->channelCount) / (float)d->sampleRate; - d->sampleRate = info.hz; - d->channelCount = info.channels; - d->sourceFormat = MakeFormatForBits(32, true, false); - d->lengthSeconds = ((float) info.samples / (float)d->channelCount) / (float)d->sampleRate; + if (info.samples == 0) throw std::runtime_error("mp3: could not read any data"); - if (info.samples == 0) throw std::runtime_error("mp3: could not read any data"); - - d->samples.resize(info.samples); - std::memcpy(d->samples.data(), info.buffer, sizeof(float) * info.samples); - } -}; + d->samples.resize(info.samples); + std::memcpy(d->samples.data(), info.buffer, sizeof(float) * info.samples); +} ////////////////////// // Public Interface // @@ -69,12 +61,12 @@ public: void Mp3Decoder::LoadFromPath(AudioData * data, const std::string & path) { auto fileBuffer = nqr::ReadFile(path); - Mp3Internal decoder(data, fileBuffer.buffer); + mp3_decode_internal(data, fileBuffer.buffer); } void Mp3Decoder::LoadFromBuffer(AudioData * data, const std::vector & memory) { - Mp3Internal decoder(data, memory); + mp3_decode_internal(data, memory); } std::vector Mp3Decoder::GetSupportedFileExtensions() diff --git a/src/MusepackDecoder.cpp b/src/MusepackDecoder.cpp index d8fd406..90ea049 100644 --- a/src/MusepackDecoder.cpp +++ b/src/MusepackDecoder.cpp @@ -23,7 +23,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "MusepackDecoder.h" +#include "Decoders.h" using namespace nqr; diff --git a/src/OpusDecoder.cpp b/src/OpusDecoder.cpp index 2256f94..ee48c37 100644 --- a/src/OpusDecoder.cpp +++ b/src/OpusDecoder.cpp @@ -23,13 +23,19 @@ 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. */ -#include "OpusDecoder.h" +#include "Decoders.h" #include "opus/opusfile/include/opusfile.h" using namespace nqr; static const int OPUS_SAMPLE_RATE = 48000; +// 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 + class OpusDecoderInternal { diff --git a/src/RiffUtils.cpp b/src/RiffUtils.cpp deleted file mode 100644 index 4b96555..0000000 --- a/src/RiffUtils.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright (c) 2019, Dimitri Diakopoulos All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "RiffUtils.h" - -using namespace nqr; - -ChunkHeaderInfo nqr::ScanForChunk(const std::vector & fileData, uint32_t chunkMarker) -{ - // D[n] aligned to 16 bytes now - const uint16_t * d = reinterpret_cast(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}; -}; - -WaveChunkHeader nqr::MakeWaveHeader(const EncoderParams param, const int sampleRate) -{ - WaveChunkHeader header; - - int bitdepth = GetFormatBitsPerSample(param.targetFormat); - - header.fmt_id = GenerateChunkCode('f', 'm', 't', ' '); - header.chunk_size = 16; - header.format = (param.targetFormat <= PCMFormat::PCM_32) ? WaveFormatCode::FORMAT_PCM : WaveFormatCode::FORMAT_IEEE; - header.channel_count = param.channelCount; - header.sample_rate = sampleRate; - header.data_rate = sampleRate * param.channelCount * (bitdepth / 8); - header.frame_size = param.channelCount * (bitdepth / 8); - header.bit_depth = bitdepth; - - return header; -} diff --git a/src/VorbisDecoder.cpp b/src/VorbisDecoder.cpp index 3d8bd4a..fe4218b 100644 --- a/src/VorbisDecoder.cpp +++ b/src/VorbisDecoder.cpp @@ -23,7 +23,7 @@ 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. */ -#include "VorbisDecoder.h" +#include "Decoders.h" #include "libvorbis/include/vorbis/vorbisfile.h" using namespace nqr; diff --git a/src/WavDecoder.cpp b/src/WavDecoder.cpp index d04e5de..467512a 100644 --- a/src/WavDecoder.cpp +++ b/src/WavDecoder.cpp @@ -23,13 +23,116 @@ 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. */ -#include "WavDecoder.h" -#include "RiffUtils.h" -#include "IMA4Util.h" +#include "Decoders.h" #include using namespace nqr; +struct ADPCMState +{ + int frame_size; + int firstDataBlockByte; + int dataSize; + int currentByte; + const uint8_t * inBuffer; +}; + +static const int ima_index_table[16] = +{ + -1, -1, -1, -1, // +0 / +3 : - the step + 2, 4, 6, 8, // +4 / +7 : + the step + -1, -1, -1, -1, // -0 / -3 : - the step + 2, 4, 6, 8, // -4 / -7 : + the step +}; + +static inline int ima_clamp_index(int index) +{ + if (index < 0) return 0; + else if (index > 88) return 88; + return index; +} + +static inline int16_t ima_clamp_predict(int16_t predict) +{ + if (predict < -32768) return -32768; + else if (predict > 32767) return 32767; + return predict; +} + +static const int ima_step_table[89] = +{ + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, + 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, + 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, + 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, + 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, + 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, + 27086, 29794, 32767 +}; + +// Decodes an IMA ADPCM nibble to a 16 bit pcm sample +static inline int16_t decode_nibble(uint8_t nibble, int16_t & p, int & s) +{ + // Compute a delta to add to the predictor value + int diff = ima_step_table[s] >> 3; + if (nibble & 4) diff += ima_step_table[s]; + if (nibble & 2) diff += ima_step_table[s] >> 1; + if (nibble & 1) diff += ima_step_table[s] >> 2; + + // Sign + if (nibble & 8) diff = -diff; + + // Add delta + p += diff; + + s += ima_index_table[nibble]; + s = ima_clamp_index(s); + + return ima_clamp_predict(p); +} + +void decode_ima_adpcm(ADPCMState & state, int16_t * outBuffer, uint32_t num_channels) +{ + const uint8_t * data = state.inBuffer; + + // Loop over the interleaved channels + for (uint32_t ch = 0; ch < num_channels; ch++) + { + const int byteOffset = ch * 4; + + // Header Structure: + // Byte0: packed low byte of the initial predictor + // Byte1: packed high byte of the initial predictor + // Byte2: initial step index + // Byte3: Reserved empty value + int16_t predictor = ((int16_t)data[byteOffset + 1] << 8) | data[byteOffset]; + int stepIndex = data[byteOffset + 2]; + + uint8_t reserved = data[byteOffset + 3]; + if (reserved != 0) throw std::runtime_error("adpcm decode error"); + + int byteIdx = num_channels * 4 + byteOffset; //the byte index of the first data word for this channel + int idx = ch; + + // Decode nibbles of the remaining data + while (byteIdx < state.frame_size) + { + for (int j = 0; j < 4; j++) + { + outBuffer[idx] = decode_nibble(data[byteIdx] & 0xf, predictor, stepIndex); // low nibble + idx += num_channels; + outBuffer[idx] = decode_nibble(data[byteIdx] >> 4, predictor, stepIndex); // high nibble + idx += num_channels; + byteIdx++; + } + byteIdx += (num_channels - 1) << 2; // Jump to the next data word for the current channel + } + + } + +} + ////////////////////// // Public Interface // ////////////////////// @@ -117,10 +220,7 @@ void WavDecoder::LoadFromBuffer(AudioData * data, const std::vector & m bool grabExtensibleData = false; bool adpcmEncoded = false; - if (wavHeader.format == WaveFormatCode::FORMAT_PCM) - { - } - else if (wavHeader.format == WaveFormatCode::FORMAT_IEEE) + if (wavHeader.format == WaveFormatCode::FORMAT_IEEE) { scanForFact = true; } @@ -131,8 +231,7 @@ void WavDecoder::LoadFromBuffer(AudioData * data, const std::vector & m } 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; + // Used when (1) PCM data has more than 16 bits; (2) channels > 2; (3) bits/sample !== container size; (4) channel/speaker mapping specified; scanForFact = true; grabExtensibleData = true; } diff --git a/src/WavPackDecoder.cpp b/src/WavPackDecoder.cpp index a11dddf..203bb7e 100644 --- a/src/WavPackDecoder.cpp +++ b/src/WavPackDecoder.cpp @@ -23,7 +23,7 @@ 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. */ -#include "WavPackDecoder.h" +#include "Decoders.h" #include "wavpack.h" #include