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(&region->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