Remove the channels from the inner engine
This commit is contained in:
parent
24f52b255f
commit
8e39ffc18e
22 changed files with 417 additions and 626 deletions
|
|
@ -94,18 +94,18 @@ int process(jack_nframes_t numFrames, void* arg [[maybe_unused]])
|
|||
switch (midi::status(event.buffer[0])) {
|
||||
case midi::noteOff:
|
||||
// DBG("[MIDI] Note " << +event.buffer[1] << " OFF at time " << event.time);
|
||||
synth->noteOff(event.time, midi::channel(event.buffer[0]), event.buffer[1], event.buffer[2]);
|
||||
synth->noteOff(event.time, event.buffer[1], event.buffer[2]);
|
||||
break;
|
||||
case midi::noteOn:
|
||||
// DBG("[MIDI] Note " << +event.buffer[1] << " ON at time " << event.time);
|
||||
synth->noteOn(event.time, midi::channel(event.buffer[0]), event.buffer[1], event.buffer[2]);
|
||||
synth->noteOn(event.time, event.buffer[1], event.buffer[2]);
|
||||
break;
|
||||
case midi::polyphonicPressure:
|
||||
// DBG("[MIDI] Polyphonic pressure on at time " << event.time);
|
||||
break;
|
||||
case midi::controlChange:
|
||||
// DBG("[MIDI] CC " << +event.buffer[1] << " at time " << event.time);
|
||||
synth->cc(event.time, midi::channel(event.buffer[0]), event.buffer[1], event.buffer[2]);
|
||||
synth->cc(event.time, event.buffer[1], event.buffer[2]);
|
||||
break;
|
||||
case midi::programChange:
|
||||
// DBG("[MIDI] Program change at time " << event.time);
|
||||
|
|
@ -114,7 +114,7 @@ int process(jack_nframes_t numFrames, void* arg [[maybe_unused]])
|
|||
// DBG("[MIDI] Channel pressure at time " << event.time);
|
||||
break;
|
||||
case midi::pitchBend:
|
||||
synth->pitchWheel(event.time, midi::channel(event.buffer[0]), midi::buildAndCenterPitch(event.buffer[1], event.buffer[2]));
|
||||
synth->pitchWheel(event.time, midi::buildAndCenterPitch(event.buffer[1], event.buffer[2]));
|
||||
// DBG("[MIDI] Pitch bend at time " << event.time);
|
||||
break;
|
||||
case midi::systemMessage:
|
||||
|
|
@ -163,7 +163,7 @@ static void done(int sig [[maybe_unused]])
|
|||
// exit(0);
|
||||
}
|
||||
|
||||
ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are 1x, 2x, 4x, 8x)");
|
||||
ABSL_FLAG(std::string, oversampling, "1x", "Internal oversampling factor (value values are x1, x2, x4, x8)");
|
||||
ABSL_FLAG(uint32_t, preload_size, 8192, "Preloaded value");
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
|
|
|||
|
|
@ -495,7 +495,6 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
// LV2_DEBUG("[process_midi] Received note on %d/%d at time %ld\n", msg[0], msg[1], ev->time.frames);
|
||||
sfizz_send_note_on(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)MIDI_CHANNEL(msg[0]),
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
|
|
@ -503,7 +502,6 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
// LV2_DEBUG("[process_midi] Received note off %d/%d at time %ld\n", msg[0], msg[1], ev->time.frames);
|
||||
sfizz_send_note_off(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)MIDI_CHANNEL(msg[0]),
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
|
|
@ -511,7 +509,6 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
// LV2_DEBUG("[process_midi] Received CC %d/%d at time %ld\n", msg[0], msg[1], ev->time.frames);
|
||||
sfizz_send_cc(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)MIDI_CHANNEL(msg[0]),
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
|
|
@ -519,7 +516,6 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
// LV2_DEBUG("[process_midi] Received pitch bend %d on channel %d at time %ld\n", PITCH_BUILD_AND_CENTER(msg[1], msg[2]), MIDI_CHANNEL(msg[0]), ev->time.frames);
|
||||
sfizz_send_pitch_wheel(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)MIDI_CHANNEL(msg[0]),
|
||||
PITCH_BUILD_AND_CENTER(msg[1], msg[2]));
|
||||
break;
|
||||
default:
|
||||
|
|
@ -697,7 +693,7 @@ lv2_get_options(LV2_Handle instance, LV2_Options_Option *options)
|
|||
for (LV2_Options_Option *opt = options; opt->value; ++opt)
|
||||
{
|
||||
if (self->unmap) {
|
||||
LV2_DEBUG("[DEBUG] Called for an option with key (subject): %s (%s) \n",
|
||||
LV2_DEBUG("[DEBUG] Called for an option with key (subject): %s (%s) \n",
|
||||
self->unmap->unmap(self->unmap->handle, opt->key),
|
||||
self->unmap->unmap(self->unmap->handle, opt->subject));
|
||||
}
|
||||
|
|
@ -963,7 +959,7 @@ work_response(LV2_Handle instance,
|
|||
|
||||
const LV2_Atom *atom = (const LV2_Atom *)data;
|
||||
if (atom->type == self->sfizz_sfz_file_uri)
|
||||
{
|
||||
{
|
||||
self->changing_state = false;
|
||||
}
|
||||
else if (atom->type == self->sfizz_num_voices_uri)
|
||||
|
|
|
|||
29
src/sfizz.h
29
src/sfizz.h
|
|
@ -141,64 +141,59 @@ void sfizz_set_sample_rate(sfizz_synth_t* synth, float sample_rate);
|
|||
/**
|
||||
* @brief Send a note on event to the synth. As with all MIDI events, this
|
||||
* needs to happen before the call to sfizz_render_block in each
|
||||
* block and should appear in order of the delays, at least within a channel.
|
||||
* block and should appear in order of the delays.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel (starting at 0 for the first channel)
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
*/
|
||||
void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int channel, int note_number, char velocity);
|
||||
void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int note_number, char velocity);
|
||||
|
||||
/**
|
||||
* @brief Send a note off event to the synth. As with all MIDI events, this
|
||||
* needs to happen before the call to sfizz_render_block in each
|
||||
* block and should appear in order of the delays, at least within a channel.
|
||||
* As per the SFZ spec the velocity of note-off events is usually replaced by
|
||||
* block and should appear in order of the delays.
|
||||
* As per the SFZ spec the velocity of note-off events is usually replaced by
|
||||
* the note-on velocity.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel (starting at 0 for the first channel)
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
*/
|
||||
void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int channel, int note_number, char velocity);
|
||||
void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int note_number, char velocity);
|
||||
|
||||
/**
|
||||
* @brief Send a CC event to the synth. As with all MIDI events, this needs
|
||||
* to happen before the call to sfizz_render_block in each block and
|
||||
* should appear in order of the delays, at least within a channel.
|
||||
* to happen before the call to sfizz_render_block in each block and
|
||||
* should appear in order of the delays.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel (starting at 0 for the first channel)
|
||||
* @param cc_number the MIDI CC number
|
||||
* @param cc_value the MIDI CC value
|
||||
*/
|
||||
void sfizz_send_cc(sfizz_synth_t* synth, int delay, int channel, int cc_number, char cc_value);
|
||||
void sfizz_send_cc(sfizz_synth_t* synth, int delay, int cc_number, char cc_value);
|
||||
/**
|
||||
* @brief Send a pitch wheel event. As with all MIDI events, this needs
|
||||
* to happen before the call to sfizz_render_block in each block and
|
||||
* should appear in order of the delays, at least within a channel.
|
||||
* to happen before the call to sfizz_render_block in each block and
|
||||
* should appear in order of the delays.
|
||||
*
|
||||
* @param synth The synth
|
||||
* @param delay The delay
|
||||
* @param channel The channel
|
||||
* @param pitch The pitch
|
||||
*/
|
||||
void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay, int channel, int pitch);
|
||||
void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay, int pitch);
|
||||
|
||||
/**
|
||||
* @brief Send an aftertouch event. (CURRENTLY UNIMPLEMENTED)
|
||||
*
|
||||
* @param synth
|
||||
* @param delay
|
||||
* @param channel
|
||||
* @param aftertouch
|
||||
*/
|
||||
void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, int channel, char aftertouch);
|
||||
void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, char aftertouch);
|
||||
|
||||
/**
|
||||
* @brief Send a tempo event. (CURRENTLY UNIMPLEMENTED)
|
||||
|
|
|
|||
|
|
@ -6,99 +6,84 @@ sfz::MidiState::MidiState()
|
|||
reset();
|
||||
}
|
||||
|
||||
void sfz::MidiState::noteOnEvent(int channel, int noteNumber, uint8_t velocity) noexcept
|
||||
void sfz::MidiState::noteOnEvent(int noteNumber, uint8_t velocity) noexcept
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
ASSERT(noteNumber >= 0 && noteNumber <= 127);
|
||||
ASSERT(velocity >= 0 && velocity <= 127);
|
||||
|
||||
|
||||
if (noteNumber >= 0 && noteNumber < 128) {
|
||||
lastNoteVelocities[channel][noteNumber] = velocity;
|
||||
noteOnTimes[channel][noteNumber] = std::chrono::steady_clock::now();
|
||||
lastNoteVelocities[noteNumber] = velocity;
|
||||
noteOnTimes[noteNumber] = std::chrono::steady_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
float sfz::MidiState::getNoteDuration(int channel, int noteNumber) const
|
||||
float sfz::MidiState::getNoteDuration(int noteNumber) const
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
ASSERT(noteNumber >= 0 && noteNumber <= 127);
|
||||
|
||||
|
||||
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[channel][noteNumber]);
|
||||
const auto duration = std::chrono::duration_cast<std::chrono::duration<float>>(noteOffTime - noteOnTimes[noteNumber]);
|
||||
return duration.count();
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
uint8_t sfz::MidiState::getNoteVelocity(int channel, int noteNumber) const noexcept
|
||||
uint8_t sfz::MidiState::getNoteVelocity(int noteNumber) const noexcept
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
ASSERT(noteNumber >= 0 && noteNumber <= 127);
|
||||
|
||||
return lastNoteVelocities[channel][noteNumber];
|
||||
|
||||
return lastNoteVelocities[noteNumber];
|
||||
}
|
||||
|
||||
void sfz::MidiState::pitchBendEvent(int channel, int pitchBendValue) noexcept
|
||||
void sfz::MidiState::pitchBendEvent(int pitchBendValue) noexcept
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
ASSERT(pitchBendValue >= -8192 && pitchBendValue <= 8192);
|
||||
|
||||
pitchBends[channel] = pitchBendValue;
|
||||
|
||||
pitchBend = pitchBendValue;
|
||||
}
|
||||
|
||||
int sfz::MidiState::getPitchBend(int channel) const noexcept
|
||||
int sfz::MidiState::getPitchBend() const noexcept
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
|
||||
return pitchBends[channel];
|
||||
return pitchBend;
|
||||
}
|
||||
|
||||
void sfz::MidiState::ccEvent(int channel, int ccNumber, uint8_t ccValue) noexcept
|
||||
void sfz::MidiState::ccEvent(int ccNumber, uint8_t ccValue) noexcept
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
ASSERT(ccNumber >= 0 && ccNumber < config::numCCs);
|
||||
ASSERT(ccValue >= 0 && ccValue <= 127);
|
||||
|
||||
cc[channel][ccNumber] = ccValue;
|
||||
|
||||
cc[ccNumber] = ccValue;
|
||||
}
|
||||
|
||||
uint8_t sfz::MidiState::getCCValue(int channel, int ccNumber) const noexcept
|
||||
uint8_t sfz::MidiState::getCCValue(int ccNumber) const noexcept
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
ASSERT(ccNumber >= 0 && ccNumber < config::numCCs);
|
||||
|
||||
return cc[channel][ccNumber];
|
||||
|
||||
return cc[ccNumber];
|
||||
}
|
||||
|
||||
const sfz::SfzCCArray& sfz::MidiState::getCCArray(int channel) const noexcept
|
||||
const sfz::SfzCCArray& sfz::MidiState::getCCArray() const noexcept
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
|
||||
return cc[channel];
|
||||
return cc;
|
||||
}
|
||||
|
||||
void sfz::MidiState::reset() noexcept
|
||||
{
|
||||
for (auto& channelArray: lastNoteVelocities)
|
||||
for (auto& velocity: channelArray)
|
||||
velocity = 0;
|
||||
for (auto& velocity: lastNoteVelocities)
|
||||
velocity = 0;
|
||||
|
||||
for (auto& channelArray: cc)
|
||||
for (auto& ccValue: channelArray)
|
||||
ccValue = 0;
|
||||
for (auto& ccValue: cc)
|
||||
ccValue = 0;
|
||||
|
||||
for (auto& bendValue: pitchBends)
|
||||
bendValue = 0;
|
||||
pitchBend = 0;
|
||||
}
|
||||
|
||||
void sfz::MidiState::resetAllControllers(int channel) noexcept
|
||||
void sfz::MidiState::resetAllControllers() noexcept
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < 16);
|
||||
|
||||
for (int idx = 0; idx < config::numCCs; idx++)
|
||||
cc[channel][idx] = 0;
|
||||
cc[idx] = 0;
|
||||
|
||||
pitchBends[channel] = 0;
|
||||
}
|
||||
pitchBend = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,88 +18,76 @@ public:
|
|||
/**
|
||||
* @brief Update the state after a note on event
|
||||
*
|
||||
* @param channel (0-based)
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
*/
|
||||
void noteOnEvent(int channel, int noteNumber, uint8_t velocity) noexcept;
|
||||
void noteOnEvent(int noteNumber, uint8_t velocity) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Register a note off and get the note duration
|
||||
*
|
||||
* @param channel (0-based)
|
||||
* @param noteNumber
|
||||
* @return float
|
||||
*/
|
||||
float getNoteDuration(int channel, int noteNumber) const;
|
||||
float getNoteDuration(int noteNumber) const;
|
||||
|
||||
/**
|
||||
* @brief Get the note on velocity for a given note
|
||||
*
|
||||
* @param channel (0-based)
|
||||
* @param noteNumber
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t getNoteVelocity(int channel, int noteNumber) const noexcept;
|
||||
uint8_t getNoteVelocity(int noteNumber) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Register a pitch bend event
|
||||
*
|
||||
* @param channel
|
||||
* @param pitchBendValue
|
||||
*
|
||||
* @param pitchBendValue
|
||||
*/
|
||||
void pitchBendEvent(int channel, int pitchBendValue) noexcept;
|
||||
void pitchBendEvent(int pitchBendValue) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the pitch bend status on a channel
|
||||
*
|
||||
* @param channel
|
||||
* @return int
|
||||
* @brief Get the pitch bend status
|
||||
|
||||
* @return int
|
||||
*/
|
||||
int getPitchBend(int channel) const noexcept;
|
||||
int getPitchBend() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Register a CC event
|
||||
*
|
||||
* @param channel
|
||||
* @param ccNumber
|
||||
* @param ccValue
|
||||
*
|
||||
* @param ccNumber
|
||||
* @param ccValue
|
||||
*/
|
||||
void ccEvent(int channel, int ccNumber, uint8_t ccValue) noexcept;
|
||||
void ccEvent(int ccNumber, uint8_t ccValue) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the CC value for a specific channel and cc number
|
||||
*
|
||||
* @param channel
|
||||
* @param ccNumber
|
||||
* @return uint8_t
|
||||
* @brief Get the CC value for CC number
|
||||
*
|
||||
* @param ccNumber
|
||||
* @return uint8_t
|
||||
*/
|
||||
uint8_t getCCValue(int channel, int ccNumber) const noexcept;
|
||||
uint8_t getCCValue(int ccNumber) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the full CC status for a specific channel
|
||||
*
|
||||
* @param channel
|
||||
* @return const SfzCCArray&
|
||||
* @brief Get the full CC status
|
||||
*
|
||||
* @return const SfzCCArray&
|
||||
*/
|
||||
const SfzCCArray& getCCArray(int channel) const noexcept;
|
||||
const SfzCCArray& getCCArray() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Reset the midi state (does not impact the last note on time)
|
||||
*
|
||||
*
|
||||
*/
|
||||
void reset() noexcept;
|
||||
|
||||
/**
|
||||
* @brief Res
|
||||
*
|
||||
* @param channel
|
||||
* @brief Reset all the controllers
|
||||
*/
|
||||
void resetAllControllers(int channel) noexcept;
|
||||
void resetAllControllers() noexcept;
|
||||
|
||||
private:
|
||||
template<class T>
|
||||
using ChannelArray = std::array<T, 16>;
|
||||
template<class T>
|
||||
using MidiNoteArray = std::array<T, 128>;
|
||||
using NoteOnTime = std::chrono::steady_clock::time_point;
|
||||
|
|
@ -107,21 +95,21 @@ private:
|
|||
* @brief Stores the note on times.
|
||||
*
|
||||
*/
|
||||
ChannelArray<MidiNoteArray<NoteOnTime>> noteOnTimes { };
|
||||
MidiNoteArray<NoteOnTime> noteOnTimes { };
|
||||
/**
|
||||
* @brief Stores the velocity of the note ons for currently
|
||||
* depressed notes.
|
||||
*
|
||||
*/
|
||||
ChannelArray<MidiNoteArray<uint8_t>> lastNoteVelocities { };
|
||||
MidiNoteArray<uint8_t> lastNoteVelocities { };
|
||||
/**
|
||||
* @brief Current known values for the CCs.
|
||||
*
|
||||
*/
|
||||
ChannelArray<SfzCCArray> cc;
|
||||
SfzCCArray cc;
|
||||
/**
|
||||
* Pitch bend status
|
||||
*/
|
||||
ChannelArray<int> pitchBends { 0 };
|
||||
int pitchBend { 0 };
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,20 +144,6 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
break;
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
case hash("lochan"):
|
||||
{
|
||||
const auto value = readOpcode(opcode.value, Default::channelRange);
|
||||
if (value)
|
||||
channelRange.setStart(*value - 1);
|
||||
}
|
||||
break;
|
||||
case hash("hichan"):
|
||||
{
|
||||
const auto value = readOpcode(opcode.value, Default::channelRange);
|
||||
if (value)
|
||||
channelRange.setEnd(*value - 1);
|
||||
}
|
||||
break;
|
||||
case hash("lobend"):
|
||||
setRangeStartFromOpcode(opcode, bendRange, Default::bendRange);
|
||||
break;
|
||||
|
|
@ -500,6 +486,8 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
break;
|
||||
|
||||
// Ignored opcodes
|
||||
case hash("hichan"):
|
||||
case hash("lochan"):
|
||||
case hash("ampeg_depth"):
|
||||
case hash("ampeg_vel2depth"):
|
||||
break;
|
||||
|
|
@ -515,12 +503,8 @@ bool sfz::Region::isSwitchedOn() const noexcept
|
|||
return keySwitched && previousKeySwitched && sequenceSwitched && pitchSwitched && bpmSwitched && aftertouchSwitched && ccSwitched.all();
|
||||
}
|
||||
|
||||
bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity, float randValue) noexcept
|
||||
bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept
|
||||
{
|
||||
const bool chanOk = channelRange.containsWithEnd(channel);
|
||||
if (!chanOk)
|
||||
return false;
|
||||
|
||||
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
||||
if (keyswitch) {
|
||||
if (*keyswitch == noteNumber)
|
||||
|
|
@ -571,15 +555,11 @@ bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity,
|
|||
const bool attackTrigger = (trigger == SfzTrigger::attack);
|
||||
const bool notFirstLegatoNote = (trigger == SfzTrigger::legato && activeNotesInRange > 0);
|
||||
|
||||
return keyOk && velOk && chanOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote);
|
||||
return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote);
|
||||
}
|
||||
|
||||
bool sfz::Region::registerNoteOff(int channel, int noteNumber, uint8_t velocity [[maybe_unused]], float randValue) noexcept
|
||||
bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity [[maybe_unused]], float randValue) noexcept
|
||||
{
|
||||
const bool chanOk = channelRange.containsWithEnd(channel);
|
||||
if (!chanOk)
|
||||
return false;
|
||||
|
||||
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
||||
if (keyswitchDown && *keyswitchDown == noteNumber)
|
||||
keySwitched = false;
|
||||
|
|
@ -603,14 +583,11 @@ bool sfz::Region::registerNoteOff(int channel, int noteNumber, uint8_t velocity
|
|||
const bool velOk = velocityRange.containsWithEnd(velocity);
|
||||
const bool randOk = randRange.contains(randValue);
|
||||
const bool releaseTrigger = (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key);
|
||||
return keyOk && velOk && chanOk && randOk && releaseTrigger;
|
||||
return keyOk && velOk && randOk && releaseTrigger;
|
||||
}
|
||||
|
||||
bool sfz::Region::registerCC(int channel, int ccNumber, uint8_t ccValue) noexcept
|
||||
bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept
|
||||
{
|
||||
if (!channelRange.containsWithEnd(channel))
|
||||
return false;
|
||||
|
||||
if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue))
|
||||
ccSwitched.set(ccNumber, true);
|
||||
else
|
||||
|
|
@ -628,22 +605,16 @@ bool sfz::Region::registerCC(int channel, int ccNumber, uint8_t ccValue) noexcep
|
|||
return false;
|
||||
}
|
||||
|
||||
void sfz::Region::registerPitchWheel(int channel, int pitch) noexcept
|
||||
void sfz::Region::registerPitchWheel(int pitch) noexcept
|
||||
{
|
||||
if (!channelRange.containsWithEnd(channel))
|
||||
return;
|
||||
|
||||
if (bendRange.containsWithEnd(pitch))
|
||||
pitchSwitched = true;
|
||||
else
|
||||
pitchSwitched = false;
|
||||
}
|
||||
|
||||
void sfz::Region::registerAftertouch(int channel, uint8_t aftertouch) noexcept
|
||||
void sfz::Region::registerAftertouch(uint8_t aftertouch) noexcept
|
||||
{
|
||||
if (!channelRange.containsWithEnd(channel))
|
||||
return;
|
||||
|
||||
if (aftertouchRange.containsWithEnd(aftertouch))
|
||||
aftertouchSwitched = true;
|
||||
else
|
||||
|
|
@ -669,11 +640,11 @@ float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) noexc
|
|||
return centsFactor(pitchVariationInCents);
|
||||
}
|
||||
|
||||
float sfz::Region::getBaseVolumedB(int channel, int noteNumber) noexcept
|
||||
float sfz::Region::getBaseVolumedB(int noteNumber) noexcept
|
||||
{
|
||||
auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator);
|
||||
if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key)
|
||||
baseVolumedB -= rtDecay * midiState.getNoteDuration(channel, noteNumber);
|
||||
baseVolumedB -= rtDecay * midiState.getNoteDuration(noteNumber);
|
||||
return baseVolumedB;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,6 @@ struct Region {
|
|||
* @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 channel MIDI channel (0-based)
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
|
||||
|
|
@ -98,12 +97,11 @@ struct Region {
|
|||
* @return true if the region should trigger on this event.
|
||||
* @return false
|
||||
*/
|
||||
bool registerNoteOn(int channel, int noteNumber, uint8_t velocity, float randValue) noexcept;
|
||||
bool registerNoteOn(int noteNumber, uint8_t 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 channel MIDI channel (0-based)
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
|
||||
|
|
@ -111,32 +109,29 @@ struct Region {
|
|||
* @return true if the region should trigger on this event.
|
||||
* @return false
|
||||
*/
|
||||
bool registerNoteOff(int channel, int noteNumber, uint8_t velocity, float randValue) noexcept;
|
||||
bool registerNoteOff(int noteNumber, uint8_t 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.
|
||||
*
|
||||
* @param channel MIDI channel (0-based)
|
||||
* @param ccNumber
|
||||
* @param ccValue
|
||||
* @return true if the region should trigger on this event
|
||||
* @return false
|
||||
*/
|
||||
bool registerCC(int channel, int ccNumber, uint8_t ccValue) noexcept;
|
||||
bool registerCC(int ccNumber, uint8_t ccValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new pitch wheel event.
|
||||
*
|
||||
* @param channel MIDI channel (0-based)
|
||||
* @param pitch
|
||||
*/
|
||||
void registerPitchWheel(int channel, int pitch) noexcept;
|
||||
void registerPitchWheel(int pitch) noexcept;
|
||||
/**
|
||||
* @brief Register a new aftertouch event.
|
||||
*
|
||||
* @param channel MIDI channel (0-based)
|
||||
* @param aftertouch
|
||||
*/
|
||||
void registerAftertouch(int channel, uint8_t aftertouch) noexcept;
|
||||
void registerAftertouch(uint8_t aftertouch) noexcept;
|
||||
/**
|
||||
* @brief Register tempo
|
||||
*
|
||||
|
|
@ -174,11 +169,10 @@ struct Region {
|
|||
* @brief Get the base volume of the region depending on which note has been
|
||||
* pressed to trigger the region.
|
||||
*
|
||||
* @param channel (0-based)
|
||||
* @param noteNumber
|
||||
* @return float
|
||||
*/
|
||||
float getBaseVolumedB(int channel, int noteNumber) noexcept;
|
||||
float getBaseVolumedB(int noteNumber) noexcept;
|
||||
/**
|
||||
* @brief Get the base gain of the region.
|
||||
*
|
||||
|
|
@ -246,7 +240,6 @@ struct Region {
|
|||
Range<uint8_t> velocityRange { Default::velocityRange }; // hivel and lovel
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
Range<uint8_t> channelRange { Default::midiChannelRange }; //lochan and hichan
|
||||
Range<int> bendRange { Default::bendRange }; // hibend and lobend
|
||||
CCMap<Range<uint8_t>> ccConditions { Default::ccValueRange };
|
||||
Range<uint8_t> keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey
|
||||
|
|
|
|||
|
|
@ -173,8 +173,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
|||
case hash("set_cc"):
|
||||
if (member.parameter && Default::ccNumberRange.containsWithEnd(*member.parameter)) {
|
||||
const auto ccValue = readOpcode(member.value, Default::ccValueRange).value_or(0);
|
||||
for (int channel = 0; channel < 16; channel++)
|
||||
midiState.ccEvent(channel, *member.parameter, ccValue);
|
||||
midiState.ccEvent(*member.parameter, ccValue);
|
||||
}
|
||||
break;
|
||||
case hash("Label_cc"):
|
||||
|
|
@ -272,18 +271,17 @@ bool sfz::Synth::loadSfzFile(const fs::path& filename)
|
|||
|
||||
// Defaults
|
||||
for (int ccIndex = 0; ccIndex < config::numCCs; ccIndex++) {
|
||||
const auto channel = region->channelRange.getStart();
|
||||
region->registerCC(channel, ccIndex, midiState.getCCValue(channel, ccIndex));
|
||||
region->registerCC(ccIndex, midiState.getCCValue(ccIndex));
|
||||
}
|
||||
|
||||
if (defaultSwitch) {
|
||||
region->registerNoteOn(region->channelRange.getStart(), *defaultSwitch, 127, 1.0);
|
||||
region->registerNoteOff(region->channelRange.getStart(), *defaultSwitch, 0, 1.0);
|
||||
region->registerNoteOn(*defaultSwitch, 127, 1.0);
|
||||
region->registerNoteOff(*defaultSwitch, 0, 1.0);
|
||||
}
|
||||
|
||||
addEndpointsToVelocityCurve(*region);
|
||||
region->registerPitchWheel(region->channelRange.getStart(), 0);
|
||||
region->registerAftertouch(region->channelRange.getStart(), 0);
|
||||
region->registerPitchWheel(0);
|
||||
region->registerAftertouch(0);
|
||||
region->registerTempo(2.0f);
|
||||
|
||||
currentRegion++;
|
||||
|
|
@ -386,12 +384,12 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
buffer.applyGain(db2mag(volume));
|
||||
}
|
||||
|
||||
void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity) noexcept
|
||||
void sfz::Synth::noteOn(int delay, int noteNumber, uint8_t velocity) noexcept
|
||||
{
|
||||
ASSERT(noteNumber < 128);
|
||||
ASSERT(noteNumber >= 0);
|
||||
|
||||
midiState.noteOnEvent(channel, noteNumber, velocity);
|
||||
midiState.noteOnEvent(noteNumber, velocity);
|
||||
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
if (!canEnterCallback)
|
||||
|
|
@ -400,22 +398,22 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
|
|||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||
|
||||
for (auto& region : noteActivationLists[noteNumber]) {
|
||||
if (region->registerNoteOn(channel, noteNumber, velocity, randValue)) {
|
||||
if (region->registerNoteOn(noteNumber, velocity, randValue)) {
|
||||
for (auto& voice : voices) {
|
||||
if (voice->checkOffGroup(delay, region->group))
|
||||
noteOff(delay, voice->getTriggerChannel(), voice->getTriggerNumber(), 0);
|
||||
noteOff(delay, voice->getTriggerNumber(), 0);
|
||||
}
|
||||
|
||||
auto voice = findFreeVoice();
|
||||
if (voice == nullptr)
|
||||
continue;
|
||||
|
||||
voice->startVoice(region, delay, channel, noteNumber, velocity, Voice::TriggerType::NoteOn);
|
||||
voice->startVoice(region, delay, noteNumber, velocity, Voice::TriggerType::NoteOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocity [[maybe_unused]]) noexcept
|
||||
void sfz::Synth::noteOff(int delay, int noteNumber, uint8_t velocity [[maybe_unused]]) noexcept
|
||||
{
|
||||
ASSERT(noteNumber < 128);
|
||||
ASSERT(noteNumber >= 0);
|
||||
|
|
@ -427,24 +425,24 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
|
|||
// FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a
|
||||
// way in sfz to specify that a release trigger should NOT use the note-on velocity?
|
||||
// auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity);
|
||||
auto replacedVelocity = midiState.getNoteVelocity(channel, noteNumber);
|
||||
auto replacedVelocity = midiState.getNoteVelocity(noteNumber);
|
||||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||
|
||||
for (auto& voice : voices)
|
||||
voice->registerNoteOff(delay, channel, noteNumber, replacedVelocity);
|
||||
voice->registerNoteOff(delay, noteNumber, replacedVelocity);
|
||||
|
||||
for (auto& region : noteActivationLists[noteNumber]) {
|
||||
if (region->registerNoteOff(channel, noteNumber, replacedVelocity, randValue)) {
|
||||
if (region->registerNoteOff(noteNumber, replacedVelocity, randValue)) {
|
||||
auto voice = findFreeVoice();
|
||||
if (voice == nullptr)
|
||||
continue;
|
||||
|
||||
voice->startVoice(region, delay, channel, noteNumber, replacedVelocity, Voice::TriggerType::NoteOff);
|
||||
voice->startVoice(region, delay, noteNumber, replacedVelocity, Voice::TriggerType::NoteOff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept
|
||||
void sfz::Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept
|
||||
{
|
||||
ASSERT(ccNumber < config::numCCs);
|
||||
ASSERT(ccNumber >= 0);
|
||||
|
|
@ -454,36 +452,36 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc
|
|||
return;
|
||||
|
||||
if (ccNumber == config::resetCC) {
|
||||
resetAllControllers(delay, channel);
|
||||
resetAllControllers(delay);
|
||||
return;
|
||||
}
|
||||
|
||||
midiState.ccEvent(channel, ccNumber, ccValue);
|
||||
midiState.ccEvent(ccNumber, ccValue);
|
||||
for (auto& voice : voices)
|
||||
voice->registerCC(delay, channel, ccNumber, ccValue);
|
||||
voice->registerCC(delay, ccNumber, ccValue);
|
||||
|
||||
for (auto& region : ccActivationLists[ccNumber]) {
|
||||
if (region->registerCC(channel, ccNumber, ccValue)) {
|
||||
if (region->registerCC(ccNumber, ccValue)) {
|
||||
auto voice = findFreeVoice();
|
||||
if (voice == nullptr)
|
||||
continue;
|
||||
|
||||
voice->startVoice(region, delay, channel, ccNumber, ccValue, Voice::TriggerType::CC);
|
||||
voice->startVoice(region, delay, ccNumber, ccValue, Voice::TriggerType::CC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::Synth::pitchWheel(int delay, int channel, int pitch) noexcept
|
||||
void sfz::Synth::pitchWheel(int delay, int pitch) noexcept
|
||||
{
|
||||
ASSERT(pitch <= 8192);
|
||||
ASSERT(pitch >= -8192);
|
||||
|
||||
midiState.pitchBendEvent(channel, pitch);
|
||||
midiState.pitchBendEvent(pitch);
|
||||
for (auto& voice: voices) {
|
||||
voice->registerPitchWheel(delay, channel, pitch);
|
||||
voice->registerPitchWheel(delay, pitch);
|
||||
}
|
||||
}
|
||||
void sfz::Synth::aftertouch(int /* delay */, int /*channel*/, uint8_t /* aftertouch */) noexcept
|
||||
void sfz::Synth::aftertouch(int /* delay */, uint8_t /* aftertouch */) noexcept
|
||||
{
|
||||
}
|
||||
void sfz::Synth::tempo(int /* delay */, float /* secondsPerQuarter */) noexcept
|
||||
|
|
@ -617,21 +615,21 @@ void sfz::Synth::disableFreeWheeling() noexcept
|
|||
}
|
||||
}
|
||||
|
||||
void sfz::Synth::resetAllControllers(int delay, int channel) noexcept
|
||||
void sfz::Synth::resetAllControllers(int delay) noexcept
|
||||
{
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
if (!canEnterCallback)
|
||||
return;
|
||||
|
||||
midiState.resetAllControllers(channel);
|
||||
midiState.resetAllControllers();
|
||||
for (auto& voice: voices) {
|
||||
voice->registerPitchWheel(delay, channel, 0);
|
||||
voice->registerPitchWheel(delay, 0);
|
||||
for (int cc = 0; cc < config::numCCs; ++cc)
|
||||
voice->registerCC(delay, channel, cc, 0);
|
||||
voice->registerCC(delay, cc, 0);
|
||||
}
|
||||
|
||||
for (auto& region: regions) {
|
||||
for (int cc = 0; cc < config::numCCs; ++cc)
|
||||
region->registerCC(channel, cc, 0);
|
||||
region->registerCC(cc, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ namespace sfz {
|
|||
* synth should have a delay parameter strictly lower than 256. Events beyond 256
|
||||
* may be completely ignored by the synth as the incoming event buffer is cleared
|
||||
* during the renderBlock() call. You SHOULD also feed the midi events in the correct
|
||||
* order, at least within a channel.
|
||||
* order.
|
||||
*
|
||||
* The jack_client.cpp file contains examples of the most classical usage of the
|
||||
* synth and can be used as a reference.
|
||||
|
|
@ -196,50 +196,45 @@ public:
|
|||
*
|
||||
* @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 channel the midi channel for the event (0-based)
|
||||
* @param noteNumber the midi note number
|
||||
* @param velocity the midi note velocity
|
||||
*/
|
||||
void noteOn(int delay, int channel, int noteNumber, uint8_t velocity) noexcept;
|
||||
void noteOn(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
/**
|
||||
* @brief Send a note off event to 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 channel the midi channel for the event (0-based)
|
||||
* @param noteNumber the midi note number
|
||||
* @param velocity the midi note velocity
|
||||
*/
|
||||
void noteOff(int delay, int channel, int noteNumber, uint8_t velocity) noexcept;
|
||||
void noteOff(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
/**
|
||||
* @brief Send a CC event to 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 channel the midi channel for the event (0-based)
|
||||
* @param ccNumber the cc number
|
||||
* @param ccValue the cc value
|
||||
*/
|
||||
void cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept;
|
||||
void cc(int delay, int ccNumber, uint8_t ccValue) noexcept;
|
||||
/**
|
||||
* @brief Send a pitch bend event to 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 channel the midi channel for the event (0-based)
|
||||
* @param pitch the pitch value centered between -8192 and 8192
|
||||
*/
|
||||
void pitchWheel(int delay, int channel, int pitch) noexcept;
|
||||
void pitchWheel(int delay, int pitch) noexcept;
|
||||
/**
|
||||
* @brief Send a aftertouch event to 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 channel the midi channel for the event (0-based)
|
||||
* @param aftertouch the aftertouch value
|
||||
*/
|
||||
void aftertouch(int delay, int channel, uint8_t aftertouch) noexcept;
|
||||
void aftertouch(int delay, uint8_t aftertouch) noexcept;
|
||||
/**
|
||||
* @brief Send a tempo event to the synth
|
||||
*
|
||||
|
|
@ -363,10 +358,9 @@ private:
|
|||
* @brief Reset all CCs; to be used on CC 121
|
||||
*
|
||||
* @param delay the delay for the controller reset
|
||||
* @param channel the channel on which to reset the controller
|
||||
*
|
||||
*/
|
||||
void resetAllControllers(int delay, int channel) noexcept;
|
||||
void resetAllControllers(int delay) noexcept;
|
||||
|
||||
int numGroups { 0 };
|
||||
int numMasters { 0 };
|
||||
|
|
|
|||
|
|
@ -36,11 +36,10 @@ sfz::Voice::Voice(const sfz::MidiState& midiState, sfz::Resources& resources)
|
|||
{
|
||||
}
|
||||
|
||||
void sfz::Voice::startVoice(Region* region, int delay, int channel, int number, uint8_t value, sfz::Voice::TriggerType triggerType) noexcept
|
||||
void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value, sfz::Voice::TriggerType triggerType) noexcept
|
||||
{
|
||||
this->triggerType = triggerType;
|
||||
triggerNumber = number;
|
||||
triggerChannel = channel;
|
||||
triggerValue = value;
|
||||
|
||||
this->region = region;
|
||||
|
|
@ -60,39 +59,39 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number,
|
|||
}
|
||||
pitchRatio = region->getBasePitchVariation(number, value);
|
||||
|
||||
baseVolumedB = region->getBaseVolumedB(channel, number);
|
||||
baseVolumedB = region->getBaseVolumedB(number);
|
||||
|
||||
auto volumedB { baseVolumedB };
|
||||
if (region->volumeCC)
|
||||
volumedB += normalizeCC(midiState.getCCValue(channel, region->volumeCC->first)) * region->volumeCC->second;
|
||||
volumedB += normalizeCC(midiState.getCCValue(region->volumeCC->first)) * region->volumeCC->second;
|
||||
volumeEnvelope.reset(db2mag(volumedB));
|
||||
|
||||
baseGain = region->getBaseGain();
|
||||
baseGain *= region->getCrossfadeGain(midiState.getCCArray(channel));
|
||||
baseGain *= region->getCrossfadeGain(midiState.getCCArray());
|
||||
if (triggerType != TriggerType::CC)
|
||||
baseGain *= region->getNoteGain(number, value);
|
||||
|
||||
float gain { baseGain };
|
||||
if (region->amplitudeCC)
|
||||
gain *= normalizeCC(midiState.getCCValue(channel, region->amplitudeCC->first)) * normalizePercents(region->amplitudeCC->second);
|
||||
gain *= normalizeCC(midiState.getCCValue(region->amplitudeCC->first)) * normalizePercents(region->amplitudeCC->second);
|
||||
amplitudeEnvelope.reset(gain);
|
||||
|
||||
basePan = normalizeNegativePercents(region->pan);
|
||||
auto pan { basePan };
|
||||
if (region->panCC)
|
||||
pan += normalizeCC(midiState.getCCValue(channel, region->panCC->first)) * normalizeNegativePercents(region->panCC->second);
|
||||
pan += normalizeCC(midiState.getCCValue(region->panCC->first)) * normalizeNegativePercents(region->panCC->second);
|
||||
panEnvelope.reset(pan);
|
||||
|
||||
basePosition = normalizeNegativePercents(region->position);
|
||||
auto position { basePosition };
|
||||
if (region->positionCC)
|
||||
position += normalizeCC(midiState.getCCValue(channel, region->positionCC->first)) * normalizeNegativePercents(region->positionCC->second);
|
||||
position += normalizeCC(midiState.getCCValue(region->positionCC->first)) * normalizeNegativePercents(region->positionCC->second);
|
||||
positionEnvelope.reset(position);
|
||||
|
||||
baseWidth = normalizeNegativePercents(region->width);
|
||||
auto width { baseWidth };
|
||||
if (region->widthCC)
|
||||
width += normalizeCC(midiState.getCCValue(channel, region->widthCC->first)) * normalizeNegativePercents(region->widthCC->second);
|
||||
width += normalizeCC(midiState.getCCValue(region->widthCC->first)) * normalizeNegativePercents(region->widthCC->second);
|
||||
widthEnvelope.reset(width);
|
||||
|
||||
pitchBendEnvelope.setFunction([region](float pitchValue){
|
||||
|
|
@ -100,21 +99,21 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number,
|
|||
const auto bendInCents = normalizedBend > 0 ? normalizedBend * region->bendUp : -normalizedBend * region->bendDown;
|
||||
return centsFactor(bendInCents);
|
||||
});
|
||||
pitchBendEnvelope.reset(midiState.getPitchBend(channel));
|
||||
pitchBendEnvelope.reset(midiState.getPitchBend());
|
||||
|
||||
sourcePosition = region->getOffset();
|
||||
initialDelay = delay + static_cast<uint32_t>(region->getDelay() * sampleRate);
|
||||
baseFrequency = midiNoteFrequency(number);
|
||||
bendStepFactor = centsFactor(region->bendStep);
|
||||
prepareEGEnvelope(channel, initialDelay, value);
|
||||
prepareEGEnvelope(initialDelay, value);
|
||||
}
|
||||
|
||||
void sfz::Voice::prepareEGEnvelope(int channel, int delay, uint8_t velocity) noexcept
|
||||
void sfz::Voice::prepareEGEnvelope(int delay, uint8_t velocity) noexcept
|
||||
{
|
||||
auto secondsToSamples = [this](auto timeInSeconds) {
|
||||
return static_cast<int>(timeInSeconds * sampleRate);
|
||||
};
|
||||
const auto& ccArray = midiState.getCCArray(channel);
|
||||
const auto& ccArray = midiState.getCCArray();
|
||||
egEnvelope.reset(
|
||||
secondsToSamples(region->amplitudeEG.getAttack(ccArray, velocity)),
|
||||
secondsToSamples(region->amplitudeEG.getRelease(ccArray, velocity)),
|
||||
|
|
@ -143,7 +142,7 @@ void sfz::Voice::release(int delay, bool fastRelease) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
void sfz::Voice::registerNoteOff(int delay, int channel, int noteNumber, uint8_t velocity [[maybe_unused]]) noexcept
|
||||
void sfz::Voice::registerNoteOff(int delay, int noteNumber, uint8_t velocity [[maybe_unused]]) noexcept
|
||||
{
|
||||
if (region == nullptr)
|
||||
return;
|
||||
|
|
@ -151,18 +150,18 @@ void sfz::Voice::registerNoteOff(int delay, int channel, int noteNumber, uint8_t
|
|||
if (state != State::playing)
|
||||
return;
|
||||
|
||||
if (triggerChannel == channel && triggerNumber == noteNumber) {
|
||||
if (triggerNumber == noteNumber) {
|
||||
noteIsOff = true;
|
||||
|
||||
if (region->loopMode == SfzLoopMode::one_shot)
|
||||
return;
|
||||
|
||||
if (!region->checkSustain || midiState.getCCValue(channel, config::sustainCC) < config::halfCCThreshold)
|
||||
if (!region->checkSustain || midiState.getCCValue(config::sustainCC) < config::halfCCThreshold)
|
||||
release(delay);
|
||||
}
|
||||
}
|
||||
|
||||
void sfz::Voice::registerCC(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept
|
||||
void sfz::Voice::registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept
|
||||
{
|
||||
if (region == nullptr)
|
||||
return;
|
||||
|
|
@ -170,9 +169,6 @@ void sfz::Voice::registerCC(int delay, int channel, int ccNumber, uint8_t ccValu
|
|||
if (state == State::idle)
|
||||
return;
|
||||
|
||||
if (triggerChannel != channel)
|
||||
return;
|
||||
|
||||
if (ccNumber == config::allNotesOffCC || ccNumber == config::allSoundOffCC) {
|
||||
reset();
|
||||
return;
|
||||
|
|
@ -207,21 +203,15 @@ void sfz::Voice::registerCC(int delay, int channel, int ccNumber, uint8_t ccValu
|
|||
}
|
||||
}
|
||||
|
||||
void sfz::Voice::registerPitchWheel(int delay, int channel, int pitch) noexcept
|
||||
void sfz::Voice::registerPitchWheel(int delay, int pitch) noexcept
|
||||
{
|
||||
if (channel != triggerChannel)
|
||||
return;
|
||||
|
||||
if (state == State::idle)
|
||||
return;
|
||||
|
||||
if (triggerChannel != channel)
|
||||
return;
|
||||
|
||||
pitchBendEnvelope.registerEvent(delay, pitch);
|
||||
}
|
||||
|
||||
void sfz::Voice::registerAftertouch(int delay [[maybe_unused]], int channel [[maybe_unused]], uint8_t aftertouch [[maybe_unused]]) noexcept
|
||||
void sfz::Voice::registerAftertouch(int delay [[maybe_unused]], uint8_t aftertouch [[maybe_unused]]) noexcept
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
|
@ -511,11 +501,6 @@ int sfz::Voice::getTriggerNumber() const noexcept
|
|||
return triggerNumber;
|
||||
}
|
||||
|
||||
int sfz::Voice::getTriggerChannel() const noexcept
|
||||
{
|
||||
return triggerChannel;
|
||||
}
|
||||
|
||||
uint8_t sfz::Voice::getTriggerValue() const noexcept
|
||||
{
|
||||
return triggerValue;
|
||||
|
|
|
|||
|
|
@ -92,54 +92,48 @@ public:
|
|||
*
|
||||
* @param region
|
||||
* @param delay
|
||||
* @param channel (0-based)
|
||||
* @param number
|
||||
* @param value
|
||||
* @param triggerType
|
||||
*/
|
||||
void startVoice(Region* region, int delay, int channel, int number, uint8_t value, TriggerType triggerType) noexcept;
|
||||
void startVoice(Region* region, int delay, int number, uint8_t value, TriggerType triggerType) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Register a note-off event; this may trigger a release.
|
||||
*
|
||||
* @param delay
|
||||
* @param channel (0-based)
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
*/
|
||||
void registerNoteOff(int delay, int channel, int noteNumber, uint8_t velocity) noexcept;
|
||||
void registerNoteOff(int delay, int noteNumber, uint8_t velocity) noexcept;
|
||||
/**
|
||||
* @brief Register a CC event; this may trigger a release. If the voice is playing and its
|
||||
* region has CC modifiers, it will use this value to compute the CC envelope to apply to the
|
||||
* parameter.
|
||||
*
|
||||
* @param delay
|
||||
* @param channel (0-based)
|
||||
* @param ccNumber
|
||||
* @param ccValue
|
||||
*/
|
||||
void registerCC(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept;
|
||||
void registerCC(int delay, int ccNumber, uint8_t ccValue) noexcept;
|
||||
/**
|
||||
* @brief Register a pitch wheel event; for now this does nothing
|
||||
*
|
||||
* @param delay
|
||||
* @param channel (0-based)
|
||||
* @param pitch
|
||||
*/
|
||||
void registerPitchWheel(int delay, int channel, int pitch) noexcept;
|
||||
void registerPitchWheel(int delay, int pitch) noexcept;
|
||||
/**
|
||||
* @brief Register an aftertouch event; for now this does nothing
|
||||
*
|
||||
* @param delay
|
||||
* @param channel (0-based)
|
||||
* @param aftertouch
|
||||
*/
|
||||
void registerAftertouch(int delay, int channel, uint8_t aftertouch) noexcept;
|
||||
void registerAftertouch(int delay, uint8_t aftertouch) noexcept;
|
||||
/**
|
||||
* @brief Register a tempo event; for now this does nothing
|
||||
*
|
||||
* @param delay
|
||||
* @param channel (0-based)
|
||||
* @param pitch
|
||||
*/
|
||||
void registerTempo(int delay, float secondsPerQuarter) noexcept;
|
||||
|
|
@ -181,12 +175,6 @@ public:
|
|||
* @return int
|
||||
*/
|
||||
int getTriggerNumber() const noexcept;
|
||||
/**
|
||||
* @brief Get the channel that triggered the voice
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int getTriggerChannel() const noexcept;
|
||||
/**
|
||||
* @brief Get the value that triggered the voice (note velocity or cc value)
|
||||
*
|
||||
|
|
@ -243,11 +231,10 @@ private:
|
|||
/**
|
||||
* @brief Computes the values for the envelope depending on the note or CC number and the velocity/cc value
|
||||
*
|
||||
* @param channel (0-based)
|
||||
* @param delay
|
||||
* @param velocity
|
||||
*/
|
||||
void prepareEGEnvelope(int channel, int delay, uint8_t velocity) noexcept;
|
||||
void prepareEGEnvelope(int delay, uint8_t velocity) noexcept;
|
||||
/**
|
||||
* @brief The function processing a mono sample source
|
||||
*
|
||||
|
|
@ -279,7 +266,6 @@ private:
|
|||
|
||||
TriggerType triggerType;
|
||||
int triggerNumber;
|
||||
int triggerChannel;
|
||||
uint8_t triggerValue;
|
||||
|
||||
float speedRatio { 1.0 };
|
||||
|
|
|
|||
|
|
@ -88,30 +88,30 @@ void sfizz_set_sample_rate(sfizz_synth_t* synth, float sample_rate)
|
|||
self->setSampleRate(sample_rate);
|
||||
}
|
||||
|
||||
void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int channel, int note_number, char velocity)
|
||||
void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int note_number, char velocity)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
self->noteOn(delay, channel, note_number, velocity);
|
||||
self->noteOn(delay, note_number, velocity);
|
||||
}
|
||||
void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int channel, int note_number, char velocity)
|
||||
void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int note_number, char velocity)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
self->noteOff(delay, channel, note_number, velocity);
|
||||
self->noteOff(delay, note_number, velocity);
|
||||
}
|
||||
void sfizz_send_cc(sfizz_synth_t* synth, int delay, int channel, int cc_number, char cc_value)
|
||||
void sfizz_send_cc(sfizz_synth_t* synth, int delay, int cc_number, char cc_value)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
self->cc(delay, channel, cc_number, cc_value);
|
||||
self->cc(delay, cc_number, cc_value);
|
||||
}
|
||||
void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay, int channel, int pitch)
|
||||
void sfizz_send_pitch_wheel(sfizz_synth_t* synth, int delay, int pitch)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
self->pitchWheel(delay, channel, pitch);
|
||||
self->pitchWheel(delay, pitch);
|
||||
}
|
||||
void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, int channel, char aftertouch)
|
||||
void sfizz_send_aftertouch(sfizz_synth_t* synth, int delay, char aftertouch)
|
||||
{
|
||||
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||
self->aftertouch(delay, channel, aftertouch);
|
||||
self->aftertouch(delay, aftertouch);
|
||||
}
|
||||
void sfizz_send_tempo(sfizz_synth_t* synth, int delay, float seconds_per_quarter)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)")
|
|||
sfz::Synth synth;
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz");
|
||||
REQUIRE(synth.getNumRegions() == 1);
|
||||
REQUIRE(synth.getRegionView(0)->channelRange == sfz::Range<uint8_t>(1, 13));
|
||||
REQUIRE(synth.getRegionView(0)->keyRange == sfz::Range<uint8_t>(2, 14));
|
||||
}
|
||||
|
||||
TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
|
||||
|
|
@ -283,20 +283,20 @@ TEST_CASE("[Files] sw_default and playing with switches")
|
|||
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
||||
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
||||
REQUIRE( synth.getRegionView(3)->isSwitchedOn() );
|
||||
synth.noteOn(0, 1, 41, 64);
|
||||
synth.noteOff(0, 1, 41, 0);
|
||||
synth.noteOn(0, 41, 64);
|
||||
synth.noteOff(0, 41, 0);
|
||||
REQUIRE( synth.getRegionView(0)->isSwitchedOn() );
|
||||
REQUIRE( !synth.getRegionView(1)->isSwitchedOn() );
|
||||
REQUIRE( synth.getRegionView(2)->isSwitchedOn() );
|
||||
REQUIRE( !synth.getRegionView(3)->isSwitchedOn() );
|
||||
synth.noteOn(0, 1, 42, 64);
|
||||
synth.noteOff(0, 1, 42, 0);
|
||||
synth.noteOn(0, 42, 64);
|
||||
synth.noteOff(0, 42, 0);
|
||||
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
||||
REQUIRE( !synth.getRegionView(1)->isSwitchedOn() );
|
||||
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
||||
REQUIRE( !synth.getRegionView(3)->isSwitchedOn() );
|
||||
synth.noteOn(0, 1, 40, 64);
|
||||
synth.noteOff(0, 1, 40, 64);
|
||||
synth.noteOn(0, 40, 64);
|
||||
synth.noteOff(0, 40, 64);
|
||||
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
||||
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
||||
REQUIRE( !synth.getRegionView(2)->isSwitchedOn() );
|
||||
|
|
@ -355,14 +355,12 @@ TEST_CASE("[Files] Default path is ignored for generators")
|
|||
REQUIRE(synth.getRegionView(0)->sample == R"(*sine)");
|
||||
}
|
||||
|
||||
TEST_CASE("[Files] Set CC applies properly to all channels")
|
||||
TEST_CASE("[Files] Set CC applies properly")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/set_cc.sfz");
|
||||
for (int channel = 0; channel < 16; channel++) {
|
||||
REQUIRE(synth.getMidiState().getCCValue(channel, 142) == 63);
|
||||
REQUIRE(synth.getMidiState().getCCValue(channel, 61) == 122);
|
||||
}
|
||||
REQUIRE(synth.getMidiState().getCCValue(142) == 63);
|
||||
REQUIRE(synth.getMidiState().getCCValue(61) == 122);
|
||||
}
|
||||
|
||||
TEST_CASE("[Files] Note and octave offsets")
|
||||
|
|
@ -412,7 +410,7 @@ TEST_CASE("[Files] Off modes")
|
|||
synth.setSamplesPerBlock(256);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/off_mode.sfz");
|
||||
REQUIRE( synth.getNumRegions() == 3 );
|
||||
synth.noteOn(0, 0, 64, 63);
|
||||
synth.noteOn(0, 64, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||
const auto* fastVoice =
|
||||
synth.getVoiceView(0)->getRegion()->offMode == SfzOffMode::fast ?
|
||||
|
|
@ -422,7 +420,7 @@ TEST_CASE("[Files] Off modes")
|
|||
synth.getVoiceView(0)->getRegion()->offMode == SfzOffMode::fast ?
|
||||
synth.getVoiceView(1) :
|
||||
synth.getVoiceView(0) ;
|
||||
synth.noteOn(100, 0, 63, 63);
|
||||
synth.noteOn(100, 63, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 3 );
|
||||
sfz::AudioBuffer<float> buffer { 2, 256 };
|
||||
synth.renderBlock(buffer);
|
||||
|
|
|
|||
|
|
@ -35,69 +35,56 @@ using namespace Catch::literals;
|
|||
TEST_CASE("[MidiState] Initial values")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
for (auto& cc: state.getCCArray(1))
|
||||
for (auto& cc: state.getCCArray())
|
||||
REQUIRE( cc == 0 );
|
||||
for (auto& cc: state.getCCArray(6))
|
||||
REQUIRE( cc == 0 );
|
||||
for (int channel = 0; channel < 16; ++channel)
|
||||
REQUIRE( state.getPitchBend(channel) == 0 );
|
||||
REQUIRE( state.getPitchBend() == 0 );
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Set and get CCs")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
const auto& cc0 = state.getCCArray(0);
|
||||
const auto& cc6 = state.getCCArray(6);
|
||||
const auto& cc12 = state.getCCArray(12);
|
||||
state.ccEvent(0, 24, 23);
|
||||
state.ccEvent(6, 123, 124);
|
||||
REQUIRE(state.getCCValue(0, 24) == 23);
|
||||
REQUIRE(cc0[24] == 23);
|
||||
REQUIRE(state.getCCValue(6, 123) == 124);
|
||||
REQUIRE(cc6[123] == 124);
|
||||
REQUIRE(+state.getCCValue(12, 24) == 0);
|
||||
REQUIRE(+state.getCCValue(12, 123) == 0);
|
||||
REQUIRE(cc12[24] == 0);
|
||||
REQUIRE(cc12[123] == 0);
|
||||
const auto& cc = state.getCCArray();
|
||||
state.ccEvent(24, 23);
|
||||
state.ccEvent(123, 124);
|
||||
REQUIRE(state.getCCValue(24) == 23);
|
||||
REQUIRE(cc[24] == 23);
|
||||
REQUIRE(state.getCCValue(123) == 124);
|
||||
REQUIRE(cc[123] == 124);
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Set and get pitch bends")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
state.pitchBendEvent(0, 894);
|
||||
REQUIRE(state.getPitchBend(0) == 894);
|
||||
REQUIRE(state.getPitchBend(6) == 0);
|
||||
state.pitchBendEvent(0, 0);
|
||||
REQUIRE(state.getPitchBend(0) == 0);
|
||||
REQUIRE(state.getPitchBend(6) == 0);
|
||||
state.pitchBendEvent(894);
|
||||
REQUIRE(state.getPitchBend() == 894);
|
||||
state.pitchBendEvent(0);
|
||||
REQUIRE(state.getPitchBend() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Reset")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
state.pitchBendEvent(0, 894);
|
||||
state.noteOnEvent(6, 64, 24);
|
||||
state.ccEvent(15, 123, 124);
|
||||
state.pitchBendEvent(894);
|
||||
state.noteOnEvent(64, 24);
|
||||
state.ccEvent(123, 124);
|
||||
state.reset();
|
||||
REQUIRE(state.getPitchBend(0) == 0);
|
||||
REQUIRE(state.getNoteVelocity(6, 64) == 0);
|
||||
REQUIRE(state.getCCValue(15, 123) == 0);
|
||||
REQUIRE(state.getPitchBend() == 0);
|
||||
REQUIRE(state.getNoteVelocity(64) == 0);
|
||||
REQUIRE(state.getCCValue(123) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Set and get note velocities")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
state.noteOnEvent(0, 64, 24);
|
||||
REQUIRE(+state.getNoteVelocity(0, 64) == 24);
|
||||
REQUIRE(+state.getNoteVelocity(1, 64) == 0);
|
||||
state.noteOnEvent(0, 64, 123);
|
||||
REQUIRE(+state.getNoteVelocity(0, 64) == 123);
|
||||
REQUIRE(+state.getNoteVelocity(15, 64) == 0);
|
||||
state.noteOnEvent(64, 24);
|
||||
REQUIRE(+state.getNoteVelocity(64) == 24);
|
||||
state.noteOnEvent(64, 123);
|
||||
REQUIRE(+state.getNoteVelocity(64) == 123);
|
||||
}
|
||||
|
||||
TEST_CASE("[MidiState] Extended CCs")
|
||||
{
|
||||
sfz::MidiState state;
|
||||
REQUIRE(state.getCCArray(1).size() >= 142);
|
||||
state.ccEvent(6, 142, 64); // should not trap
|
||||
}
|
||||
REQUIRE(state.getCCArray().size() >= 142);
|
||||
state.ccEvent(142, 64); // should not trap
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,11 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
sfz::MidiState midiState;
|
||||
sfz::Region region { midiState };
|
||||
|
||||
|
||||
region.parseOpcode({ "sample", "*sine" });
|
||||
SECTION("Basic state")
|
||||
{
|
||||
region.registerCC(1, 4, 0);
|
||||
region.registerCC(4, 0);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -41,19 +41,19 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
region.parseOpcode({ "locc4", "56" });
|
||||
region.parseOpcode({ "hicc4", "59" });
|
||||
region.registerCC(1, 4, 0);
|
||||
region.registerCC(4, 0);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 57);
|
||||
region.registerCC(4, 57);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 56);
|
||||
region.registerCC(4, 56);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 59);
|
||||
region.registerCC(4, 59);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 43);
|
||||
region.registerCC(4, 43);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 65);
|
||||
region.registerCC(4, 65);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(1, 6, 57);
|
||||
region.registerCC(6, 57);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -63,26 +63,26 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "hicc4", "59" });
|
||||
region.parseOpcode({ "locc54", "18" });
|
||||
region.parseOpcode({ "hicc54", "27" });
|
||||
region.registerCC(1, 4, 0);
|
||||
region.registerCC(1, 54, 0);
|
||||
region.registerCC(4, 0);
|
||||
region.registerCC(54, 0);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 57);
|
||||
region.registerCC(4, 57);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(1, 54, 19);
|
||||
region.registerCC(54, 19);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 54, 18);
|
||||
region.registerCC(54, 18);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 54, 27);
|
||||
region.registerCC(54, 27);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 56);
|
||||
region.registerCC(4, 56);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 59);
|
||||
region.registerCC(4, 59);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 54, 2);
|
||||
region.registerCC(54, 2);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerCC(1, 54, 26);
|
||||
region.registerCC(54, 26);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerCC(1, 4, 65);
|
||||
region.registerCC(4, 65);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -90,13 +90,13 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
region.parseOpcode({ "lobend", "56" });
|
||||
region.parseOpcode({ "hibend", "243" });
|
||||
region.registerPitchWheel(1, 0);
|
||||
region.registerPitchWheel(0);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerPitchWheel(1, 56);
|
||||
region.registerPitchWheel(56);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerPitchWheel(1, 243);
|
||||
region.registerPitchWheel(243);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerPitchWheel(1, 245);
|
||||
region.registerPitchWheel(245);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -104,13 +104,13 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
region.parseOpcode({ "lochanaft", "56" });
|
||||
region.parseOpcode({ "hichanaft", "68" });
|
||||
region.registerAftertouch(1, 0);
|
||||
region.registerAftertouch(0);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerAftertouch(1, 56);
|
||||
region.registerAftertouch(56);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerAftertouch(1, 68);
|
||||
region.registerAftertouch(68);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerAftertouch(1, 98);
|
||||
region.registerAftertouch(98);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -133,13 +133,13 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
region.parseOpcode({ "sw_last", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 64, 0.5f);
|
||||
region.registerNoteOff(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 41, 64, 0.5f);
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 41, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
}
|
||||
|
||||
SECTION("Keyswitches: sw_last with non-default keyswitch range")
|
||||
|
|
@ -148,20 +148,20 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "sw_hikey", "50" });
|
||||
region.parseOpcode({ "sw_last", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 60, 64, 0.5f);
|
||||
region.registerNoteOn(60, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 60, 0, 0.5f);
|
||||
region.registerNoteOff(60, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 60, 64, 0.5f);
|
||||
region.registerNoteOn(60, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 60, 0, 0.5f);
|
||||
region.registerNoteOn(1, 41, 64, 0.5f);
|
||||
region.registerNoteOff(60, 0, 0.5f);
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 41, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
}
|
||||
|
||||
SECTION("Keyswitches: sw_down with non-default keyswitch range")
|
||||
|
|
@ -170,20 +170,20 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "sw_hikey", "50" });
|
||||
region.parseOpcode({ "sw_down", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 60, 64, 0.5f);
|
||||
region.registerNoteOn(60, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 60, 0, 0.5f);
|
||||
region.registerNoteOff(60, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 60, 64, 0.5f);
|
||||
region.registerNoteOn(60, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 60, 0, 0.5f);
|
||||
region.registerNoteOn(1, 41, 64, 0.5f);
|
||||
region.registerNoteOff(60, 0, 0.5f);
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 41, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
}
|
||||
|
||||
SECTION("Keyswitches: sw_up with non-default keyswitch range")
|
||||
|
|
@ -192,16 +192,16 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "sw_hikey", "50" });
|
||||
region.parseOpcode({ "sw_up", "40" });
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 41, 64, 0.5f);
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(1, 41, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -209,20 +209,20 @@ TEST_CASE("Region activation", "Region tests")
|
|||
{
|
||||
region.parseOpcode({ "sw_previous", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 41, 64, 0.5f);
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(1, 41, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 41, 64, 0.5f);
|
||||
region.registerNoteOn(41, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 41, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -232,17 +232,17 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "seq_position", "1" });
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
}
|
||||
SECTION("Sequences: length 2, position 2")
|
||||
|
|
@ -251,17 +251,17 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "seq_position", "2" });
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
}
|
||||
SECTION("Sequences: length 3, position 2")
|
||||
|
|
@ -270,21 +270,21 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "seq_position", "2" });
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(!region.isSwitchedOn());
|
||||
region.registerNoteOn(1, 40, 64, 0.5f);
|
||||
region.registerNoteOn(40, 64, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
region.registerNoteOff(1, 40, 0, 0.5f);
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
REQUIRE(region.isSwitchedOn());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,23 +236,6 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
|
||||
}
|
||||
|
||||
SECTION("lochan, hichan")
|
||||
{
|
||||
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 15));
|
||||
region.parseOpcode({ "lochan", "4" });
|
||||
REQUIRE(region.channelRange == sfz::Range<uint8_t>(3, 15));
|
||||
region.parseOpcode({ "lochan", "128" });
|
||||
REQUIRE(region.channelRange == sfz::Range<uint8_t>(15, 15));
|
||||
region.parseOpcode({ "lochan", "-3" });
|
||||
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 15));
|
||||
region.parseOpcode({ "hichan", "13" });
|
||||
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 12));
|
||||
region.parseOpcode({ "hichan", "-1" });
|
||||
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 0));
|
||||
region.parseOpcode({ "hichan", "128" });
|
||||
REQUIRE(region.channelRange == sfz::Range<uint8_t>(0, 15));
|
||||
}
|
||||
|
||||
SECTION("lobend, hibend")
|
||||
{
|
||||
REQUIRE(region.bendRange == sfz::Range<int>(-8192, 8192));
|
||||
|
|
|
|||
|
|
@ -34,44 +34,44 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
SECTION("key")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(0, 40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(0, 41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(0, 63, 64));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64));
|
||||
}
|
||||
SECTION("lokey and hikey")
|
||||
{
|
||||
region.parseOpcode({ "lokey", "40" });
|
||||
region.parseOpcode({ "hikey", "42" });
|
||||
REQUIRE(!region.registerNoteOn(0, 39, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(0, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(0, 41, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(0, 42, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(0, 43, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(0, 42, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(0, 42, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(0, 63, 64));
|
||||
REQUIRE(!region.registerNoteOn(39, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(41, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(42, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(43, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(42, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(42, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64));
|
||||
}
|
||||
SECTION("key and release trigger")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "trigger", "release" });
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOff(0, 40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(0, 41, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(0, 41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(0, 63, 64));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOff(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64));
|
||||
}
|
||||
SECTION("key and release_key trigger")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "trigger", "release_key" });
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOff(0, 40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(0, 41, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(0, 41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(0, 63, 64));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOff(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOff(41, 64, 0.5f));
|
||||
REQUIRE(!region.registerCC(63, 64));
|
||||
}
|
||||
// TODO: first and legato triggers
|
||||
SECTION("lovel and hivel")
|
||||
|
|
@ -79,22 +79,11 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "lovel", "60" });
|
||||
region.parseOpcode({ "hivel", "70" });
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 60, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 70, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(0, 41, 71, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(0, 41, 59, 0.5f));
|
||||
}
|
||||
SECTION("lochan and hichan")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "lochan", "2" });
|
||||
region.parseOpcode({ "hichan", "4" });
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(1, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(2, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(3, 40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(4, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 60, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 70, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 71, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 59, 0.5f));
|
||||
}
|
||||
|
||||
SECTION("lorand and hirand")
|
||||
|
|
@ -102,54 +91,54 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "lorand", "0.35" });
|
||||
region.parseOpcode({ "hirand", "0.40" });
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.34f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.35f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.36f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.37f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.38f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.39f));
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.40f));
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.41f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.34f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.35f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.36f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.37f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.38f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.39f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.40f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.41f));
|
||||
}
|
||||
|
||||
SECTION("lorand and hirand on 1.0f")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
region.parseOpcode({ "lorand", "0.35" });
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.34f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.35f));
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 1.0f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.34f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.35f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 1.0f));
|
||||
}
|
||||
|
||||
SECTION("Disable key trigger")
|
||||
{
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 1.0f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 1.0f));
|
||||
region.parseOpcode({ "hikey", "-1" });
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 1.0f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 1.0f));
|
||||
region.parseOpcode({ "hikey", "40" });
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 1.0f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 1.0f));
|
||||
region.parseOpcode({ "key", "-1" });
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 1.0f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 1.0f));
|
||||
region.parseOpcode({ "key", "40" });
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 1.0f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 1.0f));
|
||||
}
|
||||
|
||||
SECTION("on_loccN, on_hiccN")
|
||||
{
|
||||
region.parseOpcode({ "on_locc47", "64" });
|
||||
region.parseOpcode({ "on_hicc47", "68" });
|
||||
REQUIRE(!region.registerCC(0, 47, 63));
|
||||
REQUIRE(!region.registerCC(0, 47, 64));
|
||||
REQUIRE(!region.registerCC(0, 47, 65));
|
||||
REQUIRE(!region.registerCC(47, 63));
|
||||
REQUIRE(!region.registerCC(47, 64));
|
||||
REQUIRE(!region.registerCC(47, 65));
|
||||
region.parseOpcode({ "hikey", "-1" });
|
||||
REQUIRE(region.registerCC(0, 47, 64));
|
||||
REQUIRE(region.registerCC(0, 47, 65));
|
||||
REQUIRE(region.registerCC(0, 47, 66));
|
||||
REQUIRE(region.registerCC(0, 47, 67));
|
||||
REQUIRE(region.registerCC(0, 47, 68));
|
||||
REQUIRE(!region.registerCC(0, 47, 69));
|
||||
REQUIRE(!region.registerCC(0, 40, 64));
|
||||
REQUIRE(region.registerCC(47, 64));
|
||||
REQUIRE(region.registerCC(47, 65));
|
||||
REQUIRE(region.registerCC(47, 66));
|
||||
REQUIRE(region.registerCC(47, 67));
|
||||
REQUIRE(region.registerCC(47, 68));
|
||||
REQUIRE(!region.registerCC(47, 69));
|
||||
REQUIRE(!region.registerCC(40, 64));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -163,11 +152,11 @@ TEST_CASE("Legato triggers", "Region triggers")
|
|||
region.parseOpcode({ "lokey", "40" });
|
||||
region.parseOpcode({ "hikey", "50" });
|
||||
region.parseOpcode({ "trigger", "first" });
|
||||
REQUIRE(region.registerNoteOn(0, 40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(0, 41, 64, 0.5f));
|
||||
region.registerNoteOff(0, 40, 0, 0.5f);
|
||||
region.registerNoteOff(0, 41, 0, 0.5f);
|
||||
REQUIRE(region.registerNoteOn(0, 42, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(41, 64, 0.5f));
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
REQUIRE(region.registerNoteOn(42, 64, 0.5f));
|
||||
}
|
||||
|
||||
SECTION("Second note playing")
|
||||
|
|
@ -175,10 +164,10 @@ TEST_CASE("Legato triggers", "Region triggers")
|
|||
region.parseOpcode({ "lokey", "40" });
|
||||
region.parseOpcode({ "hikey", "50" });
|
||||
region.parseOpcode({ "trigger", "legato" });
|
||||
REQUIRE(!region.registerNoteOn(0, 40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(0, 41, 64, 0.5f));
|
||||
region.registerNoteOff(0, 40, 0, 0.5f);
|
||||
region.registerNoteOff(0, 41, 0, 0.5f);
|
||||
REQUIRE(!region.registerNoteOn(0, 42, 64, 0.5f));
|
||||
REQUIRE(!region.registerNoteOn(40, 64, 0.5f));
|
||||
REQUIRE(region.registerNoteOn(41, 64, 0.5f));
|
||||
region.registerNoteOff(40, 0, 0.5f);
|
||||
region.registerNoteOff(41, 0, 0.5f);
|
||||
REQUIRE(!region.registerNoteOn(42, 64, 0.5f));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,13 +183,13 @@ TEST_CASE("[Region] Crossfade in on CC")
|
|||
region.parseOpcode({ "xfin_locc24", "20" });
|
||||
region.parseOpcode({ "xfin_hicc24", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
midiState.ccEvent(1, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a );
|
||||
midiState.ccEvent(1, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a );
|
||||
midiState.ccEvent(1, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.5_a );
|
||||
midiState.ccEvent(1, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.70711_a );
|
||||
midiState.ccEvent(1, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.86603_a );
|
||||
midiState.ccEvent(1, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a );
|
||||
midiState.ccEvent(1, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a );
|
||||
midiState.ccEvent(24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a );
|
||||
midiState.ccEvent(24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.70711_a );
|
||||
midiState.ccEvent(24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.86603_a );
|
||||
midiState.ccEvent(24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade in on CC - gain")
|
||||
|
|
@ -201,13 +201,13 @@ TEST_CASE("[Region] Crossfade in on CC - gain")
|
|||
region.parseOpcode({ "xfin_hicc24", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
region.parseOpcode({ "xf_cccurve", "gain" });
|
||||
midiState.ccEvent(1, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a );
|
||||
midiState.ccEvent(1, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a );
|
||||
midiState.ccEvent(1, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.25_a );
|
||||
midiState.ccEvent(1, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.5_a );
|
||||
midiState.ccEvent(1, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.75_a );
|
||||
midiState.ccEvent(1, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a );
|
||||
midiState.ccEvent(1, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a );
|
||||
midiState.ccEvent(24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.25_a );
|
||||
midiState.ccEvent(24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a );
|
||||
midiState.ccEvent(24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.75_a );
|
||||
midiState.ccEvent(24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
}
|
||||
TEST_CASE("[Region] Crossfade out on CC")
|
||||
{
|
||||
|
|
@ -217,13 +217,13 @@ TEST_CASE("[Region] Crossfade out on CC")
|
|||
region.parseOpcode({ "xfout_locc24", "20" });
|
||||
region.parseOpcode({ "xfout_hicc24", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
midiState.ccEvent(1, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a );
|
||||
midiState.ccEvent(1, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a );
|
||||
midiState.ccEvent(1, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.86603_a );
|
||||
midiState.ccEvent(1, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.70711_a );
|
||||
midiState.ccEvent(1, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.5_a );
|
||||
midiState.ccEvent(1, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a );
|
||||
midiState.ccEvent(1, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a );
|
||||
midiState.ccEvent(24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.86603_a );
|
||||
midiState.ccEvent(24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.70711_a );
|
||||
midiState.ccEvent(24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a );
|
||||
midiState.ccEvent(24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Crossfade out on CC - gain")
|
||||
|
|
@ -235,13 +235,13 @@ TEST_CASE("[Region] Crossfade out on CC - gain")
|
|||
region.parseOpcode({ "xfout_hicc24", "24" });
|
||||
region.parseOpcode({ "amp_veltrack", "0" });
|
||||
region.parseOpcode({ "xf_cccurve", "gain" });
|
||||
midiState.ccEvent(1, 24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a );
|
||||
midiState.ccEvent(1, 24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 1.0_a );
|
||||
midiState.ccEvent(1, 24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.75_a );
|
||||
midiState.ccEvent(1, 24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.5_a );
|
||||
midiState.ccEvent(1, 24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.25_a );
|
||||
midiState.ccEvent(1, 24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a );
|
||||
midiState.ccEvent(1, 24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray(1)) == 0.0_a );
|
||||
midiState.ccEvent(24, 19); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(24, 20); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 1.0_a );
|
||||
midiState.ccEvent(24, 21); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.75_a );
|
||||
midiState.ccEvent(24, 22); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.5_a );
|
||||
midiState.ccEvent(24, 23); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.25_a );
|
||||
midiState.ccEvent(24, 24); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
midiState.ccEvent(24, 25); REQUIRE( region.getCrossfadeGain(midiState.getCCArray()) == 0.0_a );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
|
||||
|
|
@ -282,17 +282,17 @@ TEST_CASE("[Region] rt_decay")
|
|||
region.parseOpcode({ "sample", "*sine" });
|
||||
region.parseOpcode({ "trigger", "release" });
|
||||
region.parseOpcode({ "rt_decay", "10" });
|
||||
midiState.noteOnEvent(1, 64, 64);
|
||||
midiState.noteOnEvent(64, 64);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(1, 64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
|
||||
region.parseOpcode({ "rt_decay", "20" });
|
||||
midiState.noteOnEvent(1, 64, 64);
|
||||
midiState.noteOnEvent(64, 64);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(1, 64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
|
||||
region.parseOpcode({ "trigger", "attack" });
|
||||
midiState.noteOnEvent(1, 64, 64);
|
||||
midiState.noteOnEvent(64, 64);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
REQUIRE( region.getBaseVolumedB(1, 64) == Approx(sfz::Default::volume).margin(0.1) );
|
||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
|
||||
}
|
||||
|
||||
TEST_CASE("[Region] Base delay")
|
||||
|
|
|
|||
108
tests/SynthT.cpp
108
tests/SynthT.cpp
|
|
@ -35,8 +35,8 @@ TEST_CASE("[Synth] Play and check active voices")
|
|||
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
|
||||
|
||||
synth.noteOn(0, 1, 36, 24);
|
||||
synth.noteOn(0, 1, 36, 89);
|
||||
synth.noteOn(0, 36, 24);
|
||||
synth.noteOn(0, 36, 89);
|
||||
REQUIRE(synth.getNumActiveVoices() == 2);
|
||||
// Render for a while
|
||||
for (int i = 0; i < 200; ++i)
|
||||
|
|
@ -51,8 +51,8 @@ TEST_CASE("[Synth] Change the number of voice while playing")
|
|||
sfz::AudioBuffer<float> buffer { 2, blockSize };
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
|
||||
|
||||
synth.noteOn(0, 1, 36, 24);
|
||||
synth.noteOn(0, 1, 36, 89);
|
||||
synth.noteOn(0, 36, 24);
|
||||
synth.noteOn(0, 36, 89);
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE(synth.getNumActiveVoices() == 2);
|
||||
synth.setNumVoices(8);
|
||||
|
|
@ -99,8 +99,8 @@ TEST_CASE("[Synth] Check that we can change the size of the preload before and a
|
|||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
|
||||
synth.setPreloadSize(1024);
|
||||
|
||||
synth.noteOn(0, 1, 36, 24);
|
||||
synth.noteOn(0, 1, 36, 89);
|
||||
synth.noteOn(0, 36, 24);
|
||||
synth.noteOn(0, 36, 89);
|
||||
synth.renderBlock(buffer);
|
||||
synth.setPreloadSize(2048);
|
||||
synth.renderBlock(buffer);
|
||||
|
|
@ -114,95 +114,39 @@ TEST_CASE("[Synth] Check that we can change the oversampling factor before and a
|
|||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
|
||||
synth.setOversamplingFactor(sfz::Oversampling::x4);
|
||||
|
||||
synth.noteOn(0, 1, 36, 24);
|
||||
synth.noteOn(0, 1, 36, 89);
|
||||
synth.noteOn(0, 36, 24);
|
||||
synth.noteOn(0, 36, 89);
|
||||
synth.renderBlock(buffer);
|
||||
synth.setOversamplingFactor(sfz::Oversampling::x2);
|
||||
synth.renderBlock(buffer);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("[Synth] Testing channel 1")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.setNumVoices(1);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/midi_channels.sfz");
|
||||
synth.noteOn(0, 0, 60, 63);
|
||||
const auto* region = synth.getVoiceView(0)->getRegion();
|
||||
REQUIRE( region != nullptr );
|
||||
REQUIRE( region->sample == "dummy1.wav" );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Testing channel 2")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.setNumVoices(1);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/midi_channels.sfz");
|
||||
synth.noteOn(0, 1, 60, 63);
|
||||
const auto* region = synth.getVoiceView(0)->getRegion();
|
||||
REQUIRE( region != nullptr );
|
||||
REQUIRE( region->sample == "dummy2.wav" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE("[Synth] Testing channel 16")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.setNumVoices(1);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/midi_channels.sfz");
|
||||
synth.noteOn(0, 15, 60, 63);
|
||||
const auto* region = synth.getVoiceView(0)->getRegion();
|
||||
REQUIRE( region != nullptr );
|
||||
REQUIRE( region->sample == "dummy16.wav" );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] All notes offs/all sounds off (single channel)")
|
||||
TEST_CASE("[Synth] All notes offs/all sounds off")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.setNumVoices(8);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/midi_channels.sfz");
|
||||
synth.noteOn(0, 0, 60, 63);
|
||||
synth.noteOn(1, 0, 60, 63);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sound_off.sfz");
|
||||
synth.noteOn(0, 60, 63);
|
||||
synth.noteOn(0, 62, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||
synth.cc(0, 0, 120, 63);
|
||||
synth.cc(0, 120, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 0 );
|
||||
|
||||
synth.noteOn(0, 0, 60, 63);
|
||||
synth.noteOn(1, 0, 60, 63);
|
||||
synth.noteOn(0, 62, 63);
|
||||
synth.noteOn(0, 60, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||
synth.cc(0, 0, 123, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 0 );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] All notes offs/all sounds off (multi channel)")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.setNumVoices(8);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/midi_channels.sfz");
|
||||
synth.noteOn(0, 0, 60, 63);
|
||||
synth.noteOn(1, 1, 60, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||
synth.cc(0, 0, 120, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 1 );
|
||||
|
||||
synth.noteOn(0, 0, 60, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||
synth.cc(0, 0, 123, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 1 );
|
||||
synth.cc(0, 1, 123, 63);
|
||||
synth.cc(0, 123, 63);
|
||||
REQUIRE( synth.getNumActiveVoices() == 0 );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Reset all controllers")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
synth.cc(0, 0, 12, 64);
|
||||
synth.cc(0, 1, 12, 64);
|
||||
REQUIRE( synth.getMidiState().getCCValue(0, 12) == 64 );
|
||||
synth.cc(0, 0, 121, 64);
|
||||
REQUIRE( synth.getMidiState().getCCValue(0, 12) == 0 );
|
||||
REQUIRE( synth.getMidiState().getCCValue(1, 12) == 64 );
|
||||
synth.cc(0, 12, 64);
|
||||
REQUIRE( synth.getMidiState().getCCValue(12) == 64 );
|
||||
synth.cc(0, 121, 64);
|
||||
REQUIRE( synth.getMidiState().getCCValue(12) == 0 );
|
||||
}
|
||||
|
||||
TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kills the voice")
|
||||
|
|
@ -211,13 +155,13 @@ TEST_CASE("[Synth] Releasing before the EG started smoothing (initial delay) kil
|
|||
synth.setSamplesPerBlock(1024);
|
||||
synth.setNumVoices(1);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/delay_release.sfz");
|
||||
synth.noteOn(0, 1, 60, 63);
|
||||
synth.noteOn(0, 60, 63);
|
||||
REQUIRE( !synth.getVoiceView(0)->isFree() );
|
||||
synth.noteOff(100, 1, 60, 63);
|
||||
synth.noteOff(100, 60, 63);
|
||||
REQUIRE( synth.getVoiceView(0)->isFree() );
|
||||
synth.noteOn(200, 1, 60, 63);
|
||||
synth.noteOn(200, 60, 63);
|
||||
REQUIRE( !synth.getVoiceView(0)->isFree() );
|
||||
synth.noteOff(1000, 1, 60, 63);
|
||||
synth.noteOff(1000, 60, 63);
|
||||
REQUIRE( !synth.getVoiceView(0)->isFree() );
|
||||
}
|
||||
|
||||
|
|
@ -228,11 +172,11 @@ TEST_CASE("[Synth] Releasing after the initial and normal mode does not trigger
|
|||
sfz::AudioBuffer<float> buffer(2, 1024);
|
||||
synth.setNumVoices(1);
|
||||
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/delay_release.sfz");
|
||||
synth.noteOn(200, 1, 60, 63);
|
||||
synth.noteOn(200, 60, 63);
|
||||
REQUIRE( !synth.getVoiceView(0)->isFree() );
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE( !synth.getVoiceView(0)->isFree() );
|
||||
synth.noteOff(0, 1, 60, 63);
|
||||
synth.noteOff(0, 60, 63);
|
||||
synth.renderBlock(buffer);
|
||||
REQUIRE( !synth.getVoiceView(0)->isFree() );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
<region> lochan=2 hichan=14 sample=dummy.wav
|
||||
<region> lokey=2 hikey=14 sample=dummy.wav
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
<region> lochan=1 hichan=1 sample=dummy1.wav
|
||||
<region> lochan=2 hichan=2 sample=dummy2.wav
|
||||
<region> lochan=16 hichan=16 sample=dummy16.wav
|
||||
2
tests/TestFiles/sound_off.sfz
Normal file
2
tests/TestFiles/sound_off.sfz
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<region> key=60 sample=dummy1.wav
|
||||
<region> key=62 sample=dummy2.wav
|
||||
Loading…
Add table
Reference in a new issue