Loading SFZ instruments and DX7 banks#
Goal: play a sampled instrument from an SFZ file, and an FM instrument from a vintage DX7 SysEx bank.
The synth voice model ships two instrument voices that load their sound from standard, portable formats rather than being coded in C++:
YSE::SYNTH::samplerVoicereads an SFZ instrument — a text file mapping key/velocity regions to PCM samples.YSE::SYNTH::fmVoiceplays a DX7 voice; theYSE::SYNTH::dx7SysEximporter fills its patch from a.syxbank dump.
Both draw on the optional bundled content pack; the demos degrade with a clear message when it is absent.
Sources: Demo19_SfzPiano.cpp and Demo18_FMKeyboard.cpp.
Loading an SFZ instrument#
Construct a YSE::SYNTH::samplerVoice, load an SFZ file into it,
then use it as the prototype for a synth. loadSFZ parses the file and
decodes every referenced sample into memory off the audio thread; it returns
false if the instrument is not playable:
proto_ = std::make_unique<YSE::SYNTH::samplerVoice>();
if (!proto_->loadSFZ(kSfzPath)) {
std::cout << "Failed to load the SFZ instrument at:\n " << kSfzPath << std::endl;
proto_.reset();
return;
}
synth_ = std::make_unique<YSE::synth>();
synth_->create().addVoices(*proto_, 24);
sound_ = std::make_unique<YSE::sound>();
sound_->create(*synth_);
sound_->play();
From here it is an ordinary synth: noteOn plays a mapped region,
pitch-shifting and layering as the SFZ describes.
The sustain pedal#
Sampled instruments feel right only with pedals. The synth handles CC 64 (sustain) for you — while it is down, note-offs are deferred until the pedal lifts:
void DemoSfzPiano::ToggleSustain() {
if (!available_) return;
sustainDown_ = !sustainDown_;
synth_->sustain(kMidiChannel, sustainDown_);
std::cout << "Sustain pedal " << (sustainDown_ ? "DOWN" : "UP") << "." << std::endl;
}
sostenuto (CC 66) and softPedal (CC 67) work the same way; see
YSE::synth.
Loading a DX7 bank#
An FM instrument comes from a DX7 SysEx bank. Parse the .syx file into a
YSE::SYNTH::dx7Bank, then apply one of its patches to an
YSE::SYNTH::fmVoice prototype with setPatch:
if (!YSE::SYNTH::dx7SysEx::loadBank(kBankPath, bank_) || bank_.empty()) {
std::cout << "Failed to parse the bundled DX7 bank at:\n " << kBankPath << std::endl;
return;
}
// Build a 16-voice FM synth from the first bank patch and attach it behind a
// positioned sound. The prototype must outlive addVoices (it is a member).
proto_ = std::make_unique<YSE::SYNTH::fmVoice>();
proto_->setPatch(bank_.voices[patchIndex_]);
synth_ = std::make_unique<YSE::synth>();
synth_->create().addVoices(*proto_, 16);
A bank holds up to 32 voices; bank_.size() and bank_.name(i) let you
browse them. Because the FM core bakes operator state at key-down, a
setPatch takes effect on the next note-on — so switching patches while
notes ring does not glitch the held voices; retrigger to hear the change.
What you learned#
samplerVoice::loadSFZ(path)builds a multi-layer sampled instrument; check the return value.The synth handles the sustain / sostenuto / soft pedals as CC 64 / 66 / 67.
dx7SysEx::loadBank(path, bank)parses a DX7.syx; apply a patch to anfmVoicewithsetPatch, which lands on the next note-on.
Next#
Your first synth — the synth basics.
Mixing with inserts and sends — run instruments through a mixer.
Synthesis — sampler, FM and importer reference.