2020-01-25 10:04:31 +01:00
|
|
|
// SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
|
|
2020-01-25 13:13:07 +01:00
|
|
|
// 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
|
2019-08-30 00:49:58 +02:00
|
|
|
|
2020-08-10 17:22:49 +02:00
|
|
|
#include "TestHelpers.h"
|
2020-01-18 17:35:27 +01:00
|
|
|
#include "sfizz/Synth.h"
|
2020-11-01 09:00:22 +01:00
|
|
|
#include "sfizz/Voice.h"
|
2020-03-25 23:25:32 +01:00
|
|
|
#include "sfizz/SfzHelpers.h"
|
2021-04-25 05:31:26 +02:00
|
|
|
#include "sfizz/parser/Parser.h"
|
2020-07-27 17:32:25 +02:00
|
|
|
#include "sfizz/modulations/ModId.h"
|
|
|
|
|
#include "sfizz/modulations/ModKey.h"
|
2019-08-25 14:01:03 +02:00
|
|
|
#include "catch2/catch.hpp"
|
2019-11-23 19:10:37 +01:00
|
|
|
#include "ghc/fs_std.hpp"
|
2020-04-13 17:14:04 +02:00
|
|
|
#if defined(__APPLE__)
|
|
|
|
|
#include <unistd.h> // pathconf
|
|
|
|
|
#endif
|
2019-07-29 02:13:03 +02:00
|
|
|
using namespace Catch::literals;
|
2020-03-25 23:25:32 +01:00
|
|
|
using namespace sfz::literals;
|
2020-03-31 20:13:13 +02:00
|
|
|
using namespace sfz;
|
2019-07-29 02:13:03 +02:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Single region (regions_one.sfz)")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_one.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-29 16:56:56 +02:00
|
|
|
|
2019-07-29 02:13:03 +02:00
|
|
|
TEST_CASE("[Files] Multiple regions (regions_many.sfz)")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_many.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 3);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "dummy.1.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->sampleId->filename() == "dummy.2.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(2, 14));
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2021-01-03 23:29:33 +01:00
|
|
|
REQUIRE(synth.getRegionView(0)->loopMode == LoopMode::loop_sustain);
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-04 16:33:02 +01:00
|
|
|
TEST_CASE("[Files] (regions_bad.sfz)")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2020-01-04 16:33:02 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_bad.sfz");
|
|
|
|
|
REQUIRE(synth.getNumRegions() == 2);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "dummy.wav");
|
2020-01-04 16:33:02 +01:00
|
|
|
}
|
|
|
|
|
|
2019-07-29 02:13:03 +02:00
|
|
|
TEST_CASE("[Files] Local include")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_local.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Multiple includes")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 2);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "dummy2.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Multiple includes with comments")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 2);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "dummy2.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Subdir include")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy_subdir.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Subdir include Win")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy_subdir.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-07 23:39:35 +02:00
|
|
|
TEST_CASE("[Files] Recursive include (with include guard)")
|
2019-07-29 02:13:03 +02:00
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
|
|
|
|
Parser& parser = synth.getParser();
|
2020-03-17 22:14:35 +01:00
|
|
|
parser.setRecursiveIncludeGuardEnabled(true);
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_recursive.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 2);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy_recursive2.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "dummy_recursive1.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-07 23:39:35 +02:00
|
|
|
TEST_CASE("[Files] Include loops (with include guard)")
|
2019-07-29 02:13:03 +02:00
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
|
|
|
|
Parser& parser = synth.getParser();
|
2020-03-17 22:14:35 +01:00
|
|
|
parser.setRecursiveIncludeGuardEnabled(true);
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_loop.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 2);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy_loop2.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "dummy_loop1.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Define test")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/defines.sfz");
|
2019-12-17 15:24:49 +01:00
|
|
|
REQUIRE(synth.getNumRegions() == 4);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(36, 36));
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(38, 38));
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(42, 42));
|
2019-12-17 15:24:49 +01:00
|
|
|
REQUIRE(synth.getRegionView(3)->volume == -12.0f);
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Group from AVL")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 5);
|
|
|
|
|
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
|
|
|
|
REQUIRE(synth.getRegionView(i)->volume == 6.0f);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(36, 36));
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
2021-02-04 11:02:09 +01:00
|
|
|
|
2021-05-20 20:18:52 +02:00
|
|
|
almostEqualRanges(synth.getRegionView(0)->velocityRange, { 1_norm, std::nextafter(27_norm, 0.0f) });
|
|
|
|
|
almostEqualRanges(synth.getRegionView(1)->velocityRange, { 27_norm, std::nextafter(53_norm, 0.0f) });
|
|
|
|
|
almostEqualRanges(synth.getRegionView(2)->velocityRange, { 53_norm, std::nextafter(78_norm, 0.0f) });
|
|
|
|
|
almostEqualRanges(synth.getRegionView(3)->velocityRange, { 78_norm, std::nextafter(103_norm, 0.0f) });
|
2021-02-04 11:02:09 +01:00
|
|
|
almostEqualRanges(synth.getRegionView(4)->velocityRange, { 103_norm, 127_norm });
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Full hierarchy")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 8);
|
|
|
|
|
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(i)->width == 0.4_a);
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->pan == 0.3_a);
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->delay == 67);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(60, 60));
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(1)->pan == 0.3_a);
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(1)->delay == 67);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(61, 61));
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(2)->pan == 0.3_a);
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(2)->delay == 56);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(50, 50));
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(3)->pan == 0.3_a);
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(3)->delay == 56);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(3)->keyRange == Range<uint8_t>(51, 51));
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(4)->pan == -0.1_a);
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(4)->delay == 47);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(4)->keyRange == Range<uint8_t>(40, 40));
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(5)->pan == -0.1_a);
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(5)->delay == 47);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(5)->keyRange == Range<uint8_t>(41, 41));
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(6)->pan == -0.1_a);
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(6)->delay == 36);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(6)->keyRange == Range<uint8_t>(30, 30));
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2020-03-29 19:34:45 +02:00
|
|
|
REQUIRE(synth.getRegionView(7)->pan == -0.1_a);
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(7)->delay == 36);
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(7)->keyRange == Range<uint8_t>(31, 31));
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Reloading files")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 8);
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 8);
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Full hierarchy with antislashes")
|
|
|
|
|
{
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 8);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "Regions/dummy.1.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(3)->sampleId->filename() == "Regions/dummy.1.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(4)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(5)->sampleId->filename() == "Regions/dummy.1.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(6)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(7)->sampleId->filename() == "Regions/dummy.1.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 8);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "Regions/dummy.1.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(3)->sampleId->filename() == "Regions/dummy.1.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(4)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(5)->sampleId->filename() == "Regions/dummy.1.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(6)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(7)->sampleId->filename() == "Regions/dummy.1.wav");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Pizz basic")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz");
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 4);
|
|
|
|
|
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(12, 22));
|
2021-02-04 11:02:09 +01:00
|
|
|
almostEqualRanges(synth.getRegionView(i)->velocityRange, { 97_norm, 127_norm });
|
2019-08-25 14:01:03 +02:00
|
|
|
REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21);
|
2021-04-25 00:28:58 +02:00
|
|
|
almostEqualRanges(synth.getRegionView(i)->ccConditions.getWithDefault(107),
|
|
|
|
|
{ 0_norm, std::nextafter(14_norm, 0.0f) }); // Fill in the gap from 13_norm to "almost 14_norm"
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
2021-02-04 11:02:09 +01:00
|
|
|
almostEqualRanges(synth.getRegionView(0)->randRange, { 0, 0.25 });
|
|
|
|
|
almostEqualRanges(synth.getRegionView(1)->randRange, { 0.25, 0.5 });
|
|
|
|
|
almostEqualRanges(synth.getRegionView(2)->randRange, { 0.5, 0.75 });
|
|
|
|
|
almostEqualRanges(synth.getRegionView(3)->randRange, { 0.75, 1.0 });
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == R"(../Samples/pizz/a0_vl4_rr1.wav)");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == R"(../Samples/pizz/a0_vl4_rr2.wav)");
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->sampleId->filename() == R"(../Samples/pizz/a0_vl4_rr3.wav)");
|
|
|
|
|
REQUIRE(synth.getRegionView(3)->sampleId->filename() == R"(../Samples/pizz/a0_vl4_rr4.wav)");
|
2019-07-29 02:13:03 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-29 16:56:56 +02:00
|
|
|
TEST_CASE("[Files] Channels (channels.sfz)")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz");
|
2019-08-29 16:56:56 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 2);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "mono_sample.wav");
|
2020-04-03 17:25:30 +02:00
|
|
|
REQUIRE(!synth.getRegionView(0)->isStereo());
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "stereo_sample.wav");
|
2020-04-03 17:25:30 +02:00
|
|
|
REQUIRE(synth.getRegionView(1)->isStereo());
|
2019-08-29 16:56:56 +02:00
|
|
|
}
|
2019-07-29 02:13:03 +02:00
|
|
|
|
2021-11-21 23:55:28 +01:00
|
|
|
TEST_CASE("[Files] Generators and wavetables")
|
2020-04-04 23:14:15 +02:00
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2021-11-21 23:55:28 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/wavetables.sfz");
|
2021-07-20 12:25:03 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 12);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
int regionNumber = 0;
|
|
|
|
|
const Region* region = nullptr;
|
|
|
|
|
|
|
|
|
|
// generator only
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "*sine");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
// generator with multi
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "*sine");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(region->isStereo());
|
|
|
|
|
REQUIRE(region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
// explicit wavetable
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "ramp_wave.wav");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::On);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
// explicit wavetable with multi
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "ramp_wave.wav");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::On);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
// explicit disabled wavetable
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "ramp_wave.wav");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(!region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Off);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
// explicit disabled wavetable with multi
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "ramp_wave.wav");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(!region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Off);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
2021-07-20 12:25:03 +02:00
|
|
|
// implicit wavetable (sound file < 3000 frames and wavetable tags)
|
2020-09-22 15:53:33 +02:00
|
|
|
region = synth.getRegionView(regionNumber++);
|
2021-07-20 12:25:03 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "wavetables/surge.wav");
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
|
|
|
|
|
|
|
|
|
// Parse oscillator=auto and same as above
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "wavetables/surge.wav");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
2021-07-20 12:25:03 +02:00
|
|
|
|
|
|
|
|
// implicit non wavetable (sound file < 3000 frames but no wavetable tags)
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "short_non_wavetable.wav");
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(!region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
// implicit non-wavetable (sound file >= 3000 frames)
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "snare.wav");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(!region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
// generator with multi=1 (single)
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "*sine");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
2020-09-22 15:53:33 +02:00
|
|
|
|
|
|
|
|
// generator with multi=2 (ring modulation)
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(region->sampleId->filename() == "*sine");
|
2020-09-22 15:53:33 +02:00
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
2020-12-30 01:16:39 +01:00
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
2020-04-04 23:14:15 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-21 23:55:28 +01:00
|
|
|
TEST_CASE("[Files] Embedded wavetables")
|
|
|
|
|
{
|
|
|
|
|
Synth synth;
|
|
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/wavetables_embedded.sfz");
|
|
|
|
|
REQUIRE(synth.getNumRegions() == 7);
|
|
|
|
|
|
|
|
|
|
int regionNumber = 0;
|
|
|
|
|
const Region* region = nullptr;
|
|
|
|
|
|
|
|
|
|
// explicit wavetable
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "ramp_wave.wav");
|
|
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::On);
|
|
|
|
|
|
|
|
|
|
// explicit wavetable with multi
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "ramp_wave.wav");
|
|
|
|
|
REQUIRE(region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::On);
|
|
|
|
|
|
|
|
|
|
// explicit disabled wavetable
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "ramp_wave.wav");
|
|
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(!region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Off);
|
|
|
|
|
|
|
|
|
|
// explicit disabled wavetable with multi
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "ramp_wave.wav");
|
|
|
|
|
REQUIRE(!region->isStereo());
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(!region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Off);
|
|
|
|
|
|
|
|
|
|
// implicit wavetable (sound file < 3000 frames and wavetable tags)
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "wavetables/surge.wav");
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
|
|
|
|
|
|
|
|
|
// Parse oscillator=auto and same as above
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "wavetables/surge.wav");
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
|
|
|
|
|
|
|
|
|
// implicit non wavetable (sound file < 3000 frames but no wavetable tags)
|
|
|
|
|
region = synth.getRegionView(regionNumber++);
|
|
|
|
|
REQUIRE(region->sampleId->filename() == "short_non_wavetable.wav");
|
|
|
|
|
REQUIRE(!region->isGenerator());
|
|
|
|
|
REQUIRE(!region->isOscillator());
|
|
|
|
|
REQUIRE(region->oscillatorEnabled == OscillatorEnabled::Auto);
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-10 21:16:44 +02:00
|
|
|
TEST_CASE("[Files] wrong (overlapping) replacement for defines")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-09-21 13:01:50 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/wrong-replacements.sfz");
|
2020-05-14 20:48:08 +02:00
|
|
|
|
2019-09-10 21:16:44 +02:00
|
|
|
REQUIRE( synth.getNumRegions() == 3 );
|
2020-05-14 20:48:08 +02:00
|
|
|
|
|
|
|
|
// Note: test checked to be wrong under Sforzando 1.961
|
|
|
|
|
// It is the shorter matching $-variable which matches among both.
|
|
|
|
|
// The rest of the variable name creates some trailing junk text
|
2020-05-30 15:18:09 +02:00
|
|
|
// which Sforzando accepts without warning. (eg. `key=57Edge`)
|
|
|
|
|
REQUIRE( synth.getRegionView(0)->keyRange.getStart() == 57 );
|
|
|
|
|
REQUIRE( synth.getRegionView(0)->keyRange.getEnd() == 57 );
|
2020-05-14 20:48:08 +02:00
|
|
|
|
2019-09-10 21:16:44 +02:00
|
|
|
REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 );
|
|
|
|
|
REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 );
|
2020-07-27 17:32:25 +02:00
|
|
|
|
|
|
|
|
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, synth.getRegionView(2)->getId());
|
|
|
|
|
const RegionCCView view(*synth.getRegionView(2), target);
|
|
|
|
|
REQUIRE(!view.empty());
|
2021-03-31 22:36:55 +02:00
|
|
|
REQUIRE(view.valueAt(10) == Approx(0.34f).margin(1e-3));
|
2019-12-01 20:18:59 +01:00
|
|
|
}
|
2019-12-03 15:35:27 +01:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Specific bug: relative path with backslashes")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-12-03 15:35:27 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/win_backslashes.sfz");
|
|
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == R"(Xylo/Subfolder/closedhat.wav)");
|
2019-12-03 15:35:27 +01:00
|
|
|
}
|
2019-12-08 21:11:54 +01:00
|
|
|
|
2019-12-15 17:47:46 +01:00
|
|
|
TEST_CASE("[Files] Default path")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-12-15 17:47:46 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz");
|
2019-12-21 18:14:34 +01:00
|
|
|
REQUIRE(synth.getNumRegions() == 4);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == R"(DefaultPath/SubPath1/sample1.wav)");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == R"(DefaultPath/SubPath2/sample2.wav)");
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->sampleId->filename() == R"(DefaultPath/SubPath1/sample1.wav)");
|
|
|
|
|
REQUIRE(synth.getRegionView(3)->sampleId->filename() == R"(DefaultPath/SubPath2/sample2.wav)");
|
2019-12-15 23:53:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Default path reset when calling loadSfzFile again")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-12-15 23:53:11 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path.sfz");
|
2019-12-21 18:14:34 +01:00
|
|
|
REQUIRE(synth.getNumRegions() == 4);
|
2019-12-15 23:53:11 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_reset.sfz");
|
|
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == R"(DefaultPath/SubPath2/sample2.wav)");
|
2019-12-16 00:00:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Default path is ignored for generators")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-12-16 00:00:58 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/default_path_generator.sfz");
|
|
|
|
|
REQUIRE(synth.getNumRegions() == 1);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == R"(*sine)");
|
2019-12-17 14:07:26 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-23 01:25:20 +01:00
|
|
|
TEST_CASE("[Files] Set CC applies properly")
|
2019-12-17 14:07:26 +01:00
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2021-04-25 03:52:14 +02:00
|
|
|
const MidiState& midiState = synth.getResources().getMidiState();
|
2019-12-17 14:07:26 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz");
|
2020-04-06 00:29:42 +02:00
|
|
|
REQUIRE(midiState.getCCValue(142) == 63_norm);
|
|
|
|
|
REQUIRE(midiState.getCCValue(61) == 122_norm);
|
2019-12-21 17:37:09 +01:00
|
|
|
}
|
2019-12-21 18:57:32 +01:00
|
|
|
|
2020-05-14 23:54:57 +02:00
|
|
|
TEST_CASE("[Files] Set HDCC applies properly")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
2021-04-25 03:52:14 +02:00
|
|
|
const MidiState& midiState = synth.getResources().getMidiState();
|
2020-05-14 23:54:57 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_hdcc.sfz");
|
|
|
|
|
REQUIRE(midiState.getCCValue(142) == Approx(0.5678));
|
|
|
|
|
REQUIRE(midiState.getCCValue(61) == Approx(0.1234));
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-15 00:25:37 +02:00
|
|
|
TEST_CASE("[Files] Set RealCC applies properly")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
2021-04-25 03:52:14 +02:00
|
|
|
const MidiState& midiState = synth.getResources().getMidiState();
|
2020-05-15 00:25:37 +02:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_realcc.sfz");
|
|
|
|
|
REQUIRE(midiState.getCCValue(142) == Approx(0.5678));
|
|
|
|
|
REQUIRE(midiState.getCCValue(61) == Approx(0.1234));
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-21 18:57:32 +01:00
|
|
|
TEST_CASE("[Files] Note and octave offsets")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2020-11-08 10:26:45 +01:00
|
|
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/note_offset.sfz", R"(
|
|
|
|
|
<control> note_offset=1
|
|
|
|
|
<region> key=63 sample=*sine
|
|
|
|
|
<region> lokey=50 hikey=55 pitch_keycenter=50 sample=*sine
|
|
|
|
|
<region> lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=44 xfout_hikey=48 sample=*sine
|
|
|
|
|
<control> note_offset=-1
|
|
|
|
|
<region> key=63 sw_lokey=24 sw_hikey=28 sw_last=25 sw_up=25 sw_down=25 sw_previous=62 sample=*sine
|
|
|
|
|
<control> note_offset=1 octave_offset=1
|
|
|
|
|
<region> key=63 sample=*sine
|
|
|
|
|
<control> note_offset=-1 octave_offset=-1
|
|
|
|
|
<region> key=63 sample=*sine
|
|
|
|
|
<control> // Check that this does not reset either note or octave offset
|
|
|
|
|
<region> key=63 sample=*sine
|
|
|
|
|
)");
|
2019-12-21 18:57:32 +01:00
|
|
|
REQUIRE( synth.getNumRegions() == 7 );
|
|
|
|
|
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(64, 64));
|
2019-12-21 18:57:32 +01:00
|
|
|
REQUIRE( synth.getRegionView(0)->pitchKeycenter == 64 );
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->crossfadeKeyInRange == Default::crossfadeKeyInRange);
|
|
|
|
|
REQUIRE(synth.getRegionView(0)->crossfadeKeyOutRange == Default::crossfadeKeyOutRange);
|
2019-12-21 18:57:32 +01:00
|
|
|
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(51, 56));
|
2019-12-21 18:57:32 +01:00
|
|
|
REQUIRE( synth.getRegionView(1)->pitchKeycenter == 51 );
|
|
|
|
|
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(2)->keyRange == Range<uint8_t>(41, 45));
|
2019-12-21 18:57:32 +01:00
|
|
|
REQUIRE( synth.getRegionView(2)->pitchKeycenter == 41 );
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(2)->crossfadeKeyInRange == Range<uint8_t>(37, 41));
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->crossfadeKeyOutRange == Range<uint8_t>(45, 49));
|
2019-12-21 18:57:32 +01:00
|
|
|
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(3)->keyRange == Range<uint8_t>(62, 62));
|
2020-10-27 11:19:12 +01:00
|
|
|
REQUIRE( synth.getRegionView(3)->lastKeyswitch );
|
|
|
|
|
REQUIRE( *synth.getRegionView(3)->lastKeyswitch == 24 );
|
|
|
|
|
REQUIRE( synth.getRegionView(3)->upKeyswitch );
|
|
|
|
|
REQUIRE( *synth.getRegionView(3)->upKeyswitch == 24 );
|
|
|
|
|
REQUIRE( synth.getRegionView(3)->downKeyswitch );
|
|
|
|
|
REQUIRE( *synth.getRegionView(3)->downKeyswitch == 24 );
|
|
|
|
|
REQUIRE( synth.getRegionView(3)->previousKeyswitch );
|
|
|
|
|
REQUIRE( *synth.getRegionView(3)->previousKeyswitch == 61 );
|
2019-12-21 18:57:32 +01:00
|
|
|
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(4)->keyRange == Range<uint8_t>(76, 76));
|
2019-12-21 18:57:32 +01:00
|
|
|
REQUIRE( synth.getRegionView(4)->pitchKeycenter == 76 );
|
|
|
|
|
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(5)->keyRange == Range<uint8_t>(50, 50));
|
2019-12-21 18:57:32 +01:00
|
|
|
REQUIRE( synth.getRegionView(5)->pitchKeycenter == 50 );
|
|
|
|
|
|
2020-03-31 20:13:13 +02:00
|
|
|
REQUIRE(synth.getRegionView(6)->keyRange == Range<uint8_t>(50, 50));
|
2019-12-21 18:57:32 +01:00
|
|
|
REQUIRE( synth.getRegionView(6)->pitchKeycenter == 50 );
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-21 21:24:49 +01:00
|
|
|
TEST_CASE("[Files] Off modes")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2021-04-05 00:18:33 +02:00
|
|
|
AudioBuffer<float> buffer { 2, 256 };
|
2019-12-21 21:24:49 +01:00
|
|
|
synth.setSamplesPerBlock(256);
|
2021-04-05 00:18:33 +02:00
|
|
|
|
2019-12-21 21:24:49 +01:00
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_mode.sfz");
|
|
|
|
|
REQUIRE( synth.getNumRegions() == 3 );
|
2021-04-05 00:18:33 +02:00
|
|
|
|
2019-12-23 01:25:20 +01:00
|
|
|
synth.noteOn(0, 64, 63);
|
2021-04-05 00:18:33 +02:00
|
|
|
synth.renderBlock(buffer);
|
2020-10-31 19:28:23 +01:00
|
|
|
REQUIRE( synth.getNumActiveVoices() == 2 );
|
2019-12-21 21:24:49 +01:00
|
|
|
const auto* fastVoice =
|
2021-01-03 23:29:33 +01:00
|
|
|
synth.getVoiceView(0)->getRegion()->offMode == OffMode::fast ?
|
2019-12-21 21:24:49 +01:00
|
|
|
synth.getVoiceView(0) :
|
|
|
|
|
synth.getVoiceView(1) ;
|
|
|
|
|
const auto* normalVoice =
|
2021-01-03 23:29:33 +01:00
|
|
|
synth.getVoiceView(0)->getRegion()->offMode == OffMode::fast ?
|
2019-12-21 21:24:49 +01:00
|
|
|
synth.getVoiceView(1) :
|
|
|
|
|
synth.getVoiceView(0) ;
|
2019-12-23 01:25:20 +01:00
|
|
|
synth.noteOn(100, 63, 63);
|
2021-04-05 00:18:33 +02:00
|
|
|
synth.renderBlock(buffer);
|
|
|
|
|
|
2020-10-31 19:28:23 +01:00
|
|
|
REQUIRE( synth.getNumActiveVoices() == 3 );
|
2020-08-10 17:22:49 +02:00
|
|
|
REQUIRE( numPlayingVoices(synth) == 1 );
|
2021-03-22 19:30:34 +01:00
|
|
|
for (unsigned i = 0; i < 20; ++i) // Not enough for the "normal" voice to die
|
2020-08-10 17:22:49 +02:00
|
|
|
synth.renderBlock(buffer);
|
2020-10-31 19:28:23 +01:00
|
|
|
REQUIRE( synth.getNumActiveVoices() == 2 );
|
2019-12-21 21:24:49 +01:00
|
|
|
REQUIRE( fastVoice->isFree() );
|
|
|
|
|
REQUIRE( !normalVoice->isFree() );
|
|
|
|
|
}
|
2019-12-29 20:14:39 +01:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Looped regions taken from files and possibly overriden")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2019-12-29 20:14:39 +01:00
|
|
|
synth.setSamplesPerBlock(256);
|
|
|
|
|
synth.setSampleRate(44100);
|
|
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/looped_regions.sfz");
|
|
|
|
|
REQUIRE( synth.getNumRegions() == 3 );
|
2021-01-03 23:29:33 +01:00
|
|
|
REQUIRE( synth.getRegionView(0)->loopMode == LoopMode::loop_continuous );
|
|
|
|
|
REQUIRE( synth.getRegionView(1)->loopMode == LoopMode::no_loop );
|
|
|
|
|
REQUIRE( synth.getRegionView(2)->loopMode == LoopMode::loop_continuous );
|
2019-12-29 20:14:39 +01:00
|
|
|
|
2021-03-28 23:37:38 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->loopRange == Range<int64_t> { 77554, 186581 });
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->loopRange == Range<int64_t> { 77554, 186581 });
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->loopRange == Range<int64_t> { 4, 124 });
|
2019-12-29 20:14:39 +01:00
|
|
|
}
|
2020-01-26 20:38:59 +01:00
|
|
|
|
2020-08-18 23:09:03 +02:00
|
|
|
TEST_CASE("[Files] Looped regions can start at 0")
|
|
|
|
|
{
|
|
|
|
|
Synth synth;
|
|
|
|
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/loop_can_start_at_0.sfz", R"(
|
|
|
|
|
<region> sample=wavetable_with_loop_at_endings.wav
|
|
|
|
|
)");
|
|
|
|
|
REQUIRE( synth.getNumRegions() == 1 );
|
2021-01-03 23:29:33 +01:00
|
|
|
REQUIRE( synth.getRegionView(0)->loopMode == LoopMode::loop_continuous );
|
2021-03-28 23:37:38 +02:00
|
|
|
REQUIRE( synth.getRegionView(0)->loopRange == Range<int64_t> { 0, synth.getRegionView(0)->sampleEnd } );
|
2020-08-18 23:09:03 +02:00
|
|
|
}
|
|
|
|
|
|
2020-08-19 12:35:58 +02:00
|
|
|
TEST_CASE("[Synth] Release triggers automatically sets the loop mode")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
|
|
|
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/triggers_setting_loops.sfz", R"(
|
|
|
|
|
<region> sample=kick.wav pitch_keycenter=69 loop_mode=loop_sustain trigger=release
|
|
|
|
|
<region> sample=kick.wav pitch_keycenter=69 loop_mode=loop_sustain trigger=release_key
|
|
|
|
|
<region> sample=kick.wav pitch_keycenter=69 trigger=release loop_mode=loop_sustain
|
|
|
|
|
<region> sample=kick.wav pitch_keycenter=69 trigger=release_key loop_mode=loop_sustain
|
|
|
|
|
<region> sample=looped_flute.wav pitch_keycenter=69 trigger=release_key
|
|
|
|
|
<region> sample=kick.wav pitch_keycenter=69 trigger=release_key // These are normal and set to one_shot
|
|
|
|
|
<region> sample=kick.wav pitch_keycenter=69 trigger=release
|
|
|
|
|
)");
|
|
|
|
|
REQUIRE( synth.getNumRegions() == 7 );
|
2021-01-03 23:29:33 +01:00
|
|
|
REQUIRE( synth.getRegionView(0)->loopMode == LoopMode::loop_sustain );
|
|
|
|
|
REQUIRE( synth.getRegionView(1)->loopMode == LoopMode::loop_sustain );
|
|
|
|
|
REQUIRE( synth.getRegionView(2)->loopMode == LoopMode::loop_sustain );
|
|
|
|
|
REQUIRE( synth.getRegionView(3)->loopMode == LoopMode::loop_sustain );
|
|
|
|
|
REQUIRE( synth.getRegionView(4)->loopMode == LoopMode::loop_continuous );
|
|
|
|
|
REQUIRE( synth.getRegionView(5)->loopMode == LoopMode::one_shot );
|
|
|
|
|
REQUIRE( synth.getRegionView(6)->loopMode == LoopMode::one_shot );
|
2020-08-19 12:35:58 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-26 20:38:59 +01:00
|
|
|
TEST_CASE("[Files] Case sentitiveness")
|
|
|
|
|
{
|
2020-04-13 21:38:22 +02:00
|
|
|
const fs::path sfzFilePath = fs::current_path() / "tests/TestFiles/case_insensitive.sfz";
|
2020-04-13 17:14:04 +02:00
|
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
|
const bool caseSensitiveFs = false;
|
|
|
|
|
#elif defined(__APPLE__)
|
|
|
|
|
const bool caseSensitiveFs = pathconf(sfzFilePath.string().c_str(), _PC_CASE_SENSITIVE) != 0;
|
|
|
|
|
#else
|
|
|
|
|
const bool caseSensitiveFs = true;
|
2020-01-28 21:42:15 +01:00
|
|
|
#endif
|
2020-04-13 17:14:04 +02:00
|
|
|
|
|
|
|
|
if (caseSensitiveFs) {
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
2020-04-13 17:14:04 +02:00
|
|
|
synth.loadSfzFile(sfzFilePath);
|
|
|
|
|
REQUIRE(synth.getNumRegions() == 4);
|
2020-10-21 01:36:09 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->sampleId->filename() == "dummy1.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->sampleId->filename() == "Regions/dummy.wav");
|
|
|
|
|
REQUIRE(synth.getRegionView(3)->sampleId->filename() == "Regions/dummy.wav");
|
2020-04-13 17:14:04 +02:00
|
|
|
}
|
2020-01-26 20:38:59 +01:00
|
|
|
}
|
2020-02-12 09:15:28 +01:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Empty file")
|
|
|
|
|
{
|
2020-03-31 20:13:13 +02:00
|
|
|
Synth synth;
|
|
|
|
|
Parser& parser = synth.getParser();
|
2020-02-12 09:15:28 +01:00
|
|
|
REQUIRE(!synth.loadSfzFile(""));
|
2020-03-17 22:14:35 +01:00
|
|
|
REQUIRE(parser.getIncludedFiles().empty());
|
2020-02-12 09:15:28 +01:00
|
|
|
REQUIRE(!synth.loadSfzFile({}));
|
2020-03-17 22:14:35 +01:00
|
|
|
REQUIRE(parser.getIncludedFiles().empty());
|
2020-02-12 09:15:28 +01:00
|
|
|
}
|
2020-04-08 12:23:17 +02:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Labels")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
|
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/labels.sfz");
|
2020-04-15 18:04:02 +02:00
|
|
|
auto keyLabels = synth.getKeyLabels();
|
2020-04-08 12:23:17 +02:00
|
|
|
auto ccLabels = synth.getCCLabels();
|
2020-04-15 18:04:02 +02:00
|
|
|
REQUIRE( keyLabels.size() == 2);
|
|
|
|
|
REQUIRE( keyLabels[0].first == 12 );
|
|
|
|
|
REQUIRE( keyLabels[0].second == "Cymbals" );
|
|
|
|
|
REQUIRE( keyLabels[1].first == 65 );
|
|
|
|
|
REQUIRE( keyLabels[1].second == "Crash" );
|
2020-09-16 11:29:40 +02:00
|
|
|
REQUIRE( ccLabels.size() >= 2);
|
|
|
|
|
REQUIRE( absl::c_find(ccLabels, CCNamePair { 54, "Gain" }) != ccLabels.end() );
|
|
|
|
|
REQUIRE( absl::c_find(ccLabels, CCNamePair { 2, "Other" }) != ccLabels.end() );
|
2020-04-18 21:26:56 +02:00
|
|
|
const std::string xmlMidnam = synth.exportMidnam();
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Note Number=\"12\" Name=\"Cymbals\" />") != xmlMidnam.npos);
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Note Number=\"65\" Name=\"Crash\" />") != xmlMidnam.npos);
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Control Type=\"7bit\" Number=\"54\" Name=\"Gain\" />") != xmlMidnam.npos);
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Control Type=\"7bit\" Number=\"2\" Name=\"Other\" />") != xmlMidnam.npos);
|
2020-04-08 12:23:17 +02:00
|
|
|
}
|
2020-04-18 21:35:38 +02:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Switch labels")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
|
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/labels_sw.sfz");
|
|
|
|
|
const std::string xmlMidnam = synth.exportMidnam();
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Note Number=\"36\" Name=\"Sine\" />") != xmlMidnam.npos);
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Note Number=\"38\" Name=\"Triangle\" />") != xmlMidnam.npos);
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Note Number=\"40\" Name=\"Saw\" />") != xmlMidnam.npos);
|
|
|
|
|
}
|
2020-09-16 11:29:40 +02:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Duplicate labels")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
|
|
|
|
synth.loadSfzString(
|
|
|
|
|
fs::current_path() / "tests/TestFiles/labels.sfz",
|
|
|
|
|
R"(<control> label_key60=Baz label_key60=Quux
|
|
|
|
|
<control> label_cc20=Foo label_cc20=Bar
|
|
|
|
|
<region> sample=*sine)");
|
|
|
|
|
|
|
|
|
|
auto keyLabels = synth.getKeyLabels();
|
|
|
|
|
auto ccLabels = synth.getCCLabels();
|
|
|
|
|
REQUIRE( keyLabels.size() == 1);
|
|
|
|
|
REQUIRE( keyLabels[0].first == 60 );
|
|
|
|
|
REQUIRE( keyLabels[0].second == "Quux" );
|
|
|
|
|
REQUIRE( ccLabels.size() >= 1);
|
|
|
|
|
REQUIRE( absl::c_find(ccLabels, CCNamePair { 20, "Bar" }) != ccLabels.end() );
|
|
|
|
|
const std::string xmlMidnam = synth.exportMidnam();
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Note Number=\"60\" Name=\"Quux\" />") != xmlMidnam.npos);
|
|
|
|
|
REQUIRE(xmlMidnam.find("<Control Type=\"7bit\" Number=\"20\" Name=\"Bar\" />") != xmlMidnam.npos);
|
|
|
|
|
}
|
2020-08-14 00:35:50 +02:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Key center from audio file")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
|
|
|
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sample_keycenter.sfz", R"(
|
2020-09-22 20:33:57 +02:00
|
|
|
<group> pitch_keycenter=sample oscillator=off
|
2020-08-14 00:35:50 +02:00
|
|
|
<region> sample=root_key_38.wav
|
|
|
|
|
<region> sample=root_key_62.wav
|
|
|
|
|
<region> sample=root_key_38.flac
|
|
|
|
|
<region> sample=root_key_62.flac
|
2020-09-22 20:39:17 +02:00
|
|
|
<region> pitch_keycenter=10 sample=root_key_62.flac
|
2020-08-14 03:13:31 +02:00
|
|
|
<region> key=10 sample=root_key_62.flac
|
2020-08-14 00:35:50 +02:00
|
|
|
)");
|
|
|
|
|
|
2020-09-22 20:39:17 +02:00
|
|
|
REQUIRE(synth.getNumRegions() == 6);
|
2020-08-14 00:35:50 +02:00
|
|
|
REQUIRE(synth.getRegionView(0)->pitchKeycenter == 38);
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->pitchKeycenter == 62);
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->pitchKeycenter == 38);
|
|
|
|
|
REQUIRE(synth.getRegionView(3)->pitchKeycenter == 62);
|
2020-08-14 03:13:31 +02:00
|
|
|
REQUIRE(synth.getRegionView(4)->pitchKeycenter == 10);
|
2020-09-22 20:39:17 +02:00
|
|
|
REQUIRE(synth.getRegionView(5)->pitchKeycenter == 62);
|
2020-08-14 00:35:50 +02:00
|
|
|
}
|
2021-11-09 15:00:15 +01:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Unused samples are cleared on reloading")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
|
|
|
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/unused_samples.sfz", R"(
|
|
|
|
|
<region> sample=*sine
|
|
|
|
|
<region> sample=kick.wav
|
|
|
|
|
)");
|
|
|
|
|
REQUIRE(synth.getNumPreloadedSamples() == 1);
|
|
|
|
|
|
|
|
|
|
// Same file path to reload
|
|
|
|
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/unused_samples.sfz", R"(
|
|
|
|
|
<region> sample=*sine
|
|
|
|
|
)");
|
|
|
|
|
REQUIRE(synth.getNumPreloadedSamples() == 0);
|
|
|
|
|
}
|
2021-11-21 23:55:28 +01:00
|
|
|
|
|
|
|
|
TEST_CASE("[Files] Key center from audio file, with embedded sample data")
|
|
|
|
|
{
|
|
|
|
|
sfz::Synth synth;
|
|
|
|
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sample_keycenter_embedded.sfz");
|
|
|
|
|
|
|
|
|
|
REQUIRE(synth.getNumRegions() == 6);
|
|
|
|
|
REQUIRE(synth.getRegionView(0)->pitchKeycenter == 38);
|
|
|
|
|
REQUIRE(synth.getRegionView(1)->pitchKeycenter == 62);
|
|
|
|
|
REQUIRE(synth.getRegionView(2)->pitchKeycenter == 38);
|
|
|
|
|
REQUIRE(synth.getRegionView(3)->pitchKeycenter == 62);
|
|
|
|
|
REQUIRE(synth.getRegionView(4)->pitchKeycenter == 10);
|
|
|
|
|
REQUIRE(synth.getRegionView(5)->pitchKeycenter == 62);
|
|
|
|
|
}
|