Use advanceTime to compute the note-on time

This commit is contained in:
Paul Ferrand 2020-03-28 23:41:15 +01:00
parent 45124cce53
commit 6a66077911
4 changed files with 63 additions and 14 deletions

View file

@ -20,7 +20,7 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex
if (noteNumber >= 0 && noteNumber < 128) {
lastNoteVelocities[noteNumber] = velocity;
noteOnTimes[noteNumber] = std::chrono::steady_clock::now();
noteOnTimes[noteNumber] = internalClock + static_cast<unsigned>(delay);
activeNotes++;
}
@ -28,6 +28,7 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex
void sfz::MidiState::noteOffEvent(int delay, int noteNumber, float velocity) noexcept
{
ASSERT(delay >= 0);
ASSERT(noteNumber >= 0 && noteNumber <= 127);
ASSERT(velocity >= 0.0 && velocity <= 1.0);
UNUSED(velocity);
@ -38,14 +39,30 @@ void sfz::MidiState::noteOffEvent(int delay, int noteNumber, float velocity) noe
}
float sfz::MidiState::getNoteDuration(int noteNumber) const
void sfz::MidiState::setSampleRate(float sampleRate) noexcept
{
ASSERT(noteNumber >= 0 && noteNumber <= 127);
this->sampleRate = sampleRate;
internalClock = 0;
absl::c_fill(noteOnTimes, 0);
}
void sfz::MidiState::advanceTime(int numSamples) noexcept
{
internalClock += numSamples;
}
void sfz::MidiState::setSamplesPerBlock(int samplesPerBlock) noexcept
{
this->samplesPerBlock = samplesPerBlock;
}
float sfz::MidiState::getNoteDuration(int noteNumber, int delay) const
{
ASSERT(noteNumber >= 0 && noteNumber < 128);
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();
const unsigned timeInSamples = internalClock + static_cast<unsigned>(delay) - noteOnTimes[noteNumber];
return static_cast<float>(timeInSamples) / sampleRate;
}
return 0.0f;
@ -95,6 +112,8 @@ void sfz::MidiState::reset(int delay) noexcept
pitchBend = 0;
activeNotes = 0;
internalClock = 0;
absl::c_fill(noteOnTimes, 0);
}
void sfz::MidiState::resetAllControllers(int delay) noexcept

View file

@ -5,7 +5,6 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include <chrono>
#include <array>
#include "CCMap.h"
#include "Range.h"
@ -42,13 +41,29 @@ public:
int getActiveNotes() const noexcept { return activeNotes; }
/**
* @brief Register a note off and get the note duration
* @brief Get the note duration since note on
*
* @param noteNumber
* @param delay
* @return float
*/
float getNoteDuration(int noteNumber) const;
float getNoteDuration(int noteNumber, int delay = 0) const;
/**
* @brief Set the maximum size of the blocks for the callback. The actual
* size can be lower in each callback but should not be larger
* than this value.
*
* @param samplesPerBlock
*/
void setSamplesPerBlock(int samplesPerBlock) noexcept;
/**
* @brief Set the sample rate. If you do not call it it is initialized
* to sfz::config::defaultSampleRate.
*
* @param sampleRate
*/
void setSampleRate(float sampleRate) noexcept;
/**
* @brief Get the note on velocity for a given note
*
@ -79,6 +94,13 @@ public:
*/
void ccEvent(int delay, int ccNumber, float ccValue) noexcept;
/**
* @brief Advances the internal clock of a given amount of samples.
* You should call this at each callback.
*
* @param numSamples the number of samples of clock advance
*/
void advanceTime(int numSamples) noexcept;
/**
* @brief Get the CC value for CC number
*
@ -121,13 +143,13 @@ public:
private:
template<class T>
using MidiNoteArray = std::array<T, 128>;
using NoteOnTime = std::chrono::steady_clock::time_point;
int activeNotes { 0 };
/**
* @brief Stores the note on times.
*
*/
MidiNoteArray<NoteOnTime> noteOnTimes;
MidiNoteArray<unsigned> noteOnTimes {};
/**
* @brief Stores the velocity of the note ons for currently
* depressed notes.
@ -143,5 +165,8 @@ private:
* Pitch bend status
*/
int pitchBend { 0 };
double sampleRate { config::defaultSampleRate };
int samplesPerBlock { config::defaultSamplesPerBlock };
unsigned internalClock { 0 };
};
}

View file

@ -483,6 +483,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
this->tempMixNodeBuffer.resize(samplesPerBlock);
for (auto& voice : voices)
voice->setSamplesPerBlock(samplesPerBlock);
resources.midiState.setSamplesPerBlock(samplesPerBlock);
for (auto& bus : effectBuses) {
if (bus)
@ -503,6 +504,7 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept
resources.filterPool.setSampleRate(sampleRate);
resources.eqPool.setSampleRate(sampleRate);
resources.midiState.setSampleRate(sampleRate);
for (auto& bus : effectBuses) {
if (bus)
@ -525,6 +527,8 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
auto temp = AudioSpan<float>(tempBuffer).first(numFrames);
auto tempMixNode = AudioSpan<float>(tempMixNodeBuffer).first(numFrames);
resources.midiState.advanceTime(buffer.getNumFrames());
CallbackBreakdown callbackBreakdown;
{ // Prepare the effect inputs. They are mixes of per-region outputs.

View file

@ -262,20 +262,21 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
TEST_CASE("[Region] rt_decay")
{
sfz::MidiState midiState;
midiState.setSampleRate(1000);
sfz::Region region { midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "trigger", "release" });
region.parseOpcode({ "rt_decay", "10" });
midiState.noteOnEvent(0, 64, 64_norm);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
midiState.advanceTime(100);
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
region.parseOpcode({ "rt_decay", "20" });
midiState.noteOnEvent(0, 64, 64_norm);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
midiState.advanceTime(100);
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
region.parseOpcode({ "trigger", "attack" });
midiState.noteOnEvent(0, 64, 64_norm);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
midiState.advanceTime(100);
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
}