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: IpatchSF2ModList
22  * @short_description: SoundFont modulator lists
23  * @see_also:
24  * @stability: Stable
25  *
26  * SoundFont modulators are used to define real time MIDI effect controls.
27  */
28 #include <glib.h>
29 #include <glib-object.h>
30 #include "IpatchSF2ModList.h"
31 #include "IpatchSF2Gen.h"
32 #include "ipatch_priv.h"
33 
34 /* default modulators */
35 static IpatchSF2Mod default_mods[] =
36 {
37     { 0x0502, IPATCH_SF2_GEN_ATTENUATION, 960, 0x0, 0 },
38     { 0x0102, IPATCH_SF2_GEN_FILTER_CUTOFF, -2400, 0xD02, 0 },
39     { 0x000D, IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, 50, 0x0, 0 },
40     { 0x0081, IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, 50, 0x0, 0 },
41     { 0x0587, IPATCH_SF2_GEN_ATTENUATION, 960, 0x0, 0 },
42     { 0x028A, IPATCH_SF2_GEN_PAN, 1000, 0x0, 0 },
43     { 0x058B, IPATCH_SF2_GEN_ATTENUATION, 960, 0x0, 0 },
44     { 0x00DB, IPATCH_SF2_GEN_REVERB, 200, 0x0, 0 },
45     { 0x00DD, IPATCH_SF2_GEN_CHORUS, 200, 0x0, 0 }
46     //    { 0x020E, InitialPitch WTF?, 12700, 0x0010, 0 },
47 };
48 
49 static GSList *list = NULL; /* list of default modulators */
50 
51 /* ----- Initialization/deinitialization of list ----------------------------*/
52 /* Initialize list */
_ipatch_sf2_mod_list_init(void)53 void _ipatch_sf2_mod_list_init(void)
54 {
55     list = NULL;
56 }
57 
58 /* Free list */
_ipatch_sf2_mod_list_deinit(void)59 void _ipatch_sf2_mod_list_deinit(void)
60 {
61     g_slist_free(list);
62 }
63 
64 /*------ IpatchSF2ModList object functions  ---------------------------------*/
65 
66 GType
ipatch_sf2_mod_list_get_type(void)67 ipatch_sf2_mod_list_get_type(void)
68 {
69     static GType type = 0;
70 
71     if(!type)
72         type = g_boxed_type_register_static("IpatchSF2ModList",
73                                             (GBoxedCopyFunc)ipatch_sf2_mod_list_duplicate,
74                                             (GBoxedFreeFunc)ipatch_sf2_mod_list_boxed_free);
75 
76     return (type);
77 }
78 
79 /**
80  * ipatch_sf2_mod_list_duplicate: (skip)
81  * @list: (element-type Ipatch.SF2Mod) (transfer none): Modulator list to duplicate
82  *
83  * Duplicates a modulator list (list and modulator data).
84  *
85  * Returns: (element-type Ipatch.SF2Mod) (transfer full): New duplicate modulator list which
86  *   should be freed with ipatch_sf2_mod_list_free() with @free_mods set to
87  *   %TRUE when finished with it.
88  */
89 GSList *
ipatch_sf2_mod_list_duplicate(const GSList * list)90 ipatch_sf2_mod_list_duplicate(const GSList *list)
91 {
92     GSList *newlist = NULL;
93 
94     while(list)
95     {
96         newlist = g_slist_prepend(newlist, ipatch_sf2_mod_duplicate
97                                   ((IpatchSF2Mod *)(list->data)));
98         list = list->next;
99     }
100 
101     newlist = g_slist_reverse(newlist);
102 
103     return (newlist);
104 }
105 
106 /**
107  * ipatch_sf2_mod_list_override: (skip)
108  * @alist: First modulator list
109  * @blist: Second modulator list
110  * @copy: If %TRUE then modulator data is duplicated
111  *
112  * Creates a new modulator list by combining @alist and @blist. Modulators
113  * in @blist override identical modulators in @alist. If @copy is set then
114  * the modulator data is also duplicated (a new IpatchSF2ModList is created).
115  *
116  * Returns: New IpatchSF2ModList of combined modulator lists.
117  * Should be freed with ipatch_sf2_mod_list_free() with the free_mods parameter
118  * set to the value of @copy.
119  */
120 GSList *
ipatch_sf2_mod_list_override(const GSList * alist,const GSList * blist,gboolean copy)121 ipatch_sf2_mod_list_override(const GSList *alist, const GSList *blist,
122                              gboolean copy)
123 {
124     GSList *newlist, *bcopy, *p;
125     IpatchSF2Mod *amod, *bmod;
126 
127     if(copy)
128     {
129         newlist = ipatch_sf2_mod_list_duplicate(blist);
130     }
131     else
132     {
133         newlist = g_slist_copy((GSList *)blist);
134     }
135 
136     if(!newlist)			/* optimize for empty blist */
137     {
138         if(copy)
139         {
140             return (ipatch_sf2_mod_list_duplicate(alist));
141         }
142         else
143         {
144             return (g_slist_copy((GSList *)alist));
145         }
146     }
147 
148     bcopy = newlist;
149 
150     while(alist)			/* loop over alist */
151     {
152         amod = (IpatchSF2Mod *)(alist->data);
153         p = bcopy;
154 
155         while(p)
156         {
157             bmod = (IpatchSF2Mod *)(p->data);
158 
159             if(IPATCH_SF2_MOD_ARE_IDENTICAL(amod, bmod))
160             {
161                 break;
162             }
163 
164             p = p->next;
165         }
166 
167         if(!p)			/* no duplicate found? */
168             newlist = g_slist_prepend(newlist, copy ? ipatch_sf2_mod_duplicate
169                                       (amod) : amod);
170 
171         alist = alist->next;
172     }
173 
174     return (newlist);
175 }
176 
177 /**
178  * ipatch_sf2_mod_list_override_copy: (rename-to ipatch_sf2_mod_list_override)
179  * @alist: (element-type Ipatch.SF2Mod) (transfer none) (nullable): First modulator list
180  * @blist: (element-type Ipatch.SF2Mod) (transfer none) (nullable): Second modulator list
181  *
182  * Creates a new modulator list by combining @alist and @blist. Modulators
183  * in @blist override identical modulators in @alist.
184  *
185  * Returns: (element-type Ipatch.SF2Mod) (transfer full) (nullable): New IpatchSF2ModList of
186  * combined modulator lists. Should be freed with ipatch_sf2_mod_list_free() with
187  * the free_mods parameter set to %TRUE.
188  *
189  * Since: 1.1.0
190  */
191 GSList *
ipatch_sf2_mod_list_override_copy(const GSList * alist,const GSList * blist)192 ipatch_sf2_mod_list_override_copy(const GSList *alist, const GSList *blist)
193 {
194     return (ipatch_sf2_mod_list_override(alist, blist, TRUE));
195 }
196 
197 /**
198  * ipatch_sf2_mod_list_offset:
199  * @alist: (element-type Ipatch.SF2Mod) (transfer none) (nullable): First modulator list
200  * @blist: (element-type Ipatch.SF2Mod) (transfer none) (nullable): Second modulator list
201  *
202  * Creates a new modulator list by combining @list and @blist. Modulators
203  * in @blist offset (amounts are added) identical modulators in @alist.
204  * Operation is non-destructive as a new list is created and modulator data
205  * is duplicated.
206  *
207  * NOTE: Optimized for empty @blist.
208  *
209  * Returns: (element-type Ipatch.SF2Mod) (transfer full) (nullable): New IpatchSF2ModList
210  *   of combined modulator lists. Should be freed with ipatch_sf2_mod_list_free()
211  *   with @free_mods set to %TRUE when finished with it.
212  */
213 GSList *
ipatch_sf2_mod_list_offset(const GSList * alist,const GSList * blist)214 ipatch_sf2_mod_list_offset(const GSList *alist, const GSList *blist)
215 {
216     GSList *newlist, *acopy, *p;
217     IpatchSF2Mod *amod, *bmod;
218     int add;
219 
220     newlist = ipatch_sf2_mod_list_duplicate(alist);
221 
222     if(!blist)
223     {
224         return (newlist);    /* optimize for empty blist */
225     }
226 
227     acopy = newlist;
228 
229     while(blist)			/* loop over alist */
230     {
231         bmod = (IpatchSF2Mod *)(blist->data);
232         p = acopy;
233 
234         while(p)
235         {
236             amod = (IpatchSF2Mod *)(p->data);
237 
238             if(IPATCH_SF2_MOD_ARE_IDENTICAL(amod, bmod))
239             {
240                 /* offset (add) the modulator amount */
241                 add = amod->amount + bmod->amount;
242                 add = CLAMP(add, -32768, 32767);
243                 amod->amount = add;
244                 break;
245             }
246 
247             p = p->next;
248         }
249 
250         /* no duplicate found? */
251         if(!p)
252             newlist = g_slist_prepend(newlist,
253                                       ipatch_sf2_mod_duplicate(bmod));
254 
255         blist = blist->next;
256     }
257 
258     return (newlist);
259 }
260 
261 /**
262  * ipatch_sf2_mod_list_free: (skip)
263  * @list: Modulator list to free
264  * @free_mods: If %TRUE then the modulators themselves are freed, %FALSE
265  *   makes this function act just like g_slist_free() (only the list is
266  *   freed not the modulators).
267  *
268  * Free a list of modulators
269  */
270 void
ipatch_sf2_mod_list_free(GSList * list,gboolean free_mods)271 ipatch_sf2_mod_list_free(GSList *list, gboolean free_mods)
272 {
273     GSList *p;
274 
275     if(free_mods)
276     {
277         p = list;
278 
279         while(p)
280         {
281             ipatch_sf2_mod_free((IpatchSF2Mod *)(p->data));
282             p = g_slist_delete_link(p, p);
283         }
284     }
285     else
286     {
287         g_slist_free(list);
288     }
289 }
290 
291 /**
292  * ipatch_sf2_mod_list_boxed_free: (skip)
293  * @list: Modulator list to free
294  *
295  * Like ipatch_sf2_mod_list_free() but used for boxed type declaration and so
296  * therefore frees all modulators in the list.
297  */
298 void
ipatch_sf2_mod_list_boxed_free(GSList * list)299 ipatch_sf2_mod_list_boxed_free(GSList *list)
300 {
301     ipatch_sf2_mod_list_free(list, TRUE);
302 }
303 
304 /**
305  * ipatch_sf2_mod_list_insert: (skip)
306  * @mods: (element-type Ipatch.SF2Mod) (transfer none): Modulator list to insert into
307  * @modvals: (transfer none): Modulator values to insert (a new modulator is created
308  *   and the values are copied to it)
309  * @pos: Index position in zone's modulator list to insert (0 = first, < 0 = last)
310  *
311  * Inserts a modulator into a modulator list. Does not check for
312  * duplicates! The modulator is not used directly, a new one is created and
313  * the values in @mod are copied to it.
314  *
315  * Returns: New start (root) of @mods list.
316  */
317 GSList *
ipatch_sf2_mod_list_insert(GSList * mods,const IpatchSF2Mod * modvals,int pos)318 ipatch_sf2_mod_list_insert(GSList *mods, const IpatchSF2Mod *modvals, int pos)
319 {
320     IpatchSF2Mod *newmod;
321 
322     g_return_val_if_fail(modvals != NULL, mods);
323 
324     newmod = ipatch_sf2_mod_duplicate(modvals);
325     return (g_slist_insert(mods, newmod, pos));
326 }
327 
328 /**
329  * ipatch_sf2_mod_list_remove: (skip)
330  * @mods: (transfer full): Modulator list to remove from
331  * @modvals: Values of modulator to remove
332  * @changed: (out) (optional): Pointer to store bool of whether the list was changed
333  *   (%NULL to ignore)
334  *
335  * Remove a modulator from a modulator list. The modulator values in @modvals
336  * are used to search the modulator list. The first modulator
337  * that matches all fields in @modvals is removed.
338  *
339  * Returns: New start (root) of @mods list.
340  */
341 GSList *
ipatch_sf2_mod_list_remove(GSList * mods,const IpatchSF2Mod * modvals,gboolean * changed)342 ipatch_sf2_mod_list_remove(GSList *mods, const IpatchSF2Mod *modvals,
343                            gboolean *changed)
344 {
345     IpatchSF2Mod *mod;
346     GSList *p;
347 
348     if(changed)
349     {
350         *changed = FALSE;
351     }
352 
353     g_return_val_if_fail(modvals != NULL, mods);
354 
355     for(p = mods; p; p = g_slist_next(p))
356     {
357         mod = (IpatchSF2Mod *)(p->data);
358 
359         if(IPATCH_SF2_MOD_ARE_IDENTICAL_AMOUNT(mod, modvals))
360         {
361             ipatch_sf2_mod_free(mod);
362 
363             if(changed)
364             {
365                 *changed = TRUE;
366             }
367 
368             return (g_slist_delete_link(mods, p));
369         }
370     }
371 
372     return (mods);
373 }
374 
375 /**
376  * ipatch_sf2_mod_list_change: (skip)
377  * @mods: Modulator list to change a modulator in
378  * @oldvals: Current values of modulator to set
379  * @newvals: New modulator values
380  *
381  * Sets the values of an existing modulator in a modulator list. The list
382  * is searched for a modulator that matches the values in @oldvals. If a
383  * modulator is found its values are set to those in @newvals. If it is not
384  * found, nothing is done.
385  *
386  * Returns: %TRUE if changed, %FALSE otherwise (no match)
387  */
388 gboolean
ipatch_sf2_mod_list_change(GSList * mods,const IpatchSF2Mod * oldvals,const IpatchSF2Mod * newvals)389 ipatch_sf2_mod_list_change(GSList *mods, const IpatchSF2Mod *oldvals,
390                            const IpatchSF2Mod *newvals)
391 {
392     IpatchSF2Mod *mod;
393     GSList *p;
394 
395     g_return_val_if_fail(oldvals != NULL, FALSE);
396     g_return_val_if_fail(newvals != NULL, FALSE);
397 
398     for(p = mods; p; p = p->next)
399     {
400         mod = (IpatchSF2Mod *)(p->data);
401 
402         if(IPATCH_SF2_MOD_ARE_IDENTICAL_AMOUNT(mod, oldvals))
403         {
404             *mod = *newvals;	/* replace values in modulator */
405             return (TRUE);
406         }
407     }
408 
409     return (FALSE);
410 }
411 
412 /**
413  * ipatch_sf2_mod_list_get_default:
414  *
415  * Get the list of default instrument modulators.
416  *
417  * Returns: (element-type Ipatch.SF2Mod) (transfer none): The list of default
418  *   modulators. The same modulator list is returned on subsequent calls and
419  *   should not be modified or freed.
420  */
421 G_CONST_RETURN GSList *
ipatch_sf2_mod_list_get_default(void)422 ipatch_sf2_mod_list_get_default(void)
423 {
424     int i;
425 
426     if(!list)
427         for(i = sizeof(default_mods) / sizeof(IpatchSF2Mod) - 1; i >= 0 ; i--)
428         {
429             list = g_slist_prepend(list, &default_mods[i]);
430         }
431 
432     return (list);
433 }
434