Merge pull request #1129 from lucianoiam/wavpack
Add WavPack audio files support
This commit is contained in:
commit
cfc39ca724
17 changed files with 267 additions and 74 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -13,3 +13,6 @@
|
|||
[submodule "external/simde"]
|
||||
path = external/simde
|
||||
url = https://github.com/simd-everywhere/simde.git
|
||||
[submodule "external/st_audiofile/thirdparty/wavpack"]
|
||||
path = external/st_audiofile/thirdparty/wavpack
|
||||
url = https://github.com/dbry/WavPack.git
|
||||
|
|
|
|||
39
common.mk
39
common.mk
|
|
@ -171,6 +171,45 @@ SFIZZ_CXX_FLAGS += \
|
|||
-I$(SFIZZ_DIR)/external/st_audiofile/thirdparty/dr_libs \
|
||||
-I$(SFIZZ_DIR)/external/st_audiofile/thirdparty/stb_vorbis
|
||||
|
||||
# Wavpack support
|
||||
SFIZZ_SOURCES += \
|
||||
external/st_audiofile/thirdparty/wavpack/src/common_utils.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/decorr_utils.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/entropy_utils.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/extra1.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/extra2.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/open_utils.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/open_filename.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/open_legacy.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/open_raw.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/pack.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/pack_dns.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/pack_floats.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/pack_utils.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/read_words.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/tags.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/tag_utils.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/unpack.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/unpack_floats.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/unpack_seek.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/unpack_utils.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/write_words.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/pack_dsd.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/unpack_dsd.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/unpack3.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/unpack3_open.c \
|
||||
external/st_audiofile/thirdparty/wavpack/src/unpack3_seek.c
|
||||
|
||||
SFIZZ_C_FLAGS += \
|
||||
-I$(SFIZZ_DIR)/external/st_audiofile/thirdparty/wavpack/include \
|
||||
-I$(SFIZZ_DIR)/external/st_audiofile/thirdparty/wavpack/src \
|
||||
-DENABLE_DSD -DENABLE_LEGACY
|
||||
|
||||
SFIZZ_CXX_FLAGS += \
|
||||
-I$(SFIZZ_DIR)/external/st_audiofile/thirdparty/wavpack/include \
|
||||
-I$(SFIZZ_DIR)/external/st_audiofile/thirdparty/wavpack/src \
|
||||
-DENABLE_DSD -DENABLE_LEGACY
|
||||
|
||||
ifeq ($(SFIZZ_USE_SNDFILE),1)
|
||||
SFIZZ_C_FLAGS += $(SFIZZ_SNDFILE_C_FLAGS) -DST_AUDIO_FILE_USE_SNDFILE=1
|
||||
SFIZZ_CXX_FLAGS += $(SFIZZ_SNDFILE_CXX_FLAGS) -DST_AUDIO_FILE_USE_SNDFILE=1
|
||||
|
|
|
|||
3
external/st_audiofile/CMakeLists.txt
vendored
3
external/st_audiofile/CMakeLists.txt
vendored
|
|
@ -22,6 +22,9 @@ target_include_directories(st_audiofile_formats
|
|||
add_subdirectory("thirdparty/libaiff" EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(st_audiofile_formats PUBLIC aiff::aiff)
|
||||
|
||||
add_subdirectory("thirdparty/wavpack" EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(st_audiofile_formats PUBLIC wavpack)
|
||||
|
||||
###
|
||||
if(NOT ST_AUDIO_FILE_USE_SNDFILE)
|
||||
target_link_libraries(st_audiofile PRIVATE st_audiofile_formats)
|
||||
|
|
|
|||
90
external/st_audiofile/src/st_audiofile.c
vendored
90
external/st_audiofile/src/st_audiofile.c
vendored
|
|
@ -9,6 +9,8 @@
|
|||
#include "st_audiofile_libs.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define WAVPACK_MEMORY_ASSUMED_VERSION 5
|
||||
|
||||
struct st_audio_file {
|
||||
int type;
|
||||
union {
|
||||
|
|
@ -17,12 +19,14 @@ struct st_audio_file {
|
|||
AIFF_Ref aiff;
|
||||
drmp3 *mp3;
|
||||
stb_vorbis* ogg;
|
||||
WavpackContext* wv;
|
||||
};
|
||||
|
||||
union {
|
||||
struct { uint32_t channels; float sample_rate; uint64_t frames; } aiff;
|
||||
struct { uint64_t frames; } mp3;
|
||||
struct { uint32_t channels; float sample_rate; uint64_t frames; } ogg;
|
||||
struct { uint32_t channels; float sample_rate; uint64_t frames; int bitrate; int mode; } wv;
|
||||
} cache;
|
||||
|
||||
union {
|
||||
|
|
@ -173,6 +177,25 @@ static st_audio_file* st_generic_open_file(const void* filename, int widepath)
|
|||
}
|
||||
}
|
||||
|
||||
// Try WV
|
||||
{
|
||||
af->wv =
|
||||
#if defined(_WIN32)
|
||||
WavpackOpenFileInput((const char*)filename, NULL, OPEN_FILE_UTF8, 0);
|
||||
#else
|
||||
WavpackOpenFileInput((const char*)filename, NULL, 0, 0);
|
||||
#endif
|
||||
if (af->wv) {
|
||||
af->cache.wv.channels = (uint32_t)WavpackGetNumChannels(af->wv);
|
||||
af->cache.wv.sample_rate = (float)WavpackGetSampleRate(af->wv);
|
||||
af->cache.wv.frames = (uint64_t)WavpackGetNumSamples64(af->wv);
|
||||
af->cache.wv.bitrate = WavpackGetBitsPerSample(af->wv);
|
||||
af->cache.wv.mode = WavpackGetMode(af->wv);
|
||||
af->type = st_audio_file_wv;
|
||||
return af;
|
||||
}
|
||||
}
|
||||
|
||||
free(af);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -278,6 +301,17 @@ st_audio_file* st_open_memory(const void* memory, size_t length)
|
|||
}
|
||||
}
|
||||
|
||||
// Try WV
|
||||
{
|
||||
af->wv =
|
||||
WavpackOpenRawDecoder((void*)memory, (int32_t)length, NULL, 0,
|
||||
WAVPACK_MEMORY_ASSUMED_VERSION, NULL, 0, 0);
|
||||
if (af->wv) {
|
||||
af->type = st_audio_file_wv;
|
||||
return af;
|
||||
}
|
||||
}
|
||||
|
||||
free(af);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -315,6 +349,9 @@ void st_close(st_audio_file* af)
|
|||
drmp3_uninit(af->mp3);
|
||||
free(af->mp3);
|
||||
break;
|
||||
case st_audio_file_wv:
|
||||
WavpackCloseFile(af->wv);
|
||||
break;
|
||||
}
|
||||
|
||||
free(af);
|
||||
|
|
@ -345,6 +382,9 @@ uint32_t st_get_channels(st_audio_file* af)
|
|||
case st_audio_file_mp3:
|
||||
channels = af->mp3->channels;
|
||||
break;
|
||||
case st_audio_file_wv:
|
||||
channels = af->cache.wv.channels;
|
||||
break;
|
||||
}
|
||||
|
||||
return channels;
|
||||
|
|
@ -370,6 +410,9 @@ float st_get_sample_rate(st_audio_file* af)
|
|||
case st_audio_file_mp3:
|
||||
sample_rate = af->mp3->sampleRate;
|
||||
break;
|
||||
case st_audio_file_wv:
|
||||
sample_rate = af->cache.wv.sample_rate;
|
||||
break;
|
||||
}
|
||||
|
||||
return sample_rate;
|
||||
|
|
@ -395,6 +438,9 @@ uint64_t st_get_frame_count(st_audio_file* af)
|
|||
case st_audio_file_mp3:
|
||||
frames = af->cache.mp3.frames;
|
||||
break;
|
||||
case st_audio_file_wv:
|
||||
frames = af->cache.wv.frames;
|
||||
break;
|
||||
}
|
||||
|
||||
return frames;
|
||||
|
|
@ -420,6 +466,9 @@ bool st_seek(st_audio_file* af, uint64_t frame)
|
|||
case st_audio_file_mp3:
|
||||
success = drmp3_seek_to_pcm_frame(af->mp3, frame);
|
||||
break;
|
||||
case st_audio_file_wv:
|
||||
success = WavpackSeekSample64(af->wv, (int64_t)frame);
|
||||
break;
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
@ -449,6 +498,25 @@ uint64_t st_read_s16(st_audio_file* af, int16_t* buffer, uint64_t count)
|
|||
case st_audio_file_mp3:
|
||||
count = drmp3_read_pcm_frames_s16(af->mp3, count, buffer);
|
||||
break;
|
||||
case st_audio_file_wv:
|
||||
{
|
||||
uint32_t channels = af->cache.wv.channels;
|
||||
int32_t* buf_i32 = (int32_t*)malloc(4 * channels * count);
|
||||
if (!buf_i32) {
|
||||
return 0;
|
||||
}
|
||||
count = channels * WavpackUnpackSamples(af->wv, buf_i32, (uint32_t)count);
|
||||
if (af->cache.wv.mode & MODE_FLOAT) {
|
||||
drwav_f32_to_s16((drwav_int16*)buffer, (float*)buf_i32, (size_t)count);
|
||||
} else {
|
||||
int d = af->cache.wv.bitrate - 16;
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
buffer[i] = (int16_t)(buf_i32[i] >> d);
|
||||
}
|
||||
}
|
||||
free(buf_i32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
|
@ -478,6 +546,28 @@ uint64_t st_read_f32(st_audio_file* af, float* buffer, uint64_t count)
|
|||
case st_audio_file_mp3:
|
||||
count = drmp3_read_pcm_frames_f32(af->mp3, count, buffer);
|
||||
break;
|
||||
case st_audio_file_wv:
|
||||
if (af->cache.wv.mode & MODE_FLOAT) {
|
||||
count = WavpackUnpackSamples(af->wv, (int32_t*)buffer, (uint32_t)count);
|
||||
} else {
|
||||
uint32_t channels = af->cache.wv.channels;
|
||||
int32_t* buf_i32 = (int32_t*)malloc(4 * channels * count);
|
||||
if (!buf_i32) {
|
||||
return 0;
|
||||
}
|
||||
count = channels * WavpackUnpackSamples(af->wv, buf_i32, (uint32_t)count);
|
||||
if (!(af->cache.wv.mode & MODE_FLOAT)) {
|
||||
if (af->cache.wv.bitrate < 32) {
|
||||
int d = 32 - af->cache.wv.bitrate;
|
||||
for (uint64_t i = 0; i < count; i++) {
|
||||
buf_i32[i] <<= d;
|
||||
}
|
||||
}
|
||||
drwav_s32_to_f32(buffer, (drwav_int32*)buf_i32, (size_t)count);
|
||||
}
|
||||
free(buf_i32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
|
|
|||
1
external/st_audiofile/src/st_audiofile.h
vendored
1
external/st_audiofile/src/st_audiofile.h
vendored
|
|
@ -31,6 +31,7 @@ typedef enum st_audio_file_type {
|
|||
st_audio_file_aiff,
|
||||
st_audio_file_ogg,
|
||||
st_audio_file_mp3,
|
||||
st_audio_file_wv,
|
||||
st_audio_file_other,
|
||||
} st_audio_file_type;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ const char* st_type_string(int type)
|
|||
case st_audio_file_mp3:
|
||||
type_string = "MP3";
|
||||
break;
|
||||
case st_audio_file_wv:
|
||||
type_string = "WV";
|
||||
break;
|
||||
case st_audio_file_other:
|
||||
type_string = "other";
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#endif
|
||||
#include "stb_vorbis.c"
|
||||
#include "libaiff/libaiff.h"
|
||||
#include "wavpack.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <wchar.h>
|
||||
|
|
|
|||
1
external/st_audiofile/thirdparty/wavpack
vendored
Submodule
1
external/st_audiofile/thirdparty/wavpack
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 36b08dbcb1de136e9ae477e9f1e2b57c958fff18
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
namespace sfz {
|
||||
|
||||
static const char* kRecognizedAudioExtensions[] = {
|
||||
".wav", ".flac", ".ogg", ".mp3", ".aif", ".aiff", ".aifc",
|
||||
".wav", ".flac", ".ogg", ".mp3", ".aif", ".aiff", ".aifc", ".wv",
|
||||
};
|
||||
|
||||
///
|
||||
|
|
|
|||
71
tests/AudioFilesT.cpp
Normal file
71
tests/AudioFilesT.cpp
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "AudioSpan.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "sfizz/Synth.h"
|
||||
#include "TestHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
using namespace Catch::literals;
|
||||
|
||||
TEST_CASE("[AudioFiles] No leakage on right")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) };
|
||||
std::vector<float> zeros(synth.getSamplesPerBlock(), 0);
|
||||
sfz::AudioSpan<float> span { buffer };
|
||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/wavpack.sfz", R"(
|
||||
<region> sample=kick.wav key=60 pan=-100
|
||||
)");
|
||||
synth.noteOn(0, 60, 127);
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE( numPlayingVoices(synth) == 1 );
|
||||
REQUIRE( approxEqual(span.getConstSpan(1), absl::MakeConstSpan(zeros), 1e-2f) );
|
||||
while (numPlayingVoices(synth) > 0) {
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE( approxEqual(span.getConstSpan(1), absl::MakeConstSpan(zeros), 1e-2f) );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioFiles] WavPack file")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) };
|
||||
sfz::AudioSpan<float> span { buffer };
|
||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/wavpack.sfz", R"(
|
||||
<region> sample=kick.wav key=60 pan=-100
|
||||
<region> sample=kick.wv key=60 pan=100
|
||||
)");
|
||||
synth.noteOn(0, 60, 127);
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE( numPlayingVoices(synth) == 2 );
|
||||
REQUIRE( approxEqual(span.getConstSpan(0), span.getConstSpan(1)) );
|
||||
while (numPlayingVoices(synth) > 0) {
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE( approxEqual(span.getConstSpan(0), span.getConstSpan(1)) );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[AudioFiles] Flac file")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) };
|
||||
sfz::AudioSpan<float> span { buffer };
|
||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/wavpack.sfz", R"(
|
||||
<region> sample=kick.wav key=60 pan=-100
|
||||
<region> sample=kick.flac key=60 pan=100
|
||||
)");
|
||||
synth.noteOn(0, 60, 127);
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE( numPlayingVoices(synth) == 2 );
|
||||
REQUIRE( approxEqual(span.getConstSpan(0), span.getConstSpan(1)) );
|
||||
while (numPlayingVoices(synth) > 0) {
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE( approxEqual(span.getConstSpan(0), span.getConstSpan(1)) );
|
||||
}
|
||||
}
|
||||
|
|
@ -57,6 +57,7 @@ set(SFIZZ_TEST_SOURCES
|
|||
MessagingT.cpp
|
||||
OversamplerT.cpp
|
||||
MemoryT.cpp
|
||||
AudioFilesT.cpp
|
||||
DataHelpers.h
|
||||
DataHelpers.cpp
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "sfizz/ModifierHelpers.h"
|
||||
#include "sfizz/Buffer.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include "TestHelpers.h"
|
||||
#include <absl/algorithm/container.h>
|
||||
#include <absl/types/span.h>
|
||||
#include <algorithm>
|
||||
|
|
@ -14,20 +15,6 @@
|
|||
#include <iostream>
|
||||
using namespace Catch::literals;
|
||||
|
||||
template <class Type>
|
||||
inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < rhs.size(); ++i)
|
||||
if (rhs[i] != Approx(lhs[i]).epsilon(eps)) {
|
||||
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto idModifier = [](float x) { return x; };
|
||||
const auto twiceModifier = [](float x) { return 2 * x; };
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "sfizz/simd/Common.h"
|
||||
#include "sfizz/SIMDHelpers.h"
|
||||
#include "sfizz/Panning.h"
|
||||
#include "TestHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include <absl/algorithm/container.h>
|
||||
#include <absl/types/span.h>
|
||||
|
|
@ -20,39 +21,9 @@ template <class T, std::size_t A = sfz::config::defaultAlignment>
|
|||
using aligned_vector = std::vector<T, jsl::aligned_allocator<T, A>>;
|
||||
|
||||
constexpr int bigBufferSize { 4095 };
|
||||
constexpr int medBufferSize { 127 };
|
||||
constexpr int medBufferSize { 1023 };
|
||||
constexpr float fillValue { 1.3f };
|
||||
|
||||
template <class Type>
|
||||
inline bool approxEqualMargin(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < rhs.size(); ++i)
|
||||
if (rhs[i] != Approx(lhs[i]).margin(eps)) {
|
||||
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < rhs.size(); ++i)
|
||||
if (rhs[i] != Approx(lhs[i]).epsilon(eps)) {
|
||||
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST_CASE("[Helpers] willAlign, prevAligned and unaligned tests")
|
||||
{
|
||||
aligned_vector<float, 32> array(16);
|
||||
|
|
@ -430,8 +401,8 @@ TEST_CASE("[Helpers] Linear Ramp (SIMD)")
|
|||
TEST_CASE("[Helpers] Linear Ramp (SIMD vs scalar)")
|
||||
{
|
||||
const float start { 0.0f };
|
||||
std::vector<float> outputScalar(bigBufferSize);
|
||||
std::vector<float> outputSIMD(bigBufferSize);
|
||||
std::vector<float> outputScalar(medBufferSize);
|
||||
std::vector<float> outputSIMD(medBufferSize);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
|
||||
sfz::linearRamp<float>(absl::MakeSpan(outputScalar), start, fillValue);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
|
||||
|
|
@ -442,8 +413,8 @@ TEST_CASE("[Helpers] Linear Ramp (SIMD vs scalar)")
|
|||
TEST_CASE("[Helpers] Linear Ramp unaligned (SIMD vs scalar)")
|
||||
{
|
||||
const float start { 0.0f };
|
||||
std::vector<float> outputScalar(bigBufferSize);
|
||||
std::vector<float> outputSIMD(bigBufferSize);
|
||||
std::vector<float> outputScalar(medBufferSize);
|
||||
std::vector<float> outputSIMD(medBufferSize);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, false);
|
||||
sfz::linearRamp<float>(absl::MakeSpan(outputScalar).subspan(1), start, fillValue);
|
||||
sfz::setSIMDOpStatus<float>(sfz::SIMDOps::linearRamp, true);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "sfizz/OnePoleFilter.h"
|
||||
#include "sfizz/Smoothers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include "TestHelpers.h"
|
||||
// #include "cnpy.h"
|
||||
// #include "ghc/fs_std.hpp"
|
||||
#include <absl/types/span.h>
|
||||
|
|
@ -16,21 +17,6 @@
|
|||
#include <iostream>
|
||||
using namespace Catch::literals;
|
||||
|
||||
template <class Type, size_t N>
|
||||
inline bool approxEqual(const std::array<Type, N>& lhs, const std::array<Type, N>& rhs, Type epsilon = 1e-3)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < rhs.size(); ++i)
|
||||
if (lhs[i] != Approx(rhs[i]).epsilon(epsilon)) {
|
||||
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Type, size_t N>
|
||||
void testFilter(const std::array<Type, N>& input, const std::array<Type, N>& expectedLow, const std::array<Type, N>& expectedHigh, Type gain)
|
||||
{
|
||||
|
|
@ -43,19 +29,19 @@ void testFilter(const std::array<Type, N>& input, const std::array<Type, N>& exp
|
|||
sfz::OnePoleFilter<Type> filter;
|
||||
filter.setGain(gain);
|
||||
filter.processLowpass(input, outputSpan);
|
||||
REQUIRE(approxEqual(output, expectedLow));
|
||||
REQUIRE(approxEqual(absl::MakeConstSpan(output), absl::MakeConstSpan(expectedLow)));
|
||||
|
||||
filter.reset();
|
||||
filter.processLowpass(input, outputSpan, gains);
|
||||
REQUIRE(approxEqual(output, expectedLow));
|
||||
REQUIRE(approxEqual(absl::MakeConstSpan(output), absl::MakeConstSpan(expectedLow)));
|
||||
|
||||
filter.reset();
|
||||
filter.processHighpass(input, outputSpan);
|
||||
REQUIRE(approxEqual(output, expectedHigh));
|
||||
REQUIRE(approxEqual(absl::MakeConstSpan(output), absl::MakeConstSpan(expectedHigh)));
|
||||
|
||||
filter.reset();
|
||||
filter.processHighpass(input, outputSpan, gains);
|
||||
REQUIRE(approxEqual(output, expectedHigh));
|
||||
REQUIRE(approxEqual(absl::MakeConstSpan(output), absl::MakeConstSpan(expectedHigh)));
|
||||
}
|
||||
|
||||
constexpr std::array<float, 64> floatInput01 = {
|
||||
|
|
|
|||
BIN
tests/TestFiles/kick.flac
Executable file
BIN
tests/TestFiles/kick.flac
Executable file
Binary file not shown.
BIN
tests/TestFiles/kick.wv
Normal file
BIN
tests/TestFiles/kick.wv
Normal file
Binary file not shown.
|
|
@ -12,6 +12,8 @@
|
|||
#include "sfizz/Messaging.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include "sfizz/modulations/ModKey.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
class RegionCCView {
|
||||
public:
|
||||
|
|
@ -148,18 +150,52 @@ std::string createDefaultGraph(std::vector<std::string> lines, int numRegions =
|
|||
std::string createModulationDotGraph(std::vector<std::string> lines);
|
||||
|
||||
template <class Type>
|
||||
inline bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
|
||||
bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
|
||||
{
|
||||
if (lhs.size() != rhs.size())
|
||||
return false;
|
||||
|
||||
bool different = false;
|
||||
for (size_t i = 0; i < rhs.size(); ++i)
|
||||
if (rhs[i] != Approx(lhs[i]).epsilon(eps)) {
|
||||
std::cerr << lhs[i] << " != " << rhs[i] << " at index " << i << '\n';
|
||||
return false;
|
||||
if (rhs[i] != Approx(lhs[i]).margin(eps)) {
|
||||
std::cerr << lhs[i] << " != " << rhs[i] << " (delta " << std::abs(rhs[i] - lhs[i]) << ") at index " << i << '\n';
|
||||
different = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
auto print_span = [](absl::Span<const Type> span) {
|
||||
if (span.empty()) {
|
||||
std::cerr << "{ }" << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr << "{ ";
|
||||
if (span.size() < 16) {
|
||||
for (unsigned i = 0; i < span.size() - 1; ++i) {
|
||||
std::cerr << span[i] << ", ";
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < 8; ++i) {
|
||||
std::cerr << span[i] << ", ";
|
||||
}
|
||||
std::cerr << "..., ";
|
||||
for (unsigned i = span.size() - 8; i < span.size() - 1; ++i) {
|
||||
std::cerr << span[i] << ", ";
|
||||
}
|
||||
}
|
||||
std::cerr << span.back() << " }" << '\n';
|
||||
};
|
||||
|
||||
if (different) {
|
||||
std::cerr << std::setprecision(3) << std::fixed;
|
||||
std::cerr << "Differences between spans" << '\n';
|
||||
std::cerr << "lhs: ";
|
||||
print_span(lhs);
|
||||
std::cerr << "rhs: ";
|
||||
print_span(rhs);
|
||||
}
|
||||
|
||||
return !different;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue