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: IpatchSF2Zone
22 * @short_description: Abstract base class for SoundFont zones
23 * @see_also:
24 * @stability: Stable
25 *
26 * Zones are children of #IpatchSF2Preset and #IpatchSF2Inst and define
27 * synthesis parameters and a linked item (#IpatchSF2Inst in the case of
28 * #IpatchSF2PZone and #IpatchSF2Sample in the case of #IpatchSF2IZone).
29 */
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <glib.h>
34 #include <glib-object.h>
35 #include "IpatchSF2Zone.h"
36 #include "IpatchSF2PZone.h"
37 #include "IpatchSF2.h"
38 #include "IpatchSF2Gen.h"
39 #include "IpatchSF2Mod.h"
40 #include "IpatchSF2ModItem.h"
41 #include "IpatchParamProp.h"
42 #include "IpatchRange.h"
43 #include "IpatchUnit.h"
44 #include "builtin_enums.h"
45 #include "ipatch_priv.h"
46
47 static void ipatch_sf2_zone_mod_item_iface_init
48 (IpatchSF2ModItemIface *moditem_iface);
49 static void ipatch_sf2_zone_class_init(IpatchSF2ZoneClass *klass);
50 static void ipatch_sf2_zone_finalize(GObject *gobject);
51 static void ipatch_sf2_zone_get_title(IpatchSF2Zone *zone, GValue *value);
52 static void ipatch_sf2_zone_set_property(GObject *object, guint property_id,
53 const GValue *value, GParamSpec *pspec);
54 static void ipatch_sf2_zone_get_property(GObject *object,
55 guint property_id, GValue *value,
56 GParamSpec *pspec);
57 static void ipatch_sf2_zone_item_copy(IpatchItem *dest, IpatchItem *src,
58 IpatchItemCopyLinkFunc link_func,
59 gpointer user_data);
60 static void ipatch_sf2_zone_item_remove_full(IpatchItem *item, gboolean full);
61 static void ipatch_sf2_zone_link_item_title_notify(IpatchItemPropNotify *info);
62
63 /* generator property IDs go before these */
64 enum
65 {
66 PROP_0,
67 PROP_TITLE,
68 PROP_MODULATORS
69 };
70
71
72 /* For passing between class init and mod item interface init */
73 static GParamSpec *modulators_spec = NULL;
74
75
G_DEFINE_ABSTRACT_TYPE_WITH_CODE(IpatchSF2Zone,ipatch_sf2_zone,IPATCH_TYPE_ITEM,G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SF2_MOD_ITEM,ipatch_sf2_zone_mod_item_iface_init))76 G_DEFINE_ABSTRACT_TYPE_WITH_CODE(IpatchSF2Zone, ipatch_sf2_zone, IPATCH_TYPE_ITEM,
77 G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SF2_MOD_ITEM, ipatch_sf2_zone_mod_item_iface_init))
78
79
80 static void
81 ipatch_sf2_zone_class_init(IpatchSF2ZoneClass *klass)
82 {
83 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
84 IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
85
86 obj_class->finalize = ipatch_sf2_zone_finalize;
87 obj_class->get_property = ipatch_sf2_zone_get_property;
88
89 item_class->copy = ipatch_sf2_zone_item_copy;
90 item_class->item_set_property = ipatch_sf2_zone_set_property;
91 item_class->remove_full = ipatch_sf2_zone_item_remove_full;
92
93 g_object_class_override_property(obj_class, PROP_TITLE, "title");
94 g_object_class_override_property(obj_class, PROP_MODULATORS, "modulators");
95 modulators_spec = g_object_class_find_property(obj_class, "modulators");
96 }
97
98 /* mod item interface initialization */
99 static void
ipatch_sf2_zone_mod_item_iface_init(IpatchSF2ModItemIface * moditem_iface)100 ipatch_sf2_zone_mod_item_iface_init(IpatchSF2ModItemIface *moditem_iface)
101 {
102 moditem_iface->modlist_ofs = G_STRUCT_OFFSET(IpatchSF2Zone, mods);
103
104 /* cache the modulators property for fast notifications */
105 moditem_iface->mod_pspec = modulators_spec;
106 }
107
108 static void
ipatch_sf2_zone_init(IpatchSF2Zone * zone)109 ipatch_sf2_zone_init(IpatchSF2Zone *zone)
110 {
111 }
112
113 static void
ipatch_sf2_zone_finalize(GObject * gobject)114 ipatch_sf2_zone_finalize(GObject *gobject)
115 {
116 IpatchSF2Zone *zone = IPATCH_SF2_ZONE(gobject);
117 IpatchItem *link = NULL;
118 GSList *p;
119
120 IPATCH_ITEM_WLOCK(zone);
121
122 if(zone->item)
123 {
124 link = zone->item;
125 g_object_unref(zone->item);
126 zone->item = NULL;
127 }
128
129 p = zone->mods;
130
131 while(p)
132 {
133 ipatch_sf2_mod_free((IpatchSF2Mod *)(p->data));
134 p = g_slist_next(p);
135 }
136
137 g_slist_free(zone->mods);
138 zone->mods = NULL;
139
140 IPATCH_ITEM_WUNLOCK(zone);
141
142 if(link)
143 ipatch_item_prop_disconnect_matched(link, ipatch_item_pspec_title,
144 ipatch_sf2_zone_link_item_title_notify, zone);
145
146 if(G_OBJECT_CLASS(ipatch_sf2_zone_parent_class)->finalize)
147 {
148 G_OBJECT_CLASS(ipatch_sf2_zone_parent_class)->finalize(gobject);
149 }
150 }
151
152 static void
ipatch_sf2_zone_get_title(IpatchSF2Zone * zone,GValue * value)153 ipatch_sf2_zone_get_title(IpatchSF2Zone *zone, GValue *value)
154 {
155 IpatchItem *ref;
156 char *s = NULL;
157
158 g_object_get(zone, "link-item", &ref, NULL); /* ++ ref zone linked item */
159
160 if(ref)
161 {
162 g_object_get(ref, "name", &s, NULL);
163 g_object_unref(ref); /* -- unref zone linked item */
164 }
165
166 g_value_take_string(value, s);
167 }
168
169 static void
ipatch_sf2_zone_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)170 ipatch_sf2_zone_set_property(GObject *object, guint property_id,
171 const GValue *value, GParamSpec *pspec)
172 {
173 IpatchSF2Zone *zone = IPATCH_SF2_ZONE(object);
174 IpatchSF2ModList *list;
175
176 if(property_id == PROP_MODULATORS)
177 {
178 list = (IpatchSF2ModList *)g_value_get_boxed(value);
179 ipatch_sf2_mod_item_set_mods(IPATCH_SF2_MOD_ITEM(zone), list,
180 IPATCH_SF2_MOD_NO_NOTIFY);
181 }
182 else
183 {
184 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
185 return;
186 }
187 }
188
189 static void
ipatch_sf2_zone_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)190 ipatch_sf2_zone_get_property(GObject *object, guint property_id,
191 GValue *value, GParamSpec *pspec)
192 {
193 IpatchSF2Zone *zone = IPATCH_SF2_ZONE(object);
194 IpatchSF2ModList *list;
195
196 if(property_id == PROP_TITLE)
197 {
198 ipatch_sf2_zone_get_title(zone, value);
199 }
200 else if(property_id == PROP_MODULATORS)
201 {
202 list = ipatch_sf2_mod_item_get_mods(IPATCH_SF2_MOD_ITEM(zone));
203 g_value_take_boxed(value, list);
204 }
205 else
206 {
207 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
208 return;
209 }
210 }
211
212 static void
ipatch_sf2_zone_item_copy(IpatchItem * dest,IpatchItem * src,IpatchItemCopyLinkFunc link_func,gpointer user_data)213 ipatch_sf2_zone_item_copy(IpatchItem *dest, IpatchItem *src,
214 IpatchItemCopyLinkFunc link_func,
215 gpointer user_data)
216 {
217 IpatchSF2Zone *src_zone, *dest_zone;
218 IpatchItem *refitem;
219 IpatchSF2Mod *mod;
220 GSList *p;
221
222 src_zone = IPATCH_SF2_ZONE(src);
223 dest_zone = IPATCH_SF2_ZONE(dest);
224
225 IPATCH_ITEM_RLOCK(src_zone);
226
227 refitem = IPATCH_ITEM_COPY_LINK_FUNC(dest, src_zone->item,
228 link_func, user_data);
229
230 if(refitem)
231 {
232 ipatch_sf2_zone_set_link_item(dest_zone, refitem);
233 }
234
235 p = src_zone->mods;
236
237 while(p) /* duplicate modulators */
238 {
239 mod = ipatch_sf2_mod_duplicate((IpatchSF2Mod *)(p->data));
240 dest_zone->mods = g_slist_prepend(dest_zone->mods, mod);
241 p = g_slist_next(p);
242 }
243
244 /* duplicate generator array */
245 memcpy(&dest_zone->genarray, &src_zone->genarray,
246 sizeof(IpatchSF2GenArray));
247
248 IPATCH_ITEM_RUNLOCK(src_zone);
249
250 dest_zone->mods = g_slist_reverse(dest_zone->mods);
251 }
252
253 static void
ipatch_sf2_zone_item_remove_full(IpatchItem * item,gboolean full)254 ipatch_sf2_zone_item_remove_full(IpatchItem *item, gboolean full)
255 {
256 if(full)
257 {
258 ipatch_sf2_zone_set_link_item(IPATCH_SF2_ZONE(item), NULL);
259 }
260
261 if(IPATCH_ITEM_CLASS(ipatch_sf2_zone_parent_class)->remove_full)
262 {
263 IPATCH_ITEM_CLASS(ipatch_sf2_zone_parent_class)->remove_full(item, full);
264 }
265 }
266
267 /**
268 * ipatch_sf2_zone_first: (skip)
269 * @iter: Patch item iterator containing #IpatchSF2Zone items
270 *
271 * Gets the first item in a zone iterator. A convenience
272 * wrapper for ipatch_iter_first().
273 *
274 * Returns: The first zone in @iter or %NULL if empty.
275 */
276 IpatchSF2Zone *
ipatch_sf2_zone_first(IpatchIter * iter)277 ipatch_sf2_zone_first(IpatchIter *iter)
278 {
279 GObject *obj;
280 g_return_val_if_fail(iter != NULL, NULL);
281
282 obj = ipatch_iter_first(iter);
283
284 if(obj)
285 {
286 return (IPATCH_SF2_ZONE(obj));
287 }
288 else
289 {
290 return (NULL);
291 }
292 }
293
294 /**
295 * ipatch_sf2_zone_next: (skip)
296 * @iter: Patch item iterator containing #IpatchSF2Zone items
297 *
298 * Gets the next item in a zone iterator. A convenience wrapper
299 * for ipatch_iter_next().
300 *
301 * Returns: The next zone in @iter or %NULL if at the end of
302 * the list.
303 */
304 IpatchSF2Zone *
ipatch_sf2_zone_next(IpatchIter * iter)305 ipatch_sf2_zone_next(IpatchIter *iter)
306 {
307 GObject *obj;
308 g_return_val_if_fail(iter != NULL, NULL);
309
310 obj = ipatch_iter_next(iter);
311
312 if(obj)
313 {
314 return (IPATCH_SF2_ZONE(obj));
315 }
316 else
317 {
318 return (NULL);
319 }
320 }
321
322 /**
323 * ipatch_sf2_zone_set_link_item:
324 * @zone: Zone to set zone item of
325 * @item: (nullable): New item for zone to use
326 *
327 * Sets the referenced item of a zone (a #IpatchSF2Inst for preset zones,
328 * #IpatchSF2Sample for instrument zones). The type specific item set routines
329 * for each zone type may be preferred, as this one doesn't do strict type
330 * checking.
331 */
332 void
ipatch_sf2_zone_set_link_item(IpatchSF2Zone * zone,IpatchItem * item)333 ipatch_sf2_zone_set_link_item(IpatchSF2Zone *zone, IpatchItem *item)
334 {
335 GValue oldval = { 0 }, newval = { 0 };
336 IpatchItem *olditem;
337
338 if(!ipatch_sf2_zone_set_link_item_no_notify(zone, item, &olditem))
339 {
340 return;
341 }
342
343 g_value_init(&oldval, G_TYPE_OBJECT);
344 g_value_take_object(&oldval, olditem); /* !! take over reference */
345
346 g_value_init(&newval, G_TYPE_OBJECT);
347 g_value_set_object(&newval, (GObject *)item);
348
349 ipatch_item_prop_notify_by_name((IpatchItem *)zone, "link-item",
350 &newval, &oldval);
351 g_value_unset(&oldval);
352 g_value_unset(&newval);
353 }
354
355 /**
356 * ipatch_sf2_zone_set_link_item_no_notify:
357 * @zone: Zone to set zone item of
358 * @item: (nullable): New item for zone to use
359 * @olditem: (out) (optional) (transfer full): Pointer to store old item pointer or %NULL to ignore.
360 * Caller owns reference if specified.
361 *
362 * Like ipatch_sf2_zone_set_link_item() but performs no property or item
363 * change notifications for "link-item" property (shouldn't normally be used outside of derived types),
364 * and the old value can be retrieved with the @olditem parameter.
365 *
366 * Returns: TRUE if property was changed, FALSE otherwise (invalid inputs)
367 */
368 gboolean
ipatch_sf2_zone_set_link_item_no_notify(IpatchSF2Zone * zone,IpatchItem * item,IpatchItem ** olditem)369 ipatch_sf2_zone_set_link_item_no_notify(IpatchSF2Zone *zone, IpatchItem *item,
370 IpatchItem **olditem)
371 {
372 IpatchItem *oldie;
373 GValue old_title = { 0 }, new_title = { 0 };
374
375 if(olditem)
376 {
377 *olditem = NULL; /* in case of failure */
378 }
379
380 g_return_val_if_fail(IPATCH_IS_SF2_ZONE(zone), FALSE);
381 g_return_val_if_fail(!item || IPATCH_IS_ITEM(item), FALSE);
382
383 ipatch_item_get_property_fast(IPATCH_ITEM(zone), ipatch_item_pspec_title, &old_title); // ++ alloc value
384
385 if(item)
386 {
387 g_object_ref(item); /* ref for zone */
388 }
389
390 IPATCH_ITEM_WLOCK(zone);
391 oldie = zone->item;
392 zone->item = item;
393 IPATCH_ITEM_WUNLOCK(zone);
394
395 /* remove "title" notify on old item */
396 if(oldie)
397 ipatch_item_prop_disconnect_matched(oldie, ipatch_item_pspec_title,
398 ipatch_sf2_zone_link_item_title_notify, zone);
399
400 /* add a prop notify on link-item "title" so zone can notify it's title also */
401 if(item)
402 ipatch_item_prop_connect(item, ipatch_item_pspec_title,
403 ipatch_sf2_zone_link_item_title_notify, NULL, zone);
404
405 /* either caller takes reference to old item or we unref it, unref outside of lock */
406 if(olditem)
407 {
408 *olditem = oldie;
409 }
410 else if(oldie)
411 {
412 g_object_unref(oldie);
413 }
414
415 ipatch_item_get_property_fast(IPATCH_ITEM(zone), ipatch_item_pspec_title, &new_title); // ++ alloc value
416 ipatch_item_prop_notify((IpatchItem *)zone, ipatch_item_pspec_title, &old_title, &new_title);
417
418 g_value_unset(&old_title); // -- free value
419 g_value_unset(&new_title); // -- free value
420
421 return (TRUE);
422 }
423
424 /* property notify for when link-item "title" property changes */
425 static void
ipatch_sf2_zone_link_item_title_notify(IpatchItemPropNotify * info)426 ipatch_sf2_zone_link_item_title_notify(IpatchItemPropNotify *info)
427 {
428 /* notify that zone's title changed */
429 IpatchItem *zone = (IpatchItem *)(info->user_data);
430 ipatch_item_prop_notify_by_name(zone, "title", info->new_value, info->old_value);
431 }
432
433 /**
434 * ipatch_sf2_zone_get_link_item:
435 * @zone: Zone to get referenced item of
436 *
437 * Gets the referenced item from a zone (a #IpatchSF2Inst for preset zones,
438 * #IpatchSF2Sample for instrument zones). The type specific item set routines
439 * for each zone type may be preferred, as this one doesn't do strict type
440 * checking. The returned item's reference count is incremented and the caller
441 * is responsible for unrefing it with g_object_unref().
442 *
443 * Returns: (transfer full): Zone's referenced item or %NULL if global zone. Remember to
444 * unreference the item with g_object_unref() when done with it.
445 */
446 IpatchItem *
ipatch_sf2_zone_get_link_item(IpatchSF2Zone * zone)447 ipatch_sf2_zone_get_link_item(IpatchSF2Zone *zone)
448 {
449 IpatchItem *item;
450
451 g_return_val_if_fail(IPATCH_IS_SF2_ZONE(zone), NULL);
452
453 IPATCH_ITEM_RLOCK(zone);
454 item = zone->item;
455
456 if(item)
457 {
458 g_object_ref(item);
459 }
460
461 IPATCH_ITEM_RUNLOCK(zone);
462
463 return (item);
464 }
465
466 /**
467 * ipatch_sf2_zone_peek_link_item: (skip)
468 * @zone: Zone to get referenced item of
469 *
470 * Like ipatch_sf2_zone_get_link_item() but does not add a reference to
471 * the returned item. This function should only be used if a reference
472 * of the returned item is ensured or only the pointer value is of
473 * interest.
474 *
475 * Returns: (transfer none): Zone's linked item. Remember that the item has NOT been referenced.
476 */
477 IpatchItem *
ipatch_sf2_zone_peek_link_item(IpatchSF2Zone * zone)478 ipatch_sf2_zone_peek_link_item(IpatchSF2Zone *zone)
479 {
480 g_return_val_if_fail(IPATCH_IS_SF2_ZONE(zone), NULL);
481 return (zone->item); /* atomic read */
482 }
483