Hi Christian and everyone

On 14/12/2018 12:08, Christian Schoenebeck wrote:
Well, that already a great step! Most of our valueable GSt infos we got were
by using the GSt instrument editor.

However when the limit is still 2000 then I don't understand why you got the
sample offset problem at first place. When you look into config.h you will see
that the default value for precaching samples is 32768, so even if you have
samples there with 2000 start offsest, you should not get into any problems.

I'd like to see how that Sonivox Synth instrument looks and behaves in GSEdit4 and GStudio4. That might provide some clues. I'll do that as soon as I can.

I would probably first implement it as optional dimension region option in
libgig and add a combo box for that in gigedit on dimension region level as
well.
Find attached a few patches with an implementation of this. The option can only be modified at instrument level in gigedit, but gets populated down into the DimensionRegions of the instrument during saving, where it is easily accessible by linuxsampler, and vice versa during loading. I didn't implement half-pedaling because I'm not sure that the release samples should be played until the sustain pedal is fully released, I can change this easily though. I also included some work towards supporting writing of multi-part gig files and a small fix in gigedit where a pointer to a std::vector on the stack was being used by a ChoiceEntry widget after it had gone out of scope. I will continue working on the multi-part gig file writing as I would really like to be able to edit my PMI pianos in gigedit.
Then later on one could still add a NKSP script function allowing to override
the behaviour by script. Keep in mind most users are no software developers.
So writing a NKSP script is often beyond their abilities.
You are right. The gigedit way is preferable.
CU
Christian

All the best,
Ivan

diff -Naur '--exclude=.svn' gigedit_original/src/gigedit/mainwindow.cpp gigedit_modified/src/gigedit/mainwindow.cpp
--- gigedit_original/src/gigedit/mainwindow.cpp	2018-12-17 17:07:19.361903251 +0000
+++ gigedit_modified/src/gigedit/mainwindow.cpp	2018-12-18 01:50:29.190785000 +0000
@@ -2802,17 +2802,18 @@
     const std::string sGiga = "Gigasampler/GigaStudio v";
     const int major = file->pVersion->major;
     std::vector<std::string> txts;
-    std::vector<int> values;
-    txts.push_back(sGiga + "2"); values.push_back(2);
-    txts.push_back(sGiga + "3"); values.push_back(3);
-    txts.push_back(sGiga + "4"); values.push_back(4);
+    // use a member variable for values instead of a variable on the stack
+    FileFormatChoiceValues.clear();
+    txts.push_back(sGiga + "2"); FileFormatChoiceValues.push_back(2);
+    txts.push_back(sGiga + "3"); FileFormatChoiceValues.push_back(3);
+    txts.push_back(sGiga + "4"); FileFormatChoiceValues.push_back(4);
     if (major < 2 || major > 4) {
-        txts.push_back(sGiga + ToString(major)); values.push_back(major);
+        txts.push_back(sGiga + ToString(major)); FileFormatChoiceValues.push_back(major);
     }
     std::vector<const char*> texts;
     for (int i = 0; i < txts.size(); ++i) texts.push_back(txts[i].c_str());
-    texts.push_back(NULL); values.push_back(0);
-    eFileFormat.set_choices(&texts[0], &values[0]);
+    texts.push_back(NULL); FileFormatChoiceValues.push_back(0);
+    eFileFormat.set_choices(&texts[0], &FileFormatChoiceValues[0]);
     eFileFormat.set_value(major);
 }
 
@@ -2872,7 +2873,8 @@
     ePitchbendRange(_("Pitchbend range"), 0, 48),
     ePianoReleaseMode(_("Piano release mode")),
     eDimensionKeyRangeLow(_("Keyswitching range low")),
