API BREAK: MIDI channels are now 0-based

This commit is contained in:
Paul Ferrand 2019-12-08 21:11:54 +01:00
parent c8ac0baeea
commit c6a9ba88da
12 changed files with 103 additions and 49 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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
*/

View file

@ -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;

View file

@ -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);

View file

@ -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
{

View file

@ -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;

View file

@ -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

Binary file not shown.

BIN
tests/TestFiles/dummy16.wav Executable file

Binary file not shown.

BIN
tests/TestFiles/dummy2.wav Executable file

Binary file not shown.

View 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