Updated tests
This commit is contained in:
parent
812a867194
commit
44dbbcaf54
7 changed files with 176 additions and 101 deletions
|
|
@ -5,8 +5,8 @@ project(sfizz)
|
||||||
|
|
||||||
set(SFIZZ_TEST_SOURCES
|
set(SFIZZ_TEST_SOURCES
|
||||||
RegionT.cpp
|
RegionT.cpp
|
||||||
RegionTHelpers.h
|
TestHelpers.h
|
||||||
RegionTHelpers.cpp
|
TestHelpers.cpp
|
||||||
ParsingT.cpp
|
ParsingT.cpp
|
||||||
HelpersT.cpp
|
HelpersT.cpp
|
||||||
HelpersT.cpp
|
HelpersT.cpp
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
// license. You should have receive a LICENSE.md file along with the code.
|
// 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
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
#include "RegionTHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include "sfizz/Synth.h"
|
#include "sfizz/Synth.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
#include "sfizz/modulations/ModId.h"
|
#include "sfizz/modulations/ModId.h"
|
||||||
|
|
@ -517,7 +517,8 @@ TEST_CASE("[Files] Off by with the same notes at the same time")
|
||||||
synth.renderBlock(buffer);
|
synth.renderBlock(buffer);
|
||||||
synth.noteOn(0, 65, 63);
|
synth.noteOn(0, 65, 63);
|
||||||
synth.renderBlock(buffer);
|
synth.renderBlock(buffer);
|
||||||
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
REQUIRE( synth.getNumActiveVoices(true) == 6 );
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Files] Off modes")
|
TEST_CASE("[Files] Off modes")
|
||||||
|
|
@ -538,8 +539,10 @@ TEST_CASE("[Files] Off modes")
|
||||||
synth.getVoiceView(0) ;
|
synth.getVoiceView(0) ;
|
||||||
synth.noteOn(100, 63, 63);
|
synth.noteOn(100, 63, 63);
|
||||||
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 1 );
|
||||||
AudioBuffer<float> buffer { 2, 256 };
|
AudioBuffer<float> buffer { 2, 256 };
|
||||||
synth.renderBlock(buffer);
|
for (unsigned i = 0; i < 10; ++i) // Not enough for the "normal" voice to die
|
||||||
|
synth.renderBlock(buffer);
|
||||||
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
||||||
REQUIRE( fastVoice->isFree() );
|
REQUIRE( fastVoice->isFree() );
|
||||||
REQUIRE( !normalVoice->isFree() );
|
REQUIRE( !normalVoice->isFree() );
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "sfizz/Synth.h"
|
#include "sfizz/Synth.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
|
#include "TestHelpers.h"
|
||||||
|
#include <absl/algorithm/container.h>
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
|
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
|
|
@ -13,7 +15,6 @@ using namespace sfz::literals;
|
||||||
|
|
||||||
constexpr int blockSize { 256 };
|
constexpr int blockSize { 256 };
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Polyphony in hierarchy")
|
TEST_CASE("[Polyphony] Polyphony in hierarchy")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
|
@ -71,6 +72,7 @@ TEST_CASE("[Polyphony] Polyphony groups")
|
||||||
TEST_CASE("[Polyphony] group polyphony limits")
|
TEST_CASE("[Polyphony] group polyphony limits")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<group> group=1 polyphony=2
|
<group> group=1 polyphony=2
|
||||||
<region> sample=*sine key=65
|
<region> sample=*sine key=65
|
||||||
|
|
@ -78,12 +80,15 @@ TEST_CASE("[Polyphony] group polyphony limits")
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2); // group polyphony should block the last note
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 ); // One is releasing
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Hierarchy polyphony limits")
|
TEST_CASE("[Polyphony] Hierarchy polyphony limits")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<group> polyphony=2
|
<group> polyphony=2
|
||||||
<region> sample=*sine key=65
|
<region> sample=*sine key=65
|
||||||
|
|
@ -91,12 +96,15 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits")
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 ); // One is releasing
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Hierarchy polyphony limits (group)")
|
TEST_CASE("[Polyphony] Hierarchy polyphony limits (group)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<group> polyphony=2
|
<group> polyphony=2
|
||||||
<region> sample=*sine key=65
|
<region> sample=*sine key=65
|
||||||
|
|
@ -104,12 +112,15 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits (group)")
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 ); // One is releasing
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Hierarchy polyphony limits (master)")
|
TEST_CASE("[Polyphony] Hierarchy polyphony limits (master)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<master> polyphony=2
|
<master> polyphony=2
|
||||||
<group> polyphony=5
|
<group> polyphony=5
|
||||||
|
|
@ -118,12 +129,15 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits (master)")
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 ); // One is releasing
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Hierarchy polyphony limits (limit in another master)")
|
TEST_CASE("[Polyphony] Hierarchy polyphony limits (limit in another master)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<master> polyphony=2
|
<master> polyphony=2
|
||||||
<region> sample=*saw key=65
|
<region> sample=*saw key=65
|
||||||
|
|
@ -137,12 +151,15 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits (limit in another master)")
|
||||||
synth.noteOn(0, 66, 64);
|
synth.noteOn(0, 66, 64);
|
||||||
synth.noteOn(0, 66, 64);
|
synth.noteOn(0, 66, 64);
|
||||||
synth.noteOn(0, 66, 64);
|
synth.noteOn(0, 66, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 5);
|
REQUIRE( synth.getNumActiveVoices(true) == 6);
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 5); // One is releasing
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Hierarchy polyphony limits (global)")
|
TEST_CASE("[Polyphony] Hierarchy polyphony limits (global)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<global> polyphony=2
|
<global> polyphony=2
|
||||||
<group> polyphony=5
|
<group> polyphony=5
|
||||||
|
|
@ -151,14 +168,16 @@ TEST_CASE("[Polyphony] Hierarchy polyphony limits (global)")
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 ); // One is releasing
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Polyphony in master")
|
TEST_CASE("[Polyphony] Polyphony in master")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.setSamplesPerBlock(blockSize);
|
|
||||||
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
|
synth.setSamplesPerBlock(blockSize);
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<master> polyphony=2
|
<master> polyphony=2
|
||||||
<group> group=2
|
<group> group=2
|
||||||
|
|
@ -171,75 +190,90 @@ TEST_CASE("[Polyphony] Polyphony in master")
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
synth.noteOn(0, 65, 64);
|
synth.noteOn(0, 65, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2); // group polyphony should block the last note
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 ); // One is releasing
|
||||||
synth.allSoundOff();
|
synth.allSoundOff();
|
||||||
synth.renderBlock(buffer);
|
synth.renderBlock(buffer);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 0);
|
REQUIRE( synth.getNumActiveVoices(true) == 0);
|
||||||
synth.noteOn(0, 63, 64);
|
synth.noteOn(0, 63, 64);
|
||||||
synth.noteOn(0, 63, 64);
|
synth.noteOn(0, 63, 64);
|
||||||
synth.noteOn(0, 63, 64);
|
synth.noteOn(0, 63, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2); // group polyphony should block the last note
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 ); // One is releasing
|
||||||
synth.allSoundOff();
|
synth.allSoundOff();
|
||||||
synth.renderBlock(buffer);
|
synth.renderBlock(buffer);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 0);
|
REQUIRE( synth.getNumActiveVoices(true) == 0);
|
||||||
synth.noteOn(0, 61, 64);
|
synth.noteOn(0, 61, 64);
|
||||||
synth.noteOn(0, 61, 64);
|
synth.noteOn(0, 61, 64);
|
||||||
synth.noteOn(0, 61, 64);
|
synth.noteOn(0, 61, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 3);
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 3 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Self-masking")
|
TEST_CASE("[Polyphony] Self-masking")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<region> sample=*sine key=64 note_polyphony=2
|
<region> sample=*sine key=64 note_polyphony=2
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 64, 63);
|
synth.noteOn(0, 64, 63 );
|
||||||
synth.noteOn(0, 64, 62);
|
synth.noteOn(0, 64, 62 );
|
||||||
synth.noteOn(0, 64, 64);
|
synth.noteOn(0, 64, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 3); // One of these is releasing
|
REQUIRE( synth.getNumActiveVoices(true) == 3 ); // One of these is releasing
|
||||||
REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 );
|
||||||
|
REQUIRE( synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
||||||
REQUIRE(!synth.getVoiceView(0)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(0)->releasedOrFree());
|
||||||
REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
REQUIRE( synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
||||||
REQUIRE(synth.getVoiceView(1)->releasedOrFree()); // The lowest velocity voice is the masking candidate
|
REQUIRE( synth.getVoiceView(1)->releasedOrFree()); // The lowest velocity voice is the masking candidate
|
||||||
REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 64_norm);
|
REQUIRE( synth.getVoiceView(2)->getTriggerValue() == 64_norm);
|
||||||
REQUIRE(!synth.getVoiceView(2)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(2)->releasedOrFree());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Not self-masking")
|
TEST_CASE("[Polyphony] Not self-masking")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<region> sample=*sine key=66 note_polyphony=2 note_selfmask=off
|
<region> sample=*sine key=66 note_polyphony=2 note_selfmask=off
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 66, 63);
|
synth.noteOn(0, 66, 63 );
|
||||||
synth.noteOn(0, 66, 62);
|
synth.noteOn(0, 66, 62 );
|
||||||
synth.noteOn(0, 66, 64);
|
synth.noteOn(0, 66, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 3); // One of these is releasing
|
REQUIRE( synth.getNumActiveVoices(true) == 3 ); // One of these is releasing
|
||||||
REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
synth.renderBlock(buffer);
|
||||||
REQUIRE(synth.getVoiceView(0)->releasedOrFree());
|
REQUIRE( numPlayingVoices(synth) == 2 );
|
||||||
REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
REQUIRE( synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
||||||
|
REQUIRE( synth.getVoiceView(0)->releasedOrFree());
|
||||||
|
REQUIRE( synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
||||||
REQUIRE(!synth.getVoiceView(1)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(1)->releasedOrFree());
|
||||||
REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 64_norm);
|
REQUIRE( synth.getVoiceView(2)->getTriggerValue() == 64_norm);
|
||||||
REQUIRE(!synth.getVoiceView(2)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(2)->releasedOrFree());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Self-masking with the exact same velocity")
|
TEST_CASE("[Polyphony] Self-masking with the exact same velocity")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path(), R"(
|
synth.loadSfzString(fs::current_path(), R"(
|
||||||
<region> sample=*sine key=64 note_polyphony=2
|
<region> sample=*sine key=64 note_polyphony=2
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 64, 64);
|
synth.noteOn(0, 64, 64);
|
||||||
synth.noteOn(0, 64, 63);
|
synth.noteOn(0, 64, 63 );
|
||||||
synth.noteOn(0, 64, 63);
|
synth.noteOn(0, 64, 63 );
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 3); // One of these is releasing
|
REQUIRE( synth.getNumActiveVoices(true) == 3 ); // One of these is releasing
|
||||||
REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 64_norm);
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 2 );
|
||||||
|
REQUIRE( synth.getVoiceView(0)->getTriggerValue() == 64_norm);
|
||||||
REQUIRE(!synth.getVoiceView(0)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(0)->releasedOrFree());
|
||||||
REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 63_norm);
|
REQUIRE( synth.getVoiceView(1)->getTriggerValue() == 63_norm);
|
||||||
REQUIRE(synth.getVoiceView(1)->releasedOrFree()); // The first one is the masking candidate since they have the same velocity
|
REQUIRE( synth.getVoiceView(1)->releasedOrFree()); // The first one is the masking candidate since they have the same velocity
|
||||||
REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 63_norm);
|
REQUIRE( synth.getVoiceView(2)->getTriggerValue() == 63_norm);
|
||||||
REQUIRE(!synth.getVoiceView(2)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(2)->releasedOrFree());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,53 +283,59 @@ TEST_CASE("[Polyphony] Self-masking only works from low to high")
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<region> sample=*sine key=64 note_polyphony=1
|
<region> sample=*sine key=64 note_polyphony=1
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 64, 63);
|
synth.noteOn(0, 64, 63 );
|
||||||
synth.noteOn(0, 64, 62);
|
synth.noteOn(0, 64, 62 );
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2); // Both notes are playing
|
REQUIRE( synth.getNumActiveVoices(true) == 2 ); // Both notes are playing
|
||||||
REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
REQUIRE( synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
||||||
REQUIRE(!synth.getVoiceView(0)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(0)->releasedOrFree());
|
||||||
REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
REQUIRE( synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
||||||
REQUIRE(!synth.getVoiceView(1)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(1)->releasedOrFree());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same polyphony group (default)")
|
TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same polyphony group (default)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<region> sample=*saw key=64 note_polyphony=1
|
<region> sample=*saw key=64 note_polyphony=1
|
||||||
<region> sample=*sine key=64 note_polyphony=1
|
<region> sample=*sine key=64 note_polyphony=1
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 64, 62);
|
synth.noteOn(0, 64, 62 );
|
||||||
synth.noteOn(0, 64, 63);
|
synth.noteOn(0, 64, 63 );
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 4); // Both notes are playing
|
REQUIRE( synth.getNumActiveVoices(true) == 4);
|
||||||
REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 62_norm);
|
synth.renderBlock(buffer);
|
||||||
REQUIRE(synth.getVoiceView(0)->releasedOrFree()); // got killed
|
REQUIRE( numPlayingVoices(synth) == 1 );
|
||||||
REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
REQUIRE( synth.getVoiceView(0)->getTriggerValue() == 62_norm);
|
||||||
REQUIRE(synth.getVoiceView(1)->releasedOrFree()); // got killed
|
REQUIRE( synth.getVoiceView(0)->releasedOrFree()); // got killed
|
||||||
REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 63_norm);
|
REQUIRE( synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
||||||
REQUIRE(synth.getVoiceView(2)->releasedOrFree()); // got killed
|
REQUIRE( synth.getVoiceView(1)->releasedOrFree()); // got killed
|
||||||
REQUIRE(synth.getVoiceView(3)->getTriggerValue() == 63_norm);
|
REQUIRE( synth.getVoiceView(2)->getTriggerValue() == 63_norm);
|
||||||
|
REQUIRE( synth.getVoiceView(2)->releasedOrFree()); // got killed
|
||||||
|
REQUIRE( synth.getVoiceView(3)->getTriggerValue() == 63_norm);
|
||||||
REQUIRE(!synth.getVoiceView(3)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(3)->releasedOrFree());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same polyphony group (default, with keyswitches)")
|
TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same polyphony group (default, with keyswitches)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<global> sw_lokey=36 sw_hikey=37 sw_default=36
|
<global> sw_lokey=36 sw_hikey=37 sw_default=36
|
||||||
<region> sw_last=36 key=48 note_polyphony=1 sample=*saw
|
<region> sw_last=36 key=48 note_polyphony=1 sample=*saw
|
||||||
<region> sw_last=37 key=48 transpose=12 note_polyphony=1 sample=*tri
|
<region> sw_last=37 key=48 transpose=12 note_polyphony=1 sample=*tri
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 48, 63);
|
synth.noteOn(0, 48, 63 );
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
REQUIRE( synth.getNumActiveVoices(true) == 1);
|
||||||
synth.cc(0, 64, 127);
|
synth.cc(0, 64, 127);
|
||||||
synth.noteOn(0, 37, 127);
|
synth.noteOn(0, 37, 127);
|
||||||
synth.noteOff(0, 37, 0);
|
synth.noteOff(0, 37, 0);
|
||||||
synth.noteOn(0, 48, 64);
|
synth.noteOn(0, 48, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
||||||
REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
synth.renderBlock(buffer);
|
||||||
REQUIRE(synth.getVoiceView(0)->releasedOrFree());
|
REQUIRE( numPlayingVoices(synth) == 1 );
|
||||||
REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 64_norm);
|
REQUIRE( synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
||||||
|
REQUIRE( synth.getVoiceView(0)->releasedOrFree());
|
||||||
|
REQUIRE( synth.getVoiceView(1)->getTriggerValue() == 64_norm);
|
||||||
REQUIRE(!synth.getVoiceView(1)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(1)->releasedOrFree());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,40 +343,46 @@ TEST_CASE("[Polyphony] Note polyphony checks works across regions in the same po
|
||||||
TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups")
|
TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<region> group=1 sample=*saw key=64 note_polyphony=1
|
<region> group=1 sample=*saw key=64 note_polyphony=1
|
||||||
<region> group=2 sample=*sine key=64 note_polyphony=1
|
<region> group=2 sample=*sine key=64 note_polyphony=1
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 64, 62);
|
synth.noteOn(0, 64, 62 );
|
||||||
synth.noteOn(0, 64, 63);
|
synth.noteOn(0, 64, 63 );
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 4); // Both notes are playing
|
REQUIRE( synth.getNumActiveVoices(true) == 4); // Both notes are playing
|
||||||
REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 62_norm);
|
synth.renderBlock(buffer);
|
||||||
REQUIRE(synth.getVoiceView(0)->releasedOrFree()); // got killed
|
REQUIRE(numPlayingVoices(synth) == 2 );
|
||||||
REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
REQUIRE( synth.getVoiceView(0)->getTriggerValue() == 62_norm);
|
||||||
REQUIRE(synth.getVoiceView(1)->releasedOrFree()); // got killed
|
REQUIRE( synth.getVoiceView(0)->releasedOrFree()); // got killed
|
||||||
REQUIRE(synth.getVoiceView(2)->getTriggerValue() == 63_norm);
|
REQUIRE( synth.getVoiceView(1)->getTriggerValue() == 62_norm);
|
||||||
|
REQUIRE( synth.getVoiceView(1)->releasedOrFree()); // got killed
|
||||||
|
REQUIRE( synth.getVoiceView(2)->getTriggerValue() == 63_norm);
|
||||||
REQUIRE(!synth.getVoiceView(2)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(2)->releasedOrFree());
|
||||||
REQUIRE(synth.getVoiceView(3)->getTriggerValue() == 63_norm);
|
REQUIRE( synth.getVoiceView(3)->getTriggerValue() == 63_norm);
|
||||||
REQUIRE(!synth.getVoiceView(3)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(3)->releasedOrFree());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups (with keyswitches)")
|
TEST_CASE("[Polyphony] Note polyphony do not operate across polyphony groups (with keyswitches)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/polyphony.sfz", R"(
|
||||||
<global> sw_lokey=36 sw_hikey=37 sw_default=36
|
<global> sw_lokey=36 sw_hikey=37 sw_default=36
|
||||||
<region> group=1 sw_last=36 key=48 note_polyphony=1 sample=*saw
|
<region> group=1 sw_last=36 key=48 note_polyphony=1 sample=*saw
|
||||||
<region> group=2 sw_last=37 key=48 transpose=12 note_polyphony=1 sample=*tri
|
<region> group=2 sw_last=37 key=48 transpose=12 note_polyphony=1 sample=*tri
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 48, 63);
|
synth.noteOn(0, 48, 63 );
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 1);
|
REQUIRE( synth.getNumActiveVoices(true) == 1);
|
||||||
synth.cc(0, 64, 127);
|
synth.cc(0, 64, 127);
|
||||||
synth.noteOn(0, 37, 127);
|
synth.noteOn(0, 37, 127);
|
||||||
synth.noteOff(0, 37, 0);
|
synth.noteOff(0, 37, 0);
|
||||||
synth.noteOn(0, 48, 64);
|
synth.noteOn(0, 48, 64);
|
||||||
REQUIRE(synth.getNumActiveVoices(true) == 2);
|
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
||||||
REQUIRE(synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE(numPlayingVoices(synth) == 2 );
|
||||||
|
REQUIRE( synth.getVoiceView(0)->getTriggerValue() == 63_norm);
|
||||||
REQUIRE(!synth.getVoiceView(0)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(0)->releasedOrFree());
|
||||||
REQUIRE(synth.getVoiceView(1)->getTriggerValue() == 64_norm);
|
REQUIRE( synth.getVoiceView(1)->getTriggerValue() == 64_norm);
|
||||||
REQUIRE(!synth.getVoiceView(1)->releasedOrFree());
|
REQUIRE(!synth.getVoiceView(1)->releasedOrFree());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
// license. You should have receive a LICENSE.md file along with the code.
|
// 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
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
#include "RegionTHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include "sfizz/MidiState.h"
|
#include "sfizz/MidiState.h"
|
||||||
#include "sfizz/Region.h"
|
#include "sfizz/Region.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "sfizz/SisterVoiceRing.h"
|
#include "sfizz/SisterVoiceRing.h"
|
||||||
#include "sfizz/SfzHelpers.h"
|
#include "sfizz/SfzHelpers.h"
|
||||||
#include "sfizz/NumericId.h"
|
#include "sfizz/NumericId.h"
|
||||||
|
#include "TestHelpers.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
|
|
@ -607,7 +608,8 @@ TEST_CASE("[Synth] Sisters and off-by")
|
||||||
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
||||||
synth.noteOn(0, 63, 85);
|
synth.noteOn(0, 63, 85);
|
||||||
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
REQUIRE( synth.getNumActiveVoices(true) == 3 );
|
||||||
synth.renderBlock(buffer);
|
for (unsigned i = 0; i < 100; ++i)
|
||||||
|
synth.renderBlock(buffer);
|
||||||
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
REQUIRE( synth.getNumActiveVoices(true) == 2 );
|
||||||
REQUIRE( sfz::SisterVoiceRing::countSisterVoices(synth.getVoiceView(0)) == 1 );
|
REQUIRE( sfz::SisterVoiceRing::countSisterVoices(synth.getVoiceView(0)) == 1 );
|
||||||
}
|
}
|
||||||
|
|
@ -735,30 +737,6 @@ TEST_CASE("[Synth] Sustain threshold")
|
||||||
REQUIRE( synth.getNumActiveVoices(true) == 5 );
|
REQUIRE( synth.getNumActiveVoices(true) == 5 );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class C>
|
|
||||||
void sortAll(C& container)
|
|
||||||
{
|
|
||||||
std::sort(container.begin(), container.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class C, class... Args>
|
|
||||||
void sortAll(C& container, Args&... others)
|
|
||||||
{
|
|
||||||
std::sort(container.begin(), container.end());
|
|
||||||
sortAll(others...);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<const sfz::Voice*> getActiveVoices(const sfz::Synth& synth)
|
|
||||||
{
|
|
||||||
std::vector<const sfz::Voice*> activeVoices;
|
|
||||||
for (int i = 0; i < synth.getNumVoices(); ++i) {
|
|
||||||
const auto* voice = synth.getVoiceView(i);
|
|
||||||
if (!voice->isFree())
|
|
||||||
activeVoices.push_back(voice);
|
|
||||||
}
|
|
||||||
return activeVoices;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("[Synth] Release (Multiple notes, release_key ignores the pedal)")
|
TEST_CASE("[Synth] Release (Multiple notes, release_key ignores the pedal)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
// license. You should have receive a LICENSE.md file along with the code.
|
// 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
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
#include "RegionTHelpers.h"
|
#include "TestHelpers.h"
|
||||||
#include "sfizz/modulations/ModId.h"
|
#include "sfizz/modulations/ModId.h"
|
||||||
|
|
||||||
size_t RegionCCView::size() const
|
size_t RegionCCView::size() const
|
||||||
|
|
@ -39,3 +39,21 @@ bool RegionCCView::match(const sfz::Region::Connection& conn) const
|
||||||
{
|
{
|
||||||
return conn.source.id() == sfz::ModId::Controller && conn.target == target_;
|
return conn.source.id() == sfz::ModId::Controller && conn.target == target_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<const sfz::Voice*> getActiveVoices(const sfz::Synth& synth)
|
||||||
|
{
|
||||||
|
std::vector<const sfz::Voice*> activeVoices;
|
||||||
|
for (int i = 0; i < synth.getNumVoices(); ++i) {
|
||||||
|
const auto* voice = synth.getVoiceView(i);
|
||||||
|
if (!voice->isFree())
|
||||||
|
activeVoices.push_back(voice);
|
||||||
|
}
|
||||||
|
return activeVoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned numPlayingVoices(const sfz::Synth& synth)
|
||||||
|
{
|
||||||
|
return absl::c_count_if(getActiveVoices(synth), [](const sfz::Voice* v) {
|
||||||
|
return !v->releasedOrFree();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "sfizz/Synth.h"
|
||||||
#include "sfizz/Region.h"
|
#include "sfizz/Region.h"
|
||||||
#include "sfizz/modulations/ModKey.h"
|
#include "sfizz/modulations/ModKey.h"
|
||||||
|
|
||||||
|
|
@ -26,3 +27,32 @@ private:
|
||||||
const sfz::Region& region_;
|
const sfz::Region& region_;
|
||||||
sfz::ModKey target_;
|
sfz::ModKey target_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
void sortAll(C& container)
|
||||||
|
{
|
||||||
|
std::sort(container.begin(), container.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class C, class... Args>
|
||||||
|
void sortAll(C& container, Args&... others)
|
||||||
|
{
|
||||||
|
std::sort(container.begin(), container.end());
|
||||||
|
sortAll(others...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get active voices from the synth
|
||||||
|
*
|
||||||
|
* @param synth
|
||||||
|
* @return const std::vector<const sfz::Voice*>
|
||||||
|
*/
|
||||||
|
const std::vector<const sfz::Voice*> getActiveVoices(const sfz::Synth& synth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Count the number of playing (unreleased) voices from the synth
|
||||||
|
*
|
||||||
|
* @param synth
|
||||||
|
* @return unsigned
|
||||||
|
*/
|
||||||
|
unsigned numPlayingVoices(const sfz::Synth& synth);
|
||||||
Loading…
Add table
Reference in a new issue