Region and Synth use float velocities internally

This commit is contained in:
Paul Fd 2020-03-25 23:25:32 +01:00 committed by Paul Ferrand
parent 2655418406
commit 0e1e65135e
6 changed files with 206 additions and 103 deletions

View file

@ -68,7 +68,7 @@ namespace Default
// Region logic: key mapping
constexpr Range<uint8_t> keyRange { 0, 127 };
constexpr Range<uint8_t> velocityRange { 0, 127 };
constexpr Range<float> velocityRange { 0.0f, 1.0f };
// Region logic: MIDI conditions
constexpr Range<uint8_t> channelRange { 1, 16 };
@ -121,8 +121,8 @@ namespace Default
constexpr Range<float> ampRandomRange { 0.0, 24.0 };
constexpr Range<uint8_t> crossfadeKeyInRange { 0, 0 };
constexpr Range<uint8_t> crossfadeKeyOutRange { 127, 127 };
constexpr Range<uint8_t> crossfadeVelInRange { 0, 0 };
constexpr Range<uint8_t> crossfadeVelOutRange { 127, 127 };
constexpr Range<float> crossfadeVelInRange { 0.0f, 0.0f };
constexpr Range<float> crossfadeVelOutRange { 1.0f, 1.0f };
constexpr Range<uint8_t> crossfadeCCInRange { 0, 0 };
constexpr Range<uint8_t> crossfadeCCOutRange { 127, 127 };
constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power };

View file

