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(¬eamt, &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