Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended ccs #747

Merged
merged 1 commit into from
Mar 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/sfizz/MidiState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex
lastNotePlayed = noteNumber;
activeNotes++;
noteStates[noteNumber] = true;
alternate = alternate == 0.0f ? 1.0f : 0.0f;
}

}
Expand Down Expand Up @@ -154,7 +155,6 @@ float sfz::MidiState::getChannelAftertouch() const noexcept

void sfz::MidiState::ccEvent(int delay, int ccNumber, float ccValue) noexcept
{
ASSERT(ccValue >= 0.0 && ccValue <= 1.0);
insertEventInVector(cc[ccNumber], delay, ccValue);
}

Expand Down
8 changes: 8 additions & 0 deletions src/sfizz/MidiState.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ class MidiState
const EventVector& getPitchEvents() const noexcept;
const EventVector& getChannelAftertouchEvents() const noexcept;

/**
* @brief Get the alternate state value, for extended CC 137
*
* @return float
*/
float getAlternateState() const noexcept { return alternate; }

private:

/**
Expand Down Expand Up @@ -243,6 +250,7 @@ class MidiState

float sampleRate { config::defaultSampleRate };
int samplesPerBlock { config::defaultSamplesPerBlock };
float alternate { 0.0f };
unsigned internalClock { 0 };
};
}
8 changes: 7 additions & 1 deletion src/sfizz/ModifierHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& l
{
ASSERT(events.size() > 0);
ASSERT(events[0].delay == 0);

if (envelope.size() == 0)
return;

Expand Down Expand Up @@ -103,7 +104,11 @@ void linearEnvelope(const EventVector& events, absl::Span<float> envelope, F&& l
{
ASSERT(events.size() > 0);
ASSERT(events[0].delay == 0);
ASSERT(step != 0.0);

if (step == 0.0f) {
linearEnvelope(events, envelope, std::forward<F>(lambda));
return;
}

if (envelope.size() == 0)
return;
Expand Down Expand Up @@ -155,6 +160,7 @@ void multiplicativeEnvelope(const EventVector& events, absl::Span<float> envelop

if (envelope.size() == 0)
return;

const auto maxDelay = static_cast<int>(envelope.size() - 1);

auto lastValue = lambda(events[0].value);
Expand Down
16 changes: 15 additions & 1 deletion src/sfizz/Region.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1526,7 +1526,21 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec<float> spec,
assert(false);
break;
}
conn->source = ModKey(ModId::Controller, {}, p);

switch (p.cc) {
case ExtendedCCs::noteOnVelocity: // fallthrough
case ExtendedCCs::noteOffVelocity: // fallthrough
case ExtendedCCs::keyboardNoteNumber: // fallthrough
case ExtendedCCs::keyboardNoteGate: // fallthrough
case ExtendedCCs::unipolarRandom: // fallthrough
case ExtendedCCs::bipolarRandom: // fallthrough
case ExtendedCCs::alternate:
conn->source = ModKey(ModId::PerVoiceController, id, p);
break;
default:
conn->source = ModKey(ModId::Controller, {}, p);
break;
}
}

return true;
Expand Down
5 changes: 4 additions & 1 deletion src/sfizz/Synth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Synth::Impl::Impl()
resetVoices(config::numVoices);

// modulation sources
genController_.reset(new ControllerSource(resources_));
genController_.reset(new ControllerSource(resources_, voiceManager_));
genLFO_.reset(new LFOSource(voiceManager_));
genFlexEnvelope_.reset(new FlexEnvelopeSource(voiceManager_));
genADSREnvelope_.reset(new ADSREnvelopeSource(voiceManager_, resources_.midiState));
Expand Down Expand Up @@ -1314,6 +1314,8 @@ void Synth::pitchWheel(int delay, int pitch) noexcept
for (auto& voice : impl.voiceManager_) {
voice.registerPitchWheel(delay, normalizedPitch);
}

impl.performHdcc(delay, ExtendedCCs::pitchBend, normalizedPitch, false);
}

void Synth::aftertouch(int delay, uint8_t aftertouch) noexcept
Expand Down Expand Up @@ -1704,6 +1706,7 @@ void Synth::Impl::setupModMatrix()

switch (sourceKey.id()) {
case ModId::Controller:
case ModId::PerVoiceController:
gen = genController_.get();
break;
case ModId::AmpLFO:
Expand Down
26 changes: 26 additions & 0 deletions src/sfizz/Voice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ struct Voice::Impl
*/
void off(int delay, bool fast = false) noexcept;

