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: IpatchSF2GenItem
22  * @short_description: SoundFont generator item interface
23  * @see_also: #IpatchSF2Preset, #IpatchSF2Inst, #IpatchSF2PZone, #IpatchSF2IZone
24  * @stability: Stable
25  *
26  * Provides an interface for items which have SoundFont generator properties.
27  * SoundFont generators are synthesis parameters used by #IpatchSF2Preset,
28  * #IpatchSF2Inst, #IpatchSF2PZone and #IpatchSF2IZone objects.
29  */
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <math.h>		/* for conversion functions */
33 #include <string.h>
34 #include <glib.h>
35 #include "IpatchSF2GenItem.h"
36 #include "IpatchSF2Gen.h"
37 #include "IpatchParamProp.h"
38 #include "IpatchRange.h"
39 #include "IpatchUnit.h"
40 #include "ipatch_priv.h"
41 #include "builtin_enums.h"
42 #include "util.h"
43 
44 
45 static gboolean
46 ipatch_sf2_gen_item_set_gen_flag_no_notify(IpatchSF2GenItem *item, guint genid,
47         gboolean setflag);
48 
49 /* non realtime synthesis parameters */
50 static const guint8 non_realtime[] =
51 {
52     IPATCH_SF2_GEN_SAMPLE_START,
53     IPATCH_SF2_GEN_SAMPLE_END,
54     IPATCH_SF2_GEN_SAMPLE_COARSE_START,
55     IPATCH_SF2_GEN_SAMPLE_COARSE_END,
56     IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD,
57     IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY,
58     IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD,
59     IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY,
60     IPATCH_SF2_GEN_INSTRUMENT_ID,
61     IPATCH_SF2_GEN_NOTE_RANGE,
62     IPATCH_SF2_GEN_VELOCITY_RANGE,
63     IPATCH_SF2_GEN_FIXED_NOTE,
64     IPATCH_SF2_GEN_FIXED_VELOCITY,
65     IPATCH_SF2_GEN_SAMPLE_ID,
66     IPATCH_SF2_GEN_SAMPLE_MODES,
67     IPATCH_SF2_GEN_EXCLUSIVE_CLASS,
68     IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE
69 };
70 
71 
72 GType
ipatch_sf2_gen_item_get_type(void)73 ipatch_sf2_gen_item_get_type(void)
74 {
75     static GType itype = 0;
76 
77     if(!itype)
78     {
79         static const GTypeInfo info =
80         {
81             sizeof(IpatchSF2GenItemIface),
82             NULL,			/* base_init */
83             NULL,			/* base_finalize */
84             (GClassInitFunc) NULL,
85             (GClassFinalizeFunc) NULL
86         };
87 
88         itype = g_type_register_static(G_TYPE_INTERFACE, "IpatchSF2GenItem",
89                                        &info, 0);
90 
91         /* IpatchSF2GenItemIface types must be IpatchItem objects (for locking) */
92         g_type_interface_add_prerequisite(itype, IPATCH_TYPE_ITEM);
93     }
94 
95     return (itype);
96 }
97 
98 /**
99  * ipatch_sf2_gen_item_get_amount:
100  * @item: Item with generators to get value from
101  * @genid: Generator ID (#IpatchSF2GenType) of value to get
102  * @out_amt: (out): Pointer to store generator amount to
103  *
104  * Get a generator amount from an item with generator properties.
105  *
106  * Returns: %TRUE if generator value is set, %FALSE if not set, in which case
107  * the value stored to output_amt is the default value for the given generator
108  * ID.
109  */
110 gboolean
ipatch_sf2_gen_item_get_amount(IpatchSF2GenItem * item,guint genid,IpatchSF2GenAmount * out_amt)111 ipatch_sf2_gen_item_get_amount(IpatchSF2GenItem *item, guint genid,
112                                IpatchSF2GenAmount *out_amt)
113 {
114     IpatchSF2GenItemIface *iface;
115     IpatchSF2GenArray *genarray;
116     gboolean set;
117 
118     g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), FALSE);
119     g_return_val_if_fail(genid < IPATCH_SF2_GEN_COUNT, FALSE);
120     g_return_val_if_fail(out_amt != NULL, FALSE);
121 
122     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
123     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
124     g_return_val_if_fail(iface->genarray_ofs != 0, FALSE);
125     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
126 
127     IPATCH_ITEM_RLOCK(item);
128     *out_amt = genarray->values[genid];
129     set = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid);
130     IPATCH_ITEM_RUNLOCK(item);
131 
132     return (set);
133 }
134 
135 /**
136  * ipatch_sf2_gen_item_set_amount:
137  * @item: Item with generators to set value in
138  * @genid: Generator ID (#IpatchSF2GenType) of generator to set
139  * @amt: Value to set generator to
140  *
141  * Set a generator amount for an item with generators.
142  *
143  * #IpatchItem property notify is done for the property and possibly the "-set"
144  * property if it was unset before.
145  */
146 void
ipatch_sf2_gen_item_set_amount(IpatchSF2GenItem * item,guint genid,IpatchSF2GenAmount * amt)147 ipatch_sf2_gen_item_set_amount(IpatchSF2GenItem *item, guint genid,
148                                IpatchSF2GenAmount *amt)
149 {
150     IpatchSF2GenItemIface *iface;
151     IpatchSF2GenArray *genarray;
152     IpatchSF2GenType propstype;
153     GParamSpec *pspec;
154     IpatchSF2GenAmount oldamt;
155     GValue oldval = { 0 }, newval = { 0 };
156     gboolean valchanged = FALSE, oldset;
157 
158     g_return_if_fail(IPATCH_IS_ITEM(item));
159     g_return_if_fail(amt != NULL);
160 
161     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
162     propstype = iface->propstype;	/* propstype for this class */
163 
164     g_return_if_fail(ipatch_sf2_gen_is_valid(genid, propstype));
165 
166     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
167     g_return_if_fail(iface->genarray_ofs != 0);
168     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
169 
170     IPATCH_ITEM_WLOCK(item);
171 
172     /* has different value? */
173     if(genarray->values[genid].sword != amt->sword)
174     {
175         oldamt = genarray->values[genid];	/* store old val for notify */
176         genarray->values[genid] = *amt;
177         valchanged = TRUE;
178     }
179 
180     oldset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid);
181     IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid);  /* value is set */
182 
183     IPATCH_ITEM_WUNLOCK(item);
184 
185     if(valchanged)   /* do the property change notify if it actually changed */
186     {
187         pspec = iface->specs[genid];
188         ipatch_sf2_gen_amount_to_value(genid, amt, &newval);
189         ipatch_sf2_gen_amount_to_value(genid, &oldamt, &oldval);
190         ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, &newval, &oldval);
191         g_value_unset(&newval);
192         g_value_unset(&oldval);
193     }
194 
195     if(oldset != TRUE)	/* "set" state of property changed? */
196     {
197         pspec = iface->setspecs[genid];
198         ipatch_item_prop_notify(IPATCH_ITEM(item), pspec,
199                                 ipatch_util_value_bool_true,
200                                 ipatch_util_value_bool_false);
201     }
202 }
203 
204 /**
205  * ipatch_sf2_gen_item_set_gen_flag:
206  * @item: Item with generator properties to set value of a gen "set" flag of
207  * @genid: Generator ID (#IpatchSF2GenType) of generator to set "set" flag value of
208  * @setflag: If %TRUE then generator amount is assigned, FALSE will cause the
209  *   amount to be unset (and revert to its default value)
210  *
211  * Sets the value of a generator "set" flag in an item with generators.
212  *
213  * #IpatchItem property notify is done for the property and possibly the "-set"
214  * property if it was set before.
215  */
216 void
ipatch_sf2_gen_item_set_gen_flag(IpatchSF2GenItem * item,guint genid,gboolean setflag)217 ipatch_sf2_gen_item_set_gen_flag(IpatchSF2GenItem *item, guint genid,
218                                  gboolean setflag)
219 {
220     IpatchSF2GenItemIface *iface;
221     GParamSpec *pspec;
222 
223     if(!ipatch_sf2_gen_item_set_gen_flag_no_notify(item, genid, setflag))
224     {
225         return;
226     }
227 
228     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
229     g_return_if_fail(iface != NULL);
230 
231     /* do "-set" property notify */
232     pspec = iface->setspecs[genid];
233 
234     ipatch_item_prop_notify(IPATCH_ITEM(item), pspec,
235                             IPATCH_UTIL_VALUE_BOOL(setflag),
236                             IPATCH_UTIL_VALUE_BOOL(!setflag));
237 }
238 
239 /* Like ipatch_sf2_gen_item_set_gen_flag() but doesn't do "-set" property notify.
240  * A regular property notify may occur though,
241  * if the effective amount has changed. Caller can check if "-set" parameter
242  * changed from return value (TRUE if changed from old value). */
243 static gboolean
ipatch_sf2_gen_item_set_gen_flag_no_notify(IpatchSF2GenItem * item,guint genid,gboolean setflag)244 ipatch_sf2_gen_item_set_gen_flag_no_notify(IpatchSF2GenItem *item, guint genid,
245         gboolean setflag)
246 {
247     IpatchSF2GenItemIface *iface;
248     IpatchSF2GenArray *genarray;
249     IpatchSF2GenType propstype;
250     GParamSpec *pspec;
251     IpatchSF2GenAmount oldamt, defamt;
252     GValue newval = { 0 }, oldval = { 0 };
253     gboolean valchanged = FALSE, oldset;
254 
255     g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), FALSE);
256 
257     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
258     propstype = iface->propstype;	/* propstype for this class */
259 
260     g_return_val_if_fail(ipatch_sf2_gen_is_valid(genid, propstype), FALSE);
261 
262     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
263     g_return_val_if_fail(iface->genarray_ofs != 0, FALSE);
264     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
265 
266     /* grab default val from gen info table if absolute instrument gen or a range
267        offset preset gen, otherwise offset gens are 0 by default */
268     if(!setflag)
269     {
270         if((propstype & 0x1) == IPATCH_SF2_GEN_PROPS_INST
271                 || ipatch_sf2_gen_info[genid].unit == IPATCH_UNIT_TYPE_RANGE)
272         {
273             defamt = ipatch_sf2_gen_info[genid].def;
274         }
275         else
276         {
277             defamt.sword = 0;
278         }
279     }
280 
281     IPATCH_ITEM_WLOCK(item);
282 
283     /* unsetting flag and amount has different value than default? */
284     if(!setflag && genarray->values[genid].sword != defamt.sword)
285     {
286         oldamt = genarray->values[genid];
287         genarray->values[genid] = defamt;
288         valchanged = TRUE;
289     }
290 
291     oldset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid);
292 
293     /* set/unset flag as requested */
294     if(setflag)
295     {
296         IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid);
297     }
298     else
299     {
300         IPATCH_SF2_GEN_ARRAY_CLEAR_FLAG(genarray, genid);
301     }
302 
303     IPATCH_ITEM_WUNLOCK(item);
304 
305     /* do the property change notify if it actually changed */
306     if(valchanged)
307     {
308         pspec = iface->specs[genid];
309         ipatch_sf2_gen_amount_to_value(genid, &defamt, &newval);
310         ipatch_sf2_gen_amount_to_value(genid, &oldamt, &oldval);
311         ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, &newval, &oldval);
312         g_value_unset(&newval);
313         g_value_unset(&oldval);
314     }
315 
316     return (setflag != oldset);
317 }
318 
319 /**
320  * ipatch_sf2_gen_item_count_set:
321  * @item: Item with generators
322  *
323  * Get count of "set" generators in an item with generators.
324  *
325  * Returns: Count of "set" generators.
326  */
327 guint
ipatch_sf2_gen_item_count_set(IpatchSF2GenItem * item)328 ipatch_sf2_gen_item_count_set(IpatchSF2GenItem *item)
329 {
330     IpatchSF2GenItemIface *iface;
331     IpatchSF2GenArray *genarray;
332     guint count = 0;
333     guint64 v;
334 
335     g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), 0);
336 
337     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
338     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
339     g_return_val_if_fail(iface->genarray_ofs != 0, 0);
340     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
341 
342     IPATCH_ITEM_RLOCK(item);
343 
344     for(v = genarray->flags; v; v >>= 1)
345         if(v & 0x1)
346         {
347             count++;
348         }
349 
350     IPATCH_ITEM_RUNLOCK(item);
351 
352     return (count);
353 }
354 
355 /**
356  * ipatch_sf2_gen_item_copy_all:
357  * @item: Item with generators
358  * @array: (out): Destination generator array to store to
359  *
360  * Copies an item's generators to a caller supplied generator array.
361  */
362 void
ipatch_sf2_gen_item_copy_all(IpatchSF2GenItem * item,IpatchSF2GenArray * array)363 ipatch_sf2_gen_item_copy_all(IpatchSF2GenItem *item, IpatchSF2GenArray *array)
364 {
365     IpatchSF2GenItemIface *iface;
366     IpatchSF2GenArray *genarray;
367 
368     g_return_if_fail(IPATCH_IS_SF2_GEN_ITEM(item));
369     g_return_if_fail(array != NULL);
370 
371     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
372     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
373     g_return_if_fail(iface->genarray_ofs != 0);
374     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
375 
376     IPATCH_ITEM_RLOCK(item);
377     memcpy(array, genarray, sizeof(IpatchSF2GenArray));
378     IPATCH_ITEM_RUNLOCK(item);
379 }
380 
381 /**
382  * ipatch_sf2_gen_item_copy_set:
383  * @item: Item with generators
384  * @array: (out): Destination generator array to store to
385  *
386  * Copies a item's "set" generators to a caller supplied generator array.
387  * This function differs from ipatch_sf2_gen_item_copy_all() in that it
388  * only copies generators that are set. It can be used to override values
389  * in one array with set values in another. Note that this doesn't change
390  * any generators in @item, despite "set" being in the name.
391  */
392 void
ipatch_sf2_gen_item_copy_set(IpatchSF2GenItem * item,IpatchSF2GenArray * array)393 ipatch_sf2_gen_item_copy_set(IpatchSF2GenItem *item, IpatchSF2GenArray *array)
394 {
395     IpatchSF2GenItemIface *iface;
396     IpatchSF2GenArray *genarray;
397     IpatchSF2GenAmount *vals;
398     guint64 v;
399     int i;
400 
401     g_return_if_fail(IPATCH_IS_SF2_GEN_ITEM(item));
402     g_return_if_fail(array != NULL);
403 
404     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
405     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
406     g_return_if_fail(iface->genarray_ofs != 0);
407     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
408 
409     IPATCH_ITEM_RLOCK(item);
410 
411     vals = genarray->values;
412     v = genarray->flags;
413     array->flags |= v;	  /* set destination array bits from source */
414 
415     for(i = 0; v != 0; i++, v >>= 1)
416         if(v & 0x1)
417         {
418             array->values[i] = vals[i];    /* only copy set values */
419         }
420 
421     IPATCH_ITEM_RUNLOCK(item);
422 }
423 
424 /**
425  * ipatch_sf2_gen_item_set_note_range:
426  * @item: Item with generators
427  * @low: Low value of range (MIDI note # between 0 and 127)
428  * @high: High value of range (MIDI note # between 0 and 127)
429  *
430  * Set the MIDI note range that an item with generators is active on.
431  * Only a convenience function really.
432  */
433 void
ipatch_sf2_gen_item_set_note_range(IpatchSF2GenItem * item,int low,int high)434 ipatch_sf2_gen_item_set_note_range(IpatchSF2GenItem *item, int low, int high)
435 {
436     IpatchSF2GenAmount amt;
437 
438     g_return_if_fail(IPATCH_IS_SF2_GEN_ITEM(item));
439     g_return_if_fail(low >= 0 && low <= 127);
440     g_return_if_fail(high >= 0 && high <= 127);
441 
442     if(low > high)		/* swap if backwards */
443     {
444         int temp = low;
445         low = high;
446         high = temp;
447     }
448 
449     amt.range.low = low;
450     amt.range.high = high;
451 
452     ipatch_sf2_gen_item_set_amount(item, IPATCH_SF2_GEN_NOTE_RANGE, &amt);
453 }
454 
455 /**
456  * ipatch_sf2_gen_item_set_velocity_range:
457  * @item: Item with generators
458  * @low: Low value of range (MIDI velocity # between 0 and 127)
459  * @high: High value of range (MIDI velocity # between 0 and 127)
460  *
461  * Set the MIDI velocity range that an item with generators is active on.
462  * Only a convenience function really.
463  */
464 void
ipatch_sf2_gen_item_set_velocity_range(IpatchSF2GenItem * item,int low,int high)465 ipatch_sf2_gen_item_set_velocity_range(IpatchSF2GenItem *item, int low, int high)
466 {
467     IpatchSF2GenAmount amt;
468 
469     g_return_if_fail(IPATCH_IS_SF2_GEN_ITEM(item));
470     g_return_if_fail(low >= 0 && low <= 127);
471     g_return_if_fail(high >= 0 && high <= 127);
472 
473     if(low > high)		/* swap if backwards */
474     {
475         int temp = low;
476         low = high;
477         high = temp;
478     }
479 
480     amt.range.low = low;
481     amt.range.high = high;
482 
483     ipatch_sf2_gen_item_set_amount(item, IPATCH_SF2_GEN_VELOCITY_RANGE, &amt);
484 }
485 
486 /**
487  * ipatch_sf2_gen_item_in_range:
488  * @item: Item with generators
489  * @note: MIDI note number or -1 for wildcard
490  * @velocity: MIDI velocity or -1 for wildcard
491  *
492  * Check if a note and velocity falls in the ranges of an item with generators
493  *
494  * Returns: %TRUE if @item is in @note and @velocity range, %FALSE otherwise
495  */
496 gboolean
ipatch_sf2_gen_item_in_range(IpatchSF2GenItem * item,int note,int velocity)497 ipatch_sf2_gen_item_in_range(IpatchSF2GenItem *item, int note, int velocity)
498 {
499     IpatchSF2GenAmount *noteamt, *velamt;
500     IpatchSF2GenItemIface *iface;
501     IpatchSF2GenArray *genarray;
502     gboolean in_range;
503 
504     g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), FALSE);
505 
506     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
507     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
508     g_return_val_if_fail(iface->genarray_ofs != 0, 0);
509     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
510 
511     IPATCH_ITEM_RLOCK(item);
512     noteamt = &genarray->values[IPATCH_SF2_GEN_NOTE_RANGE];
513     velamt = &genarray->values[IPATCH_SF2_GEN_VELOCITY_RANGE];
514 
515     in_range = ((note == -1) || (note >= noteamt->range.low
516                                  && note <= noteamt->range.high))
517                && ((velocity == -1) || (velocity >= velamt->range.low
518                                         && velocity <= velamt->range.high));
519     IPATCH_ITEM_RUNLOCK(item);
520 
521     return (in_range);
522 }
523 
524 /**
525  * ipatch_sf2_gen_item_intersect_test:
526  * @item: Item with generators
527  * @genarray: Generator array to test note and velocity ranges against
528  *
529  * Check if a given item's note and velocity ranges intersect with those in a
530  * generator array.
531  *
532  * Returns: %TRUE if both note and velocity ranges intersect, %FALSE if one or
533  *   both do not.
534  */
535 gboolean
ipatch_sf2_gen_item_intersect_test(IpatchSF2GenItem * item,const IpatchSF2GenArray * genarray)536 ipatch_sf2_gen_item_intersect_test(IpatchSF2GenItem *item,
537                                    const IpatchSF2GenArray *genarray)
538 {
539     IpatchSF2GenAmount noteamt, velamt;
540     IpatchSF2GenItemIface *iface;
541     IpatchSF2GenArray *itemgenarray;
542 
543     g_return_val_if_fail(IPATCH_IS_SF2_GEN_ITEM(item), FALSE);
544 
545     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
546     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
547     g_return_val_if_fail(iface->genarray_ofs != 0, 0);
548     itemgenarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
549 
550     IPATCH_ITEM_RLOCK(item);
551     noteamt = itemgenarray->values[IPATCH_SF2_GEN_NOTE_RANGE];
552     velamt = itemgenarray->values[IPATCH_SF2_GEN_VELOCITY_RANGE];
553     IPATCH_ITEM_RUNLOCK(item);
554 
555     return ipatch_sf2_gen_range_intersect_test(&noteamt, &genarray->values[IPATCH_SF2_GEN_NOTE_RANGE])
556            && ipatch_sf2_gen_range_intersect_test(&velamt, &genarray->values[IPATCH_SF2_GEN_VELOCITY_RANGE]);
557 }
558 
559 /**
560  * ipatch_sf2_gen_item_class_get_pspec: (skip)
561  * @genid: Generator ID
562  * @klass: Class with an #IpatchSF2GenItem interface
563  *
564  * Get the parameter specification for a given generator ID and object class.
565  *
566  * Returns: (transfer none): The parameter specification for the generator or %NULL if
567  *   the given @genid for @klass is not valid.
568  */
569 GParamSpec *
ipatch_sf2_gen_item_class_get_pspec(GObjectClass * klass,guint genid)570 ipatch_sf2_gen_item_class_get_pspec(GObjectClass *klass, guint genid)
571 {
572     IpatchSF2GenItemIface *gen_item_iface;
573 
574     g_return_val_if_fail(genid < IPATCH_SF2_GEN_COUNT, NULL);
575     g_return_val_if_fail(klass != NULL, NULL);
576 
577     gen_item_iface = g_type_interface_peek(klass, IPATCH_TYPE_SF2_GEN_ITEM);
578     g_return_val_if_fail(gen_item_iface != NULL, NULL);
579 
580     return (gen_item_iface->specs[genid]);
581 }
582 
583 /**
584  * ipatch_sf2_gen_item_class_get_pspec_set: (skip)
585  * @genid: Generator ID
586  * @klass: Class with an #IpatchSF2GenItem interface
587  *
588  * Get a "-set" property parameter specification for a given generator ID and
589  * object class.
590  *
591  * Returns: (transfer none): The "-set" property parameter specification for the generator or
592  *   %NULL if the given @genid or @klass are not valid.
593  */
594 GParamSpec *
ipatch_sf2_gen_item_class_get_pspec_set(GObjectClass * klass,guint genid)595 ipatch_sf2_gen_item_class_get_pspec_set(GObjectClass *klass, guint genid)
596 {
597     IpatchSF2GenItemIface *gen_item_iface;
598 
599     g_return_val_if_fail(genid < IPATCH_SF2_GEN_COUNT, NULL);
600     g_return_val_if_fail(klass != NULL, NULL);
601 
602     gen_item_iface = g_type_interface_peek(klass, IPATCH_TYPE_SF2_GEN_ITEM);
603     g_return_val_if_fail(gen_item_iface != NULL, NULL);
604 
605     return (gen_item_iface->setspecs[genid]);
606 }
607 
608 /**
609  * ipatch_sf2_gen_item_iface_install_properties: (skip)
610  * @klass: Object class to install properties on
611  * @propstype: Type of properties to install (instrument/preset)
612  * @specs: Location to store a pointer to an allocated array of parameter
613  *   specs which should get copied to the interface's specs field and then freed
614  * @setspecs: Location to store a pointer to an allocated array of parameter
615  *   specs which should get copied to the interface's setspecs field and then freed
616  *
617  * Installs generator item properties on the provided @klass.
618  * Used internally in IpatchSF2GenItemIface init functions.
619  */
620 
621 /* This function is complicated by the fact that GObject properties are supposed
622  * to be installed in the class init function, but the gen item interface has
623  * not yet been initialized, so values need to be passed to the interface init
624  * function. */
625 void
ipatch_sf2_gen_item_iface_install_properties(GObjectClass * klass,IpatchSF2GenPropsType propstype,GParamSpec *** specs,GParamSpec *** setspecs)626 ipatch_sf2_gen_item_iface_install_properties(GObjectClass *klass,
627         IpatchSF2GenPropsType propstype,
628         GParamSpec ***specs,
629         GParamSpec ***setspecs)
630 {
631     GEnumClass *enum_class;
632     GEnumValue *enum_value;
633     GParamSpec *pspec;
634     char *set_name;
635     const IpatchSF2GenInfo *gen_info;
636     int nonrt_index = 0;		/* non realtime generator array index */
637     gboolean ispreset;
638     int i, diff, unit;
639 
640     ispreset = propstype & 1;
641 
642     /* get generator type GObject enum */
643     enum_class = g_type_class_ref(IPATCH_TYPE_SF2_GEN_TYPE); /* ++ref */
644     g_return_if_fail(enum_class != NULL);
645 
646     *specs = g_new(GParamSpec *, IPATCH_SF2_GEN_COUNT);
647     *setspecs = g_new(GParamSpec *, IPATCH_SF2_GEN_COUNT);
648 
649     /* install generator properties */
650     for(i = 0; i < IPATCH_SF2_GEN_COUNT; i++)
651     {
652         /* gen is valid for zone type? */
653         if(!ipatch_sf2_gen_is_valid(i, propstype))
654         {
655             continue;
656         }
657 
658         gen_info = &ipatch_sf2_gen_info[i];
659         enum_value = g_enum_get_value(enum_class, i);
660 
661         if(gen_info->unit == IPATCH_UNIT_TYPE_RANGE)
662             pspec = ipatch_param_spec_range(enum_value->value_nick,
663                                             _(gen_info->label),
664                                             _(gen_info->descr ? gen_info->descr : gen_info->label),
665                                             0, 127, 0, 127, G_PARAM_READWRITE);
666         /* allow 30 bit number which stores fine and coarse (32k) values */
667         else if(gen_info->unit == IPATCH_UNIT_TYPE_SAMPLES)
668             pspec = g_param_spec_int(enum_value->value_nick,
669                                      _(gen_info->label),
670                                      _(gen_info->descr ? gen_info->descr : gen_info->label),
671                                      ispreset ? -0x03FFFFFFF : 0, 0x03FFFFFFF, 0,
672                                      G_PARAM_READWRITE);
673         else if(!ispreset)	/* integer absolute property */
674             pspec = g_param_spec_int(enum_value->value_nick,
675                                      _(gen_info->label),
676                                      _(gen_info->descr ? gen_info->descr : gen_info->label),
677                                      gen_info->min.sword, gen_info->max.sword,
678                                      gen_info->def.sword, G_PARAM_READWRITE);
679         else			/* integer offset property */
680         {
681             diff = (int)gen_info->max.sword - gen_info->min.sword;
682             pspec = g_param_spec_int(enum_value->value_nick,
683                                      _(gen_info->label),
684                                      _(gen_info->descr ? gen_info->descr : gen_info->label),
685                                      -diff, diff, 0, G_PARAM_READWRITE);
686         }
687 
688         /* all generators affect synthesis */
689         pspec->flags |= IPATCH_PARAM_SYNTH;
690 
691         /* if generator is not in non_realtime generator array.. */
692         if(nonrt_index >= G_N_ELEMENTS(non_realtime)
693                 || non_realtime[nonrt_index] != i)
694         {
695             pspec->flags |= IPATCH_PARAM_SYNTH_REALTIME;    /* set realtime flag */
696         }
697         else if(non_realtime[nonrt_index] == i)
698         {
699             nonrt_index++;    /* current gen is non realtime, adv to next index */
700         }
701 
702         /* install the property */
703         g_object_class_install_property(klass, i + IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID, pspec);
704 
705         unit = gen_info->unit;
706 
707         /* set parameter unit type extended property */
708         if(ispreset)
709         {
710             if(unit == IPATCH_UNIT_TYPE_SF2_ABS_PITCH)
711             {
712                 unit = IPATCH_UNIT_TYPE_SF2_OFS_PITCH;
713             }
714             else if(unit == IPATCH_UNIT_TYPE_SF2_ABS_TIME)
715             {
716                 unit = IPATCH_UNIT_TYPE_SF2_OFS_TIME;
717             }
718         }
719 
720         ipatch_param_set(pspec, "unit-type", unit, NULL);
721 
722         (*specs)[i] = g_param_spec_ref(pspec);   /* add to parameter spec array */
723 
724         /* create prop-set property and add to setspecs array */
725         set_name = g_strconcat(enum_value->value_nick, "-set", NULL);
726         pspec = g_param_spec_boolean(set_name, NULL, NULL, FALSE, G_PARAM_READWRITE);
727         g_free(set_name);
728 
729         (*setspecs)[i] = g_param_spec_ref(pspec);       /* add to set spec array */
730 
731         /* install "-set" property */
732         g_object_class_install_property(klass, i + IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID, pspec);
733     } /* for loop */
734 
735     g_type_class_unref(enum_class); /* --ref */
736 }
737 
738 /**
739  * ipatch_sf2_gen_item_iface_set_property: (skip)
740  * @item: IpatchItem instance with generator properties
741  * @property_id: Property id to set
742  * @value: Value to set property to
743  *
744  * Used internally for classes with generators, to set values thereof.
745  *
746  * Returns: %TRUE if @property_id handled, %FALSE otherwise
747  */
748 gboolean
ipatch_sf2_gen_item_iface_set_property(IpatchSF2GenItem * item,guint property_id,const GValue * value)749 ipatch_sf2_gen_item_iface_set_property(IpatchSF2GenItem *item,
750                                        guint property_id, const GValue *value)
751 {
752     IpatchSF2GenItemIface *iface;
753     IpatchSF2GenArray *genarray;
754     const IpatchSF2GenInfo *gen_info;
755     IpatchSF2GenAmount amt;
756     IpatchRange *range;
757     int genid, coarse, val;
758     gboolean oldset, oldcoarseset = 0, newcoarseset = 0;
759     IpatchSF2GenAmount oldcoarseamt, newcoarseamt;
760     gboolean coarsevalchanged = FALSE;
761     GParamSpec *pspec;
762     GValue newval = { 0 }, oldval = { 0 };
763     gboolean setflag;
764 
765     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
766 
767     /* a "-set" property? */
768     if(property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID
769             && property_id < IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID + IPATCH_SF2_GEN_COUNT)
770     {
771         genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID;
772 
773         /* generator valid for zone type? */
774         if(!ipatch_sf2_gen_is_valid(genid, iface->propstype))
775         {
776             return (FALSE);
777         }
778 
779         setflag = g_value_get_boolean(value);
780         ipatch_sf2_gen_item_set_gen_flag_no_notify(item, genid, setflag);
781 
782         return (TRUE);
783     }
784 
785     /* regular generator property? */
786     if(property_id < IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID
787             || property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID + IPATCH_SF2_GEN_COUNT)
788     {
789         return (FALSE);
790     }
791 
792     genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID;
793 
794     /* generator valid for zone type? */
795     if(!ipatch_sf2_gen_is_valid(genid, iface->propstype))
796     {
797         return (FALSE);
798     }
799 
800     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
801     g_return_val_if_fail(iface->genarray_ofs != 0, FALSE);
802     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
803 
804     gen_info = &ipatch_sf2_gen_info [genid];
805 
806     if(gen_info->unit == IPATCH_UNIT_TYPE_SAMPLES)
807     {
808         /* set 2 generators - fine and coarse (32k) sample values */
809         if(genid == IPATCH_SF2_GEN_SAMPLE_START)
810         {
811             coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_START;
812         }
813         else if(genid == IPATCH_SF2_GEN_SAMPLE_END)
814         {
815             coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_END;
816         }
817         else if(genid == IPATCH_SF2_GEN_SAMPLE_LOOP_START)
818         {
819             coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START;
820         }
821         else if(genid == IPATCH_SF2_GEN_SAMPLE_LOOP_END)
822         {
823             coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END;
824         }
825         else
826         {
827             g_return_val_if_fail(NOT_REACHED, FALSE);
828         }
829 
830         val = g_value_get_int(value);
831         newcoarseamt.uword = val >> 15;
832 
833         IPATCH_ITEM_WLOCK(item);  /* atomically set both gens */
834 
835         /* prop notify done by IpatchItem methods, so just set value */
836         genarray->values[genid].uword = val & 0x7FFF;
837 
838         oldset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid);
839         IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid);  /* value is set */
840 
841         /* only set coarse value if it has changed */
842         if(genarray->values[coarse].uword != newcoarseamt.uword)
843         {
844             oldcoarseamt = genarray->values[coarse];
845             genarray->values[coarse] = newcoarseamt;
846             coarsevalchanged = TRUE;
847 
848             oldcoarseset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid);
849 
850             if(val != 0)
851             {
852                 IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid);  /* value is set */
853                 newcoarseset = 1;
854             }
855             else
856             {
857                 IPATCH_SF2_GEN_ARRAY_CLEAR_FLAG(genarray, genid);  /* value is unset */
858                 newcoarseset = 0;
859             }
860         }
861 
862         IPATCH_ITEM_WUNLOCK(item);
863 
864         if(oldset != TRUE)	/* "set" state of property changed? */
865         {
866             pspec = iface->setspecs[genid];
867             ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, ipatch_util_value_bool_true,
868                                     ipatch_util_value_bool_false);
869         }
870 
871         if(coarsevalchanged)   /* do the property change notify if it actually changed */
872         {
873             pspec = iface->specs[coarse];
874             ipatch_sf2_gen_amount_to_value(genid, &newcoarseamt, &newval);
875             ipatch_sf2_gen_amount_to_value(genid, &oldcoarseamt, &oldval);
876             ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, &newval, &oldval);
877             g_value_unset(&newval);
878             g_value_unset(&oldval);
879         }
880 
881         if(oldcoarseset != newcoarseset)	/* "set" state of property changed? */
882         {
883             pspec = iface->setspecs[coarse];
884             ipatch_item_prop_notify(IPATCH_ITEM(item), pspec,
885                                     IPATCH_UTIL_VALUE_BOOL(newcoarseset),
886                                     IPATCH_UTIL_VALUE_BOOL(oldcoarseset));
887         }
888     }
889     else
890     {
891         if(gen_info->unit == IPATCH_UNIT_TYPE_RANGE)  /* range property? */
892         {
893             range = ipatch_value_get_range(value);
894             amt.range.low = range->low;
895             amt.range.high = range->high;
896         }
897         else
898         {
899             val = g_value_get_int(value);
900             amt.sword = val;
901         }
902 
903         IPATCH_ITEM_WLOCK(item);
904 
905         /* prop notify done by IpatchItem methods, so just set value */
906         genarray->values[genid] = amt;
907 
908         /* get old value of set flag, and then set it (if no already) */
909         oldset = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid);
910         IPATCH_SF2_GEN_ARRAY_SET_FLAG(genarray, genid);  /* value is set */
911 
912         IPATCH_ITEM_WUNLOCK(item);
913 
914         if(oldset != TRUE)	/* "set" state of property changed? */
915         {
916             pspec = iface->setspecs[genid];
917             ipatch_item_prop_notify(IPATCH_ITEM(item), pspec, ipatch_util_value_bool_true,
918                                     ipatch_util_value_bool_false);
919         }
920     }
921 
922     return (TRUE);
923 }
924 
925 /**
926  * ipatch_sf2_gen_item_iface_get_property: (skip)
927  * @item: IpatchItem instance with generators
928  * @property_id: Property id to set
929  * @value: Value to set property to
930  *
931  * Used internally for classes with generator properties, to get values thereof.
932  *
933  * Returns: %TRUE if @property_id handled, %FALSE otherwise
934  */
935 gboolean
ipatch_sf2_gen_item_iface_get_property(IpatchSF2GenItem * item,guint property_id,GValue * value)936 ipatch_sf2_gen_item_iface_get_property(IpatchSF2GenItem *item, guint property_id,
937                                        GValue *value)
938 {
939     IpatchSF2GenItemIface *iface;
940     IpatchSF2GenArray *genarray;
941     const IpatchSF2GenInfo *gen_info;
942     IpatchSF2GenAmount amt;
943     IpatchRange range;
944     int genid, coarse, val;
945     gboolean setflag;
946 
947     iface = IPATCH_SF2_GEN_ITEM_GET_IFACE(item);
948 
949     /* get pointer to genarray from IpatchSF2GenItemIface->genarray_ofs */
950     g_return_val_if_fail(iface->genarray_ofs != 0, FALSE);
951     genarray = (IpatchSF2GenArray *)G_STRUCT_MEMBER_P(item, iface->genarray_ofs);
952 
953     /* a "-set" property? */
954     if(property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID
955             && property_id < IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID + IPATCH_SF2_GEN_COUNT)
956     {
957         genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_SET_ID;
958 
959         /* generator valid for zone type? */
960         if(!ipatch_sf2_gen_is_valid(genid, iface->propstype))
961         {
962             return (FALSE);
963         }
964 
965         IPATCH_ITEM_RLOCK(item);
966         setflag = IPATCH_SF2_GEN_ARRAY_TEST_FLAG(genarray, genid);
967         IPATCH_ITEM_RUNLOCK(item);
968 
969         g_value_set_boolean(value, setflag);
970         return (TRUE);
971     }
972 
973     /* regular generator property? */
974     if(property_id < IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID
975             || property_id >= IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID + IPATCH_SF2_GEN_COUNT)
976     {
977         return (FALSE);
978     }
979 
980     genid = property_id - IPATCH_SF2_GEN_ITEM_FIRST_PROP_ID;
981 
982     /* generator valid for propstype? */
983     if(!ipatch_sf2_gen_is_valid(genid, iface->propstype))
984     {
985         return (FALSE);
986     }
987 
988     gen_info = &ipatch_sf2_gen_info [genid];
989 
990     if(gen_info->unit == IPATCH_UNIT_TYPE_RANGE)  /* range property? */
991     {
992         IPATCH_ITEM_WLOCK(item);  /* OPTME - lock might not be necessary? */
993         amt = genarray->values[genid];
994         IPATCH_ITEM_WUNLOCK(item);
995 
996         range.low = amt.range.low;
997         range.high = amt.range.high;
998         ipatch_value_set_range(value, &range);
999     }
1000     else if(gen_info->unit == IPATCH_UNIT_TYPE_SAMPLES)
1001     {
1002         /* get 2 generators - fine and coarse (32k) sample values */
1003         if(genid == IPATCH_SF2_GEN_SAMPLE_START)
1004         {
1005             coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_START;
1006         }
1007         else if(genid == IPATCH_SF2_GEN_SAMPLE_END)
1008         {
1009             coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_END;
1010         }
1011         else if(genid == IPATCH_SF2_GEN_SAMPLE_LOOP_START)
1012         {
1013             coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_START;
1014         }
1015         else if(genid == IPATCH_SF2_GEN_SAMPLE_LOOP_END)
1016         {
1017             coarse = IPATCH_SF2_GEN_SAMPLE_COARSE_LOOP_END;
1018         }
1019         else
1020         {
1021             g_return_val_if_fail(NOT_REACHED, FALSE);
1022         }
1023 
1024         IPATCH_ITEM_WLOCK(item);  /* atomically get both gens */
1025         val = genarray->values[genid].uword;
1026         val |= genarray->values[coarse].uword << 15;
1027         IPATCH_ITEM_WUNLOCK(item);
1028 
1029         g_value_set_int(value, val);
1030     }	/* sword read is atomic */
1031     else
1032     {
1033         g_value_set_int(value, genarray->values[genid].sword);
1034     }
1035 
1036     return (TRUE);
1037 }
1038