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++:

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 an fmVoice with setPatch, which lands on the next note-on.

Next#