Use advanceTime to compute the note-on time
This commit is contained in:
parent
45124cce53
commit
6a66077911
4 changed files with 63 additions and 14 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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) );
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue