Added note_offset and octave_offset

This commit is contained in:
Paul Ferrand 2019-12-21 18:57:32 +01:00
parent 8bc0a9c2f5
commit ed7d5369a7
8 changed files with 125 additions and 10 deletions

View file

@ -156,5 +156,7 @@ namespace Default
// ***** SFZ v2 ********
constexpr bool checkSustain { true }; // sustain_sw
constexpr bool checkSostenuto { true }; // sostenuto_sw
constexpr Range<int> octaveOffsetRange { -10, 10 }; // octave_offset
constexpr Range<int> noteOffsetRange { -127, 127 }; // note_offset
}
}

View file

@ -90,7 +90,7 @@ public:
* @param value
* @return Type
*/
Type clamp(Type value) const noexcept { return ::clamp(value, _start, _end); }
constexpr Type clamp(Type value) const noexcept { return ::clamp(value, _start, _end); }
/**
* @brief Checks if a value is in the range, including the endpoints
*
@ -137,6 +137,13 @@ bool operator==(const sfz::Range<Type>& lhs, const sfz::Range<Type>& rhs)
return (lhs.getStart() == rhs.getStart()) && (lhs.getEnd() == rhs.getEnd());
}
template <class Type>
bool operator!=(const sfz::Range<Type>& lhs, const sfz::Range<Type>& rhs)
{
return (lhs.getStart() != rhs.getStart()) || (lhs.getEnd() != rhs.getEnd());
}
template <class Type>
bool operator==(const sfz::Range<Type>& lhs, const std::pair<Type, Type>& rhs)
{

View file

@ -807,3 +807,57 @@ float sfz::Region::velocityCurve(uint8_t velocity) const noexcept
return gain;
}
constexpr uint8_t offsetAndClamp(uint8_t key, int offset, sfz::Range<uint8_t> range)
{
const int offsetKey { key + offset };
if (offsetKey > std::numeric_limits<uint8_t>::max())
return range.getEnd();
if (offsetKey < std::numeric_limits<uint8_t>::min())
return range.getStart();
return range.clamp(static_cast<uint8_t>(offsetKey));
}
void sfz::Region::offsetAllKeys(int offset) noexcept
{
// Offset key range
if (keyRange != Default::keyRange) {
const auto start = keyRange.getStart();
const auto end = keyRange.getEnd();
keyRange.setStart(offsetAndClamp(start, offset, Default::keyRange));
keyRange.setEnd(offsetAndClamp(end, offset, Default::keyRange));
}
pitchKeycenter = offsetAndClamp(pitchKeycenter, offset, Default::keyRange);
// Offset key switches
if (keyswitchRange != Default::keyRange) {
const auto start = keyswitchRange.getStart();
const auto end = keyswitchRange.getEnd();
keyswitchRange.setStart(offsetAndClamp(start, offset, Default::keyRange));
keyswitchRange.setEnd(offsetAndClamp(end, offset, Default::keyRange));
}
if (keyswitchUp)
keyswitchUp = offsetAndClamp(*keyswitchUp, offset, Default::keyRange);
if (keyswitch)
keyswitch = offsetAndClamp(*keyswitch, offset, Default::keyRange);
if (keyswitchDown)
keyswitchDown = offsetAndClamp(*keyswitchDown, offset, Default::keyRange);
if (previousNote)
previousNote = offsetAndClamp(*previousNote, offset, Default::keyRange);
// Offset crossfade ranges
if (crossfadeKeyInRange != Default::crossfadeKeyInRange) {
const auto start = crossfadeKeyInRange.getStart();
const auto end = crossfadeKeyInRange.getEnd();
crossfadeKeyInRange.setStart(offsetAndClamp(start, offset, Default::keyRange));
crossfadeKeyInRange.setEnd(offsetAndClamp(end, offset, Default::keyRange));
}
if (crossfadeKeyOutRange != Default::crossfadeKeyOutRange) {
const auto start = crossfadeKeyOutRange.getStart();
const auto end = crossfadeKeyOutRange.getEnd();
crossfadeKeyOutRange.setStart(offsetAndClamp(start, offset, Default::keyRange));
crossfadeKeyOutRange.setEnd(offsetAndClamp(end, offset, Default::keyRange));
}
}

View file

@ -220,6 +220,8 @@ struct Region {
*/
bool parseOpcode(const Opcode& opcode);
void offsetAllKeys(int offset) noexcept;
uint32_t loopStart(Oversampling factor = x1) const noexcept;
uint32_t loopEnd(Oversampling factor = x1) const noexcept;

View file

@ -114,6 +114,9 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
parseOpcodes(groupOpcodes);
parseOpcodes(regionOpcodes);
if (octaveOffset != 0 || noteOffset != 0)
lastRegion->offsetAllKeys(octaveOffset * 12 + noteOffset);
regions.push_back(std::move(lastRegion));
}
@ -186,6 +189,12 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
defaultPath = absl::StrReplaceAll(trim(member.value), { { "\\", "/" } });
DBG("Changing default sample path to " << defaultPath);
break;
case hash("note_offset"):
setValueFromOpcode(member, noteOffset, Default::noteOffsetRange);
break;
case hash("octave_offset"):
setValueFromOpcode(member, octaveOffset, Default::octaveOffsetRange);
break;
default:
// Unsupported control opcode
DBG("Unsupported control opcode: " << member.opcode);