-    eDimensionKeyRangeHigh(_("Keyswitching range high"))
+    eDimensionKeyRangeHigh(_("Keyswitching range high")),
+    eSustainUpRelease(_("Sustain up release samples"))
 {
     if (!Settings::singleton()->autoRestoreWindowDimension) {
         //set_default_size(470, 390);
@@ -2889,6 +2891,12 @@
         _("end of the keyboard area which should switch the "
           "\"keyswitching\" dimension")
     );
+    eSustainUpRelease.set_tip(
+        _("change whether and how release samples are played when "
+          "the sustain pedal is released")
+    );
+
+    populateSustainUpReleaseCombo();
 
     connect(eName, &InstrumentProps::set_Name);
     connect(eIsDrum, &InstrumentProps::set_IsDrum);
@@ -2902,9 +2910,11 @@
     connect(ePianoReleaseMode, &gig::Instrument::PianoReleaseMode);
     connect(eDimensionKeyRangeLow, eDimensionKeyRangeHigh,
             &gig::Instrument::DimensionKeyRange);
-
+		
     eName.signal_value_changed().connect(sig_name_changed.make_slot());
-
+    eSustainUpRelease.signal_value_changed().connect(
+            sigc::mem_fun(*this, &InstrumentProps::onSustainUpReleaseChanged));
+		
 #if USE_GTKMM_GRID
     table.set_column_spacing(5);
 #else
@@ -2923,6 +2933,7 @@
     table.add(ePianoReleaseMode);
     table.add(eDimensionKeyRangeLow);
     table.add(eDimensionKeyRangeHigh);
+    table.add(eSustainUpRelease);
 
     add(vbox);
 #if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22)
@@ -2963,9 +2974,35 @@
     eIsDrum.set_value(instrument->IsDrum);
     eMIDIBank.set_value(instrument->MIDIBank);
     eMIDIProgram.set_value(instrument->MIDIProgram);
+    updateSustainUpReleaseCombo(instrument);
     update_model--;
 }
 
+void InstrumentProps::populateSustainUpReleaseCombo()
+{
+    // populate the Sustain Up Release combo-box with text options
+    std::vector<const char*> texts;
+    // use a member variable for values instead of a variable on the stack
+    SustainUpReleaseChoiceValues.clear();
+    texts.clear();
+    texts.push_back("None played");
+    texts.push_back("Played with maximum velocity");
+    texts.push_back("Played with last Note-on velocity");
+    for (int i = 0; i < texts.size(); ++i) SustainUpReleaseChoiceValues.push_back(i);
+    texts.push_back(NULL); SustainUpReleaseChoiceValues.push_back(0xFF);
+    eSustainUpRelease.set_choices(&texts[0], &SustainUpReleaseChoiceValues[0]);
+}
+
+void InstrumentProps::onSustainUpReleaseChanged()
+{
+    gig::Instrument* instrument = get_instrument();
+    instrument->SustainUpReleaseOption = static_cast<gig::sustup_relsmp_t>(eSustainUpRelease.get_value());
+}
+
+void InstrumentProps::updateSustainUpReleaseCombo(gig::Instrument* instrument)
+{
+    eSustainUpRelease.set_value(instrument->SustainUpReleaseOption);
+}
 
 void MainWindow::file_changed()
 {
diff -Naur '--exclude=.svn' gigedit_original/src/gigedit/mainwindow.h gigedit_modified/src/gigedit/mainwindow.h
--- gigedit_original/src/gigedit/mainwindow.h	2018-12-17 17:07:19.373903201 +0000
+++ gigedit_modified/src/gigedit/mainwindow.h	2018-12-17 19:27:52.503196000 +0000
@@ -103,6 +103,7 @@
     Table table;
 
     gig::File* m_file;
+    std::vector<int> FileFormatChoiceValues;
 
     void onFileFormatChanged();
 };
@@ -129,7 +130,11 @@
     void set_IsDrum(bool value);
     void set_MIDIBank(uint16_t value);
     void set_MIDIProgram(uint32_t value);
-
+							
+    void onSustainUpReleaseChanged();
+    void populateSustainUpReleaseCombo();
+    void updateSustainUpReleaseCombo(gig::Instrument* instrument);
+							
     sigc::signal<void> sig_name_changed;
     VBox vbox;
     HButtonBox buttonBox;
