--- linuxsampler/trunk/src/engines/sfz/sfz.cpp 2010/05/15 09:02:31 2091 +++ linuxsampler/trunk/src/engines/sfz/sfz.cpp 2011/08/07 14:27:24 2231 @@ -3,7 +3,7 @@ * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2008 Anders Dahnielson * - * Copyright (C) 2009 - 2010 Anders Dahnielson and Grigor Iliev * + * Copyright (C) 2009 - 2011 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 * @@ -32,14 +32,38 @@ #include "../../common/File.h" #include "../../common/Path.h" #include "../../common/global_private.h" +#include "LookupTable.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) { + Sample* SampleManager::FindSample(std::string samplePath, int offset) { std::map >::iterator it = sampleMap.begin(); for (; it != sampleMap.end(); it++) { - if (it->first->GetFile() == samplePath) return it->first; + 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? + */ + int maxOffset = it->first->MaxOffset; + if(it->first->Offset == offset || (it->first->Offset < maxOffset && offset < maxOffset)) { + return it->first; + } + } } return NULL; @@ -89,9 +113,10 @@ Sample* Region::GetSample(bool create) { if(pSample == NULL && create) { - Sample* sf = GetInstrument()->GetSampleManager()->FindSample(sample); + int i = offset ? *offset : 0; + Sample* sf = GetInstrument()->GetSampleManager()->FindSample(sample, i); if (sf != NULL) pSample = sf; // Reuse already created sample - else pSample = new Sample(sample); + else pSample = new Sample(sample, false, i); GetInstrument()->GetSampleManager()->AddSampleConsumer(pSample, this); } return pSample; @@ -107,66 +132,32 @@ } } - bool - Region::OnKey(uint8_t chan, uint8_t key, uint8_t vel, - int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, - uint8_t prog, float rand, trigger_t trig, uint8_t* cc, - float timer, bool* sw, uint8_t last_sw_key, uint8_t prev_sw_key) - { - // chan (MIDI channel) - // key (MIDI note) - // vel (MIDI velocity) - - // bend (MIDI pitch bend) - // bpm (host BPM) - // chanaft (MIDI channel pressure) - // polyaft (MIDI polyphonic aftertouch) - // prog (MIDI program change) - // rand (generated random number) - // trigger (how it was triggered) - // cc (all 128 CC values) - - // timer (time since previous region in the group was triggered) - // sw (the state of region key switches, 128 possible values) - // last_sw_key (the last key pressed in the key switch range) - // prev_sw_key (the previous note value) - - bool is_triggered ( - chan >= lochan && chan <= hichan && - key >= lokey && key <= hikey && - vel >= lovel && vel <= hivel && - bend >= lobend && bend <= hibend && - bpm >= lobpm && bpm < hibpm && - chanaft >= lochanaft && chanaft <= hichanaft && - polyaft >= lopolyaft && polyaft <= hipolyaft && - prog >= loprog && prog <= hiprog && - rand >= lorand && rand < hirand && - timer >= lotimer && timer <= hitimer && + 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) ? (last_sw_key == sw_last) : false) ) && + ((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)) ? (sw[sw_down]) : false) ) && + ((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)) ? (!sw[sw_up]) : true) ) && + ((sw_up >= sw_lokey && (sw_hikey == -1 || sw_up <= sw_hikey)) ? (!q.sw[sw_up]) : true) ) && - ( sw_previous == -1 || - prev_sw_key == sw_previous ) && - - ((trigger & trig) != 0) + ((trigger & q.trig) != 0) ); if (!is_triggered) return false; - for (int i = 0; i < 128; ++i) - { - if (locc[i] != -1 && hicc[i] != -1 && !(cc[i] >= locc[i] && cc[i] <= hicc[i])) - return false; - } - // seq_position has to be checked last, so we know that we // increment the right counter is_triggered = (seq_counter == seq_position); @@ -175,69 +166,6 @@ return is_triggered; } - bool - Region::OnControl(uint8_t chan, uint8_t cont, uint8_t val, - int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, - uint8_t prog, float rand, trigger_t trig, uint8_t* cc, - float timer, bool* sw, uint8_t last_sw_key, uint8_t prev_sw_key) - { - // chan (MIDI channel) - // cont (MIDI controller) - // val (MIDI controller value) - - // bend (MIDI pitch bend) - // bpm (host BPM) - // chanaft (MIDI channel pressure) - // polyaft (MIDI polyphonic aftertouch) - // prog (MIDI program change) - // rand (generated random number) - // trigger (how it was triggered) - // cc (all CC values) - - // timer (time since previous region in the group was triggered) - // sw (the state of region key switches, 128 possible values) - // last_sw_key (the last key pressed in the key switch range) - // prev_sw_key (the previous note value) - - bool is_triggered = ( - chan >= lochan && chan <= hichan && - ((val >= on_locc[cont] && val <= on_hicc[cont]) || - (val >= start_locc[cont] && val <= start_hicc[cont])) && - bend >= lobend && bend <= hibend && - bpm >= lobpm && bpm < hibpm && - chanaft >= lochanaft && chanaft <= hichanaft && - polyaft >= lopolyaft && polyaft <= hipolyaft && - prog >= loprog && prog <= hiprog && - rand >= lorand && rand < hirand && - timer >= lotimer && timer <= hitimer && - - ( sw_last == -1 || - ((sw_last >= sw_lokey && sw_last <= sw_hikey) ? (last_sw_key == sw_last) : false) ) && - - ( sw_down == -1 || - ((sw_down >= sw_lokey && (sw_hikey == -1 || sw_down <= sw_hikey)) ? (sw[sw_down]) : false) ) && - - ( sw_up == -1 || - ((sw_up >= sw_lokey && (sw_hikey == -1 || sw_up <= sw_hikey)) ? (!sw[sw_up]) : true) ) && - - ( sw_previous == -1 || - prev_sw_key == sw_previous ) && - - ((trigger & trig) != 0) - ); - - if (!is_triggered) - return false; - - for (int i = 0; i < 128; ++i) - { - if (locc[i] != -1 && hicc[i] != -1 && !(cc[i] >= locc[i] && cc[i] <= hicc[i])) - return false; - } - - return true; - } - Articulation* Region::GetArticulation(int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, uint8_t* cc) { @@ -245,16 +173,17 @@ } bool Region::HasLoop() { - bool b = loop_mode == ::sfz::LOOP_CONTINUOUS || loop_mode == ::sfz::LOOP_SUSTAIN; // TODO: ONE_SHOT mode + bool b = loop_mode == ::sfz::LOOP_UNSET ? pSample->GetLoops() : + (loop_mode == ::sfz::LOOP_CONTINUOUS || loop_mode == ::sfz::LOOP_SUSTAIN); return b && GetLoopStart() && GetLoopEnd() && GetLoopEnd() > GetLoopStart(); } uint Region::GetLoopStart() { - return (!loop_start) ? 0 : *loop_start; // TODO: use sample loop when loop_start not defined + return (!loop_start) ? pSample->GetLoopStart() : *loop_start; } uint Region::GetLoopEnd() { - return (!loop_end) ? 0 : *loop_end; // TODO: use sample loop when loop_end not defined + return (!loop_end) ? pSample->GetLoopEnd() : *loop_end; } uint Region::GetLoopCount() { @@ -268,31 +197,44 @@ { 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]); + for (int i = 0; i < regions.size(); i++) { + delete regions[i]; + } + delete pLookupTable; + for (int i = 0 ; i < 128 ; i++) { + delete pLookupTableCC[i]; } - regions.clear(); } - std::vector Instrument::GetRegionsOnKey ( - uint8_t chan, uint8_t key, uint8_t vel, - int bend, uint8_t bpm, uint8_t chanaft, uint8_t polyaft, - uint8_t prog, float rand, trigger_t trig, uint8_t* cc, - float timer, bool* sw, uint8_t last_sw_key, uint8_t prev_sw_key - ) { - std::vector v; - for (int i = 0; i < regions.size(); i++) { - if (regions[i]->OnKey ( - chan, key, vel, bend, bpm, chanaft, polyaft, prog, - rand, trig, cc, timer, sw, last_sw_key, prev_sw_key) - ) { v.push_back(regions[i]); } - } + void Query::search(const Instrument* pInstrument) { + pRegionList = &pInstrument->pLookupTable->query(*this); + regionIndex = 0; + } - return v; + 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) { @@ -363,7 +305,7 @@ trigger = TRIGGER_ATTACK; group = 0; - off_by.unset(); + off_by = 0; off_mode = OFF_FAST; // sample player @@ -374,12 +316,14 @@ end.unset(); loop_crossfade.unset(); offset.unset(); offset_random.unset(); - loop_mode = NO_LOOP; + loop_mode = LOOP_UNSET; loop_start.unset(); loop_end.unset(); sync_beats.unset(); sync_offset.unset(); // amplifier volume = 0; + volume_oncc.clear(); + volume_curvecc.clear(); pan = 0; width = 100; position = 0; @@ -500,9 +444,13 @@ eq1_gain_oncc.set(i, 0); eq2_gain_oncc.set(i, 0); eq3_gain_oncc.set(i, 0); + + pitchlfo_depthcc.set(i, 0); } + cutoff_cc = 0; eg.clear(); + lfos.clear(); // deprecated ampeg_delay = 0; @@ -510,9 +458,24 @@ ampeg_attack = 0; ampeg_hold = 0; ampeg_decay = 0; - ampeg_sustain = 100; // in percentage + 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; @@ -521,6 +484,14 @@ 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; + pitcheg_delay = 0; pitcheg_start = 0; //in percentage pitcheg_attack = 0; @@ -528,21 +499,32 @@ 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; amplfo_delay = 0; amplfo_fade = 0; - amplfo_freq = 0; + amplfo_freq = -1; /* -1 is used to determine whether the LFO was initialized */ amplfo_depth = 0; + amplfo_freqcc.clear(); fillfo_delay = 0; fillfo_fade = 0; - fillfo_freq = 0; + fillfo_freq = -1; /* -1 is used to determine whether the LFO was initialized */ fillfo_depth = 0; + fillfo_freqcc.clear(); pitchlfo_delay = 0; pitchlfo_fade = 0; - pitchlfo_freq = 0; + pitchlfo_freq = -1; /* -1 is used to determine whether the LFO was initialized */ pitchlfo_depth = 0; + pitchlfo_freqcc.clear(); } Region* @@ -622,6 +604,8 @@ // amplifier region->volume = volume; + region->volume_oncc = volume_oncc; + region->volume_curvecc = volume_curvecc; region->pan = pan; region->width = width; region->position = position; @@ -663,6 +647,7 @@ region->fil_type = fil_type; region->cutoff = cutoff; region->cutoff_oncc = cutoff_oncc; + region->cutoff_cc = cutoff_cc; region->cutoff_smoothcc = cutoff_smoothcc; region->cutoff_stepcc = cutoff_stepcc; region->cutoff_curvecc = cutoff_curvecc; @@ -734,6 +719,21 @@ region->ampeg_sustain = ampeg_sustain; region->ampeg_release = ampeg_release; + region->ampeg_vel2delay = ampeg_vel2delay; + region->ampeg_vel2attack = ampeg_vel2attack; + region->ampeg_vel2hold = ampeg_vel2hold; + region->ampeg_vel2decay = ampeg_vel2decay; + region->ampeg_vel2sustain = ampeg_vel2sustain; + region->ampeg_vel2release = ampeg_vel2release; + + region->ampeg_delaycc = ampeg_delaycc; + region->ampeg_startcc = ampeg_startcc; + region->ampeg_attackcc = ampeg_attackcc; + region->ampeg_holdcc = ampeg_holdcc; + region->ampeg_decaycc = ampeg_decaycc; + region->ampeg_sustaincc = ampeg_sustaincc; + region->ampeg_releasecc = ampeg_releasecc; + region->fileg_delay = fileg_delay; region->fileg_start = fileg_start; region->fileg_attack = fileg_attack; @@ -741,6 +741,14 @@ region->fileg_decay = fileg_decay; region->fileg_sustain = fileg_sustain; region->fileg_release = fileg_release; + region->fileg_depth = fileg_depth; + + region->fileg_vel2delay = fileg_vel2delay; + region->fileg_vel2attack = fileg_vel2attack; + region->fileg_vel2hold = fileg_vel2hold; + region->fileg_vel2decay = fileg_vel2decay; + region->fileg_vel2sustain = fileg_vel2sustain; + region->fileg_vel2release = fileg_vel2release; region->pitcheg_delay = pitcheg_delay; region->pitcheg_start = pitcheg_start; @@ -749,21 +757,39 @@ region->pitcheg_decay = pitcheg_decay; region->pitcheg_sustain = pitcheg_sustain; region->pitcheg_release = pitcheg_release; + region->pitcheg_depth = pitcheg_depth; + + region->pitcheg_vel2delay = pitcheg_vel2delay; + region->pitcheg_vel2attack = pitcheg_vel2attack; + region->pitcheg_vel2hold = pitcheg_vel2hold; + region->pitcheg_vel2decay = pitcheg_vel2decay; + region->pitcheg_vel2sustain = pitcheg_vel2sustain; + region->pitcheg_vel2release = pitcheg_vel2release; region->amplfo_delay = amplfo_delay; region->amplfo_fade = amplfo_fade; region->amplfo_freq = amplfo_freq; region->amplfo_depth = amplfo_depth; + + region->amplfo_freqcc = amplfo_freqcc; region->fillfo_delay = fillfo_delay; region->fillfo_fade = fillfo_fade; region->fillfo_freq = fillfo_freq; region->fillfo_depth = fillfo_depth; + + region->fillfo_freqcc = fillfo_freqcc; region->pitchlfo_delay = pitchlfo_delay; region->pitchlfo_fade = pitchlfo_fade; region->pitchlfo_freq = pitchlfo_freq; region->pitchlfo_depth = pitchlfo_depth; + + region->pitchlfo_depthcc = pitchlfo_depthcc; + region->pitchlfo_freqcc = pitchlfo_freqcc; + + region->eg = eg; + region->lfos = lfos; return region; } @@ -867,10 +893,15 @@ ::sfz::Region* pRegion = _instrument->regions[i]; int low = pRegion->lokey; int high = pRegion->hikey; - 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; + 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 @@ -923,6 +954,22 @@ } } } + + _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 k = 0; k < _instrument->regions.size(); k++) { + Region* r = _instrument->regions[k]; + + copyCurves(r->volume_curvecc, r->volume_oncc); + + r->volume_curvecc.clear(); + } } File::~File() @@ -936,6 +983,16 @@ { return _instrument; } + + void File::copyCurves(LinuxSampler::ArrayList& curves, LinuxSampler::ArrayList& dest) { + for (int i = 0; i < curves.size(); i++) { + for (int j = 0; j < dest.size(); j++) { + if (curves[i].Controller == dest[j].Controller) { + dest[j].Curve = curves[i].Curve; + } + } + } + } void File::push_header(std::string token) @@ -961,6 +1018,12 @@ octave_offset = 0; note_offset = 0; } + else if (token == "") + { + _current_section = CURVE; + _instrument->curves.add(Curve()); + _current_curve = &_instrument->curves[_instrument->curves.size() - 1]; + } else { _current_section = UNKNOWN; @@ -977,7 +1040,20 @@ std::string::size_type delimiter_index = token.find('='); std::string key = token.substr(0, delimiter_index); std::string value = token.substr(delimiter_index + 1); - int x, y; + int x, y, z; + + if (_current_section == CURVE) { + if (sscanf(key.c_str(), "v%d", &x)) { + if (x < 0 || x > 127) { + std::cerr << "Invalid curve index: " << x << std::endl; + } + _current_curve->v[x] = check(key, 0.0f, 1.0f, ToFloat(value)); + } else { + std::cerr << "The opcode '" << key << "' in section is unsupported by libsfz!" << std::endl; + } + + return; + } // sample definition if ("sample" == key) @@ -1034,8 +1110,8 @@ else if ("hivel" == key) pCurDef->hivel = ToInt(value); else if ("lobend" == key) pCurDef->lobend = ToInt(value); else if ("hibend" == key) pCurDef->hibend = ToInt(value); - else if ("lobpm" == key) pCurDef->lobpm = ToInt(value); - else if ("hibpm" == key) pCurDef->hibpm = ToInt(value); + else if ("lobpm" == key) pCurDef->lobpm = ToFloat(value); + else if ("hibpm" == key) pCurDef->hibpm = ToFloat(value); else if ("lochanaft" == key) pCurDef->lochanaft = ToInt(value); else if ("hichanaft" == key) pCurDef->hichanaft = ToInt(value); else if ("lopolyaft" == key) pCurDef->lopolyaft = ToInt(value); @@ -1070,8 +1146,8 @@ else if ("off_by" == key || "offby" == key) pCurDef->off_by = ToInt(value); else if ("off_mode" == key || "offmode" == key) { - if (value == "fast") _current_group->off_mode = OFF_FAST; - else if (value == "normal") _current_group->off_mode = OFF_NORMAL; + if (value == "fast") pCurDef->off_mode = OFF_FAST; + else if (value == "normal") pCurDef->off_mode = OFF_NORMAL; } // sample player @@ -1095,6 +1171,7 @@ else if ("loopstart" == key) pCurDef->loop_start = ToInt(value); // nonstandard else if ("loop_end" == key) pCurDef->loop_end = ToInt(value); else if ("loopend" == key) pCurDef->loop_end = ToInt(value); // nonstandard + else if ("offset" == key) pCurDef->offset = ToInt(value); else if ("sync_beats" == key) pCurDef->sync_beats = ToInt(value); else if ("sync_offset" == key) pCurDef->sync_offset = ToInt(value); @@ -1107,7 +1184,7 @@ else if ("amp_keycenter" == key) pCurDef->amp_keycenter = parseKey(value); else if ("amp_veltrack" == key) pCurDef->amp_veltrack = ToFloat(value); else if ("amp_random" == key) pCurDef->amp_random = ToFloat(value); - else if ("rt_decay" == key) pCurDef->rt_decay = ToFloat(value); + else if ("rt_decay" == key || "rtdecay" == key) pCurDef->rt_decay = ToFloat(value); else if ("xfin_lokey" == key) pCurDef->xfin_lokey = parseKey(value); else if ("xfin_hikey" == key) pCurDef->xfin_hikey = parseKey(value); else if ("xfout_lokey" == key) pCurDef->xfout_lokey = parseKey(value); @@ -1141,7 +1218,7 @@ else if ("pitch_random" == key) pCurDef->pitch_random = ToInt(value); else if ("bend_up" == key || "bendup" == key) pCurDef->bend_up = ToInt(value); else if ("bend_down" == key || "benddown" == key) pCurDef->bend_down = ToInt(value); - else if ("bend_step" == key) pCurDef->bend_step = ToInt(value); + else if ("bend_step" == key || "bendstep" == key) pCurDef->bend_step = ToInt(value); // filter else if ("fil_type" == key || "filtype" == key) @@ -1215,7 +1292,107 @@ else if (sscanf(key.c_str(), "amp_velcurve_%d", &x)) { pCurDef->amp_velcurve.set(x, ToFloat(value)); } + + // v2 envelope generators + else if (sscanf(key.c_str(), "eg%d%n", &x, &y)) { + const char* s = key.c_str() + y; + if (sscanf(s, "_time%d%n", &y, &z)) { + const char* s2 = s + z; + if (strcmp(s2, "") == 0) egnode(x, y).time = check(key, 0.0f, 100.0f, ToFloat(value)); + else if (sscanf(s2, "_oncc%d", &z)) egnode(x, y).time_oncc.add( CC(z, check(key, 0.0f, 100.0f, ToFloat(value))) ); + } else if (sscanf(s, "_level%d%n", &y, &z)) { + const char* s2 = s + z; + if (strcmp(s2, "") == 0) egnode(x, y).level = check(key, 0.0f, 1.0f, ToFloat(value)); + else if (sscanf(s2, "_oncc%d", &z)) egnode(x, y).level_oncc.add( CC(z, check(key, 0.0f, 1.0f, ToFloat(value))) ); + } + else if (sscanf(s, "_shape%d", &y)) egnode(x, y).shape = ToFloat(value); + else if (sscanf(s, "_curve%d", &y)) egnode(x, y).curve = ToFloat(value); + else if (strcmp(s, "_sustain") == 0) eg(x).sustain = ToInt(value); + else if (strcmp(s, "_loop") == 0) eg(x).loop = ToInt(value); + else if (strcmp(s, "_loop_count") == 0) eg(x).loop_count = ToInt(value); + else if (strcmp(s, "_amplitude") == 0) eg(x).amplitude = ToFloat(value); + else if (strcmp(s, "_cutoff") == 0) eg(x).cutoff = ToFloat(value); + else std::cerr << "The opcode '" << key << "' is unsupported by libsfz!" << std::endl; + } + // v1 envelope generators + else if ("ampeg_delay" == key) pCurDef->ampeg_delay = ToFloat(value); + else if ("ampeg_start" == key) pCurDef->ampeg_start = ToFloat(value); + else if ("ampeg_attack" == key) pCurDef->ampeg_attack = ToFloat(value); + else if ("ampeg_hold" == key) pCurDef->ampeg_hold = ToFloat(value); + else if ("ampeg_decay" == key) pCurDef->ampeg_decay = ToFloat(value); + else if ("ampeg_sustain" == key) pCurDef->ampeg_sustain = ToFloat(value); + else if ("ampeg_release" == key) pCurDef->ampeg_release = ToFloat(value); + else if ("ampeg_vel2delay" == key) pCurDef->ampeg_vel2delay = ToFloat(value); + else if ("ampeg_vel2attack" == key) pCurDef->ampeg_vel2attack = ToFloat(value); + else if ("ampeg_vel2hold" == key) pCurDef->ampeg_vel2hold = ToFloat(value); + else if ("ampeg_vel2decay" == key) pCurDef->ampeg_vel2decay = ToFloat(value); + else if ("ampeg_vel2sustain" == key) pCurDef->ampeg_vel2sustain = ToFloat(value); + else if ("ampeg_vel2release" == key) pCurDef->ampeg_vel2release = ToFloat(value); + else if ("fileg_delay" == key) pCurDef->fileg_delay = ToFloat(value); + else if ("fileg_start" == key) pCurDef->fileg_start = ToFloat(value); + else if ("fileg_attack" == key) pCurDef->fileg_attack = ToFloat(value); + else if ("fileg_hold" == key) pCurDef->fileg_hold = ToFloat(value); + else if ("fileg_decay" == key) pCurDef->fileg_decay = ToFloat(value); + else if ("fileg_sustain" == key) pCurDef->fileg_sustain = ToFloat(value); + else if ("fileg_release" == key) pCurDef->fileg_release = ToFloat(value); + else if ("fileg_depth" == key) pCurDef->fileg_depth = check(key, -12000, 12000, ToInt(value)); + else if ("fileg_vel2delay" == key) pCurDef->fileg_vel2delay = check(key, -100.0f, 100.0f, ToFloat(value)); + else if ("fileg_vel2attack" == key) pCurDef->fileg_vel2attack = ToFloat(value); + else if ("fileg_vel2hold" == key) pCurDef->fileg_vel2hold = ToFloat(value); + else if ("fileg_vel2decay" == key) pCurDef->fileg_vel2decay = ToFloat(value); + else if ("fileg_vel2sustain" == key) pCurDef->fileg_vel2sustain = ToFloat(value); + else if ("fileg_vel2release" == key) pCurDef->fileg_vel2release = ToFloat(value); + else if ("pitcheg_delay" == key) pCurDef->pitcheg_delay = ToFloat(value); + else if ("pitcheg_start" == key) pCurDef->pitcheg_start = ToFloat(value); + else if ("pitcheg_attack" == key) pCurDef->pitcheg_attack = ToFloat(value); + else if ("pitcheg_hold" == key) pCurDef->pitcheg_hold = ToFloat(value); + else if ("pitcheg_decay" == key) pCurDef->pitcheg_decay = ToFloat(value); + else if ("pitcheg_sustain" == key) pCurDef->pitcheg_sustain = ToFloat(value); + else if ("pitcheg_release" == key) pCurDef->pitcheg_release = ToFloat(value); + else if ("pitcheg_depth" == key) pCurDef->pitcheg_depth = check(key, -12000, 12000, ToInt(value)); + else if ("pitcheg_vel2delay" == key) pCurDef->pitcheg_vel2delay = check(key, -100.0f, 100.0f, ToFloat(value)); + else if ("pitcheg_vel2attack" == key) pCurDef->pitcheg_vel2attack = ToFloat(value); + else if ("pitcheg_vel2hold" == key) pCurDef->pitcheg_vel2hold = ToFloat(value); + else if ("pitcheg_vel2decay" == key) pCurDef->pitcheg_vel2decay = ToFloat(value); + else if ("pitcheg_vel2sustain" == key) pCurDef->pitcheg_vel2sustain = ToFloat(value); + else if ("pitcheg_vel2release" == key) pCurDef->pitcheg_vel2release = ToFloat(value); + + + // v1 LFO + else if ("amplfo_delay" == key) pCurDef->amplfo_delay = ToFloat(value); + else if ("amplfo_fade" == key) pCurDef->amplfo_fade = ToFloat(value); + else if ("amplfo_freq" == key) pCurDef->amplfo_freq = ToFloat(value); + else if ("amplfo_depth" == key) pCurDef->amplfo_depth = ToFloat(value); + else if ("fillfo_delay" == key) pCurDef->fillfo_delay = ToFloat(value); + else if ("fillfo_fade" == key) pCurDef->fillfo_fade = ToFloat(value); + else if ("fillfo_freq" == key) pCurDef->fillfo_freq = ToFloat(value); + else if ("fillfo_depth" == key) pCurDef->fillfo_depth = ToFloat(value); + else if ("pitchlfo_delay" == key) pCurDef->pitchlfo_delay = ToFloat(value); + else if ("pitchlfo_fade" == key) pCurDef->pitchlfo_fade = ToFloat(value); + else if ("pitchlfo_freq" == key) pCurDef->pitchlfo_freq = ToFloat(value); + else if ("pitchlfo_depth" == key) pCurDef->pitchlfo_depth = ToInt(value); + + // v2 LFO + else if (sscanf(key.c_str(), "lfo%d%n", &x, &y)) { + const char* s = key.c_str() + y; + if (strcmp(s, "_freq") == 0) lfo(x).freq = check(key, 0.0f, 20.0f, ToFloat(value)); + else if (sscanf(s, "_freq_oncc%d", &y)) lfo(x).freq_oncc.add( CC(y, check(key, 0.0f, 20.0f, ToFloat(value))) ); + else if (strcmp(s, "_wave") == 0) lfo(x).wave = ToInt(value); + else if (strcmp(s, "_delay") == 0) lfo(x).delay = check(key, 0.0f, 100.0f, ToFloat(value)); + else if (strcmp(s, "_fade") == 0) lfo(x).fade = check(key, 0.0f, 100.0f, ToFloat(value)); + else if (sscanf(s, "_fade_oncc%d", &y)) lfo(x).fade_oncc.add( CC(y, check(key, 0.0f, 100.0f, ToFloat(value))) ); + else if (strcmp(s, "_phase") == 0) lfo(x).phase = check(key, 0.0f, 360.0f, ToFloat(value)); + else if (sscanf(s, "_phase_oncc%d", &y)) lfo(x).phase_oncc.add( CC(y, check(key, 0.0f, 360.0f, ToFloat(value))) ); + else if (strcmp(s, "_volume") == 0) lfo(x).volume = check(key, -144.0f, 6.0f, ToFloat(value)); + else if (strcmp(s, "_pitch") == 0) lfo(x).pitch = check(key, -9600, 9600, ToInt(value)); + else if (sscanf(s, "_pitch_oncc%d", &y)) lfo(x).pitch_oncc.add( CC(y, check(key, -9600, 9600, ToInt(value))) ); + else if (strcmp(s, "_cutoff") == 0) lfo(x).cutoff = check(key, -9600, 9600, ToInt(value)); + else if (strcmp(s, "_resonance") == 0) lfo(x).resonance = check(key, 0.0f, 40.0f, ToFloat(value)); + else if (strcmp(s, "_pan") == 0) lfo(x).pan = check(key, -100.0f, 100.0f, ToFloat(value)); + else std::cerr << "The opcode '" << key << "' is unsupported by libsfz!" << std::endl; + } + // CCs else if (key.find("cc") != std::string::npos) { @@ -1246,8 +1423,10 @@ else if ("xfout_hi" == key_cc) pCurDef->xfout_hicc.set(num_cc, ToInt(value)); // filter - else if ("cutoff_on" == key_cc || "cutoff_" == key_cc) pCurDef->cutoff_oncc.set(num_cc, ToInt(value)); - else if ("cutoff2_on" == key_cc) pCurDef->cutoff2_oncc.set(num_cc, ToInt(value)); + else if ("cutoff_on" == key_cc || "cutoff_" == key_cc) { + pCurDef->cutoff_oncc.set(num_cc, ToInt(value)); + pCurDef->cutoff_cc = num_cc; + } else if ("cutoff2_on" == key_cc) pCurDef->cutoff2_oncc.set(num_cc, ToInt(value)); else if ("cutoff_smooth" == key_cc) pCurDef->cutoff_smoothcc.set(num_cc, ToInt(value)); else if ("cutoff2_smooth" == key_cc) pCurDef->cutoff2_smoothcc.set(num_cc, ToInt(value)); else if ("cutoff_step" == key_cc) pCurDef->cutoff_stepcc.set(num_cc, ToInt(value)); @@ -1273,59 +1452,23 @@ else if ("eq1_gain_on" == key_cc || "eq1_gain" == key_cc) pCurDef->eq1_gain_oncc.set(num_cc, ToInt(value)); else if ("eq2_gain_on" == key_cc || "eq2_gain" == key_cc) pCurDef->eq2_gain_oncc.set(num_cc, ToInt(value)); else if ("eq3_gain_on" == key_cc || "eq3_gain" == key_cc) pCurDef->eq3_gain_oncc.set(num_cc, ToInt(value)); + + else if ("ampeg_delay" == key_cc) pCurDef->ampeg_delaycc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + else if ("ampeg_start" == key_cc) pCurDef->ampeg_startcc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + else if ("ampeg_attack" == key_cc) pCurDef->ampeg_attackcc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + else if ("ampeg_hold" == key_cc) pCurDef->ampeg_holdcc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + else if ("ampeg_decay" == key_cc) pCurDef->ampeg_decaycc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + else if ("ampeg_sustain" == key_cc) pCurDef->ampeg_sustaincc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + else if ("ampeg_release" == key_cc) pCurDef->ampeg_releasecc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + + else if ("pitchlfo_depth" == key_cc) pCurDef->pitchlfo_depthcc.set(num_cc, ToInt(value)); + else if ("pitchlfo_freq" == key_cc) pCurDef->pitchlfo_freqcc.add( CC(num_cc, check(key, -200.0f, 200.0f, ToFloat(value))) ); + else if ("fillfo_freq" == key_cc) pCurDef->fillfo_freqcc.add( CC(num_cc, check(key, -200.0f, 200.0f, ToFloat(value))) ); + else if ("amplfo_freq" == key_cc) pCurDef->amplfo_freqcc.add( CC(num_cc, check(key, -200.0f, 200.0f, ToFloat(value))) ); + else if ("volume_on" == key_cc) pCurDef->volume_oncc.add( CC(num_cc, check(key, -100.0f, 100.0f, ToFloat(value))) ); + else if ("volume_curve" == key_cc) pCurDef->volume_curvecc.add( CC(num_cc, 0, check(key, 0, 30000, ToInt(value))) ); else std::cerr << "The opcode '" << key << "' is unsupported by libsfz!" << std::endl; } - // v2 envelope generators - else if (sscanf(key.c_str(), "eg%d%n", &x, &y)) { - const char* s = key.c_str() + y; - if (sscanf(s, "_time%d", &y)) egnode(x, y).time = ToFloat(value); - else if (sscanf(s, "_level%d", &y)) egnode(x, y).level = ToFloat(value); - else if (sscanf(s, "_shape%d", &y)) egnode(x, y).shape = ToFloat(value); - else if (sscanf(s, "_curve%d", &y)) egnode(x, y).curve = ToFloat(value); - else if (strcmp(s, "_sustain") == 0) eg(x).sustain = ToInt(value); - else if (strcmp(s, "_loop") == 0) eg(x).loop = ToInt(value); - else if (strcmp(s, "_loop_count") == 0) eg(x).loop_count = ToInt(value); - else if (strcmp(s, "_amplitude") == 0) eg(x).amplitude = ToFloat(value); - else if (strcmp(s, "_cutoff") == 0) eg(x).cutoff = ToFloat(value); - else std::cerr << "The opcode '" << key << "' is unsupported by libsfz!" << std::endl; - } - - // v1 envelope generators - else if ("ampeg_delay" == key) pCurDef->ampeg_delay = ToFloat(value); - else if ("ampeg_start" == key) pCurDef->ampeg_start = ToFloat(value); - else if ("ampeg_attack" == key) pCurDef->ampeg_attack = ToFloat(value); - else if ("ampeg_hold" == key) pCurDef->ampeg_hold = ToFloat(value); - else if ("ampeg_decay" == key) pCurDef->ampeg_decay = ToFloat(value); - else if ("ampeg_sustain" == key) pCurDef->ampeg_sustain = ToFloat(value); - else if ("ampeg_release" == key) pCurDef->ampeg_release = ToFloat(value); - else if ("fileg_delay" == key) pCurDef->fileg_delay = ToFloat(value); - else if ("fileg_start" == key) pCurDef->fileg_start = ToFloat(value); - else if ("fileg_attack" == key) pCurDef->fileg_attack = ToFloat(value); - else if ("fileg_hold" == key) pCurDef->fileg_hold = ToFloat(value); - else if ("fileg_decay" == key) pCurDef->fileg_decay = ToFloat(value); - else if ("fileg_sustain" == key) pCurDef->fileg_sustain = ToFloat(value); - else if ("fileg_release" == key) pCurDef->fileg_release = ToFloat(value); - else if ("pitcheg_delay" == key) pCurDef->pitcheg_delay = ToFloat(value); - else if ("pitcheg_start" == key) pCurDef->pitcheg_start = ToFloat(value); - else if ("pitcheg_attack" == key) pCurDef->pitcheg_attack = ToFloat(value); - else if ("pitcheg_hold" == key) pCurDef->pitcheg_hold = ToFloat(value); - else if ("pitcheg_decay" == key) pCurDef->pitcheg_decay = ToFloat(value); - else if ("pitcheg_sustain" == key) pCurDef->pitcheg_sustain = ToFloat(value); - else if ("pitcheg_release" == key) pCurDef->pitcheg_release = ToFloat(value); - - // v1 LFO - else if ("amplfo_delay" == key) pCurDef->amplfo_delay = ToFloat(value); - else if ("amplfo_fade" == key) pCurDef->amplfo_fade = ToFloat(value); - else if ("amplfo_freq" == key) pCurDef->amplfo_freq = ToFloat(value); - else if ("amplfo_depth" == key) pCurDef->amplfo_depth = ToFloat(value); - else if ("fillfo_delay" == key) pCurDef->fillfo_delay = ToFloat(value); - else if ("fillfo_fade" == key) pCurDef->fillfo_fade = ToFloat(value); - else if ("fillfo_freq" == key) pCurDef->fillfo_freq = ToFloat(value); - else if ("fillfo_depth" == key) pCurDef->fillfo_depth = ToFloat(value); - else if ("pitchlfo_delay" == key) pCurDef->pitchlfo_delay = ToFloat(value); - else if ("pitchlfo_fade" == key) pCurDef->pitchlfo_fade = ToFloat(value); - else if ("pitchlfo_freq" == key) pCurDef->pitchlfo_freq = ToFloat(value); - else if ("pitchlfo_depth" == key) pCurDef->pitchlfo_depth = ToInt(value); else { std::cerr << "The opcode '" << key << "' is unsupported by libsfz!" << std::endl; @@ -1346,6 +1489,7 @@ case 'g': i = 7; break; case 'a': i = 9; break; case 'b': i = 11; break; + case '-': if (s == "-1") return -1; default: std::cerr << "Not a note: " << s << std::endl; return 0; @@ -1369,11 +1513,52 @@ EGNode::EGNode() : time(0), level(0), shape(0), curve(0) { } + + void EGNode::Copy(const EGNode& egNode) { + time = egNode.time; + level = egNode.level; + shape = egNode.shape; + curve = egNode.curve; + + time_oncc = egNode.time_oncc; + level_oncc = egNode.level_oncc; + } EG::EG() : sustain(0), loop(0), loop_count(0), amplitude(0), cutoff(0) { } + + void EG::Copy(const EG& eg) { + sustain = eg.sustain; + loop = eg.loop; + loop_count = eg.loop_count; + amplitude = eg.amplitude; + cutoff = eg.cutoff; + node = eg.node; + } + + LFO::LFO(): freq (-1),/* -1 is used to determine whether the LFO was initialized */ + fade(0), phase(0), wave(0), delay(0), pitch(0), cutoff(0), resonance(0), pan(0) { + + } + + void LFO::Copy(const LFO& lfo) { + delay = lfo.delay; + freq = lfo.freq; + fade = lfo.fade; + phase = lfo.phase; + wave = lfo.wave; + volume = lfo.volume; + pitch = lfo.pitch; + cutoff = lfo.cutoff; + resonance = lfo.resonance; + pan = lfo.pan; + freq_oncc = lfo.freq_oncc; + fade_oncc = lfo.fade_oncc; + phase_oncc = lfo.phase_oncc; + pitch_oncc = lfo.pitch_oncc; + } EG& File::eg(int x) { while (pCurDef->eg.size() <= x) { @@ -1390,4 +1575,11 @@ return e.node[y]; } + LFO& File::lfo(int x) { + while (pCurDef->lfos.size() <= x) { + pCurDef->lfos.add(LFO()); + } + return pCurDef->lfos[x]; + } + } // !namespace sfz