View file

@ -455,8 +455,10 @@ private:
Resources resources;
MidiState midiState;
// default_path
// Control opcodes
std::string defaultPath { "" };
int noteOffset { 0 };
int octaveOffset { 0 };
LEAK_DETECTOR(Synth);
};

View file

@ -365,3 +365,46 @@ TEST_CASE("[Files] Set CC applies properly to all channels")
REQUIRE(synth.getMidiState().getCCValue(channel, 61) == 122);
}
}
TEST_CASE("[Files] Note and octave offsets")
{
sfz::Synth synth;
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/note_offset.sfz");
REQUIRE( synth.getNumRegions() == 7 );
REQUIRE( synth.getRegionView(0)->keyRange == sfz::Range<uint8_t>(64, 64) );
REQUIRE( synth.getRegionView(0)->pitchKeycenter == 64 );
REQUIRE( synth.getRegionView(0)->keyswitchRange == sfz::Default::keyRange );
REQUIRE( synth.getRegionView(0)->crossfadeKeyInRange == sfz::Default::crossfadeKeyInRange );
REQUIRE( synth.getRegionView(0)->crossfadeKeyOutRange == sfz::Default::crossfadeKeyOutRange );
REQUIRE( synth.getRegionView(1)->keyRange == sfz::Range<uint8_t>(51, 56) );
REQUIRE( synth.getRegionView(1)->pitchKeycenter == 51 );
REQUIRE( synth.getRegionView(2)->keyRange == sfz::Range<uint8_t>(41, 45) );
REQUIRE( synth.getRegionView(2)->pitchKeycenter == 41 );
REQUIRE( synth.getRegionView(2)->crossfadeKeyInRange == sfz::Range<uint8_t>(37, 41) );
REQUIRE( synth.getRegionView(2)->crossfadeKeyOutRange == sfz::Range<uint8_t>(45, 49) );
REQUIRE( synth.getRegionView(3)->keyRange == sfz::Range<uint8_t>(62, 62) );
REQUIRE( synth.getRegionView(3)->keyswitchRange == sfz::Range<uint8_t>(23, 27) );
REQUIRE( synth.getRegionView(3)->keyswitch );
REQUIRE( *synth.getRegionView(3)->keyswitch == 24 );
REQUIRE( synth.getRegionView(3)->keyswitchUp );
REQUIRE( *synth.getRegionView(3)->keyswitchUp == 24 );
REQUIRE( synth.getRegionView(3)->keyswitchDown );
REQUIRE( *synth.getRegionView(3)->keyswitchDown == 24 );
REQUIRE( synth.getRegionView(3)->previousNote );
REQUIRE( *synth.getRegionView(3)->previousNote == 61 );
REQUIRE( synth.getRegionView(4)->keyRange == sfz::Range<uint8_t>(76, 76) );
REQUIRE( synth.getRegionView(4)->pitchKeycenter == 76 );
REQUIRE( synth.getRegionView(5)->keyRange == sfz::Range<uint8_t>(50, 50) );
REQUIRE( synth.getRegionView(5)->pitchKeycenter == 50 );
REQUIRE( synth.getRegionView(6)->keyRange == sfz::Range<uint8_t>(50, 50) );
REQUIRE( synth.getRegionView(6)->pitchKeycenter == 50 );
}

View file

@ -1,16 +1,12 @@
<control> note_offset=1
<region> key=63 sample=*sine
<region> lokey=50 hikey=55 pitch_keycenter=50 sample=*sine
<region> lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=36 xfout_hikey=40 sample=*sine
<region> lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=44 xfout_hikey=48 sample=*sine
<control> note_offset=-1
<region> key=63 sample=*sine
<region> lokey=50 hikey=55 pitch_keycenter=50 sample=*sine
<region> lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=36 xfout_hikey=40 sample=*sine
<region> key=63 sw_lokey=24 sw_hikey=28 sw_last=25 sw_up=25 sw_down=25 sw_previous=62 sample=*sine
<control> note_offset=1 octave_offset=1
<region> key=63 sample=*sine
<region> lokey=50 hikey=55 pitch_keycenter=50 sample=*sine
<region> lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=36 xfout_hikey=40 sample=*sine
<control> note_offset=-1 octave_offset=-1
<region> key=63 sample=*sine
<region> lokey=50 hikey=55 pitch_keycenter=50 sample=*sine
<region> lokey=40 hikey=44 pitch_keycenter=40 xfin_lokey=36 xfin_hikey=40 xfout_lokey=36 xfout_hikey=40 sample=*sine
<control> // Check that this does not reset either note or octave offset
<region> key=63 sample=*sine