@@ -147,6 +152,8 @@
     BoolEntry ePianoReleaseMode;
     NoteEntry eDimensionKeyRangeLow;
     NoteEntry eDimensionKeyRangeHigh;
+    ChoiceEntry<int> eSustainUpRelease;
+    std::vector<int> SustainUpReleaseChoiceValues;
 };
 
 class ProgressDialog : public Gtk::Dialog {
diff -Naur '--exclude=.svn' libgig_original/src/DLS.cpp libgig_modified/src/DLS.cpp
--- libgig_original/src/DLS.cpp	2018-12-17 19:34:26.645343163 +0000
+++ libgig_modified/src/DLS.cpp	2018-12-18 02:20:53.543794000 +0000
@@ -1801,21 +1801,54 @@
      * @param pProgress - optional: callback function for progress notification
      */
     void File::Save(const String& Path, progress_t* pProgress) {
+        // calculate number of tasks to display progress of
+        float tasks = 2.f + ExtensionFiles.size();
+        // get the destination folder name
+        std::string path = Path;
+        const std::string sep = 
+#ifdef _WIN32
+            "\\";
+#else
+            "/";
+#endif
+        std::string folder = "";
+        std::string newfile = path;
+        size_t pos = path.find_last_of(sep);
+        if (pos != std::string::npos) {
+            folder = path.substr(0, pos + 1);
+            newfile = path.substr(pos+1, path.length() - pos - 5);
+        }
         {
             // divide local progress into subprogress
             progress_t subprogress;
-            __divide_progress(pProgress, &subprogress, 2.f, 0.f); // arbitrarily subdivided into 50% of total progress
+            __divide_progress(pProgress, &subprogress, tasks, 0.f); // subdivided evenly
             // do the actual work
             UpdateChunks(&subprogress);
-            
+
         }
         {
             // divide local progress into subprogress
             progress_t subprogress;
-            __divide_progress(pProgress, &subprogress, 2.f, 1.f); // arbitrarily subdivided into 50% of total progress
+            __divide_progress(pProgress, &subprogress, tasks, 1.f); // subdivided evenly
             // do the actual work
             pRIFF->Save(Path, &subprogress);
         }
+        int extcount = 0;
+        std::list<RIFF::File*>::iterator it = ExtensionFiles.begin();
+        while (it != ExtensionFiles.end())
+        {
+            // divide local progress into subprogress
+            progress_t subprogress;
+            __divide_progress(pProgress, &subprogress, tasks, 2.0f + extcount); // subdivided evenly            // do the actual work
+            // save each extension file to its new location
+            std::string old = (*it)->GetFileName();
+            std::string ext = old.substr(old.length()-5, 5);
+            std::string newpath = folder + newfile + ext;
+            (*it)->Save(newpath, &subprogress);
+            it++;
+            extcount++;
+        }
+
         UpdateFileOffsets();
         __notify_progress(pProgress, 1.0); // notify done
     }
diff -Naur '--exclude=.svn' libgig_original/src/gig.cpp libgig_modified/src/gig.cpp
--- libgig_original/src/gig.cpp	2018-12-17 19:34:26.641343145 +0000
+++ libgig_modified/src/gig.cpp	2018-12-18 02:28:56.135237000 +0000
@@ -1747,6 +1747,16 @@
             }
         }
 
+        // format extension for sustain pedal up release samples behavior
+        // these options have no effect in Gigasampler/GigaStudio!
+        RIFF::Chunk* susr = _3ewl->GetSubChunk(CHUNK_ID_SUSR);
+        if (susr) {
+            SustainUpReleaseOption = static_cast<sustup_relsmp_t>(susr->ReadUint8());
+        }
+        else {
+            SustainUpReleaseOption = sustup_relsmp_t::sustup_relsmp_none;
+        }
+            
         pVelocityAttenuationTable = GetVelocityTable(VelocityResponseCurve,
                                                      VelocityResponseDepth,
                                                      VelocityResponseCurveScaling);