/**
* @brief Setup the extended CC values for the voice
*
*/
void updateExtendedCCValues() noexcept;

const NumericId<Voice> id_;
StateListener* stateListener_ = nullptr;

Expand Down Expand Up @@ -288,6 +294,10 @@ struct Voice::Impl

bool followPower_ { false };
PowerFollower powerFollower_;

ExtendedCCValues extendedCCValues_;
fast_real_distribution<float> unipolarDist { 0.0f, 1.0f };
fast_real_distribution<float> bipolarDist { -1.0f, 1.0f };
};

Voice::Voice(int voiceNumber, Resources& resources)
Expand Down Expand Up @@ -363,6 +373,20 @@ Voice::Impl::Impl(int voiceNumber, Resources& resources)
getSCurve();
}

const ExtendedCCValues& Voice::getExtendedCCValues() const noexcept
{
Impl& impl = *impl_;
return impl.extendedCCValues_;
}

void Voice::Impl::updateExtendedCCValues() noexcept
{
extendedCCValues_.unipolar = unipolarDist(Random::randomGenerator);
extendedCCValues_.bipolar = bipolarDist(Random::randomGenerator);
extendedCCValues_.alternate = resources_.midiState.getAlternateState();
extendedCCValues_.noteGate = resources_.midiState.getActiveNotes() > 0 ? 1.0f : 0.0f;
}

bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept
{
Impl& impl = *impl_;
Expand All @@ -384,6 +408,8 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc

impl.switchState(State::playing);

impl.updateExtendedCCValues();

ASSERT(delay >= 0);
if (delay < 0)
delay = 0;
Expand Down
15 changes: 15 additions & 0 deletions src/sfizz/Voice.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ enum InterpolatorModel : int;
class LFO;
class FlexEnvelope;
struct Layer;

struct ExtendedCCValues {
float unipolar {};
float bipolar {};
float noteGate {};
float alternate {};
};

/**
* @brief The SFZ voice are the polyphony holders. They get activated by the synth
* and tasked to play a given region until the end, stopping on note-offs, off-groups
Expand Down Expand Up @@ -384,6 +392,13 @@ class Voice {
*/
const TriggerEvent& getTriggerEvent();

/**
* @brief Get the extended CC values
*
* @return const ExtendedCCValues&
*/
const ExtendedCCValues& getExtendedCCValues() const noexcept;

public:
/**
* @brief Check if the voice already belongs to a sister ring
Expand Down
2 changes: 2 additions & 0 deletions src/sfizz/modulations/ModId.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ int ModIds::flags(ModId id) noexcept
return kModIsPerVoice;
case ModId::ChannelAftertouch:
return kModIsPerCycle;
case ModId::PerVoiceController:
return kModIsPerVoice;

// targets
case ModId::MasterAmplitude:
Expand Down
2 changes: 1 addition & 1 deletion src/sfizz/modulations/ModId.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enum class ModId : int {
PitchEG,
FilEG,
ChannelAftertouch,

PerVoiceController,
_SourcesEnd,

//--------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions src/sfizz/modulations/ModKey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ std::string ModKey::toString() const
return absl::StrCat("FilterEG {", region_.number(), "}");
case ModId::ChannelAftertouch:
return absl::StrCat("ChannelAftertouch");
case ModId::PerVoiceController:
return absl::StrCat("PerVoiceController ", params_.cc,
" {curve=", params_.curve, ", smooth=", params_.smooth,
", step=", params_.step, ", region=", region_.number(), "}");

case ModId::MasterAmplitude:
return absl::StrCat("MasterAmplitude {", region_.number(), "}");
Expand Down
90 changes: 81 additions & 9 deletions src/sfizz/modulations/sources/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ struct ControllerSource::Impl {
float getLastTransformedValue(uint16_t cc, uint8_t curve) const noexcept;
double sampleRate_ = config::defaultSampleRate;
Resources* res_ = nullptr;
VoiceManager* voiceManager_ = nullptr;
absl::flat_hash_map<ModKey, Smoother> smoother_;
};

ControllerSource::ControllerSource(Resources& res)
ControllerSource::ControllerSource(Resources& res, VoiceManager& manager)
: impl_(new Impl)
{
impl_->res_ = &res;
impl_->voiceManager_ = &manager;
}

ControllerSource::~ControllerSource()
Expand Down Expand Up @@ -85,27 +87,97 @@ void ControllerSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, u

void ControllerSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer)
{
(void)voiceId;

const ModKey::Parameters p = sourceKey.parameters();
const Resources& res = *impl_->res_;
const Curve& curve = res.curves.getCurve(p.curve);
const MidiState& ms = res.midiState;
const EventVector& events = ms.getCCEvents(p.cc);
bool canShortcut = false;

auto transformValue = [p, &curve](float x) {
return curve.evalNormalized(x);
};

if (p.step > 0.0f)
linearEnvelope(events, buffer, transformValue, p.step);
else
linearEnvelope(events, buffer, transformValue);
// Ignore the eventual curve for the extended CCs
auto quantize = [p](float x) {
if (p.step > 0.0f)
return std::trunc(x / p.step) * p.step;

return x;
};

switch(p.cc) {
case ExtendedCCs::noteOnVelocity: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue =
voice && voice->getTriggerEvent().type == TriggerEventType::NoteOn ?
voice->getTriggerEvent().value : 0.0f;

sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::noteOffVelocity: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue =
voice && voice->getTriggerEvent().type == TriggerEventType::NoteOff ?
voice->getTriggerEvent().value : 0.0f;

sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::keyboardNoteNumber: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue = voice ? normalize7Bits(voice->getTriggerEvent().number) : 0.0f;
sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::keyboardNoteGate: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue = voice ? voice->getExtendedCCValues().noteGate : 0.0f;
sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::unipolarRandom: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue = voice ? voice->getExtendedCCValues().unipolar : 0.0f;
sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::bipolarRandom: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue = voice ? voice->getExtendedCCValues().bipolar : 0.0f;
sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::alternate: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue = voice ? voice->getExtendedCCValues().alternate : 0.0f;
sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::pitchBend: // fallthrough
case ExtendedCCs::channelAftertouch: {
const EventVector& events = ms.getCCEvents(p.cc);
linearEnvelope(events, buffer, [](float x) { return x; }, p.step);
canShortcut = events.size() == 1;
break;
}
default: {
const EventVector& events = ms.getCCEvents(p.cc);
linearEnvelope(events, buffer, transformValue, p.step);
canShortcut = events.size() == 1;
}
}

auto it = impl_->smoother_.find(sourceKey);
if (it != impl_->smoother_.end()) {
Smoother& s = it->second;
bool canShortcut = events.size() == 1;
s.process(buffer, buffer, canShortcut);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/sfizz/modulations/sources/Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#pragma once
#include "../ModGenerator.h"
#include "../../VoiceManager.h"
#include <memory>

namespace sfz {
Expand All @@ -14,7 +15,7 @@ struct Resources;

class ControllerSource : public ModGenerator {
public:
explicit ControllerSource(Resources& res);
explicit ControllerSource(Resources& res, VoiceManager& manager);
~ControllerSource();
void setSampleRate(double sampleRate) override;
void setSamplesPerBlock(unsigned count) override;
Expand Down
Loading