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