@@ -2270,6 +2280,24 @@
                     (pEGOpts[i]->ReleaseCancel    ? (1<<4) : 0);
             }
         }
+        
+        // format extension for sustain pedal up release samples behavior
+        // these options have no effect in Gigasampler/GigaStudio!
+        RIFF::Chunk* susr = pParentList->GetSubChunk(CHUNK_ID_SUSR);
+        if (!susr) {
+            // only add this "SUSR" chunk if the sustain up release samples option 
+            // does not match the default behavior
+            if (SustainUpReleaseOption != sustup_relsmp_t::sustup_relsmp_none)
+            {
+                susr = pParentList->AddSubChunk(CHUNK_ID_SUSR, 1);
+                // move SUSR chunk to the end of parent list
+                pParentList->MoveSubChunk(susr, (RIFF::Chunk*)NULL);
+            }
+        }
+        if (susr) {
+            unsigned char* pData = (unsigned char*) susr->LoadChunkData();
+            pData[0] = static_cast<uint8_t>(SustainUpReleaseOption);
+        }
     }
 
     double* DimensionRegion::GetReleaseVelocityTable(curve_type_t releaseVelocityResponseCurve, uint8_t releaseVelocityResponseDepth) {
@@ -4760,7 +4788,7 @@
                 }
             }
         }
-
+        GetSustainUpReleaseOption();
         __notify_progress(pProgress, 1.0f); // notify done
     }
 
@@ -5358,6 +5386,51 @@
         UpdateRegionKeyTable();
     }
 
+    /**
+     * Get the SustainUpReleaseOption member value from the first 
+     * DimensionRegion and assign it to the instrument's SustainUpReleaseOption
+     * member.
+     *
+     */
+    void Instrument::GetSustainUpReleaseOption() {
+        SustainUpReleaseOption = sustup_relsmp_t::sustup_relsmp_none;
+        bool have_value = false;
+        for (gig::Region* rgn = GetFirstRegion(); rgn;
+            rgn = GetNextRegion()) {
+            if (rgn) {
+                for (int i = 0; i < 256; ++i) {
+                    gig::DimensionRegion* dim = rgn->pDimensionRegions[i];
+                    if (!dim)
+                        continue;
+                    else {
+                        SustainUpReleaseOption = dim->SustainUpReleaseOption;
+                        have_value = true;
+                        break;
+                    }
+                }
+            }
+            if (have_value)
+                break;
+        }
+    }
+
+    /**
+     * Assign the Instrument::SustainUpReleaseOption value to the
+     * SustainUpReleaseOption of all the instrument's DimensionRegions.
+     *
+     */
+    void Instrument::SetSustainUpReleaseOption() {
+        for (gig::Region* rgn = GetFirstRegion(); rgn;
+            rgn = GetNextRegion()) {
+                for (int i = 0; i < 256; ++i) {
+                    gig::DimensionRegion* dim = rgn->pDimensionRegions[i];
+                if (!dim)
+                    continue;
+                else
+                    dim->SustainUpReleaseOption = SustainUpReleaseOption;
+            }
+        }
+    }
 
 // *************** Group ***************
 // *
@@ -5716,7 +5789,19 @@
                     wave = wvpl->GetNextSubList();
                 }
 
-                if (fileNo == lastFileNo) break;
+                if (fileNo == lastFileNo) {
+                    // add the .gx99 extension file if present
+                    // this can be present even with a single .gig file
+                    sprintf(suffix, ".gx%02d", 99);
+                    name.replace(nameLen, 5, suffix);
+                    try {
+                        file = new RIFF::File(name);
+                        ExtensionFiles.push_back(file);
+                    }
+                    catch(...) {
+                    }
+                    break;
+                }
 
                 // open extension file (*.gx01, *.gx02, ...)
                 fileNo++;
