/[svn]/libgig/trunk/src/gig.cpp
ViewVC logotype

Diff of /libgig/trunk/src/gig.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3709 by schoenebeck, Fri Jan 10 11:21:59 2020 UTC revision 3904 by schoenebeck, Wed May 12 18:55:31 2021 UTC
# Line 2  Line 2 
2   *                                                                         *   *                                                                         *
3   *   libgig - C++ cross-platform Gigasampler format file access library    *   *   libgig - C++ cross-platform Gigasampler format file access library    *
4   *                                                                         *   *                                                                         *
5   *   Copyright (C) 2003-2019 by Christian Schoenebeck                      *   *   Copyright (C) 2003-2021 by Christian Schoenebeck                      *
6   *                              <cuse@users.sourceforge.net>               *   *                              <cuse@users.sourceforge.net>               *
7   *                                                                         *   *                                                                         *
8   *   This library is free software; you can redistribute it and/or modify  *   *   This library is free software; you can redistribute it and/or modify  *
# Line 3460  namespace { Line 3460  namespace {
3460              store32(&pData[iWavePoolOffset + i * 4], iWaveIndex);              store32(&pData[iWavePoolOffset + i * 4], iWaveIndex);
3461          }          }
3462    
3463          if (versiongt2) {          // The following chunks are just added for compatibility with the
3464            // GigaStudio software, which would show a warning if these were
3465            // missing. However currently these chunks don't cover any useful
3466            // data. So if this gig file uses any of our own gig format
3467            // extensions which would cause this gig file to be unloadable
3468            // with GSt software anyway, then just skip these GSt compatibility
3469            // chunks here as well.
3470            if (versiongt2 && !UsesAnyGigFormatExtension()) {
3471              // add 3dnm list which always seems to be empty              // add 3dnm list which always seems to be empty
3472              RIFF::List* _3dnm = pCkRegion->GetSubList(LIST_TYPE_3DNM);              RIFF::List* _3dnm = pCkRegion->GetSubList(LIST_TYPE_3DNM);
3473              if (!_3dnm) _3dnm = pCkRegion->AddSubList(LIST_TYPE_3DNM);              if (!_3dnm) _3dnm = pCkRegion->AddSubList(LIST_TYPE_3DNM);
# Line 3800  namespace { Line 3807  namespace {
3807       * @throws gig::Exception if requested zone could not be deleted       * @throws gig::Exception if requested zone could not be deleted
3808       */       */
3809      void Region::DeleteDimensionZone(dimension_t type, int zone) {      void Region::DeleteDimensionZone(dimension_t type, int zone) {
3810            if (!Dimensions)
3811                throw gig::Exception("Could not delete dimension zone, because there is no dimension at all.");
3812          dimension_def_t* oldDef = GetDimensionDefinition(type);          dimension_def_t* oldDef = GetDimensionDefinition(type);
3813          if (!oldDef)          if (!oldDef)
3814              throw gig::Exception("Could not delete dimension zone, no such dimension of given type");              throw gig::Exception("Could not delete dimension zone, no such dimension of given type");
# Line 3828  namespace { Line 3837  namespace {
3837          // requested by the arguments of this method call) to the temporary          // requested by the arguments of this method call) to the temporary
3838          // region, and don't use Region::CopyAssign() here for this task, since          // region, and don't use Region::CopyAssign() here for this task, since
3839          // it would also alter fast lookup helper variables here and there          // it would also alter fast lookup helper variables here and there
3840          dimension_def_t newDef;          dimension_def_t newDef = {};
3841          for (int i = 0; i < Dimensions; ++i) {          for (int i = 0; i < Dimensions; ++i) {
3842              dimension_def_t def = pDimensionDefinitions[i]; // copy, don't reference              dimension_def_t def = pDimensionDefinitions[i]; // copy, don't reference
3843              // is this the dimension requested by the method arguments? ...              // is this the dimension requested by the method arguments? ...
# Line 3839  namespace { Line 3848  namespace {
3848              }              }
3849              tempRgn->AddDimension(&def);              tempRgn->AddDimension(&def);
3850          }          }
3851            // silence clang sanitizer warning
3852            if (newDef.dimension == dimension_none)
3853                throw gig::Exception("Unexpected internal failure resolving dimension in DeleteDimensionZone() [this is a bug].");
3854    
3855          // find the dimension index in the tempRegion which is the dimension          // find the dimension index in the tempRegion which is the dimension
3856          // type passed to this method (paranoidly expecting different order)          // type passed to this method (paranoidly expecting different order)
# Line 3883  namespace { Line 3895  namespace {
3895          // now tempRegion's dimensions and DimensionRegions basically reflect          // now tempRegion's dimensions and DimensionRegions basically reflect
3896          // what we wanted to get for this actual Region here, so we now just          // what we wanted to get for this actual Region here, so we now just
3897          // delete and recreate the dimension in question with the new amount          // delete and recreate the dimension in question with the new amount
3898          // zones and then copy back from tempRegion                // zones and then copy back from tempRegion. we're actually deleting and
3899          DeleteDimension(oldDef);          // recreating all dimensions here, to avoid altering the precise order
3900          AddDimension(&newDef);          // of the dimensions (which would not be an error per se, but it would
3901            // cause usability issues with instrument editors)
3902            {
3903                std::vector<dimension_def_t> oldDefs;
3904                for (int i = 0; i < Dimensions; ++i)
3905                    oldDefs.push_back(pDimensionDefinitions[i]); // copy, don't reference
3906                for (int i = Dimensions - 1; i >= 0; --i)
3907                    DeleteDimension(&pDimensionDefinitions[i]);
3908                for (int i = 0; i < oldDefs.size(); ++i) {
3909                    dimension_def_t& def = oldDefs[i];
3910                    AddDimension(
3911                        (def.dimension == newDef.dimension) ? &newDef : &def
3912                    );
3913                }
3914            }
3915          for (int iSrc = 0; iSrc < 256; ++iSrc) {          for (int iSrc = 0; iSrc < 256; ++iSrc) {
3916              DimensionRegion* srcDimRgn = tempRgn->pDimensionRegions[iSrc];              DimensionRegion* srcDimRgn = tempRgn->pDimensionRegions[iSrc];
3917              if (!srcDimRgn) continue;              if (!srcDimRgn) continue;
# Line 3925  namespace { Line 3951  namespace {
3951       * @throws gig::Exception if requested zone could not be splitted       * @throws gig::Exception if requested zone could not be splitted
3952       */       */
3953      void Region::SplitDimensionZone(dimension_t type, int zone) {      void Region::SplitDimensionZone(dimension_t type, int zone) {
3954            if (!Dimensions)
3955                throw gig::Exception("Could not split dimension zone, because there is no dimension at all.");
3956          dimension_def_t* oldDef = GetDimensionDefinition(type);          dimension_def_t* oldDef = GetDimensionDefinition(type);
3957          if (!oldDef)          if (!oldDef)
3958              throw gig::Exception("Could not split dimension zone, no such dimension of given type");              throw gig::Exception("Could not split dimension zone, no such dimension of given type");
# Line 3951  namespace { Line 3979  namespace {
3979          // requested by the arguments of this method call) to the temporary          // requested by the arguments of this method call) to the temporary
3980          // region, and don't use Region::CopyAssign() here for this task, since          // region, and don't use Region::CopyAssign() here for this task, since
3981          // it would also alter fast lookup helper variables here and there          // it would also alter fast lookup helper variables here and there
3982          dimension_def_t newDef;          dimension_def_t newDef = {};
3983          for (int i = 0; i < Dimensions; ++i) {          for (int i = 0; i < Dimensions; ++i) {
3984              dimension_def_t def = pDimensionDefinitions[i]; // copy, don't reference              dimension_def_t def = pDimensionDefinitions[i]; // copy, don't reference
3985              // is this the dimension requested by the method arguments? ...              // is this the dimension requested by the method arguments? ...
# Line 3962  namespace { Line 3990  namespace {
3990              }              }
3991              tempRgn->AddDimension(&def);              tempRgn->AddDimension(&def);
3992          }          }
3993            // silence clang sanitizer warning
3994            if (newDef.dimension == dimension_none)
3995                throw gig::Exception("Unexpected internal failure resolving dimension in SplitDimensionZone() [this is a bug].");
3996    
3997          // find the dimension index in the tempRegion which is the dimension          // find the dimension index in the tempRegion which is the dimension
3998          // type passed to this method (paranoidly expecting different order)          // type passed to this method (paranoidly expecting different order)
# Line 4025  namespace { Line 4056  namespace {
4056          // delete and recreate the dimension in question with the new amount          // delete and recreate the dimension in question with the new amount
4057          // zones and then copy back from tempRegion. we're actually deleting and          // zones and then copy back from tempRegion. we're actually deleting and
4058          // recreating all dimensions here, to avoid altering the precise order          // recreating all dimensions here, to avoid altering the precise order
4059          // of the dimensions (which would not be an error per so, but it would          // of the dimensions (which would not be an error per se, but it would
4060          // cause usability issues with instrument editors)          // cause usability issues with instrument editors)
4061          {          {
4062              std::vector<dimension_def_t> oldDefs;              std::vector<dimension_def_t> oldDefs;
# Line 4363  namespace { Line 4394  namespace {
4394          Layers = orig->Layers;          Layers = orig->Layers;
4395      }      }
4396    
4397        /**
4398         * Returns @c true in case this Region object uses any gig format
4399         * extension, that is e.g. whether any DimensionRegion object currently
4400         * has any setting effective that would require our "LSDE" RIFF chunk to
4401         * be stored to the gig file.
4402         *
4403         * Right now this is a private method. It is considerable though this method
4404         * to become (in slightly modified form) a public API method in future, i.e.
4405         * to allow instrument editors to visualize and/or warn the user of any gig
4406         * format extension being used. See also comments on
4407         * DimensionRegion::UsesAnyGigFormatExtension() for details about such a
4408         * potential public API change in future.
4409         */
4410        bool Region::UsesAnyGigFormatExtension() const {
4411            for (int i = 0; i < 256; i++) {
4412                if (pDimensionRegions[i]) {
4413                    if (pDimensionRegions[i]->UsesAnyGigFormatExtension())
4414                        return true;
4415                }
4416            }
4417            return false;
4418        }
4419    
4420    
4421  // *************** MidiRule ***************  // *************** MidiRule ***************
4422  // *  // *
# Line 4544  namespace { Line 4598  namespace {
4598              Compression = (Compression_t) ckScri->ReadUint32();              Compression = (Compression_t) ckScri->ReadUint32();
4599              Encoding    = (Encoding_t) ckScri->ReadUint32();              Encoding    = (Encoding_t) ckScri->ReadUint32();
4600              Language    = (Language_t) ckScri->ReadUint32();              Language    = (Language_t) ckScri->ReadUint32();
4601              Bypass      = (Language_t) ckScri->ReadUint32() & 1;              Bypass      = ckScri->ReadUint32() & 1;
4602              crc         = ckScri->ReadUint32();              crc         = ckScri->ReadUint32();
4603              uint32_t nameSize = ckScri->ReadUint32();              uint32_t nameSize = ckScri->ReadUint32();
4604              Name.resize(nameSize, ' ');              Name.resize(nameSize, ' ');
4605              for (int i = 0; i < nameSize; ++i)              for (int i = 0; i < nameSize; ++i)
4606                  Name[i] = ckScri->ReadUint8();                  Name[i] = ckScri->ReadUint8();
4607                // check if an uuid was already stored along with this script
4608                if (headerSize >= 6*sizeof(int32_t) + nameSize + 16) { // yes ...
4609                    for (uint i = 0; i < 16; ++i) {
4610                        Uuid[i] = ckScri->ReadUint8();
4611                    }
4612                } else { // no uuid yet, generate one now ...
4613                    GenerateUuid();
4614                }
4615              // to handle potential future extensions of the header              // to handle potential future extensions of the header
4616              ckScri->SetPos(sizeof(int32_t) + headerSize);              ckScri->SetPos(sizeof(int32_t) + headerSize);
4617              // read actual script data              // read actual script data
# Line 4564  namespace { Line 4626  namespace {
4626              Bypass   = false;              Bypass   = false;
4627              crc      = 0;              crc      = 0;
4628              Name     = "Unnamed Script";              Name     = "Unnamed Script";
4629                GenerateUuid();
4630          }          }
4631      }      }
4632    
# Line 4618  namespace { Line 4681  namespace {
4681          __calculateCRC(&data[0], data.size(), crc);          __calculateCRC(&data[0], data.size(), crc);
4682          __finalizeCRC(crc);          __finalizeCRC(crc);
4683          // make sure chunk exists and has the required size          // make sure chunk exists and has the required size
4684          const file_offset_t chunkSize = (file_offset_t) 7*sizeof(int32_t) + Name.size() + data.size();          const file_offset_t chunkSize =
4685                (file_offset_t) 7*sizeof(int32_t) + Name.size() + 16 + data.size();
4686          if (!pChunk) pChunk = pGroup->pList->AddSubChunk(CHUNK_ID_SCRI, chunkSize);          if (!pChunk) pChunk = pGroup->pList->AddSubChunk(CHUNK_ID_SCRI, chunkSize);
4687          else pChunk->Resize(chunkSize);          else pChunk->Resize(chunkSize);
4688          // fill the chunk data to be written to disk          // fill the chunk data to be written to disk
4689          uint8_t* pData = (uint8_t*) pChunk->LoadChunkData();          uint8_t* pData = (uint8_t*) pChunk->LoadChunkData();
4690          int pos = 0;          int pos = 0;
4691          store32(&pData[pos], uint32_t(6*sizeof(int32_t) + Name.size())); // total header size          store32(&pData[pos], uint32_t(6*sizeof(int32_t) + Name.size() + 16)); // total header size
4692          pos += sizeof(int32_t);          pos += sizeof(int32_t);
4693          store32(&pData[pos], Compression);          store32(&pData[pos], Compression);
4694          pos += sizeof(int32_t);          pos += sizeof(int32_t);
# Line 4640  namespace { Line 4704  namespace {
4704          pos += sizeof(int32_t);          pos += sizeof(int32_t);
4705          for (int i = 0; i < Name.size(); ++i, ++pos)          for (int i = 0; i < Name.size(); ++i, ++pos)
4706              pData[pos] = Name[i];              pData[pos] = Name[i];
4707            for (int i = 0; i < 16; ++i, ++pos)
4708                pData[pos] = Uuid[i];
4709          for (int i = 0; i < data.size(); ++i, ++pos)          for (int i = 0; i < data.size(); ++i, ++pos)
4710              pData[pos] = data[i];              pData[pos] = data[i];
4711      }      }
4712    
4713      /**      /**
4714         * Generate a new Universally Unique Identifier (UUID) for this script.
4715         */
4716        void Script::GenerateUuid() {
4717            DLS::dlsid_t dlsid;
4718            DLS::Resource::GenerateDLSID(&dlsid);
4719            Uuid[0]  = dlsid.ulData1       & 0xff;
4720            Uuid[1]  = dlsid.ulData1 >>  8 & 0xff;
4721            Uuid[2]  = dlsid.ulData1 >> 16 & 0xff;
4722            Uuid[3]  = dlsid.ulData1 >> 24 & 0xff;
4723            Uuid[4]  = dlsid.usData2       & 0xff;
4724            Uuid[5]  = dlsid.usData2 >>  8 & 0xff;
4725            Uuid[6]  = dlsid.usData3       & 0xff;
4726            Uuid[7]  = dlsid.usData3 >>  8 & 0xff;
4727            Uuid[8]  = dlsid.abData[0];
4728            Uuid[9]  = dlsid.abData[1];
4729            Uuid[10] = dlsid.abData[2];
4730            Uuid[11] = dlsid.abData[3];
4731            Uuid[12] = dlsid.abData[4];
4732            Uuid[13] = dlsid.abData[5];
4733            Uuid[14] = dlsid.abData[6];
4734            Uuid[15] = dlsid.abData[7];
4735        }
4736    
4737        /**
4738       * Move this script from its current ScriptGroup to another ScriptGroup       * Move this script from its current ScriptGroup to another ScriptGroup
4739       * given by @a pGroup.       * given by @a pGroup.
4740       *       *
# Line 4914  namespace { Line 5004  namespace {
5004          // own gig format extensions          // own gig format extensions
5005          RIFF::List* lst3LS = insList->GetSubList(LIST_TYPE_3LS);          RIFF::List* lst3LS = insList->GetSubList(LIST_TYPE_3LS);
5006          if (lst3LS) {          if (lst3LS) {
5007                // script slots (that is references to instrument scripts)
5008              RIFF::Chunk* ckSCSL = lst3LS->GetSubChunk(CHUNK_ID_SCSL);              RIFF::Chunk* ckSCSL = lst3LS->GetSubChunk(CHUNK_ID_SCSL);
5009              if (ckSCSL) {              if (ckSCSL) {
5010                  ckSCSL->SetPos(0);                  ckSCSL->SetPos(0);
# Line 4933  namespace { Line 5024  namespace {
5024                      }                      }
5025                  }                  }
5026              }              }
5027    
5028                // overridden script 'patch' variables
5029                RIFF::Chunk* ckSCPV = lst3LS->GetSubChunk(CHUNK_ID_SCPV);
5030                if (ckSCPV) {
5031                    ckSCPV->SetPos(0);
5032    
5033                    int nScripts = ckSCPV->ReadUint32();
5034                    for (int iScript = 0; iScript < nScripts; ++iScript) {
5035                        _UUID uuid;
5036                        for (int i = 0; i < 16; ++i)
5037                            uuid[i] = ckSCPV->ReadUint8();
5038                        uint slot = ckSCPV->ReadUint32();
5039                        ckSCPV->ReadUint32(); // unused, reserved 32 bit
5040                        int nVars = ckSCPV->ReadUint32();
5041                        for (int iVar = 0; iVar < nVars; ++iVar) {
5042                            uint8_t type = ckSCPV->ReadUint8();
5043                            ckSCPV->ReadUint8();  // unused, reserved byte
5044                            int blobSize = ckSCPV->ReadUint16();
5045                            RIFF::file_offset_t pos = ckSCPV->GetPos();
5046                            // assuming 1st bit is set in 'type', otherwise blob not
5047                            // supported for decoding
5048                            if (type & 1) {
5049                                String name, value;
5050                                int len = ckSCPV->ReadUint16();
5051                                for (int i = 0; i < len; ++i)
5052                                    name += (char) ckSCPV->ReadUint8();
5053                                len = ckSCPV->ReadUint16();
5054                                for (int i = 0; i < len; ++i)
5055                                    value += (char) ckSCPV->ReadUint8();
5056                                if (!name.empty()) // 'name' should never be empty, but just to be sure
5057                                    scriptVars[uuid][slot][name] = value;
5058                            }
5059                            // also for potential future extensions: seek forward
5060                            // according to blob size
5061                            ckSCPV->SetPos(pos + blobSize);
5062                        }
5063                    }
5064                }
5065          }          }
5066    
5067          if (pProgress)          if (pProgress)
# Line 5024  namespace { Line 5153  namespace {
5153    
5154             RIFF::List* lst3LS = pCkInstrument->GetSubList(LIST_TYPE_3LS);             RIFF::List* lst3LS = pCkInstrument->GetSubList(LIST_TYPE_3LS);
5155             if (!lst3LS) lst3LS = pCkInstrument->AddSubList(LIST_TYPE_3LS);             if (!lst3LS) lst3LS = pCkInstrument->AddSubList(LIST_TYPE_3LS);
5156    
5157               // save script slots (that is references to instrument scripts)
5158             const int slotCount = (int) pScriptRefs->size();             const int slotCount = (int) pScriptRefs->size();
5159             const int headerSize = 3 * sizeof(uint32_t);             const int headerSize = 3 * sizeof(uint32_t);
5160             const int slotSize  = 2 * sizeof(uint32_t);             const int slotSize  = 2 * sizeof(uint32_t);
# Line 5048  namespace { Line 5179  namespace {
5179                 store32(&pData[pos], (*pScriptRefs)[i].bypass ? 1 : 0);                 store32(&pData[pos], (*pScriptRefs)[i].bypass ? 1 : 0);
5180                 pos += sizeof(uint32_t);                 pos += sizeof(uint32_t);
5181             }             }
5182    
5183               // save overridden script 'patch' variables ...
5184    
5185               // the actual 'scriptVars' member variable might contain variables of
5186               // scripts which are currently no longer assigned to any script slot
5187               // of this instrument, we need to get rid of these variables here to
5188               // prevent saving those persistently, however instead of touching the
5189               // member variable 'scriptVars' directly, rather strip a separate
5190               // copy such that the overridden values are not lost during an
5191               // instrument editor session (i.e. if script might be re-assigned)
5192               _VarsByScript vars = stripScriptVars();
5193               if (!vars.empty()) {
5194                   // determine total size required for 'SCPV' RIFF chunk, and the
5195                   // total amount of scripts being overridden (the latter is
5196                   // required because a script might be used on several script
5197                   // slots, hence vars.size() could then not be used here instead)
5198                   size_t totalChunkSize = 4;
5199                   size_t totalScriptsOverridden = 0;
5200                   for (const auto& script : vars) {
5201                       for (const auto& slot : script.second) {
5202                           totalScriptsOverridden++;
5203                           totalChunkSize += 16 + 4 + 4 + 4;
5204                           for (const auto& var : slot.second) {
5205                               totalChunkSize += 4 + 2 + var.first.length() +
5206                                                     2 + var.second.length();
5207                           }
5208                       }
5209                   }
5210    
5211                   // ensure 'SCPV' RIFF chunk exists (with required size)
5212                   RIFF::Chunk* ckSCPV = lst3LS->GetSubChunk(CHUNK_ID_SCPV);
5213                   if (!ckSCPV) ckSCPV = lst3LS->AddSubChunk(CHUNK_ID_SCPV, totalChunkSize);
5214                   else ckSCPV->Resize(totalChunkSize);
5215    
5216                   // store the actual data to 'SCPV' RIFF chunk
5217                   uint8_t* pData = (uint8_t*) ckSCPV->LoadChunkData();
5218                   int pos = 0;
5219                   store32(&pData[pos], (uint32_t) totalScriptsOverridden); // scripts count
5220                   pos += 4;
5221                   for (const auto& script : vars) {
5222                       for (const auto& slot : script.second) {
5223                           for (int i = 0; i < 16; ++i)
5224                               pData[pos+i] = script.first[i]; // uuid
5225                           pos += 16;
5226                           store32(&pData[pos], (uint32_t) slot.first); // slot index
5227                           pos += 4;
5228                           store32(&pData[pos], (uint32_t) 0); // unused, reserved 32 bit
5229                           pos += 4;
5230                           store32(&pData[pos], (uint32_t) slot.second.size()); // variables count
5231                           pos += 4;
5232                           for (const auto& var : slot.second) {
5233                               pData[pos++] = 1; // type
5234                               pData[pos++] = 0; // reserved byte
5235                               store16(&pData[pos], 2 + var.first.size() + 2 + var.second.size()); // blob size
5236                               pos += 2;
5237                               store16(&pData[pos], var.first.size()); // variable name length
5238                               pos += 2;
5239                               for (int i = 0; i < var.first.size(); ++i)
5240                                   pData[pos++] = var.first[i];
5241                               store16(&pData[pos], var.second.size()); // variable value length
5242                               pos += 2;
5243                               for (int i = 0; i < var.second.size(); ++i)
5244                                   pData[pos++] = var.second[i];
5245                           }
5246                       }
5247                   }
5248               } else {
5249                   // no script variable overridden by this instrument, so get rid
5250                   // of 'SCPV' RIFF chunk (if any)
5251                   RIFF::Chunk* ckSCPV = lst3LS->GetSubChunk(CHUNK_ID_SCPV);
5252                   if (ckSCPV) lst3LS->DeleteSubChunk(ckSCPV);
5253               }
5254         } else {         } else {
5255             // no script slots, so get rid of any LS custom RIFF chunks (if any)             // no script slots, so get rid of any LS custom RIFF chunks (if any)
5256             RIFF::List* lst3LS = pCkInstrument->GetSubList(LIST_TYPE_3LS);             RIFF::List* lst3LS = pCkInstrument->GetSubList(LIST_TYPE_3LS);
# Line 5472  namespace { Line 5675  namespace {
5675              scriptPoolFileOffsets.at(index).bypass = bBypass;              scriptPoolFileOffsets.at(index).bypass = bBypass;
5676      }      }
5677    
5678        /// type cast (by copy) uint8_t[16] -> std::array<uint8_t,16>
5679        inline std::array<uint8_t,16> _UUIDFromCArray(const uint8_t* pData) {
5680            std::array<uint8_t,16> uuid;
5681            memcpy(&uuid[0], pData, 16);
5682            return uuid;
5683        }
5684    
5685        /**
5686         * Returns true if this @c Instrument has any script slot which references
5687         * the @c Script identified by passed @p uuid.
5688         */
5689        bool Instrument::ReferencesScriptWithUuid(const _UUID& uuid) {
5690            const uint nSlots = ScriptSlotCount();
5691            for (uint iSlot = 0; iSlot < nSlots; ++iSlot)
5692                if (_UUIDFromCArray(&GetScriptOfSlot(iSlot)->Uuid[0]) == uuid)
5693                    return true;
5694            return false;
5695        }
5696    
5697        /** @brief Checks whether a certain script 'patch' variable value is set.
5698         *
5699         * Returns @c true if the initial value for the requested script variable is
5700         * currently overridden by this instrument.
5701         *
5702         * @remarks Real-time instrument scripts allow to declare special 'patch'
5703         * variables, which essentially behave like regular variables of their data
5704         * type, however their initial value may optionally be overridden on a per
5705         * instrument basis. That allows to share scripts between instruments while
5706         * still being able to fine tune certain aspects of the script for each
5707         * instrument individually.
5708         *
5709         * @note This is an own format extension which did not exist i.e. in the
5710         * GigaStudio 4 software. It will currently only work with LinuxSampler and
5711         * Gigedit.
5712         *
5713         * @param slot - script slot index of the variable to be retrieved
5714         * @param variable - name of the 'patch' variable in that script
5715         */
5716        bool Instrument::IsScriptPatchVariableSet(int slot, String variable) {
5717            if (variable.empty()) return false;
5718            Script* script = GetScriptOfSlot(slot);
5719            if (!script) return false;
5720            const _UUID uuid = _UUIDFromCArray(&script->Uuid[0]);
5721            if (!scriptVars.count(uuid)) return false;
5722            const _VarsBySlot& slots = scriptVars.find(uuid)->second;
5723            if (slots.empty()) return false;
5724            if (slots.count(slot))
5725                return slots.find(slot)->second.count(variable);
5726            else
5727                return slots.begin()->second.count(variable);
5728        }
5729    
5730        /** @brief Get all overridden script 'patch' variables.
5731         *
5732         * Returns map of key-value pairs reflecting all patch variables currently
5733         * being overridden by this instrument for the given script @p slot, where
5734         * key is the variable name and value is the hereby currently overridden
5735         * value for that variable.
5736         *
5737         * @remarks Real-time instrument scripts allow to declare special 'patch'
5738         * variables, which essentially behave like regular variables of their data
5739         * type, however their initial value may optionally be overridden on a per
5740         * instrument basis. That allows to share scripts between instruments while
5741         * still being able to fine tune certain aspects of the script for each
5742         * instrument individually.
5743         *
5744         * @note This is an own format extension which did not exist i.e. in the
5745         * GigaStudio 4 software. It will currently only work with LinuxSampler and
5746         * Gigedit.
5747         *
5748         * @param slot - script slot index of the variable to be retrieved
5749         */
5750        std::map<String,String> Instrument::GetScriptPatchVariables(int slot) {
5751            Script* script = GetScriptOfSlot(slot);
5752            if (!script) return std::map<String,String>();
5753            const _UUID uuid = _UUIDFromCArray(&script->Uuid[0]);
5754            if (!scriptVars.count(uuid)) return std::map<String,String>();
5755            const _VarsBySlot& slots = scriptVars.find(uuid)->second;
5756            if (slots.empty()) return std::map<String,String>();
5757            const _PatchVars& vars =
5758                (slots.count(slot)) ?
5759                    slots.find(slot)->second : slots.begin()->second;
5760            return vars;
5761        }
5762    
5763        /** @brief Get overridden initial value for 'patch' variable.
5764         *
5765         * Returns current initial value for the requested script variable being
5766         * overridden by this instrument.
5767         *
5768         * @remarks Real-time instrument scripts allow to declare special 'patch'
5769         * variables, which essentially behave like regular variables of their data
5770         * type, however their initial value may optionally be overridden on a per
5771         * instrument basis. That allows to share scripts between instruments while
5772         * still being able to fine tune certain aspects of the script for each
5773         * instrument individually.
5774         *
5775         * @note This is an own format extension which did not exist i.e. in the
5776         * GigaStudio 4 software. It will currently only work with LinuxSampler and
5777         * Gigedit.
5778         *
5779         * @param slot - script slot index of the variable to be retrieved
5780         * @param variable - name of the 'patch' variable in that script
5781         */
5782        String Instrument::GetScriptPatchVariable(int slot, String variable) {
5783            std::map<String,String> vars = GetScriptPatchVariables(slot);
5784            return (vars.count(variable)) ? vars.find(variable)->second : "";
5785        }
5786    
5787        /** @brief Override initial value for 'patch' variable.
5788         *
5789         * Overrides initial value for the requested script variable for this
5790         * instrument with the passed value.
5791         *
5792         * @remarks Real-time instrument scripts allow to declare special 'patch'
5793         * variables, which essentially behave like regular variables of their data
5794         * type, however their initial value may optionally be overridden on a per
5795         * instrument basis. That allows to share scripts between instruments while
5796         * still being able to fine tune certain aspects of the script for each
5797         * instrument individually.
5798         *
5799         * @note This is an own format extension which did not exist i.e. in the
5800         * GigaStudio 4 software. It will currently only work with LinuxSampler and
5801         * Gigedit.
5802         *
5803         * @param slot - script slot index of the variable to be set
5804         * @param variable - name of the 'patch' variable in that script
5805         * @param value - overridden initial value for that script variable
5806         * @throws gig::Exception if given script @p slot index is invalid or given
5807         *         @p variable name is empty
5808         */
5809        void Instrument::SetScriptPatchVariable(int slot, String variable, String value) {
5810            if (variable.empty())
5811                throw Exception("Variable name must not be empty");
5812            Script* script = GetScriptOfSlot(slot);
5813            if (!script)
5814                throw Exception("No script slot with index " + ToString(slot));
5815            const _UUID uuid = _UUIDFromCArray(&script->Uuid[0]);
5816            scriptVars[uuid][slot][variable] = value;
5817        }
5818    
5819        /** @brief Drop overridden initial value(s) for 'patch' variable(s).
5820         *
5821         * Reverts initial value(s) for requested script variable(s) back to their
5822         * default initial value(s) defined in the script itself.
5823         *
5824         * Both arguments of this method are optional. The most obvious use case of
5825         * this method would be passing a valid script @p slot index and a
5826         * (non-emtpy string as) @p variable name to this method, which would cause
5827         * that single variable to be unset for that specific script slot (on this
5828         * @c Instrument level).
5829         *
5830         * Not passing a value (or @c -1 for @p slot and/or empty string for
5831         * @p variable) means 'wildcard'. So accordingly absence of argument(s) will
5832         * cause all variables and/or for all script slots being unset. Hence this
5833         * method serves 2^2 = 4 possible use cases in total and accordingly covers
5834         * 4 different behaviours in one method.
5835         *
5836         * @remarks Real-time instrument scripts allow to declare special 'patch'
5837         * variables, which essentially behave like regular variables of their data
5838         * type, however their initial value may optionally be overridden on a per
5839         * instrument basis. That allows to share scripts between instruments while
5840         * still being able to fine tune certain aspects of the script for each
5841         * instrument individually.
5842         *
5843         * @note This is an own format extension which did not exist i.e. in the
5844         * GigaStudio 4 software. It will currently only work with LinuxSampler and
5845         * Gigedit.
5846         *
5847         * @param slot - script slot index of the variable to be unset
5848         * @param variable - name of the 'patch' variable in that script
5849         */
5850        void Instrument::UnsetScriptPatchVariable(int slot, String variable) {
5851            Script* script = GetScriptOfSlot(slot);
5852    
5853            // option 1: unset a particular variable of one particular script slot
5854            if (slot != -1 && !variable.empty()) {
5855                if (!script) return;
5856                const _UUID uuid = _UUIDFromCArray(&script->Uuid[0]);
5857                if (!scriptVars.count(uuid)) return;
5858                if (!scriptVars[uuid].count(slot)) return;
5859                if (scriptVars[uuid][slot].count(variable))
5860                    scriptVars[uuid][slot].erase(
5861                        scriptVars[uuid][slot].find(variable)
5862                    );
5863                if (scriptVars[uuid][slot].empty())
5864                    scriptVars[uuid].erase( scriptVars[uuid].find(slot) );
5865                if (scriptVars[uuid].empty())
5866                    scriptVars.erase( scriptVars.find(uuid) );
5867                return;
5868            }
5869    
5870            // option 2: unset all variables of all script slots
5871            if (slot == -1 && variable.empty()) {
5872                scriptVars.clear();
5873                return;
5874            }
5875    
5876            // option 3: unset all variables of one particular script slot only
5877            if (slot != -1) {
5878                if (!script) return;
5879                const _UUID uuid = _UUIDFromCArray(&script->Uuid[0]);
5880                if (scriptVars.count(uuid))
5881                    scriptVars.erase( scriptVars.find(uuid) );
5882                return;
5883            }
5884    
5885            // option 4: unset a particular variable of all script slots
5886            _VarsByScript::iterator itScript = scriptVars.begin();
5887            _VarsByScript::iterator endScript = scriptVars.end();
5888            while (itScript != endScript) {
5889                _VarsBySlot& slots = itScript->second;
5890                _VarsBySlot::iterator itSlot = slots.begin();
5891                _VarsBySlot::iterator endSlot = slots.end();
5892                while (itSlot != endSlot) {
5893                    _PatchVars& vars = itSlot->second;
5894                    if (vars.count(variable))
5895                        vars.erase( vars.find(variable) );
5896                    if (vars.empty())
5897                        slots.erase(itSlot++); // postfix increment to avoid iterator invalidation
5898                    else
5899                        ++itSlot;
5900                }
5901                if (slots.empty())
5902                    scriptVars.erase(itScript++); // postfix increment to avoid iterator invalidation
5903                else
5904                    ++itScript;
5905            }
5906        }
5907    
5908        /**
5909         * Returns stripped version of member variable @c scriptVars, where scripts
5910         * no longer referenced by this @c Instrument are filtered out, and so are
5911         * variables of meanwhile obsolete slots (i.e. a script still being
5912         * referenced, but previously overridden on a script slot which either no
5913         * longer exists or is hosting another script now).
5914         */
5915        Instrument::_VarsByScript Instrument::stripScriptVars() {
5916            _VarsByScript vars;
5917            _VarsByScript::const_iterator itScript = scriptVars.begin();
5918            _VarsByScript::const_iterator endScript = scriptVars.end();
5919            for (; itScript != endScript; ++itScript) {
5920                const _UUID& uuid = itScript->first;
5921                if (!ReferencesScriptWithUuid(uuid))
5922                    continue;
5923                const _VarsBySlot& slots = itScript->second;
5924                _VarsBySlot::const_iterator itSlot = slots.begin();
5925                _VarsBySlot::const_iterator endSlot = slots.end();
5926                for (; itSlot != endSlot; ++itSlot) {
5927                    Script* script = GetScriptOfSlot(itSlot->first);
5928                    if (!script) continue;
5929                    if (_UUIDFromCArray(&script->Uuid[0]) != uuid) continue;
5930                    if (itSlot->second.empty()) continue;
5931                    vars[uuid][itSlot->first] = itSlot->second;
5932                }
5933            }
5934            return vars;
5935        }
5936    
5937      /**      /**
5938       * Make a (semi) deep copy of the Instrument object given by @a orig       * Make a (semi) deep copy of the Instrument object given by @a orig
5939       * and assign it to this object.       * and assign it to this object.
# Line 5506  namespace { Line 5968  namespace {
5968          PianoReleaseMode = orig->PianoReleaseMode;          PianoReleaseMode = orig->PianoReleaseMode;
5969          DimensionKeyRange = orig->DimensionKeyRange;          DimensionKeyRange = orig->DimensionKeyRange;
5970          scriptPoolFileOffsets = orig->scriptPoolFileOffsets;          scriptPoolFileOffsets = orig->scriptPoolFileOffsets;
5971          pScriptRefs = orig->pScriptRefs;          // deep copy of pScriptRefs required (to avoid undefined behaviour)
5972            if (pScriptRefs) delete pScriptRefs;
5973            pScriptRefs = new std::vector<_ScriptPooolRef>;
5974            if (orig->pScriptRefs)
5975                *pScriptRefs = *orig->pScriptRefs;
5976            scriptVars = orig->scriptVars;
5977                    
5978          // free old midi rules          // free old midi rules
5979          for (int i = 0 ; pMidiRules[i] ; i++) {          for (int i = 0 ; pMidiRules[i] ; i++) {
# Line 5533  namespace { Line 6000  namespace {
6000          UpdateRegionKeyTable();          UpdateRegionKeyTable();
6001      }      }
6002    
6003        /**
6004         * Returns @c true in case this Instrument object uses any gig format
6005         * extension, that is e.g. whether any DimensionRegion object currently
6006         * has any setting effective that would require our "LSDE" RIFF chunk to
6007         * be stored to the gig file.
6008         *
6009         * Right now this is a private method. It is considerable though this method
6010         * to become (in slightly modified form) a public API method in future, i.e.
6011         * to allow instrument editors to visualize and/or warn the user of any gig
6012         * format extension being used. See also comments on
6013         * DimensionRegion::UsesAnyGigFormatExtension() for details about such a
6014         * potential public API change in future.
6015         */
6016        bool Instrument::UsesAnyGigFormatExtension() const {
6017            if (!pRegions) return false;
6018            if (!scriptVars.empty()) return true;
6019            RegionList::const_iterator iter = pRegions->begin();
6020            RegionList::const_iterator end  = pRegions->end();
6021            for (; iter != end; ++iter) {
6022                gig::Region* rgn = static_cast<gig::Region*>(*iter);
6023                if (rgn->UsesAnyGigFormatExtension())
6024                    return true;
6025            }
6026            return false;
6027        }
6028    
6029    
6030  // *************** Group ***************  // *************** Group ***************
6031  // *  // *
# Line 6863  namespace { Line 7356  namespace {
7356          return bAutoLoad;          return bAutoLoad;
7357      }      }
7358    
7359        /**
7360         * Returns @c true in case this gig File object uses any gig format
7361         * extension, that is e.g. whether any DimensionRegion object currently
7362         * has any setting effective that would require our "LSDE" RIFF chunk to
7363         * be stored to the gig file.
7364         *
7365         * Right now this is a private method. It is considerable though this method
7366         * to become (in slightly modified form) a public API method in future, i.e.
7367         * to allow instrument editors to visualize and/or warn the user of any gig
7368         * format extension being used. See also comments on
7369         * DimensionRegion::UsesAnyGigFormatExtension() for details about such a
7370         * potential public API change in future.
7371         */
7372        bool File::UsesAnyGigFormatExtension() const {
7373            if (!pInstruments) return false;
7374            InstrumentList::iterator iter = pInstruments->begin();
7375            InstrumentList::iterator end  = pInstruments->end();
7376            for (; iter != end; ++iter) {
7377                Instrument* pInstrument = static_cast<gig::Instrument*>(*iter);
7378                if (pInstrument->UsesAnyGigFormatExtension())
7379                    return true;
7380            }
7381            return false;
7382        }
7383    
7384    
7385  // *************** Exception ***************  // *************** Exception ***************

Legend:
Removed from v.3709  
changed lines
  Added in v.3904

  ViewVC Help
Powered by ViewVC