commit 28c6ce8d1f98c198f5403f7139dee5597f8e32da
parent afd39ce67cdf54297dfbb71e3dc2c6bf8c9b7dbc
Author: d.levin256@gmail.com <d.levin256@gmail.com>
Date: Thu, 6 Dec 2018 02:13:21 +0000
IO refactoring
Diffstat:
16 files changed, 10663 insertions(+), 354 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -44,6 +44,7 @@ build64/
cmake-build-debug/
cmake-build-release/
build-*/
+build_*/
# test directory
svg/
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -48,6 +48,9 @@ set(KFR_DFT_SRC
${CMAKE_SOURCE_DIR}/include/kfr/dft/impl/dft-impl-f64.cpp
${CMAKE_SOURCE_DIR}/include/kfr/dft/impl/convolution-impl.cpp)
+set(KFR_IO_SRC
+ ${CMAKE_SOURCE_DIR}/include/kfr/io/impl/audiofile-impl.cpp)
+
if (ENABLE_TESTS)
if (IOS)
@@ -98,3 +101,7 @@ target_include_directories(kfr INTERFACE include)
add_library(kfr_dft ${KFR_DFT_SRC})
target_link_libraries(kfr_dft kfr)
+
+add_library(kfr_io ${KFR_IO_SRC})
+target_link_libraries(kfr_io kfr)
+target_compile_definitions(kfr_io PUBLIC KFR_ENABLE_FLAC=1)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
@@ -31,7 +31,7 @@ add_executable(fir fir.cpp)
target_link_libraries(fir kfr)
add_executable(sample_rate_conversion sample_rate_conversion.cpp)
-target_link_libraries(sample_rate_conversion kfr)
+target_link_libraries(sample_rate_conversion kfr kfr_io)
add_executable(dft dft.cpp)
target_link_libraries(dft kfr kfr_dft)
diff --git a/examples/sample_rate_conversion.cpp b/examples/sample_rate_conversion.cpp
@@ -30,10 +30,11 @@ int main()
const size_t destsize = r(resampled.data(), swept_sine);
univector<i32> i32data = clamp(resampled.truncate(destsize) * i32max, -i32max, +i32max);
- univector2d<i32> data = { i32data };
-
- auto wr = sequential_file_writer("audio_high_quality.wav");
- audio_encode(wr, data, audioformat(data, output_sr));
+ {
+ audio_writer_wav<i32> writer(open_file_for_writing(KFR_FILEPATH("audio_high_quality.wav")),
+ audio_format{ 2, audio_sample_type::i32, output_sr });
+ writer.write(i32data.data(), i32data.size());
+ }
plot_save("audio_high_quality", "audio_high_quality.wav", "");
}
@@ -45,10 +46,11 @@ int main()
const size_t destsize = r(resampled.data(), swept_sine);
univector<i32> i32data = clamp(resampled.truncate(destsize) * i32max, -i32max, +i32max);
- univector2d<i32> data = { i32data };
-
- auto wr = sequential_file_writer("audio_normal_quality.wav");
- audio_encode(wr, data, audioformat(data, output_sr));
+ {
+ audio_writer_wav<i32> writer(open_file_for_writing(KFR_FILEPATH("audio_normal_quality.wav")),
+ audio_format{ 2, audio_sample_type::i32, output_sr });
+ writer.write(i32data.data(), i32data.size());
+ }
plot_save("audio_normal_quality", "audio_normal_quality.wav", "");
}
@@ -60,10 +62,11 @@ int main()
const size_t destsize = r(resampled.data(), swept_sine);
univector<i32> i32data = clamp(resampled.truncate(destsize) * i32max, -i32max, +i32max);
- univector2d<i32> data = { i32data };
-
- auto wr = sequential_file_writer("audio_low_quality.wav");
- audio_encode(wr, data, audioformat(data, output_sr));
+ {
+ audio_writer_wav<i32> writer(open_file_for_writing(KFR_FILEPATH("audio_low_quality.wav")),
+ audio_format{ 2, audio_sample_type::i32, output_sr });
+ writer.write(i32data.data(), i32data.size());
+ }
plot_save("audio_low_quality", "audio_low_quality.wav", "");
}
@@ -75,10 +78,11 @@ int main()
const size_t destsize = r(resampled.data(), swept_sine);
univector<i32> i32data = clamp(resampled.truncate(destsize) * i32max, -i32max, +i32max);
- univector2d<i32> data = { i32data };
-
- auto wr = sequential_file_writer("audio_draft_quality.wav");
- audio_encode(wr, data, audioformat(data, output_sr));
+ {
+ audio_writer_wav<i32> writer(open_file_for_writing(KFR_FILEPATH("audio_draft_quality.wav")),
+ audio_format{ 2, audio_sample_type::i32, output_sr });
+ writer.write(i32data.data(), i32data.size());
+ }
plot_save("audio_draft_quality", "audio_draft_quality.wav", "");
}
diff --git a/include/kfr/base/conversion.hpp b/include/kfr/base/conversion.hpp
@@ -31,49 +31,137 @@
namespace kfr
{
+enum class audio_sample_type
+{
+ unknown,
+ i8,
+ i16,
+ i24,
+ i32,
+ i64,
+ f32,
+ f64,
+
+ first_float = f32
+};
+
+inline constexpr size_t audio_sample_sizeof(audio_sample_type type)
+{
+ switch (type)
+ {
+ case audio_sample_type::i8:
+ return 1;
+ case audio_sample_type::i16:
+ return 2;
+ case audio_sample_type::i32:
+ case audio_sample_type::f32:
+ return 4;
+ case audio_sample_type::i64:
+ case audio_sample_type::f64:
+ return 8;
+ default:
+ return 0;
+ }
+}
+
+inline constexpr size_t audio_sample_bit_depth(audio_sample_type type)
+{
+ return audio_sample_sizeof(type) * 8;
+}
+
+using audio_sample_type_clist =
+ cvals_t<audio_sample_type, audio_sample_type::i8, audio_sample_type::i16, audio_sample_type::i24,
+ audio_sample_type::i32, audio_sample_type::i64, audio_sample_type::f32, audio_sample_type::f64>;
+
+template <audio_sample_type type>
+struct audio_sample_get_type;
+
+template <>
+struct audio_sample_get_type<audio_sample_type::i8>
+{
+ using type = i8;
+};
+template <>
+struct audio_sample_get_type<audio_sample_type::i16>
+{
+ using type = i16;
+};
+template <>
+struct audio_sample_get_type<audio_sample_type::i24>
+{
+ using type = i24;
+};
+template <>
+struct audio_sample_get_type<audio_sample_type::i32>
+{
+ using type = i32;
+};
+template <>
+struct audio_sample_get_type<audio_sample_type::i64>
+{
+ using type = i64;
+};
+template <>
+struct audio_sample_get_type<audio_sample_type::f32>
+{
+ using type = f32;
+};
+template <>
+struct audio_sample_get_type<audio_sample_type::f64>
+{
+ using type = f64;
+};
+
template <typename T>
struct audio_sample_traits;
template <>
struct audio_sample_traits<i8>
{
- constexpr static f32 scale = 127.f;
+ constexpr static f32 scale = 127.f;
+ constexpr static audio_sample_type type = audio_sample_type::i8;
};
template <>
struct audio_sample_traits<i16>
{
- constexpr static f32 scale = 32767.f;
+ constexpr static f32 scale = 32767.f;
+ constexpr static audio_sample_type type = audio_sample_type::i16;
};
template <>
struct audio_sample_traits<i24>
{
- constexpr static f32 scale = 8388607.f;
+ constexpr static f32 scale = 8388607.f;
+ constexpr static audio_sample_type type = audio_sample_type::i24;
};
template <>
struct audio_sample_traits<i32>
{
- constexpr static f64 scale = 2147483647.0;
+ constexpr static f64 scale = 2147483647.0;
+ constexpr static audio_sample_type type = audio_sample_type::i32;
};
template <>
struct audio_sample_traits<i64>
{
- constexpr static f64 scale = 9223372036854775807.0;
+ constexpr static f64 scale = 9223372036854775807.0;
+ constexpr static audio_sample_type type = audio_sample_type::i64;
};
template <>
struct audio_sample_traits<f32>
{
- constexpr static f32 scale = 1;
+ constexpr static f32 scale = 1;
+ constexpr static audio_sample_type type = audio_sample_type::f32;
};
template <>
struct audio_sample_traits<f64>
{
- constexpr static f64 scale = 1;
+ constexpr static f64 scale = 1;
+ constexpr static audio_sample_type type = audio_sample_type::f64;
};
template <typename Tout, typename Tin, typename Tout_traits = audio_sample_traits<Tout>,
@@ -122,4 +210,22 @@ void convert(Tout* out, const Tin* in, size_t size)
out[i] = convert_sample<Tout, Tin, Tout_traits, Tin_traits>(in[i]);
}
}
+
+template <typename Tout, typename Tout_traits = audio_sample_traits<Tout>>
+void convert(Tout* out, const void* in, audio_sample_type in_type, size_t size)
+{
+ cswitch(audio_sample_type_clist{}, in_type, [&](auto t) {
+ using type = typename audio_sample_get_type<val_of(t)>::type;
+ convert(out, reinterpret_cast<const type*>(in), size);
+ });
+}
+
+template <typename Tin, typename Tin_traits = audio_sample_traits<Tin>>
+void convert(void* out, audio_sample_type out_type, const Tin* in, size_t size)
+{
+ cswitch(audio_sample_type_clist{}, out_type, [&](auto t) {
+ using type = typename audio_sample_get_type<val_of(t)>::type;
+ convert(reinterpret_cast<type*>(out), in, size);
+ });
+}
} // namespace kfr
diff --git a/include/kfr/io/audiofile.hpp b/include/kfr/io/audiofile.hpp
@@ -26,358 +26,286 @@
#pragma once
#include "../base/basic_expressions.hpp"
+#include "../base/conversion.hpp"
#include "../base/univector.hpp"
#include "../base/vec.hpp"
#include "../cometa/ctti.hpp"
#include "file.hpp"
+#ifndef KFR_ENABLE_WAV
+#define KFR_ENABLE_WAV 1
+#endif
+#ifndef KFR_ENABLE_FLAC
+#define KFR_ENABLE_FLAC 0
+#endif
+
+#if KFR_ENABLE_WAV
+#define DR_WAV_NO_STDIO
+#define DR_WAV_NO_CONVERSION_API
+#include "dr/dr_wav.h"
+#endif
+#if KFR_ENABLE_FLAC
+#define DR_FLAC_NO_STDIO
+#define DR_FLAC_NO_CONVERSION_API
+#include "dr/dr_flac.h"
+#endif
+
namespace kfr
{
-template <typename Tout, typename Tin, size_t Tag1, size_t Tag2, typename E1>
-void write_interleaved(E1&& dest, const univector2d<Tin, Tag1, Tag2>& src)
+struct audio_format
{
- const size_t channels = src.size();
- if (channels == 1)
- {
- process(std::forward<E1>(dest), src[0]);
- }
- else if (channels == 2)
- {
- process(std::forward<E1>(dest), pack(src[0], src[1]), 0, infinite_size, nullptr, nullptr,
- csize_t<2>());
- }
- else
- {
- const size_t size = src[0].size();
- internal::expression_writer<Tout, E1> wr = writer<Tout>(std::forward<E1>(dest));
- for (size_t i = 0; i < size; i++)
- for (size_t ch = 0; ch < channels; ch++)
- wr.write(src[ch][i]);
- }
-}
+ size_t channels = 2;
+ audio_sample_type type = audio_sample_type::i16;
+ fmax samplerate = 44100;
+ bool use_w64 = false;
+};
-enum class audiodatatype
+struct audio_format_and_length : audio_format
{
- unknown,
- i16,
- i24,
- i24a32,
- i32,
- f32,
- f64
+ using audio_format::audio_format;
+ audio_format_and_length(const audio_format& fmt) : audio_format(fmt) {}
+
+ imax length = 0; // in samples
};
+template <typename T>
+using audio_reader = abstract_reader<T>;
+template <typename T>
+using audio_writer = abstract_writer<T>;
+
namespace internal
{
-template <typename T>
-constexpr range<fmax> audio_range()
+#if KFR_ENABLE_WAV
+static size_t drwav_writer_write_proc(abstract_writer<void>* file, const void* pData, size_t bytesToWrite)
{
- return { -std::numeric_limits<T>::max(), std::numeric_limits<T>::max() };
+ return file->write(pData, bytesToWrite);
}
-
-template <>
-constexpr range<fmax> audio_range<f32>()
+static drwav_bool32 drwav_writer_seek_proc(abstract_writer<void>* file, int offset, drwav_seek_origin origin)
{
- return { -1.0, +1.0 };
+ return file->seek(offset, origin == drwav_seek_origin_start ? seek_origin::begin : seek_origin::current);
}
-
-template <>
-constexpr range<fmax> audio_range<f64>()
+static size_t drwav_reader_read_proc(abstract_reader<void>* file, void* pBufferOut, size_t bytesToRead)
{
- return { -1.0, +1.0 };
+ return file->read(pBufferOut, bytesToRead);
}
-
-inline size_t get_audiobitdepth(audiodatatype type)
+static drwav_bool32 drwav_reader_seek_proc(abstract_reader<void>* file, int offset, drwav_seek_origin origin)
{
- constexpr size_t bits[]{ 0, 16, 24, 24, 32, 32, 64 };
- return static_cast<size_t>(type) < 7 ? bits[static_cast<size_t>(type)] : 0;
+ return file->seek(offset, origin == drwav_seek_origin_start ? seek_origin::begin : seek_origin::current);
}
-
-template <typename T>
-inline audiodatatype get_audiodatatype()
+#endif
+#if KFR_ENABLE_FLAC
+static size_t drflac_reader_read_proc(abstract_reader<void>* file, void* pBufferOut, size_t bytesToRead)
{
- if (ctypeid<T>() == ctypeid<i16>())
- return audiodatatype::i16;
- else if (ctypeid<T>() == ctypeid<i32>())
- return audiodatatype::i32;
- else if (ctypeid<T>() == ctypeid<f32>())
- return audiodatatype::f32;
- else if (ctypeid<T>() == ctypeid<f64>())
- return audiodatatype::f64;
- else
- return audiodatatype::unknown;
+ return file->read(pBufferOut, bytesToRead);
}
-}
-
-struct audioformat
-{
- size_t channels;
- size_t samples;
- audiodatatype type;
- fmax samplerate;
-
- template <typename T, size_t Tag1, size_t Tag2>
- constexpr audioformat(const univector2d<T, Tag1, Tag2>& data, fmax sample_rate)
- : channels(data.size()), samples(data[0].size()), type(internal::get_audiodatatype<T>()),
- samplerate(sample_rate)
- {
- }
-};
-
-namespace internal
-{
-static constexpr u32 FourCC(const char (&ch)[5])
+static drflac_bool32 drflac_reader_seek_proc(abstract_reader<void>* file, int offset,
+ drflac_seek_origin origin)
{
- return u32(u8(ch[0])) | u32(u8(ch[1])) << 8 | u32(u8(ch[2])) << 16 | u32(u8(ch[3])) << 24;
+ return file->seek(offset, origin == drflac_seek_origin_start ? seek_origin::begin : seek_origin::current);
}
+#endif
-constexpr u32 cWAVE_FORMAT_PCM = 1;
-constexpr u32 cWAVE_FORMAT_IEEE = 3;
+} // namespace internal
-constexpr u32 ccRIFF = FourCC("RIFF");
-constexpr u32 ccWAVE = FourCC("WAVE");
-constexpr u32 ccfmt = FourCC("fmt ");
-constexpr u32 ccdata = FourCC("data");
-
-constexpr u32 ccFORM = FourCC("FORM");
-constexpr u32 ccAIFF = FourCC("AIFF");
-constexpr u32 ccAIFC = FourCC("AIFC");
-constexpr u32 ccCOMM = FourCC("COMM");
-constexpr u32 ccSSND = FourCC("SSND");
-constexpr u32 ccNONE = FourCC("NONE");
-constexpr u32 ccsowt = FourCC("sowt");
-
-CMT_PRAGMA_PACK_PUSH_1
-
-struct WAV_FMT
-{
- i32 fId; // 'fmt '
- i32 pcmHeaderLength;
- i16 wFormatTag;
- i16 numChannels;
- i32 nSamplesPerSec;
- i32 nAvgBytesPerSec;
- i16 numBlockAlingn;
- i16 numBitsPerSample;
-} CMT_GNU_PACKED;
-
-struct WAV_DATA
-{
- i32 dId; // 'data' or 'fact'
- i32 dLen;
- u8 data[1];
-} CMT_GNU_PACKED;
-
-struct WAV_DATA_HDR
-{
- i32 dId; // 'data' or 'fact'
- i32 dLen;
-} CMT_GNU_PACKED;
-
-struct AIFF_FMT
-{
- i32 chunkID;
- i32 chunkLen;
- i16 channels;
- u32 frames;
- i16 bitsPerSample;
- f80 sampleRate;
- i32 compression;
-} CMT_GNU_PACKED;
-
-struct AIFF_DATA
-{
- i32 chunkID;
- i32 chunkLen;
- u32 offset;
-} CMT_GNU_PACKED;
-
-struct RIFF_HDR
-{
- i32 riffID; // 'RIFF' or 'COMM'
- i32 fileLen;
- i32 formatID; // 'WAVE' or 'AIFF'
-} CMT_GNU_PACKED;
-
-struct WAV_HEADER
-{
- RIFF_HDR riff;
- WAV_FMT fmt;
- WAV_DATA_HDR data;
-} CMT_GNU_PACKED;
-
-struct CHUNK_HDR
-{
- i32 chunkID;
- i32 chunkLen;
-} CMT_GNU_PACKED;
-
-CMT_PRAGMA_PACK_POP
-
-template <size_t = 0>
-bool audio_test_wav(const array_ref<u8>& rawbytes)
+#if KFR_ENABLE_WAV
+template <typename T>
+struct audio_writer_wav : audio_writer<T>
{
- if (rawbytes.size() < sizeof(RIFF_HDR))
+ audio_writer_wav(std::shared_ptr<abstract_writer<>>&& writer, const audio_format& fmt)
+ : writer(std::move(writer)), f(nullptr), fmt(fmt)
{
- return false;
+ drwav_data_format wav_fmt;
+ wav_fmt.channels = fmt.channels;
+ wav_fmt.sampleRate = fmt.samplerate;
+ wav_fmt.format =
+ fmt.type >= audio_sample_type::first_float ? DR_WAVE_FORMAT_IEEE_FLOAT : DR_WAVE_FORMAT_PCM;
+ wav_fmt.bitsPerSample = audio_sample_bit_depth(fmt.type);
+ wav_fmt.container = fmt.use_w64 ? drwav_container_w64 : drwav_container_riff;
+ f = drwav_open_write(&wav_fmt, (drwav_write_proc)&internal::drwav_writer_write_proc,
+ (drwav_seek_proc)&internal::drwav_writer_seek_proc, this->writer.get());
}
- const RIFF_HDR* hdr = reinterpret_cast<const RIFF_HDR*>(rawbytes.data());
- if (hdr->riffID != ccRIFF)
- {
- return false;
- }
- if (hdr->formatID != ccWAVE)
- {
- return false;
- }
- return true;
-}
+ ~audio_writer_wav() { drwav_close(f); }
-template <size_t = 0>
-bool audio_test_aiff(const array_ref<u8>& rawbytes)
-{
- if (rawbytes.size() < sizeof(RIFF_HDR))
- {
- return false;
- }
- const RIFF_HDR* hdr = reinterpret_cast<const RIFF_HDR*>(rawbytes.data());
- if (hdr->riffID != ccFORM)
- {
- return false;
- }
- if (hdr->formatID != ccAIFF && hdr->formatID != ccAIFC)
+ size_t write(const T* data, size_t size)
{
- return false;
+ if (fmt.type == audio_sample_type::unknown)
+ return 0;
+ if (fmt.type == audio_sample_traits<T>::type)
+ {
+ const size_t sz = drwav_write(f, size, data);
+ fmt.length += sz / fmt.channels;
+ return sz;
+ }
+ else
+ {
+ univector<uint8_t> native(size * audio_sample_sizeof(fmt.type));
+ convert(native.data(), fmt.type, data, size);
+ const size_t sz = drwav_write(f, size, native.data());
+ fmt.length += sz / fmt.channels;
+ return sz;
+ }
}
- return true;
-}
-
-enum class file_status
-{
- ok,
- unknown_format,
- bad_format,
- unsupported_compression,
- unsupported_bit_format
+ const audio_format_and_length& format() const { return fmt; }
+ imax tell() const { return fmt.length; }
+ bool seek(imax position, seek_origin origin) { return false; }
+
+private:
+ std::shared_ptr<abstract_writer<>> writer;
+ drwav* f;
+ audio_format_and_length fmt;
};
-template <size_t = 0>
-file_status audio_info_wav(audioformat& info, const array_ref<u8>& rawbytes)
+template <typename T>
+struct audio_reader_wav : audio_reader<T>
{
- const CHUNK_HDR* chunk = ptr_cast<CHUNK_HDR>(rawbytes.data() + 12);
- const void* end = ptr_cast<char>(rawbytes.end());
- const WAV_FMT* fmt = nullptr;
- const WAV_DATA* rawdata = nullptr;
- while (chunk < end)
+ audio_reader_wav(std::shared_ptr<abstract_reader<>>&& reader) : reader(std::move(reader))
{
- switch (chunk->chunkID)
+ f = drwav_open((drwav_read_proc)&internal::drwav_reader_read_proc,
+ (drwav_seek_proc)&internal::drwav_reader_seek_proc, this->reader.get());
+ fmt.channels = f->channels;
+ fmt.samplerate = f->sampleRate;
+ fmt.length = f->totalSampleCount / fmt.channels;
+ switch (f->translatedFormatTag)
{
- case ccfmt:
- fmt = ptr_cast<WAV_FMT>(chunk);
+ case DR_WAVE_FORMAT_IEEE_FLOAT:
+ switch (f->bitsPerSample)
+ {
+ case 32:
+ fmt.type = audio_sample_type::f32;
+ break;
+ case 64:
+ fmt.type = audio_sample_type::f64;
+ break;
+ default:
+ fmt.type = audio_sample_type::unknown;
+ break;
+ }
+ break;
+ case DR_WAVE_FORMAT_PCM:
+ switch (f->bitsPerSample)
+ {
+ case 8:
+ fmt.type = audio_sample_type::i8;
+ break;
+ case 16:
+ fmt.type = audio_sample_type::i16;
+ break;
+ case 32:
+ fmt.type = audio_sample_type::i32;
+ break;
+ case 64:
+ fmt.type = audio_sample_type::i64;
+ break;
+ default:
+ fmt.type = audio_sample_type::unknown;
+ break;
+ }
break;
- case ccdata:
- rawdata = ptr_cast<WAV_DATA>(chunk);
+ default:
+ fmt.type = audio_sample_type::unknown;
break;
}
- chunk = ptr_cast<CHUNK_HDR>(ptr_cast<u8>(chunk) + chunk->chunkLen + 8);
- }
- if (!fmt || !rawdata)
- {
- return file_status::bad_format;
}
+ ~audio_reader_wav() { drwav_close(f); }
- if (fmt->wFormatTag != cWAVE_FORMAT_PCM && fmt->wFormatTag != cWAVE_FORMAT_IEEE)
- {
- return file_status::unsupported_compression;
- }
+ const audio_format_and_length& format() const { return fmt; }
- int storedbits = fmt->numBlockAlingn * 8 / fmt->numChannels;
- if (fmt->wFormatTag == cWAVE_FORMAT_PCM && fmt->numBitsPerSample == 16 && storedbits == 16)
- {
- info.type = audiodatatype::i16;
- }
- else if (fmt->wFormatTag == cWAVE_FORMAT_PCM && fmt->numBitsPerSample == 24 && storedbits == 24)
+ size_t read(T* data, size_t size)
{
- info.type = audiodatatype::i24;
- }
- else if (fmt->wFormatTag == cWAVE_FORMAT_PCM && fmt->numBitsPerSample == 24 && storedbits == 32)
- {
- info.type = audiodatatype::i24a32;
+ if (fmt.type == audio_sample_type::unknown)
+ return 0;
+ if (fmt.type == audio_sample_traits<T>::type)
+ {
+ return drwav_read(f, size, data);
+ }
+ else
+ {
+ univector<uint8_t> native(size * audio_sample_sizeof(fmt.type));
+ const size_t sz = drwav_read(f, size, native.data());
+ convert(data, native.data(), fmt.type, sz);
+ return sz;
+ }
}
- else if (fmt->wFormatTag == cWAVE_FORMAT_PCM && fmt->numBitsPerSample == 32 && storedbits == 32)
+ imax tell() const { return position; }
+ bool seek(imax offset, seek_origin origin)
{
- info.type = audiodatatype::i32;
+ switch (origin)
+ {
+ case seek_origin::current:
+ return drwav_seek_to_sample(f, this->position + offset);
+ case seek_origin::begin:
+ return drwav_seek_to_sample(f, offset);
+ case seek_origin::end:
+ return drwav_seek_to_sample(f, fmt.length + offset);
+ default:
+ return false;
+ }
}
- else if (fmt->wFormatTag == cWAVE_FORMAT_IEEE && fmt->numBitsPerSample == 32 && storedbits == 32)
+
+private:
+ std::shared_ptr<abstract_reader<>> reader;
+ drwav* f;
+ audio_format_and_length fmt;
+ imax position = 0;
+};
+#endif
+
+#if KFR_ENABLE_FLAC
+template <typename T>
+struct audio_reader_flac : audio_reader<T>
+{
+ audio_reader_flac(std::shared_ptr<abstract_reader<>>&& reader) : reader(std::move(reader))
{
- info.type = audiodatatype::f32;
+ f = drflac_open((drflac_read_proc)&internal::drflac_reader_read_proc,
+ (drflac_seek_proc)&internal::drflac_reader_seek_proc, this->reader.get());
+ fmt.channels = f->channels;
+ fmt.samplerate = f->sampleRate;
+ fmt.length = f->totalSampleCount / fmt.channels;
+ fmt.type = audio_sample_type::i32;
}
- else if (fmt->wFormatTag == cWAVE_FORMAT_IEEE && fmt->numBitsPerSample == 64 && storedbits == 64)
+ ~audio_reader_flac() { drflac_close(f); }
+
+ const audio_format_and_length& format() const { return fmt; }
+
+ size_t read(T* data, size_t size)
{
- info.type = audiodatatype::f64;
+ if (fmt.type == audio_sample_type::unknown)
+ return 0;
+ if (audio_sample_traits<T>::type == audio_sample_type::i32)
+ {
+ return drflac_read_s32(f, size, reinterpret_cast<i32*>(data));
+ }
+ else
+ {
+ univector<i32> native(size * sizeof(i32));
+ const size_t sz = drflac_read_s32(f, size, native.data());
+ convert(data, native.data(), sz);
+ return sz;
+ }
}
- else
+ imax tell() const { return position; }
+ bool seek(imax offset, seek_origin origin)
{
- return file_status::unsupported_bit_format;
+ switch (origin)
+ {
+ case seek_origin::current:
+ return drflac_seek_to_sample(f, this->position + offset);
+ case seek_origin::begin:
+ return drflac_seek_to_sample(f, offset);
+ case seek_origin::end:
+ return drflac_seek_to_sample(f, fmt.length + offset);
+ default:
+ return false;
+ }
}
- if (fmt->numChannels < 1 || fmt->numChannels > 16)
- return file_status::unsupported_bit_format;
-
- info.channels = size_t(fmt->numChannels);
- info.samplerate = size_t(fmt->nSamplesPerSec);
- info.samples = size_t(rawdata->dLen) / info.channels / (get_audiobitdepth(info.type) / 8);
-
- return file_status::ok;
-}
-
-template <size_t = 0>
-file_status audio_info(audioformat& info, const array_ref<u8>& file_bytes)
-{
- if (audio_test_wav(file_bytes))
- return audio_info_wav(info, file_bytes);
- else
- return file_status::unknown_format;
-}
-}
-
-template <size_t = 0>
-void audio_encode_header(internal::expression_sequential_file_writer& dest, const audioformat& info)
-{
- using namespace internal;
- WAV_HEADER hdr;
- zeroize(hdr);
- const size_t framesize = info.channels * get_audiobitdepth(info.type) / 8;
- hdr.riff.riffID = ccRIFF;
- hdr.riff.formatID = ccWAVE;
- hdr.riff.fileLen = autocast(info.samples * framesize + sizeof(hdr) - 8);
- hdr.fmt.fId = ccfmt;
- hdr.fmt.pcmHeaderLength = autocast(sizeof(hdr.fmt) - sizeof(CHUNK_HDR));
- hdr.fmt.numBlockAlingn = autocast(framesize);
- hdr.fmt.nAvgBytesPerSec = autocast(info.samplerate * framesize);
- hdr.fmt.nSamplesPerSec = autocast(info.samplerate);
- hdr.fmt.numChannels = autocast(info.channels);
- hdr.fmt.wFormatTag = info.type >= audiodatatype::f32 ? cWAVE_FORMAT_IEEE : cWAVE_FORMAT_PCM;
- hdr.fmt.numBitsPerSample = autocast(get_audiobitdepth(info.type));
- hdr.data.dId = ccdata;
- hdr.data.dLen = autocast(info.samples * framesize);
-
- dest.write(hdr);
-}
-
-template <typename T, size_t Tag1, size_t Tag2>
-void audio_encode_audio(internal::expression_sequential_file_writer& dest,
- const univector2d<T, Tag1, Tag2>& audio)
-{
- write_interleaved<T>(dest, audio);
-}
+private:
+ std::shared_ptr<abstract_reader<>> reader;
+ drflac* f;
+ audio_format_and_length fmt;
+ imax position = 0;
+};
+#endif
-template <typename T, size_t Tag1, size_t Tag2>
-void audio_encode(internal::expression_sequential_file_writer& dest, const univector2d<T, Tag1, Tag2>& audio,
- const audioformat& info)
-{
- audio_encode_header(dest, info);
- audio_encode_audio(dest, audio);
-}
-}
+} // namespace kfr
diff --git a/include/kfr/io/dr/README.txt b/include/kfr/io/dr/README.txt
@@ -0,0 +1,9 @@
+Code for reading/writing wav and reading flac files.
+
+These two files are released to public domain by the author:
+dr_flac.h
+dr_wav.h
+
+See also information in these files.
+
+https://github.com/mackron/dr_libs
diff --git a/include/kfr/io/dr/dr_flac.h b/include/kfr/io/dr/dr_flac.h
@@ -0,0 +1,6257 @@
+// FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file.
+// dr_flac - v0.10.0 - 2018-09-11
+//
+// David Reid - mackron@gmail.com
+
+// USAGE
+//
+// dr_flac is a single-file library. To use it, do something like the following in one .c file.
+// #define DR_FLAC_IMPLEMENTATION
+// #include "dr_flac.h"
+//
+// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data,
+// do something like the following:
+//
+// drflac* pFlac = drflac_open_file("MySong.flac");
+// if (pFlac == NULL) {
+// // Failed to open FLAC file
+// }
+//
+// drflac_int32* pSamples = malloc(pFlac->totalSampleCount * sizeof(drflac_int32));
+// drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSamples);
+//
+// The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of
+// channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are
+// always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has
+// seamless support for Ogg encapsulated FLAC streams as well.
+//
+// You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and
+// the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of
+// samples, just call it again. Example:
+//
+// while (drflac_read_s32(pFlac, chunkSize, pChunkSamples) > 0) {
+// do_something();
+// }
+//
+// You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example,
+// if you were to seek to the sample at index 0 in a stereo stream, you'll be seeking to the first sample of the left channel.
+// The sample at index 1 will be the first sample of the right channel. The sample at index 2 will be the second sample of the
+// left channel, etc.
+//
+//
+// If you just want to quickly decode an entire FLAC file in one go you can do something like this:
+//
+// unsigned int channels;
+// unsigned int sampleRate;
+// drflac_uint64 totalSampleCount;
+// drflac_int32* pSampleData = drflac_open_and_decode_file_s32("MySong.flac", &channels, &sampleRate, &totalSampleCount);
+// if (pSampleData == NULL) {
+// // Failed to open and decode FLAC file.
+// }
+//
+// ...
+//
+// drflac_free(pSampleData);
+//
+//
+// You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs
+// respectively, but note that these should be considered lossy.
+//
+//
+// If you need access to metadata (album art, etc.), use drflac_open_with_metadata(), drflac_open_file_with_metdata() or
+// drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the
+// normal versions and also just a little bit harder to use.
+//
+// dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before
+// drflac_open_with_metdata() returns.
+//
+//
+// The main opening APIs (drflac_open(), etc.) will fail if the header is not present. The presents a problem in certain
+// scenarios such as broadcast style streams like internet radio where the header may not be present because the user has
+// started playback mid-stream. To handle this, use the relaxed APIs: drflac_open_relaxed() and drflac_open_with_metadata_relaxed().
+//
+// It is not recommended to use these APIs for file based streams because a missing header would usually indicate a
+// corrupted or perverse file. In addition, these APIs can take a long time to initialize because they may need to spend
+// a lot of time finding the first frame.
+//
+//
+//
+// OPTIONS
+// #define these options before including this file.
+//
+// #define DR_FLAC_NO_STDIO
+// Disable drflac_open_file() and family.
+//
+// #define DR_FLAC_NO_OGG
+// Disables support for Ogg/FLAC streams.
+//
+// #define DR_FLAC_BUFFER_SIZE <number>
+// Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls
+// back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing
+// returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of
+// onRead(), or increase it if it's very inefficient. Must be a multiple of 8.
+//
+// #define DR_FLAC_NO_CRC
+// Disables CRC checks. This will offer a performance boost when CRC is unnecessary.
+//
+// #define DR_FLAC_NO_SIMD
+// Disables SIMD optimizations (SSE on x86/x64 architectures). Use this if you are having compatibility issues with your
+// compiler.
+//
+//
+//
+// QUICK NOTES
+// - dr_flac does not currently support changing the sample rate nor channel count mid stream.
+// - Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as.
+// - This has not been tested on big-endian architectures.
+// - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization.
+// - When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open()
+// returning inconsistent samples.
+
+#ifndef dr_flac_h
+#define dr_flac_h
+
+#include <stddef.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+typedef signed char drflac_int8;
+typedef unsigned char drflac_uint8;
+typedef signed short drflac_int16;
+typedef unsigned short drflac_uint16;
+typedef signed int drflac_int32;
+typedef unsigned int drflac_uint32;
+typedef signed __int64 drflac_int64;
+typedef unsigned __int64 drflac_uint64;
+#else
+#include <stdint.h>
+typedef int8_t drflac_int8;
+typedef uint8_t drflac_uint8;
+typedef int16_t drflac_int16;
+typedef uint16_t drflac_uint16;
+typedef int32_t drflac_int32;
+typedef uint32_t drflac_uint32;
+typedef int64_t drflac_int64;
+typedef uint64_t drflac_uint64;
+#endif
+typedef drflac_uint8 drflac_bool8;
+typedef drflac_uint32 drflac_bool32;
+#define DRFLAC_TRUE 1
+#define DRFLAC_FALSE 0
+
+// As data is read from the client it is placed into an internal buffer for fast access. This controls the
+// size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing
+// returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8.
+#ifndef DR_FLAC_BUFFER_SIZE
+#define DR_FLAC_BUFFER_SIZE 4096
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Check if we can enable 64-bit optimizations.
+#if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+#define DRFLAC_64BIT
+#endif
+
+#ifdef DRFLAC_64BIT
+typedef drflac_uint64 drflac_cache_t;
+#else
+typedef drflac_uint32 drflac_cache_t;
+#endif
+
+// The various metadata block types.
+#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
+#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1
+#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2
+#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
+#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
+#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5
+#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6
+#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127
+
+// The various picture types specified in the PICTURE block.
+#define DRFLAC_PICTURE_TYPE_OTHER 0
+#define DRFLAC_PICTURE_TYPE_FILE_ICON 1
+#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
+#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3
+#define DRFLAC_PICTURE_TYPE_COVER_BACK 4
+#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5
+#define DRFLAC_PICTURE_TYPE_MEDIA 6
+#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7
+#define DRFLAC_PICTURE_TYPE_ARTIST 8
+#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9
+#define DRFLAC_PICTURE_TYPE_BAND 10
+#define DRFLAC_PICTURE_TYPE_COMPOSER 11
+#define DRFLAC_PICTURE_TYPE_LYRICIST 12
+#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13
+#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14
+#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
+#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
+#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
+#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18
+#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
+#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
+
+typedef enum
+{
+ drflac_container_native,
+ drflac_container_ogg,
+ drflac_container_unknown
+} drflac_container;
+
+typedef enum
+{
+ drflac_seek_origin_start,
+ drflac_seek_origin_current
+} drflac_seek_origin;
+
+// Packing is important on this structure because we map this directly to the raw data within the SEEKTABLE metadata block.
+#pragma pack(2)
+typedef struct
+{
+ drflac_uint64 firstSample;
+ drflac_uint64 frameOffset; // The offset from the first byte of the header of the first frame.
+ drflac_uint16 sampleCount;
+} drflac_seekpoint;
+#pragma pack()
+
+typedef struct
+{
+ drflac_uint16 minBlockSize;
+ drflac_uint16 maxBlockSize;
+ drflac_uint32 minFrameSize;
+ drflac_uint32 maxFrameSize;
+ drflac_uint32 sampleRate;
+ drflac_uint8 channels;
+ drflac_uint8 bitsPerSample;
+ drflac_uint64 totalSampleCount;
+ drflac_uint8 md5[16];
+} drflac_streaminfo;
+
+typedef struct
+{
+ // The metadata type. Use this to know how to interpret the data below.
+ drflac_uint32 type;
+
+ // A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to
+ // not modify the contents of this buffer. Use the structures below for more meaningful and structured
+ // information about the metadata. It's possible for this to be null.
+ const void* pRawData;
+
+ // The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL.
+ drflac_uint32 rawDataSize;
+
+ union
+ {
+ drflac_streaminfo streaminfo;
+
+ struct
+ {
+ int unused;
+ } padding;
+
+ struct
+ {
+ drflac_uint32 id;
+ const void* pData;
+ drflac_uint32 dataSize;
+ } application;
+
+ struct
+ {
+ drflac_uint32 seekpointCount;
+ const drflac_seekpoint* pSeekpoints;
+ } seektable;
+
+ struct
+ {
+ drflac_uint32 vendorLength;
+ const char* vendor;
+ drflac_uint32 commentCount;
+ const void* pComments;
+ } vorbis_comment;
+
+ struct
+ {
+ char catalog[128];
+ drflac_uint64 leadInSampleCount;
+ drflac_bool32 isCD;
+ drflac_uint8 trackCount;
+ const void* pTrackData;
+ } cuesheet;
+
+ struct
+ {
+ drflac_uint32 type;
+ drflac_uint32 mimeLength;
+ const char* mime;
+ drflac_uint32 descriptionLength;
+ const char* description;
+ drflac_uint32 width;
+ drflac_uint32 height;
+ drflac_uint32 colorDepth;
+ drflac_uint32 indexColorCount;
+ drflac_uint32 pictureDataSize;
+ const drflac_uint8* pPictureData;
+ } picture;
+ } data;
+} drflac_metadata;
+
+
+// Callback for when data needs to be read from the client.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// pBufferOut [out] The output buffer.
+// bytesToRead [in] The number of bytes to read.
+//
+// Returns the number of bytes actually read.
+//
+// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+// either the entire bytesToRead is filled or you have reached the end of the stream.
+typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+// Callback for when data needs to be seeked.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
+// origin [in] The origin of the seek - the current position or the start of the stream.
+//
+// Returns whether or not the seek was successful.
+//
+// The offset will never be negative. Whether or not it is relative to the beginning or current position is determined
+// by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current.
+typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin);
+
+// Callback for when a metadata block is read.
+//
+// pUserData [in] The user data that was passed to drflac_open() and family.
+// pMetadata [in] A pointer to a structure containing the data of the metadata block.
+//
+// Use pMetadata->type to determine which metadata block is being handled and how to read the data.
+typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata);
+
+
+// Structure for internal use. Only used for decoders opened with drflac_open_memory.
+typedef struct
+{
+ const drflac_uint8* data;
+ size_t dataSize;
+ size_t currentReadPos;
+} drflac__memory_stream;
+
+// Structure for internal use. Used for bit streaming.
+typedef struct
+{
+ // The function to call when more data needs to be read.
+ drflac_read_proc onRead;
+
+ // The function to call when the current read position needs to be moved.
+ drflac_seek_proc onSeek;
+
+ // The user data to pass around to onRead and onSeek.
+ void* pUserData;
+
+
+ // The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the
+ // stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether
+ // or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t).
+ size_t unalignedByteCount;
+
+ // The content of the unaligned bytes.
+ drflac_cache_t unalignedCache;
+
+ // The index of the next valid cache line in the "L2" cache.
+ drflac_uint32 nextL2Line;
+
+ // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining.
+ drflac_uint32 consumedBits;
+
+ // The cached data which was most recently read from the client. There are two levels of cache. Data flows as such:
+ // Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions.
+ drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)];
+ drflac_cache_t cache;
+
+ // CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this
+ // is reset to 0 at the beginning of each frame.
+ drflac_uint16 crc16;
+ drflac_cache_t crc16Cache; // A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded.
+ drflac_uint32 crc16CacheIgnoredBytes; // The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache.
+} drflac_bs;
+
+typedef struct
+{
+ // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC.
+ drflac_uint8 subframeType;
+
+ // The number of wasted bits per sample as specified by the sub-frame header.
+ drflac_uint8 wastedBitsPerSample;
+
+ // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC.
+ drflac_uint8 lpcOrder;
+
+ // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because
+ // an extra bit is required for side channels when interchannel decorrelation is being used.
+ drflac_uint32 bitsPerSample;
+
+ // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that
+ // it's a signed 32-bit integer for each value.
+ drflac_int32* pDecodedSamples;
+} drflac_subframe;
+
+typedef struct
+{
+ // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will
+ // always be set to 0.
+ drflac_uint64 sampleNumber;
+
+ // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0.
+ drflac_uint32 frameNumber;
+
+ // The sample rate of this frame.
+ drflac_uint32 sampleRate;
+
+ // The number of samples in each sub-frame within this frame.
+ drflac_uint16 blockSize;
+
+ // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this
+ // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE.
+ drflac_uint8 channelAssignment;
+
+ // The number of bits per sample within this frame.
+ drflac_uint8 bitsPerSample;
+
+ // The frame's CRC.
+ drflac_uint8 crc8;
+} drflac_frame_header;
+
+typedef struct
+{
+ // The header.
+ drflac_frame_header header;
+
+ // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples
+ // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame.
+ drflac_uint32 samplesRemaining;
+
+ // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels.
+ drflac_subframe subframes[8];
+} drflac_frame;
+
+typedef struct
+{
+ // The function to call when a metadata block is read.
+ drflac_meta_proc onMeta;
+
+ // The user data posted to the metadata callback function.
+ void* pUserDataMD;
+
+
+ // The sample rate. Will be set to something like 44100.
+ drflac_uint32 sampleRate;
+
+ // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the
+ // value specified in the STREAMINFO block.
+ drflac_uint8 channels;
+
+ // The bits per sample. Will be set to something like 16, 24, etc.
+ drflac_uint8 bitsPerSample;
+
+ // The maximum block size, in samples. This number represents the number of samples in each channel (not combined).
+ drflac_uint16 maxBlockSize;
+
+ // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels,
+ // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a
+ // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio.
+ drflac_uint64 totalSampleCount;
+
+
+ // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream.
+ drflac_container container;
+
+ // The number of seekpoints in the seektable.
+ drflac_uint32 seekpointCount;
+
+
+ // Information about the frame the decoder is currently sitting on.
+ drflac_frame currentFrame;
+
+ // The index of the sample the decoder is currently sitting on. This is only used for seeking.
+ drflac_uint64 currentSample;
+
+ // The position of the first frame in the stream. This is only ever used for seeking.
+ drflac_uint64 firstFramePos;
+
+
+ // A hack to avoid a malloc() when opening a decoder with drflac_open_memory().
+ drflac__memory_stream memoryStream;
+
+
+ // A pointer to the decoded sample data. This is an offset of pExtraData.
+ drflac_int32* pDecodedSamples;
+
+ // A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table.
+ drflac_seekpoint* pSeekpoints;
+
+ // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData.
+ void* _oggbs;
+
+ // The bit streamer. The raw FLAC data is fed through this object.
+ drflac_bs bs;
+
+ // Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs.
+ drflac_uint8 pExtraData[1];
+} drflac;
+
+
+// Opens a FLAC decoder.
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated
+// FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with
+// multiplexed streams which basically means it can play FLAC encoded audio tracks in videos.
+//
+// This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where
+// the header may not be present.
+//
+// See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close()
+drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData);
+
+// The same as drflac_open(), except attempts to open the stream even when a header block is not present.
+//
+// Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do
+// not set this to drflac_container_unknown - that is for internal use only.
+//
+// Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never
+// found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an
+// indicator that the end of the stream was found.
+drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData);
+
+// Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.).
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// onMeta [in] The function to call for every metadata block.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC()
+// and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks.
+//
+// The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function
+// returns.
+//
+// The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC
+// stream where the header may not be present.
+//
+// Note that this will behave inconsistently with drflac_open() if the stream is an Ogg encapsulated stream and a metadata
+// block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When drflac_open_with_metadata()
+// is being used, the open routine will try to read the contents of the metadata block, whereas drflac_open() will simply
+// seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on
+// whether or not the stream is being opened with metadata.
+//
+// See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close()
+drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData);
+
+// The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present.
+//
+// See also: drflac_open_with_metadata(), drflac_open_relaxed()
+drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData);
+
+// Closes the given FLAC decoder.
+//
+// pFlac [in] The decoder to close.
+//
+// This will destroy the decoder object.
+void drflac_close(drflac* pFlac);
+
+
+// Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM.
+//
+// pFlac [in] The decoder.
+// samplesToRead [in] The number of samples to read.
+// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
+//
+// Returns the number of samples actually read.
+//
+// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples
+// seeked.
+drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* pBufferOut);
+
+// Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit.
+//
+// pFlac [in] The decoder.
+// samplesToRead [in] The number of samples to read.
+// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
+//
+// Returns the number of samples actually read.
+//
+// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples
+// seeked.
+//
+// Note that this is lossy for streams where the bits per sample is larger than 16.
+drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut);
+
+// Same as drflac_read_s32(), except outputs samples as 32-bit floating-point PCM.
+//
+// pFlac [in] The decoder.
+// samplesToRead [in] The number of samples to read.
+// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
+//
+// Returns the number of samples actually read.
+//
+// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples
+// seeked.
+//
+// Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly
+// represent every possible number.
+drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut);
+
+// Seeks to the sample at the given index.
+//
+// pFlac [in] The decoder.
+// sampleIndex [in] The index of the sample to seek to. See notes below.
+//
+// Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise.
+//
+// The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample
+// in the left channel; the sample at index 1 is the first sample on the right channel, and so on.
+//
+// When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with
+// something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels)))
+drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex);
+
+
+
+#ifndef DR_FLAC_NO_STDIO
+// Opens a FLAC decoder from the file at the given path.
+//
+// filename [in] The path of the file to open, either absolute or relative to the current directory.
+//
+// Returns a pointer to an object representing the decoder.
+//
+// Close the decoder with drflac_close().
+//
+// This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the
+// number of files a process can have open at any given time, so keep this mind if you have many decoders open at the
+// same time.
+//
+// See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close()
+drflac* drflac_open_file(const char* filename);
+
+// Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.)
+//
+// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData);
+#endif
+
+// Opens a FLAC decoder from a pre-allocated block of memory
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the decoder.
+drflac* drflac_open_memory(const void* data, size_t dataSize);
+
+// Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.)
+//
+// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled.
+drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData);
+
+
+
+//// High Level APIs ////
+
+// Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a
+// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE().
+//
+// Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously
+// read samples into a dynamically sized buffer on the heap until no samples are left.
+//
+// Do not call this function on a broadcast type of stream (like internet radio streams and whatnot).
+drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+
+// Same as drflac_open_and_decode_s32(), except returns signed 16-bit integer samples.
+drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+
+// Same as drflac_open_and_decode_s32(), except returns 32-bit floating-point samples.
+float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+
+#ifndef DR_FLAC_NO_STDIO
+// Same as drflac_open_and_decode_s32() except opens the decoder from a file.
+drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+
+// Same as drflac_open_and_decode_file_s32(), except returns signed 16-bit integer samples.
+drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+
+// Same as drflac_open_and_decode_file_f32(), except returns 32-bit floating-point samples.
+float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+#endif
+
+// Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory.
+drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+
+// Same as drflac_open_and_decode_memory_s32(), except returns signed 16-bit integer samples.
+drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+
+// Same as drflac_open_and_decode_memory_s32(), except returns 32-bit floating-point samples.
+float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount);
+
+// Frees memory that was allocated internally by dr_flac.
+void drflac_free(void* p);
+
+
+// Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block.
+typedef struct
+{
+ drflac_uint32 countRemaining;
+ const char* pRunningData;
+} drflac_vorbis_comment_iterator;
+
+// Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT
+// metadata block.
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments);
+
+// Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The
+// returned string is NOT null terminated.
+const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut);
+
+
+// Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block.
+typedef struct
+{
+ drflac_uint32 countRemaining;
+ const char* pRunningData;
+} drflac_cuesheet_track_iterator;
+
+// Packing is important on this structure because we map this directly to the raw data within the CUESHEET metadata block.
+#pragma pack(4)
+typedef struct
+{
+ drflac_uint64 offset;
+ drflac_uint8 index;
+ drflac_uint8 reserved[3];
+} drflac_cuesheet_track_index;
+#pragma pack()
+
+typedef struct
+{
+ drflac_uint64 offset;
+ drflac_uint8 trackNumber;
+ char ISRC[12];
+ drflac_bool8 isAudio;
+ drflac_bool8 preEmphasis;
+ drflac_uint8 indexCount;
+ const drflac_cuesheet_track_index* pIndexPoints;
+} drflac_cuesheet_track;
+
+// Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata
+// block.
+void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData);
+
+// Goes to the next cuesheet track in the given iterator. If DRFLAC_FALSE is returned it means there are no more comments.
+drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif //dr_flac_h
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// IMPLEMENTATION
+//
+///////////////////////////////////////////////////////////////////////////////
+#ifdef DR_FLAC_IMPLEMENTATION
+#ifdef __linux__
+ #ifndef _BSD_SOURCE
+ #define _BSD_SOURCE
+ #endif
+ #ifndef __USE_BSD
+ #define __USE_BSD
+ #endif
+ #include <endian.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+// CPU architecture.
+#if defined(__x86_64__) || defined(_M_X64)
+ #define DRFLAC_X64
+#elif defined(__i386) || defined(_M_IX86)
+ #define DRFLAC_X86
+#elif defined(__arm__) || defined(_M_ARM)
+ #define DRFLAC_ARM
+#endif
+
+// Compile-time CPU feature support.
+#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
+ #if defined(_MSC_VER) && !defined(__clang__)
+ #if _MSC_VER >= 1400
+ #include <intrin.h>
+ static void drflac__cpuid(int info[4], int fid)
+ {
+ __cpuid(info, fid);
+ }
+ #else
+ #define DRFLAC_NO_CPUID
+ #endif
+ #else
+ #if defined(__GNUC__) || defined(__clang__)
+ static void drflac__cpuid(int info[4], int fid)
+ {
+ // It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the
+ // specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for
+ // supporting different assembly dialects.
+ //
+ // What's basically happening is that we're saving and restoring the ebx register manually.
+ #if defined(DRFLAC_X86) && defined(__PIC__)
+ __asm__ __volatile__ (
+ "xchg{l} {%%}ebx, %k1;"
+ "cpuid;"
+ "xchg{l} {%%}ebx, %k1;"
+ : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #else
+ __asm__ __volatile__ (
+ "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #endif
+ }
+ #else
+ #define DRFLAC_NO_CPUID
+ #endif
+ #endif
+#else
+ #define DRFLAC_NO_CPUID
+#endif
+
+
+#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64))
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
+#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
+#elif defined(__clang__)
+ #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
+ #endif
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1300
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+#elif defined(__clang__)
+ #if __has_builtin(__builtin_bswap16)
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap32)
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap64)
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #endif
+#elif defined(__GNUC__)
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #endif
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #endif
+#endif
+
+
+// Standard library stuff.
+#ifndef DRFLAC_ASSERT
+#include <assert.h>
+#define DRFLAC_ASSERT(expression) assert(expression)
+#endif
+#ifndef DRFLAC_MALLOC
+#define DRFLAC_MALLOC(sz) malloc((sz))
+#endif
+#ifndef DRFLAC_REALLOC
+#define DRFLAC_REALLOC(p, sz) realloc((p), (sz))
+#endif
+#ifndef DRFLAC_FREE
+#define DRFLAC_FREE(p) free((p))
+#endif
+#ifndef DRFLAC_COPY_MEMORY
+#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
+#endif
+#ifndef DRFLAC_ZERO_MEMORY
+#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
+#endif
+
+#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future.
+
+#ifdef _MSC_VER
+#define DRFLAC_INLINE __forceinline
+#else
+#ifdef __GNUC__
+#define DRFLAC_INLINE inline __attribute__((always_inline))
+#else
+#define DRFLAC_INLINE inline
+#endif
+#endif
+
+typedef drflac_int32 drflac_result;
+#define DRFLAC_SUCCESS 0
+#define DRFLAC_ERROR -1 // A generic error.
+#define DRFLAC_INVALID_ARGS -2
+#define DRFLAC_END_OF_STREAM -128
+#define DRFLAC_CRC_MISMATCH -129
+
+#define DRFLAC_SUBFRAME_CONSTANT 0
+#define DRFLAC_SUBFRAME_VERBATIM 1
+#define DRFLAC_SUBFRAME_FIXED 8
+#define DRFLAC_SUBFRAME_LPC 32
+#define DRFLAC_SUBFRAME_RESERVED 255
+
+#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
+#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
+
+#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
+#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
+#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
+#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
+
+
+#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
+#define drflac_assert DRFLAC_ASSERT
+#define drflac_copy_memory DRFLAC_COPY_MEMORY
+#define drflac_zero_memory DRFLAC_ZERO_MEMORY
+
+
+// CPU caps.
+static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
+#ifndef DRFLAC_NO_CPUID
+static drflac_bool32 drflac__gIsSSE42Supported = DRFLAC_FALSE;
+static void drflac__init_cpu_caps()
+{
+ int info[4] = {0};
+
+ // LZCNT
+ drflac__cpuid(info, 0x80000001);
+ drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
+
+ // SSE4.2
+ drflac__cpuid(info, 1);
+ drflac__gIsSSE42Supported = (info[2] & (1 << 19)) != 0;
+}
+#endif
+
+
+//// Endian Management ////
+static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian()
+{
+#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
+ return DRFLAC_TRUE;
+#else
+ int n = 1;
+ return (*(char*)&n) == 1;
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n)
+{
+#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #if defined(_MSC_VER)
+ return _byteswap_ushort(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap16(n);
+ #else
+ #error "This compiler does not support the byte swap intrinsic."
+ #endif
+#else
+ return ((n & 0xFF00) >> 8) |
+ ((n & 0x00FF) << 8);
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n)
+{
+#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #if defined(_MSC_VER)
+ return _byteswap_ulong(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap32(n);
+ #else
+ #error "This compiler does not support the byte swap intrinsic."
+ #endif
+#else
+ return ((n & 0xFF000000) >> 24) |
+ ((n & 0x00FF0000) >> 8) |
+ ((n & 0x0000FF00) << 8) |
+ ((n & 0x000000FF) << 24);
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n)
+{
+#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #if defined(_MSC_VER)
+ return _byteswap_uint64(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ return __builtin_bswap64(n);
+ #else
+ #error "This compiler does not support the byte swap intrinsic."
+ #endif
+#else
+ return ((n & (drflac_uint64)0xFF00000000000000) >> 56) |
+ ((n & (drflac_uint64)0x00FF000000000000) >> 40) |
+ ((n & (drflac_uint64)0x0000FF0000000000) >> 24) |
+ ((n & (drflac_uint64)0x000000FF00000000) >> 8) |
+ ((n & (drflac_uint64)0x00000000FF000000) << 8) |
+ ((n & (drflac_uint64)0x0000000000FF0000) << 24) |
+ ((n & (drflac_uint64)0x000000000000FF00) << 40) |
+ ((n & (drflac_uint64)0x00000000000000FF) << 56);
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n)
+{
+#ifdef __linux__
+ return be16toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint16(n);
+ }
+
+ return n;
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n)
+{
+#ifdef __linux__
+ return be32toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint32(n);
+ }
+
+ return n;
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n)
+{
+#ifdef __linux__
+ return be64toh(n);
+#else
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint64(n);
+ }
+
+ return n;
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n)
+{
+#ifdef __linux__
+ return le32toh(n);
+#else
+ if (!drflac__is_little_endian()) {
+ return drflac__swap_endian_uint32(n);
+ }
+
+ return n;
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n)
+{
+ drflac_uint32 result = 0;
+ result |= (n & 0x7F000000) >> 3;
+ result |= (n & 0x007F0000) >> 2;
+ result |= (n & 0x00007F00) >> 1;
+ result |= (n & 0x0000007F) >> 0;
+
+ return result;
+}
+
+
+
+// The CRC code below is based on this document: http://zlib.net/crc_v3.txt
+static drflac_uint8 drflac__crc8_table[] = {
+ 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
+ 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
+ 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
+ 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
+ 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
+ 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
+ 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
+ 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
+ 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
+ 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
+ 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
+ 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+ 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
+ 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
+ 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
+ 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
+};
+
+static drflac_uint16 drflac__crc16_table[] = {
+ 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
+ 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
+ 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
+ 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
+ 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
+ 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
+ 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
+ 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
+ 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
+ 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
+ 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
+ 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
+ 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
+ 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
+ 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
+ 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
+ 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
+ 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
+ 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
+ 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
+ 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
+ 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
+ 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
+ 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
+ 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
+ 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
+ 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
+ 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
+ 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
+ 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
+ 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
+ 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
+};
+
+static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data)
+{
+ return drflac__crc8_table[crc ^ data];
+}
+
+static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count)
+{
+ drflac_assert(count <= 32);
+
+#ifdef DR_FLAC_NO_CRC
+ (void)crc;
+ (void)data;
+ (void)count;
+ return 0;
+#else
+#if 0
+ // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);")
+ drflac_uint8 p = 0x07;
+ for (int i = count-1; i >= 0; --i) {
+ drflac_uint8 bit = (data & (1 << i)) >> i;
+ if (crc & 0x80) {
+ crc = ((crc << 1) | bit) ^ p;
+ } else {
+ crc = ((crc << 1) | bit);
+ }
+ }
+ return crc;
+#else
+ drflac_uint32 wholeBytes = count >> 3;
+ drflac_uint32 leftoverBits = count - (wholeBytes*8);
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+ drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ switch (wholeBytes) {
+ case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
+ case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
+ case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
+ case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
+ case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)];
+ }
+ return crc;
+#endif
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data)
+{
+ return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data];
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount)
+{
+ switch (byteCount)
+ {
+#ifdef DRFLAC_64BIT
+ case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
+ case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
+ case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
+ case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
+#endif
+ case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
+ case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
+ case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
+ case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
+ }
+
+ return crc;
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count)
+{
+ drflac_assert(count <= 64);
+
+#ifdef DR_FLAC_NO_CRC
+ (void)crc;
+ (void)data;
+ (void)count;
+ return 0;
+#else
+#if 0
+ // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);")
+ drflac_uint16 p = 0x8005;
+ for (int i = count-1; i >= 0; --i) {
+ drflac_uint16 bit = (data & (1ULL << i)) >> i;
+ if (r & 0x8000) {
+ r = ((r << 1) | bit) ^ p;
+ } else {
+ r = ((r << 1) | bit);
+ }
+ }
+
+ return crc;
+#else
+ drflac_uint32 wholeBytes = count >> 3;
+ drflac_uint32 leftoverBits = count - (wholeBytes*8);
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+ drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ switch (wholeBytes) {
+ default:
+ case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
+ case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
+ case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
+ case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
+ case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
+ }
+ return crc;
+#endif
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count)
+{
+ drflac_assert(count <= 64);
+
+#ifdef DR_FLAC_NO_CRC
+ (void)crc;
+ (void)data;
+ (void)count;
+ return 0;
+#else
+ drflac_uint32 wholeBytes = count >> 3;
+ drflac_uint32 leftoverBits = count - (wholeBytes*8);
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+ drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ switch (wholeBytes) {
+ default:
+ case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0xFF00000000000000 << leftoverBits)) >> (56 + leftoverBits)));
+ case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00FF000000000000 << leftoverBits)) >> (48 + leftoverBits)));
+ case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000FF0000000000 << leftoverBits)) >> (40 + leftoverBits)));
+ case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000FF00000000 << leftoverBits)) >> (32 + leftoverBits)));
+ case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000FF000000 << leftoverBits)) >> (24 + leftoverBits)));
+ case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000000000FF0000 << leftoverBits)) >> (16 + leftoverBits)));
+ case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000000000FF00 << leftoverBits)) >> ( 8 + leftoverBits)));
+ case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000000000FF << leftoverBits)) >> ( 0 + leftoverBits)));
+ case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
+ }
+ return crc;
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count)
+{
+#ifdef DRFLAC_64BIT
+ return drflac_crc16__64bit(crc, data, count);
+#else
+ return drflac_crc16__32bit(crc, data, count);
+#endif
+}
+
+
+#ifdef DRFLAC_64BIT
+#define drflac__be2host__cache_line drflac__be2host_64
+#else
+#define drflac__be2host__cache_line drflac__be2host_32
+#endif
+
+// BIT READING ATTEMPT #2
+//
+// This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting
+// on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache
+// is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an
+// array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data
+// from onRead() is read into.
+#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
+#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
+#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
+#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount)))
+#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
+#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount))
+#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
+#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)))
+#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
+#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
+#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
+
+
+#ifndef DR_FLAC_NO_CRC
+static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs)
+{
+ bs->crc16 = 0;
+ bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
+}
+
+static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs)
+{
+ bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
+ bs->crc16CacheIgnoredBytes = 0;
+}
+
+static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs)
+{
+ // We should never be flushing in a situation where we are not aligned on a byte boundary.
+ drflac_assert((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
+
+ // The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined
+ // by the number of bits that have been consumed.
+ if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
+ drflac__update_crc16(bs);
+ } else {
+ // We only accumulate the consumed bits.
+ bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
+
+ // The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated
+ // so we can handle that later.
+ bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
+ }
+
+ return bs->crc16;
+}
+#endif
+
+static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs)
+{
+ // Fast path. Try loading straight from L2.
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DRFLAC_TRUE;
+ }
+
+ // If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's
+ // any left.
+ if (bs->unalignedByteCount > 0) {
+ return DRFLAC_FALSE; // If we have any unaligned bytes it means there's no more aligned bytes left in the client.
+ }
+
+ size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs));
+
+ bs->nextL2Line = 0;
+ if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) {
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DRFLAC_TRUE;
+ }
+
+
+ // If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably
+ // means we've just reached the end of the file. We need to move the valid data down to the end of the buffer
+ // and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to
+ // the size of the L1 so we'll need to seek backwards by any misaligned bytes.
+ size_t alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs);
+
+ // We need to keep track of any unaligned bytes for later use.
+ bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs));
+ if (bs->unalignedByteCount > 0) {
+ bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
+ }
+
+ if (alignedL1LineCount > 0) {
+ size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
+ for (size_t i = alignedL1LineCount; i > 0; --i) {
+ bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
+ }
+
+ bs->nextL2Line = (drflac_uint32)offset;
+ bs->cache = bs->cacheL2[bs->nextL2Line++];
+ return DRFLAC_TRUE;
+ } else {
+ // If we get into this branch it means we weren't able to load any L1-aligned data.
+ bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs);
+ return DRFLAC_FALSE;
+ }
+}
+
+static drflac_bool32 drflac__reload_cache(drflac_bs* bs)
+{
+#ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+#endif
+
+ // Fast path. Try just moving the next value in the L2 cache to the L1 cache.
+ if (drflac__reload_l1_cache_from_l2(bs)) {
+ bs->cache = drflac__be2host__cache_line(bs->cache);
+ bs->consumedBits = 0;
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs->cache;
+#endif
+ return DRFLAC_TRUE;
+ }
+
+ // Slow path.
+
+ // If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last
+ // few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the
+ // data from the unaligned cache.
+ size_t bytesRead = bs->unalignedByteCount;
+ if (bytesRead == 0) {
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- The stream has been exhausted, so marked the bits as consumed.
+ return DRFLAC_FALSE;
+ }
+
+ drflac_assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs));
+ bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
+
+ bs->cache = drflac__be2host__cache_line(bs->unalignedCache);
+ bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property.
+ bs->unalignedByteCount = 0; // <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes.
+
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs->cache >> bs->consumedBits;
+ bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
+#endif
+ return DRFLAC_TRUE;
+}
+
+static void drflac__reset_cache(drflac_bs* bs)
+{
+ bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); // <-- This clears the L2 cache.
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- This clears the L1 cache.
+ bs->cache = 0;
+ bs->unalignedByteCount = 0; // <-- This clears the trailing unaligned bytes.
+ bs->unalignedCache = 0;
+
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = 0;
+ bs->crc16CacheIgnoredBytes = 0;
+#endif
+}
+
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResultOut != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 32);
+
+ if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ // If we want to load all 32-bits from a 32-bit cache we need to do it slightly differently because we can't do
+ // a 32-bit shift on a 32-bit integer. This will never be the case on 64-bit caches, so we can have a slightly
+ // more optimal solution for this.
+#ifdef DRFLAC_64BIT
+ *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
+ bs->consumedBits += bitCount;
+ bs->cache <<= bitCount;
+#else
+ if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
+ bs->consumedBits += bitCount;
+ bs->cache <<= bitCount;
+ } else {
+ // Cannot shift by 32-bits, so need to do it differently.
+ *pResultOut = (drflac_uint32)bs->cache;
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ bs->cache = 0;
+ }
+#endif
+
+ return DRFLAC_TRUE;
+ } else {
+ // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them.
+ drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ drflac_uint32 bitCountLo = bitCount - bitCountHi;
+ drflac_uint32 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
+
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
+ bs->consumedBits += bitCountLo;
+ bs->cache <<= bitCountLo;
+ return DRFLAC_TRUE;
+ }
+}
+
+static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 32);
+
+ drflac_uint32 result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint32 signbit = ((result >> (bitCount-1)) & 0x01);
+ result |= (~signbit + 1) << bitCount;
+
+ *pResult = (drflac_int32)result;
+ return DRFLAC_TRUE;
+}
+
+#ifdef DRFLAC_64BIT
+static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut)
+{
+ drflac_assert(bitCount <= 64);
+ drflac_assert(bitCount > 32);
+
+ drflac_uint32 resultHi;
+ if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint32 resultLo;
+ if (!drflac__read_uint32(bs, 32, &resultLo)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo);
+ return DRFLAC_TRUE;
+}
+#endif
+
+// Function below is unused, but leaving it here in case I need to quickly add it again.
+#if 0
+static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut)
+{
+ drflac_assert(bitCount <= 64);
+
+ drflac_uint64 result;
+ if (!drflac__read_uint64(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint64 signbit = ((result >> (bitCount-1)) & 0x01);
+ result |= (~signbit + 1) << bitCount;
+
+ *pResultOut = (drflac_int64)result;
+ return DRFLAC_TRUE;
+}
+#endif
+
+static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 16);
+
+ drflac_uint32 result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResult = (drflac_uint16)result;
+ return DRFLAC_TRUE;
+}
+
+#if 0
+static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 16);
+
+ drflac_int32 result;
+ if (!drflac__read_int32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResult = (drflac_int16)result;
+ return DRFLAC_TRUE;
+}
+#endif
+
+static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 8);
+
+ drflac_uint32 result;
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResult = (drflac_uint8)result;
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pResult != NULL);
+ drflac_assert(bitCount > 0);
+ drflac_assert(bitCount <= 8);
+
+ drflac_int32 result;
+ if (!drflac__read_int32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ *pResult = (drflac_int8)result;
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek)
+{
+ if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ bs->consumedBits += (drflac_uint32)bitsToSeek;
+ bs->cache <<= bitsToSeek;
+ return DRFLAC_TRUE;
+ } else {
+ // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here.
+ bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ bs->cache = 0;
+
+ // Simple case. Seek in groups of the same number as bits that fit within a cache line.
+#ifdef DRFLAC_64BIT
+ while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ drflac_uint64 bin;
+ if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
+ return DRFLAC_FALSE;
+ }
+ bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ }
+#else
+ while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) {
+ drflac_uint32 bin;
+ if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
+ return DRFLAC_FALSE;
+ }
+ bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ }
+#endif
+
+ // Whole leftover bytes.
+ while (bitsToSeek >= 8) {
+ drflac_uint8 bin;
+ if (!drflac__read_uint8(bs, 8, &bin)) {
+ return DRFLAC_FALSE;
+ }
+ bitsToSeek -= 8;
+ }
+
+ // Leftover bits.
+ if (bitsToSeek > 0) {
+ drflac_uint8 bin;
+ if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) {
+ return DRFLAC_FALSE;
+ }
+ bitsToSeek = 0; // <-- Necessary for the assert below.
+ }
+
+ drflac_assert(bitsToSeek == 0);
+ return DRFLAC_TRUE;
+ }
+}
+
+
+// This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16.
+static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs)
+{
+ drflac_assert(bs != NULL);
+
+ // The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first
+ // thing to do is align to the next byte.
+ if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
+ return DRFLAC_FALSE;
+ }
+
+ for (;;) {
+#ifndef DR_FLAC_NO_CRC
+ drflac__reset_crc16(bs);
+#endif
+
+ drflac_uint8 hi;
+ if (!drflac__read_uint8(bs, 8, &hi)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (hi == 0xFF) {
+ drflac_uint8 lo;
+ if (!drflac__read_uint8(bs, 6, &lo)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (lo == 0x3E) {
+ return DRFLAC_TRUE;
+ } else {
+ if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ }
+ }
+
+ // Should never get here.
+ //return DRFLAC_FALSE;
+}
+
+
+#if !defined(DR_FLAC_NO_SIMD) && defined(DRFLAC_HAS_LZCNT_INTRINSIC)
+#define DRFLAC_IMPLEMENT_CLZ_LZCNT
+#endif
+#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86))
+#define DRFLAC_IMPLEMENT_CLZ_MSVC
+#endif
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x)
+{
+ static drflac_uint32 clz_table_4[] = {
+ 0,
+ 4,
+ 3, 3,
+ 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1
+ };
+
+ drflac_uint32 n = clz_table_4[x >> (sizeof(x)*8 - 4)];
+ if (n == 0) {
+#ifdef DRFLAC_64BIT
+ if ((x & 0xFFFFFFFF00000000ULL) == 0) { n = 32; x <<= 32; }
+ if ((x & 0xFFFF000000000000ULL) == 0) { n += 16; x <<= 16; }
+ if ((x & 0xFF00000000000000ULL) == 0) { n += 8; x <<= 8; }
+ if ((x & 0xF000000000000000ULL) == 0) { n += 4; x <<= 4; }
+#else
+ if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
+ if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
+ if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
+#endif
+ n += clz_table_4[x >> (sizeof(x)*8 - 4)];
+ }
+
+ return n - 1;
+}
+
+#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
+static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported()
+{
+ // If the compiler itself does not support the intrinsic then we'll need to return false.
+#ifdef DRFLAC_HAS_LZCNT_INTRINSIC
+ return drflac__gIsLZCNTSupported;
+#else
+ return DRFLAC_FALSE;
+#endif
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x)
+{
+#if defined(_MSC_VER) && !defined(__clang__)
+ #ifdef DRFLAC_64BIT
+ return (drflac_uint32)__lzcnt64(x);
+ #else
+ return (drflac_uint32)__lzcnt(x);
+ #endif
+#else
+ #if defined(__GNUC__) || defined(__clang__)
+ #ifdef DRFLAC_64BIT
+ return (drflac_uint32)__builtin_clzll((unsigned long long)x);
+ #else
+ return (drflac_uint32)__builtin_clzl((unsigned long)x);
+ #endif
+ #else
+ // Unsupported compiler.
+ #error "This compiler does not support the lzcnt intrinsic."
+ #endif
+#endif
+}
+#endif
+
+#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
+#include <intrin.h> // For BitScanReverse().
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
+{
+ drflac_uint32 n;
+#ifdef DRFLAC_64BIT
+ _BitScanReverse64((unsigned long*)&n, x);
+#else
+ _BitScanReverse((unsigned long*)&n, x);
+#endif
+ return sizeof(x)*8 - n - 1;
+}
+#endif
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x)
+{
+ // This function assumes at least one bit is set. Checking for 0 needs to be done at a higher level, outside this function.
+#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT
+ if (drflac__is_lzcnt_supported()) {
+ return drflac__clz_lzcnt(x);
+ } else
+#endif
+ {
+#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
+ return drflac__clz_msvc(x);
+#else
+ return drflac__clz_software(x);
+#endif
+ }
+}
+
+
+static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
+{
+ drflac_uint32 zeroCounter = 0;
+ while (bs->cache == 0) {
+ zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache);
+ setBitOffsetPlus1 += 1;
+
+ bs->consumedBits += setBitOffsetPlus1;
+ bs->cache <<= setBitOffsetPlus1;
+
+ *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
+ return DRFLAC_TRUE;
+}
+
+
+
+static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(offsetFromStart > 0);
+
+ // Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which
+ // is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit.
+ // To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder.
+ if (offsetFromStart > 0x7FFFFFFF) {
+ drflac_uint64 bytesRemaining = offsetFromStart;
+ if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ bytesRemaining -= 0x7FFFFFFF;
+
+ while (bytesRemaining > 0x7FFFFFFF) {
+ if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ bytesRemaining -= 0x7FFFFFFF;
+ }
+
+ if (bytesRemaining > 0) {
+ if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ // The cache should be reset to force a reload of fresh data from the client.
+ drflac__reset_cache(bs);
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(pNumberOut != NULL);
+
+ drflac_uint8 crc = *pCRCOut;
+
+ unsigned char utf8[7] = {0};
+ if (!drflac__read_uint8(bs, 8, utf8)) {
+ *pNumberOut = 0;
+ return DRFLAC_END_OF_STREAM;
+ }
+ crc = drflac_crc8(crc, utf8[0], 8);
+
+ if ((utf8[0] & 0x80) == 0) {
+ *pNumberOut = utf8[0];
+ *pCRCOut = crc;
+ return DRFLAC_SUCCESS;
+ }
+
+ int byteCount = 1;
+ if ((utf8[0] & 0xE0) == 0xC0) {
+ byteCount = 2;
+ } else if ((utf8[0] & 0xF0) == 0xE0) {
+ byteCount = 3;
+ } else if ((utf8[0] & 0xF8) == 0xF0) {
+ byteCount = 4;
+ } else if ((utf8[0] & 0xFC) == 0xF8) {
+ byteCount = 5;
+ } else if ((utf8[0] & 0xFE) == 0xFC) {
+ byteCount = 6;
+ } else if ((utf8[0] & 0xFF) == 0xFE) {
+ byteCount = 7;
+ } else {
+ *pNumberOut = 0;
+ return DRFLAC_CRC_MISMATCH; // Bad UTF-8 encoding.
+ }
+
+ // Read extra bytes.
+ drflac_assert(byteCount > 1);
+
+ drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
+ for (int i = 1; i < byteCount; ++i) {
+ if (!drflac__read_uint8(bs, 8, utf8 + i)) {
+ *pNumberOut = 0;
+ return DRFLAC_END_OF_STREAM;
+ }
+ crc = drflac_crc8(crc, utf8[i], 8);
+
+ result = (result << 6) | (utf8[i] & 0x3F);
+ }
+
+ *pNumberOut = result;
+ *pCRCOut = crc;
+ return DRFLAC_SUCCESS;
+}
+
+
+
+
+// The next two functions are responsible for calculating the prediction.
+//
+// When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's
+// safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16.
+static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ // 32-bit version.
+
+ // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers.
+ drflac_int32 prediction = 0;
+
+ switch (order)
+ {
+ case 32: prediction += coefficients[31] * pDecodedSamples[-32];
+ case 31: prediction += coefficients[30] * pDecodedSamples[-31];
+ case 30: prediction += coefficients[29] * pDecodedSamples[-30];
+ case 29: prediction += coefficients[28] * pDecodedSamples[-29];
+ case 28: prediction += coefficients[27] * pDecodedSamples[-28];
+ case 27: prediction += coefficients[26] * pDecodedSamples[-27];
+ case 26: prediction += coefficients[25] * pDecodedSamples[-26];
+ case 25: prediction += coefficients[24] * pDecodedSamples[-25];
+ case 24: prediction += coefficients[23] * pDecodedSamples[-24];
+ case 23: prediction += coefficients[22] * pDecodedSamples[-23];
+ case 22: prediction += coefficients[21] * pDecodedSamples[-22];
+ case 21: prediction += coefficients[20] * pDecodedSamples[-21];
+ case 20: prediction += coefficients[19] * pDecodedSamples[-20];
+ case 19: prediction += coefficients[18] * pDecodedSamples[-19];
+ case 18: prediction += coefficients[17] * pDecodedSamples[-18];
+ case 17: prediction += coefficients[16] * pDecodedSamples[-17];
+ case 16: prediction += coefficients[15] * pDecodedSamples[-16];
+ case 15: prediction += coefficients[14] * pDecodedSamples[-15];
+ case 14: prediction += coefficients[13] * pDecodedSamples[-14];
+ case 13: prediction += coefficients[12] * pDecodedSamples[-13];
+ case 12: prediction += coefficients[11] * pDecodedSamples[-12];
+ case 11: prediction += coefficients[10] * pDecodedSamples[-11];
+ case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
+ case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
+ case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
+ case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
+ case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
+ case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
+ case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
+ case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
+ case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
+ case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
+ }
+
+ return (drflac_int32)(prediction >> shift);
+}
+
+static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(order <= 32);
+
+ // 64-bit version.
+
+ // This method is faster on the 32-bit build when compiling with VC++. See note below.
+#ifndef DRFLAC_64BIT
+ drflac_int64 prediction;
+ if (order == 8)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ }
+ else if (order == 7)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ }
+ else if (order == 3)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ }
+ else if (order == 6)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ }
+ else if (order == 5)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ }
+ else if (order == 4)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ }
+ else if (order == 12)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
+ prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
+ prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
+ prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
+ }
+ else if (order == 2)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ }
+ else if (order == 1)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ }
+ else if (order == 10)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
+ prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
+ }
+ else if (order == 9)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
+ }
+ else if (order == 11)
+ {
+ prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1];
+ prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2];
+ prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3];
+ prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4];
+ prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5];
+ prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6];
+ prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7];
+ prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8];
+ prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9];
+ prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10];
+ prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
+ }
+ else
+ {
+ prediction = 0;
+ for (int j = 0; j < (int)order; ++j) {
+ prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1];
+ }
+ }
+#endif
+
+ // VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some
+ // reason. The ugly version above is faster so we'll just switch between the two depending on the target platform.
+#ifdef DRFLAC_64BIT
+ drflac_int64 prediction = 0;
+
+ switch (order)
+ {
+ case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32];
+ case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31];
+ case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30];
+ case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29];
+ case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28];
+ case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27];
+ case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26];
+ case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25];
+ case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24];
+ case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23];
+ case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22];
+ case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21];
+ case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20];
+ case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19];
+ case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18];
+ case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17];
+ case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16];
+ case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15];
+ case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14];
+ case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13];
+ case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12];
+ case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11];
+ case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10];
+ case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9];
+ case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8];
+ case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7];
+ case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6];
+ case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5];
+ case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4];
+ case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3];
+ case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2];
+ case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1];
+ }
+#endif
+
+ return (drflac_int32)(prediction >> shift);
+}
+
+#if 0
+// Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the
+// sake of readability and should only be used as a reference.
+static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(pSamplesOut != NULL);
+
+ for (drflac_uint32 i = 0; i < count; ++i) {
+ drflac_uint32 zeroCounter = 0;
+ for (;;) {
+ drflac_uint8 bit;
+ if (!drflac__read_uint8(bs, 1, &bit)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (bit == 0) {
+ zeroCounter += 1;
+ } else {
+ break;
+ }
+ }
+
+ drflac_uint32 decodedRice;
+ if (riceParam > 0) {
+ if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ decodedRice = 0;
+ }
+
+ decodedRice |= (zeroCounter << riceParam);
+ if ((decodedRice & 0x01)) {
+ decodedRice = ~(decodedRice >> 1);
+ } else {
+ decodedRice = (decodedRice >> 1);
+ }
+
+
+ if (bitsPerSample > 16) {
+ pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
+ } else {
+ pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+#endif
+
+#if 0
+static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_uint32 zeroCounter = 0;
+ for (;;) {
+ drflac_uint8 bit;
+ if (!drflac__read_uint8(bs, 1, &bit)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (bit == 0) {
+ zeroCounter += 1;
+ } else {
+ break;
+ }
+ }
+
+ drflac_uint32 decodedRice;
+ if (riceParam > 0) {
+ if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ decodedRice = 0;
+ }
+
+ *pZeroCounterOut = zeroCounter;
+ *pRiceParamPartOut = decodedRice;
+ return DRFLAC_TRUE;
+}
+#endif
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_assert(riceParam > 0); // <-- riceParam should never be 0. drflac__read_rice_parts__param_equals_zero() should be used instead for this case.
+
+ drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
+
+ drflac_uint32 zeroCounter = 0;
+ while (bs->cache == 0) {
+ zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache);
+ zeroCounter += setBitOffsetPlus1;
+ setBitOffsetPlus1 += 1;
+
+
+ drflac_uint32 riceParamPart;
+ drflac_uint32 riceLength = setBitOffsetPlus1 + riceParam;
+ if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
+
+ bs->consumedBits += riceLength;
+ bs->cache <<= riceLength;
+ } else {
+ bs->consumedBits += riceLength;
+ bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); // <-- Equivalent to "if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { bs->cache <<= setBitOffsetPlus1; }"
+
+ // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them.
+ drflac_uint32 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ drflac_cache_t resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); // <-- Use DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE() if ever this function allows riceParam=0.
+
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+#ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+#endif
+ bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs->consumedBits = 0;
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs->cache;
+#endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
+
+ bs->consumedBits += bitCountLo;
+ bs->cache <<= bitCountLo;
+ }
+
+ *pZeroCounterOut = zeroCounter;
+ *pRiceParamPartOut = riceParamPart;
+ return DRFLAC_TRUE;
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts__param_equals_zero(drflac_bs* bs, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(0);
+
+ drflac_uint32 zeroCounter = 0;
+ while (bs->cache == 0) {
+ zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache);
+ zeroCounter += setBitOffsetPlus1;
+ setBitOffsetPlus1 += 1;
+
+
+ drflac_uint32 riceParamPart;
+ drflac_uint32 riceLength = setBitOffsetPlus1;
+ if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
+
+ bs->consumedBits += riceLength;
+ bs->cache <<= riceLength;
+ } else {
+ // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them.
+ drflac_uint32 bitCountLo = riceLength + bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
+
+ if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) {
+#ifndef DR_FLAC_NO_CRC
+ drflac__update_crc16(bs);
+#endif
+ bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
+ bs->consumedBits = 0;
+#ifndef DR_FLAC_NO_CRC
+ bs->crc16Cache = bs->cache;
+#endif
+ } else {
+ // Slow path. We need to fetch more data from the client.
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ riceParamPart = (drflac_uint32)(DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
+
+ bs->consumedBits += bitCountLo;
+ bs->cache <<= bitCountLo;
+ }
+
+ *pZeroCounterOut = zeroCounter;
+ *pRiceParamPartOut = riceParamPart;
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(pSamplesOut != NULL);
+
+ static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ drflac_uint32 zeroCountPart0;
+ drflac_uint32 zeroCountPart1;
+ drflac_uint32 zeroCountPart2;
+ drflac_uint32 zeroCountPart3;
+ drflac_uint32 riceParamPart0;
+ drflac_uint32 riceParamPart1;
+ drflac_uint32 riceParamPart2;
+ drflac_uint32 riceParamPart3;
+ drflac_uint32 i4 = 0;
+ drflac_uint32 count4 = count >> 2;
+ while (i4 < count4) {
+ // Rice extraction.
+ if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
+ !drflac__read_rice_parts(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
+ !drflac__read_rice_parts(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
+ !drflac__read_rice_parts(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
+ return DRFLAC_FALSE;
+ }
+
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart1 |= (zeroCountPart1 << riceParam);
+ riceParamPart2 |= (zeroCountPart2 << riceParam);
+ riceParamPart3 |= (zeroCountPart3 << riceParam);
+
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
+ riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
+ riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
+
+ if (bitsPerSample > 16) {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3);
+ } else {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
+ }
+
+ i4 += 1;
+ pSamplesOut += 4;
+ }
+
+ drflac_uint32 i = i4 << 2;
+ while (i < count) {
+ // Rice extraction.
+ if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Rice reconstruction.
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ //riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);
+
+ // Sample reconstruction.
+ if (bitsPerSample > 16) {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+ } else {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ }
+
+ i += 1;
+ pSamplesOut += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__param_equals_zero(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(pSamplesOut != NULL);
+
+ static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ drflac_uint32 zeroCountPart0;
+ drflac_uint32 zeroCountPart1;
+ drflac_uint32 zeroCountPart2;
+ drflac_uint32 zeroCountPart3;
+ drflac_uint32 riceParamPart0;
+ drflac_uint32 riceParamPart1;
+ drflac_uint32 riceParamPart2;
+ drflac_uint32 riceParamPart3;
+ drflac_uint32 i4 = 0;
+ drflac_uint32 count4 = count >> 2;
+ while (i4 < count4) {
+ // Rice extraction.
+ if (!drflac__read_rice_parts__param_equals_zero(bs, &zeroCountPart0, &riceParamPart0) ||
+ !drflac__read_rice_parts__param_equals_zero(bs, &zeroCountPart1, &riceParamPart1) ||
+ !drflac__read_rice_parts__param_equals_zero(bs, &zeroCountPart2, &riceParamPart2) ||
+ !drflac__read_rice_parts__param_equals_zero(bs, &zeroCountPart3, &riceParamPart3)) {
+ return DRFLAC_FALSE;
+ }
+
+ riceParamPart0 |= zeroCountPart0;
+ riceParamPart1 |= zeroCountPart1;
+ riceParamPart2 |= zeroCountPart2;
+ riceParamPart3 |= zeroCountPart3;
+
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
+ riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
+ riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
+
+ if (bitsPerSample > 16) {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3);
+ } else {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1);
+ pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2);
+ pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3);
+ }
+
+ i4 += 1;
+ pSamplesOut += 4;
+ }
+
+ drflac_uint32 i = i4 << 2;
+ while (i < count) {
+ // Rice extraction.
+ if (!drflac__read_rice_parts__param_equals_zero(bs, &zeroCountPart0, &riceParamPart0)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Rice reconstruction.
+ riceParamPart0 |= zeroCountPart0;
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+
+ // Sample reconstruction.
+ if (bitsPerSample > 16) {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0);
+ } else {
+ pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0);
+ }
+
+ i += 1;
+ pSamplesOut += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+#if 0
+ return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+#else
+ if (riceParam != 0) {
+ return drflac__decode_samples_with_residual__rice__simple(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ } else {
+ return drflac__decode_samples_with_residual__rice__param_equals_zero(bs, bitsPerSample, count, order, shift, coefficients, pSamplesOut);
+ }
+#endif
+}
+
+// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes.
+static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+
+ drflac_uint32 zeroCountPart;
+ drflac_uint32 riceParamPart;
+
+ if (riceParam != 0) {
+ for (drflac_uint32 i = 0; i < count; ++i) {
+ if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ for (drflac_uint32 i = 0; i < count; ++i) {
+ if (!drflac__read_rice_parts__param_equals_zero(bs, &zeroCountPart, &riceParamPart)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(count > 0);
+ drflac_assert(unencodedBitsPerSample <= 31); // <-- unencodedBitsPerSample is a 5 bit number, so cannot exceed 31.
+ drflac_assert(pSamplesOut != NULL);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ if (unencodedBitsPerSample > 0) {
+ if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ pSamplesOut[i] = 0;
+ }
+
+ if (bitsPerSample > 16) {
+ pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i);
+ } else {
+ pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i);
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+
+// Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called
+// when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be ignored. The
+// <blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(blockSize != 0);
+ drflac_assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode?
+
+ drflac_uint8 residualMethod;
+ if (!drflac__read_uint8(bs, 2, &residualMethod)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ return DRFLAC_FALSE; // Unknown or unsupported residual coding method.
+ }
+
+ // Ignore the first <order> values.
+ pDecodedSamples += order;
+
+
+ drflac_uint8 partitionOrder;
+ if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
+ return DRFLAC_FALSE;
+ }
+
+ // From the FLAC spec:
+ // The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
+ if (partitionOrder > 8) {
+ return DRFLAC_FALSE;
+ }
+
+ // Validation check.
+ if ((blockSize / (1 << partitionOrder)) <= order) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ drflac_uint32 partitionsRemaining = (1 << partitionOrder);
+ for (;;) {
+ drflac_uint8 riceParam = 0;
+ if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
+ if (!drflac__read_uint8(bs, 4, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 15) {
+ riceParam = 0xFF;
+ }
+ } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ if (!drflac__read_uint8(bs, 5, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 31) {
+ riceParam = 0xFF;
+ }
+ }
+
+ if (riceParam != 0xFF) {
+ if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ unsigned char unencodedBitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ pDecodedSamples += samplesInPartition;
+
+
+ if (partitionsRemaining == 1) {
+ break;
+ }
+
+ partitionsRemaining -= 1;
+
+ if (partitionOrder != 0) {
+ samplesInPartition = blockSize / (1 << partitionOrder);
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+// Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called
+// when the decoder is sitting at the very start of the RESIDUAL block. The first <order> residuals will be set to 0. The
+// <blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(blockSize != 0);
+
+ drflac_uint8 residualMethod;
+ if (!drflac__read_uint8(bs, 2, &residualMethod)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ return DRFLAC_FALSE; // Unknown or unsupported residual coding method.
+ }
+
+ drflac_uint8 partitionOrder;
+ if (!drflac__read_uint8(bs, 4, &partitionOrder)) {
+ return DRFLAC_FALSE;
+ }
+
+ // From the FLAC spec:
+ // The Rice partition order in a Rice-coded residual section must be less than or equal to 8.
+ if (partitionOrder > 8) {
+ return DRFLAC_FALSE;
+ }
+
+ // Validation check.
+ if ((blockSize / (1 << partitionOrder)) <= order) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ drflac_uint32 partitionsRemaining = (1 << partitionOrder);
+ for (;;)
+ {
+ drflac_uint8 riceParam = 0;
+ if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
+ if (!drflac__read_uint8(bs, 4, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 15) {
+ riceParam = 0xFF;
+ }
+ } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ if (!drflac__read_uint8(bs, 5, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 31) {
+ riceParam = 0xFF;
+ }
+ }
+
+ if (riceParam != 0xFF) {
+ if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ unsigned char unencodedBitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+
+ if (partitionsRemaining == 1) {
+ break;
+ }
+
+ partitionsRemaining -= 1;
+ samplesInPartition = blockSize / (1 << partitionOrder);
+ }
+
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples)
+{
+ // Only a single sample needs to be decoded here.
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ // We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely)
+ // we'll want to look at a more efficient way.
+ for (drflac_uint32 i = 0; i < blockSize; ++i) {
+ pDecodedSamples[i] = sample;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples)
+{
+ for (drflac_uint32 i = 0; i < blockSize; ++i) {
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
+{
+ drflac_int32 lpcCoefficientsTable[5][4] = {
+ {0, 0, 0, 0},
+ {1, 0, 0, 0},
+ {2, -1, 0, 0},
+ {3, -3, 1, 0},
+ {4, -6, 4, -1}
+ };
+
+ // Warm up samples and coefficients.
+ for (drflac_uint32 i = 0; i < lpcOrder; ++i) {
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+
+ if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
+ return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
+{
+ drflac_uint8 i;
+
+ // Warm up samples.
+ for (i = 0; i < lpcOrder; ++i) {
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, bitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+ drflac_uint8 lpcPrecision;
+ if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
+ return DRFLAC_FALSE;
+ }
+ if (lpcPrecision == 15) {
+ return DRFLAC_FALSE; // Invalid.
+ }
+ lpcPrecision += 1;
+
+
+ drflac_int8 lpcShift;
+ if (!drflac__read_int8(bs, 5, &lpcShift)) {
+ return DRFLAC_FALSE;
+ }
+
+
+ drflac_int32 coefficients[32];
+ for (i = 0; i < lpcOrder; ++i) {
+ if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) {
+ return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(header != NULL);
+
+ const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
+ const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; // -1 = reserved.
+
+ // Keep looping until we find a valid sync code.
+ for (;;) {
+ if (!drflac__find_and_seek_to_next_sync_code(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint8 crc8 = 0xCE; // 0xCE = drflac_crc8(0, 0x3FFE, 14);
+
+ drflac_uint8 reserved = 0;
+ if (!drflac__read_uint8(bs, 1, &reserved)) {
+ return DRFLAC_FALSE;
+ }
+ if (reserved == 1) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, reserved, 1);
+
+
+ drflac_uint8 blockingStrategy = 0;
+ if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, blockingStrategy, 1);
+
+
+ drflac_uint8 blockSize = 0;
+ if (!drflac__read_uint8(bs, 4, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ if (blockSize == 0) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, blockSize, 4);
+
+
+ drflac_uint8 sampleRate = 0;
+ if (!drflac__read_uint8(bs, 4, &sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, sampleRate, 4);
+
+
+ drflac_uint8 channelAssignment = 0;
+ if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
+ return DRFLAC_FALSE;
+ }
+ if (channelAssignment > 10) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, channelAssignment, 4);
+
+
+ drflac_uint8 bitsPerSample = 0;
+ if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+ if (bitsPerSample == 3 || bitsPerSample == 7) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, bitsPerSample, 3);
+
+
+ if (!drflac__read_uint8(bs, 1, &reserved)) {
+ return DRFLAC_FALSE;
+ }
+ if (reserved == 1) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, reserved, 1);
+
+
+ drflac_bool32 isVariableBlockSize = blockingStrategy == 1;
+ if (isVariableBlockSize) {
+ drflac_uint64 sampleNumber;
+ drflac_result result = drflac__read_utf8_coded_number(bs, &sampleNumber, &crc8);
+ if (result != DRFLAC_SUCCESS) {
+ if (result == DRFLAC_END_OF_STREAM) {
+ return DRFLAC_FALSE;
+ } else {
+ continue;
+ }
+ }
+ header->frameNumber = 0;
+ header->sampleNumber = sampleNumber;
+ } else {
+ drflac_uint64 frameNumber = 0;
+ drflac_result result = drflac__read_utf8_coded_number(bs, &frameNumber, &crc8);
+ if (result != DRFLAC_SUCCESS) {
+ if (result == DRFLAC_END_OF_STREAM) {
+ return DRFLAC_FALSE;
+ } else {
+ continue;
+ }
+ }
+ header->frameNumber = (drflac_uint32)frameNumber; // <-- Safe cast.
+ header->sampleNumber = 0;
+ }
+
+
+ if (blockSize == 1) {
+ header->blockSize = 192;
+ } else if (blockSize >= 2 && blockSize <= 5) {
+ header->blockSize = 576 * (1 << (blockSize - 2));
+ } else if (blockSize == 6) {
+ if (!drflac__read_uint16(bs, 8, &header->blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->blockSize, 8);
+ header->blockSize += 1;
+ } else if (blockSize == 7) {
+ if (!drflac__read_uint16(bs, 16, &header->blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->blockSize, 16);
+ header->blockSize += 1;
+ } else {
+ header->blockSize = 256 * (1 << (blockSize - 8));
+ }
+
+
+ if (sampleRate <= 11) {
+ header->sampleRate = sampleRateTable[sampleRate];
+ } else if (sampleRate == 12) {
+ if (!drflac__read_uint32(bs, 8, &header->sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->sampleRate, 8);
+ header->sampleRate *= 1000;
+ } else if (sampleRate == 13) {
+ if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->sampleRate, 16);
+ } else if (sampleRate == 14) {
+ if (!drflac__read_uint32(bs, 16, &header->sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->sampleRate, 16);
+ header->sampleRate *= 10;
+ } else {
+ continue; // Invalid. Assume an invalid block.
+ }
+
+
+ header->channelAssignment = channelAssignment;
+
+ header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
+ if (header->bitsPerSample == 0) {
+ header->bitsPerSample = streaminfoBitsPerSample;
+ }
+
+ if (!drflac__read_uint8(bs, 8, &header->crc8)) {
+ return DRFLAC_FALSE;
+ }
+
+#ifndef DR_FLAC_NO_CRC
+ if (header->crc8 != crc8) {
+ continue; // CRC mismatch. Loop back to the top and find the next sync code.
+ }
+#endif
+ return DRFLAC_TRUE;
+ }
+}
+
+static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe)
+{
+ drflac_uint8 header;
+ if (!drflac__read_uint8(bs, 8, &header)) {
+ return DRFLAC_FALSE;
+ }
+
+ // First bit should always be 0.
+ if ((header & 0x80) != 0) {
+ return DRFLAC_FALSE;
+ }
+
+ int type = (header & 0x7E) >> 1;
+ if (type == 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT;
+ } else if (type == 1) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM;
+ } else {
+ if ((type & 0x20) != 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_LPC;
+ pSubframe->lpcOrder = (type & 0x1F) + 1;
+ } else if ((type & 0x08) != 0) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED;
+ pSubframe->lpcOrder = (type & 0x07);
+ if (pSubframe->lpcOrder > 4) {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
+ pSubframe->lpcOrder = 0;
+ }
+ } else {
+ pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED;
+ }
+ }
+
+ if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) {
+ return DRFLAC_FALSE;
+ }
+
+ // Wasted bits per sample.
+ pSubframe->wastedBitsPerSample = 0;
+ if ((header & 0x01) == 1) {
+ unsigned int wastedBitsPerSample;
+ if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+ pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(frame != NULL);
+
+ drflac_subframe* pSubframe = frame->subframes + subframeIndex;
+ if (!drflac__read_subframe_header(bs, pSubframe)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Side channels require an extra bit per sample. Took a while to figure that one out...
+ pSubframe->bitsPerSample = frame->header.bitsPerSample;
+ if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
+ pSubframe->bitsPerSample += 1;
+ } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
+ pSubframe->bitsPerSample += 1;
+ }
+
+ // Need to handle wasted bits per sample.
+ if (pSubframe->wastedBitsPerSample >= pSubframe->bitsPerSample) {
+ return DRFLAC_FALSE;
+ }
+ pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample;
+ pSubframe->pDecodedSamples = pDecodedSamplesOut;
+
+ switch (pSubframe->subframeType)
+ {
+ case DRFLAC_SUBFRAME_CONSTANT:
+ {
+ drflac__decode_samples__constant(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_VERBATIM:
+ {
+ drflac__decode_samples__verbatim(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_FIXED:
+ {
+ drflac__decode_samples__fixed(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples);
+ } break;
+
+ case DRFLAC_SUBFRAME_LPC:
+ {
+ drflac__decode_samples__lpc(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples);
+ } break;
+
+ default: return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
+{
+ drflac_assert(bs != NULL);
+ drflac_assert(frame != NULL);
+
+ drflac_subframe* pSubframe = frame->subframes + subframeIndex;
+ if (!drflac__read_subframe_header(bs, pSubframe)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Side channels require an extra bit per sample. Took a while to figure that one out...
+ pSubframe->bitsPerSample = frame->header.bitsPerSample;
+ if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
+ pSubframe->bitsPerSample += 1;
+ } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
+ pSubframe->bitsPerSample += 1;
+ }
+
+ // Need to handle wasted bits per sample.
+ if (pSubframe->wastedBitsPerSample >= pSubframe->bitsPerSample) {
+ return DRFLAC_FALSE;
+ }
+ pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample;
+ pSubframe->pDecodedSamples = NULL;
+
+ switch (pSubframe->subframeType)
+ {
+ case DRFLAC_SUBFRAME_CONSTANT:
+ {
+ if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_VERBATIM:
+ {
+ unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_FIXED:
+ {
+ unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_LPC:
+ {
+ unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+
+ unsigned char lpcPrecision;
+ if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
+ return DRFLAC_FALSE;
+ }
+ if (lpcPrecision == 15) {
+ return DRFLAC_FALSE; // Invalid.
+ }
+ lpcPrecision += 1;
+
+
+ bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift.
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ default: return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+
+static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment)
+{
+ drflac_assert(channelAssignment <= 10);
+
+ drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
+ return lookup[channelAssignment];
+}
+
+static drflac_result drflac__decode_frame(drflac* pFlac)
+{
+ // This function should be called while the stream is sitting on the first byte after the frame header.
+ drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes));
+
+ // The frame block size must never be larger than the maximum block size defined by the FLAC stream.
+ if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) {
+ return DRFLAC_ERROR;
+ }
+
+ // The number of channels in the frame must match the channel count from the STREAMINFO block.
+ int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ if (channelCount != (int)pFlac->channels) {
+ return DRFLAC_ERROR;
+ }
+
+ for (int i = 0; i < channelCount; ++i) {
+ if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) {
+ return DRFLAC_ERROR;
+ }
+ }
+
+ drflac_uint8 paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7;
+ if (paddingSizeInBits > 0) {
+ drflac_uint8 padding = 0;
+ if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ }
+
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
+#endif
+ drflac_uint16 desiredCRC16;
+ if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
+ return DRFLAC_END_OF_STREAM;
+ }
+
+#ifndef DR_FLAC_NO_CRC
+ if (actualCRC16 != desiredCRC16) {
+ return DRFLAC_CRC_MISMATCH; // CRC mismatch.
+ }
+#endif
+
+ pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount;
+
+ return DRFLAC_SUCCESS;
+}
+
+static drflac_result drflac__seek_frame(drflac* pFlac)
+{
+ int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ for (int i = 0; i < channelCount; ++i) {
+ if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) {
+ return DRFLAC_ERROR;
+ }
+ }
+
+ // Padding.
+ if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
+ return DRFLAC_ERROR;
+ }
+
+ // CRC.
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs);
+#endif
+ drflac_uint16 desiredCRC16;
+ if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
+ return DRFLAC_END_OF_STREAM;
+ }
+
+#ifndef DR_FLAC_NO_CRC
+ if (actualCRC16 != desiredCRC16) {
+ return DRFLAC_CRC_MISMATCH; // CRC mismatch.
+ }
+#endif
+
+ return DRFLAC_SUCCESS;
+}
+
+static drflac_bool32 drflac__read_and_decode_next_frame(drflac* pFlac)
+{
+ drflac_assert(pFlac != NULL);
+
+ for (;;) {
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_result result = drflac__decode_frame(pFlac);
+ if (result != DRFLAC_SUCCESS) {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ continue; // CRC mismatch. Skip to the next frame.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ return DRFLAC_TRUE;
+ }
+}
+
+
+static void drflac__get_current_frame_sample_range(drflac* pFlac, drflac_uint64* pFirstSampleInFrameOut, drflac_uint64* pLastSampleInFrameOut)
+{
+ drflac_assert(pFlac != NULL);
+
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+
+ drflac_uint64 firstSampleInFrame = pFlac->currentFrame.header.sampleNumber;
+ if (firstSampleInFrame == 0) {
+ firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount;
+ }
+
+ drflac_uint64 lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount);
+ if (lastSampleInFrame > 0) {
+ lastSampleInFrame -= 1; // Needs to be zero based.
+ }
+
+ if (pFirstSampleInFrameOut) *pFirstSampleInFrameOut = firstSampleInFrame;
+ if (pLastSampleInFrameOut) *pLastSampleInFrameOut = lastSampleInFrame;
+}
+
+static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
+{
+ drflac_assert(pFlac != NULL);
+
+ drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos);
+
+ drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame));
+ pFlac->currentSample = 0;
+
+ return result;
+}
+
+static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac)
+{
+ // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section.
+ drflac_assert(pFlac != NULL);
+ return drflac__seek_frame(pFlac);
+}
+
+static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ drflac_assert(pFlac != NULL);
+
+ drflac_bool32 isMidFrame = DRFLAC_FALSE;
+
+ // If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file.
+ drflac_uint64 runningSampleCount;
+ if (sampleIndex >= pFlac->currentSample) {
+ // Seeking forward. Need to seek from the current position.
+ runningSampleCount = pFlac->currentSample;
+
+ // The frame header for the first frame may not yet have been read. We need to do that if necessary.
+ if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ isMidFrame = DRFLAC_TRUE;
+ }
+ } else {
+ // Seeking backwards. Need to seek from the start of the file.
+ runningSampleCount = 0;
+
+ // Move back to the start.
+ if (!drflac__seek_to_first_frame(pFlac)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Decode the first frame in preparation for sample-exact seeking below.
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ // We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its
+ // header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame.
+ for (;;) {
+ drflac_uint64 firstSampleInFrame = 0;
+ drflac_uint64 lastSampleInFrame = 0;
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
+
+ drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
+ // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend
+ // it never existed and keep iterating.
+ drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount;
+
+ if (!isMidFrame) {
+ drflac_result result = drflac__decode_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
+ return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail).
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // We started seeking mid-frame which means we need to skip the frame decoding part.
+ return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode;
+ }
+ } else {
+ // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ // frame never existed and leave the running sample count untouched.
+ if (!isMidFrame) {
+ drflac_result result = drflac__seek_to_next_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningSampleCount += sampleCountInThisFrame;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with
+ // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ runningSampleCount += pFlac->currentFrame.samplesRemaining;
+ pFlac->currentFrame.samplesRemaining = 0;
+ isMidFrame = DRFLAC_FALSE;
+ }
+ }
+
+ next_iteration:
+ // Grab the next frame in preparation for the next iteration.
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+}
+
+
+static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ drflac_assert(pFlac != NULL);
+
+ if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
+ return DRFLAC_FALSE;
+ }
+
+
+ drflac_uint32 iClosestSeekpoint = 0;
+ for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
+ if (pFlac->pSeekpoints[iSeekpoint].firstSample*pFlac->channels >= sampleIndex) {
+ break;
+ }
+
+ iClosestSeekpoint = iSeekpoint;
+ }
+
+
+ drflac_bool32 isMidFrame = DRFLAC_FALSE;
+
+ // At this point we should have found the seekpoint closest to our sample. If we are seeking forward and the closest seekpoint is _before_ the current sample, we
+ // just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample.
+ drflac_uint64 runningSampleCount;
+ if ((sampleIndex >= pFlac->currentSample) && (pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels <= pFlac->currentSample)) {
+ // Optimized case. Just seek forward from where we are.
+ runningSampleCount = pFlac->currentSample;
+
+ // The frame header for the first frame may not yet have been read. We need to do that if necessary.
+ if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ isMidFrame = DRFLAC_TRUE;
+ }
+ } else {
+ // Slower case. Seek to the start of the seekpoint and then seek forward from there.
+ runningSampleCount = pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels;
+
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + pFlac->pSeekpoints[iClosestSeekpoint].frameOffset)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below.
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ for (;;) {
+ drflac_uint64 firstSampleInFrame = 0;
+ drflac_uint64 lastSampleInFrame = 0;
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
+
+ drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
+ // The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend
+ // it never existed and keep iterating.
+ drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount;
+
+ if (!isMidFrame) {
+ drflac_result result = drflac__decode_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
+ return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail).
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // We started seeking mid-frame which means we need to skip the frame decoding part.
+ return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode;
+ }
+ } else {
+ // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ // frame never existed and leave the running sample count untouched.
+ if (!isMidFrame) {
+ drflac_result result = drflac__seek_to_next_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningSampleCount += sampleCountInThisFrame;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ goto next_iteration; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with
+ // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ runningSampleCount += pFlac->currentFrame.samplesRemaining;
+ pFlac->currentFrame.samplesRemaining = 0;
+ isMidFrame = DRFLAC_FALSE;
+ }
+ }
+
+ next_iteration:
+ // Grab the next frame in preparation for the next iteration.
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+}
+
+
+#ifndef DR_FLAC_NO_OGG
+typedef struct
+{
+ drflac_uint8 capturePattern[4]; // Should be "OggS"
+ drflac_uint8 structureVersion; // Always 0.
+ drflac_uint8 headerType;
+ drflac_uint64 granulePosition;
+ drflac_uint32 serialNumber;
+ drflac_uint32 sequenceNumber;
+ drflac_uint32 checksum;
+ drflac_uint8 segmentCount;
+ drflac_uint8 segmentTable[255];
+} drflac_ogg_page_header;
+#endif
+
+typedef struct
+{
+ drflac_read_proc onRead;
+ drflac_seek_proc onSeek;
+ drflac_meta_proc onMeta;
+ drflac_container container;
+ void* pUserData;
+ void* pUserDataMD;
+ drflac_uint32 sampleRate;
+ drflac_uint8 channels;
+ drflac_uint8 bitsPerSample;
+ drflac_uint64 totalSampleCount;
+ drflac_uint16 maxBlockSize;
+ drflac_uint64 runningFilePos;
+ drflac_bool32 hasStreamInfoBlock;
+ drflac_bool32 hasMetadataBlocks;
+ drflac_bs bs; // <-- A bit streamer is required for loading data during initialization.
+ drflac_frame_header firstFrameHeader; // <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block.
+
+#ifndef DR_FLAC_NO_OGG
+ drflac_uint32 oggSerial;
+ drflac_uint64 oggFirstBytePos;
+ drflac_ogg_page_header oggBosHeader;
+#endif
+} drflac_init_info;
+
+static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
+{
+ blockHeader = drflac__be2host_32(blockHeader);
+ *isLastBlock = (blockHeader & (0x01 << 31)) >> 31;
+ *blockType = (blockHeader & (0x7F << 24)) >> 24;
+ *blockSize = (blockHeader & 0xFFFFFF);
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize)
+{
+ drflac_uint32 blockHeader;
+ if (onRead(pUserData, &blockHeader, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
+ return DRFLAC_TRUE;
+}
+
+drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo)
+{
+ // min/max block size.
+ drflac_uint32 blockSizes;
+ if (onRead(pUserData, &blockSizes, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ // min/max frame size.
+ drflac_uint64 frameSizes = 0;
+ if (onRead(pUserData, &frameSizes, 6) != 6) {
+ return DRFLAC_FALSE;
+ }
+
+ // Sample rate, channels, bits per sample and total sample count.
+ drflac_uint64 importantProps;
+ if (onRead(pUserData, &importantProps, 8) != 8) {
+ return DRFLAC_FALSE;
+ }
+
+ // MD5
+ drflac_uint8 md5[16];
+ if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
+ return DRFLAC_FALSE;
+ }
+
+ blockSizes = drflac__be2host_32(blockSizes);
+ frameSizes = drflac__be2host_64(frameSizes);
+ importantProps = drflac__be2host_64(importantProps);
+
+ pStreamInfo->minBlockSize = (blockSizes & 0xFFFF0000) >> 16;
+ pStreamInfo->maxBlockSize = blockSizes & 0x0000FFFF;
+ pStreamInfo->minFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0xFFFFFF0000000000) >> 40);
+ pStreamInfo->maxFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0x000000FFFFFF0000) >> 16);
+ pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (drflac_uint64)0xFFFFF00000000000) >> 44);
+ pStreamInfo->channels = (drflac_uint8 )((importantProps & (drflac_uint64)0x00000E0000000000) >> 41) + 1;
+ pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (drflac_uint64)0x000001F000000000) >> 36) + 1;
+ pStreamInfo->totalSampleCount = (importantProps & (drflac_uint64)0x0000000FFFFFFFFF) * pStreamInfo->channels;
+ drflac_copy_memory(pStreamInfo->md5, md5, sizeof(md5));
+
+ return DRFLAC_TRUE;
+}
+
+drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize)
+{
+ // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that
+ // we'll be sitting on byte 42.
+ drflac_uint64 runningFilePos = 42;
+ drflac_uint64 seektablePos = 0;
+ drflac_uint32 seektableSize = 0;
+
+ for (;;) {
+ drflac_uint8 isLastBlock = 0;
+ drflac_uint8 blockType;
+ drflac_uint32 blockSize;
+ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ runningFilePos += 4;
+
+
+ drflac_metadata metadata;
+ metadata.type = blockType;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+
+ switch (blockType)
+ {
+ case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
+ {
+ if (blockSize < 4) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData);
+ metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32));
+ metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32);
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
+ {
+ seektablePos = runningFilePos;
+ seektableSize = blockSize;
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint);
+ metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData;
+
+ // Endian swap.
+ for (drflac_uint32 iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
+ drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
+ pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample);
+ pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset);
+ pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount);
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
+ {
+ if (blockSize < 8) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ const char* pRunningData = (const char*)pRawData;
+ const char* const pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ // Need space for the rest of the block
+ if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
+ metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ // Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment
+ if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.vorbis_comment.pComments = pRunningData;
+
+ // Check that the comments section is valid before passing it to the callback
+ for (drflac_uint32 i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
+ if (pRunningDataEnd - pRunningData < 4) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ const drflac_uint32 commentLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ pRunningData += commentLength;
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
+ {
+ if (blockSize < 396) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ char* pRunningData = (char*)pRawData;
+ const char* const pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
+ metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8;
+ metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
+ metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
+ metadata.data.cuesheet.pTrackData = pRunningData;
+
+ // Check that the cuesheet tracks are valid before passing it to the callback
+ for (drflac_uint8 i = 0; i < metadata.data.cuesheet.trackCount; ++i) {
+ if (pRunningDataEnd - pRunningData < 36) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ // Skip to the index point count
+ pRunningData += 35;
+ const drflac_uint8 indexCount = pRunningData[0]; pRunningData += 1;
+ const drflac_uint32 indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index);
+ if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ // Endian swap.
+ for (drflac_uint8 index = 0; index < indexCount; ++index) {
+ drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData;
+ pRunningData += sizeof(drflac_cuesheet_track_index);
+ pTrack->offset = drflac__be2host_64(pTrack->offset);
+ }
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
+ {
+ if (blockSize < 32) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ const char* pRunningData = (const char*)pRawData;
+ const char* const pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ metadata.data.picture.type = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.mimeLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ // Need space for the rest of the block
+ if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
+ metadata.data.picture.descriptionLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ // Need space for the rest of the block
+ if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
+ metadata.data.picture.width = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.height = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.colorDepth = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.indexColorCount = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pictureDataSize = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
+
+ // Need space for the picture after the block
+ if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { // <-- Note the order of operations to avoid overflow to a valid value
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_PADDING:
+ {
+ if (onMeta) {
+ metadata.data.padding.unused = 0;
+
+ // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback.
+ if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+ isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop.
+ } else {
+ onMeta(pUserDataMD, &metadata);
+ }
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_INVALID:
+ {
+ // Invalid chunk. Just skip over this one.
+ if (onMeta) {
+ if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+ isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop.
+ }
+ }
+ } break;
+
+ default:
+ {
+ // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we
+ // can at the very least report the chunk to the application and let it look at the raw data.
+ if (onMeta) {
+ void* pRawData = DRFLAC_MALLOC(blockSize);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ DRFLAC_FREE(pRawData);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ onMeta(pUserDataMD, &metadata);
+
+ DRFLAC_FREE(pRawData);
+ }
+ } break;
+ }
+
+ // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above.
+ if (onMeta == NULL && blockSize > 0) {
+ if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) {
+ isLastBlock = DRFLAC_TRUE;
+ }
+ }
+
+ runningFilePos += blockSize;
+ if (isLastBlock) {
+ break;
+ }
+ }
+
+ *pSeektablePos = seektablePos;
+ *pSeektableSize = seektableSize;
+ *pFirstFramePos = runningFilePos;
+
+ return DRFLAC_TRUE;
+}
+
+drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
+{
+ (void)onSeek;
+
+ // Pre: The bit stream should be sitting just past the 4-byte id header.
+
+ pInit->container = drflac_container_native;
+
+ // The first metadata block should be the STREAMINFO block.
+ drflac_uint8 isLastBlock;
+ drflac_uint8 blockType;
+ drflac_uint32 blockSize;
+ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
+ if (!relaxed) {
+ // We're opening in strict mode and the first block is not the STREAMINFO block. Error.
+ return DRFLAC_FALSE;
+ } else {
+ // Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined
+ // for that frame.
+ pInit->hasStreamInfoBlock = DRFLAC_FALSE;
+ pInit->hasMetadataBlocks = DRFLAC_FALSE;
+
+ if (!drflac__read_next_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
+ return DRFLAC_FALSE; // Couldn't find a frame.
+ }
+
+ if (pInit->firstFrameHeader.bitsPerSample == 0) {
+ return DRFLAC_FALSE; // Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist.
+ }
+
+ pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
+ pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
+ pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
+ pInit->maxBlockSize = 65535; // <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo
+ return DRFLAC_TRUE;
+ }
+ } else {
+ drflac_streaminfo streaminfo;
+ if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
+ return DRFLAC_FALSE;
+ }
+
+ pInit->hasStreamInfoBlock = DRFLAC_TRUE;
+ pInit->sampleRate = streaminfo.sampleRate;
+ pInit->channels = streaminfo.channels;
+ pInit->bitsPerSample = streaminfo.bitsPerSample;
+ pInit->totalSampleCount = streaminfo.totalSampleCount;
+ pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation).
+ pInit->hasMetadataBlocks = !isLastBlock;
+
+ if (onMeta) {
+ drflac_metadata metadata;
+ metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+ metadata.data.streaminfo = streaminfo;
+ onMeta(pUserDataMD, &metadata);
+ }
+
+ return DRFLAC_TRUE;
+ }
+}
+
+#ifndef DR_FLAC_NO_OGG
+#define DRFLAC_OGG_MAX_PAGE_SIZE 65307
+#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 // CRC-32 of "OggS".
+
+typedef enum
+{
+ drflac_ogg_recover_on_crc_mismatch,
+ drflac_ogg_fail_on_crc_mismatch
+} drflac_ogg_crc_mismatch_recovery;
+
+
+static drflac_uint32 drflac__crc32_table[] = {
+ 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
+ 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
+ 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
+ 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
+ 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
+ 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
+ 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
+ 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
+ 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
+ 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
+ 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
+ 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
+ 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
+ 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
+ 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
+ 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
+ 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
+ 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
+ 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
+ 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
+ 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
+ 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
+ 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
+ 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
+ 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
+ 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
+ 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
+ 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
+ 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
+ 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
+ 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
+ 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
+ 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
+ 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
+ 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
+ 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
+ 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
+ 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
+ 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
+ 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
+ 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
+ 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
+ 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
+ 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
+ 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
+ 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
+ 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
+ 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
+ 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
+ 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
+ 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
+ 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
+ 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
+ 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
+ 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
+ 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
+ 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
+ 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
+ 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
+ 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
+ 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
+ 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
+ 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
+ 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
+};
+
+static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data)
+{
+#ifndef DR_FLAC_NO_CRC
+ return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data];
+#else
+ (void)data;
+ return crc32;
+#endif
+}
+
+#if 0
+static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data)
+{
+ crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF));
+ crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF));
+ crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF));
+ crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF));
+ return crc32;
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data)
+{
+ crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF));
+ crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF));
+ return crc32;
+}
+#endif
+
+static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize)
+{
+ // This can be optimized.
+ for (drflac_uint32 i = 0; i < dataSize; ++i) {
+ crc32 = drflac_crc32_byte(crc32, pData[i]);
+ }
+ return crc32;
+}
+
+
+static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4])
+{
+ return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader)
+{
+ return 27 + pHeader->segmentCount;
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader)
+{
+ drflac_uint32 pageBodySize = 0;
+ for (int i = 0; i < pHeader->segmentCount; ++i) {
+ pageBodySize += pHeader->segmentTable[i];
+ }
+
+ return pageBodySize;
+}
+
+drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
+{
+ drflac_assert(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
+
+ drflac_uint8 data[23];
+ if (onRead(pUserData, data, 23) != 23) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ *pBytesRead += 23;
+
+ pHeader->structureVersion = data[0];
+ pHeader->headerType = data[1];
+ drflac_copy_memory(&pHeader->granulePosition, &data[ 2], 8);
+ drflac_copy_memory(&pHeader->serialNumber, &data[10], 4);
+ drflac_copy_memory(&pHeader->sequenceNumber, &data[14], 4);
+ drflac_copy_memory(&pHeader->checksum, &data[18], 4);
+ pHeader->segmentCount = data[22];
+
+ // Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0.
+ data[18] = 0;
+ data[19] = 0;
+ data[20] = 0;
+ data[21] = 0;
+
+ drflac_uint32 i;
+ for (i = 0; i < 23; ++i) {
+ *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]);
+ }
+
+
+ if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ *pBytesRead += pHeader->segmentCount;
+
+ for (i = 0; i < pHeader->segmentCount; ++i) {
+ *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
+ }
+
+ return DRFLAC_SUCCESS;
+}
+
+drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32)
+{
+ *pBytesRead = 0;
+
+ drflac_uint8 id[4];
+ if (onRead(pUserData, id, 4) != 4) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ *pBytesRead += 4;
+
+ // We need to read byte-by-byte until we find the OggS capture pattern.
+ for (;;) {
+ if (drflac_ogg__is_capture_pattern(id)) {
+ *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
+
+ drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
+ if (result == DRFLAC_SUCCESS) {
+ return DRFLAC_SUCCESS;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ continue;
+ } else {
+ return result;
+ }
+ }
+ } else {
+ // The first 4 bytes did not equal the capture pattern. Read the next byte and try again.
+ id[0] = id[1];
+ id[1] = id[2];
+ id[2] = id[3];
+ if (onRead(pUserData, &id[3], 1) != 1) {
+ return DRFLAC_END_OF_STREAM;
+ }
+ *pBytesRead += 1;
+ }
+ }
+}
+
+
+// The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works
+// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed
+// in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type
+// dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from
+// the physical Ogg bitstream are converted and delivered in native FLAC format.
+typedef struct
+{
+ drflac_read_proc onRead; // The original onRead callback from drflac_open() and family.
+ drflac_seek_proc onSeek; // The original onSeek callback from drflac_open() and family.
+ void* pUserData; // The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family.
+ drflac_uint64 currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking.
+ drflac_uint64 firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page.
+ drflac_uint32 serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization.
+ drflac_ogg_page_header bosPageHeader; // Used for seeking.
+ drflac_ogg_page_header currentPageHeader;
+ drflac_uint32 bytesRemainingInPage;
+ drflac_uint32 pageDataSize;
+ drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE];
+} drflac_oggbs; // oggbs = Ogg Bitstream
+
+static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
+{
+ size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
+ oggbs->currentBytePos += bytesActuallyRead;
+
+ return bytesActuallyRead;
+}
+
+static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin)
+{
+ if (origin == drflac_seek_origin_start) {
+ if (offset <= 0x7FFFFFFF) {
+ if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos = offset;
+
+ return DRFLAC_TRUE;
+ } else {
+ if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos = offset;
+
+ return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current);
+ }
+ } else {
+ while (offset > 0x7FFFFFFF) {
+ if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos += 0x7FFFFFFF;
+ offset -= 0x7FFFFFFF;
+ }
+
+ if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { // <-- Safe cast thanks to the loop above.
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos += offset;
+
+ return DRFLAC_TRUE;
+ }
+}
+
+static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod)
+{
+ drflac_ogg_page_header header;
+ for (;;) {
+ drflac_uint32 crc32 = 0;
+ drflac_uint32 bytesRead;
+ if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos += bytesRead;
+
+ drflac_uint32 pageBodySize = drflac_ogg__get_page_body_size(&header);
+ if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) {
+ continue; // Invalid page size. Assume it's corrupted and just move to the next page.
+ }
+
+ if (header.serialNumber != oggbs->serialNumber) {
+ // It's not a FLAC page. Skip it.
+ if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ continue;
+ }
+
+
+ // We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page.
+ if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->pageDataSize = pageBodySize;
+
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint32 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
+ if (actualCRC32 != header.checksum) {
+ if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) {
+ continue; // CRC mismatch. Skip this page.
+ } else {
+ // Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we
+ // go to the next valid page to ensure we're in a good state, but return false to let the caller know that the
+ // seek did not fully complete.
+ drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch);
+ return DRFLAC_FALSE;
+ }
+ }
+#else
+ (void)recoveryMethod; // <-- Silence a warning.
+#endif
+
+ oggbs->currentPageHeader = header;
+ oggbs->bytesRemainingInPage = pageBodySize;
+ return DRFLAC_TRUE;
+ }
+}
+
+// Function below is unused at the moment, but I might be re-adding it later.
+#if 0
+static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg)
+{
+ drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
+ drflac_uint8 iSeg = 0;
+ drflac_uint32 iByte = 0;
+ while (iByte < bytesConsumedInPage) {
+ drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
+ if (iByte + segmentSize > bytesConsumedInPage) {
+ break;
+ } else {
+ iSeg += 1;
+ iByte += segmentSize;
+ }
+ }
+
+ *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte);
+ return iSeg;
+}
+
+static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs)
+{
+ // The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page.
+ for (;;) {
+ drflac_bool32 atEndOfPage = DRFLAC_FALSE;
+
+ drflac_uint8 bytesRemainingInSeg;
+ drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
+
+ drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
+ for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
+ drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
+ if (segmentSize < 255) {
+ if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
+ atEndOfPage = DRFLAC_TRUE;
+ }
+
+ break;
+ }
+
+ bytesToEndOfPacketOrPage += segmentSize;
+ }
+
+ // At this point we will have found either the packet or the end of the page. If were at the end of the page we'll
+ // want to load the next page and keep searching for the end of the packet.
+ drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current);
+ oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
+
+ if (atEndOfPage) {
+ // We're potentially at the next packet, but we need to check the next page first to be sure because the packet may
+ // straddle pages.
+ if (!drflac_oggbs__goto_next_page(oggbs)) {
+ return DRFLAC_FALSE;
+ }
+
+ // If it's a fresh packet it most likely means we're at the next packet.
+ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
+ return DRFLAC_TRUE;
+ }
+ } else {
+ // We're at the next packet.
+ return DRFLAC_TRUE;
+ }
+ }
+}
+
+static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs)
+{
+ // The bitstream should be sitting on the first byte just after the header of the frame.
+
+ // What we're actually doing here is seeking to the start of the next packet.
+ return drflac_oggbs__seek_to_next_packet(oggbs);
+}
+#endif
+
+static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
+ drflac_assert(oggbs != NULL);
+
+ drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut;
+
+ // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one.
+ size_t bytesRead = 0;
+ while (bytesRead < bytesToRead) {
+ size_t bytesRemainingToRead = bytesToRead - bytesRead;
+
+ if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
+ drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
+ bytesRead += bytesRemainingToRead;
+ oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead;
+ break;
+ }
+
+ // If we get here it means some of the requested data is contained in the next pages.
+ if (oggbs->bytesRemainingInPage > 0) {
+ drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
+ bytesRead += oggbs->bytesRemainingInPage;
+ pRunningBufferOut += oggbs->bytesRemainingInPage;
+ oggbs->bytesRemainingInPage = 0;
+ }
+
+ drflac_assert(bytesRemainingToRead > 0);
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
+ break; // Failed to go to the next page. Might have simply hit the end of the stream.
+ }
+ }
+
+ return bytesRead;
+}
+
+static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pUserData;
+ drflac_assert(oggbs != NULL);
+ drflac_assert(offset >= 0); // <-- Never seek backwards.
+
+ // Seeking is always forward which makes things a lot simpler.
+ if (origin == drflac_seek_origin_start) {
+ if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
+ return DRFLAC_FALSE;
+ }
+
+ return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current);
+ }
+
+
+ drflac_assert(origin == drflac_seek_origin_current);
+
+ int bytesSeeked = 0;
+ while (bytesSeeked < offset) {
+ int bytesRemainingToSeek = offset - bytesSeeked;
+ drflac_assert(bytesRemainingToSeek >= 0);
+
+ if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
+ bytesSeeked += bytesRemainingToSeek;
+ oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
+ break;
+ }
+
+ // If we get here it means some of the requested data is contained in the next pages.
+ if (oggbs->bytesRemainingInPage > 0) {
+ bytesSeeked += (int)oggbs->bytesRemainingInPage;
+ oggbs->bytesRemainingInPage = 0;
+ }
+
+ drflac_assert(bytesRemainingToSeek > 0);
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) {
+ // Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch.
+ return DRFLAC_FALSE;
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+
+ drflac_uint64 originalBytePos = oggbs->currentBytePos; // For recovery.
+
+ // First seek to the first frame.
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->bytesRemainingInPage = 0;
+
+ drflac_uint64 runningGranulePosition = 0;
+ drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier.
+ for (;;) {
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
+ drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start);
+ return DRFLAC_FALSE; // Never did find that sample...
+ }
+
+ runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
+ if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) {
+ break; // The sample is somewhere in the previous page.
+ }
+
+
+ // At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we
+ // disregard any pages that do not begin a fresh packet.
+ if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { // <-- Is it a fresh page?
+ if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
+ drflac_uint8 firstBytesInPage[2];
+ firstBytesInPage[0] = oggbs->pageData[0];
+ firstBytesInPage[1] = oggbs->pageData[1];
+
+ if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code?
+ runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels;
+ }
+
+ continue;
+ }
+ }
+ }
+
+
+ // We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the
+ // start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of
+ // a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until
+ // we find the one containing the target sample.
+ if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) {
+ return DRFLAC_FALSE;
+ }
+ if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) {
+ return DRFLAC_FALSE;
+ }
+
+
+ // At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep
+ // looping over these frames until we find the one containing the sample we're after.
+ drflac_uint64 runningSampleCount = runningGranulePosition;
+ for (;;) {
+ // There are two ways to find the sample and seek past irrelevant frames:
+ // 1) Use the native FLAC decoder.
+ // 2) Use Ogg's framing system.
+ //
+ // Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to
+ // do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code
+ // duplication for the decoding of frame headers.
+ //
+ // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg
+ // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the
+ // standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks
+ // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read
+ // using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to
+ // avoid the use of the drflac_bs object.
+ //
+ // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons:
+ // 1) Seeking is already partially accelerated using Ogg's paging system in the code block above.
+ // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon.
+ // 3) Simplicity.
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_uint64 firstSampleInFrame = 0;
+ drflac_uint64 lastSampleInFrame = 0;
+ drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame);
+
+ drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1;
+ if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) {
+ // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend
+ // it never existed and keep iterating.
+ drflac_result result = drflac__decode_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ // The frame is valid. We just need to skip over some samples to ensure it's sample-exact.
+ drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535.
+ if (samplesToDecode == 0) {
+ return DRFLAC_TRUE;
+ }
+ return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail).
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ continue; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this
+ // frame never existed and leave the running sample count untouched.
+ drflac_result result = drflac__seek_to_next_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningSampleCount += sampleCountInThisFrame;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ continue; // CRC mismatch. Pretend this frame never existed.
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+ }
+ }
+}
+
+
+drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed)
+{
+ // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern.
+ (void)relaxed;
+
+ pInit->container = drflac_container_ogg;
+ pInit->oggFirstBytePos = 0;
+
+ // We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the
+ // stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if
+ // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed.
+ drflac_ogg_page_header header;
+
+ drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
+ drflac_uint32 bytesRead = 0;
+ if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
+ return DRFLAC_FALSE;
+ }
+ pInit->runningFilePos += bytesRead;
+
+ for (;;) {
+ // Break if we're past the beginning of stream page.
+ if ((header.headerType & 0x02) == 0) {
+ return DRFLAC_FALSE;
+ }
+
+
+ // Check if it's a FLAC header.
+ int pageBodySize = drflac_ogg__get_page_body_size(&header);
+ if (pageBodySize == 51) { // 51 = the lacing value of the FLAC header packet.
+ // It could be a FLAC page...
+ drflac_uint32 bytesRemainingInPage = pageBodySize;
+
+ drflac_uint8 packetType;
+ if (onRead(pUserData, &packetType, 1) != 1) {
+ return DRFLAC_FALSE;
+ }
+
+ bytesRemainingInPage -= 1;
+ if (packetType == 0x7F) {
+ // Increasingly more likely to be a FLAC page...
+ drflac_uint8 sig[4];
+ if (onRead(pUserData, sig, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ bytesRemainingInPage -= 4;
+ if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
+ // Almost certainly a FLAC page...
+ drflac_uint8 mappingVersion[2];
+ if (onRead(pUserData, mappingVersion, 2) != 2) {
+ return DRFLAC_FALSE;
+ }
+
+ if (mappingVersion[0] != 1) {
+ return DRFLAC_FALSE; // Only supporting version 1.x of the Ogg mapping.
+ }
+
+ // The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to
+ // be handling it in a generic way based on the serial number and packet types.
+ if (!onSeek(pUserData, 2, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+
+ // Expecting the native FLAC signature "fLaC".
+ if (onRead(pUserData, sig, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
+ // The remaining data in the page should be the STREAMINFO block.
+ drflac_uint8 isLastBlock;
+ drflac_uint8 blockType;
+ drflac_uint32 blockSize;
+ if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
+ return DRFLAC_FALSE; // Invalid block type. First block must be the STREAMINFO block.
+ }
+
+ drflac_streaminfo streaminfo;
+ if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) {
+ // Success!
+ pInit->hasStreamInfoBlock = DRFLAC_TRUE;
+ pInit->sampleRate = streaminfo.sampleRate;
+ pInit->channels = streaminfo.channels;
+ pInit->bitsPerSample = streaminfo.bitsPerSample;
+ pInit->totalSampleCount = streaminfo.totalSampleCount;
+ pInit->maxBlockSize = streaminfo.maxBlockSize;
+ pInit->hasMetadataBlocks = !isLastBlock;
+
+ if (onMeta) {
+ drflac_metadata metadata;
+ metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+ metadata.data.streaminfo = streaminfo;
+ onMeta(pUserDataMD, &metadata);
+ }
+
+ pInit->runningFilePos += pageBodySize;
+ pInit->oggFirstBytePos = pInit->runningFilePos - 79; // Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page.
+ pInit->oggSerial = header.serialNumber;
+ pInit->oggBosHeader = header;
+ break;
+ } else {
+ // Failed to read STREAMINFO block. Aww, so close...
+ return DRFLAC_FALSE;
+ }
+ } else {
+ // Invalid file.
+ return DRFLAC_FALSE;
+ }
+ } else {
+ // Not a FLAC header. Skip it.
+ if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ // Not a FLAC header. Seek past the entire page and move on to the next.
+ if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ }
+ } else {
+ if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ pInit->runningFilePos += pageBodySize;
+
+
+ // Read the header of the next page.
+ if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
+ return DRFLAC_FALSE;
+ }
+ pInit->runningFilePos += bytesRead;
+ }
+
+
+ // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next
+ // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the
+ // Ogg bistream object.
+ pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block.
+ return DRFLAC_TRUE;
+}
+#endif
+
+drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
+{
+ if (pInit == NULL || onRead == NULL || onSeek == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_zero_memory(pInit, sizeof(*pInit));
+ pInit->onRead = onRead;
+ pInit->onSeek = onSeek;
+ pInit->onMeta = onMeta;
+ pInit->container = container;
+ pInit->pUserData = pUserData;
+ pInit->pUserDataMD = pUserDataMD;
+
+ pInit->bs.onRead = onRead;
+ pInit->bs.onSeek = onSeek;
+ pInit->bs.pUserData = pUserData;
+ drflac__reset_cache(&pInit->bs);
+
+
+ // If the container is explicitly defined then we can try opening in relaxed mode.
+ drflac_bool32 relaxed = container != drflac_container_unknown;
+
+ drflac_uint8 id[4];
+
+ // Skip over any ID3 tags.
+ for (;;) {
+ if (onRead(pUserData, id, 4) != 4) {
+ return DRFLAC_FALSE; // Ran out of data.
+ }
+ pInit->runningFilePos += 4;
+
+ if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
+ drflac_uint8 header[6];
+ if (onRead(pUserData, header, 6) != 6) {
+ return DRFLAC_FALSE; // Ran out of data.
+ }
+ pInit->runningFilePos += 6;
+
+ drflac_uint8 flags = header[1];
+ drflac_uint32 headerSize;
+ drflac_copy_memory(&headerSize, header+2, 4);
+ headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize));
+ if (flags & 0x10) {
+ headerSize += 10;
+ }
+
+ if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) {
+ return DRFLAC_FALSE; // Failed to seek past the tag.
+ }
+ pInit->runningFilePos += headerSize;
+ } else {
+ break;
+ }
+ }
+
+ if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
+ return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
+ }
+#ifndef DR_FLAC_NO_OGG
+ if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
+ return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
+ }
+#endif
+
+ // If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable.
+ if (relaxed) {
+ if (container == drflac_container_native) {
+ return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
+ }
+#ifndef DR_FLAC_NO_OGG
+ if (container == drflac_container_ogg) {
+ return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
+ }
+#endif
+ }
+
+ // Unsupported container.
+ return DRFLAC_FALSE;
+}
+
+void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit)
+{
+ drflac_assert(pFlac != NULL);
+ drflac_assert(pInit != NULL);
+
+ drflac_zero_memory(pFlac, sizeof(*pFlac));
+ pFlac->bs = pInit->bs;
+ pFlac->onMeta = pInit->onMeta;
+ pFlac->pUserDataMD = pInit->pUserDataMD;
+ pFlac->maxBlockSize = pInit->maxBlockSize;
+ pFlac->sampleRate = pInit->sampleRate;
+ pFlac->channels = (drflac_uint8)pInit->channels;
+ pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
+ pFlac->totalSampleCount = pInit->totalSampleCount;
+ pFlac->container = pInit->container;
+}
+
+drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD)
+{
+#ifndef DRFLAC_NO_CPUID
+ // CPU support first.
+ drflac__init_cpu_caps();
+#endif
+
+ drflac_init_info init;
+ if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
+ return NULL;
+ }
+
+ // The size of the allocation for the drflac object needs to be large enough to fit the following:
+ // 1) The main members of the drflac structure
+ // 2) A block of memory large enough to store the decoded samples of the largest frame in the stream
+ // 3) If the container is Ogg, a drflac_oggbs object
+ //
+ // The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration
+ // the different SIMD instruction sets.
+ drflac_uint32 allocationSize = sizeof(drflac);
+
+ // The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector
+ // we are supporting.
+ drflac_uint32 wholeSIMDVectorCountPerChannel;
+ if ((init.maxBlockSize % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
+ wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
+ } else {
+ wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
+ }
+
+ drflac_uint32 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
+
+ allocationSize += decodedSamplesAllocationSize;
+ allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; // Allocate extra bytes to ensure we have enough for alignment.
+
+#ifndef DR_FLAC_NO_OGG
+ // There's additional data required for Ogg streams.
+ drflac_uint32 oggbsAllocationSize = 0;
+ if (init.container == drflac_container_ogg) {
+ oggbsAllocationSize = sizeof(drflac_oggbs);
+ allocationSize += oggbsAllocationSize;
+ }
+
+ drflac_oggbs oggbs;
+ drflac_zero_memory(&oggbs, sizeof(oggbs));
+ if (init.container == drflac_container_ogg) {
+ oggbs.onRead = onRead;
+ oggbs.onSeek = onSeek;
+ oggbs.pUserData = pUserData;
+ oggbs.currentBytePos = init.oggFirstBytePos;
+ oggbs.firstBytePos = init.oggFirstBytePos;
+ oggbs.serialNumber = init.oggSerial;
+ oggbs.bosPageHeader = init.oggBosHeader;
+ oggbs.bytesRemainingInPage = 0;
+ }
+#endif
+
+ // This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to
+ // consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading
+ // and decoding the metadata.
+ drflac_uint64 firstFramePos = 42; // <-- We know we are at byte 42 at this point.
+ drflac_uint64 seektablePos = 0;
+ drflac_uint32 seektableSize = 0;
+ if (init.hasMetadataBlocks) {
+ drflac_read_proc onReadOverride = onRead;
+ drflac_seek_proc onSeekOverride = onSeek;
+ void* pUserDataOverride = pUserData;
+
+#ifndef DR_FLAC_NO_OGG
+ if (init.container == drflac_container_ogg) {
+ onReadOverride = drflac__on_read_ogg;
+ onSeekOverride = drflac__on_seek_ogg;
+ pUserDataOverride = (void*)&oggbs;
+ }
+#endif
+
+ if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize)) {
+ return NULL;
+ }
+
+ allocationSize += seektableSize;
+ }
+
+
+ drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize);
+ drflac__init_from_info(pFlac, &init);
+ pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE);
+
+#ifndef DR_FLAC_NO_OGG
+ if (init.container == drflac_container_ogg) {
+ drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize);
+ *pInternalOggbs = oggbs;
+
+ // The Ogg bistream needs to be layered on top of the original bitstream.
+ pFlac->bs.onRead = drflac__on_read_ogg;
+ pFlac->bs.onSeek = drflac__on_seek_ogg;
+ pFlac->bs.pUserData = (void*)pInternalOggbs;
+ pFlac->_oggbs = (void*)pInternalOggbs;
+ }
+#endif
+
+ pFlac->firstFramePos = firstFramePos;
+
+ // NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now.
+#ifndef DR_FLAC_NO_OGG
+ if (init.container == drflac_container_ogg)
+ {
+ pFlac->pSeekpoints = NULL;
+ pFlac->seekpointCount = 0;
+ }
+ else
+#endif
+ {
+ // If we have a seektable we need to load it now, making sure we move back to where we were previously.
+ if (seektablePos != 0) {
+ pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints);
+ pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
+
+ // Seek to the seektable, then just read directly into our seektable buffer.
+ if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) {
+ if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) {
+ // Endian swap.
+ for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
+ pFlac->pSeekpoints[iSeekpoint].firstSample = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstSample);
+ pFlac->pSeekpoints[iSeekpoint].frameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].frameOffset);
+ pFlac->pSeekpoints[iSeekpoint].sampleCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].sampleCount);
+ }
+ } else {
+ // Failed to read the seektable. Pretend we don't have one.
+ pFlac->pSeekpoints = NULL;
+ pFlac->seekpointCount = 0;
+ }
+
+ // We need to seek back to where we were. If this fails it's a critical error.
+ if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFramePos, drflac_seek_origin_start)) {
+ DRFLAC_FREE(pFlac);
+ return NULL;
+ }
+ } else {
+ // Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one.
+ pFlac->pSeekpoints = NULL;
+ pFlac->seekpointCount = 0;
+ }
+ }
+ }
+
+
+
+ // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode
+ // the first frame.
+ if (!init.hasStreamInfoBlock) {
+ pFlac->currentFrame.header = init.firstFrameHeader;
+ do
+ {
+ drflac_result result = drflac__decode_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ break;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) {
+ DRFLAC_FREE(pFlac);
+ return NULL;
+ }
+ continue;
+ } else {
+ DRFLAC_FREE(pFlac);
+ return NULL;
+ }
+ }
+ } while (1);
+ }
+
+ return pFlac;
+}
+
+
+
+#ifndef DR_FLAC_NO_STDIO
+#include <stdio.h>
+
+static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
+}
+
+static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ drflac_assert(offset >= 0); // <-- Never seek backwards.
+
+ return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+static FILE* drflac__fopen(const char* filename)
+{
+ FILE* pFile;
+#ifdef _MSC_VER
+ if (fopen_s(&pFile, filename, "rb") != 0) {
+ return NULL;
+ }
+#else
+ pFile = fopen(filename, "rb");
+ if (pFile == NULL) {
+ return NULL;
+ }
+#endif
+
+ return pFile;
+}
+
+
+drflac* drflac_open_file(const char* filename)
+{
+ FILE* file = drflac__fopen(filename);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ drflac* pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)file);
+ if (pFlac == NULL) {
+ fclose(file);
+ return NULL;
+ }
+
+ return pFlac;
+}
+
+drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData)
+{
+ FILE* file = drflac__fopen(filename);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)file, pUserData);
+ if (pFlac == NULL) {
+ fclose(file);
+ return pFlac;
+ }
+
+ return pFlac;
+}
+#endif //DR_FLAC_NO_STDIO
+
+static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
+{
+ drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+ drflac_assert(memoryStream != NULL);
+ drflac_assert(memoryStream->dataSize >= memoryStream->currentReadPos);
+
+ size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
+ if (bytesToRead > bytesRemaining) {
+ bytesToRead = bytesRemaining;
+ }
+
+ if (bytesToRead > 0) {
+ drflac_copy_memory(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
+ memoryStream->currentReadPos += bytesToRead;
+ }
+
+ return bytesToRead;
+}
+
+static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin)
+{
+ drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData;
+ drflac_assert(memoryStream != NULL);
+ drflac_assert(offset >= 0); // <-- Never seek backwards.
+
+ if (offset > (drflac_int64)memoryStream->dataSize) {
+ return DRFLAC_FALSE;
+ }
+
+ if (origin == drflac_seek_origin_current) {
+ if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
+ memoryStream->currentReadPos += offset;
+ } else {
+ return DRFLAC_FALSE; // Trying to seek too far forward.
+ }
+ } else {
+ if ((drflac_uint32)offset <= memoryStream->dataSize) {
+ memoryStream->currentReadPos = offset;
+ } else {
+ return DRFLAC_FALSE; // Trying to seek too far forward.
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+drflac* drflac_open_memory(const void* data, size_t dataSize)
+{
+ drflac__memory_stream memoryStream;
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+ drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ pFlac->memoryStream = memoryStream;
+
+ // This is an awful hack...
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ oggbs->pUserData = &pFlac->memoryStream;
+ }
+ else
+#endif
+ {
+ pFlac->bs.pUserData = &pFlac->memoryStream;
+ }
+
+ return pFlac;
+}
+
+drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData)
+{
+ drflac__memory_stream memoryStream;
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+ drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ pFlac->memoryStream = memoryStream;
+
+ // This is an awful hack...
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ oggbs->pUserData = &pFlac->memoryStream;
+ }
+ else
+#endif
+ {
+ pFlac->bs.pUserData = &pFlac->memoryStream;
+ }
+
+ return pFlac;
+}
+
+
+
+drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData);
+}
+drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData);
+}
+
+drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData);
+}
+drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData);
+}
+
+void drflac_close(drflac* pFlac)
+{
+ if (pFlac == NULL) {
+ return;
+ }
+
+#ifndef DR_FLAC_NO_STDIO
+ // If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file()
+ // was used by looking at the callbacks.
+ if (pFlac->bs.onRead == drflac__on_read_stdio) {
+ fclose((FILE*)pFlac->bs.pUserData);
+ }
+
+#ifndef DR_FLAC_NO_OGG
+ // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained.
+ if (pFlac->container == drflac_container_ogg) {
+ drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg);
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ if (oggbs->onRead == drflac__on_read_stdio) {
+ fclose((FILE*)oggbs->pUserData);
+ }
+ }
+#endif
+#endif
+
+ DRFLAC_FREE(pFlac);
+}
+
+drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut)
+{
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+
+ // We should never be calling this when the number of samples to read is >= the sample count.
+ drflac_assert(samplesToRead < channelCount);
+ drflac_assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining);
+
+
+ drflac_uint64 samplesRead = 0;
+ while (samplesToRead > 0) {
+ drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount;
+ drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining;
+ drflac_uint64 channelIndex = samplesReadFromFrameSoFar % channelCount;
+
+ drflac_uint64 nextSampleInFrame = samplesReadFromFrameSoFar / channelCount;
+
+ int decodedSample = 0;
+ switch (pFlac->currentFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ if (channelIndex == 0) {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ } else {
+ int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample;
+ decodedSample = left - side;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ if (channelIndex == 0) {
+ int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample;
+ decodedSample = side + right;
+ } else {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ int mid;
+ int side;
+ if (channelIndex == 0) {
+ mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 1].wastedBitsPerSample;
+
+ mid = (((unsigned int)mid) << 1) | (side & 0x01);
+ decodedSample = (mid + side) >> 1;
+ } else {
+ mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex - 1].wastedBitsPerSample;
+ side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+
+ mid = (((unsigned int)mid) << 1) | (side & 0x01);
+ decodedSample = (mid - side) >> 1;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ decodedSample = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame] << pFlac->currentFrame.subframes[channelIndex + 0].wastedBitsPerSample;
+ } break;
+ }
+
+
+ decodedSample <<= (32 - pFlac->bitsPerSample);
+
+ if (bufferOut) {
+ *bufferOut++ = decodedSample;
+ }
+
+ samplesRead += 1;
+ pFlac->currentFrame.samplesRemaining -= 1;
+ samplesToRead -= 1;
+ }
+
+ return samplesRead;
+}
+
+drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 samplesToRead)
+{
+ drflac_uint64 samplesRead = 0;
+ while (samplesToRead > 0) {
+ if (pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_and_decode_next_frame(pFlac)) {
+ break; // Couldn't read the next frame, so just break from the loop and return.
+ }
+ } else {
+ if (pFlac->currentFrame.samplesRemaining > samplesToRead) {
+ samplesRead += samplesToRead;
+ pFlac->currentFrame.samplesRemaining -= (drflac_uint32)samplesToRead; // <-- Safe cast. Will always be < currentFrame.samplesRemaining < 65536.
+ samplesToRead = 0;
+ } else {
+ samplesRead += pFlac->currentFrame.samplesRemaining;
+ samplesToRead -= pFlac->currentFrame.samplesRemaining;
+ pFlac->currentFrame.samplesRemaining = 0;
+ }
+ }
+ }
+
+ pFlac->currentSample += samplesRead;
+ return samplesRead;
+}
+
+drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut)
+{
+ // Note that <bufferOut> is allowed to be null, in which case this will act like a seek.
+ if (pFlac == NULL || samplesToRead == 0) {
+ return 0;
+ }
+
+ if (bufferOut == NULL) {
+ return drflac__seek_forward_by_samples(pFlac, samplesToRead);
+ }
+
+
+ drflac_uint64 samplesRead = 0;
+ while (samplesToRead > 0) {
+ // If we've run out of samples in this frame, go to the next.
+ if (pFlac->currentFrame.samplesRemaining == 0) {
+ if (!drflac__read_and_decode_next_frame(pFlac)) {
+ break; // Couldn't read the next frame, so just break from the loop and return.
+ }
+ } else {
+ // Here is where we grab the samples and interleave them.
+
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount;
+ drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining;
+
+ drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount;
+ if (misalignedSampleCount > 0) {
+ drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut);
+ samplesRead += misalignedSamplesRead;
+ samplesReadFromFrameSoFar += misalignedSamplesRead;
+ bufferOut += misalignedSamplesRead;
+ samplesToRead -= misalignedSamplesRead;
+ pFlac->currentSample += misalignedSamplesRead;
+ }
+
+
+ drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount;
+ if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) {
+ alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount;
+ }
+
+ drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount;
+ unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+
+ switch (pFlac->currentFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int left = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int side = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ int right = left - side;
+
+ bufferOut[i*2+0] = left;
+ bufferOut[i*2+1] = right;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int side = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ int right = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ int left = right + side;
+
+ bufferOut[i*2+0] = left;
+ bufferOut[i*2+1] = right;
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ int mid = pDecodedSamples0[i] << pFlac->currentFrame.subframes[0].wastedBitsPerSample;
+ int side = pDecodedSamples1[i] << pFlac->currentFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample);
+ bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample);
+ }
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo
+ {
+ // Stereo optimized inner loop unroll.
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame;
+
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample);
+ bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample);
+ }
+ }
+ else
+ {
+ // Generic interleaving.
+ for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) {
+ for (unsigned int j = 0; j < channelCount; ++j) {
+ bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample);
+ }
+ }
+ }
+ } break;
+ }
+
+ drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount;
+ samplesRead += alignedSamplesRead;
+ samplesReadFromFrameSoFar += alignedSamplesRead;
+ bufferOut += alignedSamplesRead;
+ samplesToRead -= alignedSamplesRead;
+ pFlac->currentSample += alignedSamplesRead;
+ pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead;
+
+
+ // At this point we may still have some excess samples left to read.
+ if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) {
+ drflac_uint64 excessSamplesRead = 0;
+ if (samplesToRead < pFlac->currentFrame.samplesRemaining) {
+ excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut);
+ } else {
+ excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut);
+ }
+
+ samplesRead += excessSamplesRead;
+ samplesReadFromFrameSoFar += excessSamplesRead;
+ bufferOut += excessSamplesRead;
+ samplesToRead -= excessSamplesRead;
+ pFlac->currentSample += excessSamplesRead;
+ }
+ }
+ }
+
+ return samplesRead;
+}
+
+drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut)
+{
+ // This reads samples in 2 passes and can probably be optimized.
+ drflac_uint64 totalSamplesRead = 0;
+
+ while (samplesToRead > 0) {
+ drflac_int32 samples32[4096];
+ drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32);
+ if (samplesJustRead == 0) {
+ break; // Reached the end.
+ }
+
+ // s32 -> s16
+ for (drflac_uint64 i = 0; i < samplesJustRead; ++i) {
+ pBufferOut[i] = (drflac_int16)(samples32[i] >> 16);
+ }
+
+ totalSamplesRead += samplesJustRead;
+ samplesToRead -= samplesJustRead;
+ pBufferOut += samplesJustRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut)
+{
+ // This reads samples in 2 passes and can probably be optimized.
+ drflac_uint64 totalSamplesRead = 0;
+
+ while (samplesToRead > 0) {
+ drflac_int32 samples32[4096];
+ drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32);
+ if (samplesJustRead == 0) {
+ break; // Reached the end.
+ }
+
+ // s32 -> f32
+ for (drflac_uint64 i = 0; i < samplesJustRead; ++i) {
+ pBufferOut[i] = (float)(samples32[i] / 2147483648.0);
+ }
+
+ totalSamplesRead += samplesJustRead;
+ samplesToRead -= samplesJustRead;
+ pBufferOut += samplesJustRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex)
+{
+ if (pFlac == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ // If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present
+ // when the decoder was opened.
+ if (pFlac->firstFramePos == 0) {
+ return DRFLAC_FALSE;
+ }
+
+ if (sampleIndex == 0) {
+ pFlac->currentSample = 0;
+ return drflac__seek_to_first_frame(pFlac);
+ } else {
+ drflac_bool32 wasSuccessful = DRFLAC_FALSE;
+
+ // Clamp the sample to the end.
+ if (sampleIndex >= pFlac->totalSampleCount) {
+ sampleIndex = pFlac->totalSampleCount - 1;
+ }
+
+ // If the target sample and the current sample are in the same frame we just move the position forward.
+ if (sampleIndex > pFlac->currentSample) {
+ // Forward.
+ drflac_uint32 offset = (drflac_uint32)(sampleIndex - pFlac->currentSample);
+ if (pFlac->currentFrame.samplesRemaining > offset) {
+ pFlac->currentFrame.samplesRemaining -= offset;
+ pFlac->currentSample = sampleIndex;
+ return DRFLAC_TRUE;
+ }
+ } else {
+ // Backward.
+ drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - sampleIndex);
+ drflac_uint32 currentFrameSampleCount = pFlac->currentFrame.header.blockSize * drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment);
+ drflac_uint32 currentFrameSamplesConsumed = (drflac_uint32)(currentFrameSampleCount - pFlac->currentFrame.samplesRemaining);
+ if (currentFrameSamplesConsumed > offsetAbs) {
+ pFlac->currentFrame.samplesRemaining += offsetAbs;
+ pFlac->currentSample = sampleIndex;
+ return DRFLAC_TRUE;
+ }
+ }
+
+ // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so
+ // we'll instead use Ogg's natural seeking facility.
+#ifndef DR_FLAC_NO_OGG
+ if (pFlac->container == drflac_container_ogg)
+ {
+ wasSuccessful = drflac_ogg__seek_to_sample(pFlac, sampleIndex);
+ }
+ else
+#endif
+ {
+ // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower.
+ wasSuccessful = drflac__seek_to_sample__seek_table(pFlac, sampleIndex);
+ if (!wasSuccessful) {
+ wasSuccessful = drflac__seek_to_sample__brute_force(pFlac, sampleIndex);
+ }
+ }
+
+ pFlac->currentSample = sampleIndex;
+ return wasSuccessful;
+ }
+}
+
+
+
+//// High Level APIs ////
+
+#if defined(SIZE_MAX)
+ #define DRFLAC_SIZE_MAX SIZE_MAX
+#else
+ #if defined(DRFLAC_64BIT)
+ #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRFLAC_SIZE_MAX 0xFFFFFFFF
+ #endif
+#endif
+
+
+// Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me.
+#define DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(extension, type) \
+static type* drflac__full_decode_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)\
+{ \
+ drflac_assert(pFlac != NULL); \
+ \
+ type* pSampleData = NULL; \
+ drflac_uint64 totalSampleCount = pFlac->totalSampleCount; \
+ \
+ if (totalSampleCount == 0) { \
+ type buffer[4096]; \
+ \
+ size_t sampleDataBufferSize = sizeof(buffer); \
+ pSampleData = (type*)DRFLAC_MALLOC(sampleDataBufferSize); \
+ if (pSampleData == NULL) { \
+ goto on_error; \
+ } \
+ \
+ drflac_uint64 samplesRead; \
+ while ((samplesRead = (drflac_uint64)drflac_read_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) { \
+ if (((totalSampleCount + samplesRead) * sizeof(type)) > sampleDataBufferSize) { \
+ sampleDataBufferSize *= 2; \
+ type* pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \
+ if (pNewSampleData == NULL) { \
+ DRFLAC_FREE(pSampleData); \
+ goto on_error; \
+ } \
+ \
+ pSampleData = pNewSampleData; \
+ } \
+ \
+ drflac_copy_memory(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(type))); \
+ totalSampleCount += samplesRead; \
+ } \
+ \
+ /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \
+ protect those ears from random noise! */ \
+ drflac_zero_memory(pSampleData + totalSampleCount, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(type))); \
+ } else { \
+ drflac_uint64 dataSize = totalSampleCount * sizeof(type); \
+ if (dataSize > DRFLAC_SIZE_MAX) { \
+ goto on_error; /* The decoded data is too big. */ \
+ } \
+ \
+ pSampleData = (type*)DRFLAC_MALLOC((size_t)dataSize); /* <-- Safe cast as per the check above. */ \
+ if (pSampleData == NULL) { \
+ goto on_error; \
+ } \
+ \
+ totalSampleCount = drflac_read_##extension(pFlac, pFlac->totalSampleCount, pSampleData); \
+ } \
+ \
+ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
+ if (channelsOut) *channelsOut = pFlac->channels; \
+ if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount; \
+ \
+ drflac_close(pFlac); \
+ return pSampleData; \
+ \
+on_error: \
+ drflac_close(pFlac); \
+ return NULL; \
+}
+
+DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s32, drflac_int32)
+DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s16, drflac_int16)
+DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(f32, float)
+
+drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ // Safety.
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ // Safety.
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ // Safety.
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open(onRead, onSeek, pUserData);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+#ifndef DR_FLAC_NO_STDIO
+drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open_file(filename);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open_file(filename);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open_file(filename);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount);
+}
+#endif
+
+drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open_memory(data, dataSize);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open_memory(data, dataSize);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drflac* pFlac = drflac_open_memory(data, dataSize);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount);
+}
+
+void drflac_free(void* pSampleDataReturnedByOpenAndDecode)
+{
+ DRFLAC_FREE(pSampleDataReturnedByOpenAndDecode);
+}
+
+
+
+
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments)
+{
+ if (pIter == NULL) {
+ return;
+ }
+
+ pIter->countRemaining = commentCount;
+ pIter->pRunningData = (const char*)pComments;
+}
+
+const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut)
+{
+ // Safety.
+ if (pCommentLengthOut) *pCommentLengthOut = 0;
+
+ if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
+ return NULL;
+ }
+
+ drflac_uint32 length = drflac__le2host_32(*(const drflac_uint32*)pIter->pRunningData);
+ pIter->pRunningData += 4;
+
+ const char* pComment = pIter->pRunningData;
+ pIter->pRunningData += length;
+ pIter->countRemaining -= 1;
+
+ if (pCommentLengthOut) *pCommentLengthOut = length;
+ return pComment;
+}
+
+
+
+
+void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData)
+{
+ if (pIter == NULL) {
+ return;
+ }
+
+ pIter->countRemaining = trackCount;
+ pIter->pRunningData = (const char*)pTrackData;
+}
+
+drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack)
+{
+ if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac_cuesheet_track cuesheetTrack;
+
+ const char* pRunningData = pIter->pRunningData;
+
+ drflac_uint64 offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ drflac_uint64 offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ cuesheetTrack.offset = offsetLo | (offsetHi << 32);
+ cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
+ drflac_copy_memory(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
+ cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
+ cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
+ cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
+ cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index);
+
+ pIter->pRunningData = pRunningData;
+ pIter->countRemaining -= 1;
+
+ if (pCuesheetTrack) *pCuesheetTrack = cuesheetTrack;
+ return DRFLAC_TRUE;
+}
+#endif //DR_FLAC_IMPLEMENTATION
+
+
+// REVISION HISTORY
+//
+// v0.10.0 - 2018-09-11
+// - Remove the DR_FLAC_NO_WIN32_IO option and the Win32 file IO functionality. If you need to use Win32 file IO you
+// need to do it yourself via the callback API.
+// - Fix the clang build.
+// - Fix undefined behavior.
+// - Fix errors with CUESHEET metdata blocks.
+// - Add an API for iterating over each cuesheet track in the CUESHEET metadata block. This works the same way as the
+// Vorbis comment API.
+// - Other miscellaneous bug fixes, mostly relating to invalid FLAC streams.
+// - Minor optimizations.
+//
+// v0.9.11 - 2018-08-29
+// - Fix a bug with sample reconstruction.
+//
+// v0.9.10 - 2018-08-07
+// - Improve 64-bit detection.
+//
+// v0.9.9 - 2018-08-05
+// - Fix C++ build on older versions of GCC.
+//
+// v0.9.8 - 2018-07-24
+// - Fix compilation errors.
+//
+// v0.9.7 - 2018-07-05
+// - Fix a warning.
+//
+// v0.9.6 - 2018-06-29
+// - Fix some typos.
+//
+// v0.9.5 - 2018-06-23
+// - Fix some warnings.
+//
+// v0.9.4 - 2018-06-14
+// - Optimizations to seeking.
+// - Clean up.
+//
+// v0.9.3 - 2018-05-22
+// - Bug fix.
+//
+// v0.9.2 - 2018-05-12
+// - Fix a compilation error due to a missing break statement.
+//
+// v0.9.1 - 2018-04-29
+// - Fix compilation error with Clang.
+//
+// v0.9 - 2018-04-24
+// - Fix Clang build.
+// - Start using major.minor.revision versioning.
+//
+// v0.8g - 2018-04-19
+// - Fix build on non-x86/x64 architectures.
+//
+// v0.8f - 2018-02-02
+// - Stop pretending to support changing rate/channels mid stream.
+//
+// v0.8e - 2018-02-01
+// - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream.
+// - Fix a crash the the Rice partition order is invalid.
+//
+// v0.8d - 2017-09-22
+// - Add support for decoding streams with ID3 tags. ID3 tags are just skipped.
+//
+// v0.8c - 2017-09-07
+// - Fix warning on non-x86/x64 architectures.
+//
+// v0.8b - 2017-08-19
+// - Fix build on non-x86/x64 architectures.
+//
+// v0.8a - 2017-08-13
+// - A small optimization for the Clang build.
+//
+// v0.8 - 2017-08-12
+// - API CHANGE: Rename dr_* types to drflac_*.
+// - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation.
+// - Add support for custom implementations of malloc(), realloc(), etc.
+// - Add CRC checking to Ogg encapsulated streams.
+// - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported.
+// - Bug fixes.
+//
+// v0.7 - 2017-07-23
+// - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed().
+//
+// v0.6 - 2017-07-22
+// - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they
+// never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame.
+//
+// v0.5 - 2017-07-16
+// - Fix typos.
+// - Change drflac_bool* types to unsigned.
+// - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC.
+//
+// v0.4f - 2017-03-10
+// - Fix a couple of bugs with the bitstreaming code.
+//
+// v0.4e - 2017-02-17
+// - Fix some warnings.
+//
+// v0.4d - 2016-12-26
+// - Add support for 32-bit floating-point PCM decoding.
+// - Use drflac_int*/drflac_uint* sized types to improve compiler support.
+// - Minor improvements to documentation.
+//
+// v0.4c - 2016-12-26
+// - Add support for signed 16-bit integer PCM decoding.
+//
+// v0.4b - 2016-10-23
+// - A minor change to drflac_bool8 and drflac_bool32 types.
+//
+// v0.4a - 2016-10-11
+// - Rename drBool32 to drflac_bool32 for styling consistency.
+//
+// v0.4 - 2016-09-29
+// - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type.
+// - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32().
+// - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to
+// keep it consistent with drflac_audio.
+//
+// v0.3f - 2016-09-21
+// - Fix a warning with GCC.
+//
+// v0.3e - 2016-09-18
+// - Fixed a bug where GCC 4.3+ was not getting properly identified.
+// - Fixed a few typos.
+// - Changed date formats to ISO 8601 (YYYY-MM-DD).
+//
+// v0.3d - 2016-06-11
+// - Minor clean up.
+//
+// v0.3c - 2016-05-28
+// - Fixed compilation error.
+//
+// v0.3b - 2016-05-16
+// - Fixed Linux/GCC build.
+// - Updated documentation.
+//
+// v0.3a - 2016-05-15
+// - Minor fixes to documentation.
+//
+// v0.3 - 2016-05-11
+// - Optimizations. Now at about parity with the reference implementation on 32-bit builds.
+// - Lots of clean up.
+//
+// v0.2b - 2016-05-10
+// - Bug fixes.
+//
+// v0.2a - 2016-05-10
+// - Made drflac_open_and_decode() more robust.
+// - Removed an unused debugging variable
+//
+// v0.2 - 2016-05-09
+// - Added support for Ogg encapsulation.
+// - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek
+// should be relative to the start or the current position. Also changes the seeking rules such that
+// seeking offsets will never be negative.
+// - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count.
+//
+// v0.1b - 2016-05-07
+// - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize.
+// - Removed a stale comment.
+//
+// v0.1a - 2016-05-05
+// - Minor formatting changes.
+// - Fixed a warning on the GCC build.
+//
+// v0.1 - 2016-05-03
+// - Initial versioned release.
+
+
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
diff --git a/include/kfr/io/dr/dr_wav.h b/include/kfr/io/dr/dr_wav.h
@@ -0,0 +1,3727 @@
+// WAV audio loader and writer. Public domain. See "unlicense" statement at the end of this file.
+// dr_wav - v0.8.5 - 2018-09-11
+//
+// David Reid - mackron@gmail.com
+
+// USAGE
+//
+// This is a single-file library. To use it, do something like the following in one .c file.
+// #define DR_WAV_IMPLEMENTATION
+// #include "dr_wav.h"
+//
+// You can then #include this file in other parts of the program as you would with any other header file. Do something
+// like the following to read audio data:
+//
+// drwav wav;
+// if (!drwav_init_file(&wav, "my_song.wav")) {
+// // Error opening WAV file.
+// }
+//
+// drwav_int32* pDecodedInterleavedSamples = malloc(wav.totalSampleCount * sizeof(drwav_int32));
+// size_t numberOfSamplesActuallyDecoded = drwav_read_s32(&wav, wav.totalSampleCount, pDecodedInterleavedSamples);
+//
+// ...
+//
+// drwav_uninit(&wav);
+//
+// You can also use drwav_open() to allocate and initialize the loader for you:
+//
+// drwav* pWav = drwav_open_file("my_song.wav");
+// if (pWav == NULL) {
+// // Error opening WAV file.
+// }
+//
+// ...
+//
+// drwav_close(pWav);
+//
+// If you just want to quickly open and read the audio data in a single operation you can do something like this:
+//
+// unsigned int channels;
+// unsigned int sampleRate;
+// drwav_uint64 totalSampleCount;
+// float* pSampleData = drwav_open_and_read_file_s32("my_song.wav", &channels, &sampleRate, &totalSampleCount);
+// if (pSampleData == NULL) {
+// // Error opening and reading WAV file.
+// }
+//
+// ...
+//
+// drwav_free(pSampleData);
+//
+// The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in
+// this case), but you can still output the audio data in its internal format (see notes below for supported formats):
+//
+// size_t samplesRead = drwav_read(&wav, wav.totalSampleCount, pDecodedInterleavedSamples);
+//
+// You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for
+// a particular data format:
+//
+// size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
+//
+//
+// dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work
+// without any manual intervention.
+//
+//
+// dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at
+// drwav_open_write(), drwav_open_file_write(), etc. Use drwav_write() to write samples, or drwav_write_raw() to write
+// raw data in the "data" chunk.
+//
+// drwav_data_format format;
+// format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
+// format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
+// format.channels = 2;
+// format.sampleRate = 44100;
+// format.bitsPerSample = 16;
+// drwav* pWav = drwav_open_file_write("data/recording.wav", &format);
+//
+// ...
+//
+// drwav_uint64 samplesWritten = drwav_write(pWav, sampleCount, pSamples);
+//
+//
+//
+// OPTIONS
+// #define these options before including this file.
+//
+// #define DR_WAV_NO_CONVERSION_API
+// Disables conversion APIs such as drwav_read_f32() and drwav_s16_to_f32().
+//
+// #define DR_WAV_NO_STDIO
+// Disables drwav_open_file(), drwav_open_file_write(), etc.
+//
+//
+//
+// QUICK NOTES
+// - Samples are always interleaved.
+// - The default read function does not do any data conversion. Use drwav_read_f32() to read and convert audio data
+// to IEEE 32-bit floating point samples, drwav_read_s32() to read samples as signed 32-bit PCM and drwav_read_s16()
+// to read samples as signed 16-bit PCM. Tested and supported internal formats include the following:
+// - Unsigned 8-bit PCM
+// - Signed 12-bit PCM
+// - Signed 16-bit PCM
+// - Signed 24-bit PCM
+// - Signed 32-bit PCM
+// - IEEE 32-bit floating point
+// - IEEE 64-bit floating point
+// - A-law and u-law
+// - Microsoft ADPCM
+// - IMA ADPCM (DVI, format code 0x11)
+// - dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
+
+
+#ifndef dr_wav_h
+#define dr_wav_h
+
+#include <stddef.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+typedef signed char drwav_int8;
+typedef unsigned char drwav_uint8;
+typedef signed short drwav_int16;
+typedef unsigned short drwav_uint16;
+typedef signed int drwav_int32;
+typedef unsigned int drwav_uint32;
+typedef signed __int64 drwav_int64;
+typedef unsigned __int64 drwav_uint64;
+#else
+#include <stdint.h>
+typedef int8_t drwav_int8;
+typedef uint8_t drwav_uint8;
+typedef int16_t drwav_int16;
+typedef uint16_t drwav_uint16;
+typedef int32_t drwav_int32;
+typedef uint32_t drwav_uint32;
+typedef int64_t drwav_int64;
+typedef uint64_t drwav_uint64;
+#endif
+typedef drwav_uint8 drwav_bool8;
+typedef drwav_uint32 drwav_bool32;
+#define DRWAV_TRUE 1
+#define DRWAV_FALSE 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Common data formats.
+#define DR_WAVE_FORMAT_PCM 0x1
+#define DR_WAVE_FORMAT_ADPCM 0x2
+#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
+#define DR_WAVE_FORMAT_ALAW 0x6
+#define DR_WAVE_FORMAT_MULAW 0x7
+#define DR_WAVE_FORMAT_DVI_ADPCM 0x11
+#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
+
+typedef enum
+{
+ drwav_seek_origin_start,
+ drwav_seek_origin_current
+} drwav_seek_origin;
+
+typedef enum
+{
+ drwav_container_riff,
+ drwav_container_w64
+} drwav_container;
+
+// Callback for when data is read. Return value is the number of bytes actually read.
+//
+// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
+// pBufferOut [out] The output buffer.
+// bytesToRead [in] The number of bytes to read.
+//
+// Returns the number of bytes actually read.
+//
+// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
+// either the entire bytesToRead is filled or you have reached the end of the stream.
+typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+// Callback for when data is written. Returns value is the number of bytes actually written.
+//
+// pUserData [in] The user data that was passed to drwav_init_write(), drwav_open_write() and family.
+// pData [out] A pointer to the data to write.
+// bytesToWrite [in] The number of bytes to write.
+//
+// Returns the number of bytes actually written.
+//
+// If the return value differs from bytesToWrite, it indicates an error.
+typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
+
+// Callback for when data needs to be seeked.
+//
+// pUserData [in] The user data that was passed to drwav_init(), drwav_open() and family.
+// offset [in] The number of bytes to move, relative to the origin. Will never be negative.
+// origin [in] The origin of the seek - the current position or the start of the stream.
+//
+// Returns whether or not the seek was successful.
+//
+// Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
+// will be either drwav_seek_origin_start or drwav_seek_origin_current.
+typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
+
+// Structure for internal use. Only used for loaders opened with drwav_open_memory().
+typedef struct
+{
+ const drwav_uint8* data;
+ size_t dataSize;
+ size_t currentReadPos;
+} drwav__memory_stream;
+
+// Structure for internal use. Only used for writers opened with drwav_open_memory_write().
+typedef struct
+{
+ void** ppData;
+ size_t* pDataSize;
+ size_t dataSize;
+ size_t dataCapacity;
+ size_t currentWritePos;
+} drwav__memory_stream_write;
+
+typedef struct
+{
+ drwav_container container; // RIFF, W64.
+ drwav_uint32 format; // DR_WAVE_FORMAT_*
+ drwav_uint32 channels;
+ drwav_uint32 sampleRate;
+ drwav_uint32 bitsPerSample;
+} drwav_data_format;
+
+typedef struct
+{
+ // The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
+ // that require support for data formats not natively supported by dr_wav.
+ drwav_uint16 formatTag;
+
+ // The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc.
+ drwav_uint16 channels;
+
+ // The sample rate. Usually set to something like 44100.
+ drwav_uint32 sampleRate;
+
+ // Average bytes per second. You probably don't need this, but it's left here for informational purposes.
+ drwav_uint32 avgBytesPerSec;
+
+ // Block align. This is equal to the number of channels * bytes per sample.
+ drwav_uint16 blockAlign;
+
+ // Bits per sample.
+ drwav_uint16 bitsPerSample;
+
+ // The size of the extended data. Only used internally for validation, but left here for informational purposes.
+ drwav_uint16 extendedSize;
+
+ // The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>
+ // is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
+ // many bits a valid per sample. Mainly used for informational purposes.
+ drwav_uint16 validBitsPerSample;
+
+ // The channel mask. Not used at the moment.
+ drwav_uint32 channelMask;
+
+ // The sub-format, exactly as specified by the wave file.
+ drwav_uint8 subFormat[16];
+} drwav_fmt;
+
+typedef struct
+{
+ // A pointer to the function to call when more data is needed.
+ drwav_read_proc onRead;
+
+ // A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode.
+ drwav_write_proc onWrite;
+
+ // A pointer to the function to call when the wav file needs to be seeked.
+ drwav_seek_proc onSeek;
+
+ // The user data to pass to callbacks.
+ void* pUserData;
+
+
+ // Whether or not the WAV file is formatted as a standard RIFF file or W64.
+ drwav_container container;
+
+
+ // Structure containing format information exactly as specified by the wav file.
+ drwav_fmt fmt;
+
+ // The sample rate. Will be set to something like 44100.
+ drwav_uint32 sampleRate;
+
+ // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc.
+ drwav_uint16 channels;
+
+ // The bits per sample. Will be set to something like 16, 24, etc.
+ drwav_uint16 bitsPerSample;
+
+ // The number of bytes per sample.
+ drwav_uint16 bytesPerSample;
+
+ // Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE).
+ drwav_uint16 translatedFormatTag;
+
+ // The total number of samples making up the audio data. Use <totalSampleCount> * <bytesPerSample> to calculate
+ // the required size of a buffer to hold the entire audio data.
+ drwav_uint64 totalSampleCount;
+
+
+ // The size in bytes of the data chunk.
+ drwav_uint64 dataChunkDataSize;
+
+ // The position in the stream of the first byte of the data chunk. This is used for seeking.
+ drwav_uint64 dataChunkDataPos;
+
+ // The number of bytes remaining in the data chunk.
+ drwav_uint64 bytesRemaining;
+
+
+ // Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always
+ // set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.
+ drwav_uint64 dataChunkDataSizeTargetWrite;
+
+ // Keeps track of whether or not the wav writer was initialized in sequential mode.
+ drwav_bool32 isSequentialWrite;
+
+
+ // A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_open_memory().
+ drwav__memory_stream memoryStream;
+ drwav__memory_stream_write memoryStreamWrite;
+
+ // Generic data for compressed formats. This data is shared across all block-compressed formats.
+ struct
+ {
+ drwav_uint64 iCurrentSample; // The index of the next sample that will be read by drwav_read_*(). This is used with "totalSampleCount" to ensure we don't read excess samples at the end of the last block.
+ } compressed;
+
+ // Microsoft ADPCM specific data.
+ struct
+ {
+ drwav_uint32 bytesRemainingInBlock;
+ drwav_uint16 predictor[2];
+ drwav_int32 delta[2];
+ drwav_int32 cachedSamples[4]; // Samples are stored in this cache during decoding.
+ drwav_uint32 cachedSampleCount;
+ drwav_int32 prevSamples[2][2]; // The previous 2 samples for each channel (2 channels at most).
+ } msadpcm;
+
+ // IMA ADPCM specific data.
+ struct
+ {
+ drwav_uint32 bytesRemainingInBlock;
+ drwav_int32 predictor[2];
+ drwav_int32 stepIndex[2];
+ drwav_int32 cachedSamples[16]; // Samples are stored in this cache during decoding.
+ drwav_uint32 cachedSampleCount;
+ } ima;
+} drwav;
+
+
+// Initializes a pre-allocated drwav object.
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns true if successful; false otherwise.
+//
+// Close the loader with drwav_uninit().
+//
+// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
+// a drwav object on the heap and return a pointer to it.
+//
+// See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
+drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
+
+// Initializes a pre-allocated drwav object for writing.
+//
+// onWrite [in] The function to call when data needs to be written.
+// onSeek [in] The function to call when the write position needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+//
+// Returns true if successful; false otherwise.
+//
+// Close the writer with drwav_uninit().
+//
+// This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+// to open the stream from a file or from a block of memory respectively.
+//
+// If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform
+// a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.
+//
+// If you want dr_wav to manage the memory allocation for you, consider using drwav_open() instead. This will allocate
+// a drwav object on the heap and return a pointer to it.
+//
+// See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
+drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData);
+
+// Uninitializes the given drwav object.
+//
+// Use this only for objects initialized with drwav_init().
+void drwav_uninit(drwav* pWav);
+
+
+// Opens a wav file using the given callbacks.
+//
+// onRead [in] The function to call when data needs to be read from the client.
+// onSeek [in] The function to call when the read position of the client data needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+//
+// Returns null on error.
+//
+// Close the loader with drwav_close().
+//
+// You can also use drwav_open_file() and drwav_open_memory() to open the stream from a file or from a block of
+// memory respectively.
+//
+// This is different from drwav_init() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
+// initializing it.
+//
+// See also: drwav_open_file(), drwav_open_memory(), drwav_close()
+drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData);
+
+// Opens a wav file for writing using the given callbacks.
+//
+// onWrite [in] The function to call when data needs to be written.
+// onSeek [in] The function to call when the write position needs to move.
+// pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+//
+// Returns null on error.
+//
+// Close the loader with drwav_close().
+//
+// You can also use drwav_open_file_write() and drwav_open_memory_write() to open the stream from a file or from a block
+// of memory respectively.
+//
+// This is different from drwav_init_write() in that it will allocate the drwav object for you via DRWAV_MALLOC() before
+// initializing it.
+//
+// See also: drwav_open_file_write(), drwav_open_memory_write(), drwav_close()
+drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+drwav* drwav_open_write_sequential(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData);
+
+// Uninitializes and deletes the the given drwav object.
+//
+// Use this only for objects created with drwav_open().
+void drwav_close(drwav* pWav);
+
+
+// Reads raw audio data.
+//
+// This is the lowest level function for reading audio data. It simply reads the given number of
+// bytes of the raw internal sample data.
+//
+// Consider using drwav_read_s16(), drwav_read_s32() or drwav_read_f32() for reading sample data in
+// a consistent format.
+//
+// Returns the number of bytes actually read.
+size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
+
+// Reads a chunk of audio data in the native internal format.
+//
+// This is typically the most efficient way to retrieve audio data, but it does not do any format
+// conversions which means you'll need to convert the data manually if required.
+//
+// If the return value is less than <samplesToRead> it means the end of the file has been reached or
+// you have requested more samples than can possibly fit in the output buffer.
+//
+// This function will only work when sample data is of a fixed size and uncompressed. If you are
+// using a compressed format consider using drwav_read_raw() or drwav_read_s16/s32/f32/etc().
+drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut);
+
+// Seeks to the given sample.
+//
+// Returns true if successful; false otherwise.
+drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample);
+
+
+// Writes raw audio data.
+//
+// Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
+size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
+
+// Writes audio data based on sample counts.
+//
+// Returns the number of samples written.
+drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData);
+
+
+
+//// Conversion Utilities ////
+#ifndef DR_WAV_NO_CONVERSION_API
+
+// Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than <samplesToRead> it means the end of the file has been reached.
+drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples.
+void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples.
+void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples.
+void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples.
+void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples.
+void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to signed 16-bit PCM samples.
+void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to signed 16-bit PCM samples.
+void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+
+// Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than <samplesToRead> it means the end of the file has been reached.
+drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples.
+void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples.
+void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to IEEE 32-bit floating point samples.
+void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to IEEE 32-bit floating point samples.
+void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+
+// Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
+//
+// Returns the number of samples actually read.
+//
+// If the return value is less than <samplesToRead> it means the end of the file has been reached.
+drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut);
+
+// Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples.
+void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples.
+void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
+
+// Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples.
+void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples.
+void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
+
+// Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples.
+void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
+
+// Low-level function for converting A-law samples to signed 32-bit PCM samples.
+void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+// Low-level function for converting u-law samples to signed 32-bit PCM samples.
+void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+#endif //DR_WAV_NO_CONVERSION_API
+
+
+//// High-Level Convenience Helpers ////
+
+#ifndef DR_WAV_NO_STDIO
+
+// Helper for initializing a wave file using stdio.
+//
+// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav_bool32 drwav_init_file(drwav* pWav, const char* filename);
+
+// Helper for initializing a wave file for writing using stdio.
+//
+// This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat);
+drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+// Helper for opening a wave file using stdio.
+//
+// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav* drwav_open_file(const char* filename);
+
+// Helper for opening a wave file for writing using stdio.
+//
+// This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav
+// objects because the operating system may restrict the number of file handles an application can have open at
+// any given time.
+drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat);
+drwav* drwav_open_file_write_sequential(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+#endif //DR_WAV_NO_STDIO
+
+// Helper for initializing a loader from a pre-allocated memory buffer.
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the drwav object.
+//
+// The buffer should contain the contents of the entire wave file, not just the sample data.
+drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize);
+
+// Helper for initializing a writer which outputs data to a memory buffer.
+//
+// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+//
+// The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be
+// considered valid until after drwav_uninit() has been called anyway.
+drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
+drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+// Helper for opening a loader from a pre-allocated memory buffer.
+//
+// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
+// the lifetime of the drwav object.
+//
+// The buffer should contain the contents of the entire wave file, not just the sample data.
+drwav* drwav_open_memory(const void* data, size_t dataSize);
+
+// Helper for opening a writer which outputs data to a memory buffer.
+//
+// dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+//
+// The buffer will remain allocated even after drwav_close() is called. Indeed, the buffer should not be
+// considered valid until after drwav_close() has been called anyway.
+drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat);
+drwav* drwav_open_memory_write_sequential(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+
+#ifndef DR_WAV_NO_CONVERSION_API
+// Opens and reads a wav file in a single operation.
+drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+#ifndef DR_WAV_NO_STDIO
+// Opens and decodes a wav file in a single operation.
+drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+#endif
+
+// Opens and decodes a wav file from a block of memory in a single operation.
+drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount);
+#endif
+
+// Frees data that was allocated internally by dr_wav.
+void drwav_free(void* pDataReturnedByOpenAndRead);
+
+#ifdef __cplusplus
+}
+#endif
+#endif // dr_wav_h
+
+
+/////////////////////////////////////////////////////
+//
+// IMPLEMENTATION
+//
+/////////////////////////////////////////////////////
+
+#ifdef DR_WAV_IMPLEMENTATION
+#include <stdlib.h>
+#include <string.h> // For memcpy(), memset()
+#include <limits.h> // For INT_MAX
+
+#ifndef DR_WAV_NO_STDIO
+#include <stdio.h>
+#endif
+
+// Standard library stuff.
+#ifndef DRWAV_ASSERT
+#include <assert.h>
+#define DRWAV_ASSERT(expression) assert(expression)
+#endif
+#ifndef DRWAV_MALLOC
+#define DRWAV_MALLOC(sz) malloc((sz))
+#endif
+#ifndef DRWAV_REALLOC
+#define DRWAV_REALLOC(p, sz) realloc((p), (sz))
+#endif
+#ifndef DRWAV_FREE
+#define DRWAV_FREE(p) free((p))
+#endif
+#ifndef DRWAV_COPY_MEMORY
+#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
+#endif
+#ifndef DRWAV_ZERO_MEMORY
+#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
+#endif
+
+#define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
+#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
+#define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
+#define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
+#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
+
+#define drwav_assert DRWAV_ASSERT
+#define drwav_copy_memory DRWAV_COPY_MEMORY
+#define drwav_zero_memory DRWAV_ZERO_MEMORY
+
+
+#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future.
+
+#ifdef _MSC_VER
+#define DRWAV_INLINE __forceinline
+#else
+#ifdef __GNUC__
+#define DRWAV_INLINE inline __attribute__((always_inline))
+#else
+#define DRWAV_INLINE inline
+#endif
+#endif
+
+#if defined(SIZE_MAX)
+ #define DRWAV_SIZE_MAX SIZE_MAX
+#else
+ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+ #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRWAV_SIZE_MAX 0xFFFFFFFF
+ #endif
+#endif
+
+static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; // 66666972-912E-11CF-A5D6-28DB04C10000
+static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 65766177-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 74636166-ACF3-11D3-8CD1-00C04F8EDB8A
+static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 61746164-ACF3-11D3-8CD1-00C04F8EDB8A
+
+static DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
+{
+ const drwav_uint32* a32 = (const drwav_uint32*)a;
+ const drwav_uint32* b32 = (const drwav_uint32*)b;
+
+ return
+ a32[0] == b32[0] &&
+ a32[1] == b32[1] &&
+ a32[2] == b32[2] &&
+ a32[3] == b32[3];
+}
+
+static DRWAV_INLINE drwav_bool32 drwav__fourcc_equal(const unsigned char* a, const char* b)
+{
+ return
+ a[0] == b[0] &&
+ a[1] == b[1] &&
+ a[2] == b[2] &&
+ a[3] == b[3];
+}
+
+
+
+static DRWAV_INLINE int drwav__is_little_endian()
+{
+ int n = 1;
+ return (*(char*)&n) == 1;
+}
+
+static DRWAV_INLINE unsigned short drwav__bytes_to_u16(const unsigned char* data)
+{
+ return (data[0] << 0) | (data[1] << 8);
+}
+
+static DRWAV_INLINE short drwav__bytes_to_s16(const unsigned char* data)
+{
+ return (short)drwav__bytes_to_u16(data);
+}
+
+static DRWAV_INLINE unsigned int drwav__bytes_to_u32(const unsigned char* data)
+{
+ return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+}
+
+static DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const unsigned char* data)
+{
+ return
+ ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
+ ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
+}
+
+static DRWAV_INLINE void drwav__bytes_to_guid(const unsigned char* data, drwav_uint8* guid)
+{
+ for (int i = 0; i < 16; ++i) {
+ guid[i] = data[i];
+ }
+}
+
+
+static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
+{
+ return
+ formatTag == DR_WAVE_FORMAT_ADPCM ||
+ formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
+}
+
+
+drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+drwav* drwav_open_write__internal(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData);
+
+typedef struct
+{
+ union
+ {
+ drwav_uint8 fourcc[4];
+ drwav_uint8 guid[16];
+ } id;
+
+ // The size in bytes of the chunk.
+ drwav_uint64 sizeInBytes;
+
+ // RIFF = 2 byte alignment.
+ // W64 = 8 byte alignment.
+ unsigned int paddingSize;
+
+} drwav__chunk_header;
+
+static drwav_bool32 drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav__chunk_header* pHeaderOut)
+{
+ if (container == drwav_container_riff) {
+ if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
+ return DRWAV_FALSE;
+ }
+
+ unsigned char sizeInBytes[4];
+ if (onRead(pUserData, sizeInBytes, 4) != 4) {
+ return DRWAV_FALSE;
+ }
+
+ pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes);
+ pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 2);
+ *pRunningBytesReadOut += 8;
+ } else {
+ if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
+ return DRWAV_FALSE;
+ }
+
+ unsigned char sizeInBytes[8];
+ if (onRead(pUserData, sizeInBytes, 8) != 8) {
+ return DRWAV_FALSE;
+ }
+
+ pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; // <-- Subtract 24 because w64 includes the size of the header.
+ pHeaderOut->paddingSize = (unsigned int)(pHeaderOut->sizeInBytes % 8);
+ *pRunningBytesReadOut += 24;
+ }
+
+ return DRWAV_TRUE;
+}
+
+static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
+{
+ drwav_uint64 bytesRemainingToSeek = offset;
+ while (bytesRemainingToSeek > 0) {
+ if (bytesRemainingToSeek > 0x7FFFFFFF) {
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ bytesRemainingToSeek -= 0x7FFFFFFF;
+ } else {
+ if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ bytesRemainingToSeek = 0;
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)
+{
+ drwav__chunk_header header;
+ if (!drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header)) {
+ return DRWAV_FALSE;
+ }
+
+
+ // Skip non-fmt chunks.
+ while ((container == drwav_container_riff && !drwav__fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
+ if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
+
+ // Try the next header.
+ if (!drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ // Validation.
+ if (container == drwav_container_riff) {
+ if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) {
+ return DRWAV_FALSE;
+ }
+ } else {
+ if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ unsigned char fmt[16];
+ if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += sizeof(fmt);
+
+ fmtOut->formatTag = drwav__bytes_to_u16(fmt + 0);
+ fmtOut->channels = drwav__bytes_to_u16(fmt + 2);
+ fmtOut->sampleRate = drwav__bytes_to_u32(fmt + 4);
+ fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8);
+ fmtOut->blockAlign = drwav__bytes_to_u16(fmt + 12);
+ fmtOut->bitsPerSample = drwav__bytes_to_u16(fmt + 14);
+
+ fmtOut->extendedSize = 0;
+ fmtOut->validBitsPerSample = 0;
+ fmtOut->channelMask = 0;
+ memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
+
+ if (header.sizeInBytes > 16) {
+ unsigned char fmt_cbSize[2];
+ if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
+ return DRWAV_FALSE; // Expecting more data.
+ }
+ *pRunningBytesReadOut += sizeof(fmt_cbSize);
+
+ int bytesReadSoFar = 18;
+
+ fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize);
+ if (fmtOut->extendedSize > 0) {
+ // Simple validation.
+ if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ if (fmtOut->extendedSize != 22) {
+ return DRWAV_FALSE;
+ }
+ }
+
+ if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ unsigned char fmtext[22];
+ if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
+ return DRWAV_FALSE; // Expecting more data.
+ }
+
+ fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0);
+ fmtOut->channelMask = drwav__bytes_to_u32(fmtext + 2);
+ drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat);
+ } else {
+ if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ }
+ *pRunningBytesReadOut += fmtOut->extendedSize;
+
+ bytesReadSoFar += fmtOut->extendedSize;
+ }
+
+ // Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size.
+ if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);
+ }
+
+ if (header.paddingSize > 0) {
+ if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += header.paddingSize;
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+#ifndef DR_WAV_NO_STDIO
+FILE* drwav_fopen(const char* filePath, const char* openMode)
+{
+ FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (fopen_s(&pFile, filePath, openMode) != 0) {
+ return DRWAV_FALSE;
+ }
+#else
+ pFile = fopen(filePath, openMode);
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+#endif
+
+ return pFile;
+}
+
+static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
+}
+
+static size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
+{
+ return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
+}
+
+static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+drwav_bool32 drwav_init_file(drwav* pWav, const char* filename)
+{
+ FILE* pFile = drwav_fopen(filename, "rb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile);
+}
+
+
+drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
+{
+ FILE* pFile = drwav_fopen(filename, "wb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
+}
+
+drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat)
+{
+ return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE);
+}
+
+drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE);
+}
+
+drwav* drwav_open_file(const char* filename)
+{
+ FILE* pFile = drwav_fopen(filename, "rb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ drwav* pWav = drwav_open(drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile);
+ if (pWav == NULL) {
+ fclose(pFile);
+ return NULL;
+ }
+
+ return pWav;
+}
+
+
+drwav* drwav_open_file_write__internal(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
+{
+ FILE* pFile = drwav_fopen(filename, "wb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ drwav* pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile);
+ if (pWav == NULL) {
+ fclose(pFile);
+ return NULL;
+ }
+
+ return pWav;
+}
+
+drwav* drwav_open_file_write(const char* filename, const drwav_data_format* pFormat)
+{
+ return drwav_open_file_write__internal(filename, pFormat, 0, DRWAV_FALSE);
+}
+
+drwav* drwav_open_file_write_sequential(const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ return drwav_open_file_write__internal(filename, pFormat, totalSampleCount, DRWAV_TRUE);
+}
+#endif //DR_WAV_NO_STDIO
+
+
+static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ drwav__memory_stream* memory = (drwav__memory_stream*)pUserData;
+ drwav_assert(memory != NULL);
+ drwav_assert(memory->dataSize >= memory->currentReadPos);
+
+ size_t bytesRemaining = memory->dataSize - memory->currentReadPos;
+ if (bytesToRead > bytesRemaining) {
+ bytesToRead = bytesRemaining;
+ }
+
+ if (bytesToRead > 0) {
+ DRWAV_COPY_MEMORY(pBufferOut, memory->data + memory->currentReadPos, bytesToRead);
+ memory->currentReadPos += bytesToRead;
+ }
+
+ return bytesToRead;
+}
+
+static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ drwav__memory_stream* memory = (drwav__memory_stream*)pUserData;
+ drwav_assert(memory != NULL);
+
+ if (origin == drwav_seek_origin_current) {
+ if (offset > 0) {
+ if (memory->currentReadPos + offset > memory->dataSize) {
+ return DRWAV_FALSE; // Trying to seek too far forward.
+ }
+ } else {
+ if (memory->currentReadPos < (size_t)-offset) {
+ return DRWAV_FALSE; // Trying to seek too far backwards.
+ }
+ }
+
+ // This will never underflow thanks to the clamps above.
+ memory->currentReadPos += offset;
+ } else {
+ if ((drwav_uint32)offset <= memory->dataSize) {
+ memory->currentReadPos = offset;
+ } else {
+ return DRWAV_FALSE; // Trying to seek too far forward.
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
+{
+ drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
+ drwav_assert(memory != NULL);
+ drwav_assert(memory->dataCapacity >= memory->currentWritePos);
+
+ size_t bytesRemaining = memory->dataCapacity - memory->currentWritePos;
+ if (bytesRemaining < bytesToWrite) {
+ // Need to reallocate.
+ size_t newDataCapacity = (memory->dataCapacity == 0) ? 256 : memory->dataCapacity * 2;
+
+ // If doubling wasn't enough, just make it the minimum required size to write the data.
+ if ((newDataCapacity - memory->currentWritePos) < bytesToWrite) {
+ newDataCapacity = memory->currentWritePos + bytesToWrite;
+ }
+
+ void* pNewData = DRWAV_REALLOC(*memory->ppData, newDataCapacity);
+ if (pNewData == NULL) {
+ return 0;
+ }
+
+ *memory->ppData = pNewData;
+ memory->dataCapacity = newDataCapacity;
+ }
+
+ drwav_uint8* pDataOut = (drwav_uint8*)(*memory->ppData);
+ DRWAV_COPY_MEMORY(pDataOut + memory->currentWritePos, pDataIn, bytesToWrite);
+
+ memory->currentWritePos += bytesToWrite;
+ if (memory->dataSize < memory->currentWritePos) {
+ memory->dataSize = memory->currentWritePos;
+ }
+
+ *memory->pDataSize = memory->dataSize;
+
+ return bytesToWrite;
+}
+
+static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ drwav__memory_stream_write* memory = (drwav__memory_stream_write*)pUserData;
+ drwav_assert(memory != NULL);
+
+ if (origin == drwav_seek_origin_current) {
+ if (offset > 0) {
+ if (memory->currentWritePos + offset > memory->dataSize) {
+ offset = (int)(memory->dataSize - memory->currentWritePos); // Trying to seek too far forward.
+ }
+ } else {
+ if (memory->currentWritePos < (size_t)-offset) {
+ offset = -(int)memory->currentWritePos; // Trying to seek too far backwards.
+ }
+ }
+
+ // This will never underflow thanks to the clamps above.
+ memory->currentWritePos += offset;
+ } else {
+ if ((drwav_uint32)offset <= memory->dataSize) {
+ memory->currentWritePos = offset;
+ } else {
+ memory->currentWritePos = memory->dataSize; // Trying to seek too far forward.
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize)
+{
+ if (data == NULL || dataSize == 0) {
+ return DRWAV_FALSE;
+ }
+
+ drwav__memory_stream memoryStream;
+ drwav_zero_memory(&memoryStream, sizeof(memoryStream));
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+
+ if (!drwav_init(pWav, drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->memoryStream = memoryStream;
+ pWav->pUserData = &pWav->memoryStream;
+ return DRWAV_TRUE;
+}
+
+
+drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
+{
+ if (ppData == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ *ppData = NULL; // Important because we're using realloc()!
+ *pDataSize = 0;
+
+ drwav__memory_stream_write memoryStreamWrite;
+ drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
+ memoryStreamWrite.ppData = ppData;
+ memoryStreamWrite.pDataSize = pDataSize;
+ memoryStreamWrite.dataSize = 0;
+ memoryStreamWrite.dataCapacity = 0;
+ memoryStreamWrite.currentWritePos = 0;
+
+ if (!drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->memoryStreamWrite = memoryStreamWrite;
+ pWav->pUserData = &pWav->memoryStreamWrite;
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat)
+{
+ return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE);
+}
+
+drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE);
+}
+
+
+drwav* drwav_open_memory(const void* data, size_t dataSize)
+{
+ if (data == NULL || dataSize == 0) {
+ return NULL;
+ }
+
+ drwav__memory_stream memoryStream;
+ drwav_zero_memory(&memoryStream, sizeof(memoryStream));
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+
+ drwav* pWav = drwav_open(drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream);
+ if (pWav == NULL) {
+ return NULL;
+ }
+
+ pWav->memoryStream = memoryStream;
+ pWav->pUserData = &pWav->memoryStream;
+ return pWav;
+}
+
+
+drwav* drwav_open_memory_write__internal(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential)
+{
+ if (ppData == NULL) {
+ return NULL;
+ }
+
+ *ppData = NULL; // Important because we're using realloc()!
+ *pDataSize = 0;
+
+ drwav__memory_stream_write memoryStreamWrite;
+ drwav_zero_memory(&memoryStreamWrite, sizeof(memoryStreamWrite));
+ memoryStreamWrite.ppData = ppData;
+ memoryStreamWrite.pDataSize = pDataSize;
+ memoryStreamWrite.dataSize = 0;
+ memoryStreamWrite.dataCapacity = 0;
+ memoryStreamWrite.currentWritePos = 0;
+
+ drwav* pWav = drwav_open_write__internal(pFormat, totalSampleCount, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, (void*)&memoryStreamWrite);
+ if (pWav == NULL) {
+ return NULL;
+ }
+
+ pWav->memoryStreamWrite = memoryStreamWrite;
+ pWav->pUserData = &pWav->memoryStreamWrite;
+ return pWav;
+}
+
+drwav* drwav_open_memory_write(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat)
+{
+ return drwav_open_memory_write__internal(ppData, pDataSize, pFormat, 0, DRWAV_FALSE);
+}
+
+drwav* drwav_open_memory_write_sequential(void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ return drwav_open_memory_write__internal(ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE);
+}
+
+
+drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
+{
+ if (onRead == NULL || onSeek == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ drwav_zero_memory(pWav, sizeof(*pWav));
+
+
+ // The first 4 bytes should be the RIFF identifier.
+ unsigned char riff[4];
+ if (onRead(pUserData, riff, sizeof(riff)) != sizeof(riff)) {
+ return DRWAV_FALSE; // Failed to read data.
+ }
+
+ // The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
+ // w64 it will start with "riff".
+ if (drwav__fourcc_equal(riff, "RIFF")) {
+ pWav->container = drwav_container_riff;
+ } else if (drwav__fourcc_equal(riff, "riff")) {
+ pWav->container = drwav_container_w64;
+
+ // Check the rest of the GUID for validity.
+ drwav_uint8 riff2[12];
+ if (onRead(pUserData, riff2, sizeof(riff2)) != sizeof(riff2)) {
+ return DRWAV_FALSE;
+ }
+
+ for (int i = 0; i < 12; ++i) {
+ if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
+ return DRWAV_FALSE;
+ }
+ }
+ } else {
+ return DRWAV_FALSE; // Unknown or unsupported container.
+ }
+
+
+ if (pWav->container == drwav_container_riff) {
+ // RIFF/WAVE
+ unsigned char chunkSizeBytes[4];
+ if (onRead(pUserData, chunkSizeBytes, sizeof(chunkSizeBytes)) != sizeof(chunkSizeBytes)) {
+ return DRWAV_FALSE;
+ }
+
+ unsigned int chunkSize = drwav__bytes_to_u32(chunkSizeBytes);
+ if (chunkSize < 36) {
+ return DRWAV_FALSE; // Chunk size should always be at least 36 bytes.
+ }
+
+ unsigned char wave[4];
+ if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) {
+ return DRWAV_FALSE;
+ }
+
+ if (!drwav__fourcc_equal(wave, "WAVE")) {
+ return DRWAV_FALSE; // Expecting "WAVE".
+ }
+
+ pWav->dataChunkDataPos = 4 + sizeof(chunkSizeBytes) + sizeof(wave);
+ } else {
+ // W64
+ unsigned char chunkSize[8];
+ if (onRead(pUserData, chunkSize, sizeof(chunkSize)) != sizeof(chunkSize)) {
+ return DRWAV_FALSE;
+ }
+
+ if (drwav__bytes_to_u64(chunkSize) < 80) {
+ return DRWAV_FALSE;
+ }
+
+ drwav_uint8 wave[16];
+ if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) {
+ return DRWAV_FALSE;
+ }
+
+ if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->dataChunkDataPos = 16 + sizeof(chunkSize) + sizeof(wave);
+ }
+
+
+ // The next bytes should be the "fmt " chunk.
+ drwav_fmt fmt;
+ if (!drwav__read_fmt(onRead, onSeek, pUserData, pWav->container, &pWav->dataChunkDataPos, &fmt)) {
+ return DRWAV_FALSE; // Failed to read the "fmt " chunk.
+ }
+
+ // Basic validation.
+ if (fmt.sampleRate == 0 || fmt.channels == 0 || fmt.bitsPerSample == 0 || fmt.blockAlign == 0) {
+ return DRWAV_FALSE; // Invalid channel count. Probably an invalid WAV file.
+ }
+
+
+ // Translate the internal format.
+ unsigned short translatedFormatTag = fmt.formatTag;
+ if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0);
+ }
+
+
+ drwav_uint64 sampleCountFromFactChunk = 0;
+
+ // The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop.
+ drwav_uint64 dataSize;
+ for (;;)
+ {
+ drwav__chunk_header header;
+ if (!drwav__read_chunk_header(onRead, pUserData, pWav->container, &pWav->dataChunkDataPos, &header)) {
+ return DRWAV_FALSE;
+ }
+
+ dataSize = header.sizeInBytes;
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "data")) {
+ break;
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
+ break;
+ }
+ }
+
+ // Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats.
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "fact")) {
+ drwav_uint32 sampleCount;
+ if (onRead(pUserData, &sampleCount, 4) != 4) {
+ return DRWAV_FALSE;
+ }
+ pWav->dataChunkDataPos += 4;
+ dataSize -= 4;
+
+ // The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
+ // for Microsoft ADPCM formats.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ sampleCountFromFactChunk = sampleCount;
+ } else {
+ sampleCountFromFactChunk = 0;
+ }
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
+ if (onRead(pUserData, &sampleCountFromFactChunk, 8) != 8) {
+ return DRWAV_FALSE;
+ }
+ pWav->dataChunkDataPos += 8;
+ dataSize -= 8;
+ }
+ }
+
+ // If we get here it means we didn't find the "data" chunk. Seek past it.
+
+ // Make sure we seek past the padding.
+ dataSize += header.paddingSize;
+ drwav__seek_forward(onSeek, dataSize, pUserData);
+ pWav->dataChunkDataPos += dataSize;
+ }
+
+ // At this point we should be sitting on the first byte of the raw audio data.
+
+ pWav->onRead = onRead;
+ pWav->onSeek = onSeek;
+ pWav->pUserData = pUserData;
+ pWav->fmt = fmt;
+ pWav->sampleRate = fmt.sampleRate;
+ pWav->channels = fmt.channels;
+ pWav->bitsPerSample = fmt.bitsPerSample;
+ pWav->bytesPerSample = fmt.blockAlign / fmt.channels;
+ pWav->bytesRemaining = dataSize;
+ pWav->translatedFormatTag = translatedFormatTag;
+ pWav->dataChunkDataSize = dataSize;
+
+ // The bytes per sample should never be 0 at this point. This would indicate an invalid WAV file.
+ if (pWav->bytesPerSample == 0) {
+ return DRWAV_FALSE;
+ }
+
+ if (sampleCountFromFactChunk != 0) {
+ pWav->totalSampleCount = sampleCountFromFactChunk * fmt.channels;
+ } else {
+ pWav->totalSampleCount = dataSize / pWav->bytesPerSample;
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ drwav_uint64 blockCount = dataSize / fmt.blockAlign;
+ pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte.
+ }
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ drwav_uint64 blockCount = dataSize / fmt.blockAlign;
+ pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels);
+ }
+ }
+
+ // The way we calculate the bytes per sample does not make sense for compressed formats so we just set it to 0.
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ pWav->bytesPerSample = 0;
+ }
+
+ // Some formats only support a certain number of channels.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ if (pWav->channels > 2) {
+ return DRWAV_FALSE;
+ }
+ }
+
+#ifdef DR_WAV_LIBSNDFILE_COMPAT
+ // I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
+ // it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
+ // from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct
+ // way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should
+ // always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my
+ // correctness tests against libsndfile, and is disabled by default.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ drwav_uint64 blockCount = dataSize / fmt.blockAlign;
+ pWav->totalSampleCount = (blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2; // x2 because two samples per byte.
+ }
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ drwav_uint64 blockCount = dataSize / fmt.blockAlign;
+ pWav->totalSampleCount = ((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels);
+ }
+#endif
+
+ return DRWAV_TRUE;
+}
+
+
+drwav_uint32 drwav_riff_chunk_size_riff(drwav_uint64 dataChunkSize)
+{
+ if (dataChunkSize <= (0xFFFFFFFF - 36)) {
+ return 36 + (drwav_uint32)dataChunkSize;
+ } else {
+ return 0xFFFFFFFF;
+ }
+}
+
+drwav_uint32 drwav_data_chunk_size_riff(drwav_uint64 dataChunkSize)
+{
+ if (dataChunkSize <= 0xFFFFFFFF) {
+ return (drwav_uint32)dataChunkSize;
+ } else {
+ return 0xFFFFFFFF;
+ }
+}
+
+drwav_uint64 drwav_riff_chunk_size_w64(drwav_uint64 dataChunkSize)
+{
+ return 80 + 24 + dataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+}
+
+drwav_uint64 drwav_data_chunk_size_w64(drwav_uint64 dataChunkSize)
+{
+ return 24 + dataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+}
+
+
+drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+ if (pWav == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ if (onWrite == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ if (!isSequential && onSeek == NULL) {
+ return DRWAV_FALSE; // <-- onSeek is required when in non-sequential mode.
+ }
+
+
+ // Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this.
+ if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
+ return DRWAV_FALSE;
+ }
+ if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return DRWAV_FALSE;
+ }
+
+
+ drwav_zero_memory(pWav, sizeof(*pWav));
+ pWav->onWrite = onWrite;
+ pWav->onSeek = onSeek;
+ pWav->pUserData = pUserData;
+ pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
+ pWav->fmt.channels = (drwav_uint16)pFormat->channels;
+ pWav->fmt.sampleRate = pFormat->sampleRate;
+ pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
+ pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
+ pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
+ pWav->fmt.extendedSize = 0;
+ pWav->isSequentialWrite = isSequential;
+
+
+ size_t runningPos = 0;
+
+ // The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In
+ // sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-
+ // sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.
+ drwav_uint64 initialDataChunkSize = 0;
+ if (isSequential) {
+ initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
+
+ // The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64
+ // so for the sake of simplicity I'm not doing any validation for that.
+ if (pFormat->container == drwav_container_riff) {
+ if (initialDataChunkSize > (0xFFFFFFFF - 36)) {
+ return DRWAV_FALSE; // Not enough room to store every sample.
+ }
+ }
+ }
+
+ pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
+
+
+ // "RIFF" chunk.
+ if (pFormat->container == drwav_container_riff) {
+ drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; // +36 = "RIFF"+[RIFF Chunk Size]+"WAVE" + [sizeof "fmt " chunk]
+ runningPos += pWav->onWrite(pUserData, "RIFF", 4);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 4);
+ runningPos += pWav->onWrite(pUserData, "WAVE", 4);
+ } else {
+ drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+ runningPos += pWav->onWrite(pUserData, drwavGUID_W64_RIFF, 16);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeRIFF, 8);
+ runningPos += pWav->onWrite(pUserData, drwavGUID_W64_WAVE, 16);
+ }
+
+ // "fmt " chunk.
+ drwav_uint64 chunkSizeFMT;
+ if (pFormat->container == drwav_container_riff) {
+ chunkSizeFMT = 16;
+ runningPos += pWav->onWrite(pUserData, "fmt ", 4);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 4);
+ } else {
+ chunkSizeFMT = 40;
+ runningPos += pWav->onWrite(pUserData, drwavGUID_W64_FMT, 16);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeFMT, 8);
+ }
+
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.formatTag, 2);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.channels, 2);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.sampleRate, 4);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.avgBytesPerSec, 4);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.blockAlign, 2);
+ runningPos += pWav->onWrite(pUserData, &pWav->fmt.bitsPerSample, 2);
+
+ pWav->dataChunkDataPos = runningPos;
+
+ // "data" chunk.
+ if (pFormat->container == drwav_container_riff) {
+ drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
+ runningPos += pWav->onWrite(pUserData, "data", 4);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 4);
+ } else {
+ drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; // +24 because W64 includes the size of the GUID and size fields.
+ runningPos += pWav->onWrite(pUserData, drwavGUID_W64_DATA, 16);
+ runningPos += pWav->onWrite(pUserData, &chunkSizeDATA, 8);
+ }
+
+
+ // Simple validation.
+ if (pFormat->container == drwav_container_riff) {
+ if (runningPos != 20 + chunkSizeFMT + 8) {
+ return DRWAV_FALSE;
+ }
+ } else {
+ if (runningPos != 40 + chunkSizeFMT + 24) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+
+ // Set some properties for the client's convenience.
+ pWav->container = pFormat->container;
+ pWav->channels = (drwav_uint16)pFormat->channels;
+ pWav->sampleRate = pFormat->sampleRate;
+ pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
+ pWav->bytesPerSample = (drwav_uint16)(pFormat->bitsPerSample >> 3);
+ pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
+
+ return DRWAV_TRUE;
+}
+
+
+drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+ return drwav_init_write__internal(pWav, pFormat, 0, DRWAV_FALSE, onWrite, onSeek, pUserData); // DRWAV_FALSE = Not Sequential
+}
+
+drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData)
+{
+ return drwav_init_write__internal(pWav, pFormat, totalSampleCount, DRWAV_TRUE, onWrite, NULL, pUserData); // DRWAV_TRUE = Sequential
+}
+
+void drwav_uninit(drwav* pWav)
+{
+ if (pWav == NULL) {
+ return;
+ }
+
+ // If the drwav object was opened in write mode we'll need to finalize a few things:
+ // - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.
+ // - Set the size of the "data" chunk.
+ if (pWav->onWrite != NULL) {
+ // Validation for sequential mode.
+ if (pWav->isSequentialWrite) {
+ drwav_assert(pWav->dataChunkDataSize == pWav->dataChunkDataSizeTargetWrite);
+ }
+
+ // Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding.
+ drwav_uint32 paddingSize = 0;
+ if (pWav->container == drwav_container_riff) {
+ paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 2);
+ } else {
+ paddingSize = (drwav_uint32)(pWav->dataChunkDataSize % 8);
+ }
+
+ if (paddingSize > 0) {
+ drwav_uint64 paddingData = 0;
+ pWav->onWrite(pWav->pUserData, &paddingData, paddingSize);
+ }
+
+
+ // Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need
+ // to do this when using non-sequential mode.
+ if (pWav->onSeek && !pWav->isSequentialWrite) {
+ if (pWav->container == drwav_container_riff) {
+ // The "RIFF" chunk size.
+ if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
+ drwav_uint32 riffChunkSize = drwav_riff_chunk_size_riff(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &riffChunkSize, 4);
+ }
+
+ // the "data" chunk size.
+ if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) {
+ drwav_uint32 dataChunkSize = drwav_data_chunk_size_riff(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &dataChunkSize, 4);
+ }
+ } else {
+ // The "RIFF" chunk size.
+ if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
+ drwav_uint64 riffChunkSize = drwav_riff_chunk_size_w64(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &riffChunkSize, 8);
+ }
+
+ // The "data" chunk size.
+ if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) {
+ drwav_uint64 dataChunkSize = drwav_data_chunk_size_w64(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &dataChunkSize, 8);
+ }
+ }
+ }
+ }
+
+#ifndef DR_WAV_NO_STDIO
+ // If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()
+ // was used by looking at the onRead and onSeek callbacks.
+ if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
+ fclose((FILE*)pWav->pUserData);
+ }
+#endif
+}
+
+
+drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData)
+{
+ drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav));
+ if (pWav == NULL) {
+ return NULL;
+ }
+
+ if (!drwav_init(pWav, onRead, onSeek, pUserData)) {
+ DRWAV_FREE(pWav);
+ return NULL;
+ }
+
+ return pWav;
+}
+
+
+drwav* drwav_open_write__internal(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+ drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav));
+ if (pWav == NULL) {
+ return NULL;
+ }
+
+ if (!drwav_init_write__internal(pWav, pFormat, totalSampleCount, isSequential, onWrite, onSeek, pUserData)) {
+ DRWAV_FREE(pWav);
+ return NULL;
+ }
+
+ return pWav;
+}
+
+drwav* drwav_open_write(const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData)
+{
+ return drwav_open_write__internal(pFormat, 0, DRWAV_FALSE, onWrite, onSeek, pUserData);
+}
+
+drwav* drwav_open_write_sequential(const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData)
+{
+ return drwav_open_write__internal(pFormat, totalSampleCount, DRWAV_TRUE, onWrite, NULL, pUserData);
+}
+
+void drwav_close(drwav* pWav)
+{
+ drwav_uninit(pWav);
+ DRWAV_FREE(pWav);
+}
+
+
+size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
+{
+ if (pWav == NULL || bytesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ if (bytesToRead > pWav->bytesRemaining) {
+ bytesToRead = (size_t)pWav->bytesRemaining;
+ }
+
+ size_t bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
+
+ pWav->bytesRemaining -= bytesRead;
+ return bytesRead;
+}
+
+drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut)
+{
+ if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ // Cannot use this function for compressed formats.
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ return 0;
+ }
+
+ // Don't try to read more samples than can potentially fit in the output buffer.
+ if (samplesToRead * pWav->bytesPerSample > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / pWav->bytesPerSample;
+ }
+
+ size_t bytesRead = drwav_read_raw(pWav, (size_t)(samplesToRead * pWav->bytesPerSample), pBufferOut);
+ return bytesRead / pWav->bytesPerSample;
+}
+
+drwav_bool32 drwav_seek_to_first_sample(drwav* pWav)
+{
+ if (pWav->onWrite != NULL) {
+ return DRWAV_FALSE; // No seeking in write mode.
+ }
+
+ if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
+ return DRWAV_FALSE;
+ }
+
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ pWav->compressed.iCurrentSample = 0;
+ }
+
+ pWav->bytesRemaining = pWav->dataChunkDataSize;
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample)
+{
+ // Seeking should be compatible with wave files > 2GB.
+
+ if (pWav->onWrite != NULL) {
+ return DRWAV_FALSE; // No seeking in write mode.
+ }
+
+ if (pWav == NULL || pWav->onSeek == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ // If there are no samples, just return DRWAV_TRUE without doing anything.
+ if (pWav->totalSampleCount == 0) {
+ return DRWAV_TRUE;
+ }
+
+ // Make sure the sample is clamped.
+ if (sample >= pWav->totalSampleCount) {
+ sample = pWav->totalSampleCount - 1;
+ }
+
+
+ // For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need
+ // to seek back to the start.
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ // TODO: This can be optimized.
+
+ // If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards,
+ // we first need to seek back to the start and then just do the same thing as a forward seek.
+ if (sample < pWav->compressed.iCurrentSample) {
+ if (!drwav_seek_to_first_sample(pWav)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+ if (sample > pWav->compressed.iCurrentSample) {
+ drwav_uint64 offset = sample - pWav->compressed.iCurrentSample;
+
+ drwav_int16 devnull[2048];
+ while (offset > 0) {
+ drwav_uint64 samplesToRead = offset;
+ if (samplesToRead > 2048) {
+ samplesToRead = 2048;
+ }
+
+ drwav_uint64 samplesRead = 0;
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ samplesRead = drwav_read_s16__msadpcm(pWav, samplesToRead, devnull);
+ } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ samplesRead = drwav_read_s16__ima(pWav, samplesToRead, devnull);
+ } else {
+ assert(DRWAV_FALSE); // If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here.
+ }
+
+ if (samplesRead != samplesToRead) {
+ return DRWAV_FALSE;
+ }
+
+ offset -= samplesRead;
+ }
+ }
+ } else {
+ drwav_uint64 totalSizeInBytes = pWav->totalSampleCount * pWav->bytesPerSample;
+ drwav_assert(totalSizeInBytes >= pWav->bytesRemaining);
+
+ drwav_uint64 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
+ drwav_uint64 targetBytePos = sample * pWav->bytesPerSample;
+
+ drwav_uint64 offset;
+ if (currentBytePos < targetBytePos) {
+ // Offset forwards.
+ offset = (targetBytePos - currentBytePos);
+ } else {
+ // Offset backwards.
+ if (!drwav_seek_to_first_sample(pWav)) {
+ return DRWAV_FALSE;
+ }
+ offset = targetBytePos;
+ }
+
+ while (offset > 0) {
+ int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
+ if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->bytesRemaining -= offset32;
+ offset -= offset32;
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
+{
+ if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
+ return 0;
+ }
+
+ size_t bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
+ pWav->dataChunkDataSize += bytesWritten;
+
+ return bytesWritten;
+}
+
+drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData)
+{
+ if (pWav == NULL || samplesToWrite == 0 || pData == NULL) {
+ return 0;
+ }
+
+ drwav_uint64 bytesToWrite = ((samplesToWrite * pWav->bitsPerSample) / 8);
+ if (bytesToWrite > DRWAV_SIZE_MAX) {
+ return 0;
+ }
+
+ drwav_uint64 bytesWritten = 0;
+ const drwav_uint8* pRunningData = (const drwav_uint8*)pData;
+ while (bytesToWrite > 0) {
+ drwav_uint64 bytesToWriteThisIteration = bytesToWrite;
+ if (bytesToWriteThisIteration > DRWAV_SIZE_MAX) {
+ bytesToWriteThisIteration = DRWAV_SIZE_MAX;
+ }
+
+ size_t bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
+ if (bytesJustWritten == 0) {
+ break;
+ }
+
+ bytesToWrite -= bytesJustWritten;
+ bytesWritten += bytesJustWritten;
+ pRunningData += bytesJustWritten;
+ }
+
+ return (bytesWritten * 8) / pWav->bitsPerSample;
+}
+
+
+
+drwav_uint64 drwav_read_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_assert(pWav != NULL);
+ drwav_assert(samplesToRead > 0);
+ drwav_assert(pBufferOut != NULL);
+
+ // TODO: Lots of room for optimization here.
+
+ drwav_uint64 totalSamplesRead = 0;
+
+ while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+ // If there are no cached samples we need to load a new block.
+ if (pWav->msadpcm.cachedSampleCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
+ if (pWav->channels == 1) {
+ // Mono.
+ drwav_uint8 header[7];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalSamplesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->msadpcm.predictor[0] = header[0];
+ pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 1);
+ pWav->msadpcm.prevSamples[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 3);
+ pWav->msadpcm.prevSamples[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 5);
+ pWav->msadpcm.cachedSamples[2] = pWav->msadpcm.prevSamples[0][0];
+ pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.cachedSampleCount = 2;
+ } else {
+ // Stereo.
+ drwav_uint8 header[14];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalSamplesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->msadpcm.predictor[0] = header[0];
+ pWav->msadpcm.predictor[1] = header[1];
+ pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 2);
+ pWav->msadpcm.delta[1] = drwav__bytes_to_s16(header + 4);
+ pWav->msadpcm.prevSamples[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 6);
+ pWav->msadpcm.prevSamples[1][1] = (drwav_int32)drwav__bytes_to_s16(header + 8);
+ pWav->msadpcm.prevSamples[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 10);
+ pWav->msadpcm.prevSamples[1][0] = (drwav_int32)drwav__bytes_to_s16(header + 12);
+
+ pWav->msadpcm.cachedSamples[0] = pWav->msadpcm.prevSamples[0][0];
+ pWav->msadpcm.cachedSamples[1] = pWav->msadpcm.prevSamples[1][0];
+ pWav->msadpcm.cachedSamples[2] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.cachedSamples[3] = pWav->msadpcm.prevSamples[1][1];
+ pWav->msadpcm.cachedSampleCount = 4;
+ }
+ }
+
+ // Output anything that's cached.
+ while (samplesToRead > 0 && pWav->msadpcm.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+ pBufferOut[0] = (drwav_int16)pWav->msadpcm.cachedSamples[drwav_countof(pWav->msadpcm.cachedSamples) - pWav->msadpcm.cachedSampleCount];
+ pWav->msadpcm.cachedSampleCount -= 1;
+
+ pBufferOut += 1;
+ samplesToRead -= 1;
+ totalSamplesRead += 1;
+ pWav->compressed.iCurrentSample += 1;
+ }
+
+ if (samplesToRead == 0) {
+ return totalSamplesRead;
+ }
+
+
+ // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+ // loop iteration which will trigger the loading of a new block.
+ if (pWav->msadpcm.cachedSampleCount == 0) {
+ if (pWav->msadpcm.bytesRemainingInBlock == 0) {
+ continue;
+ } else {
+ drwav_uint8 nibbles;
+ if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
+ return totalSamplesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock -= 1;
+
+ // TODO: Optimize away these if statements.
+ drwav_int32 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
+ drwav_int32 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
+
+ static drwav_int32 adaptationTable[] = {
+ 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230
+ };
+ static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
+ static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
+
+ if (pWav->channels == 1) {
+ // Mono.
+ drwav_int32 newSample0;
+ newSample0 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample0 += nibble0 * pWav->msadpcm.delta[0];
+ newSample0 = drwav_clamp(newSample0, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.prevSamples[0][1] = newSample0;
+
+
+ drwav_int32 newSample1;
+ newSample1 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample1 += nibble1 * pWav->msadpcm.delta[0];
+ newSample1 = drwav_clamp(newSample1, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.prevSamples[0][1] = newSample1;
+
+
+ pWav->msadpcm.cachedSamples[2] = newSample0;
+ pWav->msadpcm.cachedSamples[3] = newSample1;
+ pWav->msadpcm.cachedSampleCount = 2;
+ } else {
+ // Stereo.
+
+ // Left.
+ drwav_int32 newSample0;
+ newSample0 = ((pWav->msadpcm.prevSamples[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevSamples[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample0 += nibble0 * pWav->msadpcm.delta[0];
+ newSample0 = drwav_clamp(newSample0, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevSamples[0][0] = pWav->msadpcm.prevSamples[0][1];
+ pWav->msadpcm.prevSamples[0][1] = newSample0;
+
+
+ // Right.
+ drwav_int32 newSample1;
+ newSample1 = ((pWav->msadpcm.prevSamples[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevSamples[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
+ newSample1 += nibble1 * pWav->msadpcm.delta[1];
+ newSample1 = drwav_clamp(newSample1, -32768, 32767);
+
+ pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
+ if (pWav->msadpcm.delta[1] < 16) {
+ pWav->msadpcm.delta[1] = 16;
+ }
+
+ pWav->msadpcm.prevSamples[1][0] = pWav->msadpcm.prevSamples[1][1];
+ pWav->msadpcm.prevSamples[1][1] = newSample1;
+
+ pWav->msadpcm.cachedSamples[2] = newSample0;
+ pWav->msadpcm.cachedSamples[3] = newSample1;
+ pWav->msadpcm.cachedSampleCount = 2;
+ }
+ }
+ }
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_assert(pWav != NULL);
+ drwav_assert(samplesToRead > 0);
+ drwav_assert(pBufferOut != NULL);
+
+ // TODO: Lots of room for optimization here.
+
+ drwav_uint64 totalSamplesRead = 0;
+
+ while (samplesToRead > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+ // If there are no cached samples we need to load a new block.
+ if (pWav->ima.cachedSampleCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
+ if (pWav->channels == 1) {
+ // Mono.
+ drwav_uint8 header[4];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalSamplesRead;
+ }
+ pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);
+ pWav->ima.stepIndex[0] = header[2];
+ pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[0];
+ pWav->ima.cachedSampleCount = 1;
+ } else {
+ // Stereo.
+ drwav_uint8 header[8];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalSamplesRead;
+ }
+ pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);
+ pWav->ima.stepIndex[0] = header[2];
+ pWav->ima.predictor[1] = drwav__bytes_to_s16(header + 4);
+ pWav->ima.stepIndex[1] = header[6];
+
+ pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 2] = pWav->ima.predictor[0];
+ pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - 1] = pWav->ima.predictor[1];
+ pWav->ima.cachedSampleCount = 2;
+ }
+ }
+
+ // Output anything that's cached.
+ while (samplesToRead > 0 && pWav->ima.cachedSampleCount > 0 && pWav->compressed.iCurrentSample < pWav->totalSampleCount) {
+ pBufferOut[0] = (drwav_int16)pWav->ima.cachedSamples[drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount];
+ pWav->ima.cachedSampleCount -= 1;
+
+ pBufferOut += 1;
+ samplesToRead -= 1;
+ totalSamplesRead += 1;
+ pWav->compressed.iCurrentSample += 1;
+ }
+
+ if (samplesToRead == 0) {
+ return totalSamplesRead;
+ }
+
+ // If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+ // loop iteration which will trigger the loading of a new block.
+ if (pWav->ima.cachedSampleCount == 0) {
+ if (pWav->ima.bytesRemainingInBlock == 0) {
+ continue;
+ } else {
+ static drwav_int32 indexTable[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+ static drwav_int32 stepTable[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
+ };
+
+ // From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the
+ // left channel, 4 bytes for the right channel.
+ pWav->ima.cachedSampleCount = 8 * pWav->channels;
+ for (drwav_uint32 iChannel = 0; iChannel < pWav->channels; ++iChannel) {
+ drwav_uint8 nibbles[4];
+ if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
+ return totalSamplesRead;
+ }
+ pWav->ima.bytesRemainingInBlock -= 4;
+
+ for (drwav_uint32 iByte = 0; iByte < 4; ++iByte) {
+ drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
+ drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
+
+ drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
+ drwav_int32 predictor = pWav->ima.predictor[iChannel];
+
+ drwav_int32 diff = step >> 3;
+ if (nibble0 & 1) diff += step >> 2;
+ if (nibble0 & 2) diff += step >> 1;
+ if (nibble0 & 4) diff += step;
+ if (nibble0 & 8) diff = -diff;
+
+ predictor = drwav_clamp(predictor + diff, -32768, 32767);
+ pWav->ima.predictor[iChannel] = predictor;
+ pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
+ pWav->ima.cachedSamples[(drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
+
+
+ step = stepTable[pWav->ima.stepIndex[iChannel]];
+ predictor = pWav->ima.predictor[iChannel];
+
+ diff = step >> 3;
+ if (nibble1 & 1) diff += step >> 2;
+ if (nibble1 & 2) diff += step >> 1;
+ if (nibble1 & 4) diff += step;
+ if (nibble1 & 8) diff = -diff;
+
+ predictor = drwav_clamp(predictor + diff, -32768, 32767);
+ pWav->ima.predictor[iChannel] = predictor;
+ pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
+ pWav->ima.cachedSamples[(drwav_countof(pWav->ima.cachedSamples) - pWav->ima.cachedSampleCount) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
+ }
+ }
+ }
+ }
+ }
+
+ return totalSamplesRead;
+}
+
+
+#ifndef DR_WAV_NO_CONVERSION_API
+static unsigned short g_drwavAlawTable[256] = {
+ 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
+ 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
+ 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
+ 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
+ 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
+ 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
+ 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
+ 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
+ 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
+ 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
+ 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
+ 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
+ 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
+ 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
+ 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
+ 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
+};
+
+static unsigned short g_drwavMulawTable[256] = {
+ 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
+ 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
+ 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
+ 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
+ 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
+ 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
+ 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
+ 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
+ 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
+ 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
+ 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
+ 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
+ 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
+ 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
+ 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
+ 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+};
+
+static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
+{
+ return (short)g_drwavAlawTable[sampleIn];
+}
+
+static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
+{
+ return (short)g_drwavMulawTable[sampleIn];
+}
+
+
+
+static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+ // Special case for 8-bit sample data because it's treated as unsigned.
+ if (bytesPerSample == 1) {
+ drwav_u8_to_s16(pOut, pIn, totalSampleCount);
+ return;
+ }
+
+
+ // Slightly more optimal implementation for common formats.
+ if (bytesPerSample == 2) {
+ for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ *pOut++ = ((const drwav_int16*)pIn)[i];
+ }
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_s16(pOut, pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
+ return;
+ }
+
+
+ // Anything more than 64 bits per sample is not supported.
+ if (bytesPerSample > 8) {
+ drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ // Generic, slow converter.
+ for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
+ }
+}
+
+static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
+ return;
+ } else {
+ // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+drwav_uint64 drwav_read_s16__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ // Fast path.
+ if (pWav->bytesPerSample == 2) {
+ return drwav_read(pWav, samplesToRead, pBufferOut);
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut)
+{
+ if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ // Don't try to read more samples than can potentially fit in the output buffer.
+ if (samplesToRead * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_s16__pcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_s16__msadpcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_s16__ieee(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_s16__alaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_s16__mulaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_s16__ima(pWav, samplesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ int x = pIn[i];
+ r = x - 128;
+ r = r << 8;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ int x = ((int)(((unsigned int)(((const unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const unsigned char*)pIn)[i*3+2])) << 24)) >> 8;
+ r = x >> 8;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ int x = pIn[i];
+ r = x >> 16;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ float x = pIn[i];
+ float c;
+ c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
+ c = c + 1;
+ r = (int)(c * 32767.5f);
+ r = r - 32768;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
+{
+ int r;
+ for (size_t i = 0; i < sampleCount; ++i) {
+ double x = pIn[i];
+ double c;
+ c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
+ c = c + 1;
+ r = (int)(c * 32767.5);
+ r = r - 32768;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ for (size_t i = 0; i < sampleCount; ++i) {
+ pOut[i] = drwav__alaw_to_s16(pIn[i]);
+ }
+}
+
+void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ for (size_t i = 0; i < sampleCount; ++i) {
+ pOut[i] = drwav__mulaw_to_s16(pIn[i]);
+ }
+}
+
+
+
+static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample)
+{
+ // Special case for 8-bit sample data because it's treated as unsigned.
+ if (bytesPerSample == 1) {
+ drwav_u8_to_f32(pOut, pIn, sampleCount);
+ return;
+ }
+
+ // Slightly more optimal implementation for common formats.
+ if (bytesPerSample == 2) {
+ drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_f32(pOut, pIn, sampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
+ return;
+ }
+
+
+ // Anything more than 64 bits per sample is not supported.
+ if (bytesPerSample > 8) {
+ drwav_zero_memory(pOut, sampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ // Generic, slow converter.
+ for (unsigned int i = 0; i < sampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
+ }
+}
+
+static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ for (unsigned int i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((const float*)pIn)[i];
+ }
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
+ return;
+ } else {
+ // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ drwav_zero_memory(pOut, sampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+
+drwav_uint64 drwav_read_f32__pcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+ pBufferOut += samplesRead;
+
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+ // want to duplicate that code.
+ drwav_uint64 totalSamplesRead = 0;
+ drwav_int16 samples16[2048];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__ima(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+ // want to duplicate that code.
+ drwav_uint64 totalSamplesRead = 0;
+ drwav_int16 samples16[2048];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_f32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__ieee(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ // Fast path.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bytesPerSample == 4) {
+ return drwav_read(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut)
+{
+ if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ // Don't try to read more samples than can potentially fit in the output buffer.
+ if (samplesToRead * sizeof(float) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(float);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_f32__pcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_f32__msadpcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_f32__ieee(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_f32__alaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_f32__mulaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_f32__ima(pWav, samplesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+#ifdef DR_WAV_LIBSNDFILE_COMPAT
+ // It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
+ // libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
+ // the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated
+ // correctness testing. This is disabled by default.
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
+ }
+#else
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (pIn[i] / 255.0f) * 2 - 1;
+ }
+#endif
+}
+
+void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = pIn[i] / 32768.0f;
+ }
+}
+
+void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ unsigned int s0 = pIn[i*3 + 0];
+ unsigned int s1 = pIn[i*3 + 1];
+ unsigned int s2 = pIn[i*3 + 2];
+
+ int sample32 = (int)((s0 << 8) | (s1 << 16) | (s2 << 24));
+ *pOut++ = (float)(sample32 / 2147483648.0);
+ }
+}
+
+void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (float)(pIn[i] / 2147483648.0);
+ }
+}
+
+void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (float)pIn[i];
+ }
+}
+
+void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
+ }
+}
+
+void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
+ }
+}
+
+
+
+static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+ // Special case for 8-bit sample data because it's treated as unsigned.
+ if (bytesPerSample == 1) {
+ drwav_u8_to_s32(pOut, pIn, totalSampleCount);
+ return;
+ }
+
+ // Slightly more optimal implementation for common formats.
+ if (bytesPerSample == 2) {
+ drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_s32(pOut, pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ *pOut++ = ((const drwav_int32*)pIn)[i];
+ }
+ return;
+ }
+
+
+ // Anything more than 64 bits per sample is not supported.
+ if (bytesPerSample > 8) {
+ drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ // Generic, slow converter.
+ for (unsigned int i = 0; i < totalSampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
+ }
+}
+
+static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
+ return;
+ } else {
+ // Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float.
+ drwav_zero_memory(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+
+drwav_uint64 drwav_read_s32__pcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ // Fast path.
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bytesPerSample == 4) {
+ return drwav_read(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ // We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+ // want to duplicate that code.
+ drwav_uint64 totalSamplesRead = 0;
+ drwav_int16 samples16[2048];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ // We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+ // want to duplicate that code.
+ drwav_uint64 totalSamplesRead = 0;
+ drwav_int16 samples16[2048];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, drwav_min(samplesToRead, 2048), samples16);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_s32(pBufferOut, samples16, (size_t)samplesRead); // <-- Safe cast because we're clamping to 2048.
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32__mulaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav->bytesPerSample == 0) {
+ return 0;
+ }
+
+ drwav_uint64 totalSamplesRead = 0;
+ unsigned char sampleData[4096];
+ while (samplesToRead > 0) {
+ drwav_uint64 samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData);
+ if (samplesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
+
+ pBufferOut += samplesRead;
+ samplesToRead -= samplesRead;
+ totalSamplesRead += samplesRead;
+ }
+
+ return totalSamplesRead;
+}
+
+drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ // Don't try to read more samples than can potentially fit in the output buffer.
+ if (samplesToRead * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
+ samplesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32);
+ }
+
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_s32__pcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_s32__msadpcm(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_s32__ieee(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_s32__alaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_s32__mulaw(pWav, samplesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_s32__ima(pWav, samplesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((int)pIn[i] - 128) << 24;
+ }
+}
+
+void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = pIn[i] << 16;
+ }
+}
+
+void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ unsigned int s0 = pIn[i*3 + 0];
+ unsigned int s1 = pIn[i*3 + 1];
+ unsigned int s2 = pIn[i*3 + 2];
+
+ drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
+ *pOut++ = sample32;
+ }
+}
+
+void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+ }
+}
+
+void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+ }
+}
+
+void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
+ }
+}
+
+void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (size_t i= 0; i < sampleCount; ++i) {
+ *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
+ }
+}
+
+
+
+drwav_int16* drwav__read_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ drwav_assert(pWav != NULL);
+
+ drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int16);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; // File's too big.
+ }
+
+ drwav_int16* pSampleData = (drwav_int16*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; // Failed to allocate memory.
+ }
+
+ drwav_uint64 samplesRead = drwav_read_s16(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ if (samplesRead != pWav->totalSampleCount) {
+ DRWAV_FREE(pSampleData);
+ drwav_uninit(pWav);
+ return NULL; // There was an error reading the samples.
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) *sampleRate = pWav->sampleRate;
+ if (channels) *channels = pWav->channels;
+ if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ return pSampleData;
+}
+
+float* drwav__read_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ drwav_assert(pWav != NULL);
+
+ drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(float);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; // File's too big.
+ }
+
+ float* pSampleData = (float*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; // Failed to allocate memory.
+ }
+
+ drwav_uint64 samplesRead = drwav_read_f32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ if (samplesRead != pWav->totalSampleCount) {
+ DRWAV_FREE(pSampleData);
+ drwav_uninit(pWav);
+ return NULL; // There was an error reading the samples.
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) *sampleRate = pWav->sampleRate;
+ if (channels) *channels = pWav->channels;
+ if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ return pSampleData;
+}
+
+drwav_int32* drwav__read_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ drwav_assert(pWav != NULL);
+
+ drwav_uint64 sampleDataSize = pWav->totalSampleCount * sizeof(drwav_int32);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; // File's too big.
+ }
+
+ drwav_int32* pSampleData = (drwav_int32*)DRWAV_MALLOC((size_t)sampleDataSize); // <-- Safe cast due to the check above.
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; // Failed to allocate memory.
+ }
+
+ drwav_uint64 samplesRead = drwav_read_s32(pWav, (size_t)pWav->totalSampleCount, pSampleData);
+ if (samplesRead != pWav->totalSampleCount) {
+ DRWAV_FREE(pSampleData);
+ drwav_uninit(pWav);
+ return NULL; // There was an error reading the samples.
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) *sampleRate = pWav->sampleRate;
+ if (channels) *channels = pWav->channels;
+ if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount;
+ return pSampleData;
+}
+
+
+drwav_int16* drwav_open_and_read_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init(&wav, onRead, onSeek, pUserData)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+#ifndef DR_WAV_NO_STDIO
+drwav_int16* drwav_open_and_read_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_file(&wav, filename)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_file(&wav, filename)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_file(&wav, filename)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+#endif
+
+drwav_int16* drwav_open_and_read_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_memory(&wav, data, dataSize)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s16(&wav, channels, sampleRate, totalSampleCount);
+}
+
+float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_memory(&wav, data, dataSize)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount);
+}
+
+drwav_int32* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount)
+{
+ if (sampleRate) *sampleRate = 0;
+ if (channels) *channels = 0;
+ if (totalSampleCount) *totalSampleCount = 0;
+
+ drwav wav;
+ if (!drwav_init_memory(&wav, data, dataSize)) {
+ return NULL;
+ }
+
+ return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount);
+}
+#endif //DR_WAV_NO_CONVERSION_API
+
+
+void drwav_free(void* pDataReturnedByOpenAndRead)
+{
+ DRWAV_FREE(pDataReturnedByOpenAndRead);
+}
+
+#endif //DR_WAV_IMPLEMENTATION
+
+
+// REVISION HISTORY
+//
+// v0.8.5 - 2018-09-11
+// - Const correctness.
+// - Fix a potential stack overflow.
+//
+// v0.8.4 - 2018-08-07
+// - Improve 64-bit detection.
+//
+// v0.8.3 - 2018-08-05
+// - Fix C++ build on older versions of GCC.
+//
+// v0.8.2 - 2018-08-02
+// - Fix some big-endian bugs.
+//
+// v0.8.1 - 2018-06-29
+// - Add support for sequential writing APIs.
+// - Disable seeking in write mode.
+// - Fix bugs with Wave64.
+// - Fix typos.
+//
+// v0.8 - 2018-04-27
+// - Bug fix.
+// - Start using major.minor.revision versioning.
+//
+// v0.7f - 2018-02-05
+// - Restrict ADPCM formats to a maximum of 2 channels.
+//
+// v0.7e - 2018-02-02
+// - Fix a crash.
+//
+// v0.7d - 2018-02-01
+// - Fix a crash.
+//
+// v0.7c - 2018-02-01
+// - Set drwav.bytesPerSample to 0 for all compressed formats.
+// - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for
+// all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).
+// - Fix some divide-by-zero errors.
+//
+// v0.7b - 2018-01-22
+// - Fix errors with seeking of compressed formats.
+// - Fix compilation error when DR_WAV_NO_CONVERSION_API
+//
+// v0.7a - 2017-11-17
+// - Fix some GCC warnings.
+//
+// v0.7 - 2017-11-04
+// - Add writing APIs.
+//
+// v0.6 - 2017-08-16
+// - API CHANGE: Rename dr_* types to drwav_*.
+// - Add support for custom implementations of malloc(), realloc(), etc.
+// - Add support for Microsoft ADPCM.
+// - Add support for IMA ADPCM (DVI, format code 0x11).
+// - Optimizations to drwav_read_s16().
+// - Bug fixes.
+//
+// v0.5g - 2017-07-16
+// - Change underlying type for booleans to unsigned.
+//
+// v0.5f - 2017-04-04
+// - Fix a minor bug with drwav_open_and_read_s16() and family.
+//
+// v0.5e - 2016-12-29
+// - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
+// - Minor fixes to documentation.
+//
+// v0.5d - 2016-12-28
+// - Use drwav_int*/drwav_uint* sized types to improve compiler support.
+//
+// v0.5c - 2016-11-11
+// - Properly handle JUNK chunks that come before the FMT chunk.
+//
+// v0.5b - 2016-10-23
+// - A minor change to drwav_bool8 and drwav_bool32 types.
+//
+// v0.5a - 2016-10-11
+// - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
+// - Improve A-law and mu-law efficiency.
+//
+// v0.5 - 2016-09-29
+// - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
+// keep it consistent with dr_audio and dr_flac.
+//
+// v0.4b - 2016-09-18
+// - Fixed a typo in documentation.
+//
+// v0.4a - 2016-09-18
+// - Fixed a typo.
+// - Change date format to ISO 8601 (YYYY-MM-DD)
+//
+// v0.4 - 2016-07-13
+// - API CHANGE. Make onSeek consistent with dr_flac.
+// - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.
+// - Added support for Sony Wave64.
+//
+// v0.3a - 2016-05-28
+// - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
+// - Fixed a memory leak.
+//
+// v0.3 - 2016-05-22
+// - Lots of API changes for consistency.
+//
+// v0.2a - 2016-05-16
+// - Fixed Linux/GCC build.
+//
+// v0.2 - 2016-05-11
+// - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.
+//
+// v0.1a - 2016-05-07
+// - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
+//
+// v0.1 - 2016-05-04
+// - Initial versioned release.
+
+
+/*
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+*/
diff --git a/include/kfr/io/file.hpp b/include/kfr/io/file.hpp
@@ -34,6 +34,183 @@
namespace kfr
{
+#ifdef CMT_OS_WIN
+using filepath_char = wchar_t;
+#define KFR_FILEPATH_PREFIX_CONCAT(x, y) x##y
+#define KFR_FILEPATH(s) KFR_FILEPATH_PREFIX_CONCAT(L, s)
+#else
+using filepath_char = char;
+#define KFR_FILEPATH(s) s
+#endif
+
+using filepath = std::basic_string<filepath_char>;
+
+#if defined _MSC_VER // MSVC
+#define IO_SEEK_64 _fseeki64
+#define IO_TELL_64 _ftelli64
+#elif defined _WIN32 // MingW
+#define IO_SEEK_64 fseeko64
+#define IO_TELL_64 ftello64
+#else // macOS, Linux
+#define IO_SEEK_64 fseeko
+#define IO_TELL_64 ftello
+#endif
+
+inline FILE* fopen_portable(const filepath_char* path, const filepath_char* mode)
+{
+#ifdef CMT_OS_WIN
+ FILE* f = nullptr;
+ errno_t e = _wfopen_s(&f, path, mode);
+ return f;
+#else
+ return fopen(path, mode);
+#endif
+}
+
+template <typename T = void>
+constexpr inline size_t element_size()
+{
+ return sizeof(T);
+}
+template <>
+constexpr inline size_t element_size<void>()
+{
+ return 1;
+}
+
+enum class seek_origin : int
+{
+ current = SEEK_CUR,
+ begin = SEEK_SET,
+ end = SEEK_END,
+};
+
+template <typename T = void>
+struct abstract_stream
+{
+ virtual ~abstract_stream() {}
+ virtual imax tell() const = 0;
+ virtual bool seek(imax offset, seek_origin origin) = 0;
+ bool seek(imax offset, int origin) { return seek(offset, static_cast<seek_origin>(origin)); }
+};
+
+template <typename T = void>
+struct abstract_reader : abstract_stream<T>
+{
+ virtual size_t read(T* data, size_t size) = 0;
+};
+
+template <typename T = void>
+struct abstract_writer : abstract_stream<T>
+{
+ virtual size_t write(const T* data, size_t size) = 0;
+};
+
+template <typename From, typename To = void>
+struct reader_adapter : abstract_reader<To>
+{
+ static_assert(element_size<From>() % element_size<To>() == 0 ||
+ element_size<To>() % element_size<From>() == 0,
+ "From and To sizes must be compatible");
+ reader_adapter(std::shared_ptr<abstract_reader<From>>&& reader) : reader(std::move(reader)) {}
+ virtual size_t read(To* data, size_t size) final
+ {
+ return reader->read(reinterpret_cast<From*>(data), size * element_size<From>() / element_size<To>()) *
+ element_size<To>() / element_size<From>();
+ }
+ std::shared_ptr<abstract_reader<From>> reader;
+};
+
+template <typename From, typename To = void>
+struct writer_adapter : abstract_writer<To>
+{
+ static_assert(element_size<From>() % element_size<To>() == 0 ||
+ element_size<To>() % element_size<From>() == 0,
+ "From and To sizes must be compatible");
+ writer_adapter(std::shared_ptr<abstract_writer<From>>&& writer) : writer(std::move(writer)) {}
+ virtual size_t write(const To* data, size_t size) final
+ {
+ return writer->write(reinterpret_cast<const From*>(data),
+ size * element_size<From>() / element_size<To>()) *
+ element_size<To>() / element_size<From>();
+ }
+ std::shared_ptr<abstract_writer<From>> writer;
+};
+
+using binary_reader = abstract_reader<>;
+using binary_writer = abstract_writer<>;
+using byte_reader = abstract_reader<u8>;
+using byte_writer = abstract_writer<u8>;
+using f32_reader = abstract_reader<f32>;
+using f32_writer = abstract_writer<f32>;
+
+struct file_handle
+{
+ file_handle(FILE* file) : file(file) {}
+ file_handle() = delete;
+ file_handle(const file_handle&) = delete;
+ file_handle(file_handle&& handle) : file(nullptr) { swap(handle); }
+ ~file_handle()
+ {
+ if (file)
+ {
+ fclose(file);
+ }
+ }
+ FILE* file;
+ void swap(file_handle& handle) { std::swap(file, handle.file); }
+};
+
+template <typename T = void>
+struct file_reader : abstract_reader<T>
+{
+ file_reader(file_handle&& handle) : handle(std::move(handle)) {}
+ ~file_reader() override {}
+ size_t read(T* data, size_t size) final { return fread(data, element_size<T>(), size, handle.file); }
+
+ imax tell() const final { return IO_TELL_64(handle.file); }
+ bool seek(imax offset, seek_origin origin) final
+ {
+ return !IO_SEEK_64(handle.file, offset, static_cast<int>(origin));
+ }
+ file_handle handle;
+};
+
+template <typename T = void>
+struct file_writer : abstract_writer<T>
+{
+ file_writer(file_handle&& handle) : handle(std::move(handle)) {}
+ ~file_writer() override {}
+ size_t write(const T* data, size_t size) final
+ {
+ return fwrite(data, element_size<T>(), size, handle.file);
+ }
+ imax tell() const final { return IO_TELL_64(handle.file); }
+ bool seek(imax offset, seek_origin origin) final
+ {
+ return !IO_SEEK_64(handle.file, offset, static_cast<int>(origin));
+ }
+ file_handle handle;
+};
+
+template <typename T = void>
+inline std::shared_ptr<file_reader<T>> open_file_for_reading(const filepath& path)
+{
+ return std::make_shared<file_reader<T>>(fopen_portable(path.c_str(), KFR_FILEPATH("rb")));
+}
+
+template <typename T = void>
+inline std::shared_ptr<file_writer<T>> open_file_for_writing(const filepath& path)
+{
+ return std::make_shared<file_writer<T>>(fopen_portable(path.c_str(), KFR_FILEPATH("wb")));
+}
+
+template <typename T = void>
+inline std::shared_ptr<file_writer<T>> open_file_for_appending(const filepath& path)
+{
+ return std::make_shared<file_writer<T>>(fopen_portable(path.c_str(), KFR_FILEPATH("ab")));
+}
+
namespace internal
{
struct expression_file_base
@@ -58,7 +235,12 @@ struct expression_sequential_file_writer : expression_file_base, output_expressi
template <typename U>
void write(const U& value)
{
- fwrite(std::addressof(value), 1, sizeof(U), file);
+ write(&value, 1);
+ }
+ template <typename U>
+ void write(const U* value, size_t size)
+ {
+ fwrite(value, 1, sizeof(U) * size, file);
}
};
@@ -111,31 +293,6 @@ struct expression_file_reader : expression_file_base, input_expression
}
mutable size_t position = 0;
};
-}
-
-/// @brief Creates an expression that returns values from the given file (sequential access)
-inline internal::expression_sequential_file_reader sequential_file_reader(const std::string& file_name)
-{
- return internal::expression_sequential_file_reader(fopen(file_name.c_str(), "rb"));
-}
+} // namespace internal
-/// @brief Creates an output expression that writes values to the given file (sequential access)
-inline internal::expression_sequential_file_writer sequential_file_writer(const std::string& file_name)
-{
- return internal::expression_sequential_file_writer(fopen(file_name.c_str(), "wb"));
-}
-
-/// @brief Creates an expression that returns values from the given file (random access)
-template <typename T = u8>
-internal::expression_file_reader<T> file_reader(const std::string& file_name)
-{
- return internal::expression_file_reader<T>(fopen(file_name.c_str(), "rb"));
-}
-
-/// @brief Creates an output expression that writes values to the given file (random access)
-template <typename T = u8>
-internal::expression_file_writer<T> file_writer(const std::string& file_name)
-{
- return internal::expression_file_writer<T>(fopen(file_name.c_str(), "wb"));
-}
-}
+} // namespace kfr
diff --git a/include/kfr/io/impl/audiofile-impl.cpp b/include/kfr/io/impl/audiofile-impl.cpp
@@ -0,0 +1,39 @@
+/** @addtogroup io
+ * @{
+ */
+/*
+ Copyright (C) 2016 D Levin (https://www.kfrlib.com)
+ This file is part of KFR
+
+ KFR is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ KFR is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with KFR.
+
+ If GPL is not suitable for your project, you must purchase a commercial license to use KFR.
+ Buying a commercial license is mandatory as soon as you develop commercial activities without
+ disclosing the source code of your own applications.
+ See https://www.kfrlib.com for details.
+ */
+
+#include "../audiofile.hpp"
+
+#if defined(KFR_ENABLE_WAV) && KFR_ENABLE_WAV
+#define DR_WAV_NO_STDIO
+#define DR_WAV_NO_CONVERSION_API
+#define DR_WAV_IMPLEMENTATION
+#include "../dr/dr_wav.h"
+#endif
+#if defined(KFR_ENABLE_FLAC) && KFR_ENABLE_FLAC
+#define DR_FLAC_IMPLEMENTATION
+#define DR_FLAC_NO_STDIO
+#include "../dr/dr_flac.h"
+#endif
diff --git a/sources.cmake b/sources.cmake
@@ -126,6 +126,8 @@ set(
${PROJECT_SOURCE_DIR}/include/kfr/io/file.hpp
${PROJECT_SOURCE_DIR}/include/kfr/io/python_plot.hpp
${PROJECT_SOURCE_DIR}/include/kfr/io/tostring.hpp
+ ${PROJECT_SOURCE_DIR}/include/kfr/io/dr/dr_flac.h
+ ${PROJECT_SOURCE_DIR}/include/kfr/io/dr/dr_wav.h
${PROJECT_SOURCE_DIR}/include/kfr/testo/assert.hpp
${PROJECT_SOURCE_DIR}/include/kfr/testo/comparison.hpp
${PROJECT_SOURCE_DIR}/include/kfr/testo/testo.hpp
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
@@ -44,7 +44,7 @@ set(ALL_TESTS_CPP
dft_test.cpp
dsp_test.cpp
expression_test.cpp
- intrinsic_test.cpp)
+ intrinsic_test.cpp io_test.cpp)
if (MPFR_FOUND AND GMP_FOUND)
list(APPEND ALL_TESTS_CPP transcendental_test.cpp)
@@ -55,6 +55,7 @@ endif ()
add_executable(all_tests ${ALL_TESTS_CPP})
target_compile_definitions(all_tests PRIVATE KFR_NO_MAIN)
target_link_libraries(all_tests kfr kfr_dft)
+target_link_libraries(all_tests kfr kfr_dft kfr_io)
add_executable(intrinsic_test intrinsic_test.cpp)
target_link_libraries(intrinsic_test kfr)
@@ -72,10 +73,11 @@ endif ()
function(add_x86_test NAME FLAGS)
separate_arguments(FLAGS)
- add_executable(all_tests_${NAME} ${ALL_TESTS_CPP} ${KFR_DFT_SRC})
+ add_executable(all_tests_${NAME} ${ALL_TESTS_CPP} ${KFR_DFT_SRC} ${KFR_IO_SRC})
target_compile_options(all_tests_${NAME} PRIVATE ${FLAGS})
target_compile_definitions(all_tests_${NAME} PRIVATE KFR_NO_MAIN)
target_link_libraries(all_tests_${NAME} kfr)
+ target_compile_definitions(all_tests_${NAME} PUBLIC KFR_ENABLE_FLAC=1)
if (MPFR_FOUND AND GMP_FOUND)
target_link_libraries(all_tests_${NAME} ${MPFR_LIBRARIES} ${GMP_LIBRARIES})
endif ()
@@ -111,6 +113,10 @@ target_link_libraries(expression_test kfr)
add_executable(ebu_test ebu_test.cpp)
target_link_libraries(ebu_test kfr)
+add_executable(io_test io_test.cpp)
+target_link_libraries(io_test kfr kfr_io)
+target_compile_definitions(io_test PUBLIC KFR_ENABLE_FLAC=1)
+
if(USE_SDE)
find_program(EMULATOR "sde")
list(APPEND EMULATOR "-skx")
@@ -138,6 +144,8 @@ if (NOT IOS)
COMMAND ${EMULATOR} ${PROJECT_BINARY_DIR}/bin/complex_test)
add_test(NAME expression_test
COMMAND ${EMULATOR} ${PROJECT_BINARY_DIR}/bin/expression_test)
+ add_test(NAME io_test
+ COMMAND ${EMULATOR} ${PROJECT_BINARY_DIR}/bin/io_test)
if (NOT ARM)
add_test(NAME multiarch
diff --git a/tests/base_test.cpp b/tests/base_test.cpp
@@ -425,9 +425,9 @@ TEST(sample_interleave_deinterleave)
deinterleave((float*[]){ in[0].data(), in[1].data(), in[2].data() }, out.data(), 3, size);
- CHECK(maxof(in[0] - render(counter() * 3.f + 0.f, size)) == 0);
- CHECK(maxof(in[1] - render(counter() * 3.f + 1.f, size)) == 0);
- CHECK(maxof(in[2] - render(counter() * 3.f + 2.f, size)) == 0);
+ CHECK(absmaxof(in[0] - render(counter() * 3.f + 0.f, size)) == 0);
+ CHECK(absmaxof(in[1] - render(counter() * 3.f + 1.f, size)) == 0);
+ CHECK(absmaxof(in[2] - render(counter() * 3.f + 2.f, size)) == 0);
}
#ifndef KFR_NO_MAIN
diff --git a/tests/io_test.cpp b/tests/io_test.cpp
@@ -0,0 +1,64 @@
+/**
+ * KFR (http://kfrlib.com)
+ * Copyright (C) 2016 D Levin
+ * See LICENSE.txt for details
+ */
+
+#include <kfr/testo/testo.hpp>
+
+#include <kfr/base.hpp>
+#include <kfr/cometa/function.hpp>
+#include <kfr/dsp.hpp>
+#include <kfr/io.hpp>
+
+using namespace kfr;
+
+#if KFR_ENABLE_WAV
+TEST(write_wav_file)
+{
+ audio_writer_wav<float> writer(open_file_for_writing(KFR_FILEPATH("temp_audio_file.wav")),
+ audio_format{});
+ univector<float> data(44100 * 2);
+ data = sin(counter() * 0.01f);
+ size_t wr = writer.write(data.data(), data.size());
+ CHECK(wr == data.size());
+ CHECK(writer.format().length == data.size() / 2);
+}
+
+TEST(read_wav_file)
+{
+ audio_reader_wav<float> reader(open_file_for_reading(KFR_FILEPATH("temp_audio_file.wav")));
+ CHECK(reader.format().channels == 2);
+ CHECK(reader.format().type == audio_sample_type::i16);
+ CHECK(reader.format().samplerate == 44100);
+ univector<float> data(44100 * 2);
+ CHECK(reader.format().length == data.size() / 2);
+ size_t rd = reader.read(data.data(), data.size());
+ CHECK(rd == data.size());
+ CHECK(absmaxof(data - render(sin(counter() * 0.01f), data.size())) < 0.0001f);
+}
+#endif
+
+#if KFR_ENABLE_FLAC
+TEST(read_flac_file)
+{
+ audio_reader_flac<float> reader(open_file_for_reading(KFR_FILEPATH("../../tests/test-audio/sine.flac")));
+ CHECK(reader.format().channels == 2);
+ CHECK(reader.format().type == audio_sample_type::i32);
+ CHECK(reader.format().samplerate == 44100);
+ univector<float> data(44100 * 2);
+ CHECK(reader.format().length == data.size() / 2);
+ size_t rd = reader.read(data.data(), data.size());
+ CHECK(rd == data.size());
+ CHECK(absmaxof(data - render(sin(counter() * 0.01f), data.size())) < 0.0001f);
+}
+#endif
+
+#ifndef KFR_NO_MAIN
+int main()
+{
+ println(library_version());
+
+ return testo::run_all("", true);
+}
+#endif
diff --git a/tests/test-audio/sine.flac b/tests/test-audio/sine.flac
Binary files differ.