/************************************************************************************* * Original code copyright (C) 2012 Steve Folta * Converted to Juce module (C) 2016 Leo Olivers * Forked from https://github.com/stevefolta/SFZero * For license info please see the LICENSE file distributed with this source code *************************************************************************************/ #include "SFZSynth.h" #include "SFZSound.h" #include "SFZVoice.h" namespace sfzero { Synth::Synth() : Synthesiser() { carla_zeroStructs(noteVelocities_, 128); } void Synth::noteOn(int midiChannel, int midiNoteNumber, float velocity) { int i; int midiVelocity = static_cast(velocity * 127); // First, stop any currently-playing sounds in the group. //*** Currently, this only pays attention to the first matching region. int group = 0; Sound *sound = dynamic_cast(getSound(0)); if (sound) { Region *region = sound->getRegionFor(midiNoteNumber, midiVelocity); if (region) { group = region->group; } } if (group != 0) { for (i = voices.size(); --i >= 0;) { Voice *voice = dynamic_cast(voices.getUnchecked(i)); if (voice == nullptr) { continue; } if (voice->getGroup() == group) { voice->stopNoteForGroup(); } } } // Are any notes playing? (Needed for first/legato trigger handling.) // Also stop any voices still playing this note. bool anyNotesPlaying = false; for (i = voices.size(); --i >= 0;) { Voice *voice = dynamic_cast(voices.getUnchecked(i)); if (voice == nullptr) { continue; } if (voice->isPlayingChannel(midiChannel)) { if (voice->isPlayingNoteDown()) { if (voice->getCurrentlyPlayingNote() == midiNoteNumber) { if (!voice->isPlayingOneShot()) { voice->stopNoteQuick(); } } else { anyNotesPlaying = true; } } } } // Play *all* matching regions. Region::Trigger trigger = (anyNotesPlaying ? Region::legato : Region::first); if (sound) { int numRegions = sound->getNumRegions(); for (i = 0; i < numRegions; ++i) { Region *region = sound->regionAt(i); if (region->matches(midiNoteNumber, midiVelocity, trigger)) { Voice *voice = dynamic_cast(findFreeVoice(sound, midiNoteNumber, midiChannel, isNoteStealingEnabled())); if (voice) { voice->setRegion(region); startVoice(voice, sound, midiChannel, midiNoteNumber, velocity); } } } } noteVelocities_[midiNoteNumber] = midiVelocity; } void Synth::noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff) { Synthesiser::noteOff(midiChannel, midiNoteNumber, velocity, allowTailOff); // Start release region. Sound *sound = dynamic_cast(getSound(0)); if (sound) { Region *region = sound->getRegionFor(midiNoteNumber, noteVelocities_[midiNoteNumber], Region::release); if (region) { Voice *voice = dynamic_cast(findFreeVoice(sound, midiNoteNumber, midiChannel, false)); if (voice) { // Synthesiser is too locked-down (ivars are private rt protected), so // we have to use a "setRegion()" mechanism. voice->setRegion(region); startVoice(voice, sound, midiChannel, midiNoteNumber, noteVelocities_[midiNoteNumber] / 127.0f); } } } } int Synth::numVoicesUsed() { int numUsed = 0; for (int i = voices.size(); --i >= 0;) { if (voices.getUnchecked(i)->getCurrentlyPlayingNote() >= 0) { numUsed += 1; } } return numUsed; } water::String Synth::voiceInfoString() { enum { maxShownVoices = 20, }; water::StringArray lines; int numUsed = 0, numShown = 0; for (int i = voices.size(); --i >= 0;) { Voice *voice = dynamic_cast(voices.getUnchecked(i)); if (voice->getCurrentlyPlayingNote() < 0) { continue; } numUsed += 1; if (numShown >= maxShownVoices) { continue; } lines.add(voice->infoString()); } lines.insert(0, "voices used: " + water::String(numUsed)); return lines.joinIntoString("\n"); } }