@@ -6341,6 +6426,10 @@
      * @throws Exception - on errors
      */
     void File::UpdateChunks(progress_t* pProgress) {
+        for (Instrument* instrument = GetFirstInstrument() ; instrument ;
+                 instrument = GetNextInstrument()) {
+            instrument->SetSustainUpReleaseOption();
+        }
         bool newFile = pRIFF->GetSubList(LIST_TYPE_INFO) == NULL;
 
         // update own gig format extension chunks
diff -Naur '--exclude=.svn' libgig_original/src/gig.h libgig_modified/src/gig.h
--- libgig_original/src/gig.h	2018-12-17 19:34:26.645343163 +0000
+++ libgig_modified/src/gig.h	2018-12-18 02:30:41.791991000 +0000
@@ -62,6 +62,7 @@
 # define CHUNK_ID_LSNM  0x4c534e4d // own gig format extension
 # define CHUNK_ID_SCSL  0x5343534c // own gig format extension
 # define CHUNK_ID_LSDE  0x4c534445 // own gig format extension
+# define CHUNK_ID_SUSR  0x43608e43 // own gig format extension
 #else  // little endian
 # define LIST_TYPE_3PRG	0x67727033
 # define LIST_TYPE_3EWL	0x6C776533
@@ -81,6 +82,7 @@
 # define CHUNK_ID_LSNM  0x4d4e534c // own gig format extension
 # define CHUNK_ID_SCSL  0x4c534353 // own gig format extension
 # define CHUNK_ID_LSDE  0x4544534c // own gig format extension
+# define CHUNK_ID_SUSR  0x438e6043 // own gig format extension
 #endif // WORDS_BIGENDIAN
 
 #ifndef GIG_DECLARE_ENUM
@@ -414,6 +416,16 @@
         void serialize(Serialization::Archive* archive);
     };
 
+    /** Defines options for sustain up release sample behavior.
+     *
+     * @see enumCount(), enumKey(), enumKeys(), enumValue()
+     */
+    GIG_DECLARE_ENUM(sustup_relsmp_t,
+        sustup_relsmp_none     = 0x00, /**< Default. No release samples played on sustain up. */
+        sustup_relsmp_max_vel  = 0x01, /**< Release samples played at velocity 127 on sustain up. */
+        sustup_relsmp_last_vel = 0x02, /**< Release samples played at velocity of last note-on on sustain up. */
+    );
+
     // just symbol prototyping
     class File;
     class Instrument;
@@ -555,6 +567,8 @@
             eg_opt_t           EG1Options;                    ///< [gig extension]: Behavior options which should be used for envelope generator 1 (volume amplitude EG).
             eg_opt_t           EG2Options;                    ///< [gig extension]: Behavior options which should be used for envelope generator 2 (filter cutoff EG).
 
+            sustup_relsmp_t    SustainUpReleaseOption;        ///< [gig extension]: Behavior option to control sustain up release samples
+
             // derived attributes from DLS::Sampler
             using DLS::Sampler::UnityNote;
             using DLS::Sampler::FineTune;
@@ -1200,6 +1214,10 @@
             bool      PianoReleaseMode;
             range_t   DimensionKeyRange; ///< 0-127 (where 0 means C1 and 127 means G9)
 
+            // SustainUpReleaseOption indicates how to handle release samples on sustain pedal up 
+            // read from one of the instrument's DimensionRegions in Instrument::Instrument().
+            // stored in all of the instrument's DimensionRegions in File::UpdateChunks().
+            sustup_relsmp_t   SustainUpReleaseOption; 
 
             // derived methods from DLS::Resource
             using DLS::Resource::GetParent;
@@ -1236,6 +1254,8 @@
             void UpdateRegionKeyTable();
             void LoadScripts();
             void UpdateScriptFileOffsets();
+            void GetSustainUpReleaseOption();
+            void SetSustainUpReleaseOption();
             friend class File;
             friend class Region; // so Region can call UpdateRegionKeyTable()
         private:
