API BREAK: MIDI channels are now 0-based
This commit is contained in:
parent
c8ac0baeea
commit
c6a9ba88da
12 changed files with 103 additions and 49 deletions
|
|
@ -89,18 +89,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]) + 1, event.buffer[1], event.buffer[2]);
|
||||
synth->noteOff(event.time, midi::channel(event.buffer[0]), 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]) + 1, event.buffer[1], event.buffer[2]);
|
||||
synth->noteOn(event.time, midi::channel(event.buffer[0]), 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]) + 1, event.buffer[1], event.buffer[2]);
|
||||
synth->cc(event.time, midi::channel(event.buffer[0]), event.buffer[1], event.buffer[2]);
|
||||
break;
|
||||
case midi::programChange:
|
||||
// DBG("[MIDI] Program change at time " << event.time);
|
||||
|
|
|
|||
|
|
@ -446,7 +446,7 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
// "[process_midi] Received note on %d/%d at time %d\n", msg[0], msg[1], ev->time.frames);
|
||||
sfizz_send_note_on(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)MIDI_CHANNEL(msg[0]) + 1,
|
||||
(int)MIDI_CHANNEL(msg[0]),
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
|
|
@ -455,7 +455,7 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
// "[process_midi] Received note off %d/%d at time %d\n", msg[0], msg[1], ev->time.frames);
|
||||
sfizz_send_note_off(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)MIDI_CHANNEL(msg[0]) + 1,
|
||||
(int)MIDI_CHANNEL(msg[0]),
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
|
|
@ -464,7 +464,7 @@ sfizz_lv2_process_midi_event(sfizz_plugin_t *self, const LV2_Atom_Event *ev)
|
|||
// "[process_midi] Received CC %d/%d at time %d\n", msg[0], msg[1], ev->time.frames);
|
||||
sfizz_send_cc(self->synth,
|
||||
(int)ev->time.frames,
|
||||
(int)MIDI_CHANNEL(msg[0]) + 1,
|
||||
(int)MIDI_CHANNEL(msg[0]),
|
||||
(int)msg[1],
|
||||
msg[2]);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ void sfizz_set_sample_rate(sfizz_synth_t* synth, float sample_rate);
|
|||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @param channel the MIDI channel (starting at 0 for the first channel)
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
*/
|
||||
|
|
@ -159,7 +159,7 @@ void sfizz_send_note_on(sfizz_synth_t* synth, int delay, int channel, int note_n
|
|||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @param channel the MIDI channel (starting at 0 for the first channel)
|
||||
* @param note_number the MIDI note number
|
||||
* @param velocity the MIDI velocity
|
||||
*/
|
||||
|
|
@ -171,7 +171,7 @@ void sfizz_send_note_off(sfizz_synth_t* synth, int delay, int channel, int note_
|
|||
*
|
||||
* @param synth The synth
|
||||
* @param delay the delay of the event in the block, in samples.
|
||||
* @param channel the MIDI channel; currently starts at 1 (FIXME)
|
||||
* @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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ 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
|
||||
* @param channel MIDI channel (1-based)
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
|
||||
|
|
@ -102,7 +102,7 @@ struct Region {
|
|||
* @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
|
||||
* @param channel MIDI channel (1-based)
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
|
||||
|
|
@ -115,7 +115,7 @@ struct Region {
|
|||
* @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
|
||||
* @param channel MIDI channel (1-based)
|
||||
* @param ccNumber
|
||||
* @param ccValue
|
||||
* @return true if the region should trigger on this event
|
||||
|
|
@ -125,14 +125,14 @@ struct Region {
|
|||
/**
|
||||
* @brief Register a new pitch wheel event.
|
||||
*
|
||||
* @param channel
|
||||
* @param channel MIDI channel (1-based)
|
||||
* @param pitch
|
||||
*/
|
||||
void registerPitchWheel(int channel, int pitch) noexcept;
|
||||
/**
|
||||
* @brief Register a new aftertouch event.
|
||||
*
|
||||
* @param channel
|
||||
* @param channel MIDI channel (1-based)
|
||||
* @param aftertouch
|
||||
*/
|
||||
void registerAftertouch(int channel, uint8_t aftertouch) noexcept;
|
||||
|
|
|
|||
|
|
@ -35,13 +35,24 @@ using CCValueArray = std::array<uint8_t, 128>;
|
|||
using CCValuePair = std::pair<uint8_t, float> ;
|
||||
using CCNamePair = std::pair<uint8_t, std::string>;
|
||||
|
||||
/**
|
||||
* @brief Translates the zero-based MIDI channel to the 1-based SFZ channel
|
||||
*
|
||||
* @param zeroBasedChannel
|
||||
* @return constexpr int
|
||||
*/
|
||||
inline constexpr int translateMidiChannelToSfz(int zeroBasedChannel)
|
||||
{
|
||||
return zeroBasedChannel + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converts cents to a pitch ratio
|
||||
*
|
||||
* @tparam T
|
||||
* @param cents
|
||||
* @param centsPerOctave
|
||||
* @return constexpr float
|
||||
*
|
||||
* @tparam T
|
||||
* @param cents
|
||||
* @param centsPerOctave
|
||||
* @return constexpr float
|
||||
*/
|
||||
template<class T>
|
||||
inline constexpr float centsFactor(T cents, T centsPerOctave = 1200)
|
||||
|
|
@ -51,10 +62,10 @@ inline constexpr float centsFactor(T cents, T centsPerOctave = 1200)
|
|||
|
||||
/**
|
||||
* @brief Normalize a CC value between (T)0.0 and (T)1.0
|
||||
*
|
||||
* @tparam T
|
||||
* @param ccValue
|
||||
* @return constexpr float
|
||||
*
|
||||
* @tparam T
|
||||
* @param ccValue
|
||||
* @return constexpr float
|
||||
*/
|
||||
template<class T>
|
||||
inline constexpr float normalizeCC(T ccValue)
|
||||
|
|
@ -65,10 +76,10 @@ inline constexpr float normalizeCC(T ccValue)
|
|||
|
||||
/**
|
||||
* @brief Normalize a percentage between 0 and 1
|
||||
*
|
||||
* @tparam T
|
||||
* @param percentValue
|
||||
* @return constexpr float
|
||||
*
|
||||
* @tparam T
|
||||
* @param percentValue
|
||||
* @return constexpr float
|
||||
*/
|
||||
template<class T>
|
||||
inline constexpr float normalizePercents(T percentValue)
|
||||
|
|
@ -78,10 +89,10 @@ inline constexpr float normalizePercents(T percentValue)
|
|||
|
||||
/**
|
||||
* @brief Normalize a possibly negative percentage between -1 and 1
|
||||
*
|
||||
* @tparam T
|
||||
* @param percentValue
|
||||
* @return constexpr float
|
||||
*
|
||||
* @tparam T
|
||||
* @param percentValue
|
||||
* @return constexpr float
|
||||
*/
|
||||
template<class T>
|
||||
inline constexpr float normalizeNegativePercents(T percentValue)
|
||||
|
|
@ -91,11 +102,11 @@ inline constexpr float normalizeNegativePercents(T percentValue)
|
|||
|
||||
/**
|
||||
* @brief If a cc switch exists for the value, returns the value with the CC modifier, otherwise returns the value alone.
|
||||
*
|
||||
* @param ccValues
|
||||
* @param ccSwitch
|
||||
* @param value
|
||||
* @return float
|
||||
*
|
||||
* @param ccValues
|
||||
* @param ccSwitch
|
||||
* @param value
|
||||
* @return float
|
||||
*/
|
||||
inline float ccSwitchedValue(const CCValueArray& ccValues, const absl::optional<CCValuePair>& ccSwitch, float value) noexcept
|
||||
{
|
||||
|
|
@ -107,9 +118,9 @@ inline float ccSwitchedValue(const CCValueArray& ccValues, const absl::optional<
|
|||
|
||||
/**
|
||||
* @brief Convert a note in string to its equivalent midi note number
|
||||
*
|
||||
* @param value
|
||||
* @return absl::optional<uint8_t>
|
||||
*
|
||||
* @param value
|
||||
* @return absl::optional<uint8_t>
|
||||
*/
|
||||
absl::optional<uint8_t> readNoteValue(const absl::string_view& value);
|
||||
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
|
|||
ASSERT(noteNumber < 128);
|
||||
ASSERT(noteNumber >= 0);
|
||||
|
||||
channel = translateMidiChannelToSfz(channel);
|
||||
midiState.noteOn(noteNumber, velocity);
|
||||
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
|
|
@ -401,6 +402,8 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
|
|||
ASSERT(noteNumber < 128);
|
||||
ASSERT(noteNumber >= 0);
|
||||
|
||||
channel = translateMidiChannelToSfz(channel);
|
||||
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
if (!canEnterCallback)
|
||||
return;
|
||||
|
|
@ -429,6 +432,8 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc
|
|||
ASSERT(ccNumber < 128);
|
||||
ASSERT(ccNumber >= 0);
|
||||
|
||||
channel = translateMidiChannelToSfz(channel);
|
||||
|
||||
AtomicGuard callbackGuard { inCallback };
|
||||
if (!canEnterCallback)
|
||||
return;
|
||||
|
|
@ -449,13 +454,13 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc
|
|||
}
|
||||
}
|
||||
|
||||
void sfz::Synth::pitchWheel(int /* delay */, int /* channel */, int /* pitch */) noexcept
|
||||
void sfz::Synth::pitchWheel(int /* delay */, int channel, int /* pitch */) noexcept
|
||||
{
|
||||
|
||||
channel = translateMidiChannelToSfz(channel);
|
||||
}
|
||||
void sfz::Synth::aftertouch(int /* delay */, int /* channel */, uint8_t /* aftertouch */) noexcept
|
||||
void sfz::Synth::aftertouch(int /* delay */, int channel, uint8_t /* aftertouch */) noexcept
|
||||
{
|
||||
|
||||
channel = translateMidiChannelToSfz(channel);
|
||||
}
|
||||
void sfz::Synth::tempo(int /* delay */, float /* secondsPerQuarter */) noexcept
|
||||
{
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ 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
|
||||
* @param channel the midi channel for the event (0-based)
|
||||
* @param noteNumber the midi note number
|
||||
* @param velocity the midi note velocity
|
||||
*/
|
||||
|
|
@ -201,7 +201,7 @@ 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
|
||||
* @param channel the midi channel for the event (0-based)
|
||||
* @param noteNumber the midi note number
|
||||
* @param velocity the midi note velocity
|
||||
*/
|
||||
|
|
@ -211,7 +211,7 @@ 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
|
||||
* @param channel the midi channel for the event (0-based)
|
||||
* @param ccNumber the cc number
|
||||
* @param ccValue the cc value
|
||||
*/
|
||||
|
|
@ -222,7 +222,7 @@ 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
|
||||
* @param channel the midi channel for the event (0-based)
|
||||
* @param pitch the pitch value
|
||||
*/
|
||||
void pitchWheel(int delay, int channel, int pitch) noexcept;
|
||||
|
|
@ -231,7 +231,7 @@ 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
|
||||
* @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;
|
||||
|
|
@ -240,7 +240,6 @@ 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
|
||||
* @param secondsPerQuarter the new period of the quarter note
|
||||
*/
|
||||
void tempo(int delay, float secondsPerQuarter) noexcept;
|
||||
|
|
|
|||
|
|
@ -324,3 +324,39 @@ TEST_CASE("[Files] Specific bug: relative path with backslashes")
|
|||
REQUIRE(synth.getNumRegions() == 1);
|
||||
REQUIRE(synth.getRegionView(0)->sample == R"(closedhat.wav)");
|
||||
}
|
||||
|
||||
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" );
|
||||
}
|
||||
|
|
|
|||
BIN
tests/TestFiles/dummy1.wav
Executable file
BIN
tests/TestFiles/dummy1.wav
Executable file
Binary file not shown.
BIN
tests/TestFiles/dummy16.wav
Executable file
BIN
tests/TestFiles/dummy16.wav
Executable file
Binary file not shown.
BIN
tests/TestFiles/dummy2.wav
Executable file
BIN
tests/TestFiles/dummy2.wav
Executable file
Binary file not shown.
3
tests/TestFiles/midi_channels.sfz
Normal file
3
tests/TestFiles/midi_channels.sfz
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<region> lochan=1 hichan=1 sample=dummy1.wav
|
||||
<region> lochan=2 hichan=2 sample=dummy2.wav
|
||||
<region> lochan=16 hichan=16 sample=dummy16.wav
|
||||
Loading…
Add table
Reference in a new issue