summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstefonzo <stevester.robinson@gmail.com>2022-09-05 20:17:22 -0500
committerstefonzo <stevester.robinson@gmail.com>2022-09-05 20:17:22 -0500
commit7dac325122067bd8b453c0ec60fc1a768bb6f934 (patch)
treeaab7173f2c19eacaf433f443d2fc53e198ce89d7
parent16431035f8e1c61e37adcceb8eab75e8d0dfed62 (diff)
adds libwav
-rw-r--r--3rdparty/libwav/.clang-format111
-rw-r--r--3rdparty/libwav/.gitignore41
-rw-r--r--3rdparty/libwav/CHANGELOG.md0
-rw-r--r--3rdparty/libwav/CMakeLists.txt116
-rw-r--r--3rdparty/libwav/LICENSE373
-rw-r--r--3rdparty/libwav/README.md46
-rw-r--r--3rdparty/libwav/cmake/Modules/wavTargetProperties.cmake55
-rw-r--r--3rdparty/libwav/cmake/wavConfig.cmake.in2
-rw-r--r--3rdparty/libwav/include/wav.h225
-rw-r--r--3rdparty/libwav/src/wav.c881
-rw-r--r--3rdparty/libwav/tests/write_f32/CMakeLists.txt13
-rw-r--r--3rdparty/libwav/tests/write_f32/main.c25
-rw-r--r--CMakeLists.txt3
13 files changed, 1890 insertions, 1 deletions
diff --git a/3rdparty/libwav/.clang-format b/3rdparty/libwav/.clang-format
new file mode 100644
index 0000000..9cb71b7
--- /dev/null
+++ b/3rdparty/libwav/.clang-format
@@ -0,0 +1,111 @@
+# vim: ft=yaml
+Language: Cpp
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: true
+AlignConsecutiveDeclarations: true
+AlignEscapedNewlines: Left
+AlignOperands: true
+AlignTrailingComments: true
+# AllowAllArgumentsOnNextLine: true
+# AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+# AllowShortLabelsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+# AllowShortLambdasOnASingleLine: Inline
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: MultiLine
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ # AfterCaseLabel: false
+ AfterClass: true
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: false
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: false
+BreakAfterJavaFieldAnnotations: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+BreakStringLiterals: true
+ColumnLimit: 0
+# CommentPragmas:
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+# DisableFormat: true
+# ExperimentalAutoDetectBinPacking
+FixNamespaceComments: true
+# ForEachMacros
+IncludeBlocks: Preserve
+# IncludeCategories
+# IncludeIsMainRegex
+IndentCaseLabels: true
+IndentPPDirectives: None
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+# JavaImportGroups: ['com.example', 'com', 'org']
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+# MacroBlockBegin:
+# MacroBlockEnd:
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+# NamespaceIndentation: Auto
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: false
+# PenaltyBreakAssignment
+# PenaltyBreakBeforeFirstCallParameter
+# PenaltyBreakComment
+# PenaltyBreakFirstLessLess
+# PenaltyBreakString
+# PenaltyBreakTemplateDeclaration
+# PenaltyExcessCharacter
+# PenaltyReturnTypeOnItsOwnLine
+PointerAlignment: Left
+# RawStringFormats:
+ReflowComments: true
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+# SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInContainerLiterals: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Auto
+# StatementMacros:
+TabWidth: 4
+UseTab: Never
diff --git a/3rdparty/libwav/.gitignore b/3rdparty/libwav/.gitignore
new file mode 100644
index 0000000..eb4e76f
--- /dev/null
+++ b/3rdparty/libwav/.gitignore
@@ -0,0 +1,41 @@
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+
+.ycm_extra_conf.py
+.ycm_extra_conf.pyc
+
+/build/
+
+.ccls
+.ccls-cache
diff --git a/3rdparty/libwav/CHANGELOG.md b/3rdparty/libwav/CHANGELOG.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/3rdparty/libwav/CHANGELOG.md
diff --git a/3rdparty/libwav/CMakeLists.txt b/3rdparty/libwav/CMakeLists.txt
new file mode 100644
index 0000000..c3faa46
--- /dev/null
+++ b/3rdparty/libwav/CMakeLists.txt
@@ -0,0 +1,116 @@
+cmake_minimum_required(VERSION 3.14)
+list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
+
+# Project Information
+project(wav VERSION "0.1.0")
+
+# CMake variables that affects building
+if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
+ if(CMAKE_BUILD_TYPE STREQUAL "")
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+ endif()
+
+ if(NOT DEFINED BUILD_SHARED_LIBS)
+ set(BUILD_SHARED_LIBS ON CACHE BOOL "enable building of shared libraries instead of static ones" FORCE)
+ endif()
+
+ if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "enable position independent code" FORCE)
+ endif()
+
+ include(CTest)
+ message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
+ message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
+ message(STATUS "CMAKE_POSITION_INDEPENDENT_CODE: ${CMAKE_POSITION_INDEPENDENT_CODE}")
+ message(STATUS "BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}")
+ message(STATUS "BUILD_TESTING: ${BUILD_TESTING}")
+endif()
+
+include(GNUInstallDirs)
+include(wavTargetProperties)
+
+add_library(${PROJECT_NAME} src/wav.c)
+add_library(wav::wav ALIAS wav)
+target_include_directories(${PROJECT_NAME}
+ PUBLIC
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:include>
+ PRIVATE
+ $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
+ )
+target_compile_features(${PROJECT_NAME} PRIVATE ${wav_compile_features})
+target_compile_definitions(${PROJECT_NAME} PRIVATE ${wav_compile_definitions})
+target_compile_options(${PROJECT_NAME} PRIVATE
+ ${wav_c_flags}
+ $<$<CONFIG:RELEASE>:${wav_compile_options_release}>
+ $<$<CONFIG:RELWITHDEBINFO>:${wav_compile_options_release}>
+ )
+
+if(BUILD_TESTING AND "${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
+ add_subdirectory(tests/write_f32)
+endif()
+
+export(TARGETS wav NAMESPACE wav FILE wavTargets.cmake)
+
+install(
+ TARGETS ${PROJECT_NAME}
+ EXPORT wavTargets
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ )
+
+export(PACKAGE wav)
+
+install(DIRECTORY include DESTINATION .)
+
+install(
+ FILES README.md CHANGELOG.md LICENSE
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/doc/${PROJECT_NAME}
+ )
+
+install(EXPORT wavTargets
+ FILE wavTargets.cmake
+ NAMESPACE wav::
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
+ )
+
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+ wavConfigVersion.cmake
+ VERSION ${PACKAGE_VERSION}
+ COMPATIBILITY SameMajorVersion
+ )
+
+configure_file(cmake/wavConfig.cmake.in wavConfig.cmake @ONLY)
+install(
+ FILES
+ "${CMAKE_CURRENT_BINARY_DIR}/wavConfig.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/wavConfigVersion.cmake"
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
+ )
+
+if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
+ set(CPACK_PACKAGE_NAME "wav")
+ set(CPACK_GENERATOR "TXZ")
+ set(CPACK_SOURCE_IGNORE_FILES
+ "/\\\\.git/"
+ "\\\\.git.*"
+ "/build/"
+ "/backup/"
+ "/cmake-build-.*/"
+ "/\\\\.idea/"
+ "/\\\\.ycm_extra_conf\\\\..*"
+ "/GPATH$"
+ "/GRTAGS$"
+ "/GSYMS$"
+ "/GTAGS$"
+ "\\\\.swp$"
+ "\\\\.swo$"
+ ".DS_Store"
+ ".ccls"
+ ".ccls-cache"
+ )
+ set(CPACK_SOURCE_GENERATOR "TXZ")
+ include(CPack)
+endif()
diff --git a/3rdparty/libwav/LICENSE b/3rdparty/libwav/LICENSE
new file mode 100644
index 0000000..14e2f77
--- /dev/null
+++ b/3rdparty/libwav/LICENSE
@@ -0,0 +1,373 @@
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+ means each individual or legal entity that creates, contributes to
+ the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+ means the combination of the Contributions of others (if any) used
+ by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+ means Source Code Form to which the initial Contributor has attached
+ the notice in Exhibit A, the Executable Form of such Source Code
+ Form, and Modifications of such Source Code Form, in each case
+ including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ (a) that the initial Contributor has attached the notice described
+ in Exhibit B to the Covered Software; or
+
+ (b) that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the
+ terms of a Secondary License.
+
+1.6. "Executable Form"
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+ means a work that combines Covered Software with other material, in
+ a separate file or files, that is not Covered Software.
+
+1.8. "License"
+ means this document.
+
+1.9. "Licensable"
+ means having the right to grant, to the maximum extent possible,
+ whether at the time of the initial grant or subsequently, any and
+ all of the rights conveyed by this License.
+
+1.10. "Modifications"
+ means any of the following:
+
+ (a) any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered
+ Software; or
+
+ (b) any new file in Source Code Form that contains any Covered
+ Software.
+
+1.11. "Patent Claims" of a Contributor
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the
+ License, by the making, using, selling, offering for sale, having
+ made, import, or transfer of either its Contributions or its
+ Contributor Version.
+
+1.12. "Secondary License"
+ means either the GNU General Public License, Version 2.0, the GNU
+ Lesser General Public License, Version 2.1, the GNU Affero General
+ Public License, Version 3.0, or any later versions of those
+ licenses.
+
+1.13. "Source Code Form"
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that
+ controls, is controlled by, or is under common control with You. For
+ purposes of this definition, "control" means (a) the power, direct
+ or indirect, to cause the direction or management of such entity,
+ whether by contract or otherwise, or (b) ownership of more than
+ fifty percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+ for sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+ or
+
+(b) for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+ Form, as described in Section 3.1, and You must inform recipients of
+ the Executable Form how they can obtain a copy of such Source Code
+ Form by reasonable means in a timely manner, at a charge no more
+ than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter
+ the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+* *
+* 6. Disclaimer of Warranty *
+* ------------------------- *
+* *
+* Covered Software is provided under this License on an "as is" *
+* basis, without warranty of any kind, either expressed, implied, or *
+* statutory, including, without limitation, warranties that the *
+* Covered Software is free of defects, merchantable, fit for a *
+* particular purpose or non-infringing. The entire risk as to the *
+* quality and performance of the Covered Software is with You. *
+* Should any Covered Software prove defective in any respect, You *
+* (not any Contributor) assume the cost of any necessary servicing, *
+* repair, or correction. This disclaimer of warranty constitutes an *
+* essential part of this License. No use of any Covered Software is *
+* authorized under this License except under this disclaimer. *
+* *
+************************************************************************
+
+************************************************************************
+* *
+* 7. Limitation of Liability *
+* -------------------------- *
+* *
+* Under no circumstances and under no legal theory, whether tort *
+* (including negligence), contract, or otherwise, shall any *
+* Contributor, or anyone who distributes Covered Software as *
+* permitted above, be liable to You for any direct, indirect, *
+* special, incidental, or consequential damages of any character *
+* including, without limitation, damages for lost profits, loss of *
+* goodwill, work stoppage, computer failure or malfunction, or any *
+* and all other commercial damages or losses, even if such party *
+* shall have been informed of the possibility of such damages. This *
+* limitation of liability shall not apply to liability for death or *
+* personal injury resulting from such party's negligence to the *
+* extent applicable law prohibits such limitation. Some *
+* jurisdictions do not allow the exclusion or limitation of *
+* incidental or consequential damages, so this exclusion and *
+* limitation may not apply to You. *
+* *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+ This Source Code Form is "Incompatible With Secondary Licenses", as
+ defined by the Mozilla Public License, v. 2.0.
diff --git a/3rdparty/libwav/README.md b/3rdparty/libwav/README.md
new file mode 100644
index 0000000..00560c5
--- /dev/null
+++ b/3rdparty/libwav/README.md
@@ -0,0 +1,46 @@
+# libwav
+
+libwav is a simple and tiny C library for reading or writing PCM wave (.wav)
+files.
+
+## Build and Install
+
+On Linux and macOS:
+
+ mkdir build
+ cd build
+ cmake [-DCMAKE_BUILD_TYPE=<Debug|Release|RelWithDebInfo>] ..
+ make
+ sudo make install
+
+On Windows:
+
+ mkdir build
+ cd build
+ cmake ..
+ cmake --build .
+
+## CMake Support
+
+Use `FetchContent`:
+
+ include(FetchContent)
+ FetchContent_Declare(libwav
+ GIT_REPOSITORY "https://github.com/brglng/libwav.git"
+ GIT_SHALLOW ON
+ )
+ FetchContent_MakeAvailable(libwav)
+ add_executable(yourprogram yourprogram.c)
+ target_link_libraries(yourprogram wav::wav)
+
+Use `add_subdirectory`:
+
+ add_subdirectory(libwav)
+ add_executable(yourprogram yourprogram.c)
+ target_link_libraries(yourprogram wav::wav)
+
+Use `find_package`:
+
+ find_package(wav)
+ add_executable(yourprogram yourprogram.c)
+ target_link_libraries(yourprogram wav::wav)
diff --git a/3rdparty/libwav/cmake/Modules/wavTargetProperties.cmake b/3rdparty/libwav/cmake/Modules/wavTargetProperties.cmake
new file mode 100644
index 0000000..21c5657
--- /dev/null
+++ b/3rdparty/libwav/cmake/Modules/wavTargetProperties.cmake
@@ -0,0 +1,55 @@
+set(wav_compile_features c_std_99)
+
+set(wav_compile_definitions
+ __STDC_FORMAT_MACROS
+ __STDC_LIMIT_MACROS
+ __STDC_CONSTANT_MACROS
+ )
+
+if(NOT DEFINED wav_c_flags)
+ set(wav_c_flags "")
+ include(CheckCCompilerFlag)
+ if(${CMAKE_C_COMPILER_ID} STREQUAL "MSVC")
+ elseif(${CMAKE_C_COMPILER_ID} MATCHES "^(GNU|.*Clang)$")
+ foreach(flag -fno-strict-aliasing
+ -Wall
+ -Wcast-align
+ -Wduplicated-branches
+ -Wduplicated-cond
+ -Wextra
+ -Wformat=2
+ -Wmissing-include-dirs
+ -Wnarrowing
+ -Wpointer-arith
+ -Wshadow
+ -Wuninitialized
+ -Wwrite-strings
+ -Wno-multichar
+ -Wno-format-nonliteral
+ -Wno-format-truncation
+ -Werror=discarded-qualifiers
+ -Werror=ignored-qualifiers
+ -Werror=implicit
+ -Werror=implicit-function-declaration
+ -Werror=implicit-int
+ -Werror=init-self
+ -Werror=incompatible-pointer-types
+ -Werror=int-conversion
+ -Werror=return-type
+ -Werror=strict-prototypes
+ )
+ check_c_compiler_flag(${flag} wav_has_c_flag_${flag})
+ if(wav_has_c_flag_${flag})
+ list(APPEND wav_c_flags ${flag})
+ endif()
+ endforeach()
+ endif()
+ set(wav_c_flags ${wav_c_flags} CACHE INTERNAL "C compiler flags")
+endif()
+
+if(${CMAKE_C_COMPILER_ID} STREQUAL "MSVC")
+elseif(${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
+ set(wav_compile_options_release -fomit-frame-pointer -march=native -mtune=native -fvisibility=hidden)
+elseif(${CMAKE_C_COMPILER_ID} MATCHES "^.*Clang$")
+ set(wav_compile_options_release -fomit-frame-pointer -fvisibility=hidden)
+endif()
diff --git a/3rdparty/libwav/cmake/wavConfig.cmake.in b/3rdparty/libwav/cmake/wavConfig.cmake.in
new file mode 100644
index 0000000..d120954
--- /dev/null
+++ b/3rdparty/libwav/cmake/wavConfig.cmake.in
@@ -0,0 +1,2 @@
+include(CMakeFindDependencyMacro)
+include("${CMAKE_CURRENT_LIST_DIR}/wavTargets.cmake")
diff --git a/3rdparty/libwav/include/wav.h b/3rdparty/libwav/include/wav.h
new file mode 100644
index 0000000..1178622
--- /dev/null
+++ b/3rdparty/libwav/include/wav.h
@@ -0,0 +1,225 @@
+/** Simple PCM wav file I/O library
+ *
+ * Author: Zhaosheng Pan <zhaosheng.pan@sololand.moe>
+ *
+ * The API is designed to be similar to stdio.
+ *
+ * This library does not support:
+ *
+ * - formats other than PCM, IEEE float and log-PCM
+ * - extra chunks after the data chunk
+ * - big endian platforms (might be supported in the future)
+ */
+
+#ifndef __WAV_H__
+#define __WAV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#if !defined(_MSC_VER) || _MSC_VER >= 1800
+#define WAV_INLINE static inline
+#define WAV_CONST const
+#define WAV_RESTRICT restrict
+#else
+#define WAV_INLINE static __inline
+#define WAV_CONST
+#define WAV_RESTRICT __restrict
+#endif
+
+#if defined(__APPLE__) || defined(_MSC_VER)
+typedef long long WavI64;
+typedef unsigned long long WavU64;
+typedef long long WavIntPtr;
+typedef unsigned long long WavUIntPtr;
+#else
+#if defined(_WIN64) || defined(__x86_64) || defined(__amd64)
+typedef long WavI64;
+typedef unsigned long WavU64;
+typedef long WavIntPtr;
+typedef unsigned long WavUIntPtr;
+#else
+typedef long long WavI64;
+typedef unsigned long long WavU64;
+typedef int WavIntPtr;
+typedef unsigned int WavUIntPtr;
+#endif
+#endif
+
+#if defined(__cplusplus) && __cplusplus >= 201103L
+#define WAV_THREAD_LOCAL thread_local
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+#define WAV_THREAD_LOCAL _Thread_local
+#elif defined(_MSC_VER)
+#define WAV_THREAD_LOCAL __declspec(thread)
+#else
+#define WAV_THREAD_LOCAL __thread
+#endif
+
+typedef int WavBool;
+typedef signed char WavI8;
+typedef unsigned char WavU8;
+typedef short WavI16;
+typedef unsigned short WavU16;
+typedef int WavI32;
+typedef unsigned int WavU32;
+
+enum {
+ WAV_FALSE,
+ WAV_TRUE
+};
+
+/* wave file format codes */
+#define WAV_FORMAT_PCM ((WavU16)0x0001)
+#define WAV_FORMAT_IEEE_FLOAT ((WavU16)0x0003)
+#define WAV_FORMAT_ALAW ((WavU16)0x0006)
+#define WAV_FORMAT_MULAW ((WavU16)0x0007)
+#define WAV_FORMAT_EXTENSIBLE ((WavU16)0xfffe)
+
+typedef enum {
+ WAV_OK, /** no error */
+ WAV_ERR_OS, /** error when {wave} called a stdio function */
+ WAV_ERR_FORMAT, /** not a wave file or unsupported wave format */
+ WAV_ERR_MODE, /** incorrect mode when opening the wave file or calling mode-specific API */
+ WAV_ERR_PARAM, /** incorrect parameter passed to the API function */
+} WavErrCode;
+
+typedef struct {
+ WavErrCode code;
+ char* message;
+ int _is_literal;
+} WavErr;
+
+typedef struct {
+ void* (*malloc)(void *context, size_t size);
+ void* (*realloc)(void *context, void *p, size_t size);
+ void (*free)(void *context, void *p);
+} WavAllocFuncs;
+
+void wav_set_allocator(void *context, WAV_CONST WavAllocFuncs *funcs);
+
+void* wav_malloc(size_t size);
+void* wav_realloc(void *p, size_t size);
+void wav_free(void *p);
+
+char* wav_strdup(WAV_CONST char *str);
+char* wav_strndup(WAV_CONST char *str, size_t n);
+int wav_vasprintf(char **str, WAV_CONST char *format, va_list args);
+int wav_asprintf(char **str, WAV_CONST char *format, ...);
+
+WAV_CONST WavErr* wav_err(void);
+void wav_err_clear(void);
+
+#define WAV_OPEN_READ 1
+#define WAV_OPEN_WRITE 2
+#define WAV_OPEN_APPEND 4
+
+typedef struct _WavFile WavFile;
+
+/** Open a wav file
+ *
+ * @param filename The name of the wav file
+ * @param mode The mode for open (same as {fopen})
+ * @return NULL if the memory allocation for the {WavFile} object failed. Non-NULL means the memory allocation succeeded, but there can be other errors, which can be obtained using {wav_errno} or {wav_error}.
+ */
+WavFile* wav_open(WAV_CONST char* filename, WavU32 mode);
+void wav_close(WavFile* self);
+WavFile* wav_reopen(WavFile* self, WAV_CONST char* filename, WavU32 mode);
+
+/** Read a block of samples from the wav file
+ *
+ * @param buffer A pointer to a buffer where the data will be placed
+ * @param count The number of frames (block size)
+ * @param self The pointer to the {WavFile} structure
+ * @return The number of frames read. If returned value is less than {count}, either EOF reached or an error occured
+ * @remarks This API does not support extensible format. For extensible format, use {wave_read_raw} instead.
+ */
+size_t wav_read(WavFile* self, void *buffer, size_t count);
+
+/** Write a block of samples to the wav file
+ *
+ * @param buffer A pointer to the buffer of data
+ * @param count The number of frames (block size)
+ * @param self The pointer to the {WavFile} structure
+ * @return The number of frames written. If returned value is less than {count}, either EOF reached or an error occured.
+ * @remarks This API does not support extensible format. For extensible format, use {wave_read_raw} instead.
+ */
+size_t wav_write(WavFile* self, WAV_CONST void *buffer, size_t count);
+
+/** Tell the current position in the wav file.
+ *
+ * @param self The pointer to the WavFile structure.
+ * @return The current frame index.
+ */
+long int wav_tell(WAV_CONST WavFile* self);
+
+int wav_seek(WavFile* self, long int offset, int origin);
+void wav_rewind(WavFile* self);
+
+/** Tell if the end of the wav file is reached.
+ *
+ * @param self The pointer to the WavFile structure.
+ * @return Non-zero integer if the end of the wav file is reached, otherwise zero.
+ */
+int wav_eof(WAV_CONST WavFile* self);
+
+int wav_flush(WavFile* self);
+
+/** Set the format code
+ *
+ * @param self The {WavFile} object
+ * @param format The format code, which should be one of `WAV_FORMAT_*`
+ * @remarks All data will be cleared after the call. {wav_errno} can be used to get the error code if there is an error.
+ */
+void wav_set_format(WavFile* self, WavU16 format);
+
+/** Set the number of channels
+ *
+ * @param self The {WavFile} object
+ * @param num_channels The number of channels
+ * @remarks All data will be cleared after the call. {wav_errno} can be used to get the error code if there is an error.
+ */
+void wav_set_num_channels(WavFile* self, WavU16 num_channels);
+
+/** Set the sample rate
+ *
+ * @param self The {WavFile} object
+ * @param sample_rate The sample rate
+ * @remarks All data will be cleared after the call. {wav_errno} can be used to get the error code if there is an error.
+ */
+void wav_set_sample_rate(WavFile* self, WavU32 sample_rate);
+
+/** Get the number of valid bits per sample
+ *
+ * @param self The {WavFile} object
+ * @param bits The value of valid bits to set
+ * @remarks If {bits} is 0 or larger than 8*{sample_size}, an error will occur. All data will be cleared after the call. {wav_errno} can be used to get the error code if there is an error.
+ */
+void wav_set_valid_bits_per_sample(WavFile* self, WavU16 bits);
+
+/** Set the size (in bytes) per sample
+ *
+ * @param self The WaveFile object
+ * @param sample_size Number of bytes per sample
+ * @remarks When this function is called, the {BitsPerSample} and {ValidBitsPerSample} fields in the wav file will be set to 8*{sample_size}. All data will be cleared after the call. {wav_errno} can be used to get the error code if there is an error.
+ */
+void wav_set_sample_size(WavFile* self, size_t sample_size);
+
+WavU16 wav_get_format(WAV_CONST WavFile* self);
+WavU16 wav_get_num_channels(WAV_CONST WavFile* self);
+WavU32 wav_get_sample_rate(WAV_CONST WavFile* self);
+WavU16 wav_get_valid_bits_per_sample(WAV_CONST WavFile* self);
+size_t wav_get_sample_size(WAV_CONST WavFile* self);
+size_t wav_get_length(WAV_CONST WavFile* self);
+WavU32 wav_get_channel_mask(WAV_CONST WavFile* self);
+WavU16 wav_get_sub_format(WAV_CONST WavFile* self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __WAV_H__ */
diff --git a/3rdparty/libwav/src/wav.c b/3rdparty/libwav/src/wav.c
new file mode 100644
index 0000000..a5d04dd
--- /dev/null
+++ b/3rdparty/libwav/src/wav.c
@@ -0,0 +1,881 @@
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "wav.h"
+
+#if defined(__x86_64) || defined(__amd64) || defined(__i386__) || defined(__x86_64__) || defined(__LITTLE_ENDIAN__) || defined(CORE_CM7)
+#define WAV_ENDIAN_LITTLE 1
+#elif defined(__BIG_ENDIAN__)
+#define WAV_ENDIAN_BIG 1
+#endif
+
+#if WAV_ENDIAN_LITTLE
+#define WAV_RIFF_CHUNK_ID ((WavU32)'FFIR')
+#define WAV_FORMAT_CHUNK_ID ((WavU32)' tmf')
+#define WAV_FACT_CHUNK_ID ((WavU32)'tcaf')
+#define WAV_DATA_CHUNK_ID ((WavU32)'atad')
+#define WAV_WAVE_ID ((WavU32)'EVAW')
+#endif
+
+#if WAV_ENDIAN_BIG
+#define WAV_RIFF_CHUNK_ID ((WavU32)'RIFF')
+#define WAV_FORMAT_CHUNK_ID ((WavU32)'fmt ')
+#define WAV_FACT_CHUNK_ID ((WavU32)'fact')
+#define WAV_DATA_CHUNK_ID ((WavU32)'data')
+#define WAV_WAVE_ID ((WavU32)'WAVE')
+#endif
+
+WAV_THREAD_LOCAL WavErr g_err = {WAV_OK, (char*)"", 1};
+
+static void* wav_default_malloc(void *context, size_t size)
+{
+ (void)context;
+ void *p = malloc(size);
+ assert(p != NULL);
+ return p;
+}
+
+static void* wav_default_realloc(void *context, void *p, size_t size)
+{
+ (void)context;
+ void *ptr = realloc(p, size);
+ assert(ptr != NULL);
+ return ptr;
+}
+
+static void wav_default_free(void *context, void *p)
+{
+ (void)context;
+ free(p);
+}
+
+static WavAllocFuncs g_default_alloc_funcs = {
+ &wav_default_malloc,
+ &wav_default_realloc,
+ &wav_default_free
+};
+
+static void *g_alloc_context = NULL;
+static WAV_CONST WavAllocFuncs* g_alloc_funcs = &g_default_alloc_funcs;
+
+void wav_set_allocator(void *context, WAV_CONST WavAllocFuncs *funcs)
+{
+ g_alloc_context = context;
+ g_alloc_funcs = funcs;
+}
+
+void* wav_malloc(size_t size)
+{
+ return g_alloc_funcs->malloc(g_alloc_context, size);
+}
+
+void* wav_realloc(void *p, size_t size)
+{
+ return g_alloc_funcs->realloc(g_alloc_context, p, size);
+}
+
+void wav_free(void *p)
+{
+ if (p != NULL) {
+ g_alloc_funcs->free(g_alloc_context, p);
+ }
+}
+
+char* wav_strdup(WAV_CONST char *str)
+{
+ size_t len = strlen(str) + 1;
+ void *new = wav_malloc(len);
+ if (new == NULL)
+ return NULL;
+
+ return memcpy(new, str, len);
+}
+
+char* wav_strndup(WAV_CONST char *str, size_t n)
+{
+ char *result = wav_malloc(n + 1);
+ if (result == NULL)
+ return NULL;
+
+ result[n] = 0;
+ return memcpy(result, str, n);
+}
+
+int wav_vasprintf(char **str, WAV_CONST char *format, va_list args)
+{
+ int size = 0;
+
+ va_list args_copy;
+ va_copy(args_copy, args);
+ size = vsnprintf(NULL, (size_t)size, format, args_copy);
+ va_end(args_copy);
+
+ if (size < 0) {
+ return size;
+ }
+
+ *str = wav_malloc((size_t)size + 1);
+ if (*str == NULL)
+ return -1;
+
+ return vsprintf(*str, format, args);
+}
+
+int wav_asprintf(char **str, WAV_CONST char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ int size = wav_vasprintf(str, format, args);
+ va_end(args);
+ return size;
+}
+
+WAV_CONST WavErr* wav_err(void)
+{
+ return &g_err;
+}
+
+void wav_err_clear(void)
+{
+ if (g_err.code != WAV_OK) {
+ if (!g_err._is_literal) {
+ wav_free(g_err.message);
+ }
+ g_err.code = WAV_OK;
+ g_err.message = (char*)"";
+ g_err._is_literal = 1;
+ }
+}
+
+WAV_INLINE void wav_err_set(WavErrCode code, WAV_CONST char *format, ...)
+{
+ assert(g_err.code == WAV_OK);
+ va_list args;
+ va_start(args, format);
+ g_err.code = code;
+ wav_vasprintf(&g_err.message, format, args);
+ g_err._is_literal = 0;
+ va_end(args);
+}
+
+WAV_INLINE void wav_err_set_literal(WavErrCode code, WAV_CONST char *message)
+{
+ assert(g_err.code == WAV_OK);
+ g_err.code = code;
+ g_err.message = (char *)message;
+ g_err._is_literal = 1;
+}
+
+#pragma pack(push, 1)
+
+typedef struct {
+ WavU32 id;
+ WavU32 size;
+} WavChunkHeader;
+
+typedef struct {
+ WavChunkHeader header;
+
+ WavU64 offset;
+
+ struct {
+ WavU16 format_tag;
+ WavU16 num_channels;
+ WavU32 sample_rate;
+ WavU32 avg_bytes_per_sec;
+ WavU16 block_align;
+ WavU16 bits_per_sample;
+
+ WavU16 ext_size;
+ WavU16 valid_bits_per_sample;
+ WavU32 channel_mask;
+
+ WavU8 sub_format[16];
+ } body;
+} WavFormatChunk;
+
+typedef struct {
+ WavChunkHeader header;
+
+ WavU64 offset;
+
+ struct {
+ WavU32 sample_length;
+ } body;
+} WavFactChunk;
+
+typedef struct {
+ WavChunkHeader header;
+ WavU64 offset;
+} WavDataChunk;
+
+typedef struct {
+ WavU32 id;
+ WavU32 size;
+ WavU32 wave_id;
+ WavU64 offset;
+} WavMasterChunk;
+
+#pragma pack(pop)
+
+#define WAV_CHUNK_MASTER ((WavU32)1)
+#define WAV_CHUNK_FORMAT ((WavU32)2)
+#define WAV_CHUNK_FACT ((WavU32)4)
+#define WAV_CHUNK_DATA ((WavU32)8)
+
+struct _WavFile {
+ FILE* fp;
+ char* filename;
+ WavU32 mode;
+ WavBool is_a_new_file;
+
+ WavMasterChunk riff_chunk;
+ WavFormatChunk format_chunk;
+ WavFactChunk fact_chunk;
+ WavDataChunk data_chunk;
+};
+
+static WAV_CONST WavU8 default_sub_format[16] = {
+ 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+};
+
+void wav_parse_header(WavFile* self)
+{
+ size_t read_count;
+
+ read_count = fread(&self->riff_chunk, sizeof(WavChunkHeader), 1, self->fp);
+ if (read_count != 1) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Unexpected EOF");
+ return;
+ }
+
+ if (self->riff_chunk.id != WAV_RIFF_CHUNK_ID) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Not a RIFF file");
+ return;
+ }
+
+ read_count = fread(&self->riff_chunk.wave_id, 4, 1, self->fp);
+ if (read_count != 1) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Unexpected EOF");
+ return;
+ }
+ if (self->riff_chunk.wave_id != WAV_WAVE_ID) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Not a WAVE file");
+ return;
+ }
+
+ self->riff_chunk.offset = (WavU64)ftell(self->fp);
+
+ while (self->data_chunk.header.id != WAV_DATA_CHUNK_ID) {
+ WavChunkHeader header;
+
+ read_count = fread(&header, sizeof(WavChunkHeader), 1, self->fp);
+ if (read_count != 1) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Unexpected EOF");
+ return;
+ }
+
+ switch (header.id) {
+ case WAV_FORMAT_CHUNK_ID:
+ self->format_chunk.header = header;
+ self->format_chunk.offset = (WavU64)ftell(self->fp);
+ read_count = fread(&self->format_chunk.body, header.size, 1, self->fp);
+ if (read_count != 1) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Unexpected EOF");
+ return;
+ }
+ if (self->format_chunk.body.format_tag != WAV_FORMAT_PCM &&
+ self->format_chunk.body.format_tag != WAV_FORMAT_IEEE_FLOAT &&
+ self->format_chunk.body.format_tag != WAV_FORMAT_ALAW &&
+ self->format_chunk.body.format_tag != WAV_FORMAT_MULAW)
+ {
+ wav_err_set(WAV_ERR_FORMAT, "Unsupported format tag: %#010x", self->format_chunk.body.format_tag);
+ return;
+ }
+ break;
+ case WAV_FACT_CHUNK_ID:
+ self->fact_chunk.header = header;
+ self->fact_chunk.offset = (WavU64)ftell(self->fp);
+ read_count = fread(&self->fact_chunk.body, header.size, 1, self->fp);
+ if (read_count != 1) {
+ wav_err_set(WAV_ERR_FORMAT, "Unexpected EOF");
+ }
+ break;
+ case WAV_DATA_CHUNK_ID:
+ self->data_chunk.header = header;
+ self->data_chunk.offset = (WavU64)ftell(self->fp);
+ break;
+ default:
+ if (fseek(self->fp, header.size, SEEK_CUR) < 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ break;
+ }
+ }
+}
+
+void wav_write_header(WavFile* self)
+{
+ self->riff_chunk.size =
+ sizeof(self->riff_chunk.wave_id) +
+ (self->format_chunk.header.id == WAV_FORMAT_CHUNK_ID ? (sizeof(WavChunkHeader) + self->format_chunk.header.size) : 0) +
+ (self->fact_chunk.header.id == WAV_FACT_CHUNK_ID ? (sizeof(WavChunkHeader) + self->fact_chunk.header.size) : 0) +
+ (self->data_chunk.header.id == WAV_DATA_CHUNK_ID ? (sizeof(WavChunkHeader) + self->data_chunk.header.size) : 0);
+
+ if (fseek(self->fp, 0, SEEK_SET) != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->riff_chunk, sizeof(WavChunkHeader) + 4, 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "Error while writing to %s [errno %d: %s]", self->filename, errno, strerror(errno));
+ return;
+ }
+
+ if (self->format_chunk.header.id == WAV_FORMAT_CHUNK_ID) {
+ if (fseek(self->fp, (long)(self->format_chunk.offset - sizeof(WavChunkHeader)), SEEK_SET) != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->format_chunk.header, sizeof(WavChunkHeader), 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "Error while writing to %s [errno %d: %s]", self->filename, errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->format_chunk.body, self->format_chunk.header.size, 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "Error while writing to %s [errno %d: %s]", self->filename, errno, strerror(errno));
+ return;
+ }
+ }
+
+ if (self->fact_chunk.header.id == WAV_FACT_CHUNK_ID) {
+ if (fseek(self->fp, (long)(self->fact_chunk.offset - sizeof(WavChunkHeader)), SEEK_SET) != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->fact_chunk.header, sizeof(WavChunkHeader), 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "Error while writing to %s [errno %d: %s]", self->filename, errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->fact_chunk.body, self->fact_chunk.header.size, 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "Error while writing to %s [errno %d: %s]", self->filename, errno, strerror(errno));
+ return;
+ }
+ }
+
+ if (self->data_chunk.header.id == WAV_DATA_CHUNK_ID) {
+ if (fseek(self->fp, (long)(self->data_chunk.offset - sizeof(WavChunkHeader)), SEEK_SET) != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->data_chunk.header, sizeof(WavChunkHeader), 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "Error while writing to %s [errno %d: %s]", self->filename, errno, strerror(errno));
+ return;
+ }
+ }
+}
+
+void wav_init(WavFile* self, WAV_CONST char* filename, WavU32 mode)
+{
+ memset(self, 0, sizeof(WavFile));
+
+ if (mode & WAV_OPEN_READ) {
+ if ((mode & WAV_OPEN_WRITE) || (mode & WAV_OPEN_APPEND)) {
+ self->fp = fopen(filename, "wb+");
+ } else {
+ self->fp = fopen(filename, "rb");
+ }
+ } else {
+ if ((mode & WAV_OPEN_WRITE) || (mode & WAV_OPEN_APPEND)) {
+ self->fp = fopen(filename, "wb+");
+ } else {
+ wav_err_set_literal(WAV_ERR_PARAM, "Invalid mode");
+ return;
+ }
+ }
+
+ if (self->fp == NULL) {
+ wav_err_set(WAV_ERR_OS, "Error when opening %s [errno %d: %s]", filename, errno, strerror(errno));
+ return;
+ }
+
+ self->filename = wav_strdup(filename);
+ self->mode = mode;
+
+ if (!(self->mode & WAV_OPEN_WRITE) && !(self->mode & WAV_OPEN_APPEND)) {
+ wav_parse_header(self);
+ return;
+ }
+
+ if (self->mode & WAV_OPEN_APPEND) {
+ wav_parse_header(self);
+ if (g_err.code == WAV_OK) {
+ // If the header parsing was successful, return immediately.
+ return;
+ } else {
+ // Header parsing failed. Regard it as a new file.
+ wav_err_clear();
+ rewind(self->fp);
+ self->is_a_new_file = WAV_TRUE;
+ }
+ }
+
+ // reaches here only if creating a new file
+
+ self->riff_chunk.id = WAV_RIFF_CHUNK_ID;
+ /* self->chunk.size = calculated by wav_write_header */
+ self->riff_chunk.wave_id = WAV_WAVE_ID;
+ self->riff_chunk.offset = sizeof(WavChunkHeader) + 4;
+
+ self->format_chunk.header.id = WAV_FORMAT_CHUNK_ID;
+ self->format_chunk.header.size = (WavU32)((WavUIntPtr)&self->format_chunk.body.ext_size - (WavUIntPtr)&self->format_chunk.body);
+ self->format_chunk.offset = self->riff_chunk.offset + sizeof(WavChunkHeader);
+ self->format_chunk.body.format_tag = WAV_FORMAT_PCM;
+ self->format_chunk.body.num_channels = 2;
+ self->format_chunk.body.sample_rate = 44100;
+ self->format_chunk.body.avg_bytes_per_sec = 44100 * 2 * 2;
+ self->format_chunk.body.block_align = 4;
+ self->format_chunk.body.bits_per_sample = 16;
+
+ memcpy(self->format_chunk.body.sub_format, default_sub_format, 16);
+
+ self->data_chunk.header.id = WAV_DATA_CHUNK_ID;
+ self->data_chunk.offset = self->format_chunk.offset + self->format_chunk.header.size + sizeof(WavChunkHeader);
+
+ wav_write_header(self);
+}
+
+void wav_finalize(WavFile* self)
+{
+ int ret;
+
+ wav_free(self->filename);
+
+ if (self->fp == NULL) {
+ return;
+ }
+
+ ret = fclose(self->fp);
+ if (ret != 0) {
+ fprintf(stderr, "[WARN] [libwav] fclose failed with code %d [errno %d: %s]", ret, errno, strerror(errno));
+ return;
+ }
+}
+
+WavFile* wav_open(WAV_CONST char* filename, WavU32 mode)
+{
+ WavFile* self = wav_malloc(sizeof(WavFile));
+ if (self == NULL) {
+ return NULL;
+ }
+
+ wav_init(self, filename, mode);
+
+ return self;
+}
+
+void wav_close(WavFile* self)
+{
+ wav_finalize(self);
+ wav_free(self);
+}
+
+WavFile* wav_reopen(WavFile* self, WAV_CONST char* filename, WavU32 mode)
+{
+ wav_finalize(self);
+ wav_init(self, filename, mode);
+ return self;
+}
+
+size_t wav_read(WavFile* self, void *buffer, size_t count)
+{
+ size_t read_count;
+ WavU16 n_channels = wav_get_num_channels(self);
+ size_t sample_size = wav_get_sample_size(self);
+ size_t len_remain;
+
+ if (!(self->mode & WAV_OPEN_READ)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not readable");
+ return 0;
+ }
+
+ if (self->format_chunk.body.format_tag == WAV_FORMAT_EXTENSIBLE) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Extensible format is not supported");
+ return 0;
+ }
+
+ len_remain = wav_get_length(self) - (size_t)wav_tell(self);
+ if (g_err.code != WAV_OK) {
+ return 0;
+ }
+ count = (count <= len_remain) ? count : len_remain;
+
+ if (count == 0) {
+ return 0;
+ }
+
+ read_count = fread(buffer, sample_size, n_channels * count, self->fp);
+ if (ferror(self->fp)) {
+ wav_err_set(WAV_ERR_OS, "Error when reading %s [errno %d: %s]", self->filename, errno, strerror(errno));
+ return 0;
+ }
+
+ return read_count / n_channels;
+}
+
+WAV_INLINE void wav_update_sizes(WavFile *self)
+{
+ long int save_pos = ftell(self->fp);
+ if (fseek(self->fp, (long)(sizeof(WavChunkHeader) - 4), SEEK_SET) != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->riff_chunk.size, 4, 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "fwrite() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (self->fact_chunk.header.id == WAV_FACT_CHUNK_ID) {
+ if (fseek(self->fp, (long)self->fact_chunk.offset, SEEK_SET) != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->fact_chunk.body.sample_length, 4, 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "fwrite() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ }
+ if (fseek(self->fp, (long)(self->data_chunk.offset - 4), SEEK_SET) != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (fwrite(&self->data_chunk.header.size, 4, 1, self->fp) != 1) {
+ wav_err_set(WAV_ERR_OS, "fwrite() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+ if (fseek(self->fp, save_pos, SEEK_SET) != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return;
+ }
+}
+
+size_t wav_write(WavFile* self, WAV_CONST void *buffer, size_t count)
+{
+ size_t write_count;
+ WavU16 n_channels = wav_get_num_channels(self);
+ size_t sample_size = wav_get_sample_size(self);
+
+ if (!(self->mode & WAV_OPEN_WRITE) && !(self->mode & WAV_OPEN_APPEND)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not writable");
+ return 0;
+ }
+
+ if (self->format_chunk.body.format_tag == WAV_FORMAT_EXTENSIBLE) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Extensible format is not supported");
+ return 0;
+ }
+
+ if (count == 0) {
+ return 0;
+ }
+
+ wav_tell(self);
+ if (g_err.code != WAV_OK) {
+ return 0;
+ }
+
+ if (!(self->mode & WAV_OPEN_READ) && !(self->mode & WAV_OPEN_WRITE)) {
+ wav_seek(self, 0, SEEK_END);
+ if (g_err.code != WAV_OK) {
+ return 0;
+ }
+ }
+
+ write_count = fwrite(buffer, sample_size, n_channels * count, self->fp);
+ if (ferror(self->fp)) {
+ wav_err_set(WAV_ERR_OS, "Error when writing to %s [errno %d: %s]", self->filename, errno, strerror(errno));
+ return 0;
+ }
+
+ self->riff_chunk.size += write_count * sample_size;
+ if (self->fact_chunk.header.id == WAV_FACT_CHUNK_ID) {
+ self->fact_chunk.body.sample_length += write_count / n_channels;
+ }
+ self->data_chunk.header.size += write_count * sample_size;
+
+ wav_update_sizes(self);
+ if (g_err.code != WAV_OK)
+ return 0;
+
+ return write_count / n_channels;
+}
+
+long int wav_tell(WAV_CONST WavFile* self)
+{
+ long pos = ftell(self->fp);
+
+ if (pos == -1L) {
+ wav_err_set(WAV_ERR_OS, "ftell() failed [errno %d: %s]", errno, strerror(errno));
+ return -1L;
+ }
+
+ assert(pos >= (long)self->data_chunk.offset);
+
+ return (long)(((WavU64)pos - self->data_chunk.offset) / (self->format_chunk.body.block_align));
+}
+
+int wav_seek(WavFile* self, long int offset, int origin)
+{
+ size_t length = wav_get_length(self);
+ int ret;
+
+ if (origin == SEEK_CUR) {
+ offset += (long)wav_tell(self);
+ } else if (origin == SEEK_END) {
+ offset += (long)length;
+ }
+
+ /* POSIX allows seeking beyond end of file */
+ if (offset >= 0) {
+ offset *= self->format_chunk.body.block_align;
+ } else {
+ wav_err_set_literal(WAV_ERR_PARAM, "Invalid seek");
+ return (int)g_err.code;
+ }
+
+ ret = fseek(self->fp, (long)self->data_chunk.offset + offset, SEEK_SET);
+
+ if (ret != 0) {
+ wav_err_set(WAV_ERR_OS, "fseek() failed [errno %d: %s]", errno, strerror(errno));
+ return (int)ret;
+ }
+
+ return 0;
+}
+
+void wav_rewind(WavFile* self)
+{
+ wav_seek(self, 0, SEEK_SET);
+}
+
+int wav_eof(WAV_CONST WavFile* self)
+{
+ return feof(self->fp) || ftell(self->fp) == (long)(self->data_chunk.offset + self->data_chunk.header.size);
+}
+
+int wav_flush(WavFile* self)
+{
+ int ret = fflush(self->fp);
+
+ if (ret != 0) {
+ wav_err_set(WAV_ERR_OS, "fflush() failed [errno %d: %s]", errno, strerror(errno));
+ }
+
+ return ret;
+}
+
+void wav_set_format(WavFile* self, WavU16 format)
+{
+ if (!(self->mode & WAV_OPEN_WRITE) && !((self->mode & WAV_OPEN_APPEND) && self->is_a_new_file && self->data_chunk.header.size == 0)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not writable");
+ return;
+ }
+
+ if (format == self->format_chunk.body.format_tag)
+ return;
+
+ self->format_chunk.body.format_tag = format;
+ if (format != WAV_FORMAT_PCM && format != WAV_FORMAT_EXTENSIBLE) {
+ self->format_chunk.body.ext_size = 0;
+ self->format_chunk.header.size = (WavU32)((WavUIntPtr)&self->format_chunk.body.ext_size - (WavUIntPtr)&self->format_chunk.body);
+ } else if (format == WAV_FORMAT_EXTENSIBLE) {
+ self->format_chunk.body.ext_size = 22;
+ self->format_chunk.header.size = sizeof(WavFormatChunk) - sizeof(WavChunkHeader);
+ }
+
+ if (format == WAV_FORMAT_ALAW || format == WAV_FORMAT_MULAW) {
+ WavU16 sample_size = wav_get_sample_size(self);
+ if (sample_size != 1) {
+ wav_set_sample_size(self, 1);
+ }
+ } else if (format == WAV_FORMAT_IEEE_FLOAT) {
+ WavU16 sample_size = wav_get_sample_size(self);
+ if (sample_size != 4 && sample_size != 8) {
+ wav_set_sample_size(self, 4);
+ }
+ }
+
+ wav_write_header(self);
+}
+
+void wav_set_num_channels(WavFile* self, WavU16 num_channels)
+{
+ if (!(self->mode & WAV_OPEN_WRITE) && !((self->mode & WAV_OPEN_APPEND) && self->is_a_new_file && self->data_chunk.header.size == 0)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not writable");
+ return;
+ }
+
+ if (num_channels < 1) {
+ wav_err_set(WAV_ERR_PARAM, "Invalid number of channels: %u", num_channels);
+ return;
+ }
+
+ WavU16 old_num_channels = self->format_chunk.body.num_channels;
+ if (num_channels == old_num_channels)
+ return;
+
+ self->format_chunk.body.num_channels = num_channels;
+ self->format_chunk.body.block_align = self->format_chunk.body.block_align / old_num_channels * num_channels;
+ self->format_chunk.body.avg_bytes_per_sec = self->format_chunk.body.block_align * self->format_chunk.body.sample_rate;
+
+ wav_write_header(self);
+}
+
+void wav_set_sample_rate(WavFile* self, WavU32 sample_rate)
+{
+ if (!(self->mode & WAV_OPEN_WRITE) && !((self->mode & WAV_OPEN_APPEND) && self->is_a_new_file && self->data_chunk.header.size == 0)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not writable");
+ return;
+ }
+
+ if (sample_rate == self->format_chunk.body.sample_rate)
+ return;
+
+ self->format_chunk.body.sample_rate = sample_rate;
+ self->format_chunk.body.avg_bytes_per_sec = self->format_chunk.body.block_align * self->format_chunk.body.sample_rate;
+
+ wav_write_header(self);
+}
+
+void wav_set_valid_bits_per_sample(WavFile* self, WavU16 bits)
+{
+ if (!(self->mode & WAV_OPEN_WRITE) && !((self->mode & WAV_OPEN_APPEND) && self->is_a_new_file && self->data_chunk.header.size == 0)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not writable");
+ return;
+ }
+
+ if (bits < 1 || bits > 8 * self->format_chunk.body.block_align / self->format_chunk.body.num_channels) {
+ wav_err_set(WAV_ERR_PARAM, "Invalid ValidBitsPerSample: %u", bits);
+ return;
+ }
+
+ if ((self->format_chunk.body.format_tag == WAV_FORMAT_ALAW || self->format_chunk.body.format_tag == WAV_FORMAT_MULAW) && bits != 8) {
+ wav_err_set(WAV_ERR_PARAM, "Invalid ValidBitsPerSample: %u", bits);
+ return;
+ }
+
+ if (self->format_chunk.body.format_tag != WAV_FORMAT_EXTENSIBLE) {
+ self->format_chunk.body.bits_per_sample = bits;
+ } else {
+ self->format_chunk.body.bits_per_sample = 8 * self->format_chunk.body.block_align / self->format_chunk.body.num_channels;
+ self->format_chunk.body.valid_bits_per_sample = bits;
+ }
+
+ wav_write_header(self);
+}
+
+void wav_set_sample_size(WavFile* self, size_t sample_size)
+{
+ if (!(self->mode & WAV_OPEN_WRITE) && !((self->mode & WAV_OPEN_APPEND) && self->is_a_new_file && self->data_chunk.header.size == 0)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not writable");
+ return;
+ }
+
+ if (sample_size < 1) {
+ wav_err_set(WAV_ERR_PARAM, "Invalid sample size: %zu", sample_size);
+ return;
+ }
+
+ self->format_chunk.body.block_align = (WavU16)(sample_size * self->format_chunk.body.num_channels);
+ self->format_chunk.body.avg_bytes_per_sec = self->format_chunk.body.block_align * self->format_chunk.body.sample_rate;
+ self->format_chunk.body.bits_per_sample = (WavU16)(sample_size * 8);
+ if (self->format_chunk.body.format_tag == WAV_FORMAT_EXTENSIBLE) {
+ self->format_chunk.body.valid_bits_per_sample = (WavU16)(sample_size * 8);
+ }
+
+ wav_write_header(self);
+}
+
+void wav_set_channel_mask(WavFile* self, WavU32 channel_mask)
+{
+ if (!(self->mode & WAV_OPEN_WRITE) && !((self->mode & WAV_OPEN_APPEND) && self->is_a_new_file && self->data_chunk.header.size == 0)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not writable");
+ return;
+ }
+
+ if (self->format_chunk.body.format_tag != WAV_FORMAT_EXTENSIBLE) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Extensible format is not supported");
+ return;
+ }
+
+ self->format_chunk.body.channel_mask = channel_mask;
+
+ wav_write_header(self);
+}
+
+void wav_set_sub_format(WavFile* self, WavU16 sub_format)
+{
+ if (!(self->mode & WAV_OPEN_WRITE) && !((self->mode & WAV_OPEN_APPEND) && self->is_a_new_file && self->data_chunk.header.size == 0)) {
+ wav_err_set_literal(WAV_ERR_MODE, "This WavFile is not writable");
+ return;
+ }
+
+ if (self->format_chunk.body.format_tag != WAV_FORMAT_EXTENSIBLE) {
+ wav_err_set_literal(WAV_ERR_FORMAT, "Extensible format is not supported");
+ return;
+ }
+
+ self->format_chunk.body.sub_format[0] = (WavU8)(sub_format & 0xff);
+ self->format_chunk.body.sub_format[1] = (WavU8)(sub_format >> 8);
+
+ wav_write_header(self);
+}
+
+WavU16 wav_get_format(WAV_CONST WavFile* self)
+{
+ return self->format_chunk.body.format_tag;
+}
+
+WavU16 wav_get_num_channels(WAV_CONST WavFile* self)
+{
+ return self->format_chunk.body.num_channels;
+}
+
+WavU32 wav_get_sample_rate(WAV_CONST WavFile* self)
+{
+ return self->format_chunk.body.sample_rate;
+}
+
+WavU16 wav_get_valid_bits_per_sample(WAV_CONST WavFile* self)
+{
+ if (self->format_chunk.body.format_tag != WAV_FORMAT_EXTENSIBLE) {
+ return self->format_chunk.body.bits_per_sample;
+ } else {
+ return self->format_chunk.body.valid_bits_per_sample;
+ }
+}
+
+size_t wav_get_sample_size(WAV_CONST WavFile* self)
+{
+ return self->format_chunk.body.block_align / self->format_chunk.body.num_channels;
+}
+
+size_t wav_get_length(WAV_CONST WavFile* self)
+{
+ return self->data_chunk.header.size / (self->format_chunk.body.block_align);
+}
+
+WavU32 wav_get_channel_mask(WAV_CONST WavFile* self)
+{
+ return self->format_chunk.body.channel_mask;
+}
+
+WavU16 wav_get_sub_format(WAV_CONST WavFile* self)
+{
+ WavU16 sub_format = self->format_chunk.body.sub_format[1];
+ sub_format <<= 8;
+ sub_format |= self->format_chunk.body.sub_format[0];
+ return sub_format;
+}
diff --git a/3rdparty/libwav/tests/write_f32/CMakeLists.txt b/3rdparty/libwav/tests/write_f32/CMakeLists.txt
new file mode 100644
index 0000000..cca1ecf
--- /dev/null
+++ b/3rdparty/libwav/tests/write_f32/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_executable(write-f32 main.c)
+target_link_libraries(write-f32
+ wav::wav
+ $<$<PLATFORM_ID:Linux>:m>
+ )
+target_include_directories(write-f32 PRIVATE ${CMAKE_SOURCE_DIR}/include)
+target_compile_features(write-f32 PRIVATE ${wav_compile_features})
+target_compile_definitions(write-f32 PRIVATE ${wav_compile_definitions})
+target_compile_options(write-f32 PRIVATE
+ ${wav_c_flags}
+ $<$<CONFIG:RELEASE>:${wav_compile_options_release}>
+ $<$<CONFIG:RELWITHDEBINFO>:${wav_compile_options_release}>
+ )
diff --git a/3rdparty/libwav/tests/write_f32/main.c b/3rdparty/libwav/tests/write_f32/main.c
new file mode 100644
index 0000000..14f4f33
--- /dev/null
+++ b/3rdparty/libwav/tests/write_f32/main.c
@@ -0,0 +1,25 @@
+#include <math.h>
+#include <stdlib.h>
+#include "wav.h"
+
+void generate_sine_wave(float *x, int sample_rate, int len)
+{
+ for (int i = 0; i < len; ++i) {
+ x[i] = 0.5f * cosf(2 * 3.14159265358979323f * 440.0f * i / sample_rate);
+ }
+}
+
+int main(void)
+{
+ float *buf = malloc(sizeof(float) * 10 * 44100);
+ generate_sine_wave(buf, 44100, 10 * 44100);
+ WavFile *fp = wav_open("out.wav", WAV_OPEN_WRITE);
+ wav_set_format(fp, WAV_FORMAT_IEEE_FLOAT);
+ /* wav_set_sample_size(fp, sizeof(float)); */
+ wav_set_num_channels(fp, 1);
+ wav_set_sample_rate(fp, 44100);
+ wav_write(fp, buf, 10 * 44100);
+ wav_close(fp);
+ free(buf);
+ return 0;
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 16b27d1..e223470 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@ option(MOSSROSE_BUILD_TESTS "Build the tests" OFF)
######## third-party libraries ########
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/portaudio EXCLUDE_FROM_ALL)
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/plibsys EXCLUDE_FROM_ALL)
+add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/libwav EXCLUDE_FROM_ALL)
add_library(mossrose)
set_target_properties(mossrose PROPERTIES
@@ -21,7 +22,7 @@ set_target_properties(mossrose PROPERTIES
VERSION ${PROJECT_VERSION}
PUBLIC_HEADER src/mossrose.h
)
-target_link_libraries(mossrose portaudio plibsys m)
+target_link_libraries(mossrose portaudio plibsys wav::wav m)
if (MOSSROSE_BUILD_EXAMPLES)