--- libgig/trunk/src/gig.cpp 2013/11/25 02:22:38 2482 +++ libgig/trunk/src/gig.cpp 2014/05/17 23:31:20 2557 @@ -2,7 +2,7 @@ * * * libgig - C++ cross-platform Gigasampler format file access library * * * - * Copyright (C) 2003-2013 by Christian Schoenebeck * + * Copyright (C) 2003-2014 by Christian Schoenebeck * * * * * * This library is free software; you can redistribute it and/or modify * @@ -28,6 +28,7 @@ #include #include #include +#include /// Initial size of the sample buffer which is used for decompression of /// compressed sample wave streams - this value should always be bigger than @@ -581,6 +582,14 @@ // update '3gix' chunk pData = (uint8_t*) pCk3gix->LoadChunkData(); store16(&pData[0], iSampleGroup); + + // if the library user toggled the "Compressed" attribute from true to + // false, then the EWAV chunk associated with compressed samples needs + // to be deleted + RIFF::Chunk* ewav = pWaveList->GetSubChunk(CHUNK_ID_EWAV); + if (ewav && !Compressed) { + pWaveList->DeleteSubChunk(ewav); + } } /// Scans compressed samples for mandatory informations (e.g. actual number of total sample points). @@ -1700,12 +1709,14 @@ //NOTE: copy code copied from assignment constructor above, see comment there as well *this = *orig; // default memberwise shallow copy of all parameters + + // restore members that shall not be altered pParentList = p; // restore the chunk pointer + pRegion = pOriginalRegion; - // only take the raw sample reference & parent region reference if the + // only take the raw sample reference reference if the // two DimensionRegion objects are part of the same file if (pOriginalRegion->GetParent()->GetParent() != orig->pRegion->GetParent()->GetParent()) { - pRegion = pOriginalRegion; pSample = pOriginalSample; } @@ -2079,6 +2090,12 @@ return pRegion; } +// show error if some _lev_ctrl_* enum entry is not listed in the following function +// (commented out for now, because "diagnostic push" not supported prior GCC 4.6) +// TODO: uncomment and add a GCC version check (see also commented "#pragma GCC diagnostic pop" below) +//#pragma GCC diagnostic push +//#pragma GCC diagnostic error "-Wswitch" + leverage_ctrl_t DimensionRegion::DecodeLeverageController(_lev_ctrl_t EncodedController) { leverage_ctrl_t decodedcontroller; switch (EncodedController) { @@ -2190,12 +2207,255 @@ decodedcontroller.controller_number = 95; break; + // format extension (these controllers are so far only supported by + // LinuxSampler & gigedit) they will *NOT* work with + // Gigasampler/GigaStudio ! + case _lev_ctrl_CC3_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 3; + break; + case _lev_ctrl_CC6_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 6; + break; + case _lev_ctrl_CC7_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 7; + break; + case _lev_ctrl_CC8_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 8; + break; + case _lev_ctrl_CC9_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 9; + break; + case _lev_ctrl_CC10_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 10; + break; + case _lev_ctrl_CC11_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 11; + break; + case _lev_ctrl_CC14_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 14; + break; + case _lev_ctrl_CC15_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 15; + break; + case _lev_ctrl_CC20_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 20; + break; + case _lev_ctrl_CC21_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 21; + break; + case _lev_ctrl_CC22_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 22; + break; + case _lev_ctrl_CC23_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 23; + break; + case _lev_ctrl_CC24_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 24; + break; + case _lev_ctrl_CC25_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 25; + break; + case _lev_ctrl_CC26_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 26; + break; + case _lev_ctrl_CC27_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 27; + break; + case _lev_ctrl_CC28_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 28; + break; + case _lev_ctrl_CC29_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 29; + break; + case _lev_ctrl_CC30_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 30; + break; + case _lev_ctrl_CC31_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 31; + break; + case _lev_ctrl_CC68_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 68; + break; + case _lev_ctrl_CC69_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 69; + break; + case _lev_ctrl_CC70_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 70; + break; + case _lev_ctrl_CC71_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 71; + break; + case _lev_ctrl_CC72_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 72; + break; + case _lev_ctrl_CC73_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 73; + break; + case _lev_ctrl_CC74_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 74; + break; + case _lev_ctrl_CC75_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 75; + break; + case _lev_ctrl_CC76_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 76; + break; + case _lev_ctrl_CC77_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 77; + break; + case _lev_ctrl_CC78_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 78; + break; + case _lev_ctrl_CC79_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 79; + break; + case _lev_ctrl_CC84_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 84; + break; + case _lev_ctrl_CC85_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 85; + break; + case _lev_ctrl_CC86_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 86; + break; + case _lev_ctrl_CC87_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 87; + break; + case _lev_ctrl_CC89_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 89; + break; + case _lev_ctrl_CC90_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 90; + break; + case _lev_ctrl_CC96_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 96; + break; + case _lev_ctrl_CC97_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 97; + break; + case _lev_ctrl_CC102_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 102; + break; + case _lev_ctrl_CC103_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 103; + break; + case _lev_ctrl_CC104_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 104; + break; + case _lev_ctrl_CC105_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 105; + break; + case _lev_ctrl_CC106_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 106; + break; + case _lev_ctrl_CC107_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 107; + break; + case _lev_ctrl_CC108_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 108; + break; + case _lev_ctrl_CC109_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 109; + break; + case _lev_ctrl_CC110_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 110; + break; + case _lev_ctrl_CC111_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 111; + break; + case _lev_ctrl_CC112_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 112; + break; + case _lev_ctrl_CC113_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 113; + break; + case _lev_ctrl_CC114_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 114; + break; + case _lev_ctrl_CC115_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 115; + break; + case _lev_ctrl_CC116_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 116; + break; + case _lev_ctrl_CC117_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 117; + break; + case _lev_ctrl_CC118_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 118; + break; + case _lev_ctrl_CC119_EXT: + decodedcontroller.type = leverage_ctrl_t::type_controlchange; + decodedcontroller.controller_number = 119; + break; + // unknown controller type default: throw gig::Exception("Unknown leverage controller type."); } return decodedcontroller; } + +// see above (diagnostic push not supported prior GCC 4.6) +//#pragma GCC diagnostic pop DimensionRegion::_lev_ctrl_t DimensionRegion::EncodeLeverageController(leverage_ctrl_t DecodedController) { _lev_ctrl_t encodedcontroller; @@ -2283,6 +2543,188 @@ case 95: encodedcontroller = _lev_ctrl_effect5depth; break; + + // format extension (these controllers are so far only + // supported by LinuxSampler & gigedit) they will *NOT* + // work with Gigasampler/GigaStudio ! + case 3: + encodedcontroller = _lev_ctrl_CC3_EXT; + break; + case 6: + encodedcontroller = _lev_ctrl_CC6_EXT; + break; + case 7: + encodedcontroller = _lev_ctrl_CC7_EXT; + break; + case 8: + encodedcontroller = _lev_ctrl_CC8_EXT; + break; + case 9: + encodedcontroller = _lev_ctrl_CC9_EXT; + break; + case 10: + encodedcontroller = _lev_ctrl_CC10_EXT; + break; + case 11: + encodedcontroller = _lev_ctrl_CC11_EXT; + break; + case 14: + encodedcontroller = _lev_ctrl_CC14_EXT; + break; + case 15: + encodedcontroller = _lev_ctrl_CC15_EXT; + break; + case 20: + encodedcontroller = _lev_ctrl_CC20_EXT; + break; + case 21: + encodedcontroller = _lev_ctrl_CC21_EXT; + break; + case 22: + encodedcontroller = _lev_ctrl_CC22_EXT; + break; + case 23: + encodedcontroller = _lev_ctrl_CC23_EXT; + break; + case 24: + encodedcontroller = _lev_ctrl_CC24_EXT; + break; + case 25: + encodedcontroller = _lev_ctrl_CC25_EXT; + break; + case 26: + encodedcontroller = _lev_ctrl_CC26_EXT; + break; + case 27: + encodedcontroller = _lev_ctrl_CC27_EXT; + break; + case 28: + encodedcontroller = _lev_ctrl_CC28_EXT; + break; + case 29: + encodedcontroller = _lev_ctrl_CC29_EXT; + break; + case 30: + encodedcontroller = _lev_ctrl_CC30_EXT; + break; + case 31: + encodedcontroller = _lev_ctrl_CC31_EXT; + break; + case 68: + encodedcontroller = _lev_ctrl_CC68_EXT; + break; + case 69: + encodedcontroller = _lev_ctrl_CC69_EXT; + break; + case 70: + encodedcontroller = _lev_ctrl_CC70_EXT; + break; + case 71: + encodedcontroller = _lev_ctrl_CC71_EXT; + break; + case 72: + encodedcontroller = _lev_ctrl_CC72_EXT; + break; + case 73: + encodedcontroller = _lev_ctrl_CC73_EXT; + break; + case 74: + encodedcontroller = _lev_ctrl_CC74_EXT; + break; + case 75: + encodedcontroller = _lev_ctrl_CC75_EXT; + break; + case 76: + encodedcontroller = _lev_ctrl_CC76_EXT; + break; + case 77: + encodedcontroller = _lev_ctrl_CC77_EXT; + break; + case 78: + encodedcontroller = _lev_ctrl_CC78_EXT; + break; + case 79: + encodedcontroller = _lev_ctrl_CC79_EXT; + break; + case 84: + encodedcontroller = _lev_ctrl_CC84_EXT; + break; + case 85: + encodedcontroller = _lev_ctrl_CC85_EXT; + break; + case 86: + encodedcontroller = _lev_ctrl_CC86_EXT; + break; + case 87: + encodedcontroller = _lev_ctrl_CC87_EXT; + break; + case 89: + encodedcontroller = _lev_ctrl_CC89_EXT; + break; + case 90: + encodedcontroller = _lev_ctrl_CC90_EXT; + break; + case 96: + encodedcontroller = _lev_ctrl_CC96_EXT; + break; + case 97: + encodedcontroller = _lev_ctrl_CC97_EXT; + break; + case 102: + encodedcontroller = _lev_ctrl_CC102_EXT; + break; + case 103: + encodedcontroller = _lev_ctrl_CC103_EXT; + break; + case 104: + encodedcontroller = _lev_ctrl_CC104_EXT; + break; + case 105: + encodedcontroller = _lev_ctrl_CC105_EXT; + break; + case 106: + encodedcontroller = _lev_ctrl_CC106_EXT; + break; + case 107: + encodedcontroller = _lev_ctrl_CC107_EXT; + break; + case 108: + encodedcontroller = _lev_ctrl_CC108_EXT; + break; + case 109: + encodedcontroller = _lev_ctrl_CC109_EXT; + break; + case 110: + encodedcontroller = _lev_ctrl_CC110_EXT; + break; + case 111: + encodedcontroller = _lev_ctrl_CC111_EXT; + break; + case 112: + encodedcontroller = _lev_ctrl_CC112_EXT; + break; + case 113: + encodedcontroller = _lev_ctrl_CC113_EXT; + break; + case 114: + encodedcontroller = _lev_ctrl_CC114_EXT; + break; + case 115: + encodedcontroller = _lev_ctrl_CC115_EXT; + break; + case 116: + encodedcontroller = _lev_ctrl_CC116_EXT; + break; + case 117: + encodedcontroller = _lev_ctrl_CC117_EXT; + break; + case 118: + encodedcontroller = _lev_ctrl_CC118_EXT; + break; + case 119: + encodedcontroller = _lev_ctrl_CC119_EXT; + break; + default: throw gig::Exception("leverage controller number is not supported by the gig format"); } @@ -2760,6 +3202,18 @@ * dimension bits limit is violated */ void Region::AddDimension(dimension_def_t* pDimDef) { + // some initial sanity checks of the given dimension definition + if (pDimDef->zones < 2) + throw gig::Exception("Could not add new dimension, amount of requested zones must always be at least two"); + if (pDimDef->bits < 1) + throw gig::Exception("Could not add new dimension, amount of requested requested zone bits must always be at least one"); + if (pDimDef->dimension == dimension_samplechannel) { + if (pDimDef->zones != 2) + throw gig::Exception("Could not add new 'sample channel' dimensions, the requested amount of zones must always be 2 for this dimension type"); + if (pDimDef->bits != 1) + throw gig::Exception("Could not add new 'sample channel' dimensions, the requested amount of zone bits must always be 1 for this dimension type"); + } + // check if max. amount of dimensions reached File* file = (File*) GetParent()->GetParent(); const int iMaxDimensions = (file->pVersion && file->pVersion->major == 3) ? 8 : 5; @@ -2935,6 +3389,303 @@ if (pDimDef->dimension == dimension_layer) Layers = 1; } + /** @brief Delete one split zone of a dimension (decrement zone amount). + * + * Instead of deleting an entire dimensions, this method will only delete + * one particular split zone given by @a zone of the Region's dimension + * given by @a type. So this method will simply decrement the amount of + * zones by one of the dimension in question. To be able to do that, the + * respective dimension must exist on this Region and it must have at least + * 3 zones. All DimensionRegion objects associated with the zone will be + * deleted. + * + * @param type - identifies the dimension where a zone shall be deleted + * @param zone - index of the dimension split zone that shall be deleted + * @throws gig::Exception if requested zone could not be deleted + */ + void Region::DeleteDimensionZone(dimension_t type, int zone) { + dimension_def_t* oldDef = GetDimensionDefinition(type); + if (!oldDef) + throw gig::Exception("Could not delete dimension zone, no such dimension of given type"); + if (oldDef->zones <= 2) + throw gig::Exception("Could not delete dimension zone, because it would end up with only one zone."); + if (zone < 0 || zone >= oldDef->zones) + throw gig::Exception("Could not delete dimension zone, requested zone index out of bounds."); + + const int newZoneSize = oldDef->zones - 1; + + // create a temporary Region which just acts as a temporary copy + // container and will be deleted at the end of this function and will + // also not be visible through the API during this process + gig::Region* tempRgn = NULL; + { + // adding these temporary chunks is probably not even necessary + Instrument* instr = static_cast(GetParent()); + RIFF::List* pCkInstrument = instr->pCkInstrument; + RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN); + if (!lrgn) lrgn = pCkInstrument->AddSubList(LIST_TYPE_LRGN); + RIFF::List* rgn = lrgn->AddSubList(LIST_TYPE_RGN); + tempRgn = new Region(instr, rgn); + } + + // copy this region's dimensions (with already the dimension split size + // requested by the arguments of this method call) to the temporary + // region, and don't use Region::CopyAssign() here for this task, since + // it would also alter fast lookup helper variables here and there + dimension_def_t newDef; + for (int i = 0; i < Dimensions; ++i) { + dimension_def_t def = pDimensionDefinitions[i]; // copy, don't reference + // is this the dimension requested by the method arguments? ... + if (def.dimension == type) { // ... if yes, decrement zone amount by one + def.zones = newZoneSize; + if ((1 << (def.bits - 1)) == def.zones) def.bits--; + newDef = def; + } + tempRgn->AddDimension(&def); + } + + // find the dimension index in the tempRegion which is the dimension + // type passed to this method (paranoidly expecting different order) + int tempReducedDimensionIndex = -1; + for (int d = 0; d < tempRgn->Dimensions; ++d) { + if (tempRgn->pDimensionDefinitions[d].dimension == type) { + tempReducedDimensionIndex = d; + break; + } + } + + // copy dimension regions from this region to the temporary region + for (int iDst = 0; iDst < 256; ++iDst) { + DimensionRegion* dstDimRgn = tempRgn->pDimensionRegions[iDst]; + if (!dstDimRgn) continue; + std::map dimCase; + bool isValidZone = true; + for (int d = 0, baseBits = 0; d < tempRgn->Dimensions; ++d) { + const int dstBits = tempRgn->pDimensionDefinitions[d].bits; + dimCase[tempRgn->pDimensionDefinitions[d].dimension] = + (iDst >> baseBits) & ((1 << dstBits) - 1); + baseBits += dstBits; + // there are also DimensionRegion objects of unused zones, skip them + if (dimCase[tempRgn->pDimensionDefinitions[d].dimension] >= tempRgn->pDimensionDefinitions[d].zones) { + isValidZone = false; + break; + } + } + if (!isValidZone) continue; + // a bit paranoid: cope with the chance that the dimensions would + // have different order in source and destination regions + const bool isLastZone = (dimCase[type] == newZoneSize - 1); + if (dimCase[type] >= zone) dimCase[type]++; + DimensionRegion* srcDimRgn = GetDimensionRegionByBit(dimCase); + dstDimRgn->CopyAssign(srcDimRgn); + // if this is the upper most zone of the dimension passed to this + // method, then correct (raise) its upper limit to 127 + if (newDef.split_type == split_type_normal && isLastZone) + dstDimRgn->DimensionUpperLimits[tempReducedDimensionIndex] = 127; + } + + // now tempRegion's dimensions and DimensionRegions basically reflect + // what we wanted to get for this actual Region here, so we now just + // delete and recreate the dimension in question with the new amount + // zones and then copy back from tempRegion + DeleteDimension(oldDef); + AddDimension(&newDef); + for (int iSrc = 0; iSrc < 256; ++iSrc) { + DimensionRegion* srcDimRgn = tempRgn->pDimensionRegions[iSrc]; + if (!srcDimRgn) continue; + std::map dimCase; + for (int d = 0, baseBits = 0; d < tempRgn->Dimensions; ++d) { + const int srcBits = tempRgn->pDimensionDefinitions[d].bits; + dimCase[tempRgn->pDimensionDefinitions[d].dimension] = + (iSrc >> baseBits) & ((1 << srcBits) - 1); + baseBits += srcBits; + } + // a bit paranoid: cope with the chance that the dimensions would + // have different order in source and destination regions + DimensionRegion* dstDimRgn = GetDimensionRegionByBit(dimCase); + if (!dstDimRgn) continue; + dstDimRgn->CopyAssign(srcDimRgn); + } + + // delete temporary region + delete tempRgn; + + UpdateVelocityTable(); + } + + /** @brief Divide split zone of a dimension in two (increment zone amount). + * + * This will increment the amount of zones for the dimension (given by + * @a type) by one. It will do so by dividing the zone (given by @a zone) + * in the middle of its zone range in two. So the two zones resulting from + * the zone being splitted, will be an equivalent copy regarding all their + * articulation informations and sample reference. The two zones will only + * differ in their zone's upper limit + * (DimensionRegion::DimensionUpperLimits). + * + * @param type - identifies the dimension where a zone shall be splitted + * @param zone - index of the dimension split zone that shall be splitted + * @throws gig::Exception if requested zone could not be splitted + */ + void Region::SplitDimensionZone(dimension_t type, int zone) { + dimension_def_t* oldDef = GetDimensionDefinition(type); + if (!oldDef) + throw gig::Exception("Could not split dimension zone, no such dimension of given type"); + if (zone < 0 || zone >= oldDef->zones) + throw gig::Exception("Could not split dimension zone, requested zone index out of bounds."); + + const int newZoneSize = oldDef->zones + 1; + + // create a temporary Region which just acts as a temporary copy + // container and will be deleted at the end of this function and will + // also not be visible through the API during this process + gig::Region* tempRgn = NULL; + { + // adding these temporary chunks is probably not even necessary + Instrument* instr = static_cast(GetParent()); + RIFF::List* pCkInstrument = instr->pCkInstrument; + RIFF::List* lrgn = pCkInstrument->GetSubList(LIST_TYPE_LRGN); + if (!lrgn) lrgn = pCkInstrument->AddSubList(LIST_TYPE_LRGN); + RIFF::List* rgn = lrgn->AddSubList(LIST_TYPE_RGN); + tempRgn = new Region(instr, rgn); + } + + // copy this region's dimensions (with already the dimension split size + // requested by the arguments of this method call) to the temporary + // region, and don't use Region::CopyAssign() here for this task, since + // it would also alter fast lookup helper variables here and there + dimension_def_t newDef; + for (int i = 0; i < Dimensions; ++i) { + dimension_def_t def = pDimensionDefinitions[i]; // copy, don't reference + // is this the dimension requested by the method arguments? ... + if (def.dimension == type) { // ... if yes, increment zone amount by one + def.zones = newZoneSize; + if ((1 << oldDef->bits) < newZoneSize) def.bits++; + newDef = def; + } + tempRgn->AddDimension(&def); + } + + // find the dimension index in the tempRegion which is the dimension + // type passed to this method (paranoidly expecting different order) + int tempIncreasedDimensionIndex = -1; + for (int d = 0; d < tempRgn->Dimensions; ++d) { + if (tempRgn->pDimensionDefinitions[d].dimension == type) { + tempIncreasedDimensionIndex = d; + break; + } + } + + // copy dimension regions from this region to the temporary region + for (int iSrc = 0; iSrc < 256; ++iSrc) { + DimensionRegion* srcDimRgn = pDimensionRegions[iSrc]; + if (!srcDimRgn) continue; + std::map dimCase; + bool isValidZone = true; + for (int d = 0, baseBits = 0; d < Dimensions; ++d) { + const int srcBits = pDimensionDefinitions[d].bits; + dimCase[pDimensionDefinitions[d].dimension] = + (iSrc >> baseBits) & ((1 << srcBits) - 1); + // there are also DimensionRegion objects for unused zones, skip them + if (dimCase[pDimensionDefinitions[d].dimension] >= pDimensionDefinitions[d].zones) { + isValidZone = false; + break; + } + baseBits += srcBits; + } + if (!isValidZone) continue; + // a bit paranoid: cope with the chance that the dimensions would + // have different order in source and destination regions + if (dimCase[type] > zone) dimCase[type]++; + DimensionRegion* dstDimRgn = tempRgn->GetDimensionRegionByBit(dimCase); + dstDimRgn->CopyAssign(srcDimRgn); + // if this is the requested zone to be splitted, then also copy + // the source DimensionRegion to the newly created target zone + // and set the old zones upper limit lower + if (dimCase[type] == zone) { + // lower old zones upper limit + if (newDef.split_type == split_type_normal) { + const int high = + dstDimRgn->DimensionUpperLimits[tempIncreasedDimensionIndex]; + int low = 0; + if (zone > 0) { + std::map lowerCase = dimCase; + lowerCase[type]--; + DimensionRegion* dstDimRgnLow = tempRgn->GetDimensionRegionByBit(lowerCase); + low = dstDimRgnLow->DimensionUpperLimits[tempIncreasedDimensionIndex]; + } + dstDimRgn->DimensionUpperLimits[tempIncreasedDimensionIndex] = low + (high - low) / 2; + } + // fill the newly created zone of the divided zone as well + dimCase[type]++; + dstDimRgn = tempRgn->GetDimensionRegionByBit(dimCase); + dstDimRgn->CopyAssign(srcDimRgn); + } + } + + // now tempRegion's dimensions and DimensionRegions basically reflect + // what we wanted to get for this actual Region here, so we now just + // delete and recreate the dimension in question with the new amount + // zones and then copy back from tempRegion + DeleteDimension(oldDef); + AddDimension(&newDef); + for (int iSrc = 0; iSrc < 256; ++iSrc) { + DimensionRegion* srcDimRgn = tempRgn->pDimensionRegions[iSrc]; + if (!srcDimRgn) continue; + std::map dimCase; + for (int d = 0, baseBits = 0; d < tempRgn->Dimensions; ++d) { + const int srcBits = tempRgn->pDimensionDefinitions[d].bits; + dimCase[tempRgn->pDimensionDefinitions[d].dimension] = + (iSrc >> baseBits) & ((1 << srcBits) - 1); + baseBits += srcBits; + } + // a bit paranoid: cope with the chance that the dimensions would + // have different order in source and destination regions + DimensionRegion* dstDimRgn = GetDimensionRegionByBit(dimCase); + if (!dstDimRgn) continue; + dstDimRgn->CopyAssign(srcDimRgn); + } + + // delete temporary region + delete tempRgn; + + UpdateVelocityTable(); + } + + DimensionRegion* Region::GetDimensionRegionByBit(const std::map& DimCase) { + uint8_t bits[8] = {}; + for (std::map::const_iterator it = DimCase.begin(); + it != DimCase.end(); ++it) + { + for (int d = 0; d < Dimensions; ++d) { + if (pDimensionDefinitions[d].dimension == it->first) { + bits[d] = it->second; + goto nextDimCaseSlice; + } + } + assert(false); // do crash ... too harsh maybe ? ignore it instead ? + nextDimCaseSlice: + ; // noop + } + return GetDimensionRegionByBit(bits); + } + + /** + * Searches in the current Region for a dimension of the given dimension + * type and returns the precise configuration of that dimension in this + * Region. + * + * @param type - dimension type of the sought dimension + * @returns dimension definition or NULL if there is no dimension with + * sought type in this Region. + */ + dimension_def_t* Region::GetDimensionDefinition(dimension_t type) { + for (int i = 0; i < Dimensions; ++i) + if (pDimensionDefinitions[i].dimension == type) + return &pDimensionDefinitions[i]; + return NULL; + } + Region::~Region() { for (int i = 0; i < 256; i++) { if (pDimensionRegions[i]) delete pDimensionRegions[i]; @@ -4220,6 +4971,24 @@ return NULL; } + /** + * Returns the group with the given group name. + * + * Note: group names don't have to be unique in the gig format! So there + * can be multiple groups with the same name. This method will simply + * return the first group found with the given name. + * + * @param name - name of the sought group + * @returns sought group or NULL if there's no group with that name + */ + Group* File::GetGroup(String name) { + if (!pGroups) LoadGroups(); + GroupsIterator = pGroups->begin(); + for (uint i = 0; GroupsIterator != pGroups->end(); ++GroupsIterator, ++i) + if ((*GroupsIterator)->Name == name) return *GroupsIterator; + return NULL; + } + Group* File::AddGroup() { if (!pGroups) LoadGroups(); // there must always be at least one group