Skip to content

Commit

Permalink
Merge pull request #764 from paulfd/polyaft
Browse files Browse the repository at this point in the history
Support polyphonic aftertouch basics
  • Loading branch information
paulfd authored Apr 4, 2021
2 parents 9c3b310 + 9936202 commit d0cd21a
Show file tree
Hide file tree
Showing 31 changed files with 430 additions and 24 deletions.
2 changes: 1 addition & 1 deletion clients/jack_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ int process(jack_nframes_t numFrames, void* arg)
synth->noteOn(event.time, event.buffer[1], event.buffer[2]);
break;
case midi::polyphonicPressure:
// Not implemented
synth->polyAftertouch(event.time, event.buffer[1], event.buffer[2]);
break;
case midi::controlChange:
synth->cc(event.time, event.buffer[1], event.buffer[2]);
Expand Down
1 change: 1 addition & 0 deletions common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ SFIZZ_SOURCES = \
src/sfizz/modulations/ModMatrix.cpp \
src/sfizz/modulations/sources/ADSREnvelope.cpp \
src/sfizz/modulations/sources/ChannelAftertouch.cpp \
src/sfizz/modulations/sources/PolyAftertouch.cpp \
src/sfizz/modulations/sources/Controller.cpp \
src/sfizz/modulations/sources/FlexEnvelope.cpp \
src/sfizz/modulations/sources/LFO.cpp \
Expand Down
6 changes: 6 additions & 0 deletions plugins/lv2/sfizz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,12 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
(int)ev->time.frames,
msg[1]);
break;
case LV2_MIDI_MSG_NOTE_PRESSURE:
sfizz_send_poly_aftertouch(self->synth,
(int)ev->time.frames,
(int)msg[1],
msg[2]);
break;
case LV2_MIDI_MSG_BENDER:
sfizz_send_pitch_wheel(self->synth,
(int)ev->time.frames,
Expand Down
10 changes: 7 additions & 3 deletions plugins/vst/SfizzVstProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -465,9 +465,13 @@ void SfizzVstProcessor::processEvents(Vst::IEventList& events)
_noteEventsCurrentCycle[pitch] = 0.0f;
break;
}
// case Vst::Event::kPolyPressureEvent:
// synth.aftertouch(e.sampleOffset, convertVelocityFromFloat(e.polyPressure.pressure));
// break;
case Vst::Event::kPolyPressureEvent: {
int pitch = e.polyPressure.pitch;
if (pitch < 0 || pitch >= 128)
break;
synth.polyAftertouch(e.sampleOffset, pitch, convertVelocityFromFloat(e.polyPressure.pressure));
break;
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ set(SFIZZ_HEADERS
sfizz/modulations/ModGenerator.h
sfizz/modulations/sources/ADSREnvelope.h
sfizz/modulations/sources/ChannelAftertouch.h
sfizz/modulations/sources/PolyAftertouch.h
sfizz/modulations/sources/Controller.h
sfizz/modulations/sources/FlexEnvelope.h
sfizz/modulations/sources/LFO.h
Expand Down Expand Up @@ -166,6 +167,7 @@ set(SFIZZ_SOURCES
sfizz/modulations/ModMatrix.cpp
sfizz/modulations/sources/ADSREnvelope.cpp
sfizz/modulations/sources/ChannelAftertouch.cpp
sfizz/modulations/sources/PolyAftertouch.cpp
sfizz/modulations/sources/Controller.cpp
sfizz/modulations/sources/FlexEnvelope.cpp
sfizz/modulations/sources/LFO.cpp
Expand Down
20 changes: 18 additions & 2 deletions src/sfizz.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ SFIZZ_EXPORTED_API void sfizz_automate_hdcc(sfizz_synth_t* synth, int delay, int
SFIZZ_EXPORTED_API void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay, int pitch);

/**
* @brief Send an aftertouch event. (CURRENTLY UNIMPLEMENTED)
* @brief Send an aftertouch event.
* @since 0.2.0
*
* @param synth The synth.
Expand All @@ -443,6 +443,22 @@ SFIZZ_EXPORTED_API void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay,
*/
SFIZZ_EXPORTED_API void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, char aftertouch);

/**
* @brief Send a polyphonic aftertouch event. This feature is experimental and needs more testing
* in the internal engine.
* @since 0.6.0
*
* @param synth The synth.
* @param delay The delay at which the event occurs; this should be lower
* than the size of the block in the next call to renderBlock().
* @param note_number The note number.
* @param aftertouch The aftertouch value.
*
* @par Thread-safety constraints
* - @b RT: the function must be invoked from the Real-time thread
*/
SFIZZ_EXPORTED_API void sfizz_send_poly_aftertouch(sfizz_synth_t* synth, int delay, int note_number, char aftertouch);

/**
* @brief Send a tempo event.
* @since 0.2.0
Expand Down Expand Up @@ -823,7 +839,7 @@ SFIZZ_EXPORTED_API void sfizz_clear_external_definitions(sfizz_synth_t* synth);
* @brief Index out of bound error for the requested CC/key label.
* @since 0.4.0
*/
#define SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX -1
#define SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX (-1)

/**
* @brief Get the number of key labels registered in the current sfz file.
Expand Down
18 changes: 17 additions & 1 deletion src/sfizz.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ class SFIZZ_EXPORTED_API Sfizz
void pitchWheel(int delay, int pitch) noexcept;

/**
* @brief Send a aftertouch event to the synth. (CURRENTLY UNIMPLEMENTED)
* @brief Send an aftertouch event to the synth.
*
* @since 0.2.0
*
Expand All @@ -438,6 +438,22 @@ class SFIZZ_EXPORTED_API Sfizz
*/
void aftertouch(int delay, uint8_t aftertouch) noexcept;

/**
* @brief Send a polyphonic aftertouch event to the synth. This feature is
* experimental and needs more testing in the internal engine.
*
* @since 0.6.0
*
* @param delay the delay at which the event occurs; this should be lower
* than the size of the block in the next call to renderBlock().
* @param noteNumber the note number.
* @param aftertouch the aftertouch value.
*
* @par Thread-safety constraints
* - @b RT: the function must be invoked from the Real-time thread
*/
void polyAftertouch(int delay, int noteNumber, uint8_t aftertouch) noexcept;

/**
* @brief Send a tempo event to the synth.
*
Expand Down
2 changes: 2 additions & 0 deletions src/sfizz/Defaults.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ FloatSpec loVel { 0, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds };
FloatSpec hiVel { 127, {0.0f, 127.0f}, kNormalizeMidi|kPermissiveBounds };
FloatSpec loChannelAftertouch { 0, {0, 127}, kNormalizeMidi|kPermissiveBounds };
FloatSpec hiChannelAftertouch { 127, {0, 127}, kNormalizeMidi|kPermissiveBounds };
FloatSpec loPolyAftertouch { 0, {0, 127}, kNormalizeMidi|kPermissiveBounds };
FloatSpec hiPolyAftertouch { 127, {0, 127}, kNormalizeMidi|kPermissiveBounds };
FloatSpec loBend { -8191, {-8192.0f, 8191.0f}, kNormalizeBend|kPermissiveBounds };
FloatSpec hiBend { 8191, {-8192.0f, 8191.0f}, kNormalizeBend|kPermissiveBounds };
FloatSpec loNormalized { 0.0f, {0.0f, 1.0f}, kPermissiveBounds };
Expand Down
2 changes: 2 additions & 0 deletions src/sfizz/Defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ namespace Default
extern const OpcodeSpec<float> hiBipolar;
extern const OpcodeSpec<float> loChannelAftertouch;
extern const OpcodeSpec<float> hiChannelAftertouch;
extern const OpcodeSpec<float> loPolyAftertouch;
extern const OpcodeSpec<float> hiPolyAftertouch;
extern const OpcodeSpec<uint16_t> ccNumber;
extern const OpcodeSpec<uint8_t> curveCC;
extern const OpcodeSpec<uint16_t> smoothCC;
Expand Down
10 changes: 8 additions & 2 deletions src/sfizz/Layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ bool Layer::registerNoteOn(int noteNumber, float velocity, float randValue) noex
((sequenceCounter_++ % region.sequenceLength) == region.sequencePosition - 1);
}

if (!isSwitchedOn())
const bool polyAftertouchActive =
region.polyAftertouchRange.containsWithEnd(midiState_.getPolyAftertouch(noteNumber));

if (!isSwitchedOn() || !polyAftertouchActive)
return false;

if (!region.triggerOnNote)
Expand All @@ -83,7 +86,10 @@ bool Layer::registerNoteOff(int noteNumber, float velocity, float randValue) noe

const Region& region = region_;

if (!isSwitchedOn())
const bool polyAftertouchActive =
region.polyAftertouchRange.containsWithEnd(midiState_.getPolyAftertouch(noteNumber));

if (!isSwitchedOn() || !polyAftertouchActive)
return false;

if (!region.triggerOnNote)
Expand Down
53 changes: 44 additions & 9 deletions src/sfizz/MidiState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,11 @@ void sfz::MidiState::flushEvents() noexcept
events.resize(1);
};

for (auto& ccEvents : cc)
flushEventVector(ccEvents);
for (auto& events : ccEvents)
flushEventVector(events);

for (auto& events: polyAftertouchEvents)
flushEventVector(events);

flushEventVector(pitchEvents);
flushEventVector(channelAftertouchEvents);
Expand All @@ -88,8 +91,11 @@ void sfz::MidiState::setSamplesPerBlock(int samplesPerBlock) noexcept
events.reserve(samplesPerBlock);
};
this->samplesPerBlock = samplesPerBlock;
for (auto& ccEvents : cc)
updateEventBufferSize(ccEvents);
for (auto& events: ccEvents)
updateEventBufferSize(events);

for (auto& events: polyAftertouchEvents)
updateEventBufferSize(events);

updateEventBufferSize(pitchEvents);
updateEventBufferSize(channelAftertouchEvents);
Expand Down Expand Up @@ -147,21 +153,39 @@ void sfz::MidiState::channelAftertouchEvent(int delay, float aftertouch) noexcep
insertEventInVector(channelAftertouchEvents, delay, aftertouch);
}

void sfz::MidiState::polyAftertouchEvent(int delay, int noteNumber, float aftertouch) noexcept
{
ASSERT(aftertouch >= 0.0f && aftertouch <= 1.0f);
if (noteNumber < 0 || noteNumber >= static_cast<int>(polyAftertouchEvents.size()))
return;

insertEventInVector(polyAftertouchEvents[noteNumber], delay, aftertouch);
}

float sfz::MidiState::getChannelAftertouch() const noexcept
{
ASSERT(channelAftertouchEvents.size() > 0);
return channelAftertouchEvents.back().value;
}

float sfz::MidiState::getPolyAftertouch(int noteNumber) const noexcept
{
if (noteNumber < 0 || noteNumber > 127)
return 0.0f;

ASSERT(polyAftertouchEvents[noteNumber].size() > 0);
return polyAftertouchEvents[noteNumber].back().value;
}

void sfz::MidiState::ccEvent(int delay, int ccNumber, float ccValue) noexcept
{
insertEventInVector(cc[ccNumber], delay, ccValue);
insertEventInVector(ccEvents[ccNumber], delay, ccValue);
}

float sfz::MidiState::getCCValue(int ccNumber) const noexcept
{
ASSERT(ccNumber >= 0 && ccNumber < config::numCCs);
return cc[ccNumber].back().value;
return ccEvents[ccNumber].back().value;
}

void sfz::MidiState::reset() noexcept
Expand All @@ -174,8 +198,11 @@ void sfz::MidiState::reset() noexcept
events.push_back({ 0, 0.0f });
};

for (auto& ccEvents : cc)
clearEvents(ccEvents);
for (auto& events : ccEvents)
clearEvents(events);

for (auto& events : polyAftertouchEvents)
clearEvents(events);

clearEvents(pitchEvents);
clearEvents(channelAftertouchEvents);
Expand All @@ -201,7 +228,7 @@ const sfz::EventVector& sfz::MidiState::getCCEvents(int ccIdx) const noexcept
if (ccIdx < 0 || ccIdx >= config::numCCs)
return nullEvent;

return cc[ccIdx];
return ccEvents[ccIdx];
}

const sfz::EventVector& sfz::MidiState::getPitchEvents() const noexcept
Expand All @@ -213,3 +240,11 @@ const sfz::EventVector& sfz::MidiState::getChannelAftertouchEvents() const noexc
{
return channelAftertouchEvents;
}

const sfz::EventVector& sfz::MidiState::getPolyAftertouchEvents(int noteNumber) const noexcept
{
if (noteNumber < 0 || noteNumber > 127)
return nullEvent;

return polyAftertouchEvents[noteNumber];
}
22 changes: 21 additions & 1 deletion src/sfizz/MidiState.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,27 @@ class MidiState
*/
void channelAftertouchEvent(int delay, float aftertouch) noexcept;

/**
* @brief Register a channel aftertouch event
*
* @param aftertouch
*/
void polyAftertouchEvent(int delay, int noteNumber, float aftertouch) noexcept;

/**
* @brief Get the channel aftertouch status
* @return int
*/
float getChannelAftertouch() const noexcept;

/**
* @brief Get the polyphonic aftertouch status
* @return int
*/
float getPolyAftertouch(int noteNumber) const noexcept;

/**
* @brief Register a CC event
*
Expand Down Expand Up @@ -172,6 +186,7 @@ class MidiState
void resetAllControllers(int delay) noexcept;

const EventVector& getCCEvents(int ccIdx) const noexcept;
const EventVector& getPolyAftertouchEvents(int noteNumber) const noexcept;
const EventVector& getPitchEvents() const noexcept;
const EventVector& getChannelAftertouchEvents() const noexcept;

Expand Down Expand Up @@ -230,7 +245,7 @@ class MidiState
* @brief Current known values for the CCs.
*
*/
std::array<EventVector, config::numCCs> cc;
std::array<EventVector, config::numCCs> ccEvents;

/**
* @brief Null event
Expand All @@ -248,6 +263,11 @@ class MidiState
*/
EventVector channelAftertouchEvents;

/**
* @brief Polyphonic aftertouch status.
*/
std::array<EventVector, 128> polyAftertouchEvents;

float sampleRate { config::defaultSampleRate };
int samplesPerBlock { config::defaultSamplesPerBlock };
float alternate { 0.0f };
Expand Down
13 changes: 11 additions & 2 deletions src/sfizz/Region.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,12 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
case hash("hichanaft"):
aftertouchRange.setEnd(opcode.read(Default::hiChannelAftertouch));
break;
case hash("lopolyaft"):
polyAftertouchRange.setStart(opcode.read(Default::loPolyAftertouch));
break;
case hash("hipolyaft"):
polyAftertouchRange.setEnd(opcode.read(Default::hiPolyAftertouch));
break;
case hash("lobpm"):
bpmRange.setStart(opcode.read(Default::loBPM));
break;
Expand Down Expand Up @@ -926,7 +932,9 @@ bool sfz::Region::parseLFOOpcode(const Opcode& opcode, LFODescription& lfo)
= opcode.read(depthModSpec);
break;
case_any_lfo("depthpolyaft"): // NOLINT bugprone-branch-clone
// TODO(jpc) LFO v1
getOrCreateConnection(sourceKey, targetKey).sourceDepthMod = sourceDepthKey;
getOrCreateConnection(ModKey::createNXYZ(ModId::PolyAftertouch, id), sourceDepthKey).sourceDepth
= opcode.read(depthModSpec);
break;
case_any_lfo("fade"):
lfo.fade = opcode.read(Default::lfoFade);
Expand All @@ -942,7 +950,8 @@ bool sfz::Region::parseLFOOpcode(const Opcode& opcode, LFODescription& lfo)
= opcode.read(Default::lfoFreqMod);
break;
case_any_lfo("freqpolyaft"): // NOLINT bugprone-branch-clone
// TODO(jpc) LFO v1
getOrCreateConnection(ModKey::createNXYZ(ModId::PolyAftertouch, id), lfo.freqKey).sourceDepth
= opcode.read(Default::lfoFreqMod);
break;

// sfizz extension
Expand Down
1 change: 1 addition & 0 deletions src/sfizz/Region.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ struct Region {

// Region logic: internal conditions
UncheckedRange<float> aftertouchRange { Default::loChannelAftertouch, Default::hiChannelAftertouch }; // hichanaft and lochanaft
UncheckedRange<float> polyAftertouchRange { Default::loPolyAftertouch, Default::hiPolyAftertouch }; // hipolyaft and lopolyaft
UncheckedRange<float> bpmRange { Default::loBPM, Default::hiBPM }; // hibpm and lobpm
UncheckedRange<float> randRange { Default::loNormalized, Default::hiNormalized }; // hirand and lorand
uint8_t sequenceLength { Default::sequence }; // seq_length
Expand Down
Loading

0 comments on commit d0cd21a

Please sign in to comment.