Added support for the rt_decay opcode
This commit is contained in:
parent
2714c0a0e5
commit
61d1f1963c
7 changed files with 72 additions and 7 deletions
|
|
@ -113,6 +113,8 @@ namespace Default
|
|||
inline constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power };
|
||||
inline constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power };
|
||||
inline constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power };
|
||||
inline constexpr float rtDecay { 0.0f };
|
||||
inline constexpr Range<float> rtDecayRange { 0.0f, 200.0f };
|
||||
|
||||
// Performance parameters: pitch
|
||||
inline constexpr uint8_t pitchKeycenter { 60 };
|
||||
|
|
|
|||
23
sfizz/MidiState.h
Normal file
23
sfizz/MidiState.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#include <chrono>
|
||||
#include <array>
|
||||
|
||||
namespace sfz
|
||||
{
|
||||
inline std::array<std::chrono::steady_clock::time_point, 128> noteOnTimes { };
|
||||
inline void setNoteOnTime(int noteNumber)
|
||||
{
|
||||
if (noteNumber >= 0 && noteNumber < 128)
|
||||
noteOnTimes[noteNumber] = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
inline float getNoteDuration(int noteNumber)
|
||||
{
|
||||
if (noteNumber >= 0 && noteNumber < 128) {
|
||||
const auto noteOffTime = std::chrono::steady_clock::now();
|
||||
const auto duration = std::chrono::duration_cast<std::chrono::duration<float>>(noteOffTime - noteOnTimes[noteNumber]);
|
||||
return duration.count();
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
#include "Debug.h"
|
||||
#include "Opcode.h"
|
||||
#include "StringViewHelpers.h"
|
||||
#include "MidiState.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include <random>
|
||||
|
||||
|
|
@ -381,6 +382,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
DBG("Unknown crossfade power curve: " << std::string(opcode.value));
|
||||
}
|
||||
break;
|
||||
case hash("rt_decay"):
|
||||
setValueFromOpcode(opcode, rtDecay, Default::rtDecayRange);
|
||||
break;
|
||||
|
||||
// Performance parameters: pitch
|
||||
case hash("pitch_keycenter"):
|
||||
|
|
@ -629,9 +633,12 @@ float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) noexc
|
|||
return centsFactor(pitchVariationInCents);
|
||||
}
|
||||
|
||||
float sfz::Region::getBaseVolumedB() noexcept
|
||||
float sfz::Region::getBaseVolumedB(int noteNumber) noexcept
|
||||
{
|
||||
return volume + volumeDistribution(Random::randomGenerator);
|
||||
auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator);
|
||||
if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key)
|
||||
baseVolumedB -= rtDecay * getNoteDuration(noteNumber);
|
||||
return baseVolumedB;
|
||||
}
|
||||
|
||||
float sfz::Region::getBaseGain() noexcept
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ struct Region {
|
|||
float getBasePitchVariation(int noteNumber, uint8_t velocity) noexcept;
|
||||
float getNoteGain(int noteNumber, uint8_t velocity) noexcept;
|
||||
float getCrossfadeGain(const CCValueArray& ccState) noexcept;
|
||||
float getBaseVolumedB() noexcept;
|
||||
float getBaseVolumedB(int noteNumber) noexcept;
|
||||
float getBaseGain() noexcept;
|
||||
float velocityCurve(uint8_t velocity) const noexcept;
|
||||
uint32_t getOffset() noexcept;
|
||||
|
|
@ -136,6 +136,8 @@ struct Region {
|
|||
SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve };
|
||||
CCMap<Range<uint8_t>> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN
|
||||
CCMap<Range<uint8_t>> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN
|
||||
float rtDecay { Default::rtDecay }; // rt_decay
|
||||
|
||||
|
||||
// Performance parameters: pitch
|
||||
uint8_t pitchKeycenter { Default::pitchKeycenter }; // pitch_keycenter
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "Synth.h"
|
||||
#include "MidiState.h"
|
||||
#include "Config.h"
|
||||
#include "Debug.h"
|
||||
#include "ScopedFTZ.h"
|
||||
|
|
@ -309,7 +310,10 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
|
||||
void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity) noexcept
|
||||
{
|
||||
ASSERT(noteNumber <= 128);
|
||||
ASSERT(noteNumber < 128);
|
||||
ASSERT(noteNumber >= 0);
|
||||
|
||||
setNoteOnTime(noteNumber);
|
||||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||
|
||||
for (auto& region : noteActivationLists[noteNumber]) {
|
||||
|
|
@ -334,7 +338,9 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
|
|||
|
||||
void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocity) noexcept
|
||||
{
|
||||
ASSERT(noteNumber <= 128);
|
||||
ASSERT(noteNumber < 128);
|
||||
ASSERT(noteNumber >= 0);
|
||||
|
||||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||
for (auto& voice : voices)
|
||||
voice->registerNoteOff(delay, channel, noteNumber, velocity);
|
||||
|
|
@ -356,7 +362,9 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
|
|||
|
||||
void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept
|
||||
{
|
||||
ASSERT(ccNumber <= 128);
|
||||
ASSERT(ccNumber < 128);
|
||||
ASSERT(ccNumber >= 0);
|
||||
|
||||
for (auto& voice : voices)
|
||||
voice->registerCC(delay, channel, ccNumber, ccValue);
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number,
|
|||
speedRatio = static_cast<float>(region->sampleRate / this->sampleRate);
|
||||
pitchRatio = region->getBasePitchVariation(number, value);
|
||||
|
||||
baseVolumedB = region->getBaseVolumedB();
|
||||
baseVolumedB = region->getBaseVolumedB(number);
|
||||
|
||||
auto volumedB { baseVolumedB };
|
||||
if (region->volumeCC)
|
||||
|
|
|
|||
|
|
@ -21,9 +21,13 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "Defaults.h"
|
||||
#include "Region.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include <SfzHelpers.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include "MidiState.h"
|
||||
using namespace Catch::literals;
|
||||
|
||||
TEST_CASE("[Region] Crossfade in on key")
|
||||
|
|
@ -257,4 +261,23 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
|
|||
region.parseOpcode({ "amp_veltrack", "-100" });
|
||||
REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) );
|
||||
REQUIRE( region.getNoteGain(64, 0) == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] rt_decay")
|
||||
{
|
||||
sfz::Region region {};
|
||||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "trigger", "release" });
|
||||
region.parseOpcode({ "rt_decay", "10" });
|
||||
sfz::setNoteOnTime(64);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
|
||||
region.parseOpcode({ "rt_decay", "20" });
|
||||
sfz::setNoteOnTime(64);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
|
||||
region.parseOpcode({ "trigger", "attack" });
|
||||
sfz::setNoteOnTime(64);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue