diff --git a/.gitmodules b/.gitmodules index 84a5d4d..ed443e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,18 +1,6 @@ -[submodule "modules/entt"] - path = modules/entt - url = git@cjhoward.org:entt.git -[submodule "modules/vmq"] - path = modules/vmq - url = git@cjhoward.org:vmq.git [submodule "modules/antkeeper-source"] path = modules/antkeeper-source - url = git@cjhoward.org:antkeeper-source.git + url = https://github.com/antkeeper/antkeeper-source.git [submodule "modules/antkeeper-data"] path = modules/antkeeper-data - url = git@cjhoward.org:antkeeper-data.git -[submodule "modules/SDL2"] - path = modules/SDL2 - url = git@cjhoward.org:SDL2.git -[submodule "modules/openal-soft"] - path = modules/openal-soft - url = git@cjhoward.org:openal-soft.git + url = https://github.com/antkeeper/antkeeper-data.gits \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c2daa5..45bdfda 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,6 @@ cmake_minimum_required(VERSION 3.7) # Get software package name and version include(${CMAKE_SOURCE_DIR}/modules/antkeeper-source/cmake/project.cmake) - # Setup package variables set(PACKAGE_NAME ${PROJECT_NAME}) set(PACKAGE_VERSION ${PROJECT_VERSION}) @@ -96,9 +95,49 @@ ExternalProject_Add(entt "-DCMAKE_INSTALL_PREFIX=${MODULE_INSTALL_DIR}" BUILD_ALWAYS 0) +# Build nlohmann-json module +ExternalProject_Add(nlohmann-json + SOURCE_DIR ${MODULE_SOURCE_DIR}/nlohmann + BINARY_DIR ${MODULE_BUILD_DIR}/nlohmann + INSTALL_DIR ${MODULE_INSTALL_DIR} + CMAKE_ARGS + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + "-DCMAKE_INSTALL_PREFIX=${MODULE_INSTALL_DIR}" + BUILD_ALWAYS 0) + +# Build dr_wav module +ExternalProject_Add(dr_wav + SOURCE_DIR ${MODULE_SOURCE_DIR}/dr_wav + BINARY_DIR ${MODULE_BUILD_DIR}/dr_wav + INSTALL_DIR ${MODULE_INSTALL_DIR} + CMAKE_ARGS + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + "-DCMAKE_INSTALL_PREFIX=${MODULE_INSTALL_DIR}" + BUILD_ALWAYS 0) + +# Build stb module +ExternalProject_Add(stb + SOURCE_DIR ${MODULE_SOURCE_DIR}/stb + BINARY_DIR ${MODULE_BUILD_DIR}/stb + INSTALL_DIR ${MODULE_INSTALL_DIR} + CMAKE_ARGS + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + "-DCMAKE_INSTALL_PREFIX=${MODULE_INSTALL_DIR}" + BUILD_ALWAYS 0) + +# Build glad module +ExternalProject_Add(glad + SOURCE_DIR ${MODULE_SOURCE_DIR}/glad + BINARY_DIR ${MODULE_BUILD_DIR}/glad + INSTALL_DIR ${MODULE_INSTALL_DIR} + CMAKE_ARGS + "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}" + "-DCMAKE_INSTALL_PREFIX=${MODULE_INSTALL_DIR}" + BUILD_ALWAYS 0) + # Build antkeeper-source module ExternalProject_Add(antkeeper-source - DEPENDS openal-soft vmq entt + DEPENDS SDL2 openal-soft vmq entt nlohmann-json dr_wav stb glad SOURCE_DIR ${MODULE_SOURCE_DIR}/antkeeper-source BINARY_DIR ${MODULE_BUILD_DIR}/antkeeper-source INSTALL_DIR ${PACKAGE_INSTALL_DIR} diff --git a/modules/.gitignore b/modules/.gitignore new file mode 100644 index 0000000..af71101 --- /dev/null +++ b/modules/.gitignore @@ -0,0 +1 @@ +vmq \ No newline at end of file diff --git a/modules/antkeeper-source b/modules/antkeeper-source index c9779bf..5a2d886 160000 --- a/modules/antkeeper-source +++ b/modules/antkeeper-source @@ -1 +1 @@ -Subproject commit c9779bf7663b0732163f18fb4afd2d9fb143ad1e +Subproject commit 5a2d8861130f27d4a5cc87ed1c702d8bc170566a diff --git a/modules/dr_wav/CMakeLists.txt b/modules/dr_wav/CMakeLists.txt new file mode 100644 index 0000000..5bae3ca --- /dev/null +++ b/modules/dr_wav/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.7) + +project(dr_wav) + +add_library(dr_wav ${PROJECT_SOURCE_DIR}/dr_wav.cpp) + +# Install library +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + +# Install header +install( + FILES ${PROJECT_SOURCE_DIR}/dr_wav.h + DESTINATION include/dr_libs) + +# Install CMake config file +install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}-targets.cmake + DESTINATION lib/cmake/${PROJECT_NAME}) + +include(CMakePackageConfigHelpers) +configure_package_config_file( + ${PROJECT_SOURCE_DIR}/${PROJECT_NAME}-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}) + +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake + DESTINATION lib/cmake/${PROJECT_NAME}) diff --git a/modules/dr_wav/dr_wav-config.cmake.in b/modules/dr_wav/dr_wav-config.cmake.in new file mode 100644 index 0000000..6faa122 --- /dev/null +++ b/modules/dr_wav/dr_wav-config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +include("${CMAKE_CURRENT_LIST_DIR}/dr_wav-targets.cmake") diff --git a/modules/dr_wav/dr_wav.cpp b/modules/dr_wav/dr_wav.cpp new file mode 100644 index 0000000..8f4dcd8 --- /dev/null +++ b/modules/dr_wav/dr_wav.cpp @@ -0,0 +1,2 @@ +#define DR_WAV_IMPLEMENTATION +#include "dr_wav.h" diff --git a/modules/dr_wav/dr_wav.h b/modules/dr_wav/dr_wav.h new file mode 100644 index 0000000..ac4f6b5 --- /dev/null +++ b/modules/dr_wav/dr_wav.h @@ -0,0 +1,4377 @@ +// WAV audio loader and writer. Public domain. See "unlicense" statement at the end of this file. +// dr_wav - v0.9.0 - 2018-12-16 +// +// David Reid - mackron@gmail.com + +/* +DEPRECATED APIS +=============== +Version 0.9.0 deprecated the per-sample reading and seeking APIs and replaced them with versions that work on the resolution +of a PCM frame instead. For example, given a stereo WAV file, previously you would pass 2 to drwav_read_f32() to read one +PCM frame, whereas now you would pass in 1 to drwav_read_pcm_frames_f32(). The old APIs would return the number of samples +read, whereas now it will return the number of PCM frames. Below is a list of APIs that have been deprecated and their +replacements. + + drwav_read() -> drwav_read_pcm_frames() + drwav_read_s16() -> drwav_read_pcm_frames_s16() + drwav_read_f32() -> drwav_read_pcm_frames_f32() + drwav_read_s32() -> drwav_read_pcm_frames_s32() + drwav_seek_to_sample() -> drwav_seek_to_pcm_frame() + drwav_write() -> drwav_write_pcm_frames() + drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16() + drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32() + drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32() + drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16() + drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32() + drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32() + drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16() + drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32() + drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32() + drwav::totalSampleCount -> drwav::totalPCMFrameCount + +Rationale: + 1) Most programs will want to read in multiples of the channel count which demands a per-frame reading API. Per-sample + reading just adds complexity and maintenance costs for no practical benefit. + 2) This is consistent with my other decoders - dr_flac and dr_mp3. + +These APIs will be removed completely in version 0.10.0. You can continue to use drwav_read_raw() if you need per-sample +reading. +*/ + +// 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.totalPCMFrameCount * wav.channels * sizeof(drwav_int32)); +// size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, 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 totalPCMFrameCount; +// float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount); +// 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_pcm_frames(&wav, wav.totalPCMFrameCount, 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 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_pcm_frames() 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_pcm_frames(pWav, frameCount, pSamples); +// +// +// dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work +// without any manual intervention. +// +// +// OPTIONS +// #define these options before including this file. +// +// #define DR_WAV_NO_CONVERSION_API +// Disables conversion APIs such as drwav_read_pcm_frames_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_pcm_frames_f32(), drwav_read_pcm_frames_s32() +// and drwav_read_pcm_frames_s16() to read and convert audio data to 32-bit floating point, signed 32-bit integer and +// signed 16-bit integer samples respectively. 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 + +#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 +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 + +// Constants. +#ifndef DRWAV_MAX_SMPL_LOOPS +#define DRWAV_MAX_SMPL_LOOPS 1 +#endif + +// Flags to pass into drwav_init_ex(), etc. +#define DRWAV_SEQUENTIAL 0x00000001 + +typedef enum +{ + drwav_seek_origin_start, + drwav_seek_origin_current +} drwav_seek_origin; + +typedef enum +{ + drwav_container_riff, + drwav_container_w64 +} drwav_container; + +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; + +// 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); + +// Callback for when drwav_init_ex/drwav_open_ex finds a chunk. +// +// pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex(), drwav_open_ex() and family. +// onRead [in] A pointer to the function to call when reading. +// onSeek [in] A pointer to the function to call when seeking. +// pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex(), drwav_open_ex() and family. +// pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk. +// +// Returns the number of bytes read + seeked. +// +// To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same +// for seeking with onSeek(). The return value must be the total number of bytes you have read _plus_ seeked. +// +// You must not attempt to read beyond the boundary of the chunk. +typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader); + +// 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 is equal to WAVE_FORMAT_EXTENSIBLE, + // 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 +{ + drwav_uint32 cuePointId; + drwav_uint32 type; + drwav_uint32 start; + drwav_uint32 end; + drwav_uint32 fraction; + drwav_uint32 playCount; +} drwav_smpl_loop; + + typedef struct +{ + drwav_uint32 manufacturer; + drwav_uint32 product; + drwav_uint32 samplePeriod; + drwav_uint32 midiUnityNotes; + drwav_uint32 midiPitchFraction; + drwav_uint32 smpteFormat; + drwav_uint32 smpteOffset; + drwav_uint32 numSampleLoops; + drwav_uint32 samplerData; + drwav_smpl_loop loops[DRWAV_MAX_SMPL_LOOPS]; +} drwav_smpl; + +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; + + // 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 PCM frames making up the audio data. + drwav_uint64 totalPCMFrameCount; + + + // 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; + + + // smpl chunk. + drwav_smpl smpl; + + + // 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_uint64 totalSampleCount; // <-- DEPRECATED. Will be removed in a future version. +} drwav; + + +// Initializes a pre-allocated drwav object. +// +// pWav [out] A pointer to the drwav object being initialized. +// 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. +// onChunk [in, optional] The function to call when a chunk is enumerated at initialized time. +// pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +// pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk. +// flags [in, optional] A set of flags for controlling how things are loaded. +// +// 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. +// +// Possible values for flags: +// DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function +// to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored. +// +// drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);". +// +// The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt +// after the function returns. +// +// 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); +drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags); + +// 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_init(), drwav_open_file(), drwav_open_memory(), drwav_close() +drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData); +drwav* drwav_open_ex(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags); + +// 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_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_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 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_pcm_frames_s16/s32/f32/etc(). +drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); + +// Seeks to the given PCM frame. +// +// Returns true if successful; false otherwise. +drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); + + +// 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 PCM frames. +// +// Returns the number of PCM frames written. +drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, 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 PCM frames actually read. +// +// If the return value is less than it means the end of the file has been reached. +drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, 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 PCM frames actually read. +// +// If the return value is less than it means the end of the file has been reached. +drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, 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 PCM frames actually read. +// +// If the return value is less than it means the end of the file has been reached. +drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, 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); +drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags); + +// 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); +drwav* drwav_open_file_ex(const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags); + +// 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); +drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags); + +// 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); +drwav* drwav_open_memory_ex(const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags); + +// 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_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +#ifndef DR_WAV_NO_STDIO +// Opens and decodes a wav file in a single operation. +drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +#endif + +// Opens and decodes a wav file from a block of memory in a single operation. +drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount); +#endif + +// Frees data that was allocated internally by dr_wav. +void drwav_free(void* pDataReturnedByOpenAndRead); + + +// DEPRECATED APIS +// =============== +drwav_uint64 drwav_read(drwav* pWav, drwav_uint64 samplesToRead, void* pBufferOut); +drwav_uint64 drwav_read_s16(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); +drwav_uint64 drwav_read_f32(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut); +drwav_uint64 drwav_read_s32(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut); +drwav_bool32 drwav_seek_to_sample(drwav* pWav, drwav_uint64 sample); +drwav_uint64 drwav_write(drwav* pWav, drwav_uint64 samplesToWrite, const void* pData); +#ifndef DR_WAV_NO_CONVERSION_API +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 +drwav_int16* drwav_open_memory_and_read_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +float* drwav_open_file_and_read_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +drwav_int32* drwav_open_file_and_read_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +#endif +drwav_int16* drwav_open_memory_and_read_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +float* drwav_open_memory_and_read_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +drwav_int32* drwav_open_memory_and_read_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalSampleCount); +#endif + + +#ifdef __cplusplus +} +#endif +#endif // dr_wav_h + + +///////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +///////////////////////////////////////////////////// + +#ifdef DR_WAV_IMPLEMENTATION +#include +#include // For memcpy(), memset() +#include // For INT_MAX + +#ifndef DR_WAV_NO_STDIO +#include +#endif + +// Standard library stuff. +#ifndef DRWAV_ASSERT +#include +#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 + +typedef drwav_int32 drwav_result; +#define DRWAV_SUCCESS 0 +#define DRWAV_ERROR -1 +#define DRWAV_INVALID_ARGS -2 +#define DRWAV_INVALID_OPERATION -3 +#define DRWAV_INVALID_FILE -100 +#define DRWAV_EOF -101 + +#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 const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 6C706D73-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); + +static drwav_result 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_EOF; + } + + unsigned char sizeInBytes[4]; + if (onRead(pUserData, sizeInBytes, 4) != 4) { + return DRWAV_INVALID_FILE; + } + + 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_EOF; + } + + unsigned char sizeInBytes[8]; + if (onRead(pUserData, sizeInBytes, 8) != 8) { + return DRWAV_INVALID_FILE; + } + + 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_SUCCESS; +} + +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__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +{ + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, drwav_seek_origin_start); + } + + // Larger than 32-bit seek. + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { + return DRWAV_FALSE; + } + offset -= 0x7FFFFFFF; + + for (;;) { + if (offset <= 0x7FFFFFFF) { + return onSeek(pUserData, (int)offset, drwav_seek_origin_current); + } + + if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { + return DRWAV_FALSE; + } + offset -= 0x7FFFFFFF; + } + + // Should never get here. + //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) != DRWAV_SUCCESS) { + 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) != DRWAV_SUCCESS) { + 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) +{ + return drwav_init_file_ex(pWav, filename, NULL, NULL, 0); +} + +drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) +{ + FILE* pFile = drwav_fopen(filename, "rb"); + if (pFile == NULL) { + return DRWAV_FALSE; + } + + return drwav_init_ex(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, onChunk, (void*)pFile, pChunkUserData, flags); +} + + +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) +{ + return drwav_open_file_ex(filename, NULL, NULL, 0); +} + +drwav* drwav_open_file_ex(const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) +{ + FILE* pFile = drwav_fopen(filename, "rb"); + if (pFile == NULL) { + return DRWAV_FALSE; + } + + drwav* pWav = drwav_open_ex(drwav__on_read_stdio, drwav__on_seek_stdio, onChunk, (void*)pFile, pChunkUserData, flags); + 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) +{ + return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0); +} + +drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) +{ + 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_ex(pWav, drwav__on_read_memory, drwav__on_seek_memory, onChunk, (void*)&memoryStream, pChunkUserData, flags)) { + 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) +{ + return drwav_open_memory_ex(data, dataSize, NULL, NULL, 0); +} + +drwav* drwav_open_memory_ex(const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) +{ + 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_ex(drwav__on_read_memory, drwav__on_seek_memory, onChunk, (void*)&memoryStream, pChunkUserData, flags); + 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); +} + + +size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) +{ + drwav_assert(onRead != NULL); + drwav_assert(pCursor != NULL); + + size_t bytesRead = onRead(pUserData, pBufferOut, bytesToRead); + *pCursor += bytesRead; + return bytesRead; +} + +drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) +{ + drwav_assert(onSeek != NULL); + drwav_assert(pCursor != NULL); + + if (!onSeek(pUserData, offset, origin)) { + return DRWAV_FALSE; + } + + if (origin == drwav_seek_origin_start) { + *pCursor = offset; + } else { + *pCursor += offset; + } + + return DRWAV_TRUE; +} + + +static drwav_uint32 drwav_get_bytes_per_sample(drwav* pWav) +{ + // The number of bytes per sample is based on the bits per sample or the block align. We prioritize floor(bitsPerSample/8), but if + // this is zero or the bits per sample is not a multiple of 8 we need to fall back to the block align. + drwav_uint32 bytesPerSample = pWav->bitsPerSample >> 3; + if (bytesPerSample == 0 || (pWav->bitsPerSample & 0x7) != 0) { + bytesPerSample = pWav->fmt.blockAlign/pWav->fmt.channels; + } + + return bytesPerSample; +} + +static drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) +{ + // The number of bytes per frame is based on the bits per sample or the block align. We prioritize floor(bitsPerSample*channels/8), but if + // this is zero or the bits per frame is not a multiple of 8 we need to fall back to the block align. + drwav_uint32 bitsPerFrame = pWav->bitsPerSample * pWav->fmt.channels; + drwav_uint32 bytesPerFrame = bitsPerFrame >> 3; + if (bytesPerFrame == 0 || (bitsPerFrame & 0x7) != 0) { + bytesPerFrame = pWav->fmt.blockAlign; + } + + return bytesPerFrame; +} + + +drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData) +{ + return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0); +} + +drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags) +{ + if (onRead == NULL || onSeek == NULL) { + return DRWAV_FALSE; + } + + drwav_uint64 cursor = 0; // <-- Keeps track of the byte position so we can seek to specific locations. + drwav_bool32 sequential = (flags & DRWAV_SEQUENTIAL) != 0; + + drwav_zero_memory(pWav, sizeof(*pWav)); + pWav->onRead = onRead; + pWav->onSeek = onSeek; + pWav->pUserData = pReadSeekUserData; + + // The first 4 bytes should be the RIFF identifier. + unsigned char riff[4]; + if (drwav__on_read(onRead, pReadSeekUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { + return DRWAV_FALSE; + } + + // 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 (drwav__on_read(onRead, pReadSeekUserData, riff2, sizeof(riff2), &cursor) != 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 (drwav__on_read(onRead, pReadSeekUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != 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 (drwav__on_read(onRead, pReadSeekUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return DRWAV_FALSE; + } + + if (!drwav__fourcc_equal(wave, "WAVE")) { + return DRWAV_FALSE; // Expecting "WAVE". + } + } else { + // W64 + unsigned char chunkSize[8]; + if (drwav__on_read(onRead, pReadSeekUserData, chunkSize, sizeof(chunkSize), &cursor) != sizeof(chunkSize)) { + return DRWAV_FALSE; + } + + if (drwav__bytes_to_u64(chunkSize) < 80) { + return DRWAV_FALSE; + } + + drwav_uint8 wave[16]; + if (drwav__on_read(onRead, pReadSeekUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return DRWAV_FALSE; + } + + if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) { + return DRWAV_FALSE; + } + } + + + // The next bytes should be the "fmt " chunk. + drwav_fmt fmt; + if (!drwav__read_fmt(onRead, onSeek, pReadSeekUserData, pWav->container, &cursor, &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; + + // We need to enumerate over each chunk for two reasons: + // 1) The "data" chunk may not be the next one + // 2) We may want to report each chunk back to the client + // + // In order to correctly report each chunk back to the client we will need to keep looping until the end of the file. + drwav_bool32 foundDataChunk = DRWAV_FALSE; + drwav_uint64 dataChunkSize = 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 chunkSize = 0; + for (;;) + { + drwav_chunk_header header; + drwav_result result = drwav__read_chunk_header(onRead, pReadSeekUserData, pWav->container, &cursor, &header); + if (result != DRWAV_SUCCESS) { + if (!foundDataChunk) { + return DRWAV_FALSE; + } else { + break; // Probably at the end of the file. Get out of the loop. + } + } + + // Tell the client about this chunk. + if (!sequential && onChunk != NULL) { + drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, onRead, onSeek, pReadSeekUserData, &header); + + // dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before + // we called the callback. + if (callbackBytesRead > 0) { + if (!drwav__seek_from_start(onSeek, cursor, pReadSeekUserData)) { + return DRWAV_FALSE; + } + } + } + + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + + chunkSize = header.sizeInBytes; + if (pWav->container == drwav_container_riff) { + if (drwav__fourcc_equal(header.id.fourcc, "data")) { + foundDataChunk = DRWAV_TRUE; + dataChunkSize = chunkSize; + } + } else { + if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) { + foundDataChunk = DRWAV_TRUE; + dataChunkSize = chunkSize; + } + } + + // If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for + // this is that we would otherwise require a backwards seek which sequential mode forbids. + if (foundDataChunk && sequential) { + 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 (drwav__on_read(onRead, pReadSeekUserData, &sampleCount, 4, &cursor) != 4) { + return DRWAV_FALSE; + } + chunkSize -= 4; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + + // 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 (drwav__on_read(onRead, pReadSeekUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { + return DRWAV_FALSE; + } + chunkSize -= 8; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + } + } + + // "smpl" chunk. + if (pWav->container == drwav_container_riff) { + if (drwav__fourcc_equal(header.id.fourcc, "smpl")) { + unsigned char smplHeaderData[36]; // 36 = size of the smpl header section, not including the loop data. + if (chunkSize >= sizeof(smplHeaderData)) { + drwav_uint64 bytesJustRead = drwav__on_read(onRead, pReadSeekUserData, smplHeaderData, sizeof(smplHeaderData), &cursor); + chunkSize -= bytesJustRead; + + if (bytesJustRead == sizeof(smplHeaderData)) { + pWav->smpl.manufacturer = drwav__bytes_to_u32(smplHeaderData+0); + pWav->smpl.product = drwav__bytes_to_u32(smplHeaderData+4); + pWav->smpl.samplePeriod = drwav__bytes_to_u32(smplHeaderData+8); + pWav->smpl.midiUnityNotes = drwav__bytes_to_u32(smplHeaderData+12); + pWav->smpl.midiPitchFraction = drwav__bytes_to_u32(smplHeaderData+16); + pWav->smpl.smpteFormat = drwav__bytes_to_u32(smplHeaderData+20); + pWav->smpl.smpteOffset = drwav__bytes_to_u32(smplHeaderData+24); + pWav->smpl.numSampleLoops = drwav__bytes_to_u32(smplHeaderData+28); + pWav->smpl.samplerData = drwav__bytes_to_u32(smplHeaderData+32); + + for (drwav_uint32 iLoop = 0; iLoop < pWav->smpl.numSampleLoops && iLoop < drwav_countof(pWav->smpl.loops); ++iLoop) { + unsigned char smplLoopData[24]; // 24 = size of a loop section in the smpl chunk. + bytesJustRead = drwav__on_read(onRead, pReadSeekUserData, smplLoopData, sizeof(smplLoopData), &cursor); + chunkSize -= bytesJustRead; + + if (bytesJustRead == sizeof(smplLoopData)) { + pWav->smpl.loops[iLoop].cuePointId = drwav__bytes_to_u32(smplLoopData+0); + pWav->smpl.loops[iLoop].type = drwav__bytes_to_u32(smplLoopData+4); + pWav->smpl.loops[iLoop].start = drwav__bytes_to_u32(smplLoopData+8); + pWav->smpl.loops[iLoop].end = drwav__bytes_to_u32(smplLoopData+12); + pWav->smpl.loops[iLoop].fraction = drwav__bytes_to_u32(smplLoopData+16); + pWav->smpl.loops[iLoop].playCount = drwav__bytes_to_u32(smplLoopData+20); + } else { + break; // Break from the smpl loop for loop. + } + } + } + } else { + // Looks like invalid data. Ignore the chunk. + } + } + } else { + if (drwav__guid_equal(header.id.guid, drwavGUID_W64_SMPL)) { + // This path will be hit when a W64 WAV file contains a smpl chunk. I don't have a sample file to test this path, so a contribution + // is welcome to add support for this. + } + } + + // Make sure we seek past the padding. + chunkSize += header.paddingSize; + if (!drwav__seek_forward(onSeek, chunkSize, pReadSeekUserData)) { + break; + } + cursor += chunkSize; + + if (!foundDataChunk) { + pWav->dataChunkDataPos = cursor; + } + } + + // If we haven't found a data chunk, return an error. + if (!foundDataChunk) { + return DRWAV_FALSE; + } + + // We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. + if (!sequential) { + if (!drwav__seek_from_start(onSeek, pWav->dataChunkDataPos, pReadSeekUserData)) { + return DRWAV_FALSE; + } + cursor = pWav->dataChunkDataPos; + } + + + // At this point we should be sitting on the first byte of the raw audio data. + + pWav->fmt = fmt; + pWav->sampleRate = fmt.sampleRate; + pWav->channels = fmt.channels; + pWav->bitsPerSample = fmt.bitsPerSample; + pWav->bytesRemaining = dataChunkSize; + pWav->translatedFormatTag = translatedFormatTag; + pWav->dataChunkDataSize = dataChunkSize; + + if (sampleCountFromFactChunk != 0) { + pWav->totalPCMFrameCount = sampleCountFromFactChunk; + } else { + pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav); + + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; // x2 because two samples per byte. + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; + } + } + + // 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 = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; // x2 because two samples per byte. + } + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; + } +#endif + + pWav->totalSampleCount = pWav->totalPCMFrameCount * pWav->channels; + + 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->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) +{ + return drwav_open_ex(onRead, onSeek, NULL, pUserData, NULL, 0); +} + +drwav* drwav_open_ex(drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags) +{ + drwav* pWav = (drwav*)DRWAV_MALLOC(sizeof(*pWav)); + if (pWav == NULL) { + return NULL; + } + + if (!drwav_init_ex(pWav, onRead, onSeek, onChunk, pReadSeekUserData, pChunkUserData, flags)) { + 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; + } + + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (bytesPerSample == 0) { + return 0; + } + + // Don't try to read more samples than can potentially fit in the output buffer. + if (samplesToRead * bytesPerSample > DRWAV_SIZE_MAX) { + samplesToRead = DRWAV_SIZE_MAX / bytesPerSample; + } + + size_t bytesRead = drwav_read_raw(pWav, (size_t)(samplesToRead * bytesPerSample), pBufferOut); + return bytesRead / bytesPerSample; +} + +drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +{ + if (pWav == NULL || framesToRead == 0 || pBufferOut == NULL) { + return 0; + } + + // Cannot use this function for compressed formats. + if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + return 0; + } + + drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + if (bytesPerFrame == 0) { + return 0; + } + + // Don't try to read more samples than can potentially fit in the output buffer. + if (framesToRead * bytesPerFrame > DRWAV_SIZE_MAX) { + framesToRead = DRWAV_SIZE_MAX / bytesPerFrame; + } + + size_t bytesRead = drwav_read_raw(pWav, (size_t)(framesToRead * bytesPerFrame), pBufferOut); + return bytesRead / bytesPerFrame; +} + +drwav_bool32 drwav_seek_to_first_pcm_frame(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_pcm_frame(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->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav); + drwav_assert(totalSizeInBytes >= pWav->bytesRemaining); + + drwav_uint64 currentBytePos = totalSizeInBytes - pWav->bytesRemaining; + drwav_uint64 targetBytePos = sample * drwav_get_bytes_per_sample(pWav); + + drwav_uint64 offset; + if (currentBytePos < targetBytePos) { + // Offset forwards. + offset = (targetBytePos - currentBytePos); + } else { + // Offset backwards. + if (!drwav_seek_to_first_pcm_frame(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; +} + +drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) +{ + return drwav_seek_to_sample(pWav, targetFrameIndex * pWav->channels); +} + + +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_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +{ + return drwav_write(pWav, framesToWrite * pWav->channels, pData) / pWav->channels; +} + + + +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 int 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 int 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) +{ + drwav_uint32 bytesPerSample; + + // Fast path. + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) { + return drwav_read(pWav, samplesToRead, pBufferOut); + } + + bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s16__ieee(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s16__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut) +{ + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/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_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/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; +} + +drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +{ + return drwav_read_s16(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels; +} + +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 int 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 int 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) +{ + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, 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) +{ + drwav_uint32 bytesPerSample; + + // Fast path. + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { + return drwav_read(pWav, samplesToRead, pBufferOut); + } + + bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_f32__alaw(drwav* pWav, drwav_uint64 samplesToRead, float* pBufferOut) +{ + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/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) +{ + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/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; +} + +drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +{ + return drwav_read_f32(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels; +} + +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 int 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 int 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) +{ + drwav_uint32 bytesPerSample; + + // Fast path. + if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { + return drwav_read(pWav, samplesToRead, pBufferOut); + } + + bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, 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) +{ + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/bytesPerSample), sampleData); + if (samplesRead == 0) { + break; + } + + drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + + pBufferOut += samplesRead; + samplesToRead -= samplesRead; + totalSamplesRead += samplesRead; + } + + return totalSamplesRead; +} + +drwav_uint64 drwav_read_s32__alaw(drwav* pWav, drwav_uint64 samplesToRead, drwav_int32* pBufferOut) +{ + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/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) +{ + drwav_uint32 bytesPerSample = drwav_get_bytes_per_sample(pWav); + if (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)/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; +} + +drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +{ + return drwav_read_s32(pWav, framesToRead * pWav->channels, pBufferOut) / pWav->channels; +} + +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 (channels) *channels = 0; + if (sampleRate) *sampleRate = 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); +} + +drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + drwav_int16* result = drwav_open_and_read_s16(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} + +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); +} + +float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + float* result = drwav_open_and_read_f32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} + +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); +} + +drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + drwav_int32* result = drwav_open_and_read_s32(onRead, onSeek, pUserData, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} + +#ifndef DR_WAV_NO_STDIO +drwav_int16* drwav_open_file_and_read_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); +} + +drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + drwav_int16* result = drwav_open_file_and_read_s16(filename, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} + +float* drwav_open_file_and_read_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); +} + +float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + float* result = drwav_open_file_and_read_f32(filename, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} + +drwav_int32* drwav_open_file_and_read_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); +} + +drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + drwav_int32* result = drwav_open_file_and_read_s32(filename, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} +#endif + +drwav_int16* drwav_open_memory_and_read_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); +} + +drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + drwav_int16* result = drwav_open_memory_and_read_s16(data, dataSize, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} + +float* drwav_open_memory_and_read_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); +} + +float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + float* result = drwav_open_memory_and_read_f32(data, dataSize, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} + +drwav_int32* drwav_open_memory_and_read_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); +} + +drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut) +{ + if (channelsOut) *channelsOut = 0; + if (sampleRateOut) *sampleRateOut = 0; + if (totalFrameCountOut) *totalFrameCountOut = 0; + + unsigned int channels; + unsigned int sampleRate; + drwav_uint64 totalSampleCount; + drwav_int32* result = drwav_open_memory_and_read_s32(data, dataSize, &channels, &sampleRate, &totalSampleCount); + if (result == NULL) { + return NULL; + } + + if (channelsOut) *channelsOut = channels; + if (sampleRateOut) *sampleRateOut = sampleRate; + if (totalFrameCountOut) *totalFrameCountOut = totalSampleCount / channels; + + return result; +} +#endif //DR_WAV_NO_CONVERSION_API + + +void drwav_free(void* pDataReturnedByOpenAndRead) +{ + DRWAV_FREE(pDataReturnedByOpenAndRead); +} + +#endif //DR_WAV_IMPLEMENTATION + + +// REVISION HISTORY +// +// v0.9.0 - 2018-12-16 +// - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and +// will be removed in v0.10.0. Deprecated APIs and their replacements: +// drwav_read() -> drwav_read_pcm_frames() +// drwav_read_s16() -> drwav_read_pcm_frames_s16() +// drwav_read_f32() -> drwav_read_pcm_frames_f32() +// drwav_read_s32() -> drwav_read_pcm_frames_s32() +// drwav_seek_to_sample() -> drwav_seek_to_pcm_frame() +// drwav_write() -> drwav_write_pcm_frames() +// drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16() +// drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32() +// drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32() +// drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16() +// drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32() +// drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32() +// drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16() +// drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32() +// drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32() +// drwav::totalSampleCount -> drwav::totalPCMFrameCount +// - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*(). +// - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*(). +// - Add built-in support for smpl chunks. +// - Add support for firing a callback for each chunk in the file at initialization time. +// - This is enabled through the drwav_init_ex(), etc. family of APIs. +// - Handle invalid FMT chunks more robustly. +// +// 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 +*/ diff --git a/modules/entt b/modules/entt deleted file mode 160000 index 09ff43e..0000000 --- a/modules/entt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 09ff43ef0a9bc782eec42b8b2b10818ccc16f1e9 diff --git a/modules/entt/.gitignore b/modules/entt/.gitignore new file mode 100644 index 0000000..12a3804 --- /dev/null +++ b/modules/entt/.gitignore @@ -0,0 +1,5 @@ +*.user +conan/test_package/build + +# IDEs +.idea diff --git a/modules/entt/.travis.yml b/modules/entt/.travis.yml new file mode 100644 index 0000000..34af7fe --- /dev/null +++ b/modules/entt/.travis.yml @@ -0,0 +1,97 @@ +language: cpp +dist: trusty +sudo: false + +env: + global: + - CONAN_USERNAME="skypjack" + - CONAN_PACKAGE_NAME="entt" + - CONAN_HEADER_ONLY="True" + - NON_CONAN_DEPLOYMENT="True" + +conan-buildsteps: &conan-buildsteps + before_install: + # use this step if you desire to manipulate CONAN variables programmatically + - NON_CONAN_DEPLOYMENT="False" + install: + - chmod +x ./conan/ci/install.sh + - ./conan/ci/install.sh + script: + - chmod +x ./conan/ci/build.sh + - ./conan/ci/build.sh + # the following are dummies to overwrite default build steps + before_script: + - true + after_success: + - true + if: tag IS present +conan-linux: &conan-linux + os: linux + sudo: required + language: python + python: "3.6" + services: + - docker + <<: *conan-buildsteps + +matrix: + include: + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-7'] + env: COMPILER=g++-7 + - os: linux + compiler: clang + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-6.0'] + packages: ['clang-6.0', 'g++-7'] + env: COMPILER=clang++-6.0 + - os: osx + osx_image: xcode10 + compiler: clang + env: COMPILER=clang++ + - os: linux + compiler: gcc + addons: + apt: + sources: ['ubuntu-toolchain-r-test'] + packages: ['g++-7'] + env: + - COMPILER=g++-7 + - CXXFLAGS="-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline" + before_script: + - pip install --user cpp-coveralls + after_success: + - coveralls --gcov gcov-7 --gcov-options '\-lp' --root ${TRAVIS_BUILD_DIR} --build-root ${TRAVIS_BUILD_DIR}/build --extension cpp --extension hpp --exclude deps --include src + # Conan testing and uploading + - <<: *conan-linux + env: CONAN_GCC_VERSIONS=8 CONAN_DOCKER_IMAGE=conanio/gcc8 + +notifications: + email: + on_success: never + on_failure: always + +install: +- echo ${PATH} +- cmake --version +- export CXX=${COMPILER} +- echo ${CXX} +- ${CXX} --version +- ${CXX} -v + +script: +- mkdir -p build && cd build +- cmake -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Release .. && make -j4 +- CTEST_OUTPUT_ON_FAILURE=1 ctest -j4 + +deploy: + provider: script + script: scripts/update_packages.sh $TRAVIS_TAG + on: + tags: true + condition: “$NON_CONAN_DEPLOYMENT = “True” diff --git a/modules/entt/AUTHORS b/modules/entt/AUTHORS new file mode 100644 index 0000000..71156fc --- /dev/null +++ b/modules/entt/AUTHORS @@ -0,0 +1,34 @@ +# Author + +skypjack + +# Contributors + +BenediktConze +bjadamson +ceeac +ColinH +corystegel +Croydon +dbacchet +dBagrat +djarek +DonKult +drglove +eugeneko +gale83 +ghost +Green-Sky +mhammerc +Milerius +morbo84 +m-waka +Kerndog73 +Paolo-Oliverio +pgruenbacher +prowolf +The5-1 +vblanco20-1 +willtunnels +WizardIke +w1th0utnam3 diff --git a/modules/entt/CMakeLists.txt b/modules/entt/CMakeLists.txt new file mode 100644 index 0000000..6e8a6be --- /dev/null +++ b/modules/entt/CMakeLists.txt @@ -0,0 +1,226 @@ +# +# EnTT +# + +cmake_minimum_required(VERSION 3.7.2) + +# +# Building in-tree is not allowed (we take care of your craziness). +# + +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the source code and call cmake from there. Thank you.") +endif() + +# +# Project configuration +# + +project(EnTT VERSION 3.1.0) + +include(GNUInstallDirs) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() + +set(SETTINGS_ORGANIZATION "Michele Caini") +set(SETTINGS_APPLICATION ${PROJECT_NAME}) +set(PROJECT_AUTHOR "Michele Caini") +set(PROJECT_AUTHOR_EMAIL "michele.caini@gmail.com") + +message("*") +message("* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})") +message("* Copyright (c) 2017-2019 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>") +message("*") + +option(USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if availbale." ON) +option(USE_ASAN "Use address sanitizer by adding -fsanitize=address -fno-omit-frame-pointer flags" OFF) +option(USE_COMPILE_OPTIONS "Use compile options from EnTT." ON) + +# +# Compiler stuff +# + +if(NOT MSVC AND USE_LIBCPP) + include(CheckCXXSourceCompiles) + include(CMakePushCheckState) + + cmake_push_check_state() + + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++") + + check_cxx_source_compiles(" + #include + int main() { return std::is_same_v ? 0 : 1; } + " HAS_LIBCPP) + + if(NOT HAS_LIBCPP) + message(WARNING "The option USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.") + endif() + + cmake_pop_check_state() +endif() + +# +# Add EnTT target +# + +add_library(EnTT INTERFACE) + +configure_file(${EnTT_SOURCE_DIR}/cmake/in/version.h.in ${EnTT_SOURCE_DIR}/src/entt/config/version.h @ONLY) + +target_include_directories( + EnTT INTERFACE + $ + $ +) + +target_compile_definitions( + EnTT + INTERFACE $<$,$>>:DEBUG> + INTERFACE $<$,$>>:RELEASE> +) + +if(USE_ASAN) + target_compile_options(EnTT INTERFACE $<$,$>>:-fsanitize=address -fno-omit-frame-pointer>) + target_link_libraries(EnTT INTERFACE $<$,$>>:-fsanitize=address -fno-omit-frame-pointer>) +endif() + +if(USE_COMPILE_OPTIONS) + target_compile_options( + EnTT + INTERFACE $<$,$>>:-O0 -g> + # it seems that -O3 ruins a bit the performance when using clang ... + INTERFACE $<$,$>:-O2> + # ... on the other side, GCC is incredibly comfortable with it. + INTERFACE $<$,$>:-O3> + ) +endif() + +if(HAS_LIBCPP) + target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++) +endif() + +target_compile_features(EnTT INTERFACE cxx_std_17) + +# +# Install EnTT +# + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + set(CUSTOM_INSTALL_CONFIGDIR cmake) +else() + set(CUSTOM_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/entt) +endif() + +install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(TARGETS EnTT EXPORT EnTTTargets) + +export(EXPORT EnTTTargets FILE ${EnTT_BINARY_DIR}/EnTTTargets.cmake) + +install( + EXPORT EnTTTargets + FILE EnTTTargets.cmake + DESTINATION ${CUSTOM_INSTALL_CONFIGDIR} +) + +# +# Build tree package config file +# + +configure_file(cmake/in/EnTTBuildConfig.cmake.in EnTTConfig.cmake @ONLY) + +include(CMakePackageConfigHelpers) + +# +# Install tree package config file +# + +configure_package_config_file( + cmake/in/EnTTConfig.cmake.in + ${CUSTOM_INSTALL_CONFIGDIR}/EnTTConfig.cmake + INSTALL_DESTINATION ${CUSTOM_INSTALL_CONFIGDIR} + PATH_VARS CMAKE_INSTALL_INCLUDEDIR +) + +write_basic_package_version_file( + ${EnTT_BINARY_DIR}/EnTTConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +install( + FILES + ${EnTT_BINARY_DIR}/${CUSTOM_INSTALL_CONFIGDIR}/EnTTConfig.cmake + ${EnTT_BINARY_DIR}/EnTTConfigVersion.cmake + DESTINATION ${CUSTOM_INSTALL_CONFIGDIR} +) + +export(PACKAGE EnTT) + +# +# Tests +# + +option(BUILD_TESTING "Enable testing with ctest." OFF) + +if(BUILD_TESTING) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + + option(FIND_GTEST_PACKAGE "Enable finding gtest package." OFF) + + if(FIND_GTEST_PACKAGE) + find_package(GTest REQUIRED) + else() + # gtest, gtest_main, gmock and gmock_main targets are available from now on + set(GOOGLETEST_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/googletest) + configure_file(${EnTT_SOURCE_DIR}/cmake/in/googletest.in ${GOOGLETEST_DEPS_DIR}/CMakeLists.txt) + execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR}) + execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR}) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + add_subdirectory(${GOOGLETEST_DEPS_DIR}/src ${GOOGLETEST_DEPS_DIR}/build) + target_compile_features(gmock_main PRIVATE $) + target_compile_features(gmock PRIVATE $) + add_library(GTest::Main ALIAS gtest_main) + endif() + + option(BUILD_BENCHMARK "Build benchmark." OFF) + option(BUILD_LIB "Build lib example." OFF) + option(BUILD_MOD "Build mod example." OFF) + option(BUILD_SNAPSHOT "Build snapshot example." OFF) + + enable_testing() + add_subdirectory(test) +endif() + +# +# Documentation +# + +option(BUILD_DOCS "Enable building with documentation." OFF) + +if(BUILD_DOCS) + find_package(Doxygen 1.8) + + if(DOXYGEN_FOUND) + add_subdirectory(docs) + endif() +endif() + +# +# AOB +# + +add_custom_target( + entt_aob + SOURCES + appveyor.yml + AUTHORS + CONTRIBUTING.md + LICENSE + README.md + TODO + .travis.yml +) diff --git a/modules/entt/CONTRIBUTING.md b/modules/entt/CONTRIBUTING.md new file mode 100644 index 0000000..6795dca --- /dev/null +++ b/modules/entt/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing + +First of all, thank you very much for taking the time to contribute to the +`EnTT` framework.
+How to do it mostly depends on the type of contribution: + +* If you have a question, **please** ensure there isn't already an answer for + you by searching on GitHub under + [issues](https://github.com/skypjack/entt/issues). Do not forget to search + also through the closed ones. If you are unable to find a proper answer, feel + free to [open a new issue](https://github.com/skypjack/entt/issues/new). + Usually, questions are marked as such and closed in a few days. + +* If you want to fix a typo in the inline documentation or in the README file, + if you want to add some new sections or if you want to help me with the + language by reviewing what I wrote so far (I'm not a native speaker after + all), **please** open a new + [pull request](https://github.com/skypjack/entt/pulls) with your changes. + +* If you found a bug, **please** ensure there isn't already an answer for you by + searching on GitHub under [issues](https://github.com/skypjack/entt/issues). + If you are unable to find an open issue addressing the problem, feel free to + [open a new one](https://github.com/skypjack/entt/issues/new). **Please**, do + not forget to carefully describe how to reproduce the problem, then add all + the information about the system on which you are experiencing it and point + out the version of `EnTT` you are using (tag or commit). + +* If you found a bug and you wrote a patch to fix it, open a new + [pull request](https://github.com/skypjack/entt/pulls) with your code. + **Please**, add some tests to avoid regressions in future if possible, it + would be really appreciated. Note that the `EnTT` framework has a + [coverage at 100%](https://coveralls.io/github/skypjack/entt?branch=master) + (at least it was at 100% at the time I wrote this file) and this is the reason + for which you can be confident with using it in a production environment. + +* If you want to propose a new feature and you know how to code it, **please** + do not issue directly a pull request. Before to do it, + [create a new issue](https://github.com/skypjack/entt/issues/new) to discuss + your proposal. Other users could be interested in your idea and the discussion + that will follow can refine it and therefore give us a better solution. + +* If you want to request a new feature, I'm available for hiring. Take a look at + [my profile](https://github.com/skypjack) and feel free to write me. diff --git a/modules/entt/LICENSE b/modules/entt/LICENSE new file mode 100644 index 0000000..7951502 --- /dev/null +++ b/modules/entt/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2019 Michele Caini + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copy of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copy or substantial portions of the Software. + +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 OR COPYRIGHT HOLDERS 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. diff --git a/modules/entt/README.md b/modules/entt/README.md new file mode 100644 index 0000000..4ccbc6c --- /dev/null +++ b/modules/entt/README.md @@ -0,0 +1,411 @@ +![EnTT: Gaming meets modern C++](https://user-images.githubusercontent.com/1812216/42513718-ee6e98d0-8457-11e8-9baf-8d83f61a3097.png) + + +[![GitHub version](https://badge.fury.io/gh/skypjack%2Fentt.svg)](http://badge.fury.io/gh/skypjack%2Fentt) +[![LoC](https://tokei.rs/b1/github/skypjack/entt)](https://github.com/skypjack/entt) +[![Build Status](https://travis-ci.org/skypjack/entt.svg?branch=master)](https://travis-ci.org/skypjack/entt) +[![Build status](https://ci.appveyor.com/api/projects/status/rvhaabjmghg715ck?svg=true)](https://ci.appveyor.com/project/skypjack/entt) +[![Coverage Status](https://coveralls.io/repos/github/skypjack/entt/badge.svg?branch=master)](https://coveralls.io/github/skypjack/entt?branch=master) +[![Gitter chat](https://badges.gitter.im/skypjack/entt.png)](https://gitter.im/skypjack/entt) +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/skypjack) + +`EnTT` is a header-only, tiny and easy to use library for game programming and +much more written in **modern C++**, mainly known for its innovative +**entity-component-system (ECS)** model.
+[Among others](https://github.com/skypjack/entt/wiki/EnTT-in-Action), it's used +in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang, the +[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by Esri +and [**The Forge**](https://github.com/ConfettiFX/The-Forge) by Confetti. Read +on to find out what it can offer you. + +--- + +Do you want to **keep up with changes** or do you have a **question** that +doesn't require you to open an issue?
+Join the [gitter channel](https://gitter.im/skypjack/entt) and meet other users +like you. The more we are, the better for everyone. + +If you use `EnTT` and you want to say thanks or support the project, please +**consider becoming a patron**: + +[![Patreon](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/bePatron?c=1772573) + +[Many thanks](https://skypjack.github.io/patreon/) to those who supported me and +still support me today. + +# Table of Contents + +* [Introduction](#introduction) + * [Code Example](#code-example) + * [Motivation](#motivation) + * [Performance](#performance) +* [Build Instructions](#build-instructions) + * [Requirements](#requirements) + * [Library](#library) + * [Documentation](#documentation) + * [Tests](#tests) +* [Packaging Tools](#packaging-tools) +* [EnTT in Action](#entt-in-action) +* [Contributors](#contributors) +* [License](#license) +* [Support](#support) + * [Patreon](#patreon) + * [Donation](#donation) + * [Hire me](#hire-me) + + +# Introduction + +The entity-component-system (also known as _ECS_) is an architectural pattern +used mostly in game development. For further details: + +* [Entity Systems Wiki](http://entity-systems.wikidot.com/) +* [Evolve Your Hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/) +* [ECS on Wikipedia](https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system) + +This project started off as a pure entity-component system. Over time the +codebase has grown as more and more classes and functionalities were added.
+Here is a brief, yet incomplete list of what it offers today: + +* Statically generated integer **identifiers for types** (assigned either at + compile-time or at runtime). +* A `constexpr` utility for **human readable resource identifiers**. +* A minimal **configuration system** built using the monostate pattern. +* An incredibly fast **entity-component system** based on sparse sets, with its + own _pay for what you use_ policy to adjust performance and memory usage + according to the users' requirements. +* Views and groups to iterate entities and components and allow different access + patterns, from **perfect SoA** to fully random. +* A lot of **facilities** built on top of the entity-component system to support + the users and avoid reinventing the wheel (dependencies, snapshot, actor class + for those who aren't confident with the architecture and so on). +* The smallest and most basic implementation of a **service locator** ever seen. +* A built-in, non-intrusive and macro-free **runtime reflection system**. +* A **cooperative scheduler** for processes of any type. +* All that is needed for **resource management** (cache, loaders, handles). +* **Delegates**, **signal handlers** (with built-in support for collectors) and + a tiny **event dispatcher** for immediate and delayed events to integrate in + loops. +* A general purpose **event emitter** as a CRTP idiom based class template. +* And **much more**! Check out the + [**wiki**](https://github.com/skypjack/entt/wiki). + +Consider this list a work in progress as well as the project. The whole API is +fully documented in-code for those who are brave enough to read it. + +Currently, `EnTT` is tested on Linux, Microsoft Windows and OSX. It has proven +to work also on both Android and iOS.
+Most likely it won't be problematic on other systems as well, but it hasn't been +sufficiently tested so far. + +## Code Example + +```cpp +#include +#include + +struct position { + float x; + float y; +}; + +struct velocity { + float dx; + float dy; +}; + +void update(entt::registry ®istry) { + auto view = registry.view(); + + for(auto entity: view) { + // gets only the components that are going to be used ... + + auto &vel = view.get(entity); + + vel.dx = 0.; + vel.dy = 0.; + + // ... + } +} + +void update(std::uint64_t dt, entt::registry ®istry) { + registry.view().each([dt](auto &pos, auto &vel) { + // gets all the components of the view at once ... + + pos.x += vel.dx * dt; + pos.y += vel.dy * dt; + + // ... + }); +} + +int main() { + entt::registry registry; + std::uint64_t dt = 16; + + for(auto i = 0; i < 10; ++i) { + auto entity = registry.create(); + registry.assign(entity, i * 1.f, i * 1.f); + if(i % 2 == 0) { registry.assign(entity, i * .1f, i * .1f); } + } + + update(dt, registry); + update(registry); + + // ... +} +``` + +## Motivation + +I started developing `EnTT` for the _wrong_ reason: my goal was to design an +entity-component system to beat another well known open source solution both in +terms of performance and possibly memory usage.
+In the end, I did it, but it wasn't very satisfying. Actually it wasn't +satisfying at all. The fastest and nothing more, fairly little indeed. When I +realized it, I tried hard to keep intact the great performance of `EnTT` and to +add all the features I wanted to see in *my own library* at the same time. + +Nowadays, `EnTT` is finally what I was looking for: still faster than its +_competitors_, lower memory usage in the average case, a really good API and an +amazing set of features. And even more, of course. + +## Performance + +As it stands right now, `EnTT` is just fast enough for my requirements when +compared to my first choice (it was already amazingly fast actually).
+Below is a comparison between the two (both of them compiled with GCC 7.3.0 on a +Dell XPS 13 from mid 2014): + +| Benchmark | EntityX (compile-time) | EnTT | +|-----------|-------------|-------------| +| Create 1M entities | 0.0147s | **0.0046s** | +| Destroy 1M entities | 0.0053s | **0.0045s** | +| 1M entities, one component | 0.0012s | **1.9e-07s** | +| 1M entities, two components | 0.0012s | **3.8e-07s** | +| 1M entities, two components
Half of the entities have all the components | 0.0009s | **3.8e-07s** | +| 1M entities, two components
One of the entities has all the components | 0.0008s | **1.0e-06s** | +| 1M entities, five components | 0.0010s | **7.0e-07s** | +| 1M entities, ten components | 0.0011s | **1.2e-06s** | +| 1M entities, ten components
Half of the entities have all the components | 0.0010s | **1.2e-06s** | +| 1M entities, ten components
One of the entities has all the components | 0.0008s | **1.2e-06s** | +| Sort 150k entities, one component
Arrays are in reverse order | - | **0.0036s** | +| Sort 150k entities, enforce permutation
Arrays are in reverse order | - | **0.0005s** | +| Sort 150k entities, one component
Arrays are almost sorted, std::sort | - | **0.0035s** | +| Sort 150k entities, one component
Arrays are almost sorted, insertion sort | - | **0.0007s** | + +Note: The default version of `EntityX` (`master` branch) wasn't added to the +comparison because it's already much slower than its compile-time counterpart. + +Pretty interesting results, aren't them? In fact, these benchmarks are the ones +used by `EntityX` to show _how fast it is_. To be honest, they aren't so good +and these results shouldn't be taken too seriously (indeed they are completely +unrealistic).
+The proposed entity-component system is incredibly fast to iterate entities, +this is a fact. The compiler can make a lot of optimizations because of how +`EnTT` works, even more when components aren't used at all. This is exactly the +case for these benchmarks. On the other hand, if we consider real world cases, +`EnTT` is somewhere between a bit and much faster than the other solutions +around when users also access the components and not just the entities, although +it isn't as fast as reported by these benchmarks.
+This is why they are completely wrong and cannot be used to evaluate any of the +entity-component-system libraries out there. + +The choice to use `EnTT` should be based on its carefully designed API, its +set of features and the general performance, not because some single benchmark +shows it to be the fastest tool available. + +In the future I'll likely try to get even better performance while still adding +new features, mainly for fun.
+If you want to contribute and/or have suggestions, feel free to make a PR or +open an issue to discuss your idea. + +# Build Instructions + +## Requirements + +To be able to use `EnTT`, users must provide a full-featured compiler that +supports at least C++17.
+The requirements below are mandatory to compile the tests and to extract the +documentation: + +* CMake version 3.2 or later. +* Doxygen version 1.8 or later. + +If you are looking for a C++14 version of `EnTT`, check out the git tag `cpp14`. + +## Library + +`EnTT` is a header-only library. This means that including the `entt.hpp` header +is enough to include the library as a whole and use it. For those who are +interested only in the entity-component system, consider to include the sole +`entity/registry.hpp` header instead.
+It's a matter of adding the following line to the top of a file: + +```cpp +#include +``` + +Use the line below to include only the entity-component system instead: + +```cpp +#include +``` + +Then pass the proper `-I` argument to the compiler to add the `src` directory to +the include paths. + +## Documentation + +The documentation is based on [doxygen](http://www.doxygen.nl/). +To build it: + + $ cd build + $ cmake .. -DBUILD_DOCS=ON + $ make + +The API reference will be created in HTML format within the directory +`build/docs/html`. To navigate it with your favorite browser: + + $ cd build + $ your_favorite_browser docs/html/index.html + + +It's also available [online](https://skypjack.github.io/entt/) for the latest +version.
+Finally, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated +to the project where users can find all related documentation pages. + + +## Tests + +To compile and run the tests, `EnTT` requires *googletest*.
+`cmake` will download and compile the library before compiling anything else. +In order to build the tests, set the CMake option `BUILD_TESTING` to `ON`. + +To build the most basic set of tests: + +* `$ cd build` +* `$ cmake -DBUILD_TESTING=ON ..` +* `$ make` +* `$ make test` + +Note that benchmarks are not part of this set. + +# Packaging Tools + +`EnTT` is available for some of the most known packaging tools. In particular: + +* [`Conan`](https://bintray.com/skypjack/conan/entt%3Askypjack/_latestVersion), + the C/C++ Package Manager for Developers. +* [`Homebrew`](https://github.com/skypjack/homebrew-entt), the missing package + manager for macOS.
+ Available as a homebrew formula. Just type the following to install it: + ``` + brew install skypjack/entt/entt + ``` +* [`vcpkg`](https://github.com/Microsoft/vcpkg/tree/master/ports/entt), + Microsoft VC++ Packaging Tool. + +Consider this list a work in progress and help me to make it longer. + + +# EnTT in Action + +`EnTT` is widely used in private and commercial applications. I cannot even +mention most of them because of some signatures I put on some documents time +ago. Fortunately, there are also people who took the time to implement open +source projects based on `EnTT` and did not hold back when it came to +documenting them. + +[Here](https://github.com/skypjack/entt/wiki/EnTT-in-Action) you can find an +incomplete list of games, applications and articles that can be used as a +reference. + +If you know of other resources out there that are about `EnTT`, feel free to +open an issue or a PR and I'll be glad to add them to the list. + +# Contributors + +`EnTT` was written initially as a faster alternative to other well known and +open source entity-component systems. Nowadays this library is moving its first +steps. Much more will come in the future and hopefully I'm going to work on it +for a long time.
+Requests for features, PR, suggestions ad feedback are highly appreciated. + +If you find you can help me and want to contribute to the project with your +experience or you do want to get part of the project for some other reasons, +feel free to contact me directly (you can find the mail in the +[profile](https://github.com/skypjack)).
+I can't promise that each and every contribution will be accepted, but I can +assure that I'll do my best to take them all seriously. + +If you decide to participate, please see the guidelines for +[contributing](CONTRIBUTING.md) before to create issues or pull +requests.
+Take also a look at the +[contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to +know who has participated so far. + + +# License + +Code and documentation Copyright (c) 2017-2019 Michele Caini.
+Logo Copyright (c) 2018-2019 Richard Caseres. + +Code released under +[the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE). +Documentation released under +[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
+Logo released under +[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). + + +# Support + +## Patreon + +Become a [patron](https://www.patreon.com/bePatron?c=1772573) and get access to +extra content, help me spend more time on the projects you love and create new +ones for you. Your support will help me to continue the work done so far and +make it more professional and feature-rich every day.
+It takes very little to +[become a patron](https://www.patreon.com/bePatron?c=1772573) and thus help the +software you use every day. Don't miss the chance. + +## Donation + +Developing and maintaining `EnTT` takes some time and lots of coffee. I'd like +to add more and more functionalities in future and turn it in a full-featured +solution.
+If you want to support this project, you can offer me an espresso. I'm from +Italy, we're used to turning the best coffee ever in code. If you find that +it's not enough, feel free to support me the way you prefer.
+Take a look at the donation button at the top of the page for more details or +just click [here](https://www.paypal.me/skypjack). + +## Hire me + +If you start using `EnTT` and need help, if you want a new feature and want me +to give it the highest priority, if you have any other reason to contact me: +do not hesitate. I'm available for hiring.
+Feel free to take a look at my [profile](https://github.com/skypjack) and +contact me by mail. + diff --git a/modules/entt/TODO b/modules/entt/TODO new file mode 100644 index 0000000..ffb19fa --- /dev/null +++ b/modules/entt/TODO @@ -0,0 +1,25 @@ +* long term feature: templated generic vm +* long term feature: shared_ptr less locator +* long term feature: shared_ptr less resource cache +* custom allocators and EnTT allocator-aware in general (long term feature, I don't actually need it at the moment) - see #22 +* debugging tools (#60): the issue online already contains interesting tips on this, look at it +* runner proposal: https://en.wikipedia.org/wiki/Fork%E2%80%93join_model https://slide-rs.github.io/specs/03_dispatcher.html +* work stealing job system (see #100) +* meta: sort of meta view based on meta stuff to iterate entities, void * and meta info objects +* allow for built-in parallel each if possible +* allow to replace std:: with custom implementations +* remove runtime views, welcome reflection and what about snapshot? +* empty components model allows for shared components and prefabs unity-like + - each with entity return the shared component multiple times, one per entity that refers to it + - each components only return actual component, so shared components are returned only once +* types defined at runtime that refer to the same compile-time type (but to different pools) are possible, the library is almost there +* add take functionality, eg registry.take(entity, other); where it takes the entity and all its components from registry and move them to other +* add opaque input iterators to views and groups that return tuples (proxy), multi-pass guaranteed +* add fast lane for raw iterations, extend mt doc to describe allowed add/remove with pre-allocations on fast lanes +* review 64 bit id: user defined area + dedicated member on the registry to set it +* early out in views using bitmasks with bloom filter like access based on modulus + - standard each, use bitmask to speed up the whole thing and avoid accessing the pools to test for the page + - iterator based each with a couple of iterators passed from outside (use bitmask + has) +* stable component handle that isn't affected by reallocations +* multi component registry::remove and some others? +* reactive systems diff --git a/modules/entt/appveyor.yml b/modules/entt/appveyor.yml new file mode 100644 index 0000000..03e6493 --- /dev/null +++ b/modules/entt/appveyor.yml @@ -0,0 +1,25 @@ +# can use variables like {build} and {branch} +version: 1.0.{build} + +image: Visual Studio 2017 + +environment: + BUILD_DIR: "%APPVEYOR_BUILD_FOLDER%\\build" + +platform: + - Win32 + +configuration: + - Release + +before_build: + - cd %BUILD_DIR% + - cmake .. -DBUILD_TESTING=ON -DBUILD_LIB=ON -DCMAKE_CXX_FLAGS=/W1 -G"Visual Studio 15 2017" + +after_build: + - ctest -C Release -j4 + +build: + parallel: true + project: build/entt.sln + verbosity: minimal diff --git a/modules/entt/build/.gitignore b/modules/entt/build/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/modules/entt/build/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/modules/entt/cmake/in/EnTTBuildConfig.cmake.in b/modules/entt/cmake/in/EnTTBuildConfig.cmake.in new file mode 100644 index 0000000..4b6ad4f --- /dev/null +++ b/modules/entt/cmake/in/EnTTBuildConfig.cmake.in @@ -0,0 +1,6 @@ +set(ENTT_VERSION "@PROJECT_VERSION@") +set(ENTT_INCLUDE_DIRS "@CMAKE_CURRENT_SOURCE_DIR@/src") + +if(NOT CMAKE_VERSION VERSION_LESS "3.0") + include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake") +endif() diff --git a/modules/entt/cmake/in/EnTTConfig.cmake.in b/modules/entt/cmake/in/EnTTConfig.cmake.in new file mode 100644 index 0000000..069242b --- /dev/null +++ b/modules/entt/cmake/in/EnTTConfig.cmake.in @@ -0,0 +1,11 @@ +set(ENTT_VERSION "@PROJECT_VERSION@") + +@PACKAGE_INIT@ + +set_and_check(ENTT_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") + +if(NOT CMAKE_VERSION VERSION_LESS "3.0") + include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake") +endif() + +check_required_components("@PROJECT_NAME@") diff --git a/modules/entt/cmake/in/cereal.in b/modules/entt/cmake/in/cereal.in new file mode 100644 index 0000000..ef353b7 --- /dev/null +++ b/modules/entt/cmake/in/cereal.in @@ -0,0 +1,19 @@ +project(cereal-download NONE) +cmake_minimum_required(VERSION 3.2) + +include(ExternalProject) + +ExternalProject_Add( + cereal + GIT_REPOSITORY https://github.com/USCiLab/cereal.git + GIT_TAG v1.2.2 + DOWNLOAD_DIR ${CEREAL_DEPS_DIR} + TMP_DIR ${CEREAL_DEPS_DIR}/tmp + STAMP_DIR ${CEREAL_DEPS_DIR}/stamp + SOURCE_DIR ${CEREAL_DEPS_DIR}/src + BINARY_DIR ${CEREAL_DEPS_DIR}/build + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/modules/entt/cmake/in/duktape.in b/modules/entt/cmake/in/duktape.in new file mode 100644 index 0000000..98a00c7 --- /dev/null +++ b/modules/entt/cmake/in/duktape.in @@ -0,0 +1,19 @@ +project(duktape-download NONE) +cmake_minimum_required(VERSION 3.2) + +include(ExternalProject) + +ExternalProject_Add( + duktape + GIT_REPOSITORY https://github.com/svaarala/duktape-releases.git + GIT_TAG v2.2.0 + DOWNLOAD_DIR ${DUKTAPE_DEPS_DIR} + TMP_DIR ${DUKTAPE_DEPS_DIR}/tmp + STAMP_DIR ${DUKTAPE_DEPS_DIR}/stamp + SOURCE_DIR ${DUKTAPE_DEPS_DIR}/src + BINARY_DIR ${DUKTAPE_DEPS_DIR}/build + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/modules/entt/cmake/in/googletest.in b/modules/entt/cmake/in/googletest.in new file mode 100644 index 0000000..ab1f6e3 --- /dev/null +++ b/modules/entt/cmake/in/googletest.in @@ -0,0 +1,19 @@ +project(googletest-download NONE) +cmake_minimum_required(VERSION 3.2) + +include(ExternalProject) + +ExternalProject_Add( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + DOWNLOAD_DIR ${GOOGLETEST_DEPS_DIR} + TMP_DIR ${GOOGLETEST_DEPS_DIR}/tmp + STAMP_DIR ${GOOGLETEST_DEPS_DIR}/stamp + SOURCE_DIR ${GOOGLETEST_DEPS_DIR}/src + BINARY_DIR ${GOOGLETEST_DEPS_DIR}/build + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/modules/entt/cmake/in/version.h.in b/modules/entt/cmake/in/version.h.in new file mode 100644 index 0000000..f9c9fb5 --- /dev/null +++ b/modules/entt/cmake/in/version.h.in @@ -0,0 +1,11 @@ +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + + +#define ENTT_VERSION "@PROJECT_VERSION@" +#define ENTT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@ +#define ENTT_VERSION_MINOR @PROJECT_VERSION_MINOR@ +#define ENTT_VERSION_PATCH @PROJECT_VERSION_PATCH@ + + +#endif // ENTT_CONFIG_VERSION_H diff --git a/modules/entt/conan/build.py b/modules/entt/conan/build.py new file mode 100644 index 0000000..4b3bde8 --- /dev/null +++ b/modules/entt/conan/build.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from cpt.packager import ConanMultiPackager +import os + +if __name__ == "__main__": + login_username = os.getenv("CONAN_LOGIN_USERNAME") + username = os.getenv("CONAN_USERNAME") + tag_version = os.getenv("CONAN_PACKAGE_VERSION", os.getenv("TRAVIS_TAG")) + package_version = tag_version.replace("v", "") + package_name_unset = "SET-CONAN_PACKAGE_NAME-OR-CONAN_REFERENCE" + package_name = os.getenv("CONAN_PACKAGE_NAME", package_name_unset) + reference = "{}/{}".format(package_name, package_version) + channel = os.getenv("CONAN_CHANNEL", "stable") + upload = os.getenv("CONAN_UPLOAD") + stable_branch_pattern = os.getenv("CONAN_STABLE_BRANCH_PATTERN", r"v\d+\.\d+\.\d+.*") + test_folder = os.getenv("CPT_TEST_FOLDER", os.path.join("conan", "test_package")) + upload_only_when_stable = os.getenv("CONAN_UPLOAD_ONLY_WHEN_STABLE", True) + header_only = os.getenv("CONAN_HEADER_ONLY", False) + pure_c = os.getenv("CONAN_PURE_C", False) + + disable_shared = os.getenv("CONAN_DISABLE_SHARED_BUILD", "False") + if disable_shared == "True" and package_name == package_name_unset: + raise Exception("CONAN_DISABLE_SHARED_BUILD: True is only supported when you define CONAN_PACKAGE_NAME") + + builder = ConanMultiPackager(username=username, + reference=reference, + channel=channel, + login_username=login_username, + upload=upload, + stable_branch_pattern=stable_branch_pattern, + upload_only_when_stable=upload_only_when_stable, + test_folder=test_folder) + if header_only == "False": + builder.add_common_builds(pure_c=pure_c) + else: + builder.add() + + filtered_builds = [] + for settings, options, env_vars, build_requires, reference in builder.items: + if disable_shared == "False" or not options["{}:shared".format(package_name)]: + filtered_builds.append([settings, options, env_vars, build_requires]) + builder.builds = filtered_builds + + builder.run() diff --git a/modules/entt/conan/ci/build.sh b/modules/entt/conan/ci/build.sh new file mode 100644 index 0000000..d24e9b5 --- /dev/null +++ b/modules/entt/conan/ci/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e +set -x + +conan user +python conan/build.py diff --git a/modules/entt/conan/ci/install.sh b/modules/entt/conan/ci/install.sh new file mode 100644 index 0000000..7b54284 --- /dev/null +++ b/modules/entt/conan/ci/install.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e +set -x + +pip install -U conan_package_tools conan diff --git a/modules/entt/conan/test_package/CMakeLists.txt b/modules/entt/conan/test_package/CMakeLists.txt new file mode 100644 index 0000000..2e9fedb --- /dev/null +++ b/modules/entt/conan/test_package/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.7.2) +project(test_package) + +set(CMAKE_VERBOSE_MAKEFILE TRUE) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) +conan_basic_setup() + +add_executable(${PROJECT_NAME} test_package.cpp) +target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS}) diff --git a/modules/entt/conan/test_package/conanfile.py b/modules/entt/conan/test_package/conanfile.py new file mode 100644 index 0000000..d63f408 --- /dev/null +++ b/modules/entt/conan/test_package/conanfile.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from conans import ConanFile, CMake +import os + + +class TestPackageConan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "cmake" + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def test(self): + bin_path = os.path.join("bin", "test_package") + self.run(bin_path, run_environment=True) diff --git a/modules/entt/conan/test_package/test_package.cpp b/modules/entt/conan/test_package/test_package.cpp new file mode 100644 index 0000000..e0a14bc --- /dev/null +++ b/modules/entt/conan/test_package/test_package.cpp @@ -0,0 +1,56 @@ +#include +#include + +struct position { + float x; + float y; +}; + +struct velocity { + float dx; + float dy; +}; + +void update(entt::registry ®istry) { + auto view = registry.view(); + + for(auto entity: view) { + // gets only the components that are going to be used ... + + auto &vel = view.get(entity); + + vel.dx = 0.; + vel.dy = 0.; + + // ... + } +} + +void update(std::uint64_t dt, entt::registry ®istry) { + registry.view().each([dt](auto &pos, auto &vel) { + // gets all the components of the view at once ... + + pos.x += vel.dx * dt; + pos.y += vel.dy * dt; + + // ... + }); +} + +int main() { + entt::registry registry; + std::uint64_t dt = 16; + + for(auto i = 0; i < 10; ++i) { + auto entity = registry.create(); + registry.assign(entity, i * 1.f, i * 1.f); + if(i % 2 == 0) { registry.assign(entity, i * .1f, i * .1f); } + } + + update(dt, registry); + update(registry); + + // ... + + return EXIT_SUCCESS; +} diff --git a/modules/entt/conanfile.py b/modules/entt/conanfile.py new file mode 100644 index 0000000..08176d3 --- /dev/null +++ b/modules/entt/conanfile.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from conans import ConanFile + + +class EnttConan(ConanFile): + name = "entt" + description = "Gaming meets modern C++ - a fast and reliable entity-component system (ECS) and much more " + topics = ("conan," "entt", "gaming", "entity", "ecs") + url = "https://github.com/skypjack/entt" + homepage = url + author = "Michele Caini " + license = "MIT" + exports = ["LICENSE"] + exports_sources = ["src/*"] + no_copy_source = True + + def package(self): + self.copy(pattern="LICENSE", dst="licenses") + self.copy(pattern="*", dst="include", src="src", keep_path=True) + + def package_id(self): + self.info.header_only() diff --git a/modules/entt/deps/.gitignore b/modules/entt/deps/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/modules/entt/deps/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/modules/entt/docs/CMakeLists.txt b/modules/entt/docs/CMakeLists.txt new file mode 100644 index 0000000..8fae056 --- /dev/null +++ b/modules/entt/docs/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Doxygen configuration (documentation) +# + +set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src) +set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +configure_file(doxy.in doxy.cfg @ONLY) + +add_custom_target( + docs ALL + COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg + WORKING_DIRECTORY ${EnTT_SOURCE_DIR} + VERBATIM + SOURCES doxy.in +) + +install( + DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html + DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/ +) + +add_custom_target( + docs_aob + SOURCES + dox/extra.dox + md/core.md + md/entity.md + md/faq.md + md/lib.md + md/links.md + md/locator.md + md/meta.md + md/process.md + md/resource.md + md/signal.md +) diff --git a/modules/entt/docs/dox/extra.dox b/modules/entt/docs/dox/extra.dox new file mode 100644 index 0000000..9a53e29 --- /dev/null +++ b/modules/entt/docs/dox/extra.dox @@ -0,0 +1,5 @@ +/** + * @namespace entt + * + * @brief `EnTT` default namespace. + */ diff --git a/modules/entt/docs/doxy.in b/modules/entt/docs/doxy.in new file mode 100644 index 0000000..26badb2 --- /dev/null +++ b/modules/entt/docs/doxy.in @@ -0,0 +1,2446 @@ +# Doxyfile 1.8.13 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = @PROJECT_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @PROJECT_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @DOXY_OUTPUT_DIRECTORY@ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 4 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = YES + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @DOXY_SOURCE_DIRECTORY@ \ + @DOXY_DOCS_DIRECTORY@ \ + @PROJECT_SOURCE_DIR@/README.md + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.h \ + *.hpp \ + *.md \ + *.dox + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/README.md + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /