1 /*
2 * libInstPatch
3 * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; version 2.1
8 * of the License only.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA or on the web at http://www.gnu.org.
19 */
20 /**
21 * SECTION: IpatchGigRegion
22 * @short_description: GigaSampler region object
23 * @see_also: #IpatchGigInst, #IpatchGig
24 * @stability: Stable
25 *
26 * GigaSampler region objects are children of #IpatchGigInst objects.
27 */
28 #include <glib.h>
29 #include <glib-object.h>
30 #include "IpatchGigRegion.h"
31 #include "IpatchGigFile.h"
32 #include "IpatchGigFile_priv.h"
33 #include "IpatchGigSample.h"
34 #include "IpatchRange.h"
35 #include "IpatchTypeProp.h"
36 #include "ipatch_priv.h"
37
38 enum
39 {
40 PROP_0,
41
42 PROP_TITLE,
43
44 PROP_NOTE_RANGE,
45 PROP_VELOCITY_RANGE,
46
47 PROP_KEY_GROUP,
48 PROP_LAYER_GROUP,
49 PROP_PHASE_GROUP,
50 PROP_CHANNEL,
51
52 /* IpatchItem flags (no one needs to know that though) */
53 PROP_SELF_NON_EXCLUSIVE,
54 PROP_PHASE_MASTER,
55 PROP_MULTI_CHANNEL
56 };
57
58 static void ipatch_gig_region_class_init(IpatchGigRegionClass *klass);
59 static void ipatch_gig_region_set_property(GObject *object, guint property_id,
60 const GValue *value,
61 GParamSpec *pspec);
62 static void ipatch_gig_region_get_property(GObject *object, guint property_id,
63 GValue *value, GParamSpec *pspec);
64
65 static void ipatch_gig_region_init(IpatchGigRegion *gig_region);
66 static void ipatch_gig_region_finalize(GObject *gobject);
67 static void ipatch_gig_region_item_copy(IpatchItem *dest, IpatchItem *src,
68 IpatchItemCopyLinkFunc link_func,
69 gpointer user_data);
70 static const GType *ipatch_gig_region_container_child_types(void);
71 static gboolean
72 ipatch_gig_region_container_init_iter(IpatchContainer *container,
73 IpatchIter *iter, GType type);
74
75
76 static GObjectClass *parent_class = NULL;
77 static GType gig_region_child_types[3] = { 0 };
78
79
80 GType
ipatch_gig_region_get_type(void)81 ipatch_gig_region_get_type(void)
82 {
83 static GType item_type = 0;
84
85 if(!item_type)
86 {
87 static const GTypeInfo item_info =
88 {
89 sizeof(IpatchGigRegionClass), NULL, NULL,
90 (GClassInitFunc)ipatch_gig_region_class_init, NULL, NULL,
91 sizeof(IpatchGigRegion), 0,
92 (GInstanceInitFunc)ipatch_gig_region_init,
93 };
94
95 item_type = g_type_register_static(IPATCH_TYPE_CONTAINER,
96 "IpatchGigRegion", &item_info, 0);
97 }
98
99 return (item_type);
100 }
101
102 static void
ipatch_gig_region_class_init(IpatchGigRegionClass * klass)103 ipatch_gig_region_class_init(IpatchGigRegionClass *klass)
104 {
105 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
106 IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
107 IpatchContainerClass *container_class = IPATCH_CONTAINER_CLASS(klass);
108
109 parent_class = g_type_class_peek_parent(klass);
110
111 obj_class->finalize = ipatch_gig_region_finalize;
112 obj_class->get_property = ipatch_gig_region_get_property;
113
114 item_class->item_set_property = ipatch_gig_region_set_property;
115 item_class->copy = ipatch_gig_region_item_copy;
116
117 container_class->child_types = ipatch_gig_region_container_child_types;
118 container_class->init_iter = ipatch_gig_region_container_init_iter;
119 container_class->make_unique = NULL;
120
121 g_object_class_override_property(obj_class, PROP_TITLE, "title");
122
123 g_object_class_install_property(obj_class, PROP_NOTE_RANGE,
124 ipatch_param_spec_range("note-range", _("Note range"),
125 _("MIDI note range"),
126 0, 127, 0, 127,
127 G_PARAM_READWRITE));
128 g_object_class_install_property(obj_class, PROP_VELOCITY_RANGE,
129 ipatch_param_spec_range("velocity-range", _("Velocity range"),
130 _("MIDI velocity range"),
131 0, 127, 0, 127,
132 G_PARAM_READWRITE));
133
134 g_object_class_install_property(obj_class, PROP_KEY_GROUP,
135 g_param_spec_int("key-group", _("Key group"),
136 _("Percussion key group"),
137 0, 15, 0,
138 G_PARAM_READWRITE));
139 g_object_class_install_property(obj_class, PROP_LAYER_GROUP,
140 g_param_spec_int("layer-group", _("Layer group"),
141 _("Layer group"),
142 0, G_MAXUSHORT, 0,
143 G_PARAM_READWRITE));
144 g_object_class_install_property(obj_class, PROP_PHASE_GROUP,
145 g_param_spec_int("phase-group", _("Phase group"),
146 _("Phase locked sample group"),
147 0, G_MAXUSHORT, 0,
148 G_PARAM_READWRITE));
149 g_object_class_install_property(obj_class, PROP_CHANNEL,
150 g_param_spec_int("channel", _("Channel"),
151 _("DLS audio channel identifier"),
152 0, 0x03FFFF, 0,
153 G_PARAM_READWRITE));
154
155 g_object_class_install_property(obj_class, PROP_SELF_NON_EXCLUSIVE,
156 g_param_spec_boolean("self-non-exclusive",
157 _("Non exclusive"),
158 _("Self non exclusive"),
159 FALSE,
160 G_PARAM_READWRITE));
161 g_object_class_install_property(obj_class, PROP_PHASE_MASTER,
162 g_param_spec_boolean("phase-master",
163 _("Phase master"),
164 _("Multi channel phase lock master"),
165 FALSE,
166 G_PARAM_READWRITE));
167 g_object_class_install_property(obj_class, PROP_MULTI_CHANNEL,
168 g_param_spec_boolean("multi-channel",
169 _("Multi channel"),
170 _("Multi channel"),
171 FALSE,
172 G_PARAM_READWRITE));
173
174 gig_region_child_types[0] = IPATCH_TYPE_GIG_DIMENSION;
175 gig_region_child_types[1] = IPATCH_TYPE_GIG_SUB_REGION;
176 }
177
178 static void
ipatch_gig_region_get_title(IpatchGigRegion * region,GValue * value)179 ipatch_gig_region_get_title(IpatchGigRegion *region, GValue *value)
180 {
181 IpatchRange *range = NULL;
182 char *s = NULL;
183
184 g_object_get(region, "note-range", &range, NULL);
185
186 if(range)
187 {
188 if(range->low != range->high)
189 {
190 s = g_strdup_printf(_("Note %d-%d"), range->low, range->high);
191 }
192 else
193 {
194 s = g_strdup_printf(_("Note %d"), range->low);
195 }
196
197 ipatch_range_free(range);
198 }
199
200 g_value_take_string(value, s);
201 }
202
203 static void
ipatch_gig_region_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)204 ipatch_gig_region_set_property(GObject *object, guint property_id,
205 const GValue *value, GParamSpec *pspec)
206 {
207 IpatchGigRegion *region = IPATCH_GIG_REGION(object);
208 IpatchRange *range;
209 gboolean retval;
210
211 switch(property_id)
212 {
213 case PROP_NOTE_RANGE:
214 range = ipatch_value_get_range(value);
215
216 if(range)
217 {
218 ipatch_gig_region_set_note_range(region, range->low, range->high);
219 }
220
221 break;
222
223 case PROP_VELOCITY_RANGE:
224 range = ipatch_value_get_range(value);
225
226 if(range)
227 {
228 IPATCH_ITEM_WLOCK(region);
229 region->velocity_range_low = range->low;
230 region->velocity_range_high = range->high;
231 IPATCH_ITEM_WUNLOCK(region);
232 }
233
234 break;
235
236 case PROP_KEY_GROUP:
237 region->key_group = g_value_get_int(value);
238 break;
239
240 case PROP_LAYER_GROUP:
241 region->layer_group = g_value_get_int(value);
242 break;
243
244 case PROP_PHASE_GROUP:
245 region->phase_group = g_value_get_int(value);
246 break;
247
248 case PROP_CHANNEL:
249 region->channel = g_value_get_int(value);
250 break;
251
252 case PROP_SELF_NON_EXCLUSIVE:
253 if(g_value_get_boolean(value))
254 ipatch_item_set_flags(IPATCH_ITEM(object),
255 IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE);
256 else
257 ipatch_item_clear_flags(IPATCH_ITEM(object),
258 IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE);
259
260 break;
261
262 case PROP_PHASE_MASTER:
263 if(g_value_get_boolean(value))
264 ipatch_item_set_flags(IPATCH_ITEM(object),
265 IPATCH_GIG_REGION_PHASE_MASTER);
266 else
267 ipatch_item_clear_flags(IPATCH_ITEM(object),
268 IPATCH_GIG_REGION_PHASE_MASTER);
269
270 break;
271
272 case PROP_MULTI_CHANNEL:
273 if(g_value_get_boolean(value))
274 ipatch_item_set_flags(IPATCH_ITEM(object),
275 IPATCH_GIG_REGION_MULTI_CHANNEL);
276 else
277 ipatch_item_clear_flags(IPATCH_ITEM(object),
278 IPATCH_GIG_REGION_MULTI_CHANNEL);
279
280 break;
281
282 default:
283 IPATCH_ITEM_WLOCK(region);
284 retval = ipatch_dls2_info_set_property(®ion->info,
285 property_id, value);
286 IPATCH_ITEM_WUNLOCK(region);
287
288 if(!retval)
289 {
290 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
291 return;
292 }
293
294 break;
295 }
296 }
297
298 static void
ipatch_gig_region_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)299 ipatch_gig_region_get_property(GObject *object, guint property_id,
300 GValue *value, GParamSpec *pspec)
301 {
302 IpatchGigRegion *region = IPATCH_GIG_REGION(object);
303 IpatchRange range;
304 gboolean bool, retval;
305
306 switch(property_id)
307 {
308 case PROP_TITLE:
309 ipatch_gig_region_get_title(region, value);
310 break;
311
312 case PROP_NOTE_RANGE:
313 IPATCH_ITEM_RLOCK(region);
314 range.low = region->note_range_low;
315 range.high = region->note_range_high;
316 IPATCH_ITEM_RUNLOCK(region);
317
318 ipatch_value_set_range(value, &range);
319 break;
320
321 case PROP_VELOCITY_RANGE:
322 IPATCH_ITEM_RLOCK(region);
323 range.low = region->velocity_range_low;
324 range.high = region->velocity_range_high;
325 IPATCH_ITEM_RUNLOCK(region);
326
327 ipatch_value_set_range(value, &range);
328 break;
329
330 case PROP_KEY_GROUP:
331 g_value_set_int(value, region->key_group);
332 break;
333
334 case PROP_LAYER_GROUP:
335 g_value_set_int(value, region->layer_group);
336 break;
337
338 case PROP_PHASE_GROUP:
339 g_value_set_int(value, region->phase_group);
340 break;
341
342 case PROP_CHANNEL:
343 g_value_set_int(value, region->channel);
344 break;
345
346 case PROP_SELF_NON_EXCLUSIVE:
347 bool = (ipatch_item_get_flags(IPATCH_ITEM(object))
348 & IPATCH_GIG_REGION_SELF_NON_EXCLUSIVE) > 0;
349 g_value_set_boolean(value, bool);
350 break;
351
352 case PROP_PHASE_MASTER:
353 bool = (ipatch_item_get_flags(IPATCH_ITEM(object))
354 & IPATCH_GIG_REGION_PHASE_MASTER) > 0;
355 g_value_set_boolean(value, bool);
356 break;
357
358 case PROP_MULTI_CHANNEL:
359 bool = (ipatch_item_get_flags(IPATCH_ITEM(object))
360 & IPATCH_GIG_REGION_MULTI_CHANNEL) > 0;
361 g_value_set_boolean(value, bool);
362 break;
363
364 default:
365 IPATCH_ITEM_RLOCK(region);
366 retval = ipatch_dls2_info_get_property(region->info,
367 property_id, value);
368 IPATCH_ITEM_RUNLOCK(region);
369
370 if(!retval)
371 {
372 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
373 }
374
375 break;
376 }
377 }
378
379 static void
ipatch_gig_region_init(IpatchGigRegion * region)380 ipatch_gig_region_init(IpatchGigRegion *region)
381 {
382 int i;
383
384 region->note_range_low = 0;
385 region->note_range_high = 127;
386 region->velocity_range_low = 0;
387 region->velocity_range_high = 127;
388 region->key_group = 0;
389 region->layer_group = 0;
390 region->phase_group = 0;
391 region->channel = 0;
392 region->info = NULL;
393
394 region->dimension_count = 0;
395 region->sub_region_count = 1; /* always at least 1 sub region */
396 region->sub_regions[0] = ipatch_gig_sub_region_new();
397 ipatch_item_set_parent(IPATCH_ITEM(region->sub_regions[0]),
398 IPATCH_ITEM(region));
399
400 /* FIXME - What is this for really? */
401 for(i = 0; i < IPATCH_GIG_3DDP_SIZE; i++)
402 {
403 region->chunk_3ddp[i] = 0xFF;
404 }
405 }
406
407 static void
ipatch_gig_region_finalize(GObject * gobject)408 ipatch_gig_region_finalize(GObject *gobject)
409 {
410 IpatchGigRegion *gig_region = IPATCH_GIG_REGION(gobject);
411 int i;
412
413 IPATCH_ITEM_WLOCK(gig_region);
414
415 /* delete all dimensions */
416 for(i = 0; i < gig_region->dimension_count; i++)
417 {
418 g_object_unref(gig_region->dimensions[i]);
419 gig_region->dimensions[i] = NULL;
420 }
421
422 /* delete all sub regions */
423 for(i = 0; i < gig_region->sub_region_count; i++)
424 {
425 g_object_unref(gig_region->sub_regions[i]);
426 gig_region->sub_regions[i] = NULL;
427 }
428
429 IPATCH_ITEM_WUNLOCK(gig_region);
430
431 if(parent_class->finalize)
432 {
433 parent_class->finalize(gobject);
434 }
435 }
436
437 static void
ipatch_gig_region_item_copy(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)438 ipatch_gig_region_item_copy(IpatchItem *dest, IpatchItem *src,
439 IpatchItemCopyLinkFunc link_func,
440 gpointer user_data)
441 {
442 IpatchGigRegion *src_reg, *dest_reg;
443 IpatchGigDimension *src_dim;
444 IpatchGigSubRegion *src_sub;
445 IpatchItem *new_dim, *new_sub;
446 int i;
447
448 src_reg = IPATCH_GIG_REGION(src);
449 dest_reg = IPATCH_GIG_REGION(dest);
450
451 IPATCH_ITEM_RLOCK(src_reg);
452
453 /* duplicate the flags */
454 ipatch_item_set_flags(dest_reg, ipatch_item_get_flags(src_reg)
455 & IPATCH_GIG_REGION_FLAG_MASK);
456
457 dest_reg->note_range_low = src_reg->note_range_low;
458 dest_reg->note_range_high = src_reg->note_range_high;
459 dest_reg->velocity_range_low = src_reg->velocity_range_low;
460 dest_reg->velocity_range_high = src_reg->velocity_range_high;
461 dest_reg->key_group = src_reg->key_group;
462 dest_reg->layer_group = src_reg->layer_group;
463 dest_reg->phase_group = src_reg->phase_group;
464 dest_reg->channel = src_reg->channel;
465
466 dest_reg->info = ipatch_dls2_info_duplicate(src_reg->info);
467
468 /* copy dimensions */
469 for(i = 0; i < src_reg->dimension_count; i++)
470 {
471 src_dim = src_reg->dimensions[i];
472 new_dim = ipatch_item_duplicate_link_func((IpatchItem *)src_dim,
473 link_func, user_data);
474 g_return_if_fail(new_dim != NULL);
475
476 dest_reg->dimensions[i] = IPATCH_GIG_DIMENSION(new_dim);
477 dest_reg->dimension_count = i + 1; /* update count in case of failure */
478 }
479
480 /* copy sub regions */
481 for(i = 0; i < src_reg->sub_region_count; i++)
482 {
483 src_sub = src_reg->sub_regions[i];
484 new_sub = ipatch_item_duplicate_link_func((IpatchItem *)src_sub,
485 link_func, user_data);
486 g_return_if_fail(new_sub != NULL);
487
488 dest_reg->sub_regions[i] = IPATCH_GIG_SUB_REGION(new_sub);
489 dest_reg->sub_region_count = i + 1; /* update count in case of failure */
490 }
491
492 IPATCH_ITEM_RUNLOCK(src_reg);
493 }
494
495 static const GType *
ipatch_gig_region_container_child_types(void)496 ipatch_gig_region_container_child_types(void)
497 {
498 return (gig_region_child_types);
499 }
500
501 /* container is locked by caller */
502 static gboolean
ipatch_gig_region_container_init_iter(IpatchContainer * container,IpatchIter * iter,GType type)503 ipatch_gig_region_container_init_iter(IpatchContainer *container,
504 IpatchIter *iter, GType type)
505 {
506 IpatchGigRegion *region = IPATCH_GIG_REGION(container);
507
508 if(g_type_is_a(type, IPATCH_TYPE_GIG_DIMENSION))
509 ipatch_iter_array_init(iter, (gpointer *)(region->dimensions),
510 region->dimension_count);
511 else if(g_type_is_a(type, IPATCH_TYPE_GIG_SUB_REGION))
512 ipatch_iter_array_init(iter, (gpointer *)(region->sub_regions),
513 region->sub_region_count);
514 else
515 {
516 g_critical("Invalid child type '%s' for parent of type '%s'",
517 g_type_name(type), g_type_name(G_OBJECT_TYPE(container)));
518 return (FALSE);
519 }
520
521 return (TRUE);
522 }
523
524 /**
525 * ipatch_gig_region_new:
526 *
527 * Create a new GigaSampler instrument region.
528 *
529 * Returns: New GigaSampler region with a ref count of 1 which the caller
530 * owns.
531 */
532 IpatchGigRegion *
ipatch_gig_region_new(void)533 ipatch_gig_region_new(void)
534 {
535 return (IPATCH_GIG_REGION(g_object_new(IPATCH_TYPE_GIG_REGION, NULL)));
536 }
537
538 /**
539 * ipatch_gig_region_first: (skip)
540 * @iter: Patch item iterator containing #IpatchGigRegion items
541 *
542 * Gets the first item in a region iterator. A convenience
543 * wrapper for ipatch_iter_first().
544 *
545 * Returns: The first region in @iter or %NULL if empty.
546 */
547 IpatchGigRegion *
ipatch_gig_region_first(IpatchIter * iter)548 ipatch_gig_region_first(IpatchIter *iter)
549 {
550 GObject *obj;
551 g_return_val_if_fail(iter != NULL, NULL);
552
553 obj = ipatch_iter_first(iter);
554
555 if(obj)
556 {
557 return (IPATCH_GIG_REGION(obj));
558 }
559 else
560 {
561 return (NULL);
562 }
563 }
564
565 /**
566 * ipatch_gig_region_next: (skip)
567 * @iter: Patch item iterator containing #IpatchGigRegion items
568 *
569 * Gets the next item in a region iterator. A convenience wrapper
570 * for ipatch_iter_next().
571 *
572 * Returns: The next region in @iter or %NULL if at the end of
573 * the list.
574 */
575 IpatchGigRegion *
ipatch_gig_region_next(IpatchIter * iter)576 ipatch_gig_region_next(IpatchIter *iter)
577 {
578 GObject *obj;
579 g_return_val_if_fail(iter != NULL, NULL);
580
581 obj = ipatch_iter_next(iter);
582
583 if(obj)
584 {
585 return (IPATCH_GIG_REGION(obj));
586 }
587 else
588 {
589 return (NULL);
590 }
591 }
592
593 /**
594 * ipatch_gig_region_set_note_range:
595 * @region: Region to set note range of
596 * @low: Low value of range (MIDI note # between 0 and 127)
597 * @high: High value of range (MIDI note # between 0 and 127)
598 *
599 * Set the MIDI note range that a region is active on.
600 */
601 void
ipatch_gig_region_set_note_range(IpatchGigRegion * region,int low,int high)602 ipatch_gig_region_set_note_range(IpatchGigRegion *region, int low, int high)
603 {
604 GValue titleval = { 0 };
605
606 g_return_if_fail(IPATCH_IS_GIG_REGION(region));
607 g_return_if_fail(low >= 0 && low <= 127);
608 g_return_if_fail(high >= 0 && high <= 127);
609
610 if(low > high) /* swap if backwards */
611 {
612 int temp = low;
613 low = high;
614 high = temp;
615 }
616
617 IPATCH_ITEM_WLOCK(region);
618 region->note_range_low = low;
619 region->note_range_high = high;
620 IPATCH_ITEM_WUNLOCK(region);
621
622 /* title property notify */
623 g_value_init(&titleval, G_TYPE_STRING);
624 ipatch_gig_region_get_title(region, &titleval);
625 ipatch_item_prop_notify((IpatchItem *)region, ipatch_item_pspec_title,
626 &titleval, NULL);
627 g_value_unset(&titleval);
628 }
629
630 /**
631 * ipatch_gig_region_set_velocity_range:
632 * @region: Region to set velocity range of
633 * @low: Low value of range (MIDI velocity # between 0 and 127)
634 * @high: High value of range (MIDI velocity # between 0 and 127)
635 *
636 * Set the MIDI velocity range that a region is active on.
637 */
638 void
ipatch_gig_region_set_velocity_range(IpatchGigRegion * region,int low,int high)639 ipatch_gig_region_set_velocity_range(IpatchGigRegion *region,
640 int low, int high)
641 {
642 g_return_if_fail(IPATCH_IS_GIG_REGION(region));
643 g_return_if_fail(low >= 0 && low <= 127);
644 g_return_if_fail(high >= 0 && high <= 127);
645
646 if(low > high) /* swap if backwards */
647 {
648 int temp = low;
649 low = high;
650 high = temp;
651 }
652
653 IPATCH_ITEM_WLOCK(region);
654 region->velocity_range_low = low;
655 region->velocity_range_high = high;
656 IPATCH_ITEM_WUNLOCK(region);
657 }
658
659 /**
660 * ipatch_gig_region_new_dimension:
661 * @region: GigaSampler region
662 * @type: Type of dimension to add
663 * @split_count: Split bit count
664 *
665 * Adds a new dimension to a GigaSampler region. The dimension is allocated
666 * @split_count number of dimension bits which means the total number of
667 * sub regions will be multiplied by a factor of 2 to the power of
668 * @split_count. There can be a maximum of 32 sub regions for a total of
669 * 5 used split bits.
670 */
671 void
ipatch_gig_region_new_dimension(IpatchGigRegion * region,IpatchGigDimensionType type,int split_count)672 ipatch_gig_region_new_dimension(IpatchGigRegion *region,
673 IpatchGigDimensionType type, int split_count)
674 {
675 IpatchGigDimension *dimension;
676 int new_sub_region_count;
677 int mask, shift;
678 int i;
679
680 g_return_if_fail(IPATCH_IS_GIG_REGION(region));
681 g_return_if_fail(split_count > 0);
682
683 IPATCH_ITEM_WLOCK(region);
684
685 new_sub_region_count = region->sub_region_count * (1 << split_count);
686
687 if(log_if_fail(new_sub_region_count <= 32))
688 {
689 IPATCH_ITEM_WUNLOCK(region);
690 return;
691 }
692
693 /* calculate dimension split bit shift value */
694 for(i = region->sub_region_count, shift = 0; !(i & 1); i >>= 1, shift++);
695
696 /* calculate unshifted mask for the split bit count */
697 for(i = 0, mask = 0; i < split_count; i++, mask = (mask << 1) | 1);
698
699 dimension = ipatch_gig_dimension_new();
700 dimension->type = type;
701 dimension->split_count = split_count;
702 dimension->split_mask = mask << shift;
703 dimension->split_shift = shift;
704
705 region->dimensions[region->dimension_count] = dimension;
706 region->dimension_count++;
707
708 ipatch_item_set_parent(IPATCH_ITEM(dimension), IPATCH_ITEM(region));
709
710 for(i = region->sub_region_count; i < new_sub_region_count; i++)
711 {
712 region->sub_regions[i] = ipatch_gig_sub_region_new();
713 ipatch_item_set_parent(IPATCH_ITEM(region->sub_regions[i]),
714 IPATCH_ITEM(region));
715 }
716
717 region->sub_region_count = new_sub_region_count;
718
719 IPATCH_ITEM_WUNLOCK(region);
720 }
721
722 /**
723 * ipatch_gig_region_remove_dimension:
724 * @region: GigaSampler region
725 * @dim_index: Index of an existing dimension to remove (0-4)
726 * @split_index: Split index to use in the dimension to remove
727 *
728 * Removes a dimension from a GigaSampler region, including all related
729 * sub regions (except those that correspond to the @split_index), and
730 * re-organizes sub regions for new dimension map.
731 */
732 void
ipatch_gig_region_remove_dimension(IpatchGigRegion * region,int dim_index,int split_index)733 ipatch_gig_region_remove_dimension(IpatchGigRegion *region, int dim_index,
734 int split_index)
735 {
736 IpatchGigSubRegion *new_regions[32] = { NULL };
737 int new_region_index = 0;
738 guint max_split_index;
739 guint8 index[5]; /* current index for each dimension */
740 guint8 max[5]; /* max count for each dimension (+1) */
741 int sub_index, bit_index;
742 int i;
743
744 g_return_if_fail(IPATCH_IS_GIG_REGION(region));
745
746 IPATCH_ITEM_WLOCK(region);
747
748 if(log_if_fail(dim_index >= 0 && dim_index < region->dimension_count))
749 {
750 IPATCH_ITEM_WUNLOCK(region);
751 return;
752 }
753
754 max_split_index = 1 << region->dimensions[dim_index]->split_count;
755
756 if(log_if_fail(split_index > 0 && (guint)split_index < max_split_index))
757 {
758 IPATCH_ITEM_WUNLOCK(region);
759 return;
760 }
761
762 /* initialize dimension index and max arrays */
763 for(i = 0; i < region->dimension_count; i++)
764 {
765 index[i] = 0;
766 max[i] = 1 << region->dimensions[i]->split_count;
767 }
768
769 index[dim_index] = split_index; /* the split index to use */
770
771 /* move pointers of sub regions we want to keep to new_regions array */
772 while(TRUE)
773 {
774 /* calculate current sub region index */
775 sub_index = 0;
776 bit_index = 0;
777
778 for(i = 0; i < region->dimension_count; i++)
779 {
780 sub_index += index[i] << bit_index;
781 bit_index += region->dimensions[i]->split_count;
782 }
783
784 /* move the sub region pointer to new region array */
785 new_regions[new_region_index++] = region->sub_regions[sub_index];
786 region->sub_regions[sub_index] = NULL; /* clear pointer */
787
788 /* increment dimension indexes in binary fashion */
789 i = (dim_index != 0) ? 0 : 1; /* first dimension to increment index of */
790
791 while(i < region->dimension_count)
792 {
793 if(++index[i] < max[i])
794 {
795 break; /* carry bit to next dimension? */
796 }
797
798 index[i] = 0;
799
800 if(++i == dim_index)
801 {
802 i++; /* skip remove dimension */
803 }
804 }
805
806 if(i == region->dimension_count)
807 {
808 break; /* are we done yet? */
809 }
810 }
811
812 /* free unused sub regions */
813 for(i = 0; i < region->sub_region_count; i++)
814 if(region->sub_regions[i])
815 {
816 g_object_unref(region->sub_regions[i]);
817 }
818
819 /* copy saved sub region pointers back into region */
820 for(i = 0; i < new_region_index; i++)
821 {
822 region->sub_regions[i] = new_regions[i];
823 }
824
825 /* shift dimensions down into the deleted slot */
826 for(i = dim_index; i < region->dimension_count - 1; i++)
827 {
828 region->dimensions[i] = region->dimensions[i + 1];
829 }
830
831 region->sub_region_count = new_region_index;
832 region->dimension_count--;
833
834 IPATCH_ITEM_WUNLOCK(region);
835 }
836