--- libgig/trunk/src/gig.cpp 2007/08/25 09:59:53 1301 +++ libgig/trunk/src/gig.cpp 2007/11/25 17:29:37 1524 @@ -255,6 +255,69 @@ +// *************** Internal CRC-32 (Cyclic Redundancy Check) functions *************** +// * + + static uint32_t* __initCRCTable() { + static uint32_t res[256]; + + for (int i = 0 ; i < 256 ; i++) { + uint32_t c = i; + for (int j = 0 ; j < 8 ; j++) { + c = (c & 1) ? 0xedb88320 ^ (c >> 1) : c >> 1; + } + res[i] = c; + } + return res; + } + + static const uint32_t* __CRCTable = __initCRCTable(); + + /** + * Initialize a CRC variable. + * + * @param crc - variable to be initialized + */ + inline static void __resetCRC(uint32_t& crc) { + crc = 0xffffffff; + } + + /** + * Used to calculate checksums of the sample data in a gig file. The + * checksums are stored in the 3crc chunk of the gig file and + * automatically updated when a sample is written with Sample::Write(). + * + * One should call __resetCRC() to initialize the CRC variable to be + * used before calling this function the first time. + * + * After initializing the CRC variable one can call this function + * arbitrary times, i.e. to split the overall CRC calculation into + * steps. + * + * Once the whole data was processed by __calculateCRC(), one should + * call __encodeCRC() to get the final CRC result. + * + * @param buf - pointer to data the CRC shall be calculated of + * @param bufSize - size of the data to be processed + * @param crc - variable the CRC sum shall be stored to + */ + static void __calculateCRC(unsigned char* buf, int bufSize, uint32_t& crc) { + for (int i = 0 ; i < bufSize ; i++) { + crc = __CRCTable[(crc ^ buf[i]) & 0xff] ^ (crc >> 8); + } + } + + /** + * Returns the final CRC result. + * + * @param crc - variable previously passed to __calculateCRC() + */ + inline static uint32_t __encodeCRC(const uint32_t& crc) { + return crc ^ 0xffffffff; + } + + + // *************** Other Internal functions *************** // * @@ -278,26 +341,6 @@ -// *************** CRC *************** -// * - - const uint32_t* CRC::table(initTable()); - - uint32_t* CRC::initTable() { - uint32_t* res = new uint32_t[256]; - - for (int i = 0 ; i < 256 ; i++) { - uint32_t c = i; - for (int j = 0 ; j < 8 ; j++) { - c = (c & 1) ? 0xedb88320 ^ (c >> 1) : c >> 1; - } - res[i] = c; - } - return res; - } - - - // *************** Sample *************** // * @@ -323,14 +366,16 @@ * is located, 0 otherwise */ Sample::Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset, unsigned long fileNo) : DLS::Sample((DLS::File*) pFile, waveList, WavePoolOffset) { - static const DLS::Info::FixedStringLength fixedStringLengths[] = { + static const DLS::Info::string_length_t fixedStringLengths[] = { { CHUNK_ID_INAM, 64 }, { 0, 0 } }; - pInfo->FixedStringLengths = fixedStringLengths; + pInfo->SetFixedStringLengths(fixedStringLengths); Instances++; FileNo = fileNo; + __resetCRC(crc); + pCk3gix = waveList->GetSubChunk(CHUNK_ID_3GIX); if (pCk3gix) { uint16_t iSampleGroup = pCk3gix->ReadInt16(); @@ -1168,7 +1213,7 @@ // if this is the first write in this sample, reset the // checksum calculator if (pCkData->GetPos() == 0) { - crc.reset(); + __resetCRC(crc); } if (GetSize() < SampleCount) throw Exception("Could not write sample data, current sample size to small"); unsigned long res; @@ -1178,13 +1223,13 @@ res = Channels == 2 ? pCkData->Write(pBuffer, SampleCount << 1, 2) >> 1 : pCkData->Write(pBuffer, SampleCount, 2); } - crc.update((unsigned char *)pBuffer, SampleCount * FrameSize); + __calculateCRC((unsigned char *)pBuffer, SampleCount * FrameSize, crc); // if this is the last write, update the checksum chunk in the // file if (pCkData->GetPos() == pCkData->GetSize()) { File* pFile = static_cast(GetParent()); - pFile->SetSampleChecksum(this, crc.getValue()); + pFile->SetSampleChecksum(this, __encodeCRC(crc)); } return res; } @@ -1262,10 +1307,11 @@ uint DimensionRegion::Instances = 0; DimensionRegion::VelocityTableMap* DimensionRegion::pVelocityTables = NULL; - DimensionRegion::DimensionRegion(RIFF::List* _3ewl) : DLS::Sampler(_3ewl) { + DimensionRegion::DimensionRegion(Region* pParent, RIFF::List* _3ewl) : DLS::Sampler(_3ewl) { Instances++; pSample = NULL; + pRegion = pParent; if (_3ewl->GetSubChunk(CHUNK_ID_WSMP)) memcpy(&Crossfade, &SamplerOptions, 4); else memset(&Crossfade, 0, 4); @@ -1511,32 +1557,15 @@ VelocityResponseDepth, VelocityResponseCurveScaling); - curve_type_t curveType = ReleaseVelocityResponseCurve; - uint8_t depth = ReleaseVelocityResponseDepth; - - // this models a strange behaviour or bug in GSt: two of the - // velocity response curves for release time are not used even - // if specified, instead another curve is chosen. - if ((curveType == curve_type_nonlinear && depth == 0) || - (curveType == curve_type_special && depth == 4)) { - curveType = curve_type_nonlinear; - depth = 3; - } - pVelocityReleaseTable = GetVelocityTable(curveType, depth, 0); - - curveType = VCFVelocityCurve; - depth = VCFVelocityDynamicRange; - - // even stranger GSt: two of the velocity response curves for - // filter cutoff are not used, instead another special curve - // is chosen. This curve is not used anywhere else. - if ((curveType == curve_type_nonlinear && depth == 0) || - (curveType == curve_type_special && depth == 4)) { - curveType = curve_type_special; - depth = 5; - } - pVelocityCutoffTable = GetVelocityTable(curveType, depth, - VCFCutoffController <= vcf_cutoff_ctrl_none2 ? VCFVelocityScale : 0); + pVelocityReleaseTable = GetReleaseVelocityTable( + ReleaseVelocityResponseCurve, + ReleaseVelocityResponseDepth + ); + + pVelocityCutoffTable = GetCutoffVelocityTable(VCFVelocityCurve, + VCFVelocityDynamicRange, + VCFVelocityScale, + VCFCutoffController); SampleAttenuation = pow(10.0, -Gain / (20.0 * 655360)); VelocityTable = 0; @@ -1565,6 +1594,15 @@ } /** + * Updates the respective member variable and updates @c SampleAttenuation + * which depends on this value. + */ + void DimensionRegion::SetGain(int32_t gain) { + DLS::Sampler::SetGain(gain); + SampleAttenuation = pow(10.0, -Gain / (20.0 * 655360)); + } + + /** * Apply dimension region settings to the respective RIFF chunks. You * have to call File::Save() to make changes persistent. * @@ -1572,10 +1610,6 @@ * It will be called automatically when File::Save() was called. */ void DimensionRegion::UpdateChunks() { - // check if wsmp is going to be created by - // DLS::Sampler::UpdateChunks - bool wsmp_created = !pParentList->GetSubChunk(CHUNK_ID_WSMP); - // first update base class's chunk DLS::Sampler::UpdateChunks(); @@ -1588,10 +1622,10 @@ // make sure '3ewa' chunk exists RIFF::Chunk* _3ewa = pParentList->GetSubChunk(CHUNK_ID_3EWA); - if (!_3ewa) _3ewa = pParentList->AddSubChunk(CHUNK_ID_3EWA, 140); - else if (wsmp_created) { - // make sure the chunk order is: wsmp, 3ewa - pParentList->MoveSubChunk(_3ewa, 0); + if (!_3ewa) { + File* pFile = (File*) GetParent()->GetParent()->GetParent(); + bool version3 = pFile->pVersion && pFile->pVersion->major == 3; + _3ewa = pParentList->AddSubChunk(CHUNK_ID_3EWA, version3 ? 148 : 140); } pData = (uint8_t*) _3ewa->LoadChunkData(); @@ -1859,6 +1893,40 @@ } } + double* DimensionRegion::GetReleaseVelocityTable(curve_type_t releaseVelocityResponseCurve, uint8_t releaseVelocityResponseDepth) { + curve_type_t curveType = releaseVelocityResponseCurve; + uint8_t depth = releaseVelocityResponseDepth; + // this models a strange behaviour or bug in GSt: two of the + // velocity response curves for release time are not used even + // if specified, instead another curve is chosen. + if ((curveType == curve_type_nonlinear && depth == 0) || + (curveType == curve_type_special && depth == 4)) { + curveType = curve_type_nonlinear; + depth = 3; + } + return GetVelocityTable(curveType, depth, 0); + } + + double* DimensionRegion::GetCutoffVelocityTable(curve_type_t vcfVelocityCurve, + uint8_t vcfVelocityDynamicRange, + uint8_t vcfVelocityScale, + vcf_cutoff_ctrl_t vcfCutoffController) + { + curve_type_t curveType = vcfVelocityCurve; + uint8_t depth = vcfVelocityDynamicRange; + // even stranger GSt: two of the velocity response curves for + // filter cutoff are not used, instead another special curve + // is chosen. This curve is not used anywhere else. + if ((curveType == curve_type_nonlinear && depth == 0) || + (curveType == curve_type_special && depth == 4)) { + curveType = curve_type_special; + depth = 5; + } + return GetVelocityTable(curveType, depth, + (vcfCutoffController <= vcf_cutoff_ctrl_none2) + ? vcfVelocityScale : 0); + } + // get the corresponding velocity table from the table map or create & calculate that table if it doesn't exist yet double* DimensionRegion::GetVelocityTable(curve_type_t curveType, uint8_t depth, uint8_t scaling) { @@ -1874,6 +1942,10 @@ return table; } + Region* DimensionRegion::GetParent() const { + return pRegion; + } + leverage_ctrl_t DimensionRegion::DecodeLeverageController(_lev_ctrl_t EncodedController) { leverage_ctrl_t decodedcontroller; switch (EncodedController) { @@ -2127,6 +2199,96 @@ return pVelocityCutoffTable[MIDIKeyVelocity]; } + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetVelocityResponseCurve(curve_type_t curve) { + pVelocityAttenuationTable = + GetVelocityTable( + curve, VelocityResponseDepth, VelocityResponseCurveScaling + ); + VelocityResponseCurve = curve; + } + + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetVelocityResponseDepth(uint8_t depth) { + pVelocityAttenuationTable = + GetVelocityTable( + VelocityResponseCurve, depth, VelocityResponseCurveScaling + ); + VelocityResponseDepth = depth; + } + + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetVelocityResponseCurveScaling(uint8_t scaling) { + pVelocityAttenuationTable = + GetVelocityTable( + VelocityResponseCurve, VelocityResponseDepth, scaling + ); + VelocityResponseCurveScaling = scaling; + } + + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetReleaseVelocityResponseCurve(curve_type_t curve) { + pVelocityReleaseTable = GetReleaseVelocityTable(curve, ReleaseVelocityResponseDepth); + ReleaseVelocityResponseCurve = curve; + } + + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetReleaseVelocityResponseDepth(uint8_t depth) { + pVelocityReleaseTable = GetReleaseVelocityTable(ReleaseVelocityResponseCurve, depth); + ReleaseVelocityResponseDepth = depth; + } + + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetVCFCutoffController(vcf_cutoff_ctrl_t controller) { + pVelocityCutoffTable = GetCutoffVelocityTable(VCFVelocityCurve, VCFVelocityDynamicRange, VCFVelocityScale, controller); + VCFCutoffController = controller; + } + + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetVCFVelocityCurve(curve_type_t curve) { + pVelocityCutoffTable = GetCutoffVelocityTable(curve, VCFVelocityDynamicRange, VCFVelocityScale, VCFCutoffController); + VCFVelocityCurve = curve; + } + + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetVCFVelocityDynamicRange(uint8_t range) { + pVelocityCutoffTable = GetCutoffVelocityTable(VCFVelocityCurve, range, VCFVelocityScale, VCFCutoffController); + VCFVelocityDynamicRange = range; + } + + /** + * Updates the respective member variable and the lookup table / cache + * that depends on this value. + */ + void DimensionRegion::SetVCFVelocityScale(uint8_t scaling) { + pVelocityCutoffTable = GetCutoffVelocityTable(VCFVelocityCurve, VCFVelocityDynamicRange, scaling, VCFCutoffController); + VCFVelocityScale = scaling; + } + double* DimensionRegion::CreateVelocityTable(curve_type_t curveType, uint8_t depth, uint8_t scaling) { // line-segment approximations of the 15 velocity curves @@ -2210,6 +2372,8 @@ // Actual Loading + if (!file->GetAutoLoad()) return; + LoadDimensionRegions(rgnList); RIFF::Chunk* _3lnk = rgnList->GetSubChunk(CHUNK_ID_3LNK); @@ -2253,12 +2417,14 @@ else _3lnk->SetPos(44); - // load sample references - for (uint i = 0; i < DimensionRegions; i++) { - uint32_t wavepoolindex = _3lnk->ReadUint32(); - if (file->pWavePoolTable) pDimensionRegions[i]->pSample = GetSampleFromWavePool(wavepoolindex); + // load sample references (if auto loading is enabled) + if (file->GetAutoLoad()) { + for (uint i = 0; i < DimensionRegions; i++) { + uint32_t wavepoolindex = _3lnk->ReadUint32(); + if (file->pWavePoolTable) pDimensionRegions[i]->pSample = GetSampleFromWavePool(wavepoolindex); + } + GetSample(); // load global region sample reference } - GetSample(); // load global region sample reference } else { DimensionRegions = 0; for (int i = 0 ; i < 8 ; i++) { @@ -2273,7 +2439,7 @@ RIFF::List* _3prg = rgnList->GetSubList(LIST_TYPE_3PRG); if (!_3prg) _3prg = rgnList->AddSubList(LIST_TYPE_3PRG); RIFF::List* _3ewl = _3prg->AddSubList(LIST_TYPE_3EWL); - pDimensionRegions[0] = new DimensionRegion(_3ewl); + pDimensionRegions[0] = new DimensionRegion(this, _3ewl); DimensionRegions = 1; } } @@ -2297,22 +2463,13 @@ // first update base class's chunks DLS::Region::UpdateChunks(); - File* pFile = (File*) GetParent()->GetParent(); - bool version3 = pFile->pVersion && pFile->pVersion->major == 3; - // update dimension region's chunks for (int i = 0; i < DimensionRegions; i++) { - DimensionRegion* d = pDimensionRegions[i]; - - // make sure '3ewa' chunk exists (we need to this before - // calling DimensionRegion::UpdateChunks, as - // DimensionRegion doesn't know which file version it is) - RIFF::Chunk* _3ewa = d->pParentList->GetSubChunk(CHUNK_ID_3EWA); - if (!_3ewa) d->pParentList->AddSubChunk(CHUNK_ID_3EWA, version3 ? 148 : 140); - - d->UpdateChunks(); + pDimensionRegions[i]->UpdateChunks(); } + File* pFile = (File*) GetParent()->GetParent(); + bool version3 = pFile->pVersion && pFile->pVersion->major == 3; const int iMaxDimensions = version3 ? 8 : 5; const int iMaxDimensionRegions = version3 ? 256 : 32; @@ -2368,7 +2525,7 @@ RIFF::List* _3ewl = _3prg->GetFirstSubList(); while (_3ewl) { if (_3ewl->GetListType() == LIST_TYPE_3EWL) { - pDimensionRegions[dimensionRegionNr] = new DimensionRegion(_3ewl); + pDimensionRegions[dimensionRegionNr] = new DimensionRegion(this, _3ewl); dimensionRegionNr++; } _3ewl = _3prg->GetNextSubList(); @@ -2377,6 +2534,13 @@ } } + void Region::SetKeyRange(uint16_t Low, uint16_t High) { + // update KeyRange struct and make sure regions are in correct order + DLS::Region::SetKeyRange(Low, High); + // update Region key table for fast lookup + ((gig::Instrument*)GetParent())->UpdateRegionKeyTable(); + } + void Region::UpdateVelocityTable() { // get velocity dimension's index int veldim = -1; @@ -2764,12 +2928,12 @@ // * Instrument::Instrument(File* pFile, RIFF::List* insList, progress_t* pProgress) : DLS::Instrument((DLS::File*)pFile, insList) { - static const DLS::Info::FixedStringLength fixedStringLengths[] = { + static const DLS::Info::string_length_t fixedStringLengths[] = { { CHUNK_ID_INAM, 64 }, { CHUNK_ID_ISFT, 12 }, { 0, 0 } }; - pInfo->FixedStringLengths = fixedStringLengths; + pInfo->SetFixedStringLengths(fixedStringLengths); // Initialization for (int i = 0; i < 128; i++) RegionKeyTable[i] = NULL; @@ -2797,25 +2961,28 @@ } } - if (!pRegions) pRegions = new RegionList; - RIFF::List* lrgn = insList->GetSubList(LIST_TYPE_LRGN); - if (lrgn) { - RIFF::List* rgn = lrgn->GetFirstSubList(); - while (rgn) { - if (rgn->GetListType() == LIST_TYPE_RGN) { - __notify_progress(pProgress, (float) pRegions->size() / (float) Regions); - pRegions->push_back(new Region(this, rgn)); + if (pFile->GetAutoLoad()) { + if (!pRegions) pRegions = new RegionList; + RIFF::List* lrgn = insList->GetSubList(LIST_TYPE_LRGN); + if (lrgn) { + RIFF::List* rgn = lrgn->GetFirstSubList(); + while (rgn) { + if (rgn->GetListType() == LIST_TYPE_RGN) { + __notify_progress(pProgress, (float) pRegions->size() / (float) Regions); + pRegions->push_back(new Region(this, rgn)); + } + rgn = lrgn->GetNextSubList(); } - rgn = lrgn->GetNextSubList(); + // Creating Region Key Table for fast lookup + UpdateRegionKeyTable(); } - // Creating Region Key Table for fast lookup - UpdateRegionKeyTable(); } __notify_progress(pProgress, 1.0f); // notify done } void Instrument::UpdateRegionKeyTable() { + for (int i = 0; i < 128; i++) RegionKeyTable[i] = NULL; RegionList::iterator iter = pRegions->begin(); RegionList::iterator end = pRegions->end(); for (; iter != end; ++iter) { @@ -2883,7 +3050,7 @@ * there is no Region defined for the given \a Key */ Region* Instrument::GetRegion(unsigned int Key) { - if (!pRegions || !pRegions->size() || Key > 127) return NULL; + if (!pRegions || pRegions->empty() || Key > 127) return NULL; return RegionKeyTable[Key]; /*for (int i = 0; i < Regions; i++) { @@ -3066,17 +3233,17 @@ // *************** File *************** // * - // File version 2.0, 1998-06-28 + /// Reflects Gigasampler file format version 2.0 (1998-06-28). const DLS::version_t File::VERSION_2 = { 0, 2, 19980628 & 0xffff, 19980628 >> 16 }; - // File version 3.0, 2003-03-31 + /// Reflects Gigasampler file format version 3.0 (2003-03-31). const DLS::version_t File::VERSION_3 = { 0, 3, 20030331 & 0xffff, 20030331 >> 16 }; - const DLS::Info::FixedStringLength File::FixedStringLengths[] = { + static const DLS::Info::string_length_t _FileFixedStringLengths[] = { { CHUNK_ID_IARL, 256 }, { CHUNK_ID_IART, 128 }, { CHUNK_ID_ICMS, 128 }, @@ -3098,9 +3265,10 @@ }; File::File() : DLS::File() { + bAutoLoad = true; *pVersion = VERSION_3; pGroups = NULL; - pInfo->FixedStringLengths = FixedStringLengths; + pInfo->SetFixedStringLengths(_FileFixedStringLengths); pInfo->ArchivalLocation = String(256, ' '); // add some mandatory chunks to get the file chunks in right @@ -3113,8 +3281,9 @@ } File::File(RIFF::File* pRIFF) : DLS::File(pRIFF) { + bAutoLoad = true; pGroups = NULL; - pInfo->FixedStringLengths = FixedStringLengths; + pInfo->SetFixedStringLengths(_FileFixedStringLengths); } File::~File() { @@ -3286,7 +3455,8 @@ progress_t subprogress; __divide_progress(pProgress, &subprogress, 3.0f, 0.0f); // randomly schedule 33% for this subtask __notify_progress(&subprogress, 0.0f); - GetFirstSample(&subprogress); // now force all samples to be loaded + if (GetAutoLoad()) + GetFirstSample(&subprogress); // now force all samples to be loaded __notify_progress(&subprogress, 1.0f); // instrument loading subtask @@ -3693,6 +3863,33 @@ } } + /** + * Enable / disable automatic loading. By default this properyt is + * enabled and all informations are loaded automatically. However + * loading all Regions, DimensionRegions and especially samples might + * take a long time for large .gig files, and sometimes one might only + * be interested in retrieving very superficial informations like the + * amount of instruments and their names. In this case one might disable + * automatic loading to avoid very slow response times. + * + * @e CAUTION: by disabling this property many pointers (i.e. sample + * references) and informations will have invalid or even undefined + * data! This feature is currently only intended for retrieving very + * superficial informations in a very fast way. Don't use it to retrieve + * details like synthesis informations or even to modify .gig files! + */ + void File::SetAutoLoad(bool b) { + bAutoLoad = b; + } + + /** + * Returns whether automatic loading is enabled. + * @see SetAutoLoad() + */ + bool File::GetAutoLoad() { + return bAutoLoad; + } + // *************** Exception ***************