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 Moderal 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 Moderal Public License for more details.
14  *
15  * You should have received a copy of the GNU Moderal 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: IpatchSF2ModItem
22  * @short_description: SoundFont modulator item interface
23  * @see_also:
24  * @stability: Stable
25  *
26  * An interface type which is used by #IpatchSF2Preset, #IpatchSF2Inst,
27  * #IpatchSF2PZone and #IpatchSF2IZone objects to add modulator realtime effect
28  * functionality.
29  */
30 #include <glib.h>
31 #include <glib-object.h>
32 #include "IpatchSF2ModItem.h"
33 #include "IpatchSF2Gen.h"
34 #include "ipatch_priv.h"
35 
36 static void ipatch_sf2_mod_item_iface_init(IpatchSF2ModItemIface *iface);
37 
38 
39 GType
ipatch_sf2_mod_item_get_type(void)40 ipatch_sf2_mod_item_get_type(void)
41 {
42     static GType itype = 0;
43 
44     if(!itype)
45     {
46         static const GTypeInfo info =
47         {
48             sizeof(IpatchSF2ModItemIface),
49             NULL,			/* base_init */
50             NULL,			/* base_finalize */
51             (GClassInitFunc) ipatch_sf2_mod_item_iface_init,
52             (GClassFinalizeFunc) NULL
53         };
54 
55         itype = g_type_register_static(G_TYPE_INTERFACE, "IpatchSF2ModItem",
56                                        &info, 0);
57 
58         /* IpatchSF2ModItemIface types must be IpatchItem objects (for locking) */
59         g_type_interface_add_prerequisite(itype, IPATCH_TYPE_ITEM);
60     }
61 
62     return (itype);
63 }
64 
65 static void
ipatch_sf2_mod_item_iface_init(IpatchSF2ModItemIface * iface)66 ipatch_sf2_mod_item_iface_init(IpatchSF2ModItemIface *iface)
67 {
68     /**
69      * IpatchSF2ModItem:modulators: (type GSList(Ipatch.SF2Mod))
70      *
71      * GSList of IpatchSF2Mod modulators.
72      */
73     g_object_interface_install_property(iface,
74                                         g_param_spec_boxed("modulators", _("Modulators"),
75                                                 _("Modulators"), IPATCH_TYPE_SF2_MOD_LIST,
76                                                 G_PARAM_READWRITE));
77 }
78 
79 /**
80  * ipatch_sf2_mod_item_get_mods:
81  * @item: Item with modulators
82  *
83  * Gets a list of modulators from an item with modulators. List should be freed
84  * with ipatch_sf2_mod_list_free() (free_mods set to %TRUE) when finished
85  * with it.
86  *
87  * Returns: (element-type Ipatch.SF2Mod) (transfer full) (nullable):
88  *   New list of modulators (#IpatchSF2Mod) in @item or %NULL if no modulators.
89  *   Remember to free it with ipatch_sf2_mod_list_free() when finished.
90  */
91 GSList *
ipatch_sf2_mod_item_get_mods(IpatchSF2ModItem * item)92 ipatch_sf2_mod_item_get_mods(IpatchSF2ModItem *item)
93 {
94     IpatchSF2ModItemIface *iface;
95     GSList **pmods, *newlist = NULL;
96     IpatchSF2Mod *mod;
97     GSList *p;
98 
99     g_return_val_if_fail(IPATCH_IS_SF2_MOD_ITEM(item), NULL);
100 
101     /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */
102     iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item);
103     g_return_val_if_fail(iface->modlist_ofs != 0, NULL);
104     pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs);
105 
106     IPATCH_ITEM_RLOCK(item);
107 
108     p = *pmods;
109 
110     while(p)
111     {
112         mod = ipatch_sf2_mod_duplicate((IpatchSF2Mod *)(p->data));
113         newlist = g_slist_prepend(newlist, mod);
114         p = p->next;
115     }
116 
117     IPATCH_ITEM_RUNLOCK(item);
118 
119     newlist = g_slist_reverse(newlist);
120 
121     return (newlist);
122 }
123 
124 /**
125  * ipatch_sf2_mod_item_set_mods: (skip)
126  * @item: Item with modulators
127  * @mod_list: (element-type Ipatch.SF2Mod): Modulator list to assign to zone.
128  * @flags: (type IpatchSF2ModFlags): Flags for controlling list duplication and item property
129  *   notification (#IpatchSF2ModFlags).  If #IPATCH_SF2_MOD_NO_DUPLICATE
130  *   is set then ownership of @mod_list is taken over (not duplicated).
131  *   If #IPATCH_SF2_MOD_NO_NOTIFY is set, then item property notify will not
132  *   be done.
133  *
134  * Sets the complete modulator list of an item with modulators.
135  * If #IPATCH_SF2_MOD_NO_NOTIFY is not in @flags then #IpatchItem property
136  * notify is done.
137  */
138 void
ipatch_sf2_mod_item_set_mods(IpatchSF2ModItem * item,GSList * mod_list,int flags)139 ipatch_sf2_mod_item_set_mods(IpatchSF2ModItem *item, GSList *mod_list, int flags)
140 {
141     GValue old_value = { 0 }, new_value = { 0 };
142     GSList **pmods, *oldlist, *newlist;
143     IpatchSF2ModItemIface *iface;
144 
145     g_return_if_fail(IPATCH_IS_SF2_MOD_ITEM(item));
146 
147     /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */
148     iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item);
149     g_return_if_fail(iface->modlist_ofs != 0);
150     pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs);
151 
152     if(!(flags & IPATCH_SF2_MOD_NO_DUPLICATE))
153     {
154         newlist = ipatch_sf2_mod_list_duplicate(mod_list);    // ++ Duplicate mod_list
155     }
156     else
157     {
158         newlist = mod_list;    // !! Just use mod_list directly
159     }
160 
161     if(!(flags & IPATCH_SF2_MOD_NO_NOTIFY))
162     {
163         mod_list = ipatch_sf2_mod_list_duplicate(mod_list);    // ++ Duplicate mod_list for notify
164     }
165 
166     IPATCH_ITEM_WLOCK(item);
167     oldlist = *pmods;
168     *pmods = newlist;
169     IPATCH_ITEM_WUNLOCK(item);
170 
171     /* do property notify if NO_NOTIFY flag not set */
172     if(!(flags & IPATCH_SF2_MOD_NO_NOTIFY))
173     {
174         g_value_init(&old_value, IPATCH_TYPE_SF2_MOD_LIST);
175         g_value_take_boxed(&old_value, oldlist);                    // -- Take over oldlist
176 
177         g_value_init(&new_value, IPATCH_TYPE_SF2_MOD_LIST);
178         g_value_take_boxed(&new_value, mod_list);                   // -- Take over mod_list
179 
180         ipatch_item_prop_notify(IPATCH_ITEM(item), iface->mod_pspec, &new_value, &old_value);
181         g_value_unset(&new_value);
182         g_value_unset(&old_value);
183     }
184     else
185     {
186         ipatch_sf2_mod_list_free(oldlist, TRUE);    // -- free old list if no notify
187     }
188 }
189 
190 /**
191  * ipatch_sf2_mod_item_set_mods_copy: (rename-to ipatch_sf2_mod_item_set_mods)
192  * @item: Item with modulators
193  * @mod_list: (element-type Ipatch.SF2Mod) (transfer none) (nullable): Modulator list to assign to zone.
194  *
195  * Sets the modulator list of an item with modulators.
196  */
197 void
ipatch_sf2_mod_item_set_mods_copy(IpatchSF2ModItem * item,GSList * mod_list)198 ipatch_sf2_mod_item_set_mods_copy(IpatchSF2ModItem *item, GSList *mod_list)
199 {
200     ipatch_sf2_mod_item_set_mods(item, mod_list, 0);
201 }
202 
203 /**
204  * ipatch_sf2_mod_item_add_mod:
205  * @item: Item with modulators
206  * @mod: (transfer none): Modulator to append to end of @item object's modulator list
207  *
208  * Append a modulator to an item's modulator list.
209  * NOTE: Does not check for duplicates!
210  */
211 void
ipatch_sf2_mod_item_add(IpatchSF2ModItem * item,const IpatchSF2Mod * mod)212 ipatch_sf2_mod_item_add(IpatchSF2ModItem *item, const IpatchSF2Mod *mod)
213 {
214     ipatch_sf2_mod_item_insert(item, mod, -1);
215 }
216 
217 /**
218  * ipatch_sf2_mod_item_insert:
219  * @item: Item with modulators
220  * @mod: (transfer none): Modulator to insert
221  * @pos: Index position in zone's modulator list to insert
222  *   (0 = first, < 0 = last)
223  *
224  * Inserts a modulator into an item's modulator list.
225  * NOTE: Does not check for duplicates!
226  */
227 void
ipatch_sf2_mod_item_insert(IpatchSF2ModItem * item,const IpatchSF2Mod * mod,int pos)228 ipatch_sf2_mod_item_insert(IpatchSF2ModItem *item,
229                            const IpatchSF2Mod *mod, int pos)
230 {
231     GValue old_value = { 0 }, new_value = { 0 };
232     GSList **pmods, *oldlist, *newlist;
233     IpatchSF2ModItemIface *iface;
234     IpatchSF2Mod *newmod;
235 
236     g_return_if_fail(IPATCH_IS_SF2_MOD_ITEM(item));
237     g_return_if_fail(mod != NULL);
238 
239     /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */
240     iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item);
241     g_return_if_fail(iface->modlist_ofs != 0);
242     pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs);
243 
244     newmod = ipatch_sf2_mod_duplicate(mod);                       // ++ duplicate the new modulator
245 
246     IPATCH_ITEM_WLOCK(item);
247     newlist = ipatch_sf2_mod_list_duplicate(*pmods);              // ++ Duplicate current list
248     newlist = g_slist_insert(newlist, newmod, pos);
249     oldlist = *pmods;
250     *pmods = newlist;                                             // !! Item takes over new modulator list
251     newlist = ipatch_sf2_mod_list_duplicate(newlist);             // ++ Duplicate newlist for notify
252     IPATCH_ITEM_WUNLOCK(item);
253 
254     g_value_init(&old_value, IPATCH_TYPE_SF2_MOD_LIST);
255     g_value_take_boxed(&old_value, oldlist);                    // -- Take over oldlist
256 
257     g_value_init(&new_value, IPATCH_TYPE_SF2_MOD_LIST);
258     g_value_take_boxed(&new_value, newlist);                    // -- Take over newlist
259 
260     ipatch_item_prop_notify(IPATCH_ITEM(item), iface->mod_pspec, &new_value, &old_value);
261     g_value_unset(&new_value);
262     g_value_unset(&old_value);
263 }
264 
265 /**
266  * ipatch_sf2_mod_item_remove:
267  * @item: Item with modulators
268  * @mod: (transfer none): Matching values of modulator to remove
269  *
270  * Remove a modulator from an item with modulators. The modulator values in @mod
271  * are used to search the modulator list. The first modulator
272  * that matches all fields in @mod is removed.
273  */
274 void
ipatch_sf2_mod_item_remove(IpatchSF2ModItem * item,const IpatchSF2Mod * mod)275 ipatch_sf2_mod_item_remove(IpatchSF2ModItem *item, const IpatchSF2Mod *mod)
276 {
277     GValue old_value = { 0 }, new_value = { 0 };
278     GSList **pmods, *oldlist, *newlist;
279     IpatchSF2ModItemIface *iface;
280     gboolean changed;
281 
282     g_return_if_fail(IPATCH_IS_SF2_MOD_ITEM(item));
283     g_return_if_fail(mod != NULL);
284 
285     /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */
286     iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item);
287     g_return_if_fail(iface->modlist_ofs != 0);
288     pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs);
289 
290     IPATCH_ITEM_WLOCK(item);
291     newlist = ipatch_sf2_mod_list_duplicate(*pmods);                      // ++ Duplicate current list
292     newlist = ipatch_sf2_mod_list_remove(newlist, mod, &changed);         // Remove the modulator
293     oldlist = *pmods;
294     *pmods = newlist;                                             // !! Item takes over new modulator list
295 
296     if(changed)
297     {
298         newlist = ipatch_sf2_mod_list_duplicate(newlist);    // ++ Duplicate newlist for notify
299     }
300 
301     IPATCH_ITEM_WUNLOCK(item);
302 
303     if(changed)
304     {
305         g_value_init(&old_value, IPATCH_TYPE_SF2_MOD_LIST);
306         g_value_take_boxed(&old_value, oldlist);                    // -- Take over oldlist
307 
308         g_value_init(&new_value, IPATCH_TYPE_SF2_MOD_LIST);
309         g_value_take_boxed(&new_value, newlist);                    // -- Take over newlist
310 
311         ipatch_item_prop_notify(IPATCH_ITEM(item), iface->mod_pspec, &new_value, &old_value);
312         g_value_unset(&new_value);
313         g_value_unset(&old_value);
314     }
315     else
316     {
317         ipatch_sf2_mod_list_free(oldlist, TRUE);                    // -- free oldlist
318         ipatch_sf2_mod_list_free(newlist, TRUE);                    // -- free newlist
319     }
320 }
321 
322 /**
323  * ipatch_sf2_mod_item_change:
324  * @item: Item with modulators
325  * @oldmod: (transfer none): Current values of modulator to set
326  * @newmod: (transfer none): New modulator values
327  *
328  * Sets the values of an existing modulator in an item with modulators. The
329  * modulator list in item is searched for a modulator that matches the values in
330  * @oldmod. If a modulator is found its values are set to those in @newmod.
331  * If it is not found, nothing is done.
332  */
333 void
ipatch_sf2_mod_item_change(IpatchSF2ModItem * item,const IpatchSF2Mod * oldmod,const IpatchSF2Mod * newmod)334 ipatch_sf2_mod_item_change(IpatchSF2ModItem *item, const IpatchSF2Mod *oldmod,
335                            const IpatchSF2Mod *newmod)
336 {
337     GValue old_value = { 0 }, new_value = { 0 };
338     GSList **pmods, *oldlist, *newlist;
339     IpatchSF2ModItemIface *iface;
340     gboolean changed;
341 
342     g_return_if_fail(IPATCH_IS_SF2_MOD_ITEM(item));
343     g_return_if_fail(oldmod != NULL);
344     g_return_if_fail(newmod != NULL);
345 
346     /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */
347     iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item);
348     g_return_if_fail(iface->modlist_ofs != 0);
349     pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs);
350 
351     IPATCH_ITEM_WLOCK(item);
352     newlist = ipatch_sf2_mod_list_duplicate(*pmods);                      // ++ Duplicate current list
353     changed = ipatch_sf2_mod_list_change(newlist, oldmod, newmod);        // Change the modulator
354     oldlist = *pmods;
355     *pmods = newlist;                                             // !! Item takes over new modulator list
356 
357     if(changed)
358     {
359         newlist = ipatch_sf2_mod_list_duplicate(newlist);    // ++ Duplicate newlist for notify
360     }
361 
362     IPATCH_ITEM_WUNLOCK(item);
363 
364     if(changed)
365     {
366         g_value_init(&old_value, IPATCH_TYPE_SF2_MOD_LIST);
367         g_value_take_boxed(&old_value, oldlist);                    // -- Take over oldlist
368 
369         g_value_init(&new_value, IPATCH_TYPE_SF2_MOD_LIST);
370         g_value_take_boxed(&new_value, newlist);                    // -- Take over newlist
371 
372         ipatch_item_prop_notify(IPATCH_ITEM(item), iface->mod_pspec, &new_value, &old_value);
373         g_value_unset(&new_value);
374         g_value_unset(&old_value);
375     }
376     else
377     {
378         ipatch_sf2_mod_list_free(oldlist, TRUE);                    // -- free oldlist
379         ipatch_sf2_mod_list_free(newlist, TRUE);                    // -- free newlist
380     }
381 }
382 
383 /**
384  * ipatch_sf2_mod_item_count:
385  * @item: Item with modulators
386  *
387  * Count number of modulators in an item with modulators.
388  *
389  * Returns: Count of modulators
390  */
391 guint
ipatch_sf2_mod_item_count(IpatchSF2ModItem * item)392 ipatch_sf2_mod_item_count(IpatchSF2ModItem *item)
393 {
394     IpatchSF2ModItemIface *iface;
395     GSList **pmods;
396     guint i;
397 
398     g_return_val_if_fail(IPATCH_IS_SF2_MOD_ITEM(item), 0);
399 
400     /* get pointer to GSList from IpatchSF2ModItemIface->modlist_ofs */
401     iface = IPATCH_SF2_MOD_ITEM_GET_IFACE(item);
402     g_return_val_if_fail(iface->modlist_ofs != 0, 0);
403     pmods = (GSList **)G_STRUCT_MEMBER_P(item, iface->modlist_ofs);
404 
405     IPATCH_ITEM_RLOCK(item);
406     i = g_slist_length(*pmods);
407     IPATCH_ITEM_RUNLOCK(item);
408 
409     return (i);
410 }
411