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