--- libgig/trunk/src/gig.cpp 2007/03/02 01:04:45 1050 +++ libgig/trunk/src/gig.cpp 2007/05/12 12:39:25 1180 @@ -254,6 +254,30 @@ } + +// *************** Other Internal functions *************** +// * + + static split_type_t __resolveSplitType(dimension_t dimension) { + return ( + dimension == dimension_layer || + dimension == dimension_samplechannel || + dimension == dimension_releasetrigger || + dimension == dimension_keyboard || + dimension == dimension_roundrobin || + dimension == dimension_random || + dimension == dimension_smartmidi || + dimension == dimension_roundrobinkeyboard + ) ? split_type_bit : split_type_normal; + } + + static int __resolveZoneSize(dimension_def_t& dimension_definition) { + return (dimension_definition.split_type == split_type_normal) + ? int(128.0 / dimension_definition.zones) : 0; + } + + + // *************** Sample *************** // * @@ -279,7 +303,11 @@ * is located, 0 otherwise */ Sample::Sample(File* pFile, RIFF::List* waveList, unsigned long WavePoolOffset, unsigned long fileNo) : DLS::Sample((DLS::File*) pFile, waveList, WavePoolOffset) { - pInfo->UseFixedLengthStrings = true; + static const DLS::Info::FixedStringLength fixedStringLengths[] = { + { CHUNK_ID_INAM, 64 }, + { 0, 0 } + }; + pInfo->FixedStringLengths = fixedStringLengths; Instances++; FileNo = fileNo; @@ -378,23 +406,23 @@ // update 'smpl' chunk uint8_t* pData = (uint8_t*) pCkSmpl->LoadChunkData(); SamplePeriod = uint32_t(1000000000.0 / SamplesPerSecond + 0.5); - memcpy(&pData[0], &Manufacturer, 4); - memcpy(&pData[4], &Product, 4); - memcpy(&pData[8], &SamplePeriod, 4); - memcpy(&pData[12], &MIDIUnityNote, 4); - memcpy(&pData[16], &FineTune, 4); - memcpy(&pData[20], &SMPTEFormat, 4); - memcpy(&pData[24], &SMPTEOffset, 4); - memcpy(&pData[28], &Loops, 4); + store32(&pData[0], Manufacturer); + store32(&pData[4], Product); + store32(&pData[8], SamplePeriod); + store32(&pData[12], MIDIUnityNote); + store32(&pData[16], FineTune); + store32(&pData[20], SMPTEFormat); + store32(&pData[24], SMPTEOffset); + store32(&pData[28], Loops); // we skip 'manufByt' for now (4 bytes) - memcpy(&pData[36], &LoopID, 4); - memcpy(&pData[40], &LoopType, 4); - memcpy(&pData[44], &LoopStart, 4); - memcpy(&pData[48], &LoopEnd, 4); - memcpy(&pData[52], &LoopFraction, 4); - memcpy(&pData[56], &LoopPlayCount, 4); + store32(&pData[36], LoopID); + store32(&pData[40], LoopType); + store32(&pData[44], LoopStart); + store32(&pData[48], LoopEnd); + store32(&pData[52], LoopFraction); + store32(&pData[56], LoopPlayCount); // make sure '3gix' chunk exists pCk3gix = pWaveList->GetSubChunk(CHUNK_ID_3GIX); @@ -414,7 +442,7 @@ } // update '3gix' chunk pData = (uint8_t*) pCk3gix->LoadChunkData(); - memcpy(&pData[0], &iSampleGroup, 2); + store16(&pData[0], iSampleGroup); } /// Scans compressed samples for mandatory informations (e.g. actual number of total sample points). @@ -1338,6 +1366,11 @@ if (lfo3ctrl & 0x40) // bit 6 VCFType = vcf_type_lowpassturbo; } + if (_3ewa->RemainingBytes() >= 8) { + _3ewa->Read(DimensionUpperLimits, 1, 8); + } else { + memset(DimensionUpperLimits, 0, 8); + } } else { // '3ewa' chunk does not exist yet // use default values LFO3Frequency = 1.0; @@ -1418,6 +1451,7 @@ VCFVelocityDynamicRange = 0x04; VCFVelocityCurve = curve_type_linear; VCFType = vcf_type_lowpass; + memset(DimensionUpperLimits, 0, 8); } pVelocityAttenuationTable = GetVelocityTable(VelocityResponseCurve, @@ -1473,104 +1507,104 @@ // update '3ewa' chunk with DimensionRegion's current settings - const uint32_t unknown = _3ewa->GetSize(); // unknown, always chunk size ? - memcpy(&pData[0], &unknown, 4); + const uint32_t chunksize = _3ewa->GetSize(); + store32(&pData[0], chunksize); // unknown, always chunk size? const int32_t lfo3freq = (int32_t) GIG_EXP_ENCODE(LFO3Frequency); - memcpy(&pData[4], &lfo3freq, 4); + store32(&pData[4], lfo3freq); const int32_t eg3attack = (int32_t) GIG_EXP_ENCODE(EG3Attack); - memcpy(&pData[8], &eg3attack, 4); + store32(&pData[8], eg3attack); // next 2 bytes unknown - memcpy(&pData[14], &LFO1InternalDepth, 2); + store16(&pData[14], LFO1InternalDepth); // next 2 bytes unknown - memcpy(&pData[18], &LFO3InternalDepth, 2); + store16(&pData[18], LFO3InternalDepth); // next 2 bytes unknown - memcpy(&pData[22], &LFO1ControlDepth, 2); + store16(&pData[22], LFO1ControlDepth); // next 2 bytes unknown - memcpy(&pData[26], &LFO3ControlDepth, 2); + store16(&pData[26], LFO3ControlDepth); const int32_t eg1attack = (int32_t) GIG_EXP_ENCODE(EG1Attack); - memcpy(&pData[28], &eg1attack, 4); + store32(&pData[28], eg1attack); const int32_t eg1decay1 = (int32_t) GIG_EXP_ENCODE(EG1Decay1); - memcpy(&pData[32], &eg1decay1, 4); + store32(&pData[32], eg1decay1); // next 2 bytes unknown - memcpy(&pData[38], &EG1Sustain, 2); + store16(&pData[38], EG1Sustain); const int32_t eg1release = (int32_t) GIG_EXP_ENCODE(EG1Release); - memcpy(&pData[40], &eg1release, 4); + store32(&pData[40], eg1release); const uint8_t eg1ctl = (uint8_t) EncodeLeverageController(EG1Controller); - memcpy(&pData[44], &eg1ctl, 1); + pData[44] = eg1ctl; const uint8_t eg1ctrloptions = (EG1ControllerInvert) ? 0x01 : 0x00 | GIG_EG_CTR_ATTACK_INFLUENCE_ENCODE(EG1ControllerAttackInfluence) | GIG_EG_CTR_DECAY_INFLUENCE_ENCODE(EG1ControllerDecayInfluence) | GIG_EG_CTR_RELEASE_INFLUENCE_ENCODE(EG1ControllerReleaseInfluence); - memcpy(&pData[45], &eg1ctrloptions, 1); + pData[45] = eg1ctrloptions; const uint8_t eg2ctl = (uint8_t) EncodeLeverageController(EG2Controller); - memcpy(&pData[46], &eg2ctl, 1); + pData[46] = eg2ctl; const uint8_t eg2ctrloptions = (EG2ControllerInvert) ? 0x01 : 0x00 | GIG_EG_CTR_ATTACK_INFLUENCE_ENCODE(EG2ControllerAttackInfluence) | GIG_EG_CTR_DECAY_INFLUENCE_ENCODE(EG2ControllerDecayInfluence) | GIG_EG_CTR_RELEASE_INFLUENCE_ENCODE(EG2ControllerReleaseInfluence); - memcpy(&pData[47], &eg2ctrloptions, 1); + pData[47] = eg2ctrloptions; const int32_t lfo1freq = (int32_t) GIG_EXP_ENCODE(LFO1Frequency); - memcpy(&pData[48], &lfo1freq, 4); + store32(&pData[48], lfo1freq); const int32_t eg2attack = (int32_t) GIG_EXP_ENCODE(EG2Attack); - memcpy(&pData[52], &eg2attack, 4); + store32(&pData[52], eg2attack); const int32_t eg2decay1 = (int32_t) GIG_EXP_ENCODE(EG2Decay1); - memcpy(&pData[56], &eg2decay1, 4); + store32(&pData[56], eg2decay1); // next 2 bytes unknown - memcpy(&pData[62], &EG2Sustain, 2); + store16(&pData[62], EG2Sustain); const int32_t eg2release = (int32_t) GIG_EXP_ENCODE(EG2Release); - memcpy(&pData[64], &eg2release, 4); + store32(&pData[64], eg2release); // next 2 bytes unknown - memcpy(&pData[70], &LFO2ControlDepth, 2); + store16(&pData[70], LFO2ControlDepth); const int32_t lfo2freq = (int32_t) GIG_EXP_ENCODE(LFO2Frequency); - memcpy(&pData[72], &lfo2freq, 4); + store32(&pData[72], lfo2freq); // next 2 bytes unknown - memcpy(&pData[78], &LFO2InternalDepth, 2); + store16(&pData[78], LFO2InternalDepth); const int32_t eg1decay2 = (int32_t) (EG1InfiniteSustain) ? 0x7fffffff : (int32_t) GIG_EXP_ENCODE(EG1Decay2); - memcpy(&pData[80], &eg1decay2, 4); + store32(&pData[80], eg1decay2); // next 2 bytes unknown - memcpy(&pData[86], &EG1PreAttack, 2); + store16(&pData[86], EG1PreAttack); const int32_t eg2decay2 = (int32_t) (EG2InfiniteSustain) ? 0x7fffffff : (int32_t) GIG_EXP_ENCODE(EG2Decay2); - memcpy(&pData[88], &eg2decay2, 4); + store32(&pData[88], eg2decay2); // next 2 bytes unknown - memcpy(&pData[94], &EG2PreAttack, 2); + store16(&pData[94], EG2PreAttack); { if (VelocityResponseDepth > 4) throw Exception("VelocityResponseDepth must be between 0 and 4"); @@ -1588,7 +1622,7 @@ default: throw Exception("Could not update DimensionRegion's chunk, unknown VelocityResponseCurve selected"); } - memcpy(&pData[96], &velocityresponse, 1); + pData[96] = velocityresponse; } { @@ -1607,16 +1641,16 @@ default: throw Exception("Could not update DimensionRegion's chunk, unknown ReleaseVelocityResponseCurve selected"); } - memcpy(&pData[97], &releasevelocityresponse, 1); + pData[97] = releasevelocityresponse; } - memcpy(&pData[98], &VelocityResponseCurveScaling, 1); + pData[98] = VelocityResponseCurveScaling; - memcpy(&pData[99], &AttenuationControllerThreshold, 1); + pData[99] = AttenuationControllerThreshold; // next 4 bytes unknown - memcpy(&pData[104], &SampleStartOffset, 2); + store16(&pData[104], SampleStartOffset); // next 2 bytes unknown @@ -1635,14 +1669,14 @@ default: throw Exception("Could not update DimensionRegion's chunk, unknown DimensionBypass selected"); } - memcpy(&pData[108], &pitchTrackDimensionBypass, 1); + pData[108] = pitchTrackDimensionBypass; } const uint8_t pan = (Pan >= 0) ? Pan : ((-Pan) + 63); // signed 8 bit -> signed 7 bit - memcpy(&pData[109], &pan, 1); + pData[109] = pan; const uint8_t selfmask = (SelfMask) ? 0x01 : 0x00; - memcpy(&pData[110], &selfmask, 1); + pData[110] = selfmask; // next byte unknown @@ -1651,18 +1685,18 @@ if (LFO3Sync) lfo3ctrl |= 0x20; // bit 5 if (InvertAttenuationController) lfo3ctrl |= 0x80; // bit 7 if (VCFType == vcf_type_lowpassturbo) lfo3ctrl |= 0x40; // bit 6 - memcpy(&pData[112], &lfo3ctrl, 1); + pData[112] = lfo3ctrl; } const uint8_t attenctl = EncodeLeverageController(AttenuationController); - memcpy(&pData[113], &attenctl, 1); + pData[113] = attenctl; { uint8_t lfo2ctrl = LFO2Controller & 0x07; // lower 3 bits if (LFO2FlipPhase) lfo2ctrl |= 0x80; // bit 7 if (LFO2Sync) lfo2ctrl |= 0x20; // bit 5 if (VCFResonanceController != vcf_res_ctrl_none) lfo2ctrl |= 0x40; // bit 6 - memcpy(&pData[114], &lfo2ctrl, 1); + pData[114] = lfo2ctrl; } { @@ -1671,64 +1705,68 @@ if (LFO1Sync) lfo1ctrl |= 0x40; // bit 6 if (VCFResonanceController != vcf_res_ctrl_none) lfo1ctrl |= GIG_VCF_RESONANCE_CTRL_ENCODE(VCFResonanceController); - memcpy(&pData[115], &lfo1ctrl, 1); + pData[115] = lfo1ctrl; } const uint16_t eg3depth = (EG3Depth >= 0) ? EG3Depth : uint16_t(((-EG3Depth) - 1) ^ 0xffff); /* binary complementary for negatives */ - memcpy(&pData[116], &eg3depth, 1); + pData[116] = eg3depth; // next 2 bytes unknown const uint8_t channeloffset = ChannelOffset * 4; - memcpy(&pData[120], &channeloffset, 1); + pData[120] = channeloffset; { uint8_t regoptions = 0; if (MSDecode) regoptions |= 0x01; // bit 0 if (SustainDefeat) regoptions |= 0x02; // bit 1 - memcpy(&pData[121], ®options, 1); + pData[121] = regoptions; } // next 2 bytes unknown - memcpy(&pData[124], &VelocityUpperLimit, 1); + pData[124] = VelocityUpperLimit; // next 3 bytes unknown - memcpy(&pData[128], &ReleaseTriggerDecay, 1); + pData[128] = ReleaseTriggerDecay; // next 2 bytes unknown const uint8_t eg1hold = (EG1Hold) ? 0x80 : 0x00; // bit 7 - memcpy(&pData[131], &eg1hold, 1); + pData[131] = eg1hold; const uint8_t vcfcutoff = (VCFEnabled) ? 0x80 : 0x00 | /* bit 7 */ (VCFCutoff & 0x7f); /* lower 7 bits */ - memcpy(&pData[132], &vcfcutoff, 1); + pData[132] = vcfcutoff; - memcpy(&pData[133], &VCFCutoffController, 1); + pData[133] = VCFCutoffController; const uint8_t vcfvelscale = (VCFCutoffControllerInvert) ? 0x80 : 0x00 | /* bit 7 */ (VCFVelocityScale & 0x7f); /* lower 7 bits */ - memcpy(&pData[134], &vcfvelscale, 1); + pData[134] = vcfvelscale; // next byte unknown const uint8_t vcfresonance = (VCFResonanceDynamic) ? 0x00 : 0x80 | /* bit 7 */ (VCFResonance & 0x7f); /* lower 7 bits */ - memcpy(&pData[136], &vcfresonance, 1); + pData[136] = vcfresonance; const uint8_t vcfbreakpoint = (VCFKeyboardTracking) ? 0x80 : 0x00 | /* bit 7 */ (VCFKeyboardTrackingBreakpoint & 0x7f); /* lower 7 bits */ - memcpy(&pData[137], &vcfbreakpoint, 1); + pData[137] = vcfbreakpoint; const uint8_t vcfvelocity = VCFVelocityDynamicRange % 5 | VCFVelocityCurve * 5; - memcpy(&pData[138], &vcfvelocity, 1); + pData[138] = vcfvelocity; const uint8_t vcftype = (VCFType == vcf_type_lowpassturbo) ? vcf_type_lowpass : VCFType; - memcpy(&pData[139], &vcftype, 1); + pData[139] = vcftype; + + if (chunksize >= 148) { + memcpy(&pData[140], DimensionUpperLimits, 8); + } } // get the corresponding velocity table from the table map or create & calculate that table if it doesn't exist yet @@ -2070,8 +2108,6 @@ // * Region::Region(Instrument* pInstrument, RIFF::List* rgnList) : DLS::Region((DLS::Instrument*) pInstrument, rgnList) { - pInfo->UseFixedLengthStrings = true; - // Initialization Dimensions = 0; for (int i = 0; i < 256; i++) { @@ -2105,16 +2141,8 @@ pDimensionDefinitions[i].dimension = dimension; pDimensionDefinitions[i].bits = bits; pDimensionDefinitions[i].zones = zones ? zones : 0x01 << bits; // = pow(2,bits) - pDimensionDefinitions[i].split_type = (dimension == dimension_layer || - dimension == dimension_samplechannel || - dimension == dimension_releasetrigger || - dimension == dimension_keyboard || - dimension == dimension_roundrobin || - dimension == dimension_random) ? split_type_bit - : split_type_normal; - pDimensionDefinitions[i].zone_size = - (pDimensionDefinitions[i].split_type == split_type_normal) ? 128.0 / pDimensionDefinitions[i].zones - : 0; + pDimensionDefinitions[i].split_type = __resolveSplitType(dimension); + pDimensionDefinitions[i].zone_size = __resolveZoneSize(pDimensionDefinitions[i]); Dimensions++; // if this is a layer dimension, remember the amount of layers @@ -2140,6 +2168,8 @@ if (file->pWavePoolTable) pDimensionRegions[i]->pSample = GetSampleFromWavePool(wavepoolindex); } GetSample(); // load global region sample reference + } else { + DimensionRegions = 0; } // make sure there is at least one dimension region @@ -2162,6 +2192,12 @@ * @throws gig::Exception if samples cannot be dereferenced */ void Region::UpdateChunks() { + // in the gig format we don't care about the Region's sample reference + // but we still have to provide some existing one to not corrupt the + // file, so to avoid the latter we simply always assign the sample of + // the first dimension region of this region + pSample = pDimensionRegions[0]->pSample; + // first update base class's chunks DLS::Region::UpdateChunks(); @@ -2183,7 +2219,7 @@ // update dimension definitions in '3lnk' chunk uint8_t* pData = (uint8_t*) _3lnk->LoadChunkData(); - memcpy(&pData[0], &DimensionRegions, 4); + store32(&pData[0], DimensionRegions); for (int i = 0; i < iMaxDimensions; i++) { pData[4 + i * 8] = (uint8_t) pDimensionDefinitions[i].dimension; pData[5 + i * 8] = pDimensionDefinitions[i].bits; @@ -2208,7 +2244,7 @@ } if (iWaveIndex < 0) throw gig::Exception("Could not update gig::Region, could not find DimensionRegion's sample"); } - memcpy(&pData[iWavePoolOffset + i * 4], &iWaveIndex, 4); + store32(&pData[iWavePoolOffset + i * 4], iWaveIndex); } } @@ -2248,7 +2284,8 @@ int dim[8] = { 0 }; for (int i = 0 ; i < DimensionRegions ; i++) { - if (pDimensionRegions[i]->VelocityUpperLimit) { + if (pDimensionRegions[i]->DimensionUpperLimits[veldim] || + pDimensionRegions[i]->VelocityUpperLimit) { // create the velocity table uint8_t* table = pDimensionRegions[i]->VelocityTable; if (!table) { @@ -2257,10 +2294,18 @@ } int tableidx = 0; int velocityZone = 0; - for (int k = i ; k < end ; k += step) { - DimensionRegion *d = pDimensionRegions[k]; - for (; tableidx <= d->VelocityUpperLimit ; tableidx++) table[tableidx] = velocityZone; - velocityZone++; + if (pDimensionRegions[i]->DimensionUpperLimits[veldim]) { // gig3 + for (int k = i ; k < end ; k += step) { + DimensionRegion *d = pDimensionRegions[k]; + for (; tableidx <= d->DimensionUpperLimits[veldim] ; tableidx++) table[tableidx] = velocityZone; + velocityZone++; + } + } else { // gig2 + for (int k = i ; k < end ; k += step) { + DimensionRegion *d = pDimensionRegions[k]; + for (; tableidx <= d->VelocityUpperLimit ; tableidx++) table[tableidx] = velocityZone; + velocityZone++; + } } } else { if (pDimensionRegions[i]->VelocityTable) { @@ -2327,6 +2372,12 @@ // assign definition of new dimension pDimensionDefinitions[Dimensions] = *pDimDef; + // auto correct certain dimension definition fields (where possible) + pDimensionDefinitions[Dimensions].split_type = + __resolveSplitType(pDimensionDefinitions[Dimensions].dimension); + pDimensionDefinitions[Dimensions].zone_size = + __resolveZoneSize(pDimensionDefinitions[Dimensions]); + // create new dimension region(s) for this new dimension for (int i = 1 << iCurrentBits; i < 1 << iNewBits; i++) { //TODO: maybe we should copy existing dimension regions if possible instead of simply creating new ones with default values @@ -2455,7 +2506,15 @@ } else { switch (pDimensionDefinitions[i].split_type) { case split_type_normal: - bits = uint8_t(DimValues[i] / pDimensionDefinitions[i].zone_size); + if (pDimensionRegions[0]->DimensionUpperLimits[i]) { + // gig3: all normal dimensions (not just the velocity dimension) have custom zone ranges + for (bits = 0 ; bits < pDimensionDefinitions[i].zones ; bits++) { + if (DimValues[i] <= pDimensionRegions[bits << bitpos]->DimensionUpperLimits[i]) break; + } + } else { + // gig2: evenly sized zones + bits = uint8_t(DimValues[i] / pDimensionDefinitions[i].zone_size); + } break; case split_type_bit: // the value is already the sought dimension bit number const uint8_t limiter_mask = (0xff << pDimensionDefinitions[i].bits) ^ 0xff; @@ -2469,7 +2528,7 @@ DimensionRegion* dimreg = pDimensionRegions[dimregidx]; if (veldim != -1) { // (dimreg is now the dimension region for the lowest velocity) - if (dimreg->VelocityUpperLimit) // custom defined zone ranges + if (dimreg->VelocityTable) // custom defined zone ranges bits = dimreg->VelocityTable[DimValues[veldim]]; else // normal split type bits = uint8_t(DimValues[veldim] / pDimensionDefinitions[veldim].zone_size); @@ -2535,7 +2594,12 @@ // * Instrument::Instrument(File* pFile, RIFF::List* insList, progress_t* pProgress) : DLS::Instrument((DLS::File*)pFile, insList) { - pInfo->UseFixedLengthStrings = true; + static const DLS::Info::FixedStringLength fixedStringLengths[] = { + { CHUNK_ID_INAM, 64 }, + { CHUNK_ID_ISFT, 12 }, + { 0, 0 } + }; + pInfo->FixedStringLengths = fixedStringLengths; // Initialization for (int i = 0; i < 128; i++) RegionKeyTable[i] = NULL; @@ -2617,14 +2681,14 @@ if (!_3ewg) _3ewg = lart->AddSubChunk(CHUNK_ID_3EWG, 12); // update '3ewg' RIFF chunk uint8_t* pData = (uint8_t*) _3ewg->LoadChunkData(); - memcpy(&pData[0], &EffectSend, 2); - memcpy(&pData[2], &Attenuation, 4); - memcpy(&pData[6], &FineTune, 2); - memcpy(&pData[8], &PitchbendRange, 2); + store16(&pData[0], EffectSend); + store32(&pData[2], Attenuation); + store16(&pData[6], FineTune); + store16(&pData[8], PitchbendRange); const uint8_t dimkeystart = (PianoReleaseMode) ? 0x01 : 0x00 | DimensionKeyRange.low << 1; - memcpy(&pData[10], &dimkeystart, 1); - memcpy(&pData[11], &DimensionKeyRange.high, 1); + pData[10] = dimkeystart; + pData[11] = DimensionKeyRange.high; } /** @@ -2711,12 +2775,17 @@ } Group::~Group() { + // remove the chunk associated with this group (if any) + if (pNameChunk) pNameChunk->GetParent()->DeleteSubChunk(pNameChunk); } /** @brief Update chunks with current group settings. * - * Apply current Group field values to the respective. You have to call - * File::Save() to make changes persistent. + * Apply current Group field values to the respective chunks. You have + * to call File::Save() to make changes persistent. + * + * Usually there is absolutely no need to call this method explicitly. + * It will be called automatically when File::Save() was called. */ void Group::UpdateChunks() { // make sure <3gri> and <3gnl> list chunks exist @@ -2799,14 +2868,35 @@ // *************** File *************** // * + const DLS::Info::FixedStringLength File::FixedStringLengths[] = { + { CHUNK_ID_IARL, 256 }, + { CHUNK_ID_IART, 128 }, + { CHUNK_ID_ICMS, 128 }, + { CHUNK_ID_ICMT, 1024 }, + { CHUNK_ID_ICOP, 128 }, + { CHUNK_ID_ICRD, 128 }, + { CHUNK_ID_IENG, 128 }, + { CHUNK_ID_IGNR, 128 }, + { CHUNK_ID_IKEY, 128 }, + { CHUNK_ID_IMED, 128 }, + { CHUNK_ID_INAM, 128 }, + { CHUNK_ID_IPRD, 128 }, + { CHUNK_ID_ISBJ, 128 }, + { CHUNK_ID_ISFT, 128 }, + { CHUNK_ID_ISRC, 128 }, + { CHUNK_ID_ISRF, 128 }, + { CHUNK_ID_ITCH, 128 }, + { 0, 0 } + }; + File::File() : DLS::File() { pGroups = NULL; - pInfo->UseFixedLengthStrings = true; + pInfo->FixedStringLengths = FixedStringLengths; } File::File(RIFF::File* pRIFF) : DLS::File(pRIFF) { pGroups = NULL; - pInfo->UseFixedLengthStrings = true; + pInfo->FixedStringLengths = FixedStringLengths; } File::~File() { @@ -2864,6 +2954,7 @@ if (!pSamples || !pSamples->size()) throw gig::Exception("Could not delete sample as there are no samples"); SampleList::iterator iter = find(pSamples->begin(), pSamples->end(), (DLS::Sample*) pSample); if (iter == pSamples->end()) throw gig::Exception("Could not delete sample, could not find given sample"); + if (SamplesIterator != pSamples->end() && *SamplesIterator == pSample) ++SamplesIterator; // avoid iterator invalidation pSamples->erase(iter); delete pSample; } @@ -2875,7 +2966,7 @@ void File::LoadSamples(progress_t* pProgress) { // Groups must be loaded before samples, because samples will try // to resolve the group they belong to - LoadGroups(); + if (!pGroups) LoadGroups(); if (!pSamples) pSamples = new SampleList; @@ -3000,7 +3091,7 @@ * have to call Save() to make this persistent to the file. * * @param pInstrument - instrument to delete - * @throws gig::Excption if given instrument could not be found + * @throws gig::Exception if given instrument could not be found */ void File::DeleteInstrument(Instrument* pInstrument) { if (!pInstruments) throw gig::Exception("Could not delete instrument as there are no instruments"); @@ -3078,11 +3169,44 @@ return pGroup; } + /** @brief Delete a group and its samples. + * + * This will delete the given Group object and all the samples that + * belong to this group from the gig file. You have to call Save() to + * make this persistent to the file. + * + * @param pGroup - group to delete + * @throws gig::Exception if given group could not be found + */ void File::DeleteGroup(Group* pGroup) { if (!pGroups) LoadGroups(); std::list::iterator iter = find(pGroups->begin(), pGroups->end(), pGroup); if (iter == pGroups->end()) throw gig::Exception("Could not delete group, could not find given group"); if (pGroups->size() == 1) throw gig::Exception("Cannot delete group, there must be at least one default group!"); + // delete all members of this group + for (Sample* pSample = pGroup->GetFirstSample(); pSample; pSample = pGroup->GetNextSample()) { + DeleteSample(pSample); + } + // now delete this group object + pGroups->erase(iter); + delete pGroup; + } + + /** @brief Delete a group. + * + * This will delete the given Group object from the gig file. All the + * samples that belong to this group will not be deleted, but instead + * be moved to another group. You have to call Save() to make this + * persistent to the file. + * + * @param pGroup - group to delete + * @throws gig::Exception if given group could not be found + */ + void File::DeleteGroupOnly(Group* pGroup) { + if (!pGroups) LoadGroups(); + std::list::iterator iter = find(pGroups->begin(), pGroups->end(), pGroup); + if (iter == pGroups->end()) throw gig::Exception("Could not delete group, could not find given group"); + if (pGroups->size() == 1) throw gig::Exception("Cannot delete group, there must be at least one default group!"); // move all members of this group to another group pGroup->MoveAll(); pGroups->erase(iter); @@ -3113,6 +3237,30 @@ } } + /** + * Apply all the gig file's current instruments, samples, groups and settings + * to the respective RIFF chunks. You have to call Save() to make changes + * persistent. + * + * Usually there is absolutely no need to call this method explicitly. + * It will be called automatically when File::Save() was called. + * + * @throws Exception - on errors + */ + void File::UpdateChunks() { + // first update base class's chunks + DLS::File::UpdateChunks(); + + // update group's chunks + if (pGroups) { + std::list::iterator iter = pGroups->begin(); + std::list::iterator end = pGroups->end(); + for (; iter != end; ++iter) { + (*iter)->UpdateChunks(); + } + } + } + // *************** Exception ***************