/*************************************************************************** * * * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2008 Anders Dahnielson * * Copyright (C) 2009 - 2016 Anders Dahnielson and Grigor Iliev * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * * MA 02111-1307 USA * ***************************************************************************/ #include "sfz.h" #include #include #include #include #include #include #include "../../common/File.h" #include "../../common/Path.h" #include "LookupTable.h" #include "../../common/global_private.h" namespace sfz { template T check(std::string name, T min, T max, T val) { if (val < min) { std::cerr << "sfz: The value of opcode '" << name; std::cerr << "' is below the minimum allowed value (min=" << min << "): " << val << std::endl; val = min; } if (val > max) { std::cerr << "sfz: The value of opcode '" << name; std::cerr << "' is above the maximum allowed value (max=" << max << "): " << val << std::endl; val = max; } return val; } Sample* SampleManager::FindSample(std::string samplePath, uint offset, int end) { std::map >::iterator it = sampleMap.begin(); for (; it != sampleMap.end(); it++) { if (it->first->GetFile() == samplePath) { /* Because the start of the sample is cached in RAM we treat * same sample with different offset as different samples * // TODO: Ignore offset when the whole sample is cached in RAM? */ if (it->first->Offset == offset && it->first->End == end) return it->first; } } return NULL; } ///////////////////////////////////////////////////////////// // class Script Script::Script(LinuxSampler::Path path) : m_path(path) { } Script::Script(String path) : m_path(LinuxSampler::Path::fromUnknownFS(path)) { } Script::~Script() { } String Script::Name() const { return m_path.getName(); } Script::Language_t Script::Language() { return LANGUAGE_NKSP; } String Script::GetSourceCode() { std::ifstream f(m_path.toNativeFSPath().c_str()); std::string s; // reserve required space on string object f.seekg(0, std::ios::end); s.reserve(f.tellg()); f.seekg(0, std::ios::beg); // read entire content from file and assign it to string object s.assign((std::istreambuf_iterator(f)), std::istreambuf_iterator()); return s; } ///////////////////////////////////////////////////////////// // class Articulation Articulation::Articulation() { } Articulation::~Articulation() { } ///////////////////////////////////////////////////////////// // class Definition Definition::Definition() { } Definition::~Definition() { } ///////////////////////////////////////////////////////////// // class Region Region::Region() { pSample = NULL; seq_counter = 1; } Region::~Region() { DestroySampleIfNotUsed(); } Sample* Region::GetSample(bool create) { if (pSample == NULL && create && sample != "*silence") { uint i = offset ? *offset : 0; Sample* sf = GetInstrument()->GetSampleManager()->FindSample(sample, i, end); if (sf != NULL) pSample = sf; // Reuse already created sample else pSample = new Sample(sample, false, i, end); GetInstrument()->GetSampleManager()->AddSampleConsumer(pSample, this); } return pSample; } void Region::DestroySampleIfNotUsed() { if (pSample == NULL) return; GetInstrument()->GetSampleManager()->RemoveSampleConsumer(pSample, this); if (!GetInstrument()->GetSampleManager()->HasSampleConsumers(pSample)) { GetInstrument()->GetSampleManager()->RemoveSample(pSample); delete pSample; pSample = NULL; } } bool Region::OnKey(const Query& q) { // As the region comes from a LookupTable search on the query, // the following parameters are not checked here: chan, key, // vel, chanaft, polyaft, prog, sw_previous, cc. They are all // handled by the lookup table. bool is_triggered( q.bend >= lobend && q.bend <= hibend && q.bpm >= lobpm && q.bpm < hibpm && q.rand >= lorand && q.rand < hirand && q.timer >= lotimer && q.timer <= hitimer && ( sw_last == -1 || ((sw_last >= sw_lokey && sw_last <= sw_hikey) ? (q.last_sw_key == sw_last) : false) ) && ( sw_down == -1 || ((sw_down >= sw_lokey && (sw_hikey == -1 || sw_down <= sw_hikey)) ? (q.sw[sw_down]) : false) ) && ( sw_up == -1 || ((sw_up >= sw_lokey && (sw_hikey == -1 || sw_up <= sw_hikey)) ? (!q.sw[sw_up]) : true) ) && ((trigger & q.trig) != 0) ); if (!is_triggered) return false; // seq_position has to be checked last, so we know that we // increment the right counter is_triggered = (seq_counter == seq_position); seq_counter = (seq_counter % seq_length) + 1; return is_triggered; } Articulation* Region::GetArticulation(int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, uint8_t* cc) { return new Articulation(); //todo: implement GetArticulation() } bool Region::HasLoop() { bool b = loop_mode == LOOP_UNSET ? pSample->GetLoops() : (loop_mode == LOOP_CONTINUOUS || loop_mode == LOOP_SUSTAIN); return b && GetLoopEnd() > GetLoopStart(); } uint Region::GetLoopStart() { return (!loop_start) ? pSample->GetLoopStart() : *loop_start; } uint Region::GetLoopEnd() { return (!loop_end) ? pSample->GetLoopEnd() : *loop_end; } uint Region::GetLoopCount() { return (!count) ? 0 : *count; } ///////////////////////////////////////////////////////////// // class Instrument Instrument::Instrument(File* file, std::string name, SampleManager* pSampleManager) : KeyBindings(128, false), KeySwitchBindings(128, false) { this->file = file; this->name = name; this->pSampleManager = pSampleManager ? pSampleManager : this; pLookupTable = 0; // The first 6 curves are defined internally (actually 7 with the one at index 0) Curve c; for (int i = 0; i < 128; i++) c.v[i] = i / 127.0f; curves.add(c); curves.add(c); curves.add(c); curves.add(c); curves.add(c); curves.add(c); curves.add(c); /////// } Instrument::~Instrument() { for (int i = 0; i < regions.size(); i++) { delete regions[i]; } delete pLookupTable; for (int i = 0 ; i < 128 ; i++) { delete pLookupTableCC[i]; } } void Query::search(const Instrument* pInstrument) { pRegionList = &pInstrument->pLookupTable->query(*this); regionIndex = 0; } void Query::search(const Instrument* pInstrument, int triggercc) { pRegionList = &pInstrument->pLookupTableCC[triggercc]->query(*this); regionIndex = 0; } Region* Query::next() { for ( ; regionIndex < pRegionList->size() ; regionIndex++) { if ((*pRegionList)[regionIndex]->OnKey(*this)) { return (*pRegionList)[regionIndex++]; } } return 0; } bool Instrument::DestroyRegion(Region* pRegion) { for (std::vector::iterator it = regions.begin(); it != regions.end(); it++) { if(*it == pRegion) { regions.erase(it); delete pRegion; return true; } } return false; } bool Instrument::HasKeyBinding(uint8_t key) { if (key > 127) return false; return KeyBindings[key]; } bool Instrument::HasKeySwitchBinding(uint8_t key) { if (key > 127) return false; return KeySwitchBindings[key]; } ///////////////////////////////////////////////////////////// // class ContainerDefinition ContainerDefinition::ContainerDefinition(section_type type) { Reset(); level = type; } ContainerDefinition::~ContainerDefinition() { } void ContainerDefinition::Reset() { // This is where all the default values are set. // sample definition default sample = ""; // input control lochan = 1; hichan = 16; lokey = 0; hikey = 127; lovel = 0; hivel = 127; lobend = -8192; hibend = 8192; lobpm = 0; hibpm = 500; lochanaft = 0; hichanaft = 127; lopolyaft = 0; hipolyaft = 127; loprog = 0; hiprog = 127; lorand = 0.0; hirand = 1.0; lotimer = 0.0; hitimer = 0.0; seq_length = 1; seq_position = 1; sw_lokey = -1; sw_hikey = -1; sw_last = -1; sw_down = -1; sw_up = -1; sw_previous = -1; sw_vel = VEL_CURRENT; trigger = TRIGGER_ATTACK; group = 0; off_by = 0; off_mode = OFF_FAST; // sample player count = optional::nothing; delay = optional::nothing; delay_random = optional::nothing; delay_beats = optional::nothing; stop_beats = optional::nothing; delay_samples = optional::nothing; end = 0; loop_crossfade = optional::nothing; offset = optional::nothing; offset_random = optional::nothing; loop_mode = LOOP_UNSET; loop_start = optional::nothing; loop_end = optional::nothing; sync_beats = optional::nothing; sync_offset = optional::nothing; // amplifier volume = 0; volume_oncc.clear(); volume_curvecc.clear(); volume_smoothcc.clear(); volume_stepcc.clear(); amplitude = 100; pan = 0; pan_oncc.clear(); pan_curvecc.clear(); pan_smoothcc.clear(); pan_stepcc.clear(); width = 100; position = 0; amp_keytrack = 0; amp_keycenter = 60; amp_veltrack = 100; amp_random = 0; rt_decay = 0; xfin_lokey = 0; xfin_hikey = 0; xfout_lokey = 127; xfout_hikey = 127; xf_keycurve = POWER; xfin_lovel = 0; xfin_hivel = 0; xfout_lovel = 127; xfout_hivel = 127; xf_velcurve = POWER; xf_cccurve = POWER; // pitch transpose = 0; tune = 0; pitch_keycenter = 60; pitch_keytrack = 100; pitch_veltrack = 0; pitch_random = 0; bend_up = 200; bend_down = -200; bend_step = 1; pitch_oncc.clear(); pitch_smoothcc.clear(); pitch_curvecc.clear(); pitch_stepcc.clear(); // filter fil_type = LPF_2P; cutoff = optional::nothing; cutoff_chanaft = 0; cutoff_polyaft = 0; resonance = 0; fil_keytrack = 0; fil_keycenter = 60; fil_veltrack = 0; fil_random = 0; fil2_type = LPF_2P; cutoff2 = optional::nothing; cutoff2_chanaft = 0; cutoff2_polyaft = 0; resonance2 = 0; fil2_keytrack = 0; fil2_keycenter = 60; fil2_veltrack = 0; fil2_random = 0; cutoff_oncc.clear(); cutoff_smoothcc.clear(); cutoff_curvecc.clear(); cutoff_stepcc.clear(); cutoff2_oncc.clear(); cutoff2_smoothcc.clear(); cutoff2_curvecc.clear(); cutoff2_stepcc.clear(); resonance_oncc.clear(); resonance_smoothcc.clear(); resonance_curvecc.clear(); resonance_stepcc.clear(); resonance2_oncc.clear(); resonance2_smoothcc.clear(); resonance2_curvecc.clear(); resonance2_stepcc.clear(); // per voice equalizer eq1_freq = 50; eq2_freq = 500; eq3_freq = 5000; eq1_vel2freq = 0; eq2_vel2freq = 0; eq3_vel2freq = 0; eq1_bw = 1; eq2_bw = 1; eq3_bw = 1; eq1_gain = 0; eq2_gain = 0; eq3_gain = 0; eq1_vel2gain = 0; eq2_vel2gain = 0; eq3_vel2gain = 0; // CCs for (int i = 0; i < 128; ++i) { // input control locc.set(i, 0); hicc.set(i, 127); start_locc.set(i, -1); start_hicc.set(i, -1); stop_locc.set(i, -1); stop_hicc.set(i, -1); on_locc.set(i, -1); on_hicc.set(i, -1); // sample player delay_oncc.set(i, optional::nothing); delay_samples_oncc.set(i, optional::nothing); offset_oncc.set(i, optional::nothing); // amplifier amp_velcurve.set(i, -1); gain_oncc.set(i, 0); xfin_locc.set(i, 0); xfin_hicc.set(i, 0); xfout_locc.set(i, 0); xfout_hicc.set(i, 0); // per voice equalizer eq1_freq_oncc.set(i, 0); eq2_freq_oncc.set(i, 0); eq3_freq_oncc.set(i, 0); eq1_bw_oncc.set(i, 0); eq2_bw_oncc.set(i, 0); eq3_bw_oncc.set(i, 0); eq1_gain_oncc.set(i, 0); eq2_gain_oncc.set(i, 0); eq3_gain_oncc.set(i, 0); } eg.clear(); lfos.clear(); // deprecated ampeg_delay = 0; ampeg_start = 0; //in percentage ampeg_attack = 0; ampeg_hold = 0; ampeg_decay = 0; ampeg_sustain = -1; // in percentage ampeg_release = 0; ampeg_vel2delay = 0; ampeg_vel2attack = 0; ampeg_vel2hold = 0; ampeg_vel2decay = 0; ampeg_vel2sustain = 0; ampeg_vel2release = 0; ampeg_delaycc.clear(); ampeg_startcc.clear(); ampeg_attackcc.clear(); ampeg_holdcc.clear(); ampeg_decaycc.clear(); ampeg_sustaincc.clear(); ampeg_releasecc.clear(); fileg_delay = 0; fileg_start = 0; //in percentage fileg_attack = 0; fileg_hold = 0; fileg_decay = 0; fileg_sustain = 100; // in percentage fileg_release = 0; fileg_vel2delay = 0; fileg_vel2attack = 0; fileg_vel2hold = 0; fileg_vel2decay = 0; fileg_vel2sustain = 0; fileg_vel2release = 0; fileg_depth = 0; fileg_delay_oncc.clear(); fileg_start_oncc.clear(); fileg_attack_oncc.clear(); fileg_hold_oncc.clear(); fileg_decay_oncc.clear(); fileg_sustain_oncc.clear(); fileg_release_oncc.clear(); fileg_depth_oncc.clear(); pitcheg_delay = 0; pitcheg_start = 0; //in percentage pitcheg_attack = 0; pitcheg_hold = 0; pitcheg_decay = 0; pitcheg_sustain = 100; // in percentage pitcheg_release = 0; pitcheg_depth = 0; pitcheg_vel2delay = 0; pitcheg_vel2attack = 0; pitcheg_vel2hold = 0; pitcheg_vel2decay = 0; pitcheg_vel2sustain = 0; pitcheg_vel2release = 0; pitcheg_delay_oncc.clear(); pitcheg_start_oncc.clear(); pitcheg_attack_oncc.clear(); pitcheg_hold_oncc.clear(); pitcheg_decay_oncc.clear(); pitcheg_sustain_oncc.clear(); pitcheg_release_oncc.clear(); pitcheg_depth_oncc.clear(); amplfo_delay = 0; amplfo_fade = 0; amplfo_freq = -1; /* -1 is used to determine whether the LFO was initialized */ amplfo_depth = 0; amplfo_delay_oncc.clear(); amplfo_fade_oncc.clear(); amplfo_depthcc.clear(); amplfo_freqcc.clear(); fillfo_delay = 0; fillfo_fade = 0; fillfo_freq = -1; /* -1 is used to determine whether the LFO was initialized */ fillfo_depth = 0; fillfo_delay_oncc.clear(); fillfo_fade_oncc.clear(); fillfo_depthcc.clear(); fillfo_freqcc.clear(); pitchlfo_delay = 0; pitchlfo_fade = 0; pitchlfo_freq = -1; /* -1 is used to determine whether the LFO was initialized */ pitchlfo_depth = 0; pitchlfo_delay_oncc.clear(); pitchlfo_fade_oncc.clear(); pitchlfo_depthcc.clear(); pitchlfo_freqcc.clear(); } void ContainerDefinition::CopyValuesToDefinition(Definition* definition) { // This is where the current settings are copied to the new definition. // sample definition definition->sample = sample; // input control definition->lochan = lochan; definition->hichan = hichan; definition->lokey = lokey; definition->hikey = hikey; definition->lovel = lovel; definition->hivel = hivel; definition->locc = locc; definition->hicc = hicc; definition->lobend = lobend; definition->hibend = hibend; definition->lobpm = lobpm; definition->hibpm = hibpm; definition->lochanaft = lochanaft; definition->hichanaft = hichanaft; definition->lopolyaft = lopolyaft; definition->hipolyaft = hipolyaft; definition->loprog = loprog; definition->hiprog = hiprog; definition->lorand = lorand; definition->hirand = hirand; definition->lotimer = lotimer; definition->hitimer = hitimer; definition->seq_length = seq_length; definition->seq_position = seq_position; definition->start_locc = start_locc; definition->start_hicc = start_hicc; definition->stop_locc = stop_locc; definition->stop_hicc = stop_hicc; definition->sw_lokey = sw_lokey; definition->sw_hikey = sw_hikey; definition->sw_last = sw_last; definition->sw_down = sw_down; definition->sw_up = sw_up; definition->sw_previous = sw_previous; definition->sw_vel = sw_vel; definition->trigger = trigger; definition->group = group; definition->off_by = off_by; definition->off_mode = off_mode; definition->on_locc = on_locc; definition->on_hicc = on_hicc; // sample player definition->count = count; definition->delay = delay; definition->delay_random = delay_random; definition->delay_oncc = delay_oncc; definition->delay_beats = delay_beats; definition->stop_beats = stop_beats; definition->delay_samples = delay_samples; definition->delay_samples_oncc = delay_samples_oncc; definition->end = end; definition->loop_crossfade = loop_crossfade; definition->offset = offset; definition->offset_random = offset_random; definition->offset_oncc = offset_oncc; definition->loop_mode = loop_mode; definition->loop_start = loop_start; definition->loop_end = loop_end; definition->sync_beats = sync_beats; definition->sync_offset = sync_offset; // amplifier definition->volume = volume; definition->volume_oncc = volume_oncc; definition->volume_curvecc = volume_curvecc; definition->volume_smoothcc = volume_smoothcc; definition->volume_stepcc = volume_stepcc; definition->amplitude = amplitude; definition->pan = pan; definition->pan_oncc = pan_oncc; definition->pan_curvecc = pan_curvecc; definition->pan_smoothcc = pan_smoothcc; definition->pan_stepcc = pan_stepcc; definition->width = width; definition->position = position; definition->amp_keytrack = amp_keytrack; definition->amp_keycenter = amp_keycenter; definition->amp_veltrack = amp_veltrack; definition->amp_velcurve = amp_velcurve; definition->amp_random = amp_random; definition->rt_decay = rt_decay; definition->gain_oncc = gain_oncc; definition->xfin_lokey = xfin_lokey; definition->xfin_hikey = xfin_hikey; definition->xfout_lokey = xfout_lokey; definition->xfout_hikey = xfout_hikey; definition->xf_keycurve = xf_keycurve; definition->xfin_lovel = xfin_lovel; definition->xfin_hivel = xfin_hivel; definition->xfout_lovel = xfout_lovel; definition->xfout_hivel = xfout_hivel; definition->xf_velcurve = xf_velcurve; definition->xfin_locc = xfin_locc; definition->xfin_hicc = xfin_hicc; definition->xfout_locc = xfout_locc; definition->xfout_hicc = xfout_hicc; definition->xf_cccurve = xf_cccurve; // pitch definition->transpose = transpose; definition->tune = tune; definition->pitch_keycenter = pitch_keycenter; definition->pitch_keytrack = pitch_keytrack; definition->pitch_veltrack = pitch_veltrack; definition->pitch_random = pitch_random; definition->bend_up = bend_up; definition->bend_down = bend_down; definition->bend_step = bend_step; definition->pitch_oncc = pitch_oncc; definition->pitch_smoothcc = pitch_smoothcc; definition->pitch_curvecc = pitch_curvecc; definition->pitch_stepcc = pitch_stepcc; // filter definition->fil_type = fil_type; definition->cutoff = cutoff; definition->cutoff_oncc = cutoff_oncc; definition->cutoff_smoothcc = cutoff_smoothcc; definition->cutoff_stepcc = cutoff_stepcc; definition->cutoff_curvecc = cutoff_curvecc; definition->cutoff_chanaft = cutoff_chanaft; definition->cutoff_polyaft = cutoff_polyaft; definition->resonance = resonance; definition->resonance_oncc = resonance_oncc; definition->resonance_smoothcc = resonance_smoothcc; definition->resonance_stepcc = resonance_stepcc; definition->resonance_curvecc = resonance_curvecc; definition->fil_keytrack = fil_keytrack; definition->fil_keycenter = fil_keycenter; definition->fil_veltrack = fil_veltrack; definition->fil_random = fil_random; definition->fil2_type = fil2_type; definition->cutoff2 = cutoff2; definition->cutoff2_oncc = cutoff2_oncc; definition->cutoff2_smoothcc = cutoff2_smoothcc; definition->cutoff2_stepcc = cutoff2_stepcc; definition->cutoff2_curvecc = cutoff2_curvecc; definition->cutoff2_chanaft = cutoff2_chanaft; definition->cutoff2_polyaft = cutoff2_polyaft; definition->resonance2 = resonance2; definition->resonance2_oncc = resonance2_oncc; definition->resonance2_smoothcc = resonance2_smoothcc; definition->resonance2_stepcc = resonance2_stepcc; definition->resonance2_curvecc = resonance2_curvecc; definition->fil2_keytrack = fil2_keytrack; definition->fil2_keycenter = fil2_keycenter; definition->fil2_veltrack = fil2_veltrack; definition->fil2_random = fil2_random; // per voice equalizer definition->eq1_freq = eq1_freq; definition->eq2_freq = eq2_freq; definition->eq3_freq = eq3_freq; definition->eq1_freq_oncc = eq1_freq_oncc; definition->eq2_freq_oncc = eq2_freq_oncc; definition->eq3_freq_oncc = eq3_freq_oncc; definition->eq1_vel2freq = eq1_vel2freq; definition->eq2_vel2freq = eq2_vel2freq; definition->eq3_vel2freq = eq3_vel2freq; definition->eq1_bw = eq1_bw; definition->eq2_bw = eq2_bw; definition->eq3_bw = eq3_bw; definition->eq1_bw_oncc = eq1_bw_oncc; definition->eq2_bw_oncc = eq2_bw_oncc; definition->eq3_bw_oncc = eq3_bw_oncc; definition->eq1_gain = eq1_gain; definition->eq2_gain = eq2_gain; definition->eq3_gain = eq3_gain; definition->eq1_gain_oncc = eq1_gain_oncc; definition->eq2_gain_oncc = eq2_gain_oncc; definition->eq3_gain_oncc = eq3_gain_oncc; definition->eq1_vel2gain = eq1_vel2gain; definition->eq2_vel2gain = eq2_vel2gain; definition->eq3_vel2gain = eq3_vel2gain; // envelope generator definition->eg = eg; // deprecated definition->ampeg_delay = ampeg_delay; definition->ampeg_start = ampeg_start; definition->ampeg_attack = ampeg_attack; definition->ampeg_hold = ampeg_hold; definition->ampeg_decay = ampeg_decay; definition->ampeg_sustain = ampeg_sustain; definition->ampeg_release = ampeg_release; definition->ampeg_vel2delay = ampeg_vel2delay; definition->ampeg_vel2attack = ampeg_vel2attack; definition->ampeg_vel2hold = ampeg_vel2hold; definition->ampeg_vel2decay = ampeg_vel2decay; definition->ampeg_vel2sustain = ampeg_vel2sustain; definition->ampeg_vel2release = ampeg_vel2release; definition->ampeg_delaycc = ampeg_delaycc; definition->ampeg_startcc = ampeg_startcc; definition->ampeg_attackcc = ampeg_attackcc; definition->ampeg_holdcc = ampeg_holdcc; definition->ampeg_decaycc = ampeg_decaycc; definition->ampeg_sustaincc = ampeg_sustaincc; definition->ampeg_releasecc = ampeg_releasecc; definition->fileg_delay = fileg_delay; definition->fileg_start = fileg_start; definition->fileg_attack = fileg_attack; definition->fileg_hold = fileg_hold; definition->fileg_decay = fileg_decay; definition->fileg_sustain = fileg_sustain; definition->fileg_release = fileg_release; definition->fileg_depth = fileg_depth; definition->fileg_vel2delay = fileg_vel2delay; definition->fileg_vel2attack = fileg_vel2attack; definition->fileg_vel2hold = fileg_vel2hold; definition->fileg_vel2decay = fileg_vel2decay; definition->fileg_vel2sustain = fileg_vel2sustain; definition->fileg_vel2release = fileg_vel2release; definition->fileg_delay_oncc = fileg_delay_oncc; definition->fileg_start_oncc = fileg_start_oncc; definition->fileg_attack_oncc = fileg_attack_oncc; definition->fileg_hold_oncc = fileg_hold_oncc; definition->fileg_decay_oncc = fileg_decay_oncc; definition->fileg_sustain_oncc = fileg_sustain_oncc; definition->fileg_release_oncc = fileg_release_oncc; definition->fileg_depth_oncc = fileg_depth_oncc; definition->pitcheg_delay = pitcheg_delay; definition->pitcheg_start = pitcheg_start; definition->pitcheg_attack = pitcheg_attack; definition->pitcheg_hold = pitcheg_hold; definition->pitcheg_decay = pitcheg_decay; definition->pitcheg_sustain = pitcheg_sustain; definition->pitcheg_release = pitcheg_release; definition->pitcheg_depth = pitcheg_depth; definition->pitcheg_vel2delay = pitcheg_vel2delay; definition->pitcheg_vel2attack = pitcheg_vel2attack; definition->pitcheg_vel2hold = pitcheg_vel2hold; definition->pitcheg_vel2decay = pitcheg_vel2decay; definition->pitcheg_vel2sustain = pitcheg_vel2sustain; definition->pitcheg_vel2release = pitcheg_vel2release; definition->pitcheg_delay_oncc = pitcheg_delay_oncc; definition->pitcheg_start_oncc = pitcheg_start_oncc; definition->pitcheg_attack_oncc = pitcheg_attack_oncc; definition->pitcheg_hold_oncc = pitcheg_hold_oncc; definition->pitcheg_decay_oncc = pitcheg_decay_oncc; definition->pitcheg_sustain_oncc = pitcheg_sustain_oncc; definition->pitcheg_release_oncc = pitcheg_release_oncc; definition->pitcheg_depth_oncc = pitcheg_depth_oncc; definition->amplfo_delay = amplfo_delay; definition->amplfo_fade = amplfo_fade; definition->amplfo_freq = amplfo_freq; definition->amplfo_depth = amplfo_depth; definition->amplfo_delay_oncc = amplfo_delay_oncc; definition->amplfo_fade_oncc = amplfo_fade_oncc; definition->amplfo_depthcc = amplfo_depthcc; definition->amplfo_freqcc = amplfo_freqcc; definition->fillfo_delay = fillfo_delay; definition->fillfo_fade = fillfo_fade; definition->fillfo_freq = fillfo_freq; definition->fillfo_depth = fillfo_depth; definition->fillfo_delay_oncc = fillfo_delay_oncc; definition->fillfo_fade_oncc = fillfo_fade_oncc; definition->fillfo_depthcc = fillfo_depthcc; definition->fillfo_freqcc = fillfo_freqcc; definition->pitchlfo_delay = pitchlfo_delay; definition->pitchlfo_fade = pitchlfo_fade; definition->pitchlfo_freq = pitchlfo_freq; definition->pitchlfo_depth = pitchlfo_depth; definition->pitchlfo_delay_oncc = pitchlfo_delay_oncc; definition->pitchlfo_fade_oncc = pitchlfo_fade_oncc; definition->pitchlfo_depthcc = pitchlfo_depthcc; definition->pitchlfo_freqcc = pitchlfo_freqcc; definition->eg = eg; definition->lfos = lfos; } ///////////////////////////////////////////////////////////// // class File const std::string File::MACRO_NAME_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; const std::string File::MACRO_VALUE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_. /\\"; File::File(std::string file, SampleManager* pSampleManager) : rootFile(file), _current_section(GLOBAL), id(0), default_path(""), octave_offset(0), note_offset(0) { _instrument = new Instrument(this, LinuxSampler::Path::getBaseName(file), pSampleManager); ContainerDefinition* defaultGlobalContainer = new ContainerDefinition(ContainerDefinition::GLOBAL); _current_containers.push(defaultGlobalContainer); pCurDef = defaultGlobalContainer; parseFile(file,pSampleManager); std::set velcurves; for (int i = 0; i < _instrument->regions.size(); i++) { ::sfz::Region* pRegion = _instrument->regions[i]; int low = pRegion->lokey; int high = pRegion->hikey; if (low != -1) { // lokey -1 means region doesn't play on note-on // hikey -1 is the same as no limit, except that it // also enables on_locc/on_hicc if (high == -1) high = 127; if (low < 0 || low > 127 || high < 0 || high > 127 || low > high) { std::cerr << "Invalid key range: " << low << " - " << high << std::endl; } else { for (int j = low; j <= high; j++) _instrument->KeyBindings[j] = true; } } // get keyswitches low = pRegion->sw_lokey; if (low < 0) low = 0; high = pRegion->sw_hikey; if (high == -1) { // Key switches not defined, so nothing to do } else if (low >= 0 && low <= 127 && high >= 0 && high <= 127 && high >= low) { for (int j = low; j <= high; j++) _instrument->KeySwitchBindings[j] = true; } else { std::cerr << "Invalid key switch range: " << low << " - " << high << std::endl; } // create velocity response curve // don't use copy-on-write here, instead change the actual // unique buffers in memory float* velcurve = const_cast(&pRegion->amp_velcurve[0]); if (velcurves.insert(velcurve).second) { int prev = 0; float prevvalue = 0; for (int v = 0 ; v < 128 ; v++) { if (velcurve[v] >= 0) { float step = (velcurve[v] - prevvalue) / (v - prev); for ( ; prev < v ; prev++) { velcurve[prev] = prevvalue; prevvalue += step; } } } if (prev) { float step = (1 - prevvalue) / (127 - prev); for ( ; prev < 128 ; prev++) { velcurve[prev] = prevvalue; prevvalue += step; } } else { // default curve for (int v = 0 ; v < 128 ; v++) { velcurve[v] = v * v / (127.0 * 127.0); } } } } _instrument->pLookupTable = new LookupTable(_instrument); // create separate lookup tables for controller triggered // regions, one for each CC for (int i = 0 ; i < 128 ; i++) { _instrument->pLookupTableCC[i] = new LookupTable(_instrument, i); } for (int i = 0; i < _instrument->regions.size(); i++) { Region* r = _instrument->regions[i]; copyCurves(r->volume_curvecc, r->volume_oncc); r->volume_curvecc.clear(); copySmoothValues(r->volume_smoothcc, r->volume_oncc); r->volume_smoothcc.clear(); copyStepValues(r->volume_stepcc, r->volume_oncc); r->volume_stepcc.clear(); copyCurves(r->pitch_curvecc, r->pitch_oncc); r->pitch_curvecc.clear(); copySmoothValues(r->pitch_smoothcc, r->pitch_oncc); r->pitch_smoothcc.clear(); copyStepValues(r->pitch_stepcc, r->pitch_oncc); r->pitch_stepcc.clear(); copyCurves(r->pan_curvecc, r->pan_oncc); r->pan_curvecc.clear(); copySmoothValues(r->pan_smoothcc, r->pan_oncc); r->pan_smoothcc.clear(); copyStepValues(r->pan_stepcc, r->pan_oncc); r->pan_stepcc.clear(); copyCurves(r->cutoff_curvecc, r->cutoff_oncc); r->cutoff_curvecc.clear(); copySmoothValues(r->cutoff_smoothcc, r->cutoff_oncc); r->cutoff_smoothcc.clear(); copyStepValues(r->cutoff_stepcc, r->cutoff_oncc); r->cutoff_stepcc.clear(); copyCurves(r->cutoff2_curvecc, r->cutoff2_oncc); r->cutoff2_curvecc.clear(); copySmoothValues(r->cutoff2_smoothcc, r->cutoff2_oncc); r->cutoff2_smoothcc.clear(); copyStepValues(r->cutoff2_stepcc, r->cutoff2_oncc); r->cutoff2_stepcc.clear(); copyCurves(r->resonance_curvecc, r->resonance_oncc); r->resonance_curvecc.clear(); copySmoothValues(r->resonance_smoothcc, r->resonance_oncc); r->resonance_smoothcc.clear(); copyStepValues(r->resonance_stepcc, r->resonance_oncc); r->resonance_stepcc.clear(); copyCurves(r->resonance2_curvecc, r->resonance2_oncc); r->resonance2_curvecc.clear(); copySmoothValues(r->resonance2_smoothcc, r->resonance2_oncc); r->resonance2_smoothcc.clear(); copyStepValues(r->resonance2_stepcc, r->resonance2_oncc); r->resonance2_stepcc.clear(); for (int j = 0; j < r->eg.size(); j++) { copyCurves(r->eg[j].pan_curvecc, r->eg[j].pan_oncc); r->eg[j].pan_curvecc.clear(); } for (int j = 0; j < r->lfos.size(); j++) { r->lfos[j].copySmoothValues(); r->lfos[j].copyStepValues(); copySmoothValues(r->lfos[j].volume_smoothcc, r->lfos[j].volume_oncc); r->lfos[j].volume_smoothcc.clear(); copyStepValues(r->lfos[j].volume_stepcc, r->lfos[j].volume_oncc); r->lfos[j].volume_stepcc.clear(); copySmoothValues(r->lfos[j].freq_smoothcc, r->lfos[j].freq_oncc); r->lfos[j].freq_smoothcc.clear(); copyStepValues(r->lfos[j].freq_stepcc, r->lfos[j].freq_oncc); r->lfos[j].freq_stepcc.clear(); copySmoothValues(r->lfos[j].pitch_smoothcc, r->lfos[j].pitch_oncc); r->lfos[j].pitch_smoothcc.clear(); copyStepValues(r->lfos[j].pitch_stepcc, r->lfos[j].pitch_oncc); r->lfos[j].pitch_stepcc.clear(); copySmoothValues(r->lfos[j].pan_smoothcc, r->lfos[j].pan_oncc); r->lfos[j].pan_smoothcc.clear(); copyStepValues(r->lfos[j].pan_stepcc, r->lfos[j].pan_oncc); r->lfos[j].pan_stepcc.clear(); copySmoothValues(r->lfos[j].cutoff_smoothcc, r->lfos[j].cutoff_oncc); r->lfos[j].cutoff_smoothcc.clear(); copyStepValues(r->lfos[j].cutoff_stepcc, r->lfos[j].cutoff_oncc); r->lfos[j].cutoff_stepcc.clear(); copySmoothValues(r->lfos[j].resonance_smoothcc, r->lfos[j].resonance_oncc); r->lfos[j].resonance_smoothcc.clear(); copyStepValues(r->lfos[j].resonance_stepcc, r->lfos[j].resonance_oncc); r->lfos[j].resonance_stepcc.clear(); } } } void File::parseFile(std::string file, SampleManager* pSampleManager){ enum token_type_t { HEADER, OPCODE }; token_type_t token_type = (token_type_t) -1; std::string token_string; std::ifstream fs(file.c_str()); currentDir = LinuxSampler::Path::stripLastName(file); std::string token; std::string line; currentLine = 0; // remember latest modification time of file checkFileModified(file); while (std::getline(fs, line)) { currentLine++; // COMMENT std::string::size_type slash_index = line.find("//"); if (slash_index != std::string::npos) line.resize(slash_index); // #include if (line.find("#include ") == 0) { size_t fname_start = line.find("\""); if (fname_start == std::string::npos) continue; size_t fname_end = line.find("\"", fname_start + 1); if (fname_end == std::string::npos || fname_start == fname_end) continue; std::string fname = line.substr(fname_start + 1, fname_end - fname_start - 1); if (!currentDir.empty() && !LinuxSampler::Path(fname).isAbsolute()) fname = currentDir + LinuxSampler::File::DirSeparator + fname; std::string cd = currentDir; // backup current dir int cl = currentLine; parseFile(fname, pSampleManager); currentDir = cd; // restore currentDir (since altered by parsefile()) currentLine = cl; continue; } // #define else if (line.find("#define") == 0) { size_t varname_start = line.find_first_not_of("\t ", std::strlen("#define")); size_t varname_end = line.find_first_of("\t ", varname_start + 1); size_t value_start = line.find_first_not_of("\t ", varname_end + 1); std::string varname = line.substr(varname_start, varname_end - varname_start); std::string value = line.substr(value_start, std::string::npos); if (varname.size() == 0 || value.size() == 0) { std::cerr << "sfz error: Malformed #define statement on line " << currentLine << std::endl; continue; } // Deal with DOS EOLs if (value[value.size() - 1] == '\r') { value.erase(value.size() - 1); } // Check varname if (varname[0] != '$') { std::cerr << "sfz error: Macro name '" << varname; std::cerr << "' doesn't start with '$'." << std::endl; continue; } // Invalid chars if (varname.find_first_not_of(MACRO_NAME_CHARS, 1) != std::string::npos) { std::cerr << "sfz error: Macro name '" << varname; std::cerr << "' contains invalid characters." << std::endl; } // Check value // Match alphanumeric, underscore, and decimal point. if (value.find_first_not_of(MACRO_VALUE_CHARS) != std::string::npos) { std::cerr << "sfz error: Macro value '" << value; std::cerr << "' contains invalid characters." << std::endl; continue; } _defined_macros[varname] = value; continue; } // script=path/to/scriptfile else if (line.find("script") == 0) { size_t eq = line.find_first_of("="); if (eq == std::string::npos) { std::cerr << "sfz error: opcode 'script' misses '=' character\n"; continue; } std::string value = trim( line.substr(eq+1, std::string::npos) ); if (value.empty()) { std::cerr << "sfz error: empty path assigned to opcode 'script'\n"; continue; } LinuxSampler::Path path = LinuxSampler::Path::fromUnknownFS(value); if (!currentDir.empty() && !path.isAbsolute()) path = LinuxSampler::Path::fromUnknownFS(currentDir) + path; LinuxSampler::File file(path); if (!file.Exist()) { std::cerr << "sfz error: script file '" << value << "' does not exist\n"; continue; } if (!file.IsFile()) { std::cerr << "sfz error: script '" << value << "' is not a file\n"; continue; } Script script(path); _instrument->scripts.push_back(script); continue; } // DEFINITION std::stringstream linestream(line); int spaces = 0; while (linestream >> token) { linestream >> std::noskipws; if (token[0] == '<') { std::string::size_type p = token.find('>', 1); if (p != std::string::npos && p < (token.size() - 1)) { linestream.seekg(p + 1 - token.size(), std::stringstream::cur); token.erase(p + 1); } } if (token[0] == '<' && token[token.size()-1] == '>') { // HEAD if (!token_string.empty()) { switch (token_type) { case HEADER: push_header(token_string); break; case OPCODE: push_opcode(token_string); break; } token_string.erase(); } token_string.append(token); token_type = HEADER; } else if (token.find('=') != std::string::npos) { // HEAD if (!token_string.empty()) { switch (token_type) { case HEADER: push_header(token_string); break; case OPCODE: push_opcode(token_string); break; } token_string.erase(); } token_string.append(token); token_type = OPCODE; } else { // TAIL token_string.append(spaces, ' '); token_string.append(token); } spaces = 0; while (isspace(linestream.peek())) { linestream.ignore(); spaces++; } } // EOL if (!token_string.empty()) { switch (token_type) { case HEADER: push_header(token_string); break; case OPCODE: push_opcode(token_string); break; } token_string.erase(); } } } optional File::getModTimeOfFile(std::string filename) { struct stat st; if (stat(filename.c_str(), &st)) return optional::nothing; return (Time) { #if defined(WIN32) .sec = (intmax_t) st.st_mtime, .nsec = (uint32_t) 0 #elif defined(__APPLE__) .sec = (intmax_t) st.st_mtimespec.tv_sec, .nsec = (uint32_t) st.st_mtimespec.tv_nsec #else .sec = (intmax_t) st.st_mtim.tv_sec, .nsec = (uint32_t) st.st_mtim.tv_nsec #endif }; } bool File::checkFileModified(std::string filename) { if (filename.empty()) { bool res = false; for (const auto& mod_time : mod_times) { res |= checkFileModified(mod_time.first); } return res; } optional