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