/[svn]/gigedit/trunk/src/gigedit/CombineInstrumentsDialog.cpp
ViewVC logotype

Contents of /gigedit/trunk/src/gigedit/CombineInstrumentsDialog.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2549 - (show annotations) (download)
Tue May 13 16:14:33 2014 UTC (8 years, 1 month ago) by schoenebeck
File size: 31234 byte(s)
* Fix: "combine instruments" crashed if two of the input
  instruments had a velocity dimension.

1 /*
2 Copyright (c) 2014 Christian Schoenebeck
3
4 This file is part of "gigedit" and released under the terms of the
5 GNU General Public License version 2.
6 */
7
8 #include "CombineInstrumentsDialog.h"
9
10 // enable this for debug messages being printed while combining the instruments
11 #define DEBUG_COMBINE_INSTRUMENTS 0
12
13 #include "global.h"
14
15 #include <set>
16 #include <iostream>
17 #include <assert.h>
18
19 #include <glibmm/ustring.h>
20 #include <gtkmm/stock.h>
21 #include <gtkmm/messagedialog.h>
22
23 Glib::ustring gig_to_utf8(const gig::String& gig_string);
24
25 typedef std::map<gig::Instrument*, gig::Region*> RegionGroup;
26 typedef std::map<DLS::range_t,RegionGroup> RegionGroups;
27
28 typedef std::vector<DLS::range_t> DimensionZones;
29 typedef std::map<gig::dimension_t,DimensionZones> Dimensions;
30
31 typedef std::map<gig::dimension_t,int> DimensionCase;
32
33 typedef std::map<gig::dimension_t, int> DimensionRegionUpperLimits;
34
35 typedef std::map<gig::DimensionRegion*, DimensionZones> VelocityZones;
36
37 ///////////////////////////////////////////////////////////////////////////
38 // private functions
39
40 #if DEBUG_COMBINE_INSTRUMENTS
41 static void printRanges(const RegionGroups& regions) {
42 std::cout << "{ ";
43 for (RegionGroups::const_iterator it = regions.begin(); it != regions.end(); ++it) {
44 if (it != regions.begin()) std::cout << ", ";
45 std::cout << (int)it->first.low << ".." << (int)it->first.high;
46 }
47 std::cout << " }" << std::flush;
48 }
49 #endif
50
51 /**
52 * If the two ranges overlap, then this function returns the smallest point
53 * within that overlapping zone. If the two ranges do not overlap, then this
54 * function will return -1 instead.
55 */
56 inline int smallestOverlapPoint(const DLS::range_t& r1, const DLS::range_t& r2) {
57 if (r1.overlaps(r2.low)) return r2.low;
58 if (r2.overlaps(r1.low)) return r1.low;
59 return -1;
60 }
61
62 /**
63 * Get the most smallest region point (not necessarily its region start point)
64 * of all regions of the given instruments, start searching at keyboard
65 * position @a iStart.
66 *
67 * @returns very first region point >= iStart, or -1 if no region could be
68 * found with a range member point >= iStart
69 */
70 static int findLowestRegionPoint(std::vector<gig::Instrument*>& instruments, int iStart) {
71 DLS::range_t searchRange = { iStart, 127 };
72 int result = -1;
73 for (uint i = 0; i < instruments.size(); ++i) {
74 gig::Instrument* instr = instruments[i];
75 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
76 if (rgn->KeyRange.overlaps(searchRange)) {
77 int lowest = smallestOverlapPoint(rgn->KeyRange, searchRange);
78 if (result == -1 || lowest < result) result = lowest;
79 }
80 }
81 }
82 return result;
83 }
84
85 /**
86 * Get the most smallest region end of all regions of the given instruments,
87 * start searching at keyboard position @a iStart.
88 *
89 * @returns very first region end >= iStart, or -1 if no region could be found
90 * with a range end >= iStart
91 */
92 static int findFirstRegionEnd(std::vector<gig::Instrument*>& instruments, int iStart) {
93 DLS::range_t searchRange = { iStart, 127 };
94 int result = -1;
95 for (uint i = 0; i < instruments.size(); ++i) {
96 gig::Instrument* instr = instruments[i];
97 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
98 if (rgn->KeyRange.overlaps(searchRange)) {
99 if (result == -1 || rgn->KeyRange.high < result)
100 result = rgn->KeyRange.high;
101 }
102 }
103 }
104 return result;
105 }
106
107 /**
108 * Returns a list of all regions of the given @a instrument where the respective
109 * region's key range overlaps the given @a range.
110 */
111 static std::vector<gig::Region*> getAllRegionsWhichOverlapRange(gig::Instrument* instrument, DLS::range_t range) {
112 //std::cout << "All regions which overlap { " << (int)range.low << ".." << (int)range.high << " } : " << std::flush;
113 std::vector<gig::Region*> v;
114 for (gig::Region* rgn = instrument->GetFirstRegion(); rgn; rgn = instrument->GetNextRegion()) {
115 if (rgn->KeyRange.overlaps(range)) {
116 v.push_back(rgn);
117 //std::cout << (int)rgn->KeyRange.low << ".." << (int)rgn->KeyRange.high << ", " << std::flush;
118 }
119 }
120 //std::cout << " END." << std::endl;
121 return v;
122 }
123
124 /**
125 * Returns all regions of the given @a instruments where the respective region's
126 * key range overlaps the given @a range. The regions returned are ordered (in a
127 * map) by their instrument pointer.
128 */
129 static RegionGroup getAllRegionsWhichOverlapRange(std::vector<gig::Instrument*>& instruments, DLS::range_t range) {
130 RegionGroup group;
131 for (uint i = 0; i < instruments.size(); ++i) {
132 gig::Instrument* instr = instruments[i];
133 std::vector<gig::Region*> v = getAllRegionsWhichOverlapRange(instr, range);
134 if (v.empty()) continue;
135 if (v.size() > 1) {
136 std::cerr << "WARNING: More than one region found!" << std::endl;
137 }
138 group[instr] = v[0];
139 }
140 return group;
141 }
142
143 /** @brief Identify required regions.
144 *
145 * Takes a list of @a instruments as argument (which are planned to be combined
146 * as layers in one single new instrument) and fulfills the following tasks:
147 *
148 * - 1. Identification of total amount of regions required to create a new
149 * instrument to become a layered version of the given instruments.
150 * - 2. Precise key range of each of those identified required regions to be
151 * created in that new instrument.
152 * - 3. Grouping the original source regions of the given original instruments
153 * to the respective target key range (new region) of the instrument to be
154 * created.
155 *
156 * @param instruments - list of instruments that are planned to be combined
157 * @returns structured result of the tasks described above
158 */
159 static RegionGroups groupByRegionIntersections(std::vector<gig::Instrument*>& instruments) {
160 RegionGroups groups;
161
162 // find all region intersections of all instruments
163 std::vector<DLS::range_t> intersections;
164 for (int iStart = 0; iStart <= 127; ) {
165 iStart = findLowestRegionPoint(instruments, iStart);
166 if (iStart < 0) break;
167 const int iEnd = findFirstRegionEnd(instruments, iStart);
168 DLS::range_t range = { iStart, iEnd };
169 intersections.push_back(range);
170 iStart = iEnd + 1;
171 }
172
173 // now sort all regions to those found intersections
174 for (uint i = 0; i < intersections.size(); ++i) {
175 const DLS::range_t& range = intersections[i];
176 RegionGroup group = getAllRegionsWhichOverlapRange(instruments, range);
177 if (!group.empty())
178 groups[range] = group;
179 else
180 std::cerr << "WARNING: empty region group!" << std::endl;
181 }
182
183 return groups;
184 }
185
186 /** @brief Identify required dimensions.
187 *
188 * Takes a planned new region (@a regionGroup) as argument and identifies which
189 * precise dimensions would have to be created for that new region, along with
190 * the amount of dimension zones and their precise individual zone sizes.
191 *
192 * @param regionGroup - planned new region for a new instrument
193 * @returns set of dimensions that shall be created for the given planned region
194 */
195 static Dimensions getDimensionsForRegionGroup(RegionGroup& regionGroup) {
196 std::map<gig::dimension_t, std::set<int> > dimUpperLimits;
197
198 // collect all dimension region zones' upper limits
199 for (RegionGroup::iterator it = regionGroup.begin();
200 it != regionGroup.end(); ++it)
201 {
202 gig::Region* rgn = it->second;
203 int previousBits = 0;
204 for (uint d = 0; d < rgn->Dimensions; ++d) {
205 const gig::dimension_def_t& def = rgn->pDimensionDefinitions[d];
206 for (uint z = 0; z < def.zones; ++z) {
207 int dr = z << previousBits;
208 gig::DimensionRegion* dimRgn = rgn->pDimensionRegions[dr];
209 // Store the individual dimension zone sizes (or actually their
210 // upper limits here) for each dimension.
211 // HACK: Note that the velocity dimension is specially handled
212 // here. Instead of taking over custom velocity split sizes
213 // here, only a bogus number (zone index number) is stored for
214 // each velocity zone, that way only the maxiumum amount of
215 // velocity splits of all regions is stored here, and when their
216 // individual DimensionRegions are finally copied (later), the
217 // individual velocity split size are copied by that.
218 dimUpperLimits[def.dimension].insert(
219 (def.dimension == gig::dimension_velocity) ?
220 z : (def.split_type == gig::split_type_bit) ?
221 ((z+1) * 128/def.zones - 1) : dimRgn->DimensionUpperLimits[dr]
222 );
223 }
224 previousBits += def.bits;
225 }
226 }
227
228 // convert upper limit set to range vector
229 Dimensions dims;
230 for (std::map<gig::dimension_t, std::set<int> >::const_iterator it = dimUpperLimits.begin();
231 it != dimUpperLimits.end(); ++it)
232 {
233 gig::dimension_t type = it->first;
234 int iLow = 0;
235 for (std::set<int>::const_iterator itNums = it->second.begin();
236 itNums != it->second.end(); ++itNums)
237 {
238 const int iUpperLimit = *itNums;
239 DLS::range_t range = { iLow, iUpperLimit };
240 dims[type].push_back(range);
241 iLow = iUpperLimit + 1;
242 }
243 }
244
245 return dims;
246 }
247
248 inline int getDimensionIndex(gig::dimension_t type, gig::Region* rgn) {
249 for (uint i = 0; i < rgn->Dimensions; ++i)
250 if (rgn->pDimensionDefinitions[i].dimension == type)
251 return i;
252 return -1;
253 }
254
255 static void fillDimValues(uint* values/*[8]*/, DimensionCase dimCase, gig::Region* rgn, bool bShouldHaveAllDimensionsPassed) {
256 for (DimensionCase::iterator it = dimCase.begin(); it != dimCase.end(); ++it) {
257 gig::dimension_t type = it->first;
258 int iDimIndex = getDimensionIndex(type, rgn);
259 if (bShouldHaveAllDimensionsPassed) assert(iDimIndex >= 0);
260 else if (iDimIndex < 0) continue;
261 values[iDimIndex] = it->second;
262 }
263 }
264
265 static DimensionRegionUpperLimits getDimensionRegionUpperLimits(gig::DimensionRegion* dimRgn) {
266 DimensionRegionUpperLimits limits;
267 gig::Region* rgn = dimRgn->GetParent();
268 for (uint d = 0; d < rgn->Dimensions; ++d) {
269 const gig::dimension_def_t& def = rgn->pDimensionDefinitions[d];
270 limits[def.dimension] = dimRgn->DimensionUpperLimits[d];
271 }
272 return limits;
273 }
274
275 static void restoreDimensionRegionUpperLimits(gig::DimensionRegion* dimRgn, const DimensionRegionUpperLimits& limits) {
276 gig::Region* rgn = dimRgn->GetParent();
277 for (DimensionRegionUpperLimits::const_iterator it = limits.begin();
278 it != limits.end(); ++it)
279 {
280 int index = getDimensionIndex(it->first, rgn);
281 assert(index >= 0);
282 dimRgn->DimensionUpperLimits[index] = it->second;
283 }
284 }
285
286 /**
287 * Returns the sum of all bits of all dimensions defined before the given
288 * dimensions (@a type). This allows to access cases of that particular
289 * dimension directly.
290 *
291 * @param type - dimension that shall be used
292 * @param rgn - parent region of that dimension
293 */
294 inline int baseBits(gig::dimension_t type, gig::Region* rgn) {
295 int previousBits = 0;
296 for (uint i = 0; i < rgn->Dimensions; ++i) {
297 if (rgn->pDimensionDefinitions[i].dimension == type) break;
298 previousBits += rgn->pDimensionDefinitions[i].bits;
299 }
300 return previousBits;
301 }
302
303 inline int dimensionRegionIndex(gig::DimensionRegion* dimRgn) {
304 gig::Region* rgn = dimRgn->GetParent();
305 int sz = sizeof(rgn->pDimensionRegions) / sizeof(gig::DimensionRegion*);
306 for (int i = 0; i < sz; ++i)
307 if (rgn->pDimensionRegions[i] == dimRgn)
308 return i;
309 return -1;
310 }
311
312 /** @brief Get exact zone ranges of given dimension.
313 *
314 * This function is useful for the velocity type dimension. In contrast to other
315 * dimension types, this dimension can have different zone ranges (that is
316 * different individual start and end points of its dimension zones) depending
317 * on which zones of other dimensions (on that gig::Region) are currently
318 * selected.
319 *
320 * @param type - dimension where the zone ranges should be retrieved for
321 * (usually the velocity dimension in this context)
322 * @param dimRgn - reflects the exact cases (zone selections) of all other
323 * dimensions than the given one in question
324 * @returns individual ranges for each zone of the questioned dimension type,
325 * it returns an empty result on errors instead
326 */
327 static DimensionZones preciseDimensionZonesFor(gig::dimension_t type, gig::DimensionRegion* dimRgn) {
328 DimensionZones zones;
329 gig::Region* rgn = dimRgn->GetParent();
330 int iDimension = getDimensionIndex(type, rgn);
331 if (iDimension < 0) return zones;
332 const gig::dimension_def_t& def = rgn->pDimensionDefinitions[iDimension];
333 int iDimRgn = dimensionRegionIndex(dimRgn);
334 int iBaseBits = baseBits(type, rgn);
335 int mask = ~(((1 << def.bits) - 1) << iBaseBits);
336
337 int iLow = 0;
338 for (int z = 0; z < def.zones; ++z) {
339 gig::DimensionRegion* dimRgn2 =
340 rgn->pDimensionRegions[ (iDimRgn & mask) | ( z << iBaseBits) ];
341 int iHigh = dimRgn2->DimensionUpperLimits[iDimension];
342 DLS::range_t range = { iLow, iHigh};
343 zones.push_back(range);
344 iLow = iHigh + 1;
345 }
346 return zones;
347 }
348
349 static VelocityZones getVelocityZones(gig::Region* rgn) {
350 VelocityZones zones;
351 for (uint i = 0; i < rgn->DimensionRegions; ++i) {
352 gig::DimensionRegion* dimRgn = rgn->pDimensionRegions[i];
353 zones[dimRgn] = preciseDimensionZonesFor(gig::dimension_velocity, dimRgn);
354 }
355 return zones;
356 }
357
358 /** @brief Copy all DimensionRegions from source Region to target Region.
359 *
360 * Copies the entire articulation informations (including sample reference of
361 * course) from all individual DimensionRegions of source Region @a inRgn to
362 * target Region @a outRgn. There are no dimension regions created during this
363 * task. It is expected that the required dimensions (thus the required
364 * dimension regions) were already created before calling this function.
365 *
366 * To be precise, it does the task above only for the layer selected by
367 * @a iSrcLayer and @a iDstLayer. All dimensions regions of other layers that
368 * may exist, will not be copied by one single call of this function. So if
369 * there is a layer dimension, this function needs to be called several times.
370 *
371 * @param outRgn - where the dimension regions shall be copied to
372 * @param inRgn - all dimension regions that shall be copied from
373 * @param dims - dimension definitions of target region
374 * @param iDstLayer - layer number of destination region where the dimension
375 * regions shall be copied to
376 * @param iSrcLayer - layer number of the source region where the dimension
377 * regions shall be copied from
378 * @param dstVelocityZones - all precise velocity zones for destination region
379 * (since this information is stored on
380 * DimensionRegion level and this function is
381 * modifying target DimensionRegions, this
382 * informations thus needs to be retrieved before
383 * calling this function)
384 * @param dimCase - just for internal purpose (function recursion), don't pass
385 * anything here, this function will call itself recursively
386 * will fill this container with concrete dimension values for
387 * selecting the precise dimension regions during its task
388 */
389 static void copyDimensionRegions(gig::Region* outRgn, gig::Region* inRgn, Dimensions dims, int iDstLayer, int iSrcLayer, const VelocityZones& dstVelocityZones, DimensionCase dimCase = DimensionCase()) {
390 if (dims.empty()) { // finally reached end of function recursion ...
391 // resolve the respective source & destination DimensionRegion ...
392 uint srcDimValues[8] = {};
393 uint dstDimValues[8] = {};
394 DimensionCase srcDimCase = dimCase;
395 DimensionCase dstDimCase = dimCase;
396 srcDimCase[gig::dimension_layer] = iSrcLayer;
397 dstDimCase[gig::dimension_layer] = iDstLayer;
398
399 // first select source & target dimension region with an arbitrary
400 // velocity split zone, to get access to the precise individual velocity
401 // split zone sizes (if there is actually a velocity dimension at all,
402 // otherwise we already select the desired source & target dimension
403 // region here)
404 fillDimValues(srcDimValues, srcDimCase, inRgn, false);
405 fillDimValues(dstDimValues, dstDimCase, outRgn, true);
406 gig::DimensionRegion* srcDimRgn = inRgn->GetDimensionRegionByValue(srcDimValues);
407 gig::DimensionRegion* dstDimRgn = outRgn->GetDimensionRegionByValue(dstDimValues);
408 #if DEBUG_COMBINE_INSTRUMENTS
409 printf("srcDimRgn=%lx dstDimRgn=%lx\n", (uint64_t)srcDimRgn, (uint64_t)dstDimRgn);
410 #endif
411
412 // now that we have access to the precise velocity split zone upper
413 // limits, we can select the actual source & destination dimension
414 // regions we need to copy (assuming that source or target region has
415 // a velocity dimension)
416 if (outRgn->GetDimensionDefinition(gig::dimension_velocity)) {
417 // re-select target dimension region
418 assert(dstVelocityZones.find(dstDimRgn) != dstVelocityZones.end());
419 DimensionZones dstZones = dstVelocityZones.find(dstDimRgn)->second;
420 assert(dstZones.size() > 1);
421 int iZoneIndex = dstDimCase[gig::dimension_velocity];
422 #if DEBUG_COMBINE_INSTRUMENTS
423 printf("dst velocity zone: %d/%d\n", iZoneIndex, dstZones.size());
424 #endif
425 assert(iZoneIndex < dstZones.size());
426 dstDimCase[gig::dimension_velocity] = dstZones[iZoneIndex].low; // arbitrary value between low and high
427 #if DEBUG_COMBINE_INSTRUMENTS
428 printf("dst velocity value = %d\n", dstDimCase[gig::dimension_velocity]);
429 #endif
430 fillDimValues(dstDimValues, dstDimCase, outRgn, true);
431 dstDimRgn = outRgn->GetDimensionRegionByValue(dstDimValues);
432 #if DEBUG_COMBINE_INSTRUMENTS
433 printf("reselected dstDimRgn=%lx\n", (uint64_t)dstDimRgn);
434 #endif
435
436 // re-select source dimension region
437 // (if it has a velocity dimension)
438 if (inRgn->GetDimensionDefinition(gig::dimension_velocity)) {
439 DimensionZones srcZones = preciseDimensionZonesFor(gig::dimension_velocity, srcDimRgn);
440 assert(srcZones.size() > 1);
441 if (iZoneIndex >= srcZones.size())
442 iZoneIndex = srcZones.size();
443 srcDimCase[gig::dimension_velocity] = srcZones[iZoneIndex].low; // same zone as used above for target dimension region (no matter what the precise zone ranges are)
444 fillDimValues(srcDimValues, srcDimCase, inRgn, false);
445 srcDimRgn = inRgn->GetDimensionRegionByValue(srcDimValues);
446 #if DEBUG_COMBINE_INSTRUMENTS
447 printf("reselected srcDimRgn=%lx\n", (uint64_t)srcDimRgn);
448 #endif
449 }
450 }
451
452 // backup the target DimensionRegion's current dimension zones upper
453 // limits (because the target DimensionRegion's upper limits are already
454 // defined correctly since calling AddDimension(), and the CopyAssign()
455 // call next, will overwrite those upper limits unfortunately
456 DimensionRegionUpperLimits dstUpperLimits = getDimensionRegionUpperLimits(dstDimRgn);
457 DimensionRegionUpperLimits srcUpperLimits = getDimensionRegionUpperLimits(srcDimRgn);
458
459 // copy over the selected DimensionRegion
460 const gig::Region* const origRgn = dstDimRgn->GetParent(); // just for sanity check below
461 dstDimRgn->CopyAssign(srcDimRgn);
462 assert(origRgn == dstDimRgn->GetParent());
463
464 // restore all original dimension zone upper limits except of the
465 // velocity dimension, because the velocity dimension zone sizes are
466 // allowed to differ for individual DimensionRegions in gig v3 format
467 if (srcUpperLimits.count(gig::dimension_velocity)) {
468 assert(dstUpperLimits.count(gig::dimension_velocity));
469 dstUpperLimits[gig::dimension_velocity] = srcUpperLimits[gig::dimension_velocity];
470 }
471 restoreDimensionRegionUpperLimits(dstDimRgn, dstUpperLimits);
472
473 return; // end of recursion
474 }
475
476 // Copying n dimensions requires n nested loops. That's why this function
477 // is calling itself recursively to provide the required amount of nested
478 // loops. With each call it pops from argument 'dims' and pushes to
479 // argument 'dimCase'.
480
481 Dimensions::iterator itDimension = dims.begin();
482
483 gig::dimension_t type = itDimension->first;
484 DimensionZones zones = itDimension->second;
485
486 dims.erase(itDimension);
487
488 int iZone = 0;
489 for (DimensionZones::iterator itZone = zones.begin();
490 itZone != zones.end(); ++itZone, ++iZone)
491 {
492 DLS::range_t zoneRange = *itZone;
493 gig::dimension_def_t* def = outRgn->GetDimensionDefinition(type);
494 dimCase[type] = (def->split_type == gig::split_type_bit) ? iZone : zoneRange.low;
495 // recurse until 'dims' is exhausted (and dimCase filled up with concrete value)
496 copyDimensionRegions(outRgn, inRgn, dims, iDstLayer, iSrcLayer, dstVelocityZones, dimCase);
497 }
498 }
499
500 /** @brief Combine given list of instruments to one instrument.
501 *
502 * Takes a list of @a instruments as argument and combines them to one single
503 * new @a output instrument. For this task, it will create a 'layer' dimension
504 * in the new instrument and copies the source instruments to those layers.
505 *
506 * @param instruments - (input) list of instruments that shall be combined,
507 * they will only be read, so they will be left untouched
508 * @param gig - (input/output) .gig file where the new combined instrument shall
509 * be created
510 * @param output - (output) on success this pointer will be set to the new
511 * instrument being created
512 * @throw RIFF::Exception on any kinds of errors
513 */
514 static void combineInstruments(std::vector<gig::Instrument*>& instruments, gig::File* gig, gig::Instrument*& output) {
515 output = NULL;
516
517 // divide the individual regions to (probably even smaller) groups of
518 // regions, coping with the fact that the source regions of the instruments
519 // might have quite different range sizes and start and end points
520 RegionGroups groups = groupByRegionIntersections(instruments);
521 #if DEBUG_COMBINE_INSTRUMENTS
522 std::cout << std::endl << "New regions: " << std::flush;
523 printRanges(groups);
524 std::cout << std::endl;
525 #endif
526
527 if (groups.empty())
528 throw gig::Exception(_("No regions found to create a new instrument with."));
529
530 // create a new output instrument
531 gig::Instrument* outInstr = gig->AddInstrument();
532 outInstr->pInfo->Name = _("NEW COMBINATION");
533
534 // Distinguishing in the following code block between 'horizontal' and
535 // 'vertical' regions. The 'horizontal' ones are meant to be the key ranges
536 // in the output instrument, while the 'vertical' regions are meant to be
537 // the set of source regions that shall be layered to that 'horizontal'
538 // region / key range. It is important to know, that the key ranges defined
539 // in the 'horizontal' and 'vertical' regions might differ.
540
541 // merge the instruments to the new output instrument
542 for (RegionGroups::iterator itGroup = groups.begin();
543 itGroup != groups.end(); ++itGroup) // iterate over 'horizontal' / target regions ...
544 {
545 gig::Region* outRgn = outInstr->AddRegion();
546 outRgn->SetKeyRange(itGroup->first.low, itGroup->first.high);
547
548 // detect the total amount of layers required to build up this combi
549 // for current key range
550 int iTotalLayers = 0;
551 for (RegionGroup::iterator itRgn = itGroup->second.begin();
552 itRgn != itGroup->second.end(); ++itRgn)
553 {
554 gig::Region* inRgn = itRgn->second;
555 iTotalLayers += inRgn->Layers;
556 }
557
558 // create all required dimensions for this output region
559 // (except the layer dimension, which we create as next step)
560 Dimensions dims = getDimensionsForRegionGroup(itGroup->second);
561 for (Dimensions::iterator itDim = dims.begin();
562 itDim != dims.end(); ++itDim)
563 {
564 if (itDim->first == gig::dimension_layer) continue;
565
566 gig::dimension_def_t def;
567 def.dimension = itDim->first; // dimension type
568 def.zones = itDim->second.size();
569 def.bits = zoneCountToBits(def.zones);
570 #if DEBUG_COMBINE_INSTRUMENTS
571 std::cout << "Adding new regular dimension type=" << std::hex << (int)def.dimension << std::dec << ", zones=" << (int)def.zones << ", bits=" << (int)def.bits << " ... " << std::flush;
572 #endif
573 outRgn->AddDimension(&def);
574 #if DEBUG_COMBINE_INSTRUMENTS
575 std::cout << "OK" << std::endl << std::flush;
576 #endif
577 }
578
579 // create the layer dimension (if necessary for current key range)
580 if (iTotalLayers > 1) {
581 gig::dimension_def_t def;
582 def.dimension = gig::dimension_layer; // dimension type
583 def.zones = iTotalLayers;
584 def.bits = zoneCountToBits(def.zones);
585 #if DEBUG_COMBINE_INSTRUMENTS
586 std::cout << "Adding new (layer) dimension type=" << std::hex << (int)def.dimension << std::dec << ", zones=" << (int)def.zones << ", bits=" << (int)def.bits << " ... " << std::flush;
587 #endif
588 outRgn->AddDimension(&def);
589 #if DEBUG_COMBINE_INSTRUMENTS
590 std::cout << "OK" << std::endl << std::flush;
591 #endif
592 }
593
594 // now copy the source dimension regions to the target dimension regions
595 int iDstLayer = 0;
596 for (RegionGroup::iterator itRgn = itGroup->second.begin();
597 itRgn != itGroup->second.end(); ++itRgn) // iterate over 'vertical' / source regions ...
598 {
599 gig::Region* inRgn = itRgn->second;
600 VelocityZones dstVelocityZones = getVelocityZones(outRgn);
601 for (uint iSrcLayer = 0; iSrcLayer < inRgn->Layers; ++iSrcLayer, ++iDstLayer) {
602 copyDimensionRegions(outRgn, inRgn, dims, iDstLayer, iSrcLayer, dstVelocityZones);
603 }
604 }
605 }
606
607 // success
608 output = outInstr;
609 }
610
611 ///////////////////////////////////////////////////////////////////////////
612 // class 'CombineInstrumentsDialog'
613
614 CombineInstrumentsDialog::CombineInstrumentsDialog(Gtk::Window& parent, gig::File* gig)
615 : Gtk::Dialog(_("Combine Instruments"), parent, true),
616 m_gig(gig), m_fileWasChanged(false), m_newCombinedInstrument(NULL),
617 m_cancelButton(Gtk::Stock::CANCEL), m_OKButton(Gtk::Stock::OK),
618 m_descriptionLabel()
619 {
620 get_vbox()->pack_start(m_descriptionLabel, Gtk::PACK_SHRINK);
621 get_vbox()->pack_start(m_treeView);
622 get_vbox()->pack_start(m_buttonBox, Gtk::PACK_SHRINK);
623
624 #if GTKMM_MAJOR_VERSION >= 3
625 description.set_line_wrap();
626 #endif
627 m_descriptionLabel.set_text(_(
628 "Select at least two instruments below that shall be combined "
629 "as layers (using a \"Layer\" dimension) to a new instrument. The "
630 "original instruments remain untouched.")
631 );
632
633 m_refTreeModel = Gtk::ListStore::create(m_columns);
634 m_treeView.set_model(m_refTreeModel);
635 //m_treeView.set_tooltip_text(_("asdf"));
636 m_treeView.append_column("Instrument", m_columns.m_col_name);
637 m_treeView.set_headers_visible(false);
638 m_treeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE);
639 m_treeView.get_selection()->signal_changed().connect(
640 sigc::mem_fun(*this, &CombineInstrumentsDialog::onSelectionChanged)
641 );
642 m_treeView.show();
643
644 for (int i = 0; true; ++i) {
645 gig::Instrument* instr = gig->GetInstrument(i);
646 if (!instr) break;
647
648 #if DEBUG_COMBINE_INSTRUMENTS
649 {
650 std::cout << "Instrument (" << i << ") '" << instr->pInfo->Name << "' Regions: " << std::flush;
651 for (gig::Region* rgn = instr->GetFirstRegion(); rgn; rgn = instr->GetNextRegion()) {
652 std::cout << rgn->KeyRange.low << ".." << rgn->KeyRange.high << ", " << std::flush;
653 }
654 std::cout << std::endl;
655 }
656 std::cout << std::endl;
657 #endif
658
659 Glib::ustring name(gig_to_utf8(instr->pInfo->Name));
660 Gtk::TreeModel::iterator iter = m_refTreeModel->append();
661 Gtk::TreeModel::Row row = *iter;
662 row[m_columns.m_col_name] = name;
663 row[m_columns.m_col_instr] = instr;
664 }
665
666 m_buttonBox.set_layout(Gtk::BUTTONBOX_END);
667 m_buttonBox.set_border_width(5);
668 m_buttonBox.pack_start(m_cancelButton, Gtk::PACK_SHRINK);
669 m_buttonBox.pack_start(m_OKButton, Gtk::PACK_SHRINK);
670 m_buttonBox.show();
671
672 m_cancelButton.show();
673 m_OKButton.set_sensitive(false);
674 m_OKButton.show();
675
676 m_cancelButton.signal_clicked().connect(
677 sigc::mem_fun(*this, &CombineInstrumentsDialog::hide)
678 );
679
680 m_OKButton.signal_clicked().connect(
681 sigc::mem_fun(*this, &CombineInstrumentsDialog::combineSelectedInstruments)
682 );
683
684 show_all_children();
685 }
686
687 void CombineInstrumentsDialog::combineSelectedInstruments() {
688 std::vector<gig::Instrument*> instruments;
689 std::vector<Gtk::TreeModel::Path> v = m_treeView.get_selection()->get_selected_rows();
690 for (uint i = 0; i < v.size(); ++i) {
691 Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(v[i]);
692 Gtk::TreeModel::Row row = *it;
693 Glib::ustring name = row[m_columns.m_col_name];
694 gig::Instrument* instrument = row[m_columns.m_col_instr];
695 #if DEBUG_COMBINE_INSTRUMENTS
696 printf("Selection '%s' 0x%lx\n\n", name.c_str(), int64_t((void*)instrument));
697 #endif
698 instruments.push_back(instrument);
699 }
700
701 try {
702 combineInstruments(instruments, m_gig, m_newCombinedInstrument);
703 } catch (RIFF::Exception e) {;
704 Gtk::MessageDialog msg(*this, e.Message, false, Gtk::MESSAGE_ERROR);
705 msg.run();
706 return;
707 }
708
709 // no error occurred
710 m_fileWasChanged = true;
711 hide();
712 }
713
714 void CombineInstrumentsDialog::onSelectionChanged() {
715 std::vector<Gtk::TreeModel::Path> v = m_treeView.get_selection()->get_selected_rows();
716 m_OKButton.set_sensitive(v.size() >= 2);
717 }
718
719 bool CombineInstrumentsDialog::fileWasChanged() const {
720 return m_fileWasChanged;
721 }
722
723 gig::Instrument* CombineInstrumentsDialog::newCombinedInstrument() const {
724 return m_newCombinedInstrument;
725 }

  ViewVC Help
Powered by ViewVC