@ -14,6 +14,7 @@
#include "MidiState.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_cat.h"
#include "absl/algorithm/container.h"
#include <random>
template<class T>
@ -138,10 +139,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange);
break;
case hash("lovel"):
setRangeStartFromOpcode(opcode, velocityRange, Default::velocityRange);
if (auto value = readOpcode(opcode.value, Default::midi7Range))
velocityRange.setStart(normalizeVelocity(*value));
break;
case hash("hivel"):
setRangeEndFromOpcode(opcode, velocityRange, Default::velocityRange);
if (auto value = readOpcode(opcode.value, Default::midi7Range))
velocityRange.setEnd(normalizeVelocity(*value));
break;
// Region logic: MIDI conditions
@ -303,8 +306,11 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
case hash("amp_velcurve_&"):
{
auto value = readOpcode(opcode.value, Default::ampVelcurveRange);
if (opcode.parameters.back() > 127)
return false;
if (value)
velocityPoints.emplace_back(opcode.parameters.back(), *value);
velocityPoints.emplace_back(normalizeVelocity(opcode.parameters.back()), *value);
}
break;
case hash("xfin_lokey"):
@ -320,16 +326,20 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setRangeEndFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange);
break;
case hash("xfin_lovel"):
setRangeStartFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange);
if (auto value = readOpcode(opcode.value, Default::midi7Range))
crossfadeVelInRange.setStart(normalizeVelocity(*value));
break;
case hash("xfin_hivel"):
setRangeEndFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange);
if (auto value = readOpcode(opcode.value, Default::midi7Range))
crossfadeVelInRange.setEnd(normalizeVelocity(*value));
break;
case hash("xfout_lovel"):
setRangeStartFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
if (auto value = readOpcode(opcode.value, Default::midi7Range))
crossfadeVelOutRange.setStart(normalizeVelocity(*value));
break;
case hash("xfout_hivel"):
setRangeEndFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
if (auto value = readOpcode(opcode.value, Default::midi7Range))
crossfadeVelOutRange.setEnd(normalizeVelocity(*value));
break;
case hash("xf_keycurve"):
switch (hash(opcode.value)) {
@ -773,6 +783,13 @@ bool sfz::Region::isSwitchedOn() const noexcept
bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept
{
return registerNoteOnNormalized(noteNumber, normalizeVelocity(velocity), randValue);
}
bool sfz::Region::registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
if (keyswitchRange.containsWithEnd(noteNumber)) {
if (keyswitch) {
if (*keyswitch == noteNumber)
@ -825,6 +842,13 @@ bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randVal
bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept
{
return registerNoteOffNormalized(noteNumber, normalizeVelocity(velocity), randValue);
}
bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
if (keyswitchRange.containsWithEnd(noteNumber)) {
if (keyswitchDown && *keyswitchDown == noteNumber)
keySwitched = false;
@ -893,11 +917,18 @@ void sfz::Region::registerTempo(float secondsPerQuarter) noexcept
float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept
{
return getBasePitchVariationNormalized(noteNumber, normalizeVelocity(velocity));
}
float sfz::Region::getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
std::uniform_int_distribution<int> pitchDistribution { -pitchRandom, pitchRandom };
auto pitchVariationInCents = pitchKeytrack * (noteNumber - (int)pitchKeycenter); // note difference with pitch center
pitchVariationInCents += tune; // sample tuning
pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose
pitchVariationInCents += velocity / 127 * pitchVeltrack; // track velocity
pitchVariationInCents += velocity * pitchVeltrack; // track velocity
pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes
return centsFactor(pitchVariationInCents);
}
@ -961,8 +992,13 @@ float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurv
{
if (value < crossfadeRange.getStart())
return 0.0f;
const auto length = static_cast<float>(crossfadeRange.length());
if (length == 0.0f)
return 1.0f;
else if (value < crossfadeRange.getEnd()) {
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / std::max(static_cast<float>(crossfadeRange.length()), 1.0f);
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
if (curve == SfzCrossfadeCurve::power)
return sqrt(crossfadePosition);
if (curve == SfzCrossfadeCurve::gain)
@ -977,8 +1013,13 @@ float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCur
{
if (value > crossfadeRange.getEnd())
return 0.0f;
const auto length = static_cast<float>(crossfadeRange.length());
if (length == 0.0f)
return 1.0f;
else if (value > crossfadeRange.getStart()) {
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / std::max(static_cast<float>(crossfadeRange.length()), 1.0f);
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
if (curve == SfzCrossfadeCurve::power)
return std::sqrt(1 - crossfadePosition);
if (curve == SfzCrossfadeCurve::gain)
@ -990,6 +1031,13 @@ float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCur
float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept
{
return getNoteGainNormalized(noteNumber, normalizeVelocity(velocity));
}
float sfz::Region::getNoteGainNormalized(int noteNumber, float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
float baseGain { 1.0f };
// Amplitude key tracking
@ -1000,7 +1048,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept
baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve);
// Amplitude velocity tracking
baseGain *= velocityCurve(velocity);
baseGain *= velocityCurveNormalized(velocity);
// Crossfades related to velocity
baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve);
@ -1031,25 +1079,30 @@ float sfz::Region::getCrossfadeGain() const noexcept
float sfz::Region::velocityCurve(uint8_t velocity) const noexcept
{
float gain { 1.0f };
return velocityCurveNormalized(normalizeVelocity(velocity));
}
float sfz::Region::velocityCurveNormalized(float velocity) const noexcept
{
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
float gain { 1.0f };
if (velocityPoints.size() > 0) { // Custom velocity curve
auto after = std::find_if(velocityPoints.begin(), velocityPoints.end(), [velocity](const std::pair<int, float>& val) { return val.first >= velocity; });
auto after = absl::c_find_if(velocityPoints, [velocity](const std::pair<float, float>& val) { return val.first >= velocity; });
auto before = after == velocityPoints.begin() ? velocityPoints.begin() : after - 1;
// Linear interpolation
float relativePositionInSegment {
static_cast<float>(velocity - before->first) / static_cast<float>(after->first - before->first)
(velocity - before->first) / (after->first - before->first)
};
float segmentEndpoints { after->second - before->second };
gain *= relativePositionInSegment * segmentEndpoints;
} else { // Standard velocity curve
const float floatVelocity { static_cast<float>(velocity) / 127.0f };
// FIXME: Maybe there's a prettier way to check the boundaries?
const float gaindB = [&]() {
if (ampVeltrack >= 0)
return floatVelocity == 0.0f ? -90.0f : 40 * std::log(floatVelocity) / std::log(10.0f);
return velocity == 0.0f ? -90.0f : 40 * std::log(velocity) / std::log(10.0f);
else
return floatVelocity == 1.0f ? -90.0f : 40 * std::log(1 - floatVelocity) / std::log(10.0f);
return velocity == 1.0f ? -90.0f : 40 * std::log(1 - velocity) / std::log(10.0f);
}();
gain *= db2mag( gaindB * std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd());
}

View file

@ -98,6 +98,30 @@ struct Region {
* @return false
*/
bool registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept;
/**
* @brief Register a new note on event. The region may be switched on or off using keys so
* this function updates the keyswitches state.
*
* @param noteNumber
* @param velocity
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
* and vary the samples
* @return true if the region should trigger on this event.
* @return false
*/
bool registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept;
/**
* @brief Register a new note off event. The region may be switched on or off using keys so
* this function updates the keyswitches state.
*
* @param noteNumber
* @param velocity
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
* and vary the samples
* @return true if the region should trigger on this event.
* @return false
*/
bool registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept;
/**
* @brief Register a new CC event. The region may be switched on or off using CCs so
* this function checks if it indeeds need to activate or not.
@ -136,6 +160,15 @@ struct Region {
* @return float
*/
float getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept;
/**
* @brief Get the base pitch of the region depending on which note has been
* pressed and at which velocity.
*
* @param noteNumber
* @param velocity
* @return float
*/
float getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept;
/**
* @brief Get the note-related gain of the region depending on which note has been
* pressed and at which velocity.
@ -145,6 +178,15 @@ struct Region {
* @return float
*/
float getNoteGain(int noteNumber, uint8_t velocity) const noexcept;
/**
* @brief Get the note-related gain of the region depending on which note has been
* pressed and at which velocity.
*
* @param noteNumber
* @param velocity
* @return float
*/
float getNoteGainNormalized(int noteNumber, float velocity) const noexcept;
/**
* @brief Get the additional crossfade gain of the region depending on the
* CC values
@ -179,6 +221,12 @@ struct Region {
* @return float
*/
float velocityCurve(uint8_t velocity) const noexcept;
/**
* @brief Computes the gain value related to the velocity of the note
*
* @return float
*/
float velocityCurveNormalized(float velocity) const noexcept;
/**
* @brief Get the region offset in samples
*
@ -243,7 +291,7 @@ struct Region {
// Region logic: key mapping
Range<uint8_t> keyRange { Default::keyRange }; //lokey, hikey and key
Range<uint8_t> velocityRange { Default::velocityRange }; // hivel and lovel
Range<float> velocityRange { Default::velocityRange }; // hivel and lovel
// Region logic: MIDI conditions
Range<int> bendRange { Default::bendRange }; // hibend and lobend
@ -282,12 +330,12 @@ struct Region {
uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter
float ampKeytrack { Default::ampKeytrack }; // amp_keytrack
float ampVeltrack { Default::ampVeltrack }; // amp_keytrack
std::vector<std::pair<int, float>> velocityPoints; // amp_velcurve_N
std::vector<std::pair<float, float>> velocityPoints; // amp_velcurve_N
float ampRandom { Default::ampRandom }; // amp_random
Range<uint8_t> crossfadeKeyInRange { Default::crossfadeKeyInRange };
Range<uint8_t> crossfadeKeyOutRange { Default::crossfadeKeyOutRange };
Range<uint8_t> crossfadeVelInRange { Default::crossfadeVelInRange };
Range<uint8_t> crossfadeVelOutRange { Default::crossfadeVelOutRange };
Range<float> crossfadeVelInRange { Default::crossfadeVelInRange };
Range<float> crossfadeVelOutRange { Default::crossfadeVelOutRange };
SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve };
SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve };
SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve };

View file

@ -44,7 +44,7 @@ sfz::Synth::~Synth()
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
for (auto& voice: voices)
for (auto& voice : voices)
voice->reset();
resources.filePool.emptyFileLoadingQueues();
@ -129,11 +129,11 @@ void sfz::Synth::clear()
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
for (auto &voice: voices)
for (auto& voice : voices)
voice->reset();
for (auto& list: noteActivationLists)
for (auto& list : noteActivationLists)
list.clear();
for (auto& list: ccActivationLists)
for (auto& list : ccActivationLists)
list.clear();
regions.clear();
effectBuses.clear();
@ -275,17 +275,19 @@ void sfz::Synth::handleEffectOpcodes(const std::vector<Opcode>& members)
void addEndpointsToVelocityCurve(sfz::Region& region)
{
if (region.velocityPoints.size() > 0) {
absl::c_sort(region.velocityPoints, [](const std::pair<int, float>& lhs, const std::pair<int, float>& rhs) { return lhs.first < rhs.first; });
const auto velocityStart = sfz::Default::velocityRange.getStart();
const auto velocityEnd = sfz::Default::velocityRange.getEnd();
absl::c_sort(region.velocityPoints, [](const std::pair<float, float>& lhs, const std::pair<float, float>& rhs) { return lhs.first < rhs.first; });
if (region.ampVeltrack > 0) {
if (region.velocityPoints.back().first != sfz::Default::velocityRange.getEnd())
region.velocityPoints.push_back(std::make_pair<int, float>(127, 1.0f));
if (region.velocityPoints.front().first != sfz::Default::velocityRange.getStart())
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair<int, float>(0, 0.0f));
if (region.velocityPoints.front().first != velocityStart)
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityStart, velocityStart));
if (region.velocityPoints.back().first != velocityEnd)
region.velocityPoints.push_back(std::make_pair(velocityEnd, velocityEnd));
} else {
if (region.velocityPoints.front().first != sfz::Default::velocityRange.getEnd())
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair<int, float>(127, 0.0f));
if (region.velocityPoints.back().first != sfz::Default::velocityRange.getStart())
region.velocityPoints.push_back(std::make_pair<int, float>(0, 1.0f));
if (region.velocityPoints.front().first != velocityEnd)
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityEnd, velocityStart));
if (region.velocityPoints.back().first != velocityStart)
region.velocityPoints.push_back(std::make_pair(velocityStart, velocityEnd));
}
}
}
@ -341,8 +343,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
if (region->loopRange.getEnd() == Default::loopRange.getEnd())
region->loopRange.setEnd(region->sampleEnd);
if (fileInformation->loopBegin != Default::loopRange.getStart() &&
fileInformation->loopEnd != Default::loopRange.getEnd()) {
if (fileInformation->loopBegin != Default::loopRange.getStart() && fileInformation->loopEnd != Default::loopRange.getEnd()) {
if (region->loopRange.getStart() == Default::loopRange.getStart())
region->loopRange.setStart(fileInformation->loopBegin);
@ -374,8 +375,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
}
for (auto note = 0; note < 128; note++) {
if (region->keyRange.containsWithEnd(note) ||
(region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note)))
if (region->keyRange.containsWithEnd(note) || (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note)))
noteActivationLists[note].push_back(region);
}
@ -396,16 +396,13 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
// Set the default frequencies on equalizers if needed
if (region->equalizers.size() > 0
&& region->equalizers[0].frequency == Default::eqFrequencyUnset)
{
&& region->equalizers[0].frequency == Default::eqFrequencyUnset) {
region->equalizers[0].frequency = Default::eqFrequency1;
if (region->equalizers.size() > 1
&& region->equalizers[1].frequency == Default::eqFrequencyUnset)
{
&& region->equalizers[1].frequency == Default::eqFrequencyUnset) {
region->equalizers[1].frequency = Default::eqFrequency2;
if (region->equalizers.size() > 2
&& region->equalizers[2].frequency == Default::eqFrequencyUnset)
{
&& region->equalizers[2].frequency == Default::eqFrequencyUnset) {
region->equalizers[2].frequency = Default::eqFrequency3;
}
}
@ -425,7 +422,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
regions.resize(remainingRegions);
modificationTime = checkModificationTime();
for (auto& voice: voices) {
for (auto& voice : voices) {
voice->setMaxFiltersPerVoice(maxFilters);
voice->setMaxEQsPerVoice(maxEQs);
}
@ -468,7 +465,6 @@ int sfz::Synth::getNumActiveVoices() const noexcept
void sfz::Synth::garbageCollect() noexcept
{
}
void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
@ -487,7 +483,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
for (auto& voice : voices)
voice->setSamplesPerBlock(samplesPerBlock);
for (auto& bus: effectBuses) {
for (auto& bus : effectBuses) {
if (bus)
bus->setSamplesPerBlock(samplesPerBlock);
}
@ -507,7 +503,7 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept
resources.filterPool.setSampleRate(sampleRate);
resources.eqPool.setSampleRate(sampleRate);
for (auto& bus: effectBuses) {
for (auto& bus : effectBuses) {
if (bus)
bus->setSampleRate(sampleRate);
}
@ -532,7 +528,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
{ // Prepare the effect inputs. They are mixes of per-region outputs.
ScopedTiming logger { callbackBreakdown.effects };
for (auto& bus: effectBuses) {
for (auto& bus : effectBuses) {
if (bus)
bus->clearInputs(numFrames);
}
@ -576,7 +572,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
// without any <effect>, the signal is just going to flow through it.
ScopedTiming logger { callbackBreakdown.effects, ScopedTiming::Operation::addToDuration };
for (auto& bus: effectBuses) {
for (auto& bus : effectBuses) {
if (bus) {
bus->process(numFrames);
bus->mixOutputsTo(buffer, tempMixNode, numFrames);
@ -711,11 +707,11 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
resources.midiState.pitchBendEvent(delay, pitch);
for (auto& region: regions) {
for (auto& region : regions) {
region->registerPitchWheel(pitch);
}
for (auto& voice: voices) {
for (auto& voice : voices) {
voice->registerPitchWheel(delay, pitch);
}
}
@ -753,10 +749,9 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
if (model.empty())
model = config::midnamModel;
doc.append_child(pugi::node_doctype).set_value(
"MIDINameDocument PUBLIC"
" \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\""
" \"http://www.midi.org/dtds/MIDINameDocument10.dtd\"");
doc.append_child(pugi::node_doctype).set_value("MIDINameDocument PUBLIC"
" \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\""
" \"http://www.midi.org/dtds/MIDINameDocument10.dtd\"");
pugi::xml_node root = doc.append_child("MIDINameDocument");
@ -767,9 +762,11 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
pugi::xml_node device = root.append_child("MasterDeviceNames");
device.append_child("Manufacturer")
.append_child(pugi::node_pcdata).set_value(std::string(manufacturer).c_str());
.append_child(pugi::node_pcdata)
.set_value(std::string(manufacturer).c_str());
device.append_child("Model")
.append_child(pugi::node_pcdata).set_value(std::string(model).c_str());
.append_child(pugi::node_pcdata)
.set_value(std::string(model).c_str());
{
pugi::xml_node devmode = device.append_child("CustomDeviceMode");
@ -795,7 +792,8 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
}
chns.append_child("UsesControlNameList")
.append_attribute("Name").set_value("Controls");
.append_attribute("Name")
.set_value("Controls");
}
{
@ -876,7 +874,7 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept
void sfz::Synth::resetVoices(int numVoices)
{
AtomicDisabler callbackDisabler{ canEnterCallback };
AtomicDisabler callbackDisabler { canEnterCallback };
while (inCallback) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
@ -885,7 +883,7 @@ void sfz::Synth::resetVoices(int numVoices)
for (int i = 0; i < numVoices; ++i)
voices.push_back(absl::make_unique<Voice>(resources));
for (auto& voice: voices) {
for (auto& voice : voices) {
voice->setSampleRate(this->sampleRate);
voice->setSamplesPerBlock(this->samplesPerBlock);
}
@ -896,12 +894,12 @@ void sfz::Synth::resetVoices(int numVoices)
void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept
{
AtomicDisabler callbackDisabler{ canEnterCallback };
AtomicDisabler callbackDisabler { canEnterCallback };
while (inCallback) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
for (auto& voice: voices)
for (auto& voice : voices)
voice->reset();
resources.filePool.emptyFileLoadingQueues();
@ -916,7 +914,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept
void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept
{
AtomicDisabler callbackDisabler{ canEnterCallback };
AtomicDisabler callbackDisabler { canEnterCallback };
while (inCallback) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
@ -951,13 +949,13 @@ void sfz::Synth::resetAllControllers(int delay) noexcept
return;
resources.midiState.resetAllControllers(delay);
for (auto& voice: voices) {
for (auto& voice : voices) {
voice->registerPitchWheel(delay, 0);
for (unsigned cc = 0; cc < config::numCCs; ++cc)
voice->registerCC(delay, cc, 0);
}
for (auto& region: regions) {
for (auto& region : regions) {
for (unsigned cc = 0; cc < config::numCCs; ++cc)
region->registerCC(cc, 0);
}
@ -996,13 +994,13 @@ void sfz::Synth::disableLogging() noexcept
void sfz::Synth::allSoundOff() noexcept
{
AtomicDisabler callbackDisabler{ canEnterCallback };
AtomicDisabler callbackDisabler { canEnterCallback };
while (inCallback) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
for (auto &voice: voices)
for (auto& voice : voices)
voice->reset();
for (auto& effectBus: effectBuses)
for (auto& effectBus : effectBuses)
effectBus->clear();
}

View file

@ -5,9 +5,11 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "sfizz/Synth.h"
#include "sfizz/SfzHelpers.h"
#include "catch2/catch.hpp"
#include "ghc/fs_std.hpp"
using namespace Catch::literals;
using namespace sfz::literals;
TEST_CASE("[Files] Single region (regions_one.sfz)")
{
@ -137,11 +139,11 @@ TEST_CASE("[Files] Group from AVL")
REQUIRE(synth.getRegionView(i)->volume == 6.0f);
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(36, 36));
}
REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range<uint8_t>(1, 26));
REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range<uint8_t>(27, 52));
REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range<uint8_t>(53, 77));
REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range<uint8_t>(78, 102));
REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range<uint8_t>(103, 127));
REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range<float>(1_norm, 26_norm));
REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range<float>(27_norm, 52_norm));
REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range<float>(53_norm, 77_norm));
REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range<float>(78_norm, 102_norm));
REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range<float>(103_norm, 127_norm));
}
TEST_CASE("[Files] Full hierarchy")
@ -232,7 +234,7 @@ TEST_CASE("[Files] Pizz basic")
REQUIRE(synth.getNumRegions() == 4);
for (int i = 0; i < synth.getNumRegions(); ++i) {
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(12, 22));
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<uint8_t>(97, 127));
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<float>(97_norm, 127_norm));
REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21);
REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range<uint8_t>(0, 13));
}

View file

@ -6,8 +6,10 @@
#include "sfizz/MidiState.h"
#include "sfizz/Region.h"
#include "sfizz/SfzHelpers.h"
#include "catch2/catch.hpp"
using namespace Catch::literals;
using namespace sfz::literals;
TEST_CASE("[Region] Parsing opcodes")
{
@ -204,19 +206,19 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("lovel, hivel")
{
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
region.parseOpcode({ "lovel", "37" });
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(37, 127));
REQUIRE(region.velocityRange == sfz::Range<float>(37_norm, 127_norm));
region.parseOpcode({ "lovel", "128" });
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(127, 127));
REQUIRE(region.velocityRange == sfz::Range<float>(127_norm, 127_norm));
region.parseOpcode({ "lovel", "-3" });
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
region.parseOpcode({ "hivel", "65" });
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 65));
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 65_norm));
region.parseOpcode({ "hivel", "-1" });
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 0_norm));
region.parseOpcode({ "hivel", "128" });
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
}
SECTION("lobend, hibend")
@ -563,9 +565,9 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("amp_velcurve")
{
region.parseOpcode({ "amp_velcurve_6", "0.4" });
REQUIRE(region.velocityPoints.back() == std::make_pair<int, float>(6, 0.4f));
REQUIRE(region.velocityPoints.back() == std::make_pair<float, float>(6_norm, 0.4f));
region.parseOpcode({ "amp_velcurve_127", "-1.0" });
REQUIRE(region.velocityPoints.back() == std::make_pair<int, float>(127, 0.0f));
REQUIRE(region.velocityPoints.back() == std::make_pair<float, float>(127_norm, 0.0f));
}
SECTION("xfin_lokey, xfin_hikey")
@ -589,21 +591,21 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("xfin_lovel, xfin_hivel")
{
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
region.parseOpcode({ "xfin_lovel", "4" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(4, 4));
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(4_norm, 4_norm));
region.parseOpcode({ "xfin_lovel", "128" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(127, 127));
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(127_norm, 127_norm));
region.parseOpcode({ "xfin_lovel", "59" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 127));
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfin_hivel", "59" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 59));
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 59_norm));
region.parseOpcode({ "xfin_hivel", "128" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 127));
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfin_hivel", "0" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
region.parseOpcode({ "xfin_hivel", "-1" });
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
}
SECTION("xfout_lokey, xfout_hikey")
@ -627,21 +629,21 @@ TEST_CASE("[Region] Parsing opcodes")
SECTION("xfout_lovel, xfout_hivel")
{
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(127, 127));
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(127_norm, 127_norm));
region.parseOpcode({ "xfout_lovel", "4" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(4, 127));
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(4_norm, 127_norm));
region.parseOpcode({ "xfout_lovel", "128" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(127, 127));
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(127_norm, 127_norm));
region.parseOpcode({ "xfout_lovel", "59" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 127));
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfout_hivel", "59" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 59));
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 59_norm));
region.parseOpcode({ "xfout_hivel", "128" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 127));
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm));
region.parseOpcode({ "xfout_hivel", "0" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm));
region.parseOpcode({ "xfout_hivel", "-1" });
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(0, 0));
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm));
}
SECTION("xfin_locc, xfin_hicc")