diff -Naur '--exclude=.svn' linuxsampler_original/src/engines/common/MidiKeyboardManager.h linuxsampler_modified/src/engines/common/MidiKeyboardManager.h
--- linuxsampler_original/src/engines/common/MidiKeyboardManager.h	2018-12-17 20:19:46.567208572 +0000
+++ linuxsampler_modified/src/engines/common/MidiKeyboardManager.h	2018-12-18 01:22:45.719134000 +0000
@@ -31,6 +31,7 @@
 #include "../../common/Pool.h"
 #include "../../common/global_private.h"
 #include "Note.h"
+#include "libgig/gig.h"
 
 namespace LinuxSampler {
 
@@ -692,23 +693,51 @@
             void ProcessSustainPedalUp(Pool<Event>::Iterator& itEvent) {
                 // release voices if their respective key is not pressed
                 RTList<uint>::Iterator iuiKey = pActiveKeys->first();
+                AbstractEngineChannel* pChannel = (AbstractEngineChannel*) itEvent->pEngineChannel;
+                AbstractEngine* pEngine = (AbstractEngine*)pChannel->GetEngine();
+                bool isGigEngine = pEngine->GetEngineFormat() == AbstractEngine::Format::GIG;
                 for (; iuiKey; ++iuiKey) {
                     MidiKey* pKey = &pMIDIKeyInfo[*iuiKey];
                     if (!pKey->KeyPressed && ShouldReleaseVoice(*iuiKey)) {
+                        bool triggerReleaseSamples = true;
+                        int releaseSamplesVelocity = 127;
+                        if (isGigEngine) {
+                            // gig engine, try to get sustain up release option from DimensionRegion
+                            RTListNoteIterator itNote = pKey->pActiveNotes->first();
+                            if (itNote != pKey->pActiveNotes->end()) {
+                                RTListVoiceIterator itVoice = itNote->pActiveVoices->first();
+                                if (itVoice != itNote->pActiveVoices->end()) {
+                                    ::gig::DimensionRegion* pRegion = (::gig::DimensionRegion*)itVoice->GetRegion();
+                                    if (pRegion) {
+                                        switch(pRegion->SustainUpReleaseOption) {
+                                            case ::gig::sustup_relsmp_t::sustup_relsmp_none:
+                                                triggerReleaseSamples = false;
+                                                break;
+                                            case ::gig::sustup_relsmp_t::sustup_relsmp_max_vel:
+                                                // change nothing
+                                                break;
+                                            case ::gig::sustup_relsmp_t::sustup_relsmp_last_vel:
+                                                releaseSamplesVelocity = pKey->Velocity;
+                                                break;
+                                        }
+                                    }
+                                }
+                            }
+                        }
                         RTList<Event>::Iterator itNewEvent = pKey->pEvents->allocAppend();
                         if (itNewEvent) {
                             *itNewEvent = *itEvent; // copy event to the key's own event list
                             itNewEvent->Type = Event::type_release_key; // transform event type
                             itNewEvent->Param.Note.Key = *iuiKey;
-                            itNewEvent->Param.Note.Velocity = 127;
+                            itNewEvent->Param.Note.Velocity = releaseSamplesVelocity;
                             if (!SostenutoActiveOnKey(*iuiKey)) {
                                 //HACK: set sustain CC (64) as "pressed down" for a short moment, so that release trigger voices can distinguish between note off and sustain pedal up cases
-                                AbstractEngineChannel* pChannel = (AbstractEngineChannel*) itEvent->pEngineChannel;
                                 const int8_t CC64Value = pChannel->ControllerTable[64];
                                 pChannel->ControllerTable[64] = 127;
 
                                 // now spawn release trigger voices (if required)
-                                ProcessReleaseTrigger(itNewEvent);
+                                if (triggerReleaseSamples)
+                                    ProcessReleaseTrigger(itNewEvent);
 
                                 //HACK: reset sustain pedal CC value to old one (see comment above)
                                 pChannel->ControllerTable[64] = CC64Value;
_______________________________________________
Linuxsampler-devel mailing list
Linuxsampler-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxsampler-devel

Reply via email to