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: IpatchSF2IZone
22  * @short_description: SoundFont instrument zone object
23  * @see_also: #IpatchSF2Inst, #IpatchSF2Sample
24  * @stability: Stable
25  *
26  * Instrument zones are children to #IpatchSF2Inst objects and define how
27  * their referenced #IpatchSF2Sample is synthesized.
28  */
29 #include <stdarg.h>
30 #include <string.h>
31 #include <glib.h>
32 #include <glib-object.h>
33 #include "IpatchSF2IZone.h"
34 #include "IpatchSF2GenItem.h"
35 #include "IpatchSample.h"
36 #include "IpatchContainer.h"
37 #include "IpatchTypeProp.h"
38 #include "ipatch_priv.h"
39 
40 enum
41 {
42     /* generator IDs are used for lower numbers */
43     PROP_LINK_ITEM = IPATCH_SF2_GEN_ITEM_FIRST_PROP_USER_ID,
44     PROP_SAMPLE_SIZE,
45     PROP_SAMPLE_FORMAT,
46     PROP_SAMPLE_RATE,
47     PROP_SAMPLE_DATA,
48     PROP_LOOP_TYPE,
49     PROP_LOOP_START,
50     PROP_LOOP_END,
51     PROP_ROOT_NOTE,
52     PROP_FINE_TUNE
53 };
54 
55 static void ipatch_sf2_izone_sample_iface_init(IpatchSampleIface *sample_iface);
56 static gboolean ipatch_sf2_izone_sample_iface_open(IpatchSampleHandle *handle,
57         GError **err);
58 static void ipatch_sf2_izone_class_init(IpatchSF2IZoneClass *klass);
59 static void ipatch_sf2_izone_gen_item_iface_init
60 (IpatchSF2GenItemIface *genitem_iface);
61 static void ipatch_sf2_izone_init(IpatchSF2IZone *izone);
62 static inline void ipatch_sf2_izone_get_root_note(IpatchSF2IZone *izone,
63         GValue *value);
64 static inline void ipatch_sf2_izone_get_fine_tune(IpatchSF2IZone *izone,
65         GValue *value);
66 static void ipatch_sf2_izone_set_property(GObject *object,
67         guint property_id,
68         const GValue *value,
69         GParamSpec *pspec);
70 static void ipatch_sf2_izone_get_property(GObject *object,
71         guint property_id, GValue *value,
72         GParamSpec *pspec);
73 /* For quicker access without lookup */
74 static GParamSpec *root_note_pspec;
75 static GParamSpec *fine_tune_pspec;
76 
77 /* For passing data from class init to gen item interface init */
78 static GParamSpec **gen_item_specs = NULL;
79 static GParamSpec **gen_item_setspecs = NULL;
80 
81 
G_DEFINE_TYPE_WITH_CODE(IpatchSF2IZone,ipatch_sf2_izone,IPATCH_TYPE_SF2_ZONE,G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SAMPLE,ipatch_sf2_izone_sample_iface_init)G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SF2_GEN_ITEM,ipatch_sf2_izone_gen_item_iface_init))82 G_DEFINE_TYPE_WITH_CODE(IpatchSF2IZone, ipatch_sf2_izone,
83                         IPATCH_TYPE_SF2_ZONE,
84                         G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE,
85                                 ipatch_sf2_izone_sample_iface_init)
86                         G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SF2_GEN_ITEM,
87                                 ipatch_sf2_izone_gen_item_iface_init))
88 
89 /* sample interface initialization */
90 static void
91 ipatch_sf2_izone_sample_iface_init(IpatchSampleIface *sample_iface)
92 {
93     sample_iface->open = ipatch_sf2_izone_sample_iface_open;
94     sample_iface->loop_types = ipatch_sample_loop_types_standard_release;
95 }
96 
97 static gboolean
ipatch_sf2_izone_sample_iface_open(IpatchSampleHandle * handle,GError ** err)98 ipatch_sf2_izone_sample_iface_open(IpatchSampleHandle *handle, GError **err)
99 {
100     IpatchSF2Zone *zone = IPATCH_SF2_ZONE(handle->sample);
101     IpatchItem *link_item;
102     gboolean retval;
103 
104     link_item = ipatch_sf2_zone_get_link_item(zone);      /* ++ ref link_item */
105     g_return_val_if_fail(link_item != NULL, FALSE);
106     retval = ipatch_sample_handle_cascade_open(handle, IPATCH_SAMPLE(link_item), err);
107     g_object_unref(link_item);    /* -- unref link_item */
108     return (retval);
109 }
110 
111 /* gen item interface initialization */
112 static void
ipatch_sf2_izone_gen_item_iface_init(IpatchSF2GenItemIface * genitem_iface)113 ipatch_sf2_izone_gen_item_iface_init(IpatchSF2GenItemIface *genitem_iface)
114 {
115     genitem_iface->genarray_ofs = G_STRUCT_OFFSET(IpatchSF2Zone, genarray);
116     genitem_iface->propstype = IPATCH_SF2_GEN_PROPS_INST;
117 
118     g_return_if_fail(gen_item_specs != NULL);
119     g_return_if_fail(gen_item_setspecs != NULL);
120 
121     memcpy(&genitem_iface->specs, gen_item_specs, sizeof(genitem_iface->specs));
122     memcpy(&genitem_iface->setspecs, gen_item_setspecs, sizeof(genitem_iface->setspecs));
123     g_free(gen_item_specs);
124     g_free(gen_item_setspecs);
125 }
126 
127 static void
ipatch_sf2_izone_class_init(IpatchSF2IZoneClass * klass)128 ipatch_sf2_izone_class_init(IpatchSF2IZoneClass *klass)
129 {
130     GObjectClass *obj_class = G_OBJECT_CLASS(klass);
131     IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
132 
133     obj_class->get_property = ipatch_sf2_izone_get_property;
134 
135     item_class->item_set_property = ipatch_sf2_izone_set_property;
136 
137     g_object_class_install_property(obj_class, PROP_LINK_ITEM,
138                                     g_param_spec_object("link-item", _("Link item"),
139                                             _("Link item"),
140                                             IPATCH_TYPE_SF2_SAMPLE,
141                                             G_PARAM_READWRITE));
142 
143     /* properties defined by IpatchSample interface */
144     ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_SIZE, "sample-size");
145     ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_FORMAT, "sample-format");
146     ipatch_sample_install_property(obj_class, PROP_SAMPLE_RATE, "sample-rate");
147     ipatch_sample_install_property_readonly(obj_class, PROP_SAMPLE_DATA, "sample-data");
148     ipatch_sample_install_property(obj_class, PROP_LOOP_TYPE, "loop-type");
149     ipatch_sample_install_property(obj_class, PROP_LOOP_START, "loop-start");
150     ipatch_sample_install_property(obj_class, PROP_LOOP_END, "loop-end");
151     root_note_pspec = ipatch_sample_install_property(obj_class,
152                       PROP_ROOT_NOTE, "root-note");
153     fine_tune_pspec = ipatch_sample_install_property(obj_class,
154                       PROP_FINE_TUNE, "fine-tune");
155 
156     /* install generator properties */
157     ipatch_sf2_gen_item_iface_install_properties(obj_class,
158             IPATCH_SF2_GEN_PROPS_INST,
159             &gen_item_specs, &gen_item_setspecs);
160 }
161 
162 static inline void
ipatch_sf2_izone_get_root_note(IpatchSF2IZone * izone,GValue * value)163 ipatch_sf2_izone_get_root_note(IpatchSF2IZone *izone, GValue *value)
164 {
165     IpatchSF2GenAmount amt;
166     IpatchSF2Sample *sample;
167     int val = 0;
168 
169     /* root note override not set or -1? - Get sample root note value. */
170     if(!ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone),
171                                        IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amt)
172             || amt.sword == -1)
173     {
174         /* root note override not set, get from sample */
175         sample = ipatch_sf2_izone_get_sample(izone);  /* ++ ref sample */
176 
177         if(sample)
178         {
179             g_object_get(sample, "root-note", &val, NULL);
180             g_object_unref(sample);  /* -- unref sample */
181         }
182     }
183     else
184     {
185         val = amt.uword;
186     }
187 
188     g_value_set_int(value, val);
189 }
190 
191 static inline void
ipatch_sf2_izone_get_fine_tune(IpatchSF2IZone * izone,GValue * value)192 ipatch_sf2_izone_get_fine_tune(IpatchSF2IZone *izone, GValue *value)
193 {
194     IpatchSF2GenAmount amt;
195     IpatchSF2Sample *sample;
196     int val = 0;
197 
198     /* fine tune override set? */
199     if(!ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone),
200                                        IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amt))
201     {
202         /* fine tune override not set, get from sample */
203         sample = ipatch_sf2_izone_get_sample(izone);  /* ++ ref sample */
204 
205         if(sample)
206         {
207             g_object_get(sample, "fine-tune", &val, NULL);
208             g_object_unref(sample);  /* -- unref sample */
209         }
210     }
211     else
212     {
213         val = amt.sword;
214     }
215 
216     g_value_set_int(value, val);
217 }
218 
219 static void
ipatch_sf2_izone_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)220 ipatch_sf2_izone_set_property(GObject *object, guint property_id,
221                               const GValue *value, GParamSpec *pspec)
222 {
223     IpatchSF2IZone *izone = IPATCH_SF2_IZONE(object);
224     IpatchSF2Sample *sample;
225     IpatchSF2GenAmount amt;
226     GValue vals[2];	/* Gets zeroed below */
227     guint genid;
228     guint uval;
229     int val = 0;
230 
231     /* "root-note" and "fine-tune" sample properties get updated for IZone
232      * override property or -set property */
233     if(property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID)
234     {
235         genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID;
236     }
237     else
238     {
239         genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID;
240     }
241 
242     if(genid == IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE)
243     {
244         memset(vals, 0, sizeof(vals));
245         g_value_init(&vals[0], G_TYPE_INT);
246         ipatch_sf2_izone_get_root_note(izone, &vals[0]);
247     }
248     else if(genid == IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE)
249     {
250         memset(vals, 0, sizeof(vals));
251         g_value_init(&vals[0], G_TYPE_INT);
252         ipatch_sf2_izone_get_fine_tune(izone, &vals[0]);
253     }
254 
255     if(ipatch_sf2_gen_item_iface_set_property((IpatchSF2GenItem *)object,
256             property_id, value))
257     {
258         if(genid == IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE)
259         {
260             g_value_init(&vals[1], G_TYPE_INT);
261             ipatch_sf2_izone_get_root_note(izone, &vals[1]);
262             ipatch_item_prop_notify((IpatchItem *)object, root_note_pspec,
263                                     &vals[1], &vals[0]);
264         }
265         else if(genid == IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE)
266         {
267             g_value_init(&vals[1], G_TYPE_INT);
268             ipatch_sf2_izone_get_fine_tune(izone, &vals[1]);
269             ipatch_item_prop_notify((IpatchItem *)object, fine_tune_pspec,
270                                     &vals[1], &vals[0]);
271         }
272     }
273     else
274     {
275         switch(property_id)
276         {
277         case PROP_LINK_ITEM:
278             sample = g_value_get_object(value);
279             g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample));
280             ipatch_sf2_zone_set_link_item_no_notify((IpatchSF2Zone *)izone,
281                                                     (IpatchItem *)sample, NULL);
282             break;
283 
284         case PROP_SAMPLE_RATE:
285             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
286 
287             if(sample)
288             {
289                 g_object_set(sample, "sample-rate", g_value_get_int(value), NULL);
290                 g_object_unref(sample);  /* -- unref sample */
291             }
292 
293             break;
294 
295         case PROP_LOOP_TYPE:
296             val = g_value_get_enum(value);
297 
298             if(val == IPATCH_SAMPLE_LOOP_NONE)
299             {
300                 amt.uword = IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP;
301             }
302             else if(val == IPATCH_SAMPLE_LOOP_RELEASE)
303             {
304                 amt.uword = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE;
305             }
306             else
307             {
308                 amt.uword = IPATCH_SF2_GEN_SAMPLE_MODE_LOOP;
309             }
310 
311             ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone),
312                                            IPATCH_SF2_GEN_SAMPLE_MODES, &amt);
313             break;
314 
315         case PROP_LOOP_START:
316             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
317 
318             if(sample)
319             {
320                 g_object_get(sample, "loop-start", &uval, NULL);
321                 val = g_value_get_uint(value) - uval;  /* loop start offset */
322                 g_object_unref(sample);  /* -- unref sample */
323 
324                 if(val >= 0)
325                 {
326                     amt.sword = val >> 15;
327                 }
328                 else
329                 {
330                     amt.sword = -(-val >> 15);
331                 }
332 
333                 ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone),
334                                                IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START,
335                                                &amt);
336 
337                 if(val >= 0)
338                 {
339                     amt.sword = val & 0x7FFF;
340                 }
341                 else
342                 {
343                     amt.sword = -(-val & 0x7FFF);
344                 }
345 
346                 ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone),
347                                                IPATCH_SF2_GEN_SAMPLE_LOOP_START, &amt);
348             }
349 
350             break;
351 
352         case PROP_LOOP_END:
353             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
354 
355             if(sample)
356             {
357                 g_object_get(sample, "loop-end", &uval, NULL);
358                 val = g_value_get_uint(value) - uval;  /* loop end offset */
359                 g_object_unref(sample);  /* -- unref sample */
360 
361                 if(val >= 0)
362                 {
363                     amt.sword = val >> 15;
364                 }
365                 else
366                 {
367                     amt.sword = -(-val >> 15);
368                 }
369 
370                 ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone),
371                                                IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END,
372                                                &amt);
373 
374                 if(val >= 0)
375                 {
376                     amt.sword = val & 0x7FFF;
377                 }
378                 else
379                 {
380                     amt.sword = -(-val & 0x7FFF);
381                 }
382 
383                 ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone),
384                                                IPATCH_SF2_GEN_SAMPLE_LOOP_END, &amt);
385             }
386 
387             break;
388 
389         case PROP_ROOT_NOTE:
390             amt.uword = g_value_get_int(value);
391             ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone),
392                                            IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amt);
393             break;
394 
395         case PROP_FINE_TUNE:
396             amt.sword = g_value_get_int(value);
397             ipatch_sf2_gen_item_set_amount(IPATCH_SF2_GEN_ITEM(izone),
398                                            IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amt);
399             break;
400 
401         default:
402             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
403             return;
404         }
405     }
406 }
407 
408 static void
ipatch_sf2_izone_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)409 ipatch_sf2_izone_get_property(GObject *object, guint property_id,
410                               GValue *value, GParamSpec *pspec)
411 {
412     IpatchSF2IZone *izone = IPATCH_SF2_IZONE(object);
413     IpatchSF2Sample *sample;
414     IpatchSF2GenAmount amt;
415     guint uval = 0;
416     int val = 0;
417 
418     if(!ipatch_sf2_gen_item_iface_get_property((IpatchSF2GenItem *)object,
419             property_id, value))
420     {
421         switch(property_id)
422         {
423         case PROP_LINK_ITEM:
424             g_value_take_object(value, ipatch_sf2_zone_get_link_item
425                                 ((IpatchSF2Zone *)izone));
426             break;
427 
428         case PROP_SAMPLE_SIZE:
429             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
430 
431             if(sample)
432             {
433                 g_object_get_property((GObject *)sample, "sample-size", value);
434                 g_object_unref(sample);  /* -- unref sample */
435             }
436 
437             break;
438 
439         case PROP_SAMPLE_FORMAT:
440             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
441 
442             if(sample)
443             {
444                 g_object_get_property((GObject *)sample, "sample-format", value);
445                 g_object_unref(sample);  /* -- unref sample */
446             }
447 
448             break;
449 
450         case PROP_SAMPLE_RATE:
451             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
452 
453             if(sample)
454             {
455                 g_object_get_property((GObject *)sample, "sample-rate", value);
456                 g_object_unref(sample);  /* -- unref sample */
457             }
458 
459             break;
460 
461         case PROP_SAMPLE_DATA:
462             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
463 
464             if(sample)
465             {
466                 g_object_get_property((GObject *)sample, "sample-data", value);
467                 g_object_unref(sample);  /* -- unref sample */
468             }
469 
470             break;
471 
472         case PROP_LOOP_TYPE:
473             ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone),
474                                            IPATCH_SF2_GEN_SAMPLE_MODES, &amt);
475 
476             if(amt.uword == IPATCH_SF2_GEN_SAMPLE_MODE_NOLOOP)
477             {
478                 val = IPATCH_SAMPLE_LOOP_NONE;
479             }
480             /* not used. Should be interpreted as "no loop" */
481             else if(amt.uword == IPATCH_SF2_GEN_SAMPLE_MODE_UNUSED)
482             {
483                 val = IPATCH_SAMPLE_LOOP_NONE;
484             }
485             else if(amt.uword == IPATCH_SF2_GEN_SAMPLE_MODE_LOOP_RELEASE)
486             {
487                 val = IPATCH_SAMPLE_LOOP_RELEASE;
488             }
489             else
490             {
491                 val = IPATCH_SAMPLE_LOOP_STANDARD;
492             }
493 
494             g_value_set_enum(value, val);
495             break;
496 
497         case PROP_LOOP_START:
498             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
499 
500             if(sample)
501             {
502                 g_object_get(sample, "loop-start", &uval, NULL);
503                 g_object_unref(sample);  /* -- unref sample */
504                 val = uval;
505             }
506 
507             ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone),
508                                            IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START,
509                                            &amt);
510             val += (int)amt.sword << 15;
511 
512             ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone),
513                                            IPATCH_SF2_GEN_SAMPLE_LOOP_START, &amt);
514             val += amt.sword;
515 
516             g_value_set_uint(value, CLAMP(val, 0, G_MAXINT));
517             break;
518 
519         case PROP_LOOP_END:
520             sample = ipatch_sf2_izone_get_sample(izone);	/* ++ ref sample */
521 
522             if(sample)
523             {
524                 g_object_get(sample, "loop-end", &uval, NULL);
525                 g_object_unref(sample);  /* -- unref sample */
526                 val = uval;
527             }
528 
529             ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone),
530                                            IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END,
531                                            &amt);
532             val += (int)amt.sword << 15;
533 
534             ipatch_sf2_gen_item_get_amount(IPATCH_SF2_GEN_ITEM(izone),
535                                            IPATCH_SF2_GEN_SAMPLE_LOOP_END, &amt);
536             val += amt.sword;
537 
538             g_value_set_uint(value, CLAMP(val, 0, G_MAXINT));
539             break;
540 
541         case PROP_ROOT_NOTE:
542             ipatch_sf2_izone_get_root_note(izone, value);
543             break;
544 
545         case PROP_FINE_TUNE:
546             ipatch_sf2_izone_get_fine_tune(izone, value);
547             break;
548 
549         default:
550             G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
551             return;
552         }
553     }
554 }
555 
556 static
ipatch_sf2_izone_init(IpatchSF2IZone * izone)557 void ipatch_sf2_izone_init(IpatchSF2IZone *izone)
558 {
559     ipatch_sf2_gen_array_init(&((IpatchSF2Zone *)izone)->genarray, FALSE, FALSE);
560 }
561 
562 /**
563  * ipatch_sf2_izone_new:
564  *
565  * Create a new SoundFont instrument zone object.
566  *
567  * Returns: New SoundFont instrument zone with a reference count of 1. Caller
568  * owns the reference and removing it will destroy the item, unless another
569  * reference is added (if its parented for example).
570  */
571 IpatchSF2IZone *
ipatch_sf2_izone_new(void)572 ipatch_sf2_izone_new(void)
573 {
574     return (IPATCH_SF2_IZONE(g_object_new(IPATCH_TYPE_SF2_IZONE, NULL)));
575 }
576 
577 /**
578  * ipatch_sf2_izone_first: (skip)
579  * @iter: Patch item iterator containing #IpatchSF2IZone items
580  *
581  * Gets the first item in an instrument zone iterator. A convenience
582  * wrapper for ipatch_iter_first().
583  *
584  * Returns: The first instrument zone in @iter or %NULL if empty.
585  */
586 IpatchSF2IZone *
ipatch_sf2_izone_first(IpatchIter * iter)587 ipatch_sf2_izone_first(IpatchIter *iter)
588 {
589     GObject *obj;
590     g_return_val_if_fail(iter != NULL, NULL);
591 
592     obj = ipatch_iter_first(iter);
593 
594     if(obj)
595     {
596         return (IPATCH_SF2_IZONE(obj));
597     }
598     else
599     {
600         return (NULL);
601     }
602 }
603 
604 /**
605  * ipatch_sf2_izone_next: (skip)
606  * @iter: Patch item iterator containing #IpatchSF2IZone items
607  *
608  * Gets the next item in an instrument zone iterator. A convenience wrapper
609  * for ipatch_iter_next().
610  *
611  * Returns: The next instrument zone in @iter or %NULL if at the end of
612  *   the list.
613  */
614 IpatchSF2IZone *
ipatch_sf2_izone_next(IpatchIter * iter)615 ipatch_sf2_izone_next(IpatchIter *iter)
616 {
617     GObject *obj;
618     g_return_val_if_fail(iter != NULL, NULL);
619 
620     obj = ipatch_iter_next(iter);
621 
622     if(obj)
623     {
624         return (IPATCH_SF2_IZONE(obj));
625     }
626     else
627     {
628         return (NULL);
629     }
630 }
631 
632 /**
633  * ipatch_sf2_izone_set_sample:
634  * @izone: Instrument zone to set referenced sample of
635  * @sample: Sample to set instrument zone's referenced item to
636  *
637  * Sets the referenced sample of an instrument zone.
638  */
639 void
ipatch_sf2_izone_set_sample(IpatchSF2IZone * izone,IpatchSF2Sample * sample)640 ipatch_sf2_izone_set_sample(IpatchSF2IZone *izone, IpatchSF2Sample *sample)
641 {
642     g_return_if_fail(IPATCH_IS_SF2_IZONE(izone));
643     g_return_if_fail(IPATCH_IS_SF2_SAMPLE(sample));
644 
645     ipatch_sf2_zone_set_link_item(IPATCH_SF2_ZONE(izone), IPATCH_ITEM(sample));
646 }
647 
648 /**
649  * ipatch_sf2_izone_get_sample:
650  * @izone: Instrument zone to get referenced sample from
651  *
652  * Gets the referenced sample from an instrument zone.
653  * The returned sample's reference count is incremented and the caller
654  * is responsible for unrefing it with g_object_unref().
655  *
656  * Returns: (transfer full): Instrument zone's referenced sample or %NULL if global
657  * zone. Remember to unreference the sample with g_object_unref() when
658  * done with it.
659  */
660 IpatchSF2Sample *
ipatch_sf2_izone_get_sample(IpatchSF2IZone * izone)661 ipatch_sf2_izone_get_sample(IpatchSF2IZone *izone)
662 {
663     IpatchItem *item;
664 
665     g_return_val_if_fail(IPATCH_IS_SF2_IZONE(izone), NULL);
666 
667     item = ipatch_sf2_zone_get_link_item(IPATCH_SF2_ZONE(izone));
668     return (item ? IPATCH_SF2_SAMPLE(item) : NULL);
669 }
670 
671 /**
672  * ipatch_sf2_izone_get_stereo_link:
673  * @izone: Instrument zone
674  *
675  * Get the stereo linked instrument zone of another zone.  This is a zone which
676  * has the same #IpatchSF2Inst parent and has its link-item set to the counter
677  * part of @izone.
678  *
679  * Returns: (transfer full): Stereo linked instrument zone or %NULL if not stereo or it could not
680  *   be found in the same instrument.  Caller owns a reference to the returned
681  *   object.
682  */
683 /* FIXME - This function is kind of a hack, until stereo IpatchSF2Sample and
684  * IpatchSF2IZones are implemented */
685 IpatchSF2IZone *
ipatch_sf2_izone_get_stereo_link(IpatchSF2IZone * izone)686 ipatch_sf2_izone_get_stereo_link(IpatchSF2IZone *izone)
687 {
688     IpatchSF2IZone *linked_izone = NULL;
689     IpatchSF2Sample *sample = NULL, *linked_sample = NULL;
690     IpatchItem *parent = NULL;
691     IpatchList *children = NULL;
692     IpatchSF2GenAmount z_noterange, cmp_noterange, z_velrange, cmp_velrange;
693     int channel;
694     GList *p;
695 
696     g_return_val_if_fail(IPATCH_IS_SF2_IZONE(izone), NULL);
697 
698     sample = ipatch_sf2_izone_get_sample(izone);          /* ++ ref sample */
699 
700     if(!sample)
701     {
702         return (NULL);
703     }
704 
705     g_object_get(sample,
706                  "channel", &channel,
707                  "linked-sample", &linked_sample,        /* ++ ref linked sample */
708                  NULL);
709 
710     if(channel == IPATCH_SF2_SAMPLE_CHANNEL_MONO || !linked_sample)
711     {
712         goto ret;
713     }
714 
715     parent = ipatch_item_get_parent((IpatchItem *)izone);         /* ++ ref parent */
716 
717     if(!IPATCH_IS_CONTAINER(parent))
718     {
719         goto ret;
720     }
721 
722     /* ++ ref children */
723     if(!(children = ipatch_container_get_children((IpatchContainer *)parent,
724                     IPATCH_TYPE_SF2_IZONE)))
725     {
726         goto ret;
727     }
728 
729     /* Check likely previous and next zone of izone for performance */
730 
731     p = g_list_find(children->items, izone);
732 
733     if(p->prev && ipatch_sf2_zone_peek_link_item(p->prev->data)
734             == (IpatchItem *)linked_sample)
735     {
736         linked_izone = g_object_ref(p->prev->data);
737     }
738 
739     if(p->next && ipatch_sf2_zone_peek_link_item(p->next->data)
740             == (IpatchItem *)linked_sample)
741     {
742         if(!linked_izone)
743         {
744             linked_izone = g_object_ref(p->next->data);
745             goto ret;
746         }
747 
748         /* prev is also a match, this can happen in instruments with multiple pairs
749          * of the same stereo sample - Return zone with intersecting note/velocity
750          * ranges or fall through to exhaustive search. */
751 
752         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)izone,
753                                        IPATCH_SF2_GEN_NOTE_RANGE, &z_noterange);
754         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)izone,
755                                        IPATCH_SF2_GEN_VELOCITY_RANGE, &z_velrange);
756         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)linked_izone,
757                                        IPATCH_SF2_GEN_NOTE_RANGE, &cmp_noterange);
758         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)linked_izone,
759                                        IPATCH_SF2_GEN_VELOCITY_RANGE, &cmp_velrange);
760 
761         if(ipatch_sf2_gen_range_intersect_test(&z_noterange, &cmp_noterange)
762                 && ipatch_sf2_gen_range_intersect_test(&z_velrange, &cmp_velrange))
763         {
764             goto ret;
765         }
766 
767         g_object_unref(linked_izone);       /* -- unref linked izone */
768         linked_izone = NULL;
769 
770         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)(p->next->data),
771                                        IPATCH_SF2_GEN_NOTE_RANGE, &cmp_noterange);
772         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)(p->next->data),
773                                        IPATCH_SF2_GEN_VELOCITY_RANGE, &cmp_velrange);
774 
775         if(ipatch_sf2_gen_range_intersect_test(&z_noterange, &cmp_noterange)
776                 && ipatch_sf2_gen_range_intersect_test(&z_velrange, &cmp_velrange))
777         {
778             linked_izone = g_object_ref(p->next->data);
779             goto ret;
780         }
781     }
782     else
783     {
784         if(linked_izone)
785         {
786             goto ret;    /* prev matched, but next did not */
787         }
788 
789         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)izone,
790                                        IPATCH_SF2_GEN_NOTE_RANGE, &z_noterange);
791         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)izone,
792                                        IPATCH_SF2_GEN_VELOCITY_RANGE, &z_velrange);
793     }
794 
795     /* Not previous/next or both of them match, check all items. */
796     for(p = children->items; p; p = p->next)
797     {
798         if(p->data == izone || ipatch_sf2_zone_peek_link_item(p->data)
799                 != (IpatchItem *)linked_sample)
800         {
801             continue;
802         }
803 
804         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)(p->data),
805                                        IPATCH_SF2_GEN_NOTE_RANGE, &cmp_noterange);
806         ipatch_sf2_gen_item_get_amount((IpatchSF2GenItem *)(p->data),
807                                        IPATCH_SF2_GEN_VELOCITY_RANGE, &cmp_velrange);
808 
809         if(!ipatch_sf2_gen_range_intersect_test(&z_noterange, &cmp_noterange)
810                 || !ipatch_sf2_gen_range_intersect_test(&z_velrange, &cmp_velrange))
811         {
812             continue;
813         }
814 
815         linked_izone = g_object_ref(p->data);
816         break;
817     }
818 
819 ret:
820 
821     if(children)
822     {
823         g_object_unref(children);    /* -- unref children */
824     }
825 
826     if(parent)
827     {
828         g_object_unref(parent);    /* -- unref parent */
829     }
830 
831     if(linked_sample)
832     {
833         g_object_unref(linked_sample);    /* -- unref linked sample */
834     }
835 
836     g_object_unref(sample);                               /* -- unref sample */
837 
838     return (linked_izone);